@aswin.dev/editor 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +56 -0
- package/README.md +3 -3
- package/dist/{AccessibilityPanel-CvQGLdu6.js → AccessibilityPanel-EmQ19wiS.js} +37 -37
- package/dist/{AiChatSidebar-B3SJIKG_.js → AiChatSidebar-DeB0w_32.js} +67 -81
- package/dist/{AiFeatureMenu-BLLKoOos.js → AiFeatureMenu-DoLD5Cde.js} +22 -22
- package/dist/{BlockA11yBadge-CXDLqkcJ.js → BlockA11yBadge-DkNbDvJA.js} +12 -12
- package/dist/CloudEditor-zo9PjjvY.js +1214 -0
- package/dist/{CollaboratorBar-DuPYW5iF.js → CollaboratorBar-BsXMY-8e.js} +21 -21
- package/dist/CommentsSidebar-DIWIa4rS.js +436 -0
- package/dist/CountdownBlock-BCi7-DAM.js +92 -0
- package/dist/CountdownToolbar-BuS8p5ju.js +210 -0
- package/dist/{DesignReferenceSidebar-B8V_F2yF.js → DesignReferenceSidebar-RDlnhL-6.js} +58 -58
- package/dist/{LoadingTrack-B0CWFHXQ.js → LoadingTrack-BJ59h9ok.js} +2 -2
- package/dist/ModuleBrowserModal-b7HbpByz.js +206 -0
- package/dist/ModulePreviewCanvas-D__YlOLS.js +108 -0
- package/dist/{NumberWithSuffix-DkXUez9t.js → NumberWithSuffix-Ca3vNY84.js} +87 -87
- package/dist/{ParagraphEditor-D75wl3BX.js → ParagraphEditor-B1aYPO-6.js} +182 -182
- package/dist/{RichTextEditorContent-DYkIauIk.js → RichTextEditorContent-BmEVMrkJ.js} +38 -38
- package/dist/{SaveModuleDialog-FZ9lxY7_.js → SaveModuleDialog-ClovqI6h.js} +28 -28
- package/dist/{SnapshotHistory-BR3eV120.js → SnapshotHistory-DoqtH0cw.js} +45 -50
- package/dist/{TemplateScoringPanel-4GTNHej5.js → TemplateScoringPanel-DQv2ZAiL.js} +75 -75
- package/dist/{TestEmailModal--ue5w9fT.js → TestEmailModal-iIeYJYsj.js} +28 -28
- package/dist/{TitleEditor-fStSADI-.js → TitleEditor-CTDIwRF_.js} +68 -68
- package/dist/{TplModal-BwSfxIHf.js → TplModal-CBq1J1pG.js} +14 -14
- package/dist/{accessibility-e8JYu_zd.js → accessibility-BU09xZrQ.js} +1 -1
- package/dist/{blockTypeIcons-BcTrDjmH.js → blockTypeIcons-QkC6f5UE.js} +19 -5
- package/dist/bundle-stats.json +7 -7
- package/dist/cdn/chunks/{AccessibilityPanel-B6DOjojm.js → AccessibilityPanel-BeU8nz7A.js} +28 -28
- package/dist/cdn/chunks/{AccessibilityPanel-B6DOjojm.js.map → AccessibilityPanel-BeU8nz7A.js.map} +1 -1
- package/dist/cdn/chunks/{AiFeatureMenu-qEdB2fZJ.js → AiFeatureMenu-B2mhscyP.js} +21 -21
- package/dist/cdn/chunks/AiFeatureMenu-B2mhscyP.js.map +1 -0
- package/dist/cdn/chunks/{BlockA11yBadge-DcEZftf6.js → BlockA11yBadge-C4g77gF0.js} +11 -11
- package/dist/cdn/chunks/{BlockA11yBadge-DcEZftf6.js.map → BlockA11yBadge-C4g77gF0.js.map} +1 -1
- package/dist/{CloudEditor-BCz1ZTYC.js → cdn/chunks/CloudEditor-Btyr0b0_.js} +486 -475
- package/dist/cdn/chunks/CloudEditor-Btyr0b0_.js.map +1 -0
- package/dist/cdn/chunks/{CollaboratorBar--nO7TX6b.js → CollaboratorBar-YBiIjiRh.js} +15 -15
- package/dist/cdn/chunks/CollaboratorBar-YBiIjiRh.js.map +1 -0
- package/dist/cdn/chunks/CountdownBlock-B06UZoWe.js +93 -0
- package/dist/cdn/chunks/CountdownBlock-B06UZoWe.js.map +1 -0
- package/dist/cdn/chunks/CountdownToolbar-C9XZr33O.js +212 -0
- package/dist/cdn/chunks/CountdownToolbar-C9XZr33O.js.map +1 -0
- package/dist/{ModuleBrowserModal-DrUFMTDx.js → cdn/chunks/ModuleBrowserModal-C2CDWKW6.js} +51 -62
- package/dist/cdn/chunks/ModuleBrowserModal-C2CDWKW6.js.map +1 -0
- package/dist/cdn/chunks/ModulePreviewCanvas-Drt8Evai.js +109 -0
- package/dist/cdn/chunks/ModulePreviewCanvas-Drt8Evai.js.map +1 -0
- package/dist/cdn/chunks/{NumberWithSuffix-CE3NrZhH.js → NumberWithSuffix-Ty1bp9vB.js} +64 -64
- package/dist/cdn/chunks/{NumberWithSuffix-CE3NrZhH.js.map → NumberWithSuffix-Ty1bp9vB.js.map} +1 -1
- package/dist/cdn/chunks/{ParagraphEditor-B6Ygu-Mq.js → ParagraphEditor-BA1WbHI7.js} +188 -188
- package/dist/cdn/chunks/{ParagraphEditor-B6Ygu-Mq.js.map → ParagraphEditor-BA1WbHI7.js.map} +1 -1
- package/dist/cdn/chunks/{RichTextEditorContent-DL_y2SrR.js → RichTextEditorContent-BtWCA_Oc.js} +30 -30
- package/dist/cdn/chunks/{RichTextEditorContent-DL_y2SrR.js.map → RichTextEditorContent-BtWCA_Oc.js.map} +1 -1
- package/dist/cdn/chunks/{SaveModuleDialog-B0TnO_o9.js → SaveModuleDialog-AwL0tkCV.js} +21 -21
- package/dist/cdn/chunks/SaveModuleDialog-AwL0tkCV.js.map +1 -0
- package/dist/cdn/chunks/{TitleEditor-BHpfxvwy.js → TitleEditor-DbSyeixS.js} +65 -65
- package/dist/cdn/chunks/TitleEditor-DbSyeixS.js.map +1 -0
- package/dist/cdn/chunks/blockTypeIcons-pQIkxJzc.js +23 -0
- package/dist/cdn/chunks/blockTypeIcons-pQIkxJzc.js.map +1 -0
- package/dist/cdn/chunks/{de-D8CnZxV9.js → de-_tooy3Q8.js} +250 -2
- package/dist/cdn/chunks/de-_tooy3Q8.js.map +1 -0
- package/dist/cdn/chunks/{draggable-Bcb86AsV.js → draggable-C-1_gch3.js} +2 -2
- package/dist/cdn/chunks/{draggable-Bcb86AsV.js.map → draggable-C-1_gch3.js.map} +1 -1
- package/dist/cdn/chunks/{en-8FHaQv4V.js → en-CNqLoIm9.js} +250 -2
- package/dist/cdn/chunks/en-CNqLoIm9.js.map +1 -0
- package/dist/cdn/chunks/{extensions-DIxF31tA.js → extensions-BVKQw_sp.js} +107 -107
- package/dist/cdn/chunks/extensions-BVKQw_sp.js.map +1 -0
- package/dist/cdn/chunks/{features-DEMb13KS.js → features-DIBEo4xl.js} +3388 -2289
- package/dist/cdn/chunks/features-DIBEo4xl.js.map +1 -0
- package/dist/cdn/chunks/{icons-CsLTcirh.js → icons-C1Gg-ov-.js} +346 -54
- package/dist/cdn/chunks/icons-C1Gg-ov-.js.map +1 -0
- package/dist/cdn/chunks/{media-library-CVaNvhpM.js → media-library-BTF_Ko70.js} +985 -985
- package/dist/cdn/chunks/media-library-BTF_Ko70.js.map +1 -0
- package/dist/cdn/chunks/{quality-BaBfc54_.js → quality-C5AmotmP.js} +312 -312
- package/dist/cdn/chunks/quality-C5AmotmP.js.map +1 -0
- package/dist/cdn/chunks/{renderer-CUxvx7ro.js → renderer-D0L44Vlp.js} +236 -142
- package/dist/cdn/chunks/renderer-D0L44Vlp.js.map +1 -0
- package/dist/cdn/chunks/{src-CRaqN-p8.js → src-RKexlYjA.js} +161 -161
- package/dist/cdn/chunks/src-RKexlYjA.js.map +1 -0
- package/dist/cdn/chunks/styleConstants-PgmvNBzQ.js +57 -0
- package/dist/cdn/chunks/styleConstants-PgmvNBzQ.js.map +1 -0
- package/dist/cdn/chunks/styles-CQR6ed13.js +4976 -0
- package/dist/cdn/chunks/styles-CQR6ed13.js.map +1 -0
- package/dist/cdn/chunks/{tiptap-ZhwKyFp7.js → tiptap-CDzAbF2j.js} +43 -43
- package/dist/cdn/chunks/{tiptap-ZhwKyFp7.js.map → tiptap-CDzAbF2j.js.map} +1 -1
- package/dist/cdn/editor.css +1 -1
- package/dist/cdn/editor.js +211 -123
- package/dist/cdn/editor.js.map +1 -1
- package/dist/{check-Da05j8yl.js → check-DJrpDKO_.js} +1 -1
- package/dist/{chevron-down-R2uY34iD.js → chevron-down-C5oBUhT8.js} +1 -1
- package/dist/chevron-right-BqCptpdp.js +10 -0
- package/dist/{circle-alert-DZuGWPX-.js → circle-alert-ZQQc98HC.js} +1 -1
- package/dist/{clock-CRp2sIub.js → clock-ik2pRJKG.js} +1 -1
- package/dist/{cloud-DEk_b4CR.js → cloud-DJG4tb4_.js} +485 -427
- package/dist/{createLucideIcon-C3pa2siy.js → createLucideIcon-ClREiSx3.js} +6 -6
- package/dist/{de-Brqvgr43.js → de-2LEvILeZ.js} +249 -1
- package/dist/{dist-Cp0zXPAD.js → dist-BHAeXaUY.js} +1 -1
- package/dist/{dist-BaQIYPsn.js → dist-BSJvAvH3.js} +1 -1
- package/dist/{dist-wzMIGj-D.js → dist-BeFnymxK.js} +1 -1
- package/dist/dist-Bmir0gYb.js +1167 -0
- package/dist/{dist-DDJIWTRY.js → dist-BvPgo-UK.js} +1 -1
- package/dist/{dist-BFawx6IS.js → dist-CPVBKMmd.js} +51 -51
- package/dist/dist-CivF9P8b.js +382 -0
- package/dist/{dist-D6uC2xhi.js → dist-Df4ie7vZ.js} +1 -1
- package/dist/{dist-us-RpCWN.js → dist-DxOoemkW.js} +1 -1
- package/dist/{dist-KoBJjK1G.js → dist-HEQ52gTJ.js} +1 -1
- package/dist/{dist-DjviJBCi.js → dist-b-XUqAoF.js} +1 -1
- package/dist/{dist-D90y8dvT.js → dist-kZfaVUpW.js} +3 -3
- package/dist/{dist-aRzjfSRN.js → dist-wfAedlzi.js} +1 -1
- package/dist/{en-WDVp87TE.js → en-D2RU2Poj.js} +249 -1
- package/dist/{extensions-CUcl9Ok4.js → extensions-BQ1xXx3d.js} +103 -103
- package/dist/{image-up-MBZKKg9p.js → image-up-DT7gcJLN.js} +1 -1
- package/dist/index.d.ts +102 -14
- package/dist/{info-CJEC7piy.js → info-BSPGcsSM.js} +1 -1
- package/dist/keys-CvX8D-8C.js +10 -0
- package/dist/{loader-circle-DsY5Yg33.js → loader-circle-Balo8p3d.js} +1 -1
- package/dist/{message-circle-yElBbR2C.js → message-circle-B39qAHxs.js} +1 -1
- package/dist/pencil-BZJPNYWR.js +10 -0
- package/dist/{refresh-cw-CE_AGtn8.js → refresh-cw-DwDqGUM0.js} +1 -1
- package/dist/{scan-line-D0vcUekt.js → scan-line-DlghmhNf.js} +1 -1
- package/dist/{send-DH4oDQqC.js → send-DDdhIRj8.js} +1 -1
- package/dist/{shield-check-CfJgs2Hd.js → shield-check-OSQ-JVTX.js} +1 -1
- package/dist/{sparkles-CvRXGqFs.js → sparkles-BN4a-CoF.js} +1 -1
- package/dist/style.css +1 -1
- package/dist/styleConstants-wWRbcuEK.js +55 -0
- package/dist/styles-CavWjvol.js +5487 -0
- package/dist/templatical-editor.js +211 -119
- package/dist/{text-align-start-BT9VUDxK.js → text-align-start-D1weisjS.js} +1 -1
- package/dist/{trash-2-DbP2Y6t2.js → trash-2-CMWvQ5KX.js} +1 -1
- package/dist/{triangle-alert-aOXceTSe.js → triangle-alert-DyidRNX_.js} +1 -1
- package/dist/undo-2-Cg8I7obC.js +16 -0
- package/dist/{useCloudI18n-BuIwR6OE.js → useCloudI18n-BTTNBk5i.js} +2 -2
- package/dist/{useEditorCore-C6ost42Q.js → useEditorCore-BGnzcT7p.js} +2424 -2249
- package/dist/{useI18n-lb2DHDiu.js → useI18n-C2xQZ6K9.js} +2 -2
- package/dist/{useMergeTag-CBwKnnNB.js → useMergeTag-CfuZq2fF.js} +4 -4
- package/dist/{vue.runtime.esm-bundler-DpvJL-nX.js → vue.runtime.esm-bundler-CjauPXjj.js} +1 -1
- package/dist/{x-u2oVmjN_.js → x-CgIhNcT9.js} +1 -1
- package/package.json +15 -15
- package/dist/CommentsSidebar-B1pvJdqF.js +0 -441
- package/dist/CountdownBlock-BNSj1jvJ.js +0 -92
- package/dist/CountdownToolbar-ClJr2GzL.js +0 -210
- package/dist/ModulePreviewCanvas-CHdOwV_4.js +0 -106
- package/dist/cdn/chunks/AiFeatureMenu-qEdB2fZJ.js.map +0 -1
- package/dist/cdn/chunks/CloudEditor-D2GsEC_n.js +0 -1143
- package/dist/cdn/chunks/CloudEditor-D2GsEC_n.js.map +0 -1
- package/dist/cdn/chunks/CollaboratorBar--nO7TX6b.js.map +0 -1
- package/dist/cdn/chunks/CountdownBlock-5YdT1uUu.js +0 -93
- package/dist/cdn/chunks/CountdownBlock-5YdT1uUu.js.map +0 -1
- package/dist/cdn/chunks/CountdownToolbar-DXPXrbAA.js +0 -212
- package/dist/cdn/chunks/CountdownToolbar-DXPXrbAA.js.map +0 -1
- package/dist/cdn/chunks/ModuleBrowserModal-DxoPp81s.js +0 -195
- package/dist/cdn/chunks/ModuleBrowserModal-DxoPp81s.js.map +0 -1
- package/dist/cdn/chunks/ModulePreviewCanvas-CoLdb4ar.js +0 -107
- package/dist/cdn/chunks/ModulePreviewCanvas-CoLdb4ar.js.map +0 -1
- package/dist/cdn/chunks/SaveModuleDialog-B0TnO_o9.js.map +0 -1
- package/dist/cdn/chunks/TitleEditor-BHpfxvwy.js.map +0 -1
- package/dist/cdn/chunks/blockTypeIcons-BzzY9_kA.js +0 -22
- package/dist/cdn/chunks/blockTypeIcons-BzzY9_kA.js.map +0 -1
- package/dist/cdn/chunks/de-D8CnZxV9.js.map +0 -1
- package/dist/cdn/chunks/en-8FHaQv4V.js.map +0 -1
- package/dist/cdn/chunks/extensions-DIxF31tA.js.map +0 -1
- package/dist/cdn/chunks/features-DEMb13KS.js.map +0 -1
- package/dist/cdn/chunks/icons-CsLTcirh.js.map +0 -1
- package/dist/cdn/chunks/media-library-CVaNvhpM.js.map +0 -1
- package/dist/cdn/chunks/quality-BaBfc54_.js.map +0 -1
- package/dist/cdn/chunks/renderer-CUxvx7ro.js.map +0 -1
- package/dist/cdn/chunks/src-CRaqN-p8.js.map +0 -1
- package/dist/cdn/chunks/styleConstants-DP1VOca8.js +0 -57
- package/dist/cdn/chunks/styleConstants-DP1VOca8.js.map +0 -1
- package/dist/cdn/chunks/styles-BHJULjNR.js +0 -2947
- package/dist/cdn/chunks/styles-BHJULjNR.js.map +0 -1
- package/dist/dist-B1IR0bpH.js +0 -326
- package/dist/dist-DJmnUmW9.js +0 -362
- package/dist/keys-ciNfSSGj.js +0 -10
- package/dist/styleConstants-fWzlIIwN.js +0 -55
- package/dist/styles-DEXEkBvg.js +0 -3176
- /package/dist/{_plugin-vue_export-helper-B0hnzhyu.js → _plugin-vue_export-helper-Bwh4ceeO.js} +0 -0
- /package/dist/{de-DCaaCE5s.js → de-D2npYjrZ.js} +0 -0
- /package/dist/{dist-iLBdeBDR.js → dist-DvHLtWVP.js} +0 -0
- /package/dist/{emojiData-PQyVa4bU.js → emojiData-DvQBBzmO.js} +0 -0
- /package/dist/{en-DXCyK4-X.js → en-ib-4h3_o.js} +0 -0
- /package/dist/{formatRelativeTime-BOEf47hq.js → formatRelativeTime-CFDZnEIs.js} +0 -0
- /package/dist/{liquid.browser-CdMv1BTn.js → liquid.browser-7Rv0QDiO.js} +0 -0
- /package/dist/{readableTextColor-CY3SiRnt.js → readableTextColor-C_9OpzBw.js} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"styles-BHJULjNR.js","names":["$emit","$emit"],"sources":["../../../src/utils/resolveAccessibilityOptions.ts","../../../src/components/Canvas.vue","../../../src/components/Canvas.vue","../../../src/components/Sidebar.vue","../../../src/components/Sidebar.vue","../../../src/composables/useMergeTagField.ts","../../../src/components/MergeTagSegments.vue","../../../src/components/MergeTagSegments.vue","../../../src/components/MergeTagInsertButton.vue","../../../src/components/MergeTagInsertButton.vue","../../../src/components/MergeTagTextarea.vue","../../../src/components/MergeTagTextarea.vue","../../../src/components/TemplateSettings.vue","../../../src/components/TemplateSettings.vue","../../../src/components/MergeTagInput.vue","../../../src/components/MergeTagInput.vue","../../../src/components/toolbar/ButtonToolbar.vue","../../../src/components/toolbar/ButtonToolbar.vue","../../../src/components/SpacingControl.vue","../../../src/components/SpacingControl.vue","../../../src/components/toolbar/CollapsibleSection.vue","../../../src/components/toolbar/CollapsibleSection.vue","../../../src/components/toolbar/CommonBlockSettings.vue","../../../src/components/toolbar/CommonBlockSettings.vue","../../../src/components/toolbar/fields/BooleanField.vue","../../../src/components/toolbar/fields/BooleanField.vue","../../../src/components/toolbar/fields/FieldWrapper.vue","../../../src/components/toolbar/fields/FieldWrapper.vue","../../../src/components/toolbar/fields/ColorField.vue","../../../src/components/toolbar/fields/ColorField.vue","../../../src/components/toolbar/fields/ImageField.vue","../../../src/components/toolbar/fields/ImageField.vue","../../../src/components/toolbar/fields/NumberField.vue","../../../src/components/toolbar/fields/NumberField.vue","../../../src/components/toolbar/fields/RepeatableField.vue","../../../src/components/toolbar/fields/RepeatableField.vue","../../../src/components/toolbar/fields/SelectField.vue","../../../src/components/toolbar/fields/SelectField.vue","../../../src/components/toolbar/fields/TextField.vue","../../../src/components/toolbar/fields/TextField.vue","../../../src/components/toolbar/fields/TextareaField.vue","../../../src/components/toolbar/fields/TextareaField.vue","../../../src/components/toolbar/fields/index.ts","../../../src/components/toolbar/CustomBlockToolbar.vue","../../../src/components/toolbar/CustomBlockToolbar.vue","../../../src/components/toolbar/DividerToolbar.vue","../../../src/components/toolbar/DividerToolbar.vue","../../../src/components/toolbar/HtmlToolbar.vue","../../../src/components/toolbar/HtmlToolbar.vue","../../../src/components/toolbar/ImageToolbar.vue","../../../src/components/toolbar/ImageToolbar.vue","../../../src/components/toolbar/MenuToolbar.vue","../../../src/components/toolbar/MenuToolbar.vue","../../../src/components/toolbar/SectionToolbar.vue","../../../src/components/toolbar/SectionToolbar.vue","../../../src/components/toolbar/SocialToolbar.vue","../../../src/components/toolbar/SocialToolbar.vue","../../../src/components/toolbar/SpacerToolbar.vue","../../../src/components/toolbar/SpacerToolbar.vue","../../../src/components/toolbar/TableToolbar.vue","../../../src/components/toolbar/TableToolbar.vue","../../../src/components/toolbar/TitleToolbar.vue","../../../src/components/toolbar/TitleToolbar.vue","../../../src/components/toolbar/VideoToolbar.vue","../../../src/components/toolbar/VideoToolbar.vue","../../../src/components/Toolbar.vue","../../../src/components/Toolbar.vue","../../../src/components/RightSidebar.vue","../../../src/components/RightSidebar.vue","../../../src/components/ViewportToggle.vue","../../../src/components/ViewportToggle.vue","../../../src/components/PreviewToggle.vue","../../../src/components/PreviewToggle.vue","../../../src/components/DarkModeToggle.vue","../../../src/components/DarkModeToggle.vue","../../../src/components/EditorFooter.vue","../../../src/components/EditorFooter.vue"],"sourcesContent":["import type { A11yOptions } from \"@templatical/quality\";\n\n/**\n * Build the `A11yOptions` object the editor passes to the lint composable.\n *\n * In editor / cloud-editor mode the linter locale is forced to match the\n * editor UI locale — `accessibility.locale` set by the consumer is\n * ignored. Headless callers (`lintAccessibility(...)` directly) keep\n * full control.\n */\nexport function resolveAccessibilityOptions(config: {\n locale?: string;\n accessibility?: A11yOptions;\n}): A11yOptions {\n return {\n ...config.accessibility,\n locale: config.locale,\n };\n}\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useCloudI18n } from \"../composables/useCloudI18n\";\nimport type {\n Block,\n Collaborator,\n CustomBlock as CustomBlockType,\n TemplateContent,\n ViewportSize,\n} from \"@templatical/types\";\nimport { ImageUp, Sparkles, SquarePlus } from \"@lucide/vue\";\nimport { computed, inject, type Component } from \"vue\";\nimport {\n EDITOR_KEY,\n CONDITION_PREVIEW_KEY,\n BLOCK_REGISTRY_KEY,\n CAPABILITIES_KEY,\n requireInject,\n} from \"../keys\";\nimport draggable from \"vuedraggable\";\nimport { resolveBlockComponent } from \"../utils/blockComponentResolver\";\nimport { readableTextColor } from \"../utils/readableTextColor\";\n\nimport BlockWrapper from \"./blocks/BlockWrapper.vue\";\nimport SectionBlock from \"./blocks/SectionBlock.vue\";\nimport TitleBlock from \"./blocks/TitleBlock.vue\";\nimport ParagraphBlock from \"./blocks/ParagraphBlock.vue\";\nimport ImageBlock from \"./blocks/ImageBlock.vue\";\nimport ButtonBlock from \"./blocks/ButtonBlock.vue\";\nimport DividerBlock from \"./blocks/DividerBlock.vue\";\nimport SpacerBlock from \"./blocks/SpacerBlock.vue\";\nimport HtmlBlock from \"./blocks/HtmlBlock.vue\";\nimport SocialIconsBlock from \"./blocks/SocialIconsBlock.vue\";\nimport MenuBlock from \"./blocks/MenuBlock.vue\";\nimport TableBlock from \"./blocks/TableBlock.vue\";\nimport CustomBlock from \"./blocks/CustomBlock.vue\";\nimport VideoBlock from \"./blocks/VideoBlock.vue\";\nimport CountdownBlockComponent from \"./blocks/CountdownBlock.vue\";\n\nconst blockComponentMap: Record<string, Component> = {\n section: SectionBlock,\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n video: VideoBlock,\n countdown: CountdownBlockComponent,\n custom: CustomBlock,\n};\n\nconst props = defineProps<{\n viewport: ViewportSize;\n content: TemplateContent;\n selectedBlockId: string | null;\n darkMode: boolean;\n previewMode: boolean;\n lockedBlocks?: Map<string, Collaborator>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"select-block\", blockId: string | null): void;\n (e: \"open-ai-chat\"): void;\n (e: \"open-design-reference\"): void;\n}>();\n\nconst { t } = useI18n();\nconst { t: cloudT } = useCloudI18n();\n\nconst editor = requireInject(EDITOR_KEY, \"Canvas\");\nconst conditionPreview = inject(CONDITION_PREVIEW_KEY, null);\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY, null);\n\nconst caps = inject(CAPABILITIES_KEY, {});\n\nconst canUseAiChat = computed(\n () =>\n (caps.plan?.hasFeature(\"ai_generation\") ?? false) &&\n (caps.ai?.isFeatureEnabled(\"chat\") ?? false),\n);\nconst canUseDesignToTemplate = computed(\n () =>\n (caps.plan?.hasFeature(\"ai_generation\") ?? false) &&\n (caps.ai?.isFeatureEnabled(\"designToTemplate\") ?? false),\n);\n\nconst blocks = computed({\n get: () => props.content.blocks,\n set: (value: Block[]) => {\n editor.setContent({\n ...props.content,\n blocks: value,\n });\n },\n});\n\nconst viewportWidth = computed(() => {\n switch (props.viewport) {\n case \"mobile\":\n return 375;\n case \"tablet\":\n return 768;\n default:\n return props.content.settings.width;\n }\n});\n\n// Canvas dark mode preview: simulates how the email will appear in recipients'\n// dark-themed email clients. Uses CSS filter inversion — independent of the\n// editor UI theme (light/dark/auto) which is controlled via uiTheme config.\nconst canvasStyle = computed(() => ({\n backgroundColor: props.content.settings.backgroundColor,\n fontFamily: props.content.settings.fontFamily,\n}));\n\nfunction handleCanvasClick(event: MouseEvent): void {\n if (props.previewMode) {\n return;\n }\n if (event.target === event.currentTarget) {\n emit(\"select-block\", null);\n }\n}\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, blockComponentMap);\n}\n\nfunction getBlockLock(blockId: string): Collaborator | null {\n return props.lockedBlocks?.get(blockId) ?? null;\n}\n\nfunction handleFetchData(\n block: Block,\n payload: {\n fieldValues: Record<string, unknown>;\n dataSourceFetched: boolean;\n },\n): void {\n if (block.type !== \"custom\") {\n return;\n }\n\n editor.updateBlock(block.id, {\n fieldValues: payload.fieldValues,\n dataSourceFetched: payload.dataSourceFetched,\n } as Partial<CustomBlockType>);\n}\n</script>\n\n<template>\n <div\n data-testid=\"canvas-wrapper\"\n role=\"region\"\n :aria-label=\"t.landmarks.canvas\"\n class=\"tpl-canvas-wrapper tpl:rounded-lg\"\n :style=\"{\n width: `${viewportWidth}px`,\n backgroundColor: content.settings.backgroundColor,\n boxShadow: darkMode ? 'none' : 'var(--tpl-shadow-xl)',\n filter: darkMode ? 'invert(1) hue-rotate(180deg)' : 'none',\n transition:\n 'width 300ms cubic-bezier(0.34, 1.56, 0.64, 1), filter 300ms ease',\n }\"\n >\n <div\n class=\"tpl-canvas tpl:rounded-lg\"\n :class=\"{\n 'tpl-canvas--dark-mode': darkMode,\n 'tpl-preview-mode': previewMode,\n }\"\n :style=\"canvasStyle\"\n @click=\"handleCanvasClick\"\n >\n <draggable\n v-model=\"blocks\"\n group=\"blocks\"\n item-key=\"id\"\n :animation=\"150\"\n ghost-class=\"tpl-ghost\"\n drag-class=\"tpl-dragging\"\n handle=\".tpl-block-btn\"\n :invert-swap=\"true\"\n :inverted-swap-threshold=\"0.65\"\n :disabled=\"previewMode\"\n class=\"tpl-canvas-blocks\"\n >\n <template #item=\"{ element: block }\">\n <div v-show=\"!conditionPreview?.isHidden(block.id)\">\n <div class=\"tpl:relative\">\n <!-- Collaboration lock overlay -->\n <div\n v-if=\"getBlockLock(block.id)\"\n class=\"tpl-collab-lock tpl:pointer-events-none tpl:absolute tpl:inset-0 tpl:z-[4] tpl:rounded-sm\"\n :style=\"{\n outline: `2px solid ${getBlockLock(block.id)!.color}`,\n outlineOffset: '-1px',\n }\"\n >\n <span\n class=\"tpl:absolute tpl:-top-0.5 tpl:left-1/2 tpl:z-[5] tpl:flex tpl:-translate-x-1/2 tpl:-translate-y-full tpl:items-center tpl:gap-1 tpl:rounded-full tpl:px-2 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:whitespace-nowrap\"\n :style=\"{\n backgroundColor: getBlockLock(block.id)!.color,\n color: readableTextColor(getBlockLock(block.id)!.color),\n }\"\n >\n <span\n class=\"tpl:inline-flex tpl:size-3 tpl:items-center tpl:justify-center tpl:rounded-full tpl:text-[8px] tpl:font-bold\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-bg) 30%,\n transparent\n );\n \"\n >\n {{ getBlockLock(block.id)!.name.charAt(0) }}\n </span>\n {{ getBlockLock(block.id)!.name }}\n </span>\n </div>\n <BlockWrapper\n :block=\"block\"\n :is-selected=\"\n !previewMode &&\n selectedBlockId === block.id &&\n !getBlockLock(block.id)\n \"\n :viewport=\"viewport\"\n :preview-mode=\"previewMode\"\n @select=\"\n previewMode || getBlockLock(block.id)\n ? undefined\n : emit('select-block', block.id)\n \"\n >\n <component\n :is=\"getBlockComponent(block)\"\n :block=\"block\"\n :viewport=\"viewport\"\n @fetch-data=\"handleFetchData(block, $event)\"\n @update=\"\n (updates: Partial<Block>) =>\n editor.updateBlock(block.id, updates)\n \"\n />\n </BlockWrapper>\n </div>\n </div>\n </template>\n <template #footer>\n <div\n v-if=\"blocks.length === 0 && !previewMode\"\n class=\"tpl-canvas-empty tpl:m-6 tpl:flex tpl:min-h-[400px] tpl:flex-col tpl:items-center tpl:justify-center tpl:rounded-xl tpl:border-2 tpl:border-dashed tpl:px-10 tpl:py-12 tpl:text-center tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-bg-elevated)] tpl:font-[var(--tpl-font-family)]\"\n >\n <div\n class=\"tpl-canvas-empty-icon tpl:mb-4 tpl:text-[var(--tpl-primary)]\"\n >\n <SquarePlus :size=\"48\" :stroke-width=\"1\" />\n </div>\n <p\n class=\"tpl-canvas-empty-title tpl:m-0 tpl:mb-2 tpl:text-base tpl:font-semibold tpl:text-[var(--tpl-primary)]\"\n >\n {{ t.canvas.noBlocks }}\n </p>\n <p\n class=\"tpl-canvas-empty-text tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.canvas.dragHint }}\n </p>\n <p\n v-if=\"canUseAiChat && cloudT\"\n class=\"tpl:m-0 tpl:mt-2 tpl:flex tpl:flex-wrap tpl:items-center tpl:justify-center tpl:gap-x-1 tpl:gap-y-0.5 tpl:text-sm tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.canvas.aiHintChat }}\n <button\n class=\"tpl:inline-flex tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:gap-1 tpl:whitespace-nowrap tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-2 tpl:py-0.5 tpl:text-sm tpl:font-semibold tpl:transition-colors tpl:duration-150 tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary-hover)]\"\n @click=\"emit('open-ai-chat')\"\n >\n <Sparkles :size=\"14\" :stroke-width=\"2\" />\n {{ cloudT.aiMenu.aiAssistant }}\n </button>\n {{ t.canvas.aiHintChatSuffix }}\n </p>\n <p\n v-if=\"canUseDesignToTemplate && cloudT\"\n class=\"tpl:m-0 tpl:mt-4 tpl:flex tpl:flex-wrap tpl:items-center tpl:justify-center tpl:gap-x-1 tpl:gap-y-0.5 tpl:text-sm tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.canvas.aiHintDesign }}\n <button\n class=\"tpl:inline-flex tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:gap-1 tpl:whitespace-nowrap tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-2 tpl:py-0.5 tpl:text-sm tpl:font-semibold tpl:transition-colors tpl:duration-150 tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary-hover)]\"\n @click=\"emit('open-design-reference')\"\n >\n <ImageUp :size=\"14\" :stroke-width=\"2\" />\n {{ cloudT.aiMenu.designToTemplate }}\n </button>\n {{ t.canvas.aiHintDesignSuffix }}\n </p>\n </div>\n </template>\n </draggable>\n </div>\n </div>\n</template>\n\n<style scoped>\n/* Counter-invert images so they look normal in dark mode */\n.tpl-canvas--dark-mode :deep(img) {\n filter: invert(1) hue-rotate(180deg);\n}\n\n/* Counter-invert editor UI chrome so controls stay readable */\n.tpl-canvas--dark-mode :deep(.tpl-block-actions),\n.tpl-canvas--dark-mode :deep(.tpl-condition-toggle),\n.tpl-canvas--dark-mode :deep(.tpl-block-hidden-overlay) {\n filter: invert(1) hue-rotate(180deg);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useCloudI18n } from \"../composables/useCloudI18n\";\nimport type {\n Block,\n Collaborator,\n CustomBlock as CustomBlockType,\n TemplateContent,\n ViewportSize,\n} from \"@templatical/types\";\nimport { ImageUp, Sparkles, SquarePlus } from \"@lucide/vue\";\nimport { computed, inject, type Component } from \"vue\";\nimport {\n EDITOR_KEY,\n CONDITION_PREVIEW_KEY,\n BLOCK_REGISTRY_KEY,\n CAPABILITIES_KEY,\n requireInject,\n} from \"../keys\";\nimport draggable from \"vuedraggable\";\nimport { resolveBlockComponent } from \"../utils/blockComponentResolver\";\nimport { readableTextColor } from \"../utils/readableTextColor\";\n\nimport BlockWrapper from \"./blocks/BlockWrapper.vue\";\nimport SectionBlock from \"./blocks/SectionBlock.vue\";\nimport TitleBlock from \"./blocks/TitleBlock.vue\";\nimport ParagraphBlock from \"./blocks/ParagraphBlock.vue\";\nimport ImageBlock from \"./blocks/ImageBlock.vue\";\nimport ButtonBlock from \"./blocks/ButtonBlock.vue\";\nimport DividerBlock from \"./blocks/DividerBlock.vue\";\nimport SpacerBlock from \"./blocks/SpacerBlock.vue\";\nimport HtmlBlock from \"./blocks/HtmlBlock.vue\";\nimport SocialIconsBlock from \"./blocks/SocialIconsBlock.vue\";\nimport MenuBlock from \"./blocks/MenuBlock.vue\";\nimport TableBlock from \"./blocks/TableBlock.vue\";\nimport CustomBlock from \"./blocks/CustomBlock.vue\";\nimport VideoBlock from \"./blocks/VideoBlock.vue\";\nimport CountdownBlockComponent from \"./blocks/CountdownBlock.vue\";\n\nconst blockComponentMap: Record<string, Component> = {\n section: SectionBlock,\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n video: VideoBlock,\n countdown: CountdownBlockComponent,\n custom: CustomBlock,\n};\n\nconst props = defineProps<{\n viewport: ViewportSize;\n content: TemplateContent;\n selectedBlockId: string | null;\n darkMode: boolean;\n previewMode: boolean;\n lockedBlocks?: Map<string, Collaborator>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"select-block\", blockId: string | null): void;\n (e: \"open-ai-chat\"): void;\n (e: \"open-design-reference\"): void;\n}>();\n\nconst { t } = useI18n();\nconst { t: cloudT } = useCloudI18n();\n\nconst editor = requireInject(EDITOR_KEY, \"Canvas\");\nconst conditionPreview = inject(CONDITION_PREVIEW_KEY, null);\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY, null);\n\nconst caps = inject(CAPABILITIES_KEY, {});\n\nconst canUseAiChat = computed(\n () =>\n (caps.plan?.hasFeature(\"ai_generation\") ?? false) &&\n (caps.ai?.isFeatureEnabled(\"chat\") ?? false),\n);\nconst canUseDesignToTemplate = computed(\n () =>\n (caps.plan?.hasFeature(\"ai_generation\") ?? false) &&\n (caps.ai?.isFeatureEnabled(\"designToTemplate\") ?? false),\n);\n\nconst blocks = computed({\n get: () => props.content.blocks,\n set: (value: Block[]) => {\n editor.setContent({\n ...props.content,\n blocks: value,\n });\n },\n});\n\nconst viewportWidth = computed(() => {\n switch (props.viewport) {\n case \"mobile\":\n return 375;\n case \"tablet\":\n return 768;\n default:\n return props.content.settings.width;\n }\n});\n\n// Canvas dark mode preview: simulates how the email will appear in recipients'\n// dark-themed email clients. Uses CSS filter inversion — independent of the\n// editor UI theme (light/dark/auto) which is controlled via uiTheme config.\nconst canvasStyle = computed(() => ({\n backgroundColor: props.content.settings.backgroundColor,\n fontFamily: props.content.settings.fontFamily,\n}));\n\nfunction handleCanvasClick(event: MouseEvent): void {\n if (props.previewMode) {\n return;\n }\n if (event.target === event.currentTarget) {\n emit(\"select-block\", null);\n }\n}\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, blockComponentMap);\n}\n\nfunction getBlockLock(blockId: string): Collaborator | null {\n return props.lockedBlocks?.get(blockId) ?? null;\n}\n\nfunction handleFetchData(\n block: Block,\n payload: {\n fieldValues: Record<string, unknown>;\n dataSourceFetched: boolean;\n },\n): void {\n if (block.type !== \"custom\") {\n return;\n }\n\n editor.updateBlock(block.id, {\n fieldValues: payload.fieldValues,\n dataSourceFetched: payload.dataSourceFetched,\n } as Partial<CustomBlockType>);\n}\n</script>\n\n<template>\n <div\n data-testid=\"canvas-wrapper\"\n role=\"region\"\n :aria-label=\"t.landmarks.canvas\"\n class=\"tpl-canvas-wrapper tpl:rounded-lg\"\n :style=\"{\n width: `${viewportWidth}px`,\n backgroundColor: content.settings.backgroundColor,\n boxShadow: darkMode ? 'none' : 'var(--tpl-shadow-xl)',\n filter: darkMode ? 'invert(1) hue-rotate(180deg)' : 'none',\n transition:\n 'width 300ms cubic-bezier(0.34, 1.56, 0.64, 1), filter 300ms ease',\n }\"\n >\n <div\n class=\"tpl-canvas tpl:rounded-lg\"\n :class=\"{\n 'tpl-canvas--dark-mode': darkMode,\n 'tpl-preview-mode': previewMode,\n }\"\n :style=\"canvasStyle\"\n @click=\"handleCanvasClick\"\n >\n <draggable\n v-model=\"blocks\"\n group=\"blocks\"\n item-key=\"id\"\n :animation=\"150\"\n ghost-class=\"tpl-ghost\"\n drag-class=\"tpl-dragging\"\n handle=\".tpl-block-btn\"\n :invert-swap=\"true\"\n :inverted-swap-threshold=\"0.65\"\n :disabled=\"previewMode\"\n class=\"tpl-canvas-blocks\"\n >\n <template #item=\"{ element: block }\">\n <div v-show=\"!conditionPreview?.isHidden(block.id)\">\n <div class=\"tpl:relative\">\n <!-- Collaboration lock overlay -->\n <div\n v-if=\"getBlockLock(block.id)\"\n class=\"tpl-collab-lock tpl:pointer-events-none tpl:absolute tpl:inset-0 tpl:z-[4] tpl:rounded-sm\"\n :style=\"{\n outline: `2px solid ${getBlockLock(block.id)!.color}`,\n outlineOffset: '-1px',\n }\"\n >\n <span\n class=\"tpl:absolute tpl:-top-0.5 tpl:left-1/2 tpl:z-[5] tpl:flex tpl:-translate-x-1/2 tpl:-translate-y-full tpl:items-center tpl:gap-1 tpl:rounded-full tpl:px-2 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:whitespace-nowrap\"\n :style=\"{\n backgroundColor: getBlockLock(block.id)!.color,\n color: readableTextColor(getBlockLock(block.id)!.color),\n }\"\n >\n <span\n class=\"tpl:inline-flex tpl:size-3 tpl:items-center tpl:justify-center tpl:rounded-full tpl:text-[8px] tpl:font-bold\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-bg) 30%,\n transparent\n );\n \"\n >\n {{ getBlockLock(block.id)!.name.charAt(0) }}\n </span>\n {{ getBlockLock(block.id)!.name }}\n </span>\n </div>\n <BlockWrapper\n :block=\"block\"\n :is-selected=\"\n !previewMode &&\n selectedBlockId === block.id &&\n !getBlockLock(block.id)\n \"\n :viewport=\"viewport\"\n :preview-mode=\"previewMode\"\n @select=\"\n previewMode || getBlockLock(block.id)\n ? undefined\n : emit('select-block', block.id)\n \"\n >\n <component\n :is=\"getBlockComponent(block)\"\n :block=\"block\"\n :viewport=\"viewport\"\n @fetch-data=\"handleFetchData(block, $event)\"\n @update=\"\n (updates: Partial<Block>) =>\n editor.updateBlock(block.id, updates)\n \"\n />\n </BlockWrapper>\n </div>\n </div>\n </template>\n <template #footer>\n <div\n v-if=\"blocks.length === 0 && !previewMode\"\n class=\"tpl-canvas-empty tpl:m-6 tpl:flex tpl:min-h-[400px] tpl:flex-col tpl:items-center tpl:justify-center tpl:rounded-xl tpl:border-2 tpl:border-dashed tpl:px-10 tpl:py-12 tpl:text-center tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-bg-elevated)] tpl:font-[var(--tpl-font-family)]\"\n >\n <div\n class=\"tpl-canvas-empty-icon tpl:mb-4 tpl:text-[var(--tpl-primary)]\"\n >\n <SquarePlus :size=\"48\" :stroke-width=\"1\" />\n </div>\n <p\n class=\"tpl-canvas-empty-title tpl:m-0 tpl:mb-2 tpl:text-base tpl:font-semibold tpl:text-[var(--tpl-primary)]\"\n >\n {{ t.canvas.noBlocks }}\n </p>\n <p\n class=\"tpl-canvas-empty-text tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.canvas.dragHint }}\n </p>\n <p\n v-if=\"canUseAiChat && cloudT\"\n class=\"tpl:m-0 tpl:mt-2 tpl:flex tpl:flex-wrap tpl:items-center tpl:justify-center tpl:gap-x-1 tpl:gap-y-0.5 tpl:text-sm tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.canvas.aiHintChat }}\n <button\n class=\"tpl:inline-flex tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:gap-1 tpl:whitespace-nowrap tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-2 tpl:py-0.5 tpl:text-sm tpl:font-semibold tpl:transition-colors tpl:duration-150 tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary-hover)]\"\n @click=\"emit('open-ai-chat')\"\n >\n <Sparkles :size=\"14\" :stroke-width=\"2\" />\n {{ cloudT.aiMenu.aiAssistant }}\n </button>\n {{ t.canvas.aiHintChatSuffix }}\n </p>\n <p\n v-if=\"canUseDesignToTemplate && cloudT\"\n class=\"tpl:m-0 tpl:mt-4 tpl:flex tpl:flex-wrap tpl:items-center tpl:justify-center tpl:gap-x-1 tpl:gap-y-0.5 tpl:text-sm tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.canvas.aiHintDesign }}\n <button\n class=\"tpl:inline-flex tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:gap-1 tpl:whitespace-nowrap tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-2 tpl:py-0.5 tpl:text-sm tpl:font-semibold tpl:transition-colors tpl:duration-150 tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary-hover)]\"\n @click=\"emit('open-design-reference')\"\n >\n <ImageUp :size=\"14\" :stroke-width=\"2\" />\n {{ cloudT.aiMenu.designToTemplate }}\n </button>\n {{ t.canvas.aiHintDesignSuffix }}\n </p>\n </div>\n </template>\n </draggable>\n </div>\n </div>\n</template>\n\n<style scoped>\n/* Counter-invert images so they look normal in dark mode */\n.tpl-canvas--dark-mode :deep(img) {\n filter: invert(1) hue-rotate(180deg);\n}\n\n/* Counter-invert editor UI chrome so controls stay readable */\n.tpl-canvas--dark-mode :deep(.tpl-block-actions),\n.tpl-canvas--dark-mode :deep(.tpl-condition-toggle),\n.tpl-canvas--dark-mode :deep(.tpl-block-hidden-overlay) {\n filter: invert(1) hue-rotate(180deg);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useCloudI18n } from \"../composables/useCloudI18n\";\nimport type { Block, BlockType } from \"@templatical/types\";\nimport { createBlock, createCustomBlock } from \"@templatical/types\";\nimport { Package } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport draggable from \"vuedraggable\";\nimport CustomBlockIcon from \"./CustomBlockIcon.vue\";\nimport { blockTypeIcons } from \"../utils/blockTypeIcons\";\nimport { getBlockTypeLabel } from \"../utils/blockTypeLabels\";\nimport {\n CUSTOM_BLOCK_DEFINITIONS_KEY,\n BLOCK_DEFAULTS_KEY,\n CAPABILITIES_KEY,\n EDITOR_KEY,\n} from \"../keys\";\n\ninterface BlockTypeItem {\n type: BlockType | string;\n label: string;\n isCustom?: boolean;\n icon?: string;\n}\n\nconst { t, format } = useI18n();\nconst { t: cloudT } = useCloudI18n();\nconst customBlockDefinitions = inject(CUSTOM_BLOCK_DEFINITIONS_KEY, []);\nconst blockDefaults = inject(BLOCK_DEFAULTS_KEY, undefined);\nconst editor = inject(EDITOR_KEY, null);\n\nconst caps = inject(CAPABILITIES_KEY, {});\n\nconst showModulesSection = computed(\n () => (caps.savedModules?.moduleCount.value ?? 0) > 0,\n);\n\nconst isExpanded = ref(false);\n\nconst builtInBlockTypeOrder: string[] = [\n \"section\",\n \"image\",\n \"title\",\n \"paragraph\",\n \"button\",\n \"divider\",\n \"video\",\n \"social\",\n \"menu\",\n \"table\",\n \"spacer\",\n \"html\",\n];\n\nconst builtInBlockTypes = computed<BlockTypeItem[]>(() => {\n const types: BlockTypeItem[] = builtInBlockTypeOrder.map((type) => ({\n type,\n label: getBlockTypeLabel(type, t),\n }));\n\n // Countdown requires Templatical Cloud for server-side GIF rendering\n if (caps.plan) {\n types.splice(-1, 0, {\n type: \"countdown\",\n label: getBlockTypeLabel(\"countdown\", t),\n });\n }\n\n return types;\n});\n\nconst customBlockItems = computed<BlockTypeItem[]>(() => {\n return customBlockDefinitions.map((def) => ({\n type: `custom:${def.type}`,\n label: def.name,\n isCustom: true,\n icon: def.icon,\n }));\n});\n\nconst blockTypes = computed<BlockTypeItem[]>(() => [\n ...builtInBlockTypes.value,\n ...customBlockItems.value,\n]);\n\nfunction createBlockFromItem(item: BlockTypeItem): Block {\n if (item.isCustom) {\n const customType = item.type.replace(\"custom:\", \"\");\n const definition = customBlockDefinitions.find(\n (d) => d.type === customType,\n );\n if (definition) {\n return createCustomBlock(definition);\n }\n }\n\n return createBlock(item.type as BlockType, blockDefaults);\n}\n\nfunction insertBlockFromItem(item: BlockTypeItem): void {\n if (!editor) return;\n const block = createBlockFromItem(item);\n editor.addBlock(block);\n editor.selectBlock(block.id);\n}\n\nfunction handlePaletteKeydown(event: KeyboardEvent, item: BlockTypeItem): void {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n insertBlockFromItem(item);\n }\n}\n</script>\n\n<template>\n <aside\n :aria-label=\"t.sidebarNav.palette\"\n class=\"tpl-sidebar-rail tpl:absolute tpl:top-14 tpl:bottom-0 tpl:left-0 tpl:z-40 tpl:overflow-hidden\"\n :style=\"{\n width: isExpanded ? '200px' : '48px',\n backgroundColor: 'var(--tpl-bg-elevated)',\n borderRight: '1px solid var(--tpl-border)',\n boxShadow: isExpanded ? 'var(--tpl-shadow-lg)' : 'none',\n transition:\n 'width 200ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 200ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n @mouseenter=\"isExpanded = true\"\n @mouseleave=\"isExpanded = false\"\n @focusin=\"isExpanded = true\"\n @focusout=\"isExpanded = false\"\n >\n <!-- Saved Modules browser trigger (cloud only) -->\n <div\n v-if=\"showModulesSection && cloudT\"\n class=\"tpl:border-b tpl:px-1 tpl:pb-1 tpl:border-[var(--tpl-border)]\"\n >\n <button\n type=\"button\"\n :aria-label=\"t.sidebarNav.browseModules\"\n class=\"tpl:flex tpl:h-10 tpl:w-full tpl:cursor-pointer tpl:items-center tpl:gap-3 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:bg-transparent tpl:px-3 tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] hover:tpl:bg-[var(--tpl-primary-light)] hover:tpl:text-[var(--tpl-primary)]\"\n :style=\"{\n justifyContent: isExpanded ? 'flex-start' : 'center',\n }\"\n @click=\"caps.savedModules?.openBrowser()\"\n >\n <Package :size=\"20\" :stroke-width=\"1.5\" class=\"tpl:shrink-0\" />\n <span\n v-if=\"isExpanded\"\n class=\"tpl:flex-1 tpl:truncate tpl:text-sm tpl:font-medium\"\n >\n {{ cloudT.modules.title }}\n </span>\n <span\n v-if=\"isExpanded\"\n class=\"tpl:shrink-0 tpl:rounded-full tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ caps.savedModules?.moduleCount.value ?? 0 }}\n </span>\n </button>\n </div>\n <draggable\n :list=\"blockTypes\"\n :group=\"{ name: 'blocks', pull: 'clone', put: false }\"\n :clone=\"createBlockFromItem\"\n :sort=\"false\"\n item-key=\"type\"\n :animation=\"150\"\n ghost-class=\"tpl-ghost\"\n class=\"tpl:flex tpl:flex-col tpl:gap-0.5 tpl:p-1\"\n >\n <template #item=\"{ element: blockType }\">\n <button\n type=\"button\"\n :data-palette-type=\"blockType.type\"\n :aria-label=\"\n format(t.sidebarNav.insertBlock, { block: blockType.label })\n \"\n class=\"tpl:flex tpl:h-10 tpl:w-full tpl:cursor-grab tpl:items-center tpl:gap-3 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:bg-transparent tpl:px-3 tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] hover:tpl:bg-[var(--tpl-primary-light)] hover:tpl:text-[var(--tpl-primary)] active:tpl:cursor-grabbing\"\n :style=\"{\n justifyContent: isExpanded ? 'flex-start' : 'center',\n }\"\n @click=\"insertBlockFromItem(blockType)\"\n @keydown=\"handlePaletteKeydown($event, blockType)\"\n >\n <div\n class=\"tpl:flex tpl:shrink-0 tpl:items-center tpl:justify-center tpl:transition-transform tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] hover:tpl:scale-105\"\n >\n <component\n :is=\"blockTypeIcons[blockType.type]\"\n v-if=\"blockTypeIcons[blockType.type]\"\n :size=\"20\"\n :stroke-width=\"1.5\"\n />\n <CustomBlockIcon\n v-else-if=\"blockType.isCustom\"\n :icon=\"blockType.icon\"\n :size=\"20\"\n />\n </div>\n <span\n v-if=\"isExpanded\"\n class=\"tpl:truncate tpl:text-sm tpl:font-medium\"\n >\n {{ blockType.label }}\n </span>\n </button>\n </template>\n </draggable>\n </aside>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { useCloudI18n } from \"../composables/useCloudI18n\";\nimport type { Block, BlockType } from \"@templatical/types\";\nimport { createBlock, createCustomBlock } from \"@templatical/types\";\nimport { Package } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport draggable from \"vuedraggable\";\nimport CustomBlockIcon from \"./CustomBlockIcon.vue\";\nimport { blockTypeIcons } from \"../utils/blockTypeIcons\";\nimport { getBlockTypeLabel } from \"../utils/blockTypeLabels\";\nimport {\n CUSTOM_BLOCK_DEFINITIONS_KEY,\n BLOCK_DEFAULTS_KEY,\n CAPABILITIES_KEY,\n EDITOR_KEY,\n} from \"../keys\";\n\ninterface BlockTypeItem {\n type: BlockType | string;\n label: string;\n isCustom?: boolean;\n icon?: string;\n}\n\nconst { t, format } = useI18n();\nconst { t: cloudT } = useCloudI18n();\nconst customBlockDefinitions = inject(CUSTOM_BLOCK_DEFINITIONS_KEY, []);\nconst blockDefaults = inject(BLOCK_DEFAULTS_KEY, undefined);\nconst editor = inject(EDITOR_KEY, null);\n\nconst caps = inject(CAPABILITIES_KEY, {});\n\nconst showModulesSection = computed(\n () => (caps.savedModules?.moduleCount.value ?? 0) > 0,\n);\n\nconst isExpanded = ref(false);\n\nconst builtInBlockTypeOrder: string[] = [\n \"section\",\n \"image\",\n \"title\",\n \"paragraph\",\n \"button\",\n \"divider\",\n \"video\",\n \"social\",\n \"menu\",\n \"table\",\n \"spacer\",\n \"html\",\n];\n\nconst builtInBlockTypes = computed<BlockTypeItem[]>(() => {\n const types: BlockTypeItem[] = builtInBlockTypeOrder.map((type) => ({\n type,\n label: getBlockTypeLabel(type, t),\n }));\n\n // Countdown requires Templatical Cloud for server-side GIF rendering\n if (caps.plan) {\n types.splice(-1, 0, {\n type: \"countdown\",\n label: getBlockTypeLabel(\"countdown\", t),\n });\n }\n\n return types;\n});\n\nconst customBlockItems = computed<BlockTypeItem[]>(() => {\n return customBlockDefinitions.map((def) => ({\n type: `custom:${def.type}`,\n label: def.name,\n isCustom: true,\n icon: def.icon,\n }));\n});\n\nconst blockTypes = computed<BlockTypeItem[]>(() => [\n ...builtInBlockTypes.value,\n ...customBlockItems.value,\n]);\n\nfunction createBlockFromItem(item: BlockTypeItem): Block {\n if (item.isCustom) {\n const customType = item.type.replace(\"custom:\", \"\");\n const definition = customBlockDefinitions.find(\n (d) => d.type === customType,\n );\n if (definition) {\n return createCustomBlock(definition);\n }\n }\n\n return createBlock(item.type as BlockType, blockDefaults);\n}\n\nfunction insertBlockFromItem(item: BlockTypeItem): void {\n if (!editor) return;\n const block = createBlockFromItem(item);\n editor.addBlock(block);\n editor.selectBlock(block.id);\n}\n\nfunction handlePaletteKeydown(event: KeyboardEvent, item: BlockTypeItem): void {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n insertBlockFromItem(item);\n }\n}\n</script>\n\n<template>\n <aside\n :aria-label=\"t.sidebarNav.palette\"\n class=\"tpl-sidebar-rail tpl:absolute tpl:top-14 tpl:bottom-0 tpl:left-0 tpl:z-40 tpl:overflow-hidden\"\n :style=\"{\n width: isExpanded ? '200px' : '48px',\n backgroundColor: 'var(--tpl-bg-elevated)',\n borderRight: '1px solid var(--tpl-border)',\n boxShadow: isExpanded ? 'var(--tpl-shadow-lg)' : 'none',\n transition:\n 'width 200ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 200ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n @mouseenter=\"isExpanded = true\"\n @mouseleave=\"isExpanded = false\"\n @focusin=\"isExpanded = true\"\n @focusout=\"isExpanded = false\"\n >\n <!-- Saved Modules browser trigger (cloud only) -->\n <div\n v-if=\"showModulesSection && cloudT\"\n class=\"tpl:border-b tpl:px-1 tpl:pb-1 tpl:border-[var(--tpl-border)]\"\n >\n <button\n type=\"button\"\n :aria-label=\"t.sidebarNav.browseModules\"\n class=\"tpl:flex tpl:h-10 tpl:w-full tpl:cursor-pointer tpl:items-center tpl:gap-3 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:bg-transparent tpl:px-3 tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] hover:tpl:bg-[var(--tpl-primary-light)] hover:tpl:text-[var(--tpl-primary)]\"\n :style=\"{\n justifyContent: isExpanded ? 'flex-start' : 'center',\n }\"\n @click=\"caps.savedModules?.openBrowser()\"\n >\n <Package :size=\"20\" :stroke-width=\"1.5\" class=\"tpl:shrink-0\" />\n <span\n v-if=\"isExpanded\"\n class=\"tpl:flex-1 tpl:truncate tpl:text-sm tpl:font-medium\"\n >\n {{ cloudT.modules.title }}\n </span>\n <span\n v-if=\"isExpanded\"\n class=\"tpl:shrink-0 tpl:rounded-full tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ caps.savedModules?.moduleCount.value ?? 0 }}\n </span>\n </button>\n </div>\n <draggable\n :list=\"blockTypes\"\n :group=\"{ name: 'blocks', pull: 'clone', put: false }\"\n :clone=\"createBlockFromItem\"\n :sort=\"false\"\n item-key=\"type\"\n :animation=\"150\"\n ghost-class=\"tpl-ghost\"\n class=\"tpl:flex tpl:flex-col tpl:gap-0.5 tpl:p-1\"\n >\n <template #item=\"{ element: blockType }\">\n <button\n type=\"button\"\n :data-palette-type=\"blockType.type\"\n :aria-label=\"\n format(t.sidebarNav.insertBlock, { block: blockType.label })\n \"\n class=\"tpl:flex tpl:h-10 tpl:w-full tpl:cursor-grab tpl:items-center tpl:gap-3 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:bg-transparent tpl:px-3 tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] hover:tpl:bg-[var(--tpl-primary-light)] hover:tpl:text-[var(--tpl-primary)] active:tpl:cursor-grabbing\"\n :style=\"{\n justifyContent: isExpanded ? 'flex-start' : 'center',\n }\"\n @click=\"insertBlockFromItem(blockType)\"\n @keydown=\"handlePaletteKeydown($event, blockType)\"\n >\n <div\n class=\"tpl:flex tpl:shrink-0 tpl:items-center tpl:justify-center tpl:transition-transform tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] hover:tpl:scale-105\"\n >\n <component\n :is=\"blockTypeIcons[blockType.type]\"\n v-if=\"blockTypeIcons[blockType.type]\"\n :size=\"20\"\n :stroke-width=\"1.5\"\n />\n <CustomBlockIcon\n v-else-if=\"blockType.isCustom\"\n :icon=\"blockType.icon\"\n :size=\"20\"\n />\n </div>\n <span\n v-if=\"isExpanded\"\n class=\"tpl:truncate tpl:text-sm tpl:font-medium\"\n >\n {{ blockType.label }}\n </span>\n </button>\n </template>\n </draggable>\n </aside>\n</template>\n","import {\n getLogicMergeTagKeyword,\n isLogicMergeTagValue,\n} from \"@templatical/types\";\nimport { computed, nextTick, ref, type ComputedRef, type Ref } from \"vue\";\nimport { useMergeTag } from \"./useMergeTag\";\n\nexport type MergeTagSegment =\n | { type: \"text\"; value: string }\n | { type: \"mergeTag\"; value: string; label: string }\n | { type: \"logicMergeTag\"; value: string; keyword: string };\n\nexport interface UseMergeTagFieldOptions {\n modelValue: () => string;\n emit: (value: string) => void;\n elementRef: Ref<HTMLInputElement | HTMLTextAreaElement | null>;\n}\n\nexport interface UseMergeTagFieldReturn {\n segments: ComputedRef<MergeTagSegment[]>;\n hasMergeTags: ComputedRef<boolean>;\n canRequestMergeTag: boolean;\n isRequestingMergeTag: Ref<boolean>;\n isEditing: Ref<boolean>;\n startEditing: () => void;\n stopEditing: () => void;\n handleInput: (event: Event) => void;\n clearValue: () => void;\n insertMergeTag: () => Promise<void>;\n}\n\nexport function useMergeTagField(\n options: UseMergeTagFieldOptions,\n): UseMergeTagFieldReturn {\n const { modelValue, emit, elementRef } = options;\n\n const {\n canRequestMergeTag,\n isRequesting: isRequestingMergeTag,\n isMergeTagValue,\n getMergeTagLabel,\n requestMergeTag,\n syntax,\n } = useMergeTag();\n\n const isEditing = ref(false);\n let insertingMergeTag = false;\n\n const segments = computed((): MergeTagSegment[] => {\n const val = modelValue();\n if (!val) return [];\n\n const result: MergeTagSegment[] = [];\n const combinedSource = `(${syntax.value.source}|${syntax.logic.source})`;\n const regex = new RegExp(combinedSource, \"g\");\n let lastIndex = 0;\n let match;\n\n while ((match = regex.exec(val)) !== null) {\n if (match.index > lastIndex) {\n result.push({\n type: \"text\",\n value: val.slice(lastIndex, match.index),\n });\n }\n\n const matched = match[0];\n if (isMergeTagValue(matched)) {\n result.push({\n type: \"mergeTag\",\n value: matched,\n label: getMergeTagLabel(matched),\n });\n } else if (isLogicMergeTagValue(matched, syntax)) {\n result.push({\n type: \"logicMergeTag\",\n value: matched,\n keyword: getLogicMergeTagKeyword(matched, syntax),\n });\n } else {\n result.push({ type: \"text\", value: matched });\n }\n\n lastIndex = match.index + matched.length;\n }\n\n if (lastIndex < val.length) {\n result.push({ type: \"text\", value: val.slice(lastIndex) });\n }\n\n return result;\n });\n\n const hasMergeTags = computed(() =>\n segments.value.some(\n (s) => s.type === \"mergeTag\" || s.type === \"logicMergeTag\",\n ),\n );\n\n function startEditing(): void {\n isEditing.value = true;\n nextTick(() => {\n elementRef.value?.focus();\n const len = modelValue()?.length || 0;\n elementRef.value?.setSelectionRange(len, len);\n });\n }\n\n function stopEditing(): void {\n if (insertingMergeTag) return;\n isEditing.value = false;\n }\n\n function handleInput(event: Event): void {\n emit((event.target as HTMLInputElement | HTMLTextAreaElement).value);\n }\n\n function clearValue(): void {\n emit(\"\");\n }\n\n async function insertMergeTag(): Promise<void> {\n const cursorPos =\n isEditing.value && elementRef.value\n ? (elementRef.value.selectionStart ?? modelValue().length)\n : modelValue().length;\n\n insertingMergeTag = true;\n let mergeTag: Awaited<ReturnType<typeof requestMergeTag>>;\n try {\n mergeTag = await requestMergeTag();\n } finally {\n insertingMergeTag = false;\n }\n\n if (mergeTag) {\n const before = modelValue().slice(0, cursorPos);\n const after = modelValue().slice(cursorPos);\n const newValue = before + mergeTag.value + after;\n emit(newValue);\n\n isEditing.value = true;\n nextTick(() => {\n const newPos = cursorPos + mergeTag.value.length;\n elementRef.value?.focus();\n elementRef.value?.setSelectionRange(newPos, newPos);\n });\n }\n }\n\n return {\n segments,\n hasMergeTags,\n canRequestMergeTag,\n isRequestingMergeTag,\n isEditing,\n startEditing,\n stopEditing,\n handleInput,\n clearValue,\n insertMergeTag,\n };\n}\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { MergeTagSegment } from \"../composables/useMergeTagField\";\nimport { X } from \"@lucide/vue\";\n\ndefineProps<{\n segments: MergeTagSegment[];\n displayClass: string;\n pulse?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"edit\"): void;\n (e: \"clear\"): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction onEdit(): void {\n emit(\"edit\");\n}\n</script>\n\n<template>\n <div\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.clickToEdit\"\n :class=\"[displayClass, { 'tpl-pulse-fill': pulse }]\"\n @click=\"onEdit\"\n @keydown.enter=\"onEdit\"\n @keydown.space.prevent=\"onEdit\"\n >\n <template\n v-for=\"(seg, i) in segments\"\n :key=\"`${seg.type}-${i}-${seg.value}`\"\n >\n <span\n v-if=\"seg.type === 'mergeTag'\"\n class=\"tpl-tooltip tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium\"\n :data-tooltip=\"seg.value\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-primary) 20%,\n transparent\n );\n color: var(--tpl-primary);\n \"\n >\n {{ seg.label }}\n </span>\n <span\n v-else-if=\"seg.type === 'logicMergeTag'\"\n class=\"tpl-tooltip tpl:inline-flex tpl:items-center tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.8em] tpl:font-bold tpl:tracking-wide tpl:uppercase\"\n :data-tooltip=\"seg.value\"\n style=\"\n background-color: transparent;\n border: 1.5px solid\n color-mix(in srgb, var(--tpl-primary) 50%, transparent);\n color: var(--tpl-primary);\n \"\n >\n {{ seg.keyword }}\n </span>\n <span v-else class=\"tpl:text-sm tpl:text-[var(--tpl-text)]\">{{\n seg.value\n }}</span>\n </template>\n <button\n type=\"button\"\n class=\"tpl:ml-auto tpl:flex tpl:size-6 tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-[var(--tpl-text-dim)] tpl:opacity-60 tpl:transition-all hover:tpl:text-[var(--tpl-danger)] hover:tpl:opacity-100\"\n :aria-label=\"t.mergeTag.remove\"\n :title=\"t.mergeTag.remove\"\n @click.stop=\"emit('clear')\"\n >\n <X :size=\"12\" :stroke-width=\"2.5\" />\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { MergeTagSegment } from \"../composables/useMergeTagField\";\nimport { X } from \"@lucide/vue\";\n\ndefineProps<{\n segments: MergeTagSegment[];\n displayClass: string;\n pulse?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"edit\"): void;\n (e: \"clear\"): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction onEdit(): void {\n emit(\"edit\");\n}\n</script>\n\n<template>\n <div\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.clickToEdit\"\n :class=\"[displayClass, { 'tpl-pulse-fill': pulse }]\"\n @click=\"onEdit\"\n @keydown.enter=\"onEdit\"\n @keydown.space.prevent=\"onEdit\"\n >\n <template\n v-for=\"(seg, i) in segments\"\n :key=\"`${seg.type}-${i}-${seg.value}`\"\n >\n <span\n v-if=\"seg.type === 'mergeTag'\"\n class=\"tpl-tooltip tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium\"\n :data-tooltip=\"seg.value\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-primary) 20%,\n transparent\n );\n color: var(--tpl-primary);\n \"\n >\n {{ seg.label }}\n </span>\n <span\n v-else-if=\"seg.type === 'logicMergeTag'\"\n class=\"tpl-tooltip tpl:inline-flex tpl:items-center tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.8em] tpl:font-bold tpl:tracking-wide tpl:uppercase\"\n :data-tooltip=\"seg.value\"\n style=\"\n background-color: transparent;\n border: 1.5px solid\n color-mix(in srgb, var(--tpl-primary) 50%, transparent);\n color: var(--tpl-primary);\n \"\n >\n {{ seg.keyword }}\n </span>\n <span v-else class=\"tpl:text-sm tpl:text-[var(--tpl-text)]\">{{\n seg.value\n }}</span>\n </template>\n <button\n type=\"button\"\n class=\"tpl:ml-auto tpl:flex tpl:size-6 tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-[var(--tpl-text-dim)] tpl:opacity-60 tpl:transition-all hover:tpl:text-[var(--tpl-danger)] hover:tpl:opacity-100\"\n :aria-label=\"t.mergeTag.remove\"\n :title=\"t.mergeTag.remove\"\n @click.stop=\"emit('clear')\"\n >\n <X :size=\"12\" :stroke-width=\"2.5\" />\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { ScanLine } from \"@lucide/vue\";\n\ndefineProps<{\n disabled?: boolean;\n}>();\n\ndefineEmits<{\n (e: \"insert\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst mergeTagBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] hover:tpl:bg-[var(--tpl-primary-light)] hover:tpl:text-[var(--tpl-primary)] hover:tpl:border-[var(--tpl-primary)]\";\n</script>\n\n<template>\n <button\n type=\"button\"\n :class=\"[mergeTagBtnClass, 'tpl:mt-1.5']\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n :disabled=\"disabled\"\n @click=\"$emit('insert')\"\n >\n <ScanLine :size=\"12\" :stroke-width=\"2\" />\n {{ t.mergeTag.insert }}\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { ScanLine } from \"@lucide/vue\";\n\ndefineProps<{\n disabled?: boolean;\n}>();\n\ndefineEmits<{\n (e: \"insert\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst mergeTagBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] hover:tpl:bg-[var(--tpl-primary-light)] hover:tpl:text-[var(--tpl-primary)] hover:tpl:border-[var(--tpl-primary)]\";\n</script>\n\n<template>\n <button\n type=\"button\"\n :class=\"[mergeTagBtnClass, 'tpl:mt-1.5']\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n :disabled=\"disabled\"\n @click=\"$emit('insert')\"\n >\n <ScanLine :size=\"12\" :stroke-width=\"2\" />\n {{ t.mergeTag.insert }}\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { useMergeTagField } from \"../composables/useMergeTagField\";\nimport MergeTagSegments from \"./MergeTagSegments.vue\";\nimport MergeTagInsertButton from \"./MergeTagInsertButton.vue\";\nimport { ref } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string;\n placeholder?: string;\n rows?: number;\n }>(),\n {\n placeholder: \"\",\n rows: 3,\n },\n);\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst textareaRef = ref<HTMLTextAreaElement | null>(null);\n\nconst {\n segments,\n hasMergeTags,\n canRequestMergeTag,\n isRequestingMergeTag,\n isEditing,\n startEditing,\n stopEditing,\n handleInput,\n clearValue,\n insertMergeTag,\n} = useMergeTagField({\n modelValue: () => props.modelValue,\n emit: (value) => emit(\"update:modelValue\", value),\n elementRef: textareaRef,\n});\n\nconst textareaClass =\n \"tpl:w-full tpl:resize-y tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:shadow-xs tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] tpl:placeholder:text-[var(--tpl-text-dim)] tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)]\";\nconst displayClass =\n \"tpl:flex tpl:w-full tpl:min-h-[5rem] tpl:cursor-pointer tpl:items-start tpl:flex-wrap tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:shadow-xs tpl:bg-[var(--tpl-bg)] tpl:border-[var(--tpl-border)] tpl:px-3 tpl:py-2 tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\";\n</script>\n\n<template>\n <div v-if=\"hasMergeTags && !isEditing\">\n <MergeTagSegments\n :segments=\"segments\"\n :display-class=\"displayClass\"\n @edit=\"startEditing\"\n @clear=\"clearValue\"\n />\n <MergeTagInsertButton\n v-if=\"canRequestMergeTag\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n </div>\n <div v-else>\n <textarea\n ref=\"textareaRef\"\n :class=\"textareaClass\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n :rows=\"rows\"\n @input=\"handleInput\"\n @blur=\"stopEditing\"\n @keydown.escape=\"stopEditing\"\n />\n <MergeTagInsertButton\n v-if=\"canRequestMergeTag\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useMergeTagField } from \"../composables/useMergeTagField\";\nimport MergeTagSegments from \"./MergeTagSegments.vue\";\nimport MergeTagInsertButton from \"./MergeTagInsertButton.vue\";\nimport { ref } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string;\n placeholder?: string;\n rows?: number;\n }>(),\n {\n placeholder: \"\",\n rows: 3,\n },\n);\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst textareaRef = ref<HTMLTextAreaElement | null>(null);\n\nconst {\n segments,\n hasMergeTags,\n canRequestMergeTag,\n isRequestingMergeTag,\n isEditing,\n startEditing,\n stopEditing,\n handleInput,\n clearValue,\n insertMergeTag,\n} = useMergeTagField({\n modelValue: () => props.modelValue,\n emit: (value) => emit(\"update:modelValue\", value),\n elementRef: textareaRef,\n});\n\nconst textareaClass =\n \"tpl:w-full tpl:resize-y tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:shadow-xs tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] tpl:placeholder:text-[var(--tpl-text-dim)] tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)]\";\nconst displayClass =\n \"tpl:flex tpl:w-full tpl:min-h-[5rem] tpl:cursor-pointer tpl:items-start tpl:flex-wrap tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:shadow-xs tpl:bg-[var(--tpl-bg)] tpl:border-[var(--tpl-border)] tpl:px-3 tpl:py-2 tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\";\n</script>\n\n<template>\n <div v-if=\"hasMergeTags && !isEditing\">\n <MergeTagSegments\n :segments=\"segments\"\n :display-class=\"displayClass\"\n @edit=\"startEditing\"\n @clear=\"clearValue\"\n />\n <MergeTagInsertButton\n v-if=\"canRequestMergeTag\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n </div>\n <div v-else>\n <textarea\n ref=\"textareaRef\"\n :class=\"textareaClass\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n :rows=\"rows\"\n @input=\"handleInput\"\n @blur=\"stopEditing\"\n @keydown.escape=\"stopEditing\"\n />\n <MergeTagInsertButton\n v-if=\"canRequestMergeTag\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"./ColorPicker.vue\";\nimport MergeTagTextarea from \"./MergeTagTextarea.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type { TemplateSettings } from \"@templatical/types\";\nimport {\n cardClass,\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n DEFAULT_BG_COLOR,\n} from \"../constants/styleConstants\";\nimport { Circle, Eye, Globe, Info, Square } from \"@lucide/vue\";\nimport { computed } from \"vue\";\nimport { FONTS_MANAGER_KEY, requireInject } from \"../keys\";\n\nconst props = defineProps<{\n settings: TemplateSettings;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", settings: Partial<TemplateSettings>): void;\n}>();\n\nconst PREHEADER_MAX_LENGTH = 150;\n\nconst { t } = useI18n();\n\nconst fontsManager = requireInject(FONTS_MANAGER_KEY, \"TemplateSettings\");\nconst fontFamilies = computed(() => fontsManager.fonts.value);\n\n// If current font is not in available list (e.g., custom font when disabled), use default\nconst displayedFontFamily = computed(() => {\n const isAvailable = fontFamilies.value.some(\n (font) => font.value === props.settings.fontFamily,\n );\n return isAvailable\n ? props.settings.fontFamily\n : fontsManager.defaultFont.value;\n});\n\nconst widthPresets = [\n { value: 480, label: \"480px\" },\n { value: 600, label: \"600px\" },\n { value: 700, label: \"700px\" },\n { value: 800, label: \"800px\" },\n];\n</script>\n\n<template>\n <aside\n class=\"tpl:flex tpl:w-full tpl:flex-1 tpl:flex-col tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <div\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:gap-3 tpl:overflow-y-auto tpl:p-4\"\n >\n <!-- Layout card -->\n <div :class=\"cardClass\">\n <div\n class=\"tpl:mb-3.5 tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n <Square\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span>{{ t.templateSettings.layout }}</span>\n </div>\n\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{\n t.templateSettings.widthPreset\n }}</label>\n <div\n class=\"tpl:grid tpl:grid-cols-4 tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1 tpl:bg-[var(--tpl-bg-hover)]\"\n >\n <button\n v-for=\"preset in widthPresets\"\n :key=\"preset.value\"\n class=\"tpl:cursor-pointer tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-2 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :style=\"{\n backgroundColor:\n settings.width === preset.value\n ? 'var(--tpl-bg)'\n : 'transparent',\n color:\n settings.width === preset.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n boxShadow:\n settings.width === preset.value\n ? 'var(--tpl-shadow)'\n : 'none',\n }\"\n @click=\"emit('update', { width: preset.value })\"\n >\n {{ preset.label }}\n </button>\n </div>\n </div>\n\n <div>\n <label :class=\"labelClass\">{{\n t.templateSettings.customWidth\n }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"settings.width\"\n min=\"300\"\n max=\"900\"\n @input=\"\n emit('update', {\n width: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n </div>\n\n <!-- Appearance card -->\n <div :class=\"cardClass\">\n <div\n class=\"tpl:mb-3.5 tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n <Circle\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span>{{ t.templateSettings.appearance }}</span>\n </div>\n\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{\n t.templateSettings.backgroundColor\n }}</label>\n <ColorPicker\n :model-value=\"settings.backgroundColor\"\n :placeholder=\"DEFAULT_BG_COLOR\"\n @update:model-value=\"emit('update', { backgroundColor: $event })\"\n />\n </div>\n\n <div>\n <label :class=\"labelClass\">{{ t.templateSettings.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"displayedFontFamily\"\n @change=\"\n emit('update', {\n fontFamily: ($event.target as HTMLSelectElement).value,\n })\n \"\n >\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n </div>\n\n <!-- Language card -->\n <div :class=\"cardClass\">\n <div\n class=\"tpl:mb-3.5 tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n <Globe\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span>{{ t.templateSettings.language }}</span>\n </div>\n\n <div>\n <label :class=\"labelClass\">{{\n t.templateSettings.contentLocale\n }}</label>\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"settings.locale ?? ''\"\n placeholder=\"en\"\n spellcheck=\"false\"\n autocapitalize=\"off\"\n autocomplete=\"off\"\n @input=\"\n emit('update', {\n locale:\n ($event.target as HTMLInputElement).value.trim() || undefined,\n })\n \"\n />\n <p\n class=\"tpl:mt-1 tpl:text-xs tpl:leading-relaxed tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.templateSettings.contentLocaleHint }}\n </p>\n </div>\n </div>\n\n <!-- Preheader card -->\n <div :class=\"cardClass\">\n <div\n class=\"tpl:mb-3.5 tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n <Eye\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span>{{ t.templateSettings.preheaderText }}</span>\n </div>\n\n <div>\n <MergeTagTextarea\n :model-value=\"settings.preheaderText ?? ''\"\n :placeholder=\"t.templateSettings.preheaderTextPlaceholder\"\n :rows=\"2\"\n @update:model-value=\"\n emit('update', {\n preheaderText: $event.replace(/[\\r\\n]/g, ' ') || undefined,\n })\n \"\n />\n <div\n class=\"tpl:mt-1 tpl:flex tpl:items-start tpl:justify-between tpl:gap-2\"\n >\n <span\n class=\"tpl:text-xs tpl:leading-relaxed tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.templateSettings.preheaderTextHint }}\n </span>\n <span\n class=\"tpl:shrink-0 tpl:text-xs tpl:tabular-nums tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ (settings.preheaderText ?? \"\").length }}/{{\n PREHEADER_MAX_LENGTH\n }}\n </span>\n </div>\n </div>\n </div>\n\n <!-- Tips card -->\n <div\n class=\"tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3\"\n >\n <div\n class=\"tpl:mb-2.5 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text-muted)]\"\n >\n <Info :size=\"14\" :stroke-width=\"2\" />\n <span>{{ t.templateSettings.tips }}</span>\n </div>\n <ul\n class=\"tpl:m-0 tpl:pl-[18px] tpl:text-xs tpl:leading-relaxed tpl:text-[var(--tpl-text-dim)]\"\n >\n <li class=\"tpl:mb-1 tpl:last:mb-0\">\n {{ t.templateSettings.tip1 }}\n </li>\n <li class=\"tpl:mb-1 tpl:last:mb-0\">\n {{ t.templateSettings.tip2 }}\n </li>\n <li class=\"tpl:mb-1 tpl:last:mb-0\">\n {{ t.templateSettings.tip3 }}\n </li>\n </ul>\n </div>\n </div>\n </aside>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"./ColorPicker.vue\";\nimport MergeTagTextarea from \"./MergeTagTextarea.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type { TemplateSettings } from \"@templatical/types\";\nimport {\n cardClass,\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n DEFAULT_BG_COLOR,\n} from \"../constants/styleConstants\";\nimport { Circle, Eye, Globe, Info, Square } from \"@lucide/vue\";\nimport { computed } from \"vue\";\nimport { FONTS_MANAGER_KEY, requireInject } from \"../keys\";\n\nconst props = defineProps<{\n settings: TemplateSettings;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", settings: Partial<TemplateSettings>): void;\n}>();\n\nconst PREHEADER_MAX_LENGTH = 150;\n\nconst { t } = useI18n();\n\nconst fontsManager = requireInject(FONTS_MANAGER_KEY, \"TemplateSettings\");\nconst fontFamilies = computed(() => fontsManager.fonts.value);\n\n// If current font is not in available list (e.g., custom font when disabled), use default\nconst displayedFontFamily = computed(() => {\n const isAvailable = fontFamilies.value.some(\n (font) => font.value === props.settings.fontFamily,\n );\n return isAvailable\n ? props.settings.fontFamily\n : fontsManager.defaultFont.value;\n});\n\nconst widthPresets = [\n { value: 480, label: \"480px\" },\n { value: 600, label: \"600px\" },\n { value: 700, label: \"700px\" },\n { value: 800, label: \"800px\" },\n];\n</script>\n\n<template>\n <aside\n class=\"tpl:flex tpl:w-full tpl:flex-1 tpl:flex-col tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <div\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:gap-3 tpl:overflow-y-auto tpl:p-4\"\n >\n <!-- Layout card -->\n <div :class=\"cardClass\">\n <div\n class=\"tpl:mb-3.5 tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n <Square\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span>{{ t.templateSettings.layout }}</span>\n </div>\n\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{\n t.templateSettings.widthPreset\n }}</label>\n <div\n class=\"tpl:grid tpl:grid-cols-4 tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1 tpl:bg-[var(--tpl-bg-hover)]\"\n >\n <button\n v-for=\"preset in widthPresets\"\n :key=\"preset.value\"\n class=\"tpl:cursor-pointer tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-2 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :style=\"{\n backgroundColor:\n settings.width === preset.value\n ? 'var(--tpl-bg)'\n : 'transparent',\n color:\n settings.width === preset.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n boxShadow:\n settings.width === preset.value\n ? 'var(--tpl-shadow)'\n : 'none',\n }\"\n @click=\"emit('update', { width: preset.value })\"\n >\n {{ preset.label }}\n </button>\n </div>\n </div>\n\n <div>\n <label :class=\"labelClass\">{{\n t.templateSettings.customWidth\n }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"settings.width\"\n min=\"300\"\n max=\"900\"\n @input=\"\n emit('update', {\n width: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n </div>\n\n <!-- Appearance card -->\n <div :class=\"cardClass\">\n <div\n class=\"tpl:mb-3.5 tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n <Circle\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span>{{ t.templateSettings.appearance }}</span>\n </div>\n\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{\n t.templateSettings.backgroundColor\n }}</label>\n <ColorPicker\n :model-value=\"settings.backgroundColor\"\n :placeholder=\"DEFAULT_BG_COLOR\"\n @update:model-value=\"emit('update', { backgroundColor: $event })\"\n />\n </div>\n\n <div>\n <label :class=\"labelClass\">{{ t.templateSettings.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"displayedFontFamily\"\n @change=\"\n emit('update', {\n fontFamily: ($event.target as HTMLSelectElement).value,\n })\n \"\n >\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n </div>\n\n <!-- Language card -->\n <div :class=\"cardClass\">\n <div\n class=\"tpl:mb-3.5 tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n <Globe\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span>{{ t.templateSettings.language }}</span>\n </div>\n\n <div>\n <label :class=\"labelClass\">{{\n t.templateSettings.contentLocale\n }}</label>\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"settings.locale ?? ''\"\n placeholder=\"en\"\n spellcheck=\"false\"\n autocapitalize=\"off\"\n autocomplete=\"off\"\n @input=\"\n emit('update', {\n locale:\n ($event.target as HTMLInputElement).value.trim() || undefined,\n })\n \"\n />\n <p\n class=\"tpl:mt-1 tpl:text-xs tpl:leading-relaxed tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.templateSettings.contentLocaleHint }}\n </p>\n </div>\n </div>\n\n <!-- Preheader card -->\n <div :class=\"cardClass\">\n <div\n class=\"tpl:mb-3.5 tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n <Eye\n class=\"tpl:text-[var(--tpl-text-muted)]\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span>{{ t.templateSettings.preheaderText }}</span>\n </div>\n\n <div>\n <MergeTagTextarea\n :model-value=\"settings.preheaderText ?? ''\"\n :placeholder=\"t.templateSettings.preheaderTextPlaceholder\"\n :rows=\"2\"\n @update:model-value=\"\n emit('update', {\n preheaderText: $event.replace(/[\\r\\n]/g, ' ') || undefined,\n })\n \"\n />\n <div\n class=\"tpl:mt-1 tpl:flex tpl:items-start tpl:justify-between tpl:gap-2\"\n >\n <span\n class=\"tpl:text-xs tpl:leading-relaxed tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.templateSettings.preheaderTextHint }}\n </span>\n <span\n class=\"tpl:shrink-0 tpl:text-xs tpl:tabular-nums tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ (settings.preheaderText ?? \"\").length }}/{{\n PREHEADER_MAX_LENGTH\n }}\n </span>\n </div>\n </div>\n </div>\n\n <!-- Tips card -->\n <div\n class=\"tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3\"\n >\n <div\n class=\"tpl:mb-2.5 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text-muted)]\"\n >\n <Info :size=\"14\" :stroke-width=\"2\" />\n <span>{{ t.templateSettings.tips }}</span>\n </div>\n <ul\n class=\"tpl:m-0 tpl:pl-[18px] tpl:text-xs tpl:leading-relaxed tpl:text-[var(--tpl-text-dim)]\"\n >\n <li class=\"tpl:mb-1 tpl:last:mb-0\">\n {{ t.templateSettings.tip1 }}\n </li>\n <li class=\"tpl:mb-1 tpl:last:mb-0\">\n {{ t.templateSettings.tip2 }}\n </li>\n <li class=\"tpl:mb-1 tpl:last:mb-0\">\n {{ t.templateSettings.tip3 }}\n </li>\n </ul>\n </div>\n </div>\n </aside>\n</template>\n","<script setup lang=\"ts\">\nimport { useMergeTagField } from \"../composables/useMergeTagField\";\nimport { inputClass } from \"../constants/styleConstants\";\nimport MergeTagSegments from \"./MergeTagSegments.vue\";\nimport MergeTagInsertButton from \"./MergeTagInsertButton.vue\";\nimport { ref } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string;\n type?: \"text\" | \"url\";\n placeholder?: string;\n pulse?: boolean;\n }>(),\n {\n type: \"text\",\n placeholder: \"\",\n pulse: false,\n },\n);\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst inputRef = ref<HTMLInputElement | null>(null);\n\nconst {\n segments,\n hasMergeTags,\n canRequestMergeTag,\n isRequestingMergeTag,\n isEditing,\n startEditing,\n stopEditing,\n handleInput,\n clearValue,\n insertMergeTag,\n} = useMergeTagField({\n modelValue: () => props.modelValue,\n emit: (value) => emit(\"update:modelValue\", value),\n elementRef: inputRef,\n});\n\nconst displayClass =\n \"tpl:flex tpl:w-full tpl:min-h-10 tpl:cursor-pointer tpl:items-center tpl:flex-wrap tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:shadow-xs tpl:bg-[var(--tpl-bg)] tpl:border-[var(--tpl-border)] tpl:px-3.5 tpl:py-1.5 tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\";\n</script>\n\n<template>\n <div v-if=\"hasMergeTags && !isEditing\">\n <MergeTagSegments\n :segments=\"segments\"\n :display-class=\"displayClass\"\n :pulse=\"pulse\"\n @edit=\"startEditing\"\n @clear=\"clearValue\"\n />\n <MergeTagInsertButton\n v-if=\"canRequestMergeTag\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n </div>\n <div v-else>\n <input\n ref=\"inputRef\"\n :type=\"type\"\n :class=\"[inputClass, { 'tpl-pulse-fill': pulse }]\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n @input=\"handleInput\"\n @blur=\"stopEditing\"\n @keydown.escape=\"stopEditing\"\n />\n <MergeTagInsertButton\n v-if=\"canRequestMergeTag\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n </div>\n</template>\n\n<style scoped>\n.tpl-pulse-fill {\n animation: tpl-field-pulse 1s ease-out;\n}\n\n@keyframes tpl-field-pulse {\n 0% {\n box-shadow: 0 0 0 0 color-mix(in srgb, var(--tpl-warning) 30%, transparent);\n background-color: color-mix(in srgb, var(--tpl-warning) 6%, transparent);\n }\n 40% {\n box-shadow: 0 0 0 3px\n color-mix(in srgb, var(--tpl-warning) 15%, transparent);\n background-color: color-mix(in srgb, var(--tpl-warning) 5%, transparent);\n }\n 100% {\n box-shadow: 0 0 0 0 transparent;\n background-color: transparent;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .tpl-pulse-fill {\n animation: none;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useMergeTagField } from \"../composables/useMergeTagField\";\nimport { inputClass } from \"../constants/styleConstants\";\nimport MergeTagSegments from \"./MergeTagSegments.vue\";\nimport MergeTagInsertButton from \"./MergeTagInsertButton.vue\";\nimport { ref } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string;\n type?: \"text\" | \"url\";\n placeholder?: string;\n pulse?: boolean;\n }>(),\n {\n type: \"text\",\n placeholder: \"\",\n pulse: false,\n },\n);\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst inputRef = ref<HTMLInputElement | null>(null);\n\nconst {\n segments,\n hasMergeTags,\n canRequestMergeTag,\n isRequestingMergeTag,\n isEditing,\n startEditing,\n stopEditing,\n handleInput,\n clearValue,\n insertMergeTag,\n} = useMergeTagField({\n modelValue: () => props.modelValue,\n emit: (value) => emit(\"update:modelValue\", value),\n elementRef: inputRef,\n});\n\nconst displayClass =\n \"tpl:flex tpl:w-full tpl:min-h-10 tpl:cursor-pointer tpl:items-center tpl:flex-wrap tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:shadow-xs tpl:bg-[var(--tpl-bg)] tpl:border-[var(--tpl-border)] tpl:px-3.5 tpl:py-1.5 tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\";\n</script>\n\n<template>\n <div v-if=\"hasMergeTags && !isEditing\">\n <MergeTagSegments\n :segments=\"segments\"\n :display-class=\"displayClass\"\n :pulse=\"pulse\"\n @edit=\"startEditing\"\n @clear=\"clearValue\"\n />\n <MergeTagInsertButton\n v-if=\"canRequestMergeTag\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n </div>\n <div v-else>\n <input\n ref=\"inputRef\"\n :type=\"type\"\n :class=\"[inputClass, { 'tpl-pulse-fill': pulse }]\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n @input=\"handleInput\"\n @blur=\"stopEditing\"\n @keydown.escape=\"stopEditing\"\n />\n <MergeTagInsertButton\n v-if=\"canRequestMergeTag\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n </div>\n</template>\n\n<style scoped>\n.tpl-pulse-fill {\n animation: tpl-field-pulse 1s ease-out;\n}\n\n@keyframes tpl-field-pulse {\n 0% {\n box-shadow: 0 0 0 0 color-mix(in srgb, var(--tpl-warning) 30%, transparent);\n background-color: color-mix(in srgb, var(--tpl-warning) 6%, transparent);\n }\n 40% {\n box-shadow: 0 0 0 3px\n color-mix(in srgb, var(--tpl-warning) 15%, transparent);\n background-color: color-mix(in srgb, var(--tpl-warning) 5%, transparent);\n }\n 100% {\n box-shadow: 0 0 0 0 transparent;\n background-color: transparent;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .tpl-pulse-fill {\n animation: none;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { ButtonBlock } from \"@templatical/types\";\n\ndefineProps<{\n block: ButtonBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ButtonBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ButtonBlock>);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.button.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.text }}</label>\n <MergeTagInput\n :model-value=\"block.text\"\n type=\"text\"\n @update:model-value=\"updateField('text', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.url }}</label>\n <MergeTagInput\n :model-value=\"block.url\"\n type=\"url\"\n :placeholder=\"t.button.urlPlaceholder\"\n @update:model-value=\"updateField('url', $event)\"\n />\n <label\n v-if=\"block.url\"\n class=\"tpl:mt-2 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:size-3.5 tpl:cursor-pointer tpl:accent-[var(--tpl-primary)]\"\n :checked=\"block.openInNewTab ?? false\"\n @change=\"\n updateField(\n 'openInNewTab',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.button.openInNewTab }}\n </label>\n </div>\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.background }}</label>\n <ColorPicker\n :model-value=\"block.backgroundColor\"\n @update:model-value=\"updateField('backgroundColor', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.textColor }}</label>\n <ColorPicker\n :model-value=\"block.textColor\"\n @update:model-value=\"updateField('textColor', $event)\"\n />\n </div>\n </div>\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.borderRadius }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.borderRadius\"\n min=\"0\"\n max=\"50\"\n @input=\"\n updateField(\n 'borderRadius',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.fontSize }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.fontSize\"\n min=\"10\"\n max=\"36\"\n @input=\"\n updateField(\n 'fontSize',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { ButtonBlock } from \"@templatical/types\";\n\ndefineProps<{\n block: ButtonBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ButtonBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ButtonBlock>);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.button.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.text }}</label>\n <MergeTagInput\n :model-value=\"block.text\"\n type=\"text\"\n @update:model-value=\"updateField('text', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.url }}</label>\n <MergeTagInput\n :model-value=\"block.url\"\n type=\"url\"\n :placeholder=\"t.button.urlPlaceholder\"\n @update:model-value=\"updateField('url', $event)\"\n />\n <label\n v-if=\"block.url\"\n class=\"tpl:mt-2 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:size-3.5 tpl:cursor-pointer tpl:accent-[var(--tpl-primary)]\"\n :checked=\"block.openInNewTab ?? false\"\n @change=\"\n updateField(\n 'openInNewTab',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.button.openInNewTab }}\n </label>\n </div>\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.background }}</label>\n <ColorPicker\n :model-value=\"block.backgroundColor\"\n @update:model-value=\"updateField('backgroundColor', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.textColor }}</label>\n <ColorPicker\n :model-value=\"block.textColor\"\n @update:model-value=\"updateField('textColor', $event)\"\n />\n </div>\n </div>\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.borderRadius }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.borderRadius\"\n min=\"0\"\n max=\"50\"\n @input=\"\n updateField(\n 'borderRadius',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.button.fontSize }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.fontSize\"\n min=\"10\"\n max=\"36\"\n @input=\"\n updateField(\n 'fontSize',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Lock, LockOpen, Minus, Plus } from \"@lucide/vue\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n modelValue: SpacingValue;\n label: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: SpacingValue): void;\n}>();\n\nconst { t } = useI18n();\n\ninterface SpacingValue {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\nconst isUniform = computed(\n () =>\n props.modelValue.top === props.modelValue.right &&\n props.modelValue.right === props.modelValue.bottom &&\n props.modelValue.bottom === props.modelValue.left,\n);\n\nconst locked = ref(isUniform.value);\n\nwatch(isUniform, (uniform) => {\n if (!uniform && locked.value) {\n locked.value = false;\n }\n});\n\nfunction updateValue(direction: keyof SpacingValue, delta: number): void {\n const currentValue = props.modelValue[direction];\n const newValue = Math.max(0, currentValue + delta);\n\n if (locked.value) {\n emit(\"update:modelValue\", {\n top: newValue,\n right: newValue,\n bottom: newValue,\n left: newValue,\n });\n } else {\n emit(\"update:modelValue\", {\n ...props.modelValue,\n [direction]: newValue,\n });\n }\n}\n\nfunction setDirectValue(direction: keyof SpacingValue, value: number): void {\n const newValue = Math.max(0, value);\n\n if (locked.value) {\n emit(\"update:modelValue\", {\n top: newValue,\n right: newValue,\n bottom: newValue,\n left: newValue,\n });\n } else {\n emit(\"update:modelValue\", {\n ...props.modelValue,\n [direction]: newValue,\n });\n }\n}\n\nfunction toggleLock(): void {\n locked.value = !locked.value;\n if (locked.value) {\n const value = props.modelValue.top;\n emit(\"update:modelValue\", {\n top: value,\n right: value,\n bottom: value,\n left: value,\n });\n }\n}\n\nconst stepperBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:w-8 tpl:h-8 tpl:text-[var(--tpl-text-muted)] tpl:bg-[var(--tpl-bg)] tpl:border tpl:border-[var(--tpl-border)] tpl:cursor-pointer tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)] active:tpl:bg-[var(--tpl-bg-active)]\";\nconst inputClass =\n \"tpl:w-10 tpl:h-8 tpl:text-center tpl:text-xs tpl:font-medium tpl:border-y tpl:border-x-0 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)] tpl:outline-none tpl:transition-all tpl:duration-[120ms] focus:tpl:border-[var(--tpl-primary)] focus:tpl:shadow-[var(--tpl-ring)]\";\n</script>\n\n<template>\n <div class=\"spacing-control\">\n <label\n class=\"tpl:mb-2 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ label }}\n </label>\n\n <div class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-1.5\">\n <!-- Top -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseTop\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('top', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.top\"\n :aria-label=\"t.spacingControl.top\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'top',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseTop\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('top', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Middle row: Left - Lock - Right -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <!-- Left -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseLeft\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('left', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.left\"\n :aria-label=\"t.spacingControl.left\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'left',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseLeft\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('left', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Lock button -->\n <button\n class=\"tpl:flex tpl:h-8 tpl:w-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"\n locked\n ? 'tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary)]'\n : 'tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] hover:tpl:bg-[var(--tpl-bg-hover)]'\n \"\n :aria-label=\"\n locked ? t.spacingControl.unlock : t.spacingControl.lockAll\n \"\n :title=\"locked ? t.spacingControl.unlock : t.spacingControl.lockAll\"\n @click=\"toggleLock\"\n >\n <Lock v-if=\"locked\" :size=\"14\" :stroke-width=\"2\" />\n <LockOpen v-else :size=\"14\" :stroke-width=\"2\" />\n </button>\n\n <!-- Right -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseRight\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('right', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.right\"\n :aria-label=\"t.spacingControl.right\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'right',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseRight\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('right', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Bottom -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseBottom\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('bottom', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.bottom\"\n :aria-label=\"t.spacingControl.bottom\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'bottom',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseBottom\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('bottom', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\ninput[type=\"number\"]::-webkit-outer-spin-button,\ninput[type=\"number\"]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\ninput[type=\"number\"] {\n -moz-appearance: textfield;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Lock, LockOpen, Minus, Plus } from \"@lucide/vue\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n modelValue: SpacingValue;\n label: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: SpacingValue): void;\n}>();\n\nconst { t } = useI18n();\n\ninterface SpacingValue {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\nconst isUniform = computed(\n () =>\n props.modelValue.top === props.modelValue.right &&\n props.modelValue.right === props.modelValue.bottom &&\n props.modelValue.bottom === props.modelValue.left,\n);\n\nconst locked = ref(isUniform.value);\n\nwatch(isUniform, (uniform) => {\n if (!uniform && locked.value) {\n locked.value = false;\n }\n});\n\nfunction updateValue(direction: keyof SpacingValue, delta: number): void {\n const currentValue = props.modelValue[direction];\n const newValue = Math.max(0, currentValue + delta);\n\n if (locked.value) {\n emit(\"update:modelValue\", {\n top: newValue,\n right: newValue,\n bottom: newValue,\n left: newValue,\n });\n } else {\n emit(\"update:modelValue\", {\n ...props.modelValue,\n [direction]: newValue,\n });\n }\n}\n\nfunction setDirectValue(direction: keyof SpacingValue, value: number): void {\n const newValue = Math.max(0, value);\n\n if (locked.value) {\n emit(\"update:modelValue\", {\n top: newValue,\n right: newValue,\n bottom: newValue,\n left: newValue,\n });\n } else {\n emit(\"update:modelValue\", {\n ...props.modelValue,\n [direction]: newValue,\n });\n }\n}\n\nfunction toggleLock(): void {\n locked.value = !locked.value;\n if (locked.value) {\n const value = props.modelValue.top;\n emit(\"update:modelValue\", {\n top: value,\n right: value,\n bottom: value,\n left: value,\n });\n }\n}\n\nconst stepperBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:w-8 tpl:h-8 tpl:text-[var(--tpl-text-muted)] tpl:bg-[var(--tpl-bg)] tpl:border tpl:border-[var(--tpl-border)] tpl:cursor-pointer tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)] hover:tpl:bg-[var(--tpl-bg-hover)] hover:tpl:text-[var(--tpl-text)] active:tpl:bg-[var(--tpl-bg-active)]\";\nconst inputClass =\n \"tpl:w-10 tpl:h-8 tpl:text-center tpl:text-xs tpl:font-medium tpl:border-y tpl:border-x-0 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)] tpl:outline-none tpl:transition-all tpl:duration-[120ms] focus:tpl:border-[var(--tpl-primary)] focus:tpl:shadow-[var(--tpl-ring)]\";\n</script>\n\n<template>\n <div class=\"spacing-control\">\n <label\n class=\"tpl:mb-2 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ label }}\n </label>\n\n <div class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-1.5\">\n <!-- Top -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseTop\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('top', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.top\"\n :aria-label=\"t.spacingControl.top\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'top',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseTop\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('top', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Middle row: Left - Lock - Right -->\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <!-- Left -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseLeft\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('left', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.left\"\n :aria-label=\"t.spacingControl.left\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'left',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseLeft\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('left', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Lock button -->\n <button\n class=\"tpl:flex tpl:h-8 tpl:w-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"\n locked\n ? 'tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-primary)]'\n : 'tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] hover:tpl:bg-[var(--tpl-bg-hover)]'\n \"\n :aria-label=\"\n locked ? t.spacingControl.unlock : t.spacingControl.lockAll\n \"\n :title=\"locked ? t.spacingControl.unlock : t.spacingControl.lockAll\"\n @click=\"toggleLock\"\n >\n <Lock v-if=\"locked\" :size=\"14\" :stroke-width=\"2\" />\n <LockOpen v-else :size=\"14\" :stroke-width=\"2\" />\n </button>\n\n <!-- Right -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseRight\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('right', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.right\"\n :aria-label=\"t.spacingControl.right\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'right',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseRight\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('right', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Bottom -->\n <div class=\"tpl:flex tpl:items-center\">\n <button\n :aria-label=\"t.spacingControl.decreaseBottom\"\n :class=\"[stepperBtnClass, 'tpl:rounded-l-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('bottom', -1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <input\n type=\"number\"\n :class=\"inputClass\"\n :value=\"modelValue.bottom\"\n :aria-label=\"t.spacingControl.bottom\"\n min=\"0\"\n @input=\"\n setDirectValue(\n 'bottom',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <button\n :aria-label=\"t.spacingControl.increaseBottom\"\n :class=\"[stepperBtnClass, 'tpl:rounded-r-[var(--tpl-radius-sm)]']\"\n @click=\"updateValue('bottom', 1)\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </div>\n</template>\n\n<style scoped>\ninput[type=\"number\"]::-webkit-outer-spin-button,\ninput[type=\"number\"]::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\ninput[type=\"number\"] {\n -moz-appearance: textfield;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { ChevronDown } from \"@lucide/vue\";\n\ndefineProps<{\n title: string;\n open: boolean;\n noBorder?: boolean;\n}>();\n\ndefineEmits<{\n (e: \"toggle\"): void;\n}>();\n</script>\n\n<template>\n <div\n class=\"tpl:py-3\"\n :class=\"noBorder ? '' : 'tpl:border-t tpl:border-[var(--tpl-border)]'\"\n >\n <button\n type=\"button\"\n class=\"tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-center tpl:gap-1.5 tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n @click=\"$emit('toggle')\"\n >\n <ChevronDown\n class=\"tpl:transition-transform tpl:duration-200\"\n :class=\"open ? 'tpl:rotate-0' : 'tpl:-rotate-90'\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n <span>{{ title }}</span>\n </button>\n <div v-show=\"open\" class=\"tpl:mt-3\">\n <slot />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { ChevronDown } from \"@lucide/vue\";\n\ndefineProps<{\n title: string;\n open: boolean;\n noBorder?: boolean;\n}>();\n\ndefineEmits<{\n (e: \"toggle\"): void;\n}>();\n</script>\n\n<template>\n <div\n class=\"tpl:py-3\"\n :class=\"noBorder ? '' : 'tpl:border-t tpl:border-[var(--tpl-border)]'\"\n >\n <button\n type=\"button\"\n class=\"tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-center tpl:gap-1.5 tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n @click=\"$emit('toggle')\"\n >\n <ChevronDown\n class=\"tpl:transition-transform tpl:duration-200\"\n :class=\"open ? 'tpl:rotate-0' : 'tpl:-rotate-90'\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n <span>{{ title }}</span>\n </button>\n <div v-show=\"open\" class=\"tpl:mt-3\">\n <slot />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SpacingControl from \"../SpacingControl.vue\";\nimport CollapsibleSection from \"./CollapsibleSection.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n labelClass,\n monoTextareaClass,\n DEFAULT_BG_COLOR,\n} from \"../../constants/styleConstants\";\nimport type { Block, DisplayCondition } from \"@templatical/types\";\nimport { Monitor, Smartphone, Tablet } from \"@lucide/vue\";\nimport { computed, inject, reactive, ref, watch, type Component } from \"vue\";\nimport {\n DISPLAY_CONDITIONS_KEY,\n ALLOW_CUSTOM_CONDITIONS_KEY,\n} from \"../../keys\";\n\ntype SectionKey = \"spacing\" | \"bg\" | \"display\" | \"css\" | \"condition\";\ntype VisibilityKey = \"desktop\" | \"tablet\" | \"mobile\";\n\nconst props = defineProps<{\n block: Block;\n isFirstSection?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<Block>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst displayConditions = inject(DISPLAY_CONDITIONS_KEY, []);\nconst allowCustomConditions = inject(ALLOW_CUSTOM_CONDITIONS_KEY, false);\n\nconst openSections = reactive(new Set<SectionKey>());\nconst customConditionMode = ref(false);\nconst customBefore = ref(\"\");\nconst customAfter = ref(\"\");\n\nconst VISIBILITY_ITEMS: {\n key: VisibilityKey;\n icon: Component;\n labelKey: \"showOnDesktop\" | \"showOnTablet\" | \"showOnMobile\";\n}[] = [\n { key: \"desktop\", icon: Monitor, labelKey: \"showOnDesktop\" },\n { key: \"tablet\", icon: Tablet, labelKey: \"showOnTablet\" },\n { key: \"mobile\", icon: Smartphone, labelKey: \"showOnMobile\" },\n];\n\nfunction toggleSection(key: SectionKey): void {\n if (openSections.has(key)) openSections.delete(key);\n else openSections.add(key);\n}\n\nconst hasDisplayConditions = computed(\n () => displayConditions.length > 0 || allowCustomConditions,\n);\n\nconst isCustomCondition = computed(() => {\n if (!props.block.displayCondition) return false;\n return !displayConditions.some(\n (c) => c.label === props.block.displayCondition?.label,\n );\n});\n\nfunction startCustomCondition(): void {\n customConditionMode.value = true;\n if (isCustomCondition.value && props.block.displayCondition) {\n customBefore.value = props.block.displayCondition.before;\n customAfter.value = props.block.displayCondition.after ?? \"\";\n } else {\n customBefore.value = \"\";\n customAfter.value = \"\";\n }\n}\n\nfunction applyCustomCondition(): void {\n if (!customBefore.value.trim()) return;\n emit(\"update\", {\n displayCondition: {\n label: t.blockSettings.customCondition,\n before: customBefore.value.trim(),\n after: customAfter.value.trim(),\n },\n });\n customConditionMode.value = false;\n customBefore.value = \"\";\n customAfter.value = \"\";\n}\n\nwatch(\n () => props.block.displayCondition,\n (condition) => {\n if (!condition) {\n customConditionMode.value = false;\n customBefore.value = \"\";\n customAfter.value = \"\";\n return;\n }\n if (isCustomCondition.value) {\n customBefore.value = condition.before;\n customAfter.value = condition.after ?? \"\";\n }\n },\n { immediate: true },\n);\n\nconst groupedDisplayConditions = computed(() => {\n const groups: Record<string, DisplayCondition[]> = {};\n for (const condition of displayConditions) {\n const group = condition.group ?? \"\";\n if (!groups[group]) groups[group] = [];\n groups[group].push(condition);\n }\n return groups;\n});\n\nfunction updateStyle(field: string, value: unknown): void {\n emit(\"update\", {\n styles: { ...props.block.styles, [field]: value },\n });\n}\n\nfunction isVisible(key: VisibilityKey): boolean {\n return props.block.visibility?.[key] !== false;\n}\n\nfunction toggleVisibility(key: VisibilityKey): void {\n const next: Record<VisibilityKey, boolean> = {\n desktop: isVisible(\"desktop\"),\n tablet: isVisible(\"tablet\"),\n mobile: isVisible(\"mobile\"),\n };\n next[key] = !next[key];\n emit(\"update\", { visibility: next });\n}\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:flex-col\" :class=\"isFirstSection ? '' : 'tpl:mt-4'\">\n <CollapsibleSection\n :title=\"t.blockSettings.spacing\"\n :open=\"openSections.has('spacing')\"\n :no-border=\"isFirstSection\"\n @toggle=\"toggleSection('spacing')\"\n >\n <SpacingControl\n :label=\"t.blockSettings.padding\"\n :model-value=\"block.styles.padding\"\n @update:model-value=\"updateStyle('padding', $event)\"\n />\n <div class=\"tpl:mt-4\">\n <SpacingControl\n :label=\"t.blockSettings.margin\"\n :model-value=\"block.styles.margin\"\n @update:model-value=\"updateStyle('margin', $event)\"\n />\n </div>\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.blockSettings.background\"\n :open=\"openSections.has('bg')\"\n @toggle=\"toggleSection('bg')\"\n >\n <label :class=\"labelClass\">{{ t.blockSettings.color }}</label>\n <ColorPicker\n size=\"large\"\n :model-value=\"block.styles.backgroundColor || DEFAULT_BG_COLOR\"\n @update:model-value=\"updateStyle('backgroundColor', $event)\"\n />\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.blockSettings.display\"\n :open=\"openSections.has('display')\"\n @toggle=\"toggleSection('display')\"\n >\n <div class=\"tpl:space-y-2\">\n <label\n v-for=\"item in VISIBILITY_ITEMS\"\n :key=\"item.key\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-xs tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:accent-[var(--tpl-primary)]\"\n :checked=\"isVisible(item.key)\"\n @change=\"toggleVisibility(item.key)\"\n />\n <component :is=\"item.icon\" :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.blockSettings[item.labelKey] }}\n </label>\n </div>\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.blockSettings.customCss\"\n :open=\"openSections.has('css')\"\n @toggle=\"toggleSection('css')\"\n >\n <label :class=\"labelClass\">{{ t.blockSettings.css }}</label>\n <textarea\n :value=\"block.customCss || ''\"\n :placeholder=\"t.blockSettings.cssPlaceholder\"\n rows=\"3\"\n :class=\"monoTextareaClass\"\n @input=\"\n emit('update', {\n customCss: ($event.target as HTMLTextAreaElement).value,\n })\n \"\n />\n </CollapsibleSection>\n\n <CollapsibleSection\n v-if=\"hasDisplayConditions\"\n :title=\"t.blockSettings.displayCondition\"\n :open=\"openSections.has('condition')\"\n @toggle=\"toggleSection('condition')\"\n >\n <div class=\"tpl:space-y-2\">\n <select\n class=\"tpl:w-full tpl:rounded-md tpl:border tpl:px-2.5 tpl:py-2 tpl:text-xs tpl:outline-none tpl:transition-all tpl:duration-150 tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[0_0_0_3px_var(--tpl-primary-light)]\"\n :class=\"\n block.displayCondition\n ? 'tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-text)]'\n : 'tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]'\n \"\n :value=\"\n customConditionMode || isCustomCondition\n ? '__custom__'\n : (block.displayCondition?.label ?? '')\n \"\n @change=\"\n (e: Event) => {\n const label = (e.target as HTMLSelectElement).value;\n if (label === '__custom__') {\n startCustomCondition();\n return;\n }\n customConditionMode = false;\n if (!label) {\n emit('update', { displayCondition: undefined });\n return;\n }\n const condition = displayConditions.find(\n (c) => c.label === label,\n );\n if (condition) {\n emit('update', { displayCondition: condition });\n }\n }\n \"\n >\n <option value=\"\">{{ t.blockSettings.noCondition }}</option>\n <template\n v-for=\"(conditions, group) in groupedDisplayConditions\"\n :key=\"group\"\n >\n <optgroup v-if=\"group\" :label=\"String(group)\">\n <option\n v-for=\"condition in conditions\"\n :key=\"condition.label\"\n :value=\"condition.label\"\n >\n {{ condition.label }}\n </option>\n </optgroup>\n <template v-else>\n <option\n v-for=\"condition in conditions\"\n :key=\"condition.label\"\n :value=\"condition.label\"\n >\n {{ condition.label }}\n </option>\n </template>\n </template>\n <option v-if=\"allowCustomConditions\" value=\"__custom__\">\n {{ t.blockSettings.customCondition }}\n </option>\n </select>\n\n <template v-if=\"customConditionMode || isCustomCondition\">\n <div class=\"tpl:space-y-2\">\n <div>\n <label\n class=\"tpl:mb-1 tpl:block tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings.customConditionBefore }}</label\n >\n <textarea\n v-model=\"customBefore\"\n rows=\"2\"\n :class=\"monoTextareaClass\"\n />\n </div>\n <div>\n <label\n class=\"tpl:mb-1 tpl:block tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings.customConditionAfter }}</label\n >\n <textarea\n v-model=\"customAfter\"\n rows=\"2\"\n :class=\"monoTextareaClass\"\n />\n </div>\n <div class=\"tpl:flex tpl:justify-end\">\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-[var(--tpl-primary)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-bg)] tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:opacity-50\"\n :disabled=\"!customBefore.trim()\"\n @click=\"applyCustomCondition\"\n >\n {{ t.blockSettings.applyCondition }}\n </button>\n </div>\n </div>\n </template>\n\n <template v-else-if=\"block.displayCondition && !isCustomCondition\">\n <p\n v-if=\"block.displayCondition.description\"\n class=\"tpl:text-[11px] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ block.displayCondition.description }}\n </p>\n <div class=\"tpl:space-y-1\">\n <pre\n class=\"tpl:m-0 tpl:overflow-x-auto tpl:rounded tpl:bg-[var(--tpl-bg)] tpl:p-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n >{{ block.displayCondition.before }}</pre\n >\n <pre\n v-if=\"block.displayCondition.after\"\n class=\"tpl:m-0 tpl:overflow-x-auto tpl:rounded tpl:bg-[var(--tpl-bg)] tpl:p-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n >{{ block.displayCondition.after }}</pre\n >\n </div>\n </template>\n </div>\n </CollapsibleSection>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SpacingControl from \"../SpacingControl.vue\";\nimport CollapsibleSection from \"./CollapsibleSection.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n labelClass,\n monoTextareaClass,\n DEFAULT_BG_COLOR,\n} from \"../../constants/styleConstants\";\nimport type { Block, DisplayCondition } from \"@templatical/types\";\nimport { Monitor, Smartphone, Tablet } from \"@lucide/vue\";\nimport { computed, inject, reactive, ref, watch, type Component } from \"vue\";\nimport {\n DISPLAY_CONDITIONS_KEY,\n ALLOW_CUSTOM_CONDITIONS_KEY,\n} from \"../../keys\";\n\ntype SectionKey = \"spacing\" | \"bg\" | \"display\" | \"css\" | \"condition\";\ntype VisibilityKey = \"desktop\" | \"tablet\" | \"mobile\";\n\nconst props = defineProps<{\n block: Block;\n isFirstSection?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<Block>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst displayConditions = inject(DISPLAY_CONDITIONS_KEY, []);\nconst allowCustomConditions = inject(ALLOW_CUSTOM_CONDITIONS_KEY, false);\n\nconst openSections = reactive(new Set<SectionKey>());\nconst customConditionMode = ref(false);\nconst customBefore = ref(\"\");\nconst customAfter = ref(\"\");\n\nconst VISIBILITY_ITEMS: {\n key: VisibilityKey;\n icon: Component;\n labelKey: \"showOnDesktop\" | \"showOnTablet\" | \"showOnMobile\";\n}[] = [\n { key: \"desktop\", icon: Monitor, labelKey: \"showOnDesktop\" },\n { key: \"tablet\", icon: Tablet, labelKey: \"showOnTablet\" },\n { key: \"mobile\", icon: Smartphone, labelKey: \"showOnMobile\" },\n];\n\nfunction toggleSection(key: SectionKey): void {\n if (openSections.has(key)) openSections.delete(key);\n else openSections.add(key);\n}\n\nconst hasDisplayConditions = computed(\n () => displayConditions.length > 0 || allowCustomConditions,\n);\n\nconst isCustomCondition = computed(() => {\n if (!props.block.displayCondition) return false;\n return !displayConditions.some(\n (c) => c.label === props.block.displayCondition?.label,\n );\n});\n\nfunction startCustomCondition(): void {\n customConditionMode.value = true;\n if (isCustomCondition.value && props.block.displayCondition) {\n customBefore.value = props.block.displayCondition.before;\n customAfter.value = props.block.displayCondition.after ?? \"\";\n } else {\n customBefore.value = \"\";\n customAfter.value = \"\";\n }\n}\n\nfunction applyCustomCondition(): void {\n if (!customBefore.value.trim()) return;\n emit(\"update\", {\n displayCondition: {\n label: t.blockSettings.customCondition,\n before: customBefore.value.trim(),\n after: customAfter.value.trim(),\n },\n });\n customConditionMode.value = false;\n customBefore.value = \"\";\n customAfter.value = \"\";\n}\n\nwatch(\n () => props.block.displayCondition,\n (condition) => {\n if (!condition) {\n customConditionMode.value = false;\n customBefore.value = \"\";\n customAfter.value = \"\";\n return;\n }\n if (isCustomCondition.value) {\n customBefore.value = condition.before;\n customAfter.value = condition.after ?? \"\";\n }\n },\n { immediate: true },\n);\n\nconst groupedDisplayConditions = computed(() => {\n const groups: Record<string, DisplayCondition[]> = {};\n for (const condition of displayConditions) {\n const group = condition.group ?? \"\";\n if (!groups[group]) groups[group] = [];\n groups[group].push(condition);\n }\n return groups;\n});\n\nfunction updateStyle(field: string, value: unknown): void {\n emit(\"update\", {\n styles: { ...props.block.styles, [field]: value },\n });\n}\n\nfunction isVisible(key: VisibilityKey): boolean {\n return props.block.visibility?.[key] !== false;\n}\n\nfunction toggleVisibility(key: VisibilityKey): void {\n const next: Record<VisibilityKey, boolean> = {\n desktop: isVisible(\"desktop\"),\n tablet: isVisible(\"tablet\"),\n mobile: isVisible(\"mobile\"),\n };\n next[key] = !next[key];\n emit(\"update\", { visibility: next });\n}\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:flex-col\" :class=\"isFirstSection ? '' : 'tpl:mt-4'\">\n <CollapsibleSection\n :title=\"t.blockSettings.spacing\"\n :open=\"openSections.has('spacing')\"\n :no-border=\"isFirstSection\"\n @toggle=\"toggleSection('spacing')\"\n >\n <SpacingControl\n :label=\"t.blockSettings.padding\"\n :model-value=\"block.styles.padding\"\n @update:model-value=\"updateStyle('padding', $event)\"\n />\n <div class=\"tpl:mt-4\">\n <SpacingControl\n :label=\"t.blockSettings.margin\"\n :model-value=\"block.styles.margin\"\n @update:model-value=\"updateStyle('margin', $event)\"\n />\n </div>\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.blockSettings.background\"\n :open=\"openSections.has('bg')\"\n @toggle=\"toggleSection('bg')\"\n >\n <label :class=\"labelClass\">{{ t.blockSettings.color }}</label>\n <ColorPicker\n size=\"large\"\n :model-value=\"block.styles.backgroundColor || DEFAULT_BG_COLOR\"\n @update:model-value=\"updateStyle('backgroundColor', $event)\"\n />\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.blockSettings.display\"\n :open=\"openSections.has('display')\"\n @toggle=\"toggleSection('display')\"\n >\n <div class=\"tpl:space-y-2\">\n <label\n v-for=\"item in VISIBILITY_ITEMS\"\n :key=\"item.key\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-xs tpl:text-[var(--tpl-text)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:accent-[var(--tpl-primary)]\"\n :checked=\"isVisible(item.key)\"\n @change=\"toggleVisibility(item.key)\"\n />\n <component :is=\"item.icon\" :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.blockSettings[item.labelKey] }}\n </label>\n </div>\n </CollapsibleSection>\n\n <CollapsibleSection\n :title=\"t.blockSettings.customCss\"\n :open=\"openSections.has('css')\"\n @toggle=\"toggleSection('css')\"\n >\n <label :class=\"labelClass\">{{ t.blockSettings.css }}</label>\n <textarea\n :value=\"block.customCss || ''\"\n :placeholder=\"t.blockSettings.cssPlaceholder\"\n rows=\"3\"\n :class=\"monoTextareaClass\"\n @input=\"\n emit('update', {\n customCss: ($event.target as HTMLTextAreaElement).value,\n })\n \"\n />\n </CollapsibleSection>\n\n <CollapsibleSection\n v-if=\"hasDisplayConditions\"\n :title=\"t.blockSettings.displayCondition\"\n :open=\"openSections.has('condition')\"\n @toggle=\"toggleSection('condition')\"\n >\n <div class=\"tpl:space-y-2\">\n <select\n class=\"tpl:w-full tpl:rounded-md tpl:border tpl:px-2.5 tpl:py-2 tpl:text-xs tpl:outline-none tpl:transition-all tpl:duration-150 tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[0_0_0_3px_var(--tpl-primary-light)]\"\n :class=\"\n block.displayCondition\n ? 'tpl:border-[var(--tpl-primary)] tpl:bg-[var(--tpl-primary-light)] tpl:text-[var(--tpl-text)]'\n : 'tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]'\n \"\n :value=\"\n customConditionMode || isCustomCondition\n ? '__custom__'\n : (block.displayCondition?.label ?? '')\n \"\n @change=\"\n (e: Event) => {\n const label = (e.target as HTMLSelectElement).value;\n if (label === '__custom__') {\n startCustomCondition();\n return;\n }\n customConditionMode = false;\n if (!label) {\n emit('update', { displayCondition: undefined });\n return;\n }\n const condition = displayConditions.find(\n (c) => c.label === label,\n );\n if (condition) {\n emit('update', { displayCondition: condition });\n }\n }\n \"\n >\n <option value=\"\">{{ t.blockSettings.noCondition }}</option>\n <template\n v-for=\"(conditions, group) in groupedDisplayConditions\"\n :key=\"group\"\n >\n <optgroup v-if=\"group\" :label=\"String(group)\">\n <option\n v-for=\"condition in conditions\"\n :key=\"condition.label\"\n :value=\"condition.label\"\n >\n {{ condition.label }}\n </option>\n </optgroup>\n <template v-else>\n <option\n v-for=\"condition in conditions\"\n :key=\"condition.label\"\n :value=\"condition.label\"\n >\n {{ condition.label }}\n </option>\n </template>\n </template>\n <option v-if=\"allowCustomConditions\" value=\"__custom__\">\n {{ t.blockSettings.customCondition }}\n </option>\n </select>\n\n <template v-if=\"customConditionMode || isCustomCondition\">\n <div class=\"tpl:space-y-2\">\n <div>\n <label\n class=\"tpl:mb-1 tpl:block tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings.customConditionBefore }}</label\n >\n <textarea\n v-model=\"customBefore\"\n rows=\"2\"\n :class=\"monoTextareaClass\"\n />\n </div>\n <div>\n <label\n class=\"tpl:mb-1 tpl:block tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >{{ t.blockSettings.customConditionAfter }}</label\n >\n <textarea\n v-model=\"customAfter\"\n rows=\"2\"\n :class=\"monoTextareaClass\"\n />\n </div>\n <div class=\"tpl:flex tpl:justify-end\">\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-[var(--tpl-primary)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-bg)] tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:opacity-50\"\n :disabled=\"!customBefore.trim()\"\n @click=\"applyCustomCondition\"\n >\n {{ t.blockSettings.applyCondition }}\n </button>\n </div>\n </div>\n </template>\n\n <template v-else-if=\"block.displayCondition && !isCustomCondition\">\n <p\n v-if=\"block.displayCondition.description\"\n class=\"tpl:text-[11px] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ block.displayCondition.description }}\n </p>\n <div class=\"tpl:space-y-1\">\n <pre\n class=\"tpl:m-0 tpl:overflow-x-auto tpl:rounded tpl:bg-[var(--tpl-bg)] tpl:p-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n >{{ block.displayCondition.before }}</pre\n >\n <pre\n v-if=\"block.displayCondition.after\"\n class=\"tpl:m-0 tpl:overflow-x-auto tpl:rounded tpl:bg-[var(--tpl-bg)] tpl:p-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n >{{ block.displayCondition.after }}</pre\n >\n </div>\n </template>\n </div>\n </CollapsibleSection>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockBooleanField } from \"@templatical/types\";\nimport { Lock } from \"@lucide/vue\";\n\ndefineProps<{\n field: CustomBlockBooleanField;\n modelValue: boolean;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: boolean): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n class=\"tpl:mb-3.5\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n >\n <label\n :class=\"[\n 'tpl:flex tpl:items-center tpl:justify-between tpl:gap-2',\n readOnly ? 'tpl:cursor-not-allowed' : 'tpl:cursor-pointer',\n ]\"\n >\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ field.label }}\n <Lock\n v-if=\"readOnly\"\n :size=\"12\"\n class=\"tpl:inline tpl:text-[var(--tpl-text-dim)]\"\n />\n <span v-if=\"field.required\" class=\"tpl:text-[var(--tpl-danger)]\">\n *\n </span>\n </span>\n <button\n type=\"button\"\n role=\"switch\"\n :aria-checked=\"modelValue\"\n :aria-label=\"field.label\"\n :class=\"[\n 'tpl:relative tpl:inline-flex tpl:h-5 tpl:w-9 tpl:shrink-0 tpl:rounded-full tpl:border-2 tpl:border-transparent tpl:transition-colors tpl:duration-200',\n modelValue\n ? 'tpl:bg-[var(--tpl-primary)]'\n : 'tpl:bg-[var(--tpl-border)]',\n readOnly\n ? 'tpl:opacity-60 tpl:cursor-not-allowed'\n : 'tpl:cursor-pointer',\n ]\"\n :disabled=\"readOnly\"\n @click=\"!readOnly && emit('update:modelValue', !modelValue)\"\n >\n <span\n class=\"tpl:pointer-events-none tpl:inline-block tpl:size-4 tpl:rounded-full tpl:bg-[var(--tpl-bg)] tpl:shadow tpl:transition-transform tpl:duration-200\"\n :class=\"modelValue ? 'tpl:translate-x-4' : 'tpl:translate-x-0'\"\n />\n </button>\n </label>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockBooleanField } from \"@templatical/types\";\nimport { Lock } from \"@lucide/vue\";\n\ndefineProps<{\n field: CustomBlockBooleanField;\n modelValue: boolean;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: boolean): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n class=\"tpl:mb-3.5\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n >\n <label\n :class=\"[\n 'tpl:flex tpl:items-center tpl:justify-between tpl:gap-2',\n readOnly ? 'tpl:cursor-not-allowed' : 'tpl:cursor-pointer',\n ]\"\n >\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ field.label }}\n <Lock\n v-if=\"readOnly\"\n :size=\"12\"\n class=\"tpl:inline tpl:text-[var(--tpl-text-dim)]\"\n />\n <span v-if=\"field.required\" class=\"tpl:text-[var(--tpl-danger)]\">\n *\n </span>\n </span>\n <button\n type=\"button\"\n role=\"switch\"\n :aria-checked=\"modelValue\"\n :aria-label=\"field.label\"\n :class=\"[\n 'tpl:relative tpl:inline-flex tpl:h-5 tpl:w-9 tpl:shrink-0 tpl:rounded-full tpl:border-2 tpl:border-transparent tpl:transition-colors tpl:duration-200',\n modelValue\n ? 'tpl:bg-[var(--tpl-primary)]'\n : 'tpl:bg-[var(--tpl-border)]',\n readOnly\n ? 'tpl:opacity-60 tpl:cursor-not-allowed'\n : 'tpl:cursor-pointer',\n ]\"\n :disabled=\"readOnly\"\n @click=\"!readOnly && emit('update:modelValue', !modelValue)\"\n >\n <span\n class=\"tpl:pointer-events-none tpl:inline-block tpl:size-4 tpl:rounded-full tpl:bg-[var(--tpl-bg)] tpl:shadow tpl:transition-transform tpl:duration-200\"\n :class=\"modelValue ? 'tpl:translate-x-4' : 'tpl:translate-x-0'\"\n />\n </button>\n </label>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { labelClass } from \"../../../constants/styleConstants\";\nimport { Lock } from \"@lucide/vue\";\n\ndefineProps<{\n label: string;\n required?: boolean;\n readOnly?: boolean;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ label }}\n <Lock\n v-if=\"readOnly\"\n :size=\"12\"\n class=\"tpl:inline tpl:text-[var(--tpl-text-dim)]\"\n />\n <span v-if=\"required\" class=\"tpl:text-[var(--tpl-danger)]\">*</span>\n </label>\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { labelClass } from \"../../../constants/styleConstants\";\nimport { Lock } from \"@lucide/vue\";\n\ndefineProps<{\n label: string;\n required?: boolean;\n readOnly?: boolean;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ label }}\n <Lock\n v-if=\"readOnly\"\n :size=\"12\"\n class=\"tpl:inline tpl:text-[var(--tpl-text-dim)]\"\n />\n <span v-if=\"required\" class=\"tpl:text-[var(--tpl-danger)]\">*</span>\n </label>\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockColorField } from \"@templatical/types\";\nimport { DEFAULT_TEXT_COLOR } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport ColorPicker from \"../../ColorPicker.vue\";\n\ndefineProps<{\n field: CustomBlockColorField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <ColorPicker\n :model-value=\"modelValue || DEFAULT_TEXT_COLOR\"\n :placeholder=\"field.placeholder || DEFAULT_TEXT_COLOR\"\n :disabled=\"readOnly\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockColorField } from \"@templatical/types\";\nimport { DEFAULT_TEXT_COLOR } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport ColorPicker from \"../../ColorPicker.vue\";\n\ndefineProps<{\n field: CustomBlockColorField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <ColorPicker\n :model-value=\"modelValue || DEFAULT_TEXT_COLOR\"\n :placeholder=\"field.placeholder || DEFAULT_TEXT_COLOR\"\n :disabled=\"readOnly\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockImageField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport { Image } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { ON_REQUEST_MEDIA_KEY } from \"../../../keys\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\ndefineProps<{\n field: CustomBlockImageField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\n\nasync function browseMedia(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (result) {\n emit(\"update:modelValue\", result.url);\n }\n}\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <input\n v-if=\"readOnly\"\n type=\"url\"\n :class=\"[inputClass, 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder || 'https://...'\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n />\n <input\n v-else\n type=\"url\"\n :class=\"inputClass\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder || 'https://...'\"\n @input=\"\n emit('update:modelValue', ($event.target as HTMLInputElement).value)\n \"\n />\n <button\n v-if=\"canBrowseMedia && !readOnly\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-primary)] tpl:bg-[var(--tpl-bg)]\"\n @click=\"browseMedia()\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockImageField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport { Image } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { ON_REQUEST_MEDIA_KEY } from \"../../../keys\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\ndefineProps<{\n field: CustomBlockImageField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\n\nasync function browseMedia(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (result) {\n emit(\"update:modelValue\", result.url);\n }\n}\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <input\n v-if=\"readOnly\"\n type=\"url\"\n :class=\"[inputClass, 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder || 'https://...'\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n />\n <input\n v-else\n type=\"url\"\n :class=\"inputClass\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder || 'https://...'\"\n @input=\"\n emit('update:modelValue', ($event.target as HTMLInputElement).value)\n \"\n />\n <button\n v-if=\"canBrowseMedia && !readOnly\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-primary)] tpl:bg-[var(--tpl-bg)]\"\n @click=\"browseMedia()\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockNumberField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\ndefineProps<{\n field: CustomBlockNumberField;\n modelValue: number;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: number): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <input\n type=\"number\"\n :class=\"[inputClass, readOnly && 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n :min=\"field.min\"\n :max=\"field.max\"\n :step=\"field.step\"\n :disabled=\"readOnly\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @input=\"\n !readOnly &&\n emit(\n 'update:modelValue',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockNumberField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\ndefineProps<{\n field: CustomBlockNumberField;\n modelValue: number;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: number): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <input\n type=\"number\"\n :class=\"[inputClass, readOnly && 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n :min=\"field.min\"\n :max=\"field.max\"\n :step=\"field.step\"\n :disabled=\"readOnly\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @input=\"\n !readOnly &&\n emit(\n 'update:modelValue',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockRepeatableField } from \"@templatical/types\";\nimport { Plus, Trash2 } from \"@lucide/vue\";\nimport { computed } from \"vue\";\nimport { resolveFieldComponent } from \"./index\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport { addItemBtnClass } from \"../../../constants/styleConstants\";\n\nconst props = defineProps<{\n field: CustomBlockRepeatableField;\n modelValue: Record<string, unknown>[];\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: Record<string, unknown>[]): void;\n}>();\n\nconst { t } = useI18n();\n\nconst items = computed(() => props.modelValue || []);\n\nconst canAdd = computed(\n () => !props.field.maxItems || items.value.length < props.field.maxItems,\n);\n\nconst canRemove = computed(\n () => !props.field.minItems || items.value.length > props.field.minItems,\n);\n\nfunction addItem(): void {\n if (!canAdd.value || props.readOnly) {\n return;\n }\n\n const newItem: Record<string, unknown> = {};\n for (const subField of props.field.fields) {\n newItem[subField.key] = subField.default ?? \"\";\n }\n\n emit(\"update:modelValue\", [...items.value, newItem]);\n}\n\nfunction removeItem(index: number): void {\n if (!canRemove.value || props.readOnly) {\n return;\n }\n\n const updated = [...items.value];\n updated.splice(index, 1);\n emit(\"update:modelValue\", updated);\n}\n\nfunction updateItemField(index: number, key: string, value: unknown): void {\n const updated = items.value.map((item, i) =>\n i === index ? { ...item, [key]: value } : item,\n );\n emit(\"update:modelValue\", updated);\n}\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"(item, index) in items\"\n :key=\"`${field.key}-${index}`\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-3\"\n >\n <div class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:justify-between\">\n <span\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-dim)]\"\n >\n #{{ index + 1 }}\n </span>\n <button\n v-if=\"canRemove && !readOnly\"\n type=\"button\"\n class=\"tpl:flex tpl:size-6 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-150 tpl:hover:border-[var(--tpl-danger)] tpl:hover:bg-[var(--tpl-danger-light)] tpl:hover:text-[var(--tpl-danger)]\"\n :title=\"t.customBlocks.fields.removeItem\"\n @click=\"removeItem(index)\"\n >\n <Trash2 :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <template v-for=\"subField in field.fields\" :key=\"subField.key\">\n <component\n :is=\"resolveFieldComponent(subField.type)\"\n :field=\"subField\"\n :model-value=\"item[subField.key]\"\n :read-only=\"readOnly\"\n @update:model-value=\"updateItemField(index, subField.key, $event)\"\n />\n </template>\n </div>\n\n <button\n v-if=\"canAdd && !readOnly\"\n type=\"button\"\n :class=\"addItemBtnClass\"\n @click=\"addItem\"\n >\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.customBlocks.fields.addItem }}\n </button>\n\n <p\n v-if=\"!canAdd && !readOnly\"\n class=\"tpl:m-0 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.customBlocks.fields.maxItemsReached }}\n </p>\n </div>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockRepeatableField } from \"@templatical/types\";\nimport { Plus, Trash2 } from \"@lucide/vue\";\nimport { computed } from \"vue\";\nimport { resolveFieldComponent } from \"./index\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport { addItemBtnClass } from \"../../../constants/styleConstants\";\n\nconst props = defineProps<{\n field: CustomBlockRepeatableField;\n modelValue: Record<string, unknown>[];\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: Record<string, unknown>[]): void;\n}>();\n\nconst { t } = useI18n();\n\nconst items = computed(() => props.modelValue || []);\n\nconst canAdd = computed(\n () => !props.field.maxItems || items.value.length < props.field.maxItems,\n);\n\nconst canRemove = computed(\n () => !props.field.minItems || items.value.length > props.field.minItems,\n);\n\nfunction addItem(): void {\n if (!canAdd.value || props.readOnly) {\n return;\n }\n\n const newItem: Record<string, unknown> = {};\n for (const subField of props.field.fields) {\n newItem[subField.key] = subField.default ?? \"\";\n }\n\n emit(\"update:modelValue\", [...items.value, newItem]);\n}\n\nfunction removeItem(index: number): void {\n if (!canRemove.value || props.readOnly) {\n return;\n }\n\n const updated = [...items.value];\n updated.splice(index, 1);\n emit(\"update:modelValue\", updated);\n}\n\nfunction updateItemField(index: number, key: string, value: unknown): void {\n const updated = items.value.map((item, i) =>\n i === index ? { ...item, [key]: value } : item,\n );\n emit(\"update:modelValue\", updated);\n}\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"(item, index) in items\"\n :key=\"`${field.key}-${index}`\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-3\"\n >\n <div class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:justify-between\">\n <span\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-dim)]\"\n >\n #{{ index + 1 }}\n </span>\n <button\n v-if=\"canRemove && !readOnly\"\n type=\"button\"\n class=\"tpl:flex tpl:size-6 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-150 tpl:hover:border-[var(--tpl-danger)] tpl:hover:bg-[var(--tpl-danger-light)] tpl:hover:text-[var(--tpl-danger)]\"\n :title=\"t.customBlocks.fields.removeItem\"\n @click=\"removeItem(index)\"\n >\n <Trash2 :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <template v-for=\"subField in field.fields\" :key=\"subField.key\">\n <component\n :is=\"resolveFieldComponent(subField.type)\"\n :field=\"subField\"\n :model-value=\"item[subField.key]\"\n :read-only=\"readOnly\"\n @update:model-value=\"updateItemField(index, subField.key, $event)\"\n />\n </template>\n </div>\n\n <button\n v-if=\"canAdd && !readOnly\"\n type=\"button\"\n :class=\"addItemBtnClass\"\n @click=\"addItem\"\n >\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.customBlocks.fields.addItem }}\n </button>\n\n <p\n v-if=\"!canAdd && !readOnly\"\n class=\"tpl:m-0 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.customBlocks.fields.maxItemsReached }}\n </p>\n </div>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockSelectField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\ndefineProps<{\n field: CustomBlockSelectField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <select\n :class=\"[inputClass, readOnly && 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :disabled=\"readOnly\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @change=\"\n !readOnly &&\n emit('update:modelValue', ($event.target as HTMLSelectElement).value)\n \"\n >\n <option\n v-for=\"option in field.options\"\n :key=\"option.value\"\n :value=\"option.value\"\n >\n {{ option.label }}\n </option>\n </select>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockSelectField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\n\ndefineProps<{\n field: CustomBlockSelectField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <select\n :class=\"[inputClass, readOnly && 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :disabled=\"readOnly\"\n :title=\"readOnly ? t.customBlocks.dataSource.readOnlyTooltip : undefined\"\n @change=\"\n !readOnly &&\n emit('update:modelValue', ($event.target as HTMLSelectElement).value)\n \"\n >\n <option\n v-for=\"option in field.options\"\n :key=\"option.value\"\n :value=\"option.value\"\n >\n {{ option.label }}\n </option>\n </select>\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockTextField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport MergeTagInput from \"../../MergeTagInput.vue\";\n\ndefineProps<{\n field: CustomBlockTextField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <input\n v-if=\"readOnly\"\n type=\"text\"\n :class=\"[inputClass, 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n />\n <MergeTagInput\n v-else\n :model-value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockTextField } from \"@templatical/types\";\nimport { inputClass } from \"../../../constants/styleConstants\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport MergeTagInput from \"../../MergeTagInput.vue\";\n\ndefineProps<{\n field: CustomBlockTextField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <input\n v-if=\"readOnly\"\n type=\"text\"\n :class=\"[inputClass, 'tpl:opacity-60 tpl:cursor-not-allowed']\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n />\n <MergeTagInput\n v-else\n :model-value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockTextareaField } from \"@templatical/types\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport MergeTagTextarea from \"../../MergeTagTextarea.vue\";\n\ndefineProps<{\n field: CustomBlockTextareaField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n\nconst readOnlyTextareaClass =\n \"tpl:w-full tpl:resize-y tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none tpl:opacity-60 tpl:cursor-not-allowed\";\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <textarea\n v-if=\"readOnly\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n rows=\"3\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n :class=\"readOnlyTextareaClass\"\n />\n <MergeTagTextarea\n v-else\n :model-value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../../composables/useI18n\";\nimport type { CustomBlockTextareaField } from \"@templatical/types\";\nimport FieldWrapper from \"./FieldWrapper.vue\";\nimport MergeTagTextarea from \"../../MergeTagTextarea.vue\";\n\ndefineProps<{\n field: CustomBlockTextareaField;\n modelValue: string;\n readOnly?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst { t } = useI18n();\n\nconst readOnlyTextareaClass =\n \"tpl:w-full tpl:resize-y tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:text-sm tpl:text-[var(--tpl-text)] tpl:outline-none tpl:opacity-60 tpl:cursor-not-allowed\";\n</script>\n\n<template>\n <FieldWrapper\n :label=\"field.label\"\n :required=\"field.required\"\n :read-only=\"readOnly\"\n >\n <textarea\n v-if=\"readOnly\"\n :value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n rows=\"3\"\n disabled\n :title=\"t.customBlocks.dataSource.readOnlyTooltip\"\n :class=\"readOnlyTextareaClass\"\n />\n <MergeTagTextarea\n v-else\n :model-value=\"modelValue\"\n :placeholder=\"field.placeholder\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n </FieldWrapper>\n</template>\n","import type { CustomBlockFieldType } from \"@templatical/types\";\nimport type { Component } from \"vue\";\nimport BooleanField from \"./BooleanField.vue\";\nimport ColorField from \"./ColorField.vue\";\nimport ImageField from \"./ImageField.vue\";\nimport NumberField from \"./NumberField.vue\";\nimport RepeatableField from \"./RepeatableField.vue\";\nimport SelectField from \"./SelectField.vue\";\nimport TextField from \"./TextField.vue\";\nimport TextareaField from \"./TextareaField.vue\";\n\nexport const fieldComponentMap: Record<CustomBlockFieldType, Component> = {\n text: TextField,\n textarea: TextareaField,\n image: ImageField,\n color: ColorField,\n number: NumberField,\n select: SelectField,\n boolean: BooleanField,\n repeatable: RepeatableField,\n};\n\nexport function resolveFieldComponent(type: CustomBlockFieldType): Component {\n return fieldComponentMap[type] ?? TextField;\n}\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { useDataSourceFetch } from \"@templatical/core\";\nimport type { CustomBlock, CustomBlockField } from \"@templatical/types\";\nimport { CircleAlert, RefreshCw } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { CUSTOM_BLOCK_DEFINITIONS_KEY } from \"../../keys\";\nimport { resolveFieldComponent } from \"./fields\";\n\nconst props = defineProps<{\n block: CustomBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"updateFieldValues\", values: Record<string, unknown>): void;\n (e: \"updateDataSourceFetched\", fetched: boolean): void;\n}>();\n\nconst { t } = useI18n();\n\nconst customBlockDefinitions = inject(CUSTOM_BLOCK_DEFINITIONS_KEY, []);\n\nconst definition = computed(() =>\n customBlockDefinitions.find((d) => d.type === props.block.customType),\n);\n\nconst blockRef = computed(() => props.block);\n\nconst {\n isFetching,\n fetchError,\n fetch: fetchData,\n hasDataSource,\n needsFetch,\n} = useDataSourceFetch({\n definition,\n block: blockRef,\n onUpdate: (fieldValues, fetched) => {\n emit(\"updateFieldValues\", fieldValues);\n emit(\"updateDataSourceFetched\", fetched);\n },\n});\n\nfunction isFieldReadOnly(field: CustomBlockField): boolean {\n return (\n field.readOnly === true &&\n hasDataSource.value &&\n !!props.block.dataSourceFetched\n );\n}\n\nfunction updateField(key: string, value: unknown): void {\n emit(\"updateFieldValues\", {\n ...props.block.fieldValues,\n [key]: value,\n });\n}\n</script>\n\n<template>\n <div v-if=\"!definition\" class=\"tpl:p-4\">\n <p\n class=\"tpl:m-0 tpl:text-center tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.customBlocks.toolbar.noDefinition }}\n </p>\n </div>\n\n <div v-else>\n <p\n v-if=\"definition.description\"\n class=\"tpl:m-0 tpl:mb-3 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ definition.description }}\n </p>\n\n <div v-if=\"hasDataSource\" class=\"tpl:mb-4\">\n <!-- Before fetch: prominent button -->\n <button\n v-if=\"needsFetch && !isFetching\"\n type=\"button\"\n class=\"tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-2 tpl:rounded-md tpl:px-3 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-bg)] tpl:transition-all tpl:duration-150 tpl:bg-[var(--tpl-primary)]\"\n @click=\"fetchData\"\n >\n {{\n definition?.dataSource?.label || t.customBlocks.dataSource.fetchButton\n }}\n </button>\n\n <!-- Loading / Change button -->\n <div v-else class=\"tpl:flex tpl:h-[32px] tpl:items-center\">\n <div\n v-if=\"isFetching\"\n class=\"tpl:w-full tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.customBlocks.dataSource.fetching }}\n </div>\n <button\n v-else\n type=\"button\"\n class=\"tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-150 tpl:hover:border-[var(--tpl-primary)] tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"fetchData\"\n >\n <RefreshCw :size=\"12\" />\n {{ t.customBlocks.dataSource.changeButton }}\n </button>\n </div>\n\n <p\n v-if=\"fetchError\"\n class=\"tpl:m-0 tpl:mt-2 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n <CircleAlert :size=\"14\" class=\"tpl:shrink-0\" />\n {{ t.customBlocks.dataSource.fetchError }}\n </p>\n </div>\n\n <template v-for=\"field in definition.fields\" :key=\"field.key\">\n <component\n :is=\"resolveFieldComponent(field.type)\"\n :field=\"field\"\n :model-value=\"block.fieldValues[field.key]\"\n :read-only=\"isFieldReadOnly(field)\"\n @update:model-value=\"updateField(field.key, $event)\"\n />\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { useDataSourceFetch } from \"@templatical/core\";\nimport type { CustomBlock, CustomBlockField } from \"@templatical/types\";\nimport { CircleAlert, RefreshCw } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { CUSTOM_BLOCK_DEFINITIONS_KEY } from \"../../keys\";\nimport { resolveFieldComponent } from \"./fields\";\n\nconst props = defineProps<{\n block: CustomBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"updateFieldValues\", values: Record<string, unknown>): void;\n (e: \"updateDataSourceFetched\", fetched: boolean): void;\n}>();\n\nconst { t } = useI18n();\n\nconst customBlockDefinitions = inject(CUSTOM_BLOCK_DEFINITIONS_KEY, []);\n\nconst definition = computed(() =>\n customBlockDefinitions.find((d) => d.type === props.block.customType),\n);\n\nconst blockRef = computed(() => props.block);\n\nconst {\n isFetching,\n fetchError,\n fetch: fetchData,\n hasDataSource,\n needsFetch,\n} = useDataSourceFetch({\n definition,\n block: blockRef,\n onUpdate: (fieldValues, fetched) => {\n emit(\"updateFieldValues\", fieldValues);\n emit(\"updateDataSourceFetched\", fetched);\n },\n});\n\nfunction isFieldReadOnly(field: CustomBlockField): boolean {\n return (\n field.readOnly === true &&\n hasDataSource.value &&\n !!props.block.dataSourceFetched\n );\n}\n\nfunction updateField(key: string, value: unknown): void {\n emit(\"updateFieldValues\", {\n ...props.block.fieldValues,\n [key]: value,\n });\n}\n</script>\n\n<template>\n <div v-if=\"!definition\" class=\"tpl:p-4\">\n <p\n class=\"tpl:m-0 tpl:text-center tpl:text-sm tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.customBlocks.toolbar.noDefinition }}\n </p>\n </div>\n\n <div v-else>\n <p\n v-if=\"definition.description\"\n class=\"tpl:m-0 tpl:mb-3 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ definition.description }}\n </p>\n\n <div v-if=\"hasDataSource\" class=\"tpl:mb-4\">\n <!-- Before fetch: prominent button -->\n <button\n v-if=\"needsFetch && !isFetching\"\n type=\"button\"\n class=\"tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-2 tpl:rounded-md tpl:px-3 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-bg)] tpl:transition-all tpl:duration-150 tpl:bg-[var(--tpl-primary)]\"\n @click=\"fetchData\"\n >\n {{\n definition?.dataSource?.label || t.customBlocks.dataSource.fetchButton\n }}\n </button>\n\n <!-- Loading / Change button -->\n <div v-else class=\"tpl:flex tpl:h-[32px] tpl:items-center\">\n <div\n v-if=\"isFetching\"\n class=\"tpl:w-full tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.customBlocks.dataSource.fetching }}\n </div>\n <button\n v-else\n type=\"button\"\n class=\"tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-150 tpl:hover:border-[var(--tpl-primary)] tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"fetchData\"\n >\n <RefreshCw :size=\"12\" />\n {{ t.customBlocks.dataSource.changeButton }}\n </button>\n </div>\n\n <p\n v-if=\"fetchError\"\n class=\"tpl:m-0 tpl:mt-2 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n <CircleAlert :size=\"14\" class=\"tpl:shrink-0\" />\n {{ t.customBlocks.dataSource.fetchError }}\n </p>\n </div>\n\n <template v-for=\"field in definition.fields\" :key=\"field.key\">\n <component\n :is=\"resolveFieldComponent(field.type)\"\n :field=\"field\"\n :model-value=\"block.fieldValues[field.key]\"\n :read-only=\"isFieldReadOnly(field)\"\n @update:model-value=\"updateField(field.key, $event)\"\n />\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { DividerBlock } from \"@templatical/types\";\n\ndefineProps<{\n block: DividerBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<DividerBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<DividerBlock>);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.style }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'solid', label: t.divider.solid },\n { value: 'dashed', label: t.divider.dashed },\n { value: 'dotted', label: t.divider.dotted },\n ]\"\n :model-value=\"block.lineStyle\"\n @update:model-value=\"updateField('lineStyle', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.color }}</label>\n <ColorPicker\n :model-value=\"block.color\"\n @update:model-value=\"updateField('color', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.thickness }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.thickness\"\n min=\"1\"\n max=\"10\"\n @input=\"\n updateField(\n 'thickness',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { DividerBlock } from \"@templatical/types\";\n\ndefineProps<{\n block: DividerBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<DividerBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<DividerBlock>);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.style }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'solid', label: t.divider.solid },\n { value: 'dashed', label: t.divider.dashed },\n { value: 'dotted', label: t.divider.dotted },\n ]\"\n :model-value=\"block.lineStyle\"\n @update:model-value=\"updateField('lineStyle', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.color }}</label>\n <ColorPicker\n :model-value=\"block.color\"\n @update:model-value=\"updateField('color', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.divider.thickness }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.thickness\"\n min=\"1\"\n max=\"10\"\n @input=\"\n updateField(\n 'thickness',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { labelClass, monoTextareaClass } from \"../../constants/styleConstants\";\nimport type { HtmlBlock } from \"@templatical/types\";\nimport { Info } from \"@lucide/vue\";\n\ndefineProps<{\n block: HtmlBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<HtmlBlock>): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.html.content }}</label>\n <textarea\n :value=\"block.content\"\n :placeholder=\"'<div>...</div>'\"\n rows=\"10\"\n :class=\"monoTextareaClass\"\n @input=\"\n emit('update', {\n content: ($event.target as HTMLTextAreaElement).value,\n })\n \"\n />\n <p\n class=\"tpl:mt-1.5 tpl:flex tpl:items-start tpl:gap-1.5 tpl:text-[11px] tpl:text-[var(--tpl-text-dim)]\"\n >\n <Info :size=\"12\" class=\"tpl:mt-0.5 tpl:shrink-0\" />\n {{ t.html.sanitizationHint }}\n </p>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { labelClass, monoTextareaClass } from \"../../constants/styleConstants\";\nimport type { HtmlBlock } from \"@templatical/types\";\nimport { Info } from \"@lucide/vue\";\n\ndefineProps<{\n block: HtmlBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<HtmlBlock>): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.html.content }}</label>\n <textarea\n :value=\"block.content\"\n :placeholder=\"'<div>...</div>'\"\n rows=\"10\"\n :class=\"monoTextareaClass\"\n @input=\"\n emit('update', {\n content: ($event.target as HTMLTextAreaElement).value,\n })\n \"\n />\n <p\n class=\"tpl:mt-1.5 tpl:flex tpl:items-start tpl:gap-1.5 tpl:text-[11px] tpl:text-[var(--tpl-text-dim)]\"\n >\n <Info :size=\"12\" class=\"tpl:mt-0.5 tpl:shrink-0\" />\n {{ t.html.sanitizationHint }}\n </p>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { ImageBlock } from \"@templatical/types\";\nimport { containsMergeTag, SYNTAX_PRESETS } from \"@templatical/types\";\nimport { Image } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport { ON_REQUEST_MEDIA_KEY, MERGE_TAG_SYNTAX_KEY } from \"../../keys\";\nimport { useTimeoutFn } from \"@vueuse/core\";\n\ndefineProps<{\n block: ImageBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ImageBlock>): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst mergeTagSyntax = inject(MERGE_TAG_SYNTAX_KEY, SYNTAX_PRESETS.liquid);\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\n\nconst pulseSrc = ref(false);\nconst pulseAlt = ref(false);\n\nconst { start: startPulseSrc } = useTimeoutFn(\n () => {\n pulseSrc.value = false;\n },\n 1000,\n { immediate: false },\n);\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ImageBlock>);\n}\n\nasync function openMediaBrowser(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (result) {\n updateField(\"src\", result.url);\n if (result.alt) {\n updateField(\"alt\", result.alt);\n pulseAlt.value = true;\n }\n pulseSrc.value = true;\n startPulseSrc();\n }\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.imageUrl }}</label>\n <MergeTagInput\n :model-value=\"block.src\"\n type=\"url\"\n :placeholder=\"t.image.imageUrlPlaceholder\"\n :pulse=\"pulseSrc\"\n @update:model-value=\"updateField('src', $event)\"\n />\n <button\n v-if=\"canBrowseMedia\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150\"\n style=\"\n border-color: var(--tpl-border);\n color: var(--tpl-primary);\n background-color: var(--tpl-bg);\n \"\n @click=\"openMediaBrowser\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </div>\n <div v-if=\"containsMergeTag(block.src, mergeTagSyntax)\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\"\n >{{ t.image.placeholderUrl }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">{{\n \"(optional)\"\n }}</span>\n </label>\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.placeholderUrl || ''\"\n :placeholder=\"t.image.placeholderUrlPlaceholder\"\n @input=\"\n updateField('placeholderUrl', ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.altText }}</label>\n <MergeTagInput\n :model-value=\"block.alt\"\n type=\"text\"\n :placeholder=\"t.image.altTextPlaceholder\"\n :pulse=\"pulseAlt\"\n :disabled=\"block.decorative === true\"\n @update:model-value=\"updateField('alt', $event)\"\n />\n <label\n class=\"tpl:mt-2 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:size-3.5 tpl:cursor-pointer tpl:accent-[var(--tpl-primary)]\"\n :checked=\"block.decorative === true\"\n @change=\"\n updateField('decorative', ($event.target as HTMLInputElement).checked)\n \"\n />\n <span>\n {{ t.image.decorative }}\n <span class=\"tpl:block tpl:text-[var(--tpl-text-dim)]\">\n {{ t.image.decorativeHint }}\n </span>\n </span>\n </label>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.width\"\n @change=\"\n updateField(\n 'width',\n ($event.target as HTMLSelectElement).value === 'full'\n ? 'full'\n : Number(($event.target as HTMLSelectElement).value),\n )\n \"\n >\n <option value=\"full\">{{ t.image.fullWidth }}</option>\n <option value=\"300\">300px</option>\n <option value=\"400\">400px</option>\n <option value=\"500\">500px</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.linkUrl }}</label>\n <MergeTagInput\n :model-value=\"block.linkUrl || ''\"\n type=\"url\"\n :placeholder=\"t.image.imageUrlPlaceholder\"\n @update:model-value=\"updateField('linkUrl', $event)\"\n />\n <label\n v-if=\"block.linkUrl\"\n class=\"tpl:mt-2 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:size-3.5 tpl:cursor-pointer tpl:accent-[var(--tpl-primary)]\"\n :checked=\"block.linkOpenInNewTab ?? false\"\n @change=\"\n updateField(\n 'linkOpenInNewTab',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.image.openInNewTab }}\n </label>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { ImageBlock } from \"@templatical/types\";\nimport { containsMergeTag, SYNTAX_PRESETS } from \"@templatical/types\";\nimport { Image } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport { ON_REQUEST_MEDIA_KEY, MERGE_TAG_SYNTAX_KEY } from \"../../keys\";\nimport { useTimeoutFn } from \"@vueuse/core\";\n\ndefineProps<{\n block: ImageBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<ImageBlock>): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst mergeTagSyntax = inject(MERGE_TAG_SYNTAX_KEY, SYNTAX_PRESETS.liquid);\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\n\nconst pulseSrc = ref(false);\nconst pulseAlt = ref(false);\n\nconst { start: startPulseSrc } = useTimeoutFn(\n () => {\n pulseSrc.value = false;\n },\n 1000,\n { immediate: false },\n);\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<ImageBlock>);\n}\n\nasync function openMediaBrowser(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (result) {\n updateField(\"src\", result.url);\n if (result.alt) {\n updateField(\"alt\", result.alt);\n pulseAlt.value = true;\n }\n pulseSrc.value = true;\n startPulseSrc();\n }\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.imageUrl }}</label>\n <MergeTagInput\n :model-value=\"block.src\"\n type=\"url\"\n :placeholder=\"t.image.imageUrlPlaceholder\"\n :pulse=\"pulseSrc\"\n @update:model-value=\"updateField('src', $event)\"\n />\n <button\n v-if=\"canBrowseMedia\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150\"\n style=\"\n border-color: var(--tpl-border);\n color: var(--tpl-primary);\n background-color: var(--tpl-bg);\n \"\n @click=\"openMediaBrowser\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </div>\n <div v-if=\"containsMergeTag(block.src, mergeTagSyntax)\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\"\n >{{ t.image.placeholderUrl }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">{{\n \"(optional)\"\n }}</span>\n </label>\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.placeholderUrl || ''\"\n :placeholder=\"t.image.placeholderUrlPlaceholder\"\n @input=\"\n updateField('placeholderUrl', ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.altText }}</label>\n <MergeTagInput\n :model-value=\"block.alt\"\n type=\"text\"\n :placeholder=\"t.image.altTextPlaceholder\"\n :pulse=\"pulseAlt\"\n :disabled=\"block.decorative === true\"\n @update:model-value=\"updateField('alt', $event)\"\n />\n <label\n class=\"tpl:mt-2 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:size-3.5 tpl:cursor-pointer tpl:accent-[var(--tpl-primary)]\"\n :checked=\"block.decorative === true\"\n @change=\"\n updateField('decorative', ($event.target as HTMLInputElement).checked)\n \"\n />\n <span>\n {{ t.image.decorative }}\n <span class=\"tpl:block tpl:text-[var(--tpl-text-dim)]\">\n {{ t.image.decorativeHint }}\n </span>\n </span>\n </label>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.width\"\n @change=\"\n updateField(\n 'width',\n ($event.target as HTMLSelectElement).value === 'full'\n ? 'full'\n : Number(($event.target as HTMLSelectElement).value),\n )\n \"\n >\n <option value=\"full\">{{ t.image.fullWidth }}</option>\n <option value=\"300\">300px</option>\n <option value=\"400\">400px</option>\n <option value=\"500\">500px</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.image.linkUrl }}</label>\n <MergeTagInput\n :model-value=\"block.linkUrl || ''\"\n type=\"url\"\n :placeholder=\"t.image.imageUrlPlaceholder\"\n @update:model-value=\"updateField('linkUrl', $event)\"\n />\n <label\n v-if=\"block.linkUrl\"\n class=\"tpl:mt-2 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:size-3.5 tpl:cursor-pointer tpl:accent-[var(--tpl-primary)]\"\n :checked=\"block.linkOpenInNewTab ?? false\"\n @change=\"\n updateField(\n 'linkOpenInNewTab',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.image.openInNewTab }}\n </label>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport FieldRow from \"./FieldRow.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n labelClass,\n removeItemBtnClass,\n addItemBtnClass,\n} from \"../../constants/styleConstants\";\nimport type { MenuBlock, MenuItemData } from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Plus, X } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n block: MenuBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<MenuBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst ITEM_TOGGLES = computed(() => [\n { key: \"openInNewTab\" as const, label: t.menu.openInNewTab },\n { key: \"bold\" as const, label: t.menu.bold },\n { key: \"underline\" as const, label: t.menu.underline },\n]);\n\nconst ALIGN_OPTIONS = computed(() => [\n { value: \"left\", label: t.title.alignLeft, icon: AlignLeft },\n { value: \"center\", label: t.title.alignCenter, icon: AlignCenter },\n { value: \"right\", label: t.title.alignRight, icon: AlignRight },\n]);\n\nfunction updateField(field: keyof MenuBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<MenuBlock>);\n}\n\nfunction addMenuItem(): void {\n const newItem: MenuItemData = {\n id: generateId(),\n text: \"\",\n url: \"\",\n openInNewTab: false,\n bold: false,\n underline: false,\n };\n emit(\"update\", { items: [...props.block.items, newItem] });\n}\n\nfunction updateMenuItem(\n itemId: string,\n field: keyof MenuItemData,\n value: unknown,\n): void {\n const updatedItems = props.block.items.map((item) =>\n item.id === itemId ? { ...item, [field]: value } : item,\n );\n emit(\"update\", { items: updatedItems });\n}\n\nfunction removeMenuItem(itemId: string): void {\n emit(\"update\", {\n items: props.block.items.filter((item) => item.id !== itemId),\n });\n}\n</script>\n\n<template>\n <FieldRow :label=\"t.menu.items\">\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"item in block.items\"\n :key=\"item.id\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-2\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n class=\"tpl:flex-1\"\n :value=\"item.text\"\n :placeholder=\"t.menu.text\"\n @input=\"\n updateMenuItem(\n item.id,\n 'text',\n ($event.target as HTMLInputElement).value,\n )\n \"\n />\n <button\n :class=\"removeItemBtnClass\"\n :title=\"t.menu.removeItem\"\n @click=\"removeMenuItem(item.id)\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n <MergeTagInput\n :model-value=\"item.url\"\n type=\"url\"\n :placeholder=\"t.menu.urlPlaceholder\"\n @update:model-value=\"updateMenuItem(item.id, 'url', $event)\"\n />\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-3 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n <label\n v-for=\"toggle in ITEM_TOGGLES\"\n :key=\"toggle.key\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-1\"\n >\n <input\n type=\"checkbox\"\n :checked=\"item[toggle.key]\"\n class=\"tpl:accent-[var(--tpl-primary)]\"\n @change=\"\n updateMenuItem(\n item.id,\n toggle.key,\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ toggle.label }}\n </label>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <label :class=\"labelClass\" class=\"tpl:!mb-0\">{{\n t.menu.color\n }}</label>\n <ColorPicker\n swatch-only\n :model-value=\"item.color || block.linkColor || block.color\"\n @update:model-value=\"updateMenuItem(item.id, 'color', $event)\"\n />\n </div>\n </div>\n <button :class=\"addItemBtnClass\" @click=\"addMenuItem\">\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.menu.addItem }}\n </button>\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.menu.fontFamily\">\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </FieldRow>\n\n <FieldRow :label=\"t.menu.fontSize\">\n <NumberWithSuffix\n :model-value=\"block.fontSize\"\n :min=\"8\"\n :max=\"48\"\n suffix=\"px\"\n @update:model-value=\"updateField('fontSize', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.color\">\n <ColorPicker\n :model-value=\"block.color\"\n @update:model-value=\"updateField('color', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.linkColor\">\n <ColorPicker\n :model-value=\"block.linkColor || block.color\"\n @update:model-value=\"updateField('linkColor', $event || undefined)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.textAlign\">\n <SlidingPillSelect\n :options=\"ALIGN_OPTIONS\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.separator\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.separator\"\n @input=\"\n updateField('separator', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.separatorColor\">\n <ColorPicker\n :model-value=\"block.separatorColor\"\n @update:model-value=\"updateField('separatorColor', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.spacing\">\n <NumberWithSuffix\n :model-value=\"block.spacing\"\n :min=\"0\"\n :max=\"50\"\n suffix=\"px\"\n @update:model-value=\"updateField('spacing', $event)\"\n />\n </FieldRow>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport FieldRow from \"./FieldRow.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n labelClass,\n removeItemBtnClass,\n addItemBtnClass,\n} from \"../../constants/styleConstants\";\nimport type { MenuBlock, MenuItemData } from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Plus, X } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n block: MenuBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<MenuBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst ITEM_TOGGLES = computed(() => [\n { key: \"openInNewTab\" as const, label: t.menu.openInNewTab },\n { key: \"bold\" as const, label: t.menu.bold },\n { key: \"underline\" as const, label: t.menu.underline },\n]);\n\nconst ALIGN_OPTIONS = computed(() => [\n { value: \"left\", label: t.title.alignLeft, icon: AlignLeft },\n { value: \"center\", label: t.title.alignCenter, icon: AlignCenter },\n { value: \"right\", label: t.title.alignRight, icon: AlignRight },\n]);\n\nfunction updateField(field: keyof MenuBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<MenuBlock>);\n}\n\nfunction addMenuItem(): void {\n const newItem: MenuItemData = {\n id: generateId(),\n text: \"\",\n url: \"\",\n openInNewTab: false,\n bold: false,\n underline: false,\n };\n emit(\"update\", { items: [...props.block.items, newItem] });\n}\n\nfunction updateMenuItem(\n itemId: string,\n field: keyof MenuItemData,\n value: unknown,\n): void {\n const updatedItems = props.block.items.map((item) =>\n item.id === itemId ? { ...item, [field]: value } : item,\n );\n emit(\"update\", { items: updatedItems });\n}\n\nfunction removeMenuItem(itemId: string): void {\n emit(\"update\", {\n items: props.block.items.filter((item) => item.id !== itemId),\n });\n}\n</script>\n\n<template>\n <FieldRow :label=\"t.menu.items\">\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"item in block.items\"\n :key=\"item.id\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-2\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n class=\"tpl:flex-1\"\n :value=\"item.text\"\n :placeholder=\"t.menu.text\"\n @input=\"\n updateMenuItem(\n item.id,\n 'text',\n ($event.target as HTMLInputElement).value,\n )\n \"\n />\n <button\n :class=\"removeItemBtnClass\"\n :title=\"t.menu.removeItem\"\n @click=\"removeMenuItem(item.id)\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n <MergeTagInput\n :model-value=\"item.url\"\n type=\"url\"\n :placeholder=\"t.menu.urlPlaceholder\"\n @update:model-value=\"updateMenuItem(item.id, 'url', $event)\"\n />\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-3 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n <label\n v-for=\"toggle in ITEM_TOGGLES\"\n :key=\"toggle.key\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-1\"\n >\n <input\n type=\"checkbox\"\n :checked=\"item[toggle.key]\"\n class=\"tpl:accent-[var(--tpl-primary)]\"\n @change=\"\n updateMenuItem(\n item.id,\n toggle.key,\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ toggle.label }}\n </label>\n </div>\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <label :class=\"labelClass\" class=\"tpl:!mb-0\">{{\n t.menu.color\n }}</label>\n <ColorPicker\n swatch-only\n :model-value=\"item.color || block.linkColor || block.color\"\n @update:model-value=\"updateMenuItem(item.id, 'color', $event)\"\n />\n </div>\n </div>\n <button :class=\"addItemBtnClass\" @click=\"addMenuItem\">\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.menu.addItem }}\n </button>\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.menu.fontFamily\">\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </FieldRow>\n\n <FieldRow :label=\"t.menu.fontSize\">\n <NumberWithSuffix\n :model-value=\"block.fontSize\"\n :min=\"8\"\n :max=\"48\"\n suffix=\"px\"\n @update:model-value=\"updateField('fontSize', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.color\">\n <ColorPicker\n :model-value=\"block.color\"\n @update:model-value=\"updateField('color', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.linkColor\">\n <ColorPicker\n :model-value=\"block.linkColor || block.color\"\n @update:model-value=\"updateField('linkColor', $event || undefined)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.textAlign\">\n <SlidingPillSelect\n :options=\"ALIGN_OPTIONS\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.separator\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.separator\"\n @input=\"\n updateField('separator', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.separatorColor\">\n <ColorPicker\n :model-value=\"block.separatorColor\"\n @update:model-value=\"updateField('separatorColor', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.menu.spacing\">\n <NumberWithSuffix\n :model-value=\"block.spacing\"\n :min=\"0\"\n :max=\"50\"\n suffix=\"px\"\n @update:model-value=\"updateField('spacing', $event)\"\n />\n </FieldRow>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { ColumnLayout, SectionBlock } from \"@templatical/types\";\nimport { computed } from \"vue\";\n\ndefineProps<{\n block: SectionBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SectionBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst columnOptions = computed(() => [\n { value: \"1\" as ColumnLayout, label: t.section.column1 },\n { value: \"2\" as ColumnLayout, label: t.section.column2 },\n { value: \"3\" as ColumnLayout, label: t.section.column3 },\n { value: \"1-2\" as ColumnLayout, label: t.section.ratio12 },\n { value: \"2-1\" as ColumnLayout, label: t.section.ratio21 },\n]);\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.section.columns }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.columns\"\n @change=\"\n emit('update', {\n columns: ($event.target as HTMLSelectElement).value as ColumnLayout,\n })\n \"\n >\n <option\n v-for=\"option in columnOptions\"\n :key=\"option.value\"\n :value=\"option.value\"\n >\n {{ option.label }}\n </option>\n </select>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { ColumnLayout, SectionBlock } from \"@templatical/types\";\nimport { computed } from \"vue\";\n\ndefineProps<{\n block: SectionBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SectionBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst columnOptions = computed(() => [\n { value: \"1\" as ColumnLayout, label: t.section.column1 },\n { value: \"2\" as ColumnLayout, label: t.section.column2 },\n { value: \"3\" as ColumnLayout, label: t.section.column3 },\n { value: \"1-2\" as ColumnLayout, label: t.section.ratio12 },\n { value: \"2-1\" as ColumnLayout, label: t.section.ratio21 },\n]);\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.section.columns }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.columns\"\n @change=\"\n emit('update', {\n columns: ($event.target as HTMLSelectElement).value as ColumnLayout,\n })\n \"\n >\n <option\n v-for=\"option in columnOptions\"\n :key=\"option.value\"\n :value=\"option.value\"\n >\n {{ option.label }}\n </option>\n </select>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n removeItemBtnClass,\n addItemBtnClass,\n} from \"../../constants/styleConstants\";\nimport {\n socialIcons,\n socialPlatformOptions,\n} from \"../../constants/socialIcons\";\nimport type {\n SocialIcon,\n SocialIconsBlock,\n SocialPlatform,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Plus, X } from \"@lucide/vue\";\n\nconst props = defineProps<{\n block: SocialIconsBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SocialIconsBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<SocialIconsBlock>);\n}\n\nfunction addSocialIcon(): void {\n const newIcon: SocialIcon = {\n id: generateId(),\n platform: \"facebook\",\n url: \"\",\n };\n emit(\"update\", { icons: [...props.block.icons, newIcon] });\n}\n\nfunction updateSocialIcon(\n iconId: string,\n field: keyof SocialIcon,\n value: string,\n): void {\n const updatedIcons = props.block.icons.map((icon) =>\n icon.id === iconId ? { ...icon, [field]: value } : icon,\n );\n emit(\"update\", { icons: updatedIcons });\n}\n\nfunction removeSocialIcon(iconId: string): void {\n emit(\"update\", {\n icons: props.block.icons.filter((icon) => icon.id !== iconId),\n });\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.icons }}</label>\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"icon in block.icons\"\n :key=\"icon.id\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-2\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <select\n :class=\"inputClass\"\n class=\"tpl:flex-1\"\n :value=\"icon.platform\"\n @change=\"\n updateSocialIcon(\n icon.id,\n 'platform',\n ($event.target as HTMLSelectElement).value as SocialPlatform,\n )\n \"\n >\n <option\n v-for=\"platform in socialPlatformOptions\"\n :key=\"platform\"\n :value=\"platform\"\n >\n {{ socialIcons[platform].name }}\n </option>\n </select>\n <button\n :class=\"removeItemBtnClass\"\n :title=\"t.social.removeIcon\"\n @click=\"removeSocialIcon(icon.id)\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n <MergeTagInput\n :model-value=\"icon.url\"\n type=\"url\"\n :placeholder=\"t.social.urlPlaceholder\"\n @update:model-value=\"updateSocialIcon(icon.id, 'url', $event)\"\n />\n </div>\n <button :class=\"addItemBtnClass\" @click=\"addSocialIcon\">\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.social.addIcon }}\n </button>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.style }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'solid', label: t.social.styleSolid },\n { value: 'outlined', label: t.social.styleOutlined },\n { value: 'rounded', label: t.social.styleRounded },\n { value: 'square', label: t.social.styleSquare },\n { value: 'circle', label: t.social.styleCircle },\n ]\"\n :model-value=\"block.iconStyle\"\n @update:model-value=\"updateField('iconStyle', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.size }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'small', label: t.social.sizeSmall },\n { value: 'medium', label: t.social.sizeMedium },\n { value: 'large', label: t.social.sizeLarge },\n ]\"\n :model-value=\"block.iconSize\"\n @update:model-value=\"updateField('iconSize', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.spacing }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.spacing\"\n min=\"0\"\n max=\"50\"\n @input=\"\n updateField(\n 'spacing',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n removeItemBtnClass,\n addItemBtnClass,\n} from \"../../constants/styleConstants\";\nimport {\n socialIcons,\n socialPlatformOptions,\n} from \"../../constants/socialIcons\";\nimport type {\n SocialIcon,\n SocialIconsBlock,\n SocialPlatform,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Plus, X } from \"@lucide/vue\";\n\nconst props = defineProps<{\n block: SocialIconsBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SocialIconsBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<SocialIconsBlock>);\n}\n\nfunction addSocialIcon(): void {\n const newIcon: SocialIcon = {\n id: generateId(),\n platform: \"facebook\",\n url: \"\",\n };\n emit(\"update\", { icons: [...props.block.icons, newIcon] });\n}\n\nfunction updateSocialIcon(\n iconId: string,\n field: keyof SocialIcon,\n value: string,\n): void {\n const updatedIcons = props.block.icons.map((icon) =>\n icon.id === iconId ? { ...icon, [field]: value } : icon,\n );\n emit(\"update\", { icons: updatedIcons });\n}\n\nfunction removeSocialIcon(iconId: string): void {\n emit(\"update\", {\n icons: props.block.icons.filter((icon) => icon.id !== iconId),\n });\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.icons }}</label>\n <div class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n v-for=\"icon in block.icons\"\n :key=\"icon.id\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1.5 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:p-2\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <select\n :class=\"inputClass\"\n class=\"tpl:flex-1\"\n :value=\"icon.platform\"\n @change=\"\n updateSocialIcon(\n icon.id,\n 'platform',\n ($event.target as HTMLSelectElement).value as SocialPlatform,\n )\n \"\n >\n <option\n v-for=\"platform in socialPlatformOptions\"\n :key=\"platform\"\n :value=\"platform\"\n >\n {{ socialIcons[platform].name }}\n </option>\n </select>\n <button\n :class=\"removeItemBtnClass\"\n :title=\"t.social.removeIcon\"\n @click=\"removeSocialIcon(icon.id)\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n <MergeTagInput\n :model-value=\"icon.url\"\n type=\"url\"\n :placeholder=\"t.social.urlPlaceholder\"\n @update:model-value=\"updateSocialIcon(icon.id, 'url', $event)\"\n />\n </div>\n <button :class=\"addItemBtnClass\" @click=\"addSocialIcon\">\n <Plus :size=\"14\" :stroke-width=\"2\" />\n {{ t.social.addIcon }}\n </button>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.style }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'solid', label: t.social.styleSolid },\n { value: 'outlined', label: t.social.styleOutlined },\n { value: 'rounded', label: t.social.styleRounded },\n { value: 'square', label: t.social.styleSquare },\n { value: 'circle', label: t.social.styleCircle },\n ]\"\n :model-value=\"block.iconStyle\"\n @update:model-value=\"updateField('iconStyle', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.size }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'small', label: t.social.sizeSmall },\n { value: 'medium', label: t.social.sizeMedium },\n { value: 'large', label: t.social.sizeLarge },\n ]\"\n :model-value=\"block.iconSize\"\n @update:model-value=\"updateField('iconSize', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.spacing }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.spacing\"\n min=\"0\"\n max=\"50\"\n @input=\"\n updateField(\n 'spacing',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.social.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { SpacerBlock } from \"@templatical/types\";\n\ndefineProps<{\n block: SpacerBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SpacerBlock>): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.spacer.height }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.height\"\n min=\"10\"\n max=\"100\"\n @input=\"\n emit('update', {\n height: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n <input\n type=\"range\"\n class=\"tpl:mt-2 tpl:w-full tpl:accent-[var(--tpl-primary)]\"\n :value=\"block.height\"\n min=\"10\"\n max=\"100\"\n @input=\"\n emit('update', {\n height: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n} from \"../../constants/styleConstants\";\nimport type { SpacerBlock } from \"@templatical/types\";\n\ndefineProps<{\n block: SpacerBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<SpacerBlock>): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.spacer.height }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.height\"\n min=\"10\"\n max=\"100\"\n @input=\"\n emit('update', {\n height: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n <input\n type=\"range\"\n class=\"tpl:mt-2 tpl:w-full tpl:accent-[var(--tpl-primary)]\"\n :value=\"block.height\"\n min=\"10\"\n max=\"100\"\n @input=\"\n emit('update', {\n height: Number(($event.target as HTMLInputElement).value),\n })\n \"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n DEFAULT_TABLE_ROW_BG,\n} from \"../../constants/styleConstants\";\nimport type {\n TableBlock,\n TableCellData,\n TableRowData,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Minus, Plus } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n block: TableBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<TableBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst tableColumnCount = computed(() => {\n return props.block.rows.length > 0 ? props.block.rows[0].cells.length : 0;\n});\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<TableBlock>);\n}\n\nfunction addTableRow(): void {\n const columnCount =\n props.block.rows.length > 0 ? props.block.rows[0].cells.length : 3;\n const newRow: TableRowData = {\n id: generateId(),\n cells: Array.from(\n { length: columnCount },\n (): TableCellData => ({\n id: generateId(),\n content: \"\",\n }),\n ),\n };\n emit(\"update\", { rows: [...props.block.rows, newRow] });\n}\n\nfunction removeTableRow(rowId: string): void {\n emit(\"update\", {\n rows: props.block.rows.filter((row) => row.id !== rowId),\n });\n}\n\nfunction addTableColumn(): void {\n const updatedRows = props.block.rows.map((row) => ({\n ...row,\n cells: [...row.cells, { id: generateId(), content: \"\" } as TableCellData],\n }));\n emit(\"update\", { rows: updatedRows });\n}\n\nfunction removeTableColumn(colIndex: number): void {\n const updatedRows = props.block.rows.map((row) => ({\n ...row,\n cells: row.cells.filter((_, i) => i !== colIndex),\n }));\n emit(\"update\", { rows: updatedRows });\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.dimensions }}</label>\n <div class=\"tpl:flex tpl:items-center tpl:gap-3\">\n <div class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:gap-1.5\">\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\">{{\n t.table.rows\n }}</span>\n <div\n class=\"tpl:flex tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:duration-150 tpl:hover:text-[var(--tpl-primary)] tpl:disabled:opacity-30\"\n :disabled=\"block.rows.length <= 1\"\n @click=\"removeTableRow(block.rows[block.rows.length - 1].id)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:min-w-[20px] tpl:text-center tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >{{ block.rows.length }}</span\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:duration-150 tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"addTableRow\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n <div class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:gap-1.5\">\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\">{{\n t.table.columns\n }}</span>\n <div\n class=\"tpl:flex tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:duration-150 tpl:hover:text-[var(--tpl-primary)] tpl:disabled:opacity-30\"\n :disabled=\"tableColumnCount <= 1\"\n @click=\"removeTableColumn(tableColumnCount - 1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:min-w-[20px] tpl:text-center tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >{{ tableColumnCount }}</span\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:duration-150 tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"addTableColumn\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label\n class=\"tpl:mb-1.5 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n <input\n type=\"checkbox\"\n :checked=\"block.hasHeaderRow\"\n class=\"tpl:accent-[var(--tpl-primary)]\"\n @change=\"\n updateField(\n 'hasHeaderRow',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.table.hasHeaderRow }}\n </label>\n </div>\n <div v-if=\"block.hasHeaderRow\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.headerBackgroundColor }}</label>\n <ColorPicker\n :model-value=\"block.headerBackgroundColor || DEFAULT_TABLE_ROW_BG\"\n :placeholder=\"t.table.noHeaderBg\"\n @update:model-value=\"updateField('headerBackgroundColor', $event || null)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.borderColor }}</label>\n <ColorPicker\n :model-value=\"block.borderColor\"\n @update:model-value=\"updateField('borderColor', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.borderWidth }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.borderWidth\"\n min=\"0\"\n max=\"10\"\n @input=\"\n updateField(\n 'borderWidth',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.cellPadding }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.cellPadding\"\n min=\"0\"\n max=\"30\"\n @input=\"\n updateField(\n 'cellPadding',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.fontSize }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.fontSize\"\n min=\"10\"\n max=\"32\"\n @input=\"\n updateField(\n 'fontSize',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.color }}</label>\n <ColorPicker\n :model-value=\"block.color\"\n @update:model-value=\"updateField('color', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.textAlign }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport {\n inputClass,\n inputGroupInputClass,\n inputSuffixClass,\n labelClass,\n DEFAULT_TABLE_ROW_BG,\n} from \"../../constants/styleConstants\";\nimport type {\n TableBlock,\n TableCellData,\n TableRowData,\n} from \"@templatical/types\";\nimport { generateId } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight, Minus, Plus } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n block: TableBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<TableBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst tableColumnCount = computed(() => {\n return props.block.rows.length > 0 ? props.block.rows[0].cells.length : 0;\n});\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<TableBlock>);\n}\n\nfunction addTableRow(): void {\n const columnCount =\n props.block.rows.length > 0 ? props.block.rows[0].cells.length : 3;\n const newRow: TableRowData = {\n id: generateId(),\n cells: Array.from(\n { length: columnCount },\n (): TableCellData => ({\n id: generateId(),\n content: \"\",\n }),\n ),\n };\n emit(\"update\", { rows: [...props.block.rows, newRow] });\n}\n\nfunction removeTableRow(rowId: string): void {\n emit(\"update\", {\n rows: props.block.rows.filter((row) => row.id !== rowId),\n });\n}\n\nfunction addTableColumn(): void {\n const updatedRows = props.block.rows.map((row) => ({\n ...row,\n cells: [...row.cells, { id: generateId(), content: \"\" } as TableCellData],\n }));\n emit(\"update\", { rows: updatedRows });\n}\n\nfunction removeTableColumn(colIndex: number): void {\n const updatedRows = props.block.rows.map((row) => ({\n ...row,\n cells: row.cells.filter((_, i) => i !== colIndex),\n }));\n emit(\"update\", { rows: updatedRows });\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.dimensions }}</label>\n <div class=\"tpl:flex tpl:items-center tpl:gap-3\">\n <div class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:gap-1.5\">\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\">{{\n t.table.rows\n }}</span>\n <div\n class=\"tpl:flex tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:duration-150 tpl:hover:text-[var(--tpl-primary)] tpl:disabled:opacity-30\"\n :disabled=\"block.rows.length <= 1\"\n @click=\"removeTableRow(block.rows[block.rows.length - 1].id)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:min-w-[20px] tpl:text-center tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >{{ block.rows.length }}</span\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:duration-150 tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"addTableRow\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n <div class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:gap-1.5\">\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\">{{\n t.table.columns\n }}</span>\n <div\n class=\"tpl:flex tpl:items-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:duration-150 tpl:hover:text-[var(--tpl-primary)] tpl:disabled:opacity-30\"\n :disabled=\"tableColumnCount <= 1\"\n @click=\"removeTableColumn(tableColumnCount - 1)\"\n >\n <Minus :size=\"12\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:min-w-[20px] tpl:text-center tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n >{{ tableColumnCount }}</span\n >\n <button\n class=\"tpl:flex tpl:items-center tpl:justify-center tpl:px-1.5 tpl:py-1 tpl:text-[var(--tpl-text-muted)] tpl:transition-colors tpl:duration-150 tpl:hover:text-[var(--tpl-primary)]\"\n @click=\"addTableColumn\"\n >\n <Plus :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label\n class=\"tpl:mb-1.5 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n <input\n type=\"checkbox\"\n :checked=\"block.hasHeaderRow\"\n class=\"tpl:accent-[var(--tpl-primary)]\"\n @change=\"\n updateField(\n 'hasHeaderRow',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.table.hasHeaderRow }}\n </label>\n </div>\n <div v-if=\"block.hasHeaderRow\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.headerBackgroundColor }}</label>\n <ColorPicker\n :model-value=\"block.headerBackgroundColor || DEFAULT_TABLE_ROW_BG\"\n :placeholder=\"t.table.noHeaderBg\"\n @update:model-value=\"updateField('headerBackgroundColor', $event || null)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.borderColor }}</label>\n <ColorPicker\n :model-value=\"block.borderColor\"\n @update:model-value=\"updateField('borderColor', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.borderWidth }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.borderWidth\"\n min=\"0\"\n max=\"10\"\n @input=\"\n updateField(\n 'borderWidth',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.cellPadding }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.cellPadding\"\n min=\"0\"\n max=\"30\"\n @input=\"\n updateField(\n 'cellPadding',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.fontSize }}</label>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"block.fontSize\"\n min=\"10\"\n max=\"32\"\n @input=\"\n updateField(\n 'fontSize',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">px</span>\n </div>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.color }}</label>\n <ColorPicker\n :model-value=\"block.color\"\n @update:model-value=\"updateField('color', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.table.textAlign }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { TitleBlock } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight } from \"@lucide/vue\";\n\ndefineProps<{\n block: TitleBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<TitleBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<TitleBlock>);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.level }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.level\"\n @change=\"\n updateField('level', Number(($event.target as HTMLSelectElement).value))\n \"\n >\n <option :value=\"1\">{{ t.title.heading1 }}</option>\n <option :value=\"2\">{{ t.title.heading2 }}</option>\n <option :value=\"3\">{{ t.title.heading3 }}</option>\n <option :value=\"4\">{{ t.title.heading4 }}</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.color }}</label>\n <ColorPicker\n :model-value=\"block.color\"\n @update:model-value=\"updateField('color', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { TitleBlock } from \"@templatical/types\";\nimport { AlignCenter, AlignLeft, AlignRight } from \"@lucide/vue\";\n\ndefineProps<{\n block: TitleBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<TitleBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction updateField(field: string, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<TitleBlock>);\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.level }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.level\"\n @change=\"\n updateField('level', Number(($event.target as HTMLSelectElement).value))\n \"\n >\n <option :value=\"1\">{{ t.title.heading1 }}</option>\n <option :value=\"2\">{{ t.title.heading2 }}</option>\n <option :value=\"3\">{{ t.title.heading3 }}</option>\n <option :value=\"4\">{{ t.title.heading4 }}</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.fontFamily }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.title.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.color }}</label>\n <ColorPicker\n :model-value=\"block.color\"\n @update:model-value=\"updateField('color', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft, icon: AlignLeft },\n { value: 'center', label: t.title.alignCenter, icon: AlignCenter },\n { value: 'right', label: t.title.alignRight, icon: AlignRight },\n ]\"\n :model-value=\"block.textAlign\"\n @update:model-value=\"updateField('textAlign', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { VideoBlock } from \"@templatical/types\";\nimport { containsMergeTag, SYNTAX_PRESETS } from \"@templatical/types\";\nimport { Image } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport { ON_REQUEST_MEDIA_KEY, MERGE_TAG_SYNTAX_KEY } from \"../../keys\";\nimport { useTimeoutFn } from \"@vueuse/core\";\n\nconst props = defineProps<{\n block: VideoBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<VideoBlock>): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst mergeTagSyntax = inject(MERGE_TAG_SYNTAX_KEY, SYNTAX_PRESETS.liquid);\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\nconst urlHasMergeTag = computed(() =>\n containsMergeTag(props.block.url, mergeTagSyntax),\n);\n\nconst pulseThumbnail = ref(false);\nconst { start: startPulseThumbnail } = useTimeoutFn(\n () => {\n pulseThumbnail.value = false;\n },\n 1000,\n { immediate: false },\n);\n\nfunction updateField(field: keyof VideoBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<VideoBlock>);\n}\n\nasync function openMediaBrowser(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (result) {\n updateField(\"thumbnailUrl\", result.url);\n pulseThumbnail.value = true;\n startPulseThumbnail();\n }\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.videoUrl }}</label>\n <MergeTagInput\n :model-value=\"block.url\"\n type=\"url\"\n :placeholder=\"t.video.videoUrlPlaceholder\"\n @update:model-value=\"updateField('url', $event)\"\n />\n <label\n v-if=\"block.url\"\n class=\"tpl:mt-2 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:size-3.5 tpl:cursor-pointer tpl:accent-[var(--tpl-primary)]\"\n :checked=\"block.openInNewTab ?? false\"\n @change=\"\n updateField(\n 'openInNewTab',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.video.openInNewTab }}\n </label>\n </div>\n <div v-if=\"urlHasMergeTag\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ t.video.placeholderUrl }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.video.optional }}\n </span>\n </label>\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.placeholderUrl || ''\"\n :placeholder=\"t.video.placeholderUrlPlaceholder\"\n :title=\"t.video.placeholderUrlTooltip\"\n @input=\"\n updateField('placeholderUrl', ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ t.video.customThumbnail }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.video.optional }}\n </span>\n </label>\n <MergeTagInput\n :model-value=\"block.thumbnailUrl\"\n type=\"url\"\n :placeholder=\"t.video.thumbnailPlaceholder\"\n :pulse=\"pulseThumbnail\"\n @update:model-value=\"updateField('thumbnailUrl', $event)\"\n />\n <button\n v-if=\"canBrowseMedia\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150\"\n style=\"\n border-color: var(--tpl-border);\n color: var(--tpl-primary);\n background-color: var(--tpl-bg);\n \"\n @click=\"openMediaBrowser\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.altText }}</label>\n <MergeTagInput\n :model-value=\"block.alt\"\n type=\"text\"\n :placeholder=\"t.video.altTextPlaceholder\"\n @update:model-value=\"updateField('alt', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.width\"\n @change=\"\n updateField(\n 'width',\n ($event.target as HTMLSelectElement).value === 'full'\n ? 'full'\n : Number(($event.target as HTMLSelectElement).value),\n )\n \"\n >\n <option value=\"full\">{{ t.video.fullWidth }}</option>\n <option value=\"300\">300px</option>\n <option value=\"400\">400px</option>\n <option value=\"500\">500px</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport MergeTagInput from \"../MergeTagInput.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass, labelClass } from \"../../constants/styleConstants\";\nimport type { VideoBlock } from \"@templatical/types\";\nimport { containsMergeTag, SYNTAX_PRESETS } from \"@templatical/types\";\nimport { Image } from \"@lucide/vue\";\nimport { computed, inject, ref } from \"vue\";\nimport { ON_REQUEST_MEDIA_KEY, MERGE_TAG_SYNTAX_KEY } from \"../../keys\";\nimport { useTimeoutFn } from \"@vueuse/core\";\n\nconst props = defineProps<{\n block: VideoBlock;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<VideoBlock>): void;\n}>();\n\nconst { t } = useI18n();\nconst onRequestMedia = inject(ON_REQUEST_MEDIA_KEY, null);\nconst mergeTagSyntax = inject(MERGE_TAG_SYNTAX_KEY, SYNTAX_PRESETS.liquid);\n\nconst canBrowseMedia = computed(() => !!onRequestMedia);\nconst urlHasMergeTag = computed(() =>\n containsMergeTag(props.block.url, mergeTagSyntax),\n);\n\nconst pulseThumbnail = ref(false);\nconst { start: startPulseThumbnail } = useTimeoutFn(\n () => {\n pulseThumbnail.value = false;\n },\n 1000,\n { immediate: false },\n);\n\nfunction updateField(field: keyof VideoBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<VideoBlock>);\n}\n\nasync function openMediaBrowser(): Promise<void> {\n const result = await onRequestMedia?.({ accept: [\"images\"] });\n if (result) {\n updateField(\"thumbnailUrl\", result.url);\n pulseThumbnail.value = true;\n startPulseThumbnail();\n }\n}\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.videoUrl }}</label>\n <MergeTagInput\n :model-value=\"block.url\"\n type=\"url\"\n :placeholder=\"t.video.videoUrlPlaceholder\"\n @update:model-value=\"updateField('url', $event)\"\n />\n <label\n v-if=\"block.url\"\n class=\"tpl:mt-2 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-2 tpl:text-[12px] tpl:text-[var(--tpl-text-muted)]\"\n >\n <input\n type=\"checkbox\"\n class=\"tpl:size-3.5 tpl:cursor-pointer tpl:accent-[var(--tpl-primary)]\"\n :checked=\"block.openInNewTab ?? false\"\n @change=\"\n updateField(\n 'openInNewTab',\n ($event.target as HTMLInputElement).checked,\n )\n \"\n />\n {{ t.video.openInNewTab }}\n </label>\n </div>\n <div v-if=\"urlHasMergeTag\" class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ t.video.placeholderUrl }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.video.optional }}\n </span>\n </label>\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.placeholderUrl || ''\"\n :placeholder=\"t.video.placeholderUrlPlaceholder\"\n :title=\"t.video.placeholderUrlTooltip\"\n @input=\"\n updateField('placeholderUrl', ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">\n {{ t.video.customThumbnail }}\n <span class=\"tpl:font-normal tpl:text-[var(--tpl-text-dim)]\">\n {{ t.video.optional }}\n </span>\n </label>\n <MergeTagInput\n :model-value=\"block.thumbnailUrl\"\n type=\"url\"\n :placeholder=\"t.video.thumbnailPlaceholder\"\n :pulse=\"pulseThumbnail\"\n @update:model-value=\"updateField('thumbnailUrl', $event)\"\n />\n <button\n v-if=\"canBrowseMedia\"\n class=\"tpl:mt-2 tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150\"\n style=\"\n border-color: var(--tpl-border);\n color: var(--tpl-primary);\n background-color: var(--tpl-bg);\n \"\n @click=\"openMediaBrowser\"\n >\n <Image :size=\"14\" :stroke-width=\"1.5\" />\n {{ t.image.browseMedia }}\n </button>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.altText }}</label>\n <MergeTagInput\n :model-value=\"block.alt\"\n type=\"text\"\n :placeholder=\"t.video.altTextPlaceholder\"\n @update:model-value=\"updateField('alt', $event)\"\n />\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.video.width }}</label>\n <select\n :class=\"inputClass\"\n :value=\"block.width\"\n @change=\"\n updateField(\n 'width',\n ($event.target as HTMLSelectElement).value === 'full'\n ? 'full'\n : Number(($event.target as HTMLSelectElement).value),\n )\n \"\n >\n <option value=\"full\">{{ t.video.fullWidth }}</option>\n <option value=\"300\">300px</option>\n <option value=\"400\">400px</option>\n <option value=\"500\">500px</option>\n </select>\n </div>\n <div class=\"tpl:mb-3.5\">\n <label :class=\"labelClass\">{{ t.title.align }}</label>\n <SlidingPillSelect\n :options=\"[\n { value: 'left', label: t.title.alignLeft },\n { value: 'center', label: t.title.alignCenter },\n { value: 'right', label: t.title.alignRight },\n ]\"\n :model-value=\"block.align\"\n @update:model-value=\"updateField('align', $event)\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { defineAsyncComponent } from \"vue\";\nimport ButtonToolbar from \"./toolbar/ButtonToolbar.vue\";\nimport CommonBlockSettings from \"./toolbar/CommonBlockSettings.vue\";\nconst CountdownToolbar = defineAsyncComponent(\n () => import(\"./toolbar/CountdownToolbar.vue\"),\n);\nimport CustomBlockToolbar from \"./toolbar/CustomBlockToolbar.vue\";\nimport DividerToolbar from \"./toolbar/DividerToolbar.vue\";\nimport HtmlToolbar from \"./toolbar/HtmlToolbar.vue\";\nimport ImageToolbar from \"./toolbar/ImageToolbar.vue\";\nimport MenuToolbar from \"./toolbar/MenuToolbar.vue\";\nimport SectionToolbar from \"./toolbar/SectionToolbar.vue\";\nimport SocialToolbar from \"./toolbar/SocialToolbar.vue\";\nimport SpacerToolbar from \"./toolbar/SpacerToolbar.vue\";\nimport TableToolbar from \"./toolbar/TableToolbar.vue\";\nimport TitleToolbar from \"./toolbar/TitleToolbar.vue\";\nimport VideoToolbar from \"./toolbar/VideoToolbar.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type {\n Block,\n ButtonBlock,\n CountdownBlock,\n CustomBlock,\n DividerBlock,\n HtmlBlock,\n ImageBlock,\n MenuBlock,\n SectionBlock,\n SocialIconsBlock,\n SpacerBlock,\n TableBlock,\n TitleBlock,\n VideoBlock,\n} from \"@templatical/types\";\nimport { isCustomBlock } from \"@templatical/types\";\nimport { Code, Copy, Trash2 } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { blockTypeIcons } from \"../utils/blockTypeIcons\";\nimport { getBlockTypeLabel } from \"../utils/blockTypeLabels\";\nimport {\n FONTS_MANAGER_KEY,\n CUSTOM_BLOCK_DEFINITIONS_KEY,\n requireInject,\n} from \"../keys\";\n\nconst props = defineProps<{\n block: Block;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<Block>): void;\n (e: \"delete\"): void;\n (e: \"duplicate\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst fontsManager = requireInject(FONTS_MANAGER_KEY, \"Toolbar\");\nconst customBlockDefinitions = inject(CUSTOM_BLOCK_DEFINITIONS_KEY, []);\n\nconst blockType = computed(() => props.block.type);\n\nconst isCustom = computed(() => isCustomBlock(props.block));\n\nconst customBlockDefinition = computed(() => {\n if (!isCustom.value) {\n return undefined;\n }\n return customBlockDefinitions.find(\n (d) => d.type === (props.block as CustomBlock).customType,\n );\n});\n\nconst blockTypeLabel = computed(() => {\n if (isCustom.value) {\n return (\n customBlockDefinition.value?.name ??\n (props.block as CustomBlock).customType\n );\n }\n\n return getBlockTypeLabel(blockType.value, t);\n});\n\n// Font families from shared fontsManager (provided by Editor.vue / CloudEditor.vue)\nconst fontFamilies = fontsManager.fonts;\n\nfunction handleUpdate(updates: Partial<Block>): void {\n emit(\"update\", updates);\n}\n</script>\n\n<template>\n <aside\n :aria-label=\"t.landmarks.blockToolbar\"\n class=\"tpl:flex tpl:w-full tpl:flex-1 tpl:flex-col tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-3.5\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-2 tpl:text-[var(--tpl-primary)]\"\n >\n <component\n :is=\"blockTypeIcons[blockType]\"\n v-if=\"blockTypeIcons[blockType]\"\n :size=\"16\"\n :stroke-width=\"1.5\"\n />\n <Code v-else-if=\"isCustom\" :size=\"16\" :stroke-width=\"1.5\" />\n <h3\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ blockTypeLabel }}\n </h3>\n </div>\n <div class=\"tpl:flex tpl:gap-1\">\n <button\n class=\"tpl:flex tpl:size-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)] tpl:hover:text-[var(--tpl-text)]\"\n :title=\"t.toolbar.duplicate\"\n @click=\"emit('duplicate')\"\n >\n <Copy :size=\"14\" :stroke-width=\"2\" />\n </button>\n <button\n class=\"tpl:flex tpl:size-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-150 tpl:hover:border-[var(--tpl-danger)] tpl:hover:bg-[var(--tpl-danger-light)] tpl:hover:text-[var(--tpl-danger)]\"\n :title=\"t.toolbar.delete\"\n @click=\"emit('delete')\"\n >\n <Trash2 :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <template v-if=\"isCustom\">\n <CustomBlockToolbar\n :block=\"block as CustomBlock\"\n @update-field-values=\"emit('update', { fieldValues: $event })\"\n @update-data-source-fetched=\"\n emit('update', { dataSourceFetched: $event })\n \"\n />\n </template>\n\n <SectionToolbar\n v-else-if=\"blockType === 'section'\"\n :block=\"block as SectionBlock\"\n @update=\"handleUpdate\"\n />\n\n <TitleToolbar\n v-else-if=\"blockType === 'title'\"\n :block=\"block as TitleBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <!-- Paragraph block: no text-specific sidebar controls — all formatting is in the TipTap toolbar -->\n <template v-else-if=\"blockType === 'paragraph'\" />\n\n <ImageToolbar\n v-else-if=\"blockType === 'image'\"\n :block=\"block as ImageBlock\"\n @update=\"handleUpdate\"\n />\n\n <VideoToolbar\n v-else-if=\"blockType === 'video'\"\n :block=\"block as VideoBlock\"\n @update=\"handleUpdate\"\n />\n\n <ButtonToolbar\n v-else-if=\"blockType === 'button'\"\n :block=\"block as ButtonBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <DividerToolbar\n v-else-if=\"blockType === 'divider'\"\n :block=\"block as DividerBlock\"\n @update=\"handleUpdate\"\n />\n\n <SocialToolbar\n v-else-if=\"blockType === 'social'\"\n :block=\"block as SocialIconsBlock\"\n @update=\"handleUpdate\"\n />\n\n <MenuToolbar\n v-else-if=\"blockType === 'menu'\"\n :block=\"block as MenuBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <TableToolbar\n v-else-if=\"blockType === 'table'\"\n :block=\"block as TableBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <SpacerToolbar\n v-else-if=\"blockType === 'spacer'\"\n :block=\"block as SpacerBlock\"\n @update=\"handleUpdate\"\n />\n\n <HtmlToolbar\n v-else-if=\"blockType === 'html'\"\n :block=\"block as HtmlBlock\"\n @update=\"handleUpdate\"\n />\n\n <CountdownToolbar\n v-else-if=\"blockType === 'countdown'\"\n :block=\"block as CountdownBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <!-- Common block settings -->\n <CommonBlockSettings\n :block=\"block\"\n :is-first-section=\"blockType === 'paragraph'\"\n @update=\"handleUpdate\"\n />\n </div>\n </aside>\n</template>\n\n<style scoped>\n.tpl-collapsible {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-collapsible--open {\n grid-template-rows: 1fr;\n}\n\n.tpl-collapsible > div {\n overflow: hidden;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { defineAsyncComponent } from \"vue\";\nimport ButtonToolbar from \"./toolbar/ButtonToolbar.vue\";\nimport CommonBlockSettings from \"./toolbar/CommonBlockSettings.vue\";\nconst CountdownToolbar = defineAsyncComponent(\n () => import(\"./toolbar/CountdownToolbar.vue\"),\n);\nimport CustomBlockToolbar from \"./toolbar/CustomBlockToolbar.vue\";\nimport DividerToolbar from \"./toolbar/DividerToolbar.vue\";\nimport HtmlToolbar from \"./toolbar/HtmlToolbar.vue\";\nimport ImageToolbar from \"./toolbar/ImageToolbar.vue\";\nimport MenuToolbar from \"./toolbar/MenuToolbar.vue\";\nimport SectionToolbar from \"./toolbar/SectionToolbar.vue\";\nimport SocialToolbar from \"./toolbar/SocialToolbar.vue\";\nimport SpacerToolbar from \"./toolbar/SpacerToolbar.vue\";\nimport TableToolbar from \"./toolbar/TableToolbar.vue\";\nimport TitleToolbar from \"./toolbar/TitleToolbar.vue\";\nimport VideoToolbar from \"./toolbar/VideoToolbar.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport type {\n Block,\n ButtonBlock,\n CountdownBlock,\n CustomBlock,\n DividerBlock,\n HtmlBlock,\n ImageBlock,\n MenuBlock,\n SectionBlock,\n SocialIconsBlock,\n SpacerBlock,\n TableBlock,\n TitleBlock,\n VideoBlock,\n} from \"@templatical/types\";\nimport { isCustomBlock } from \"@templatical/types\";\nimport { Code, Copy, Trash2 } from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport { blockTypeIcons } from \"../utils/blockTypeIcons\";\nimport { getBlockTypeLabel } from \"../utils/blockTypeLabels\";\nimport {\n FONTS_MANAGER_KEY,\n CUSTOM_BLOCK_DEFINITIONS_KEY,\n requireInject,\n} from \"../keys\";\n\nconst props = defineProps<{\n block: Block;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<Block>): void;\n (e: \"delete\"): void;\n (e: \"duplicate\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst fontsManager = requireInject(FONTS_MANAGER_KEY, \"Toolbar\");\nconst customBlockDefinitions = inject(CUSTOM_BLOCK_DEFINITIONS_KEY, []);\n\nconst blockType = computed(() => props.block.type);\n\nconst isCustom = computed(() => isCustomBlock(props.block));\n\nconst customBlockDefinition = computed(() => {\n if (!isCustom.value) {\n return undefined;\n }\n return customBlockDefinitions.find(\n (d) => d.type === (props.block as CustomBlock).customType,\n );\n});\n\nconst blockTypeLabel = computed(() => {\n if (isCustom.value) {\n return (\n customBlockDefinition.value?.name ??\n (props.block as CustomBlock).customType\n );\n }\n\n return getBlockTypeLabel(blockType.value, t);\n});\n\n// Font families from shared fontsManager (provided by Editor.vue / CloudEditor.vue)\nconst fontFamilies = fontsManager.fonts;\n\nfunction handleUpdate(updates: Partial<Block>): void {\n emit(\"update\", updates);\n}\n</script>\n\n<template>\n <aside\n :aria-label=\"t.landmarks.blockToolbar\"\n class=\"tpl:flex tpl:w-full tpl:flex-1 tpl:flex-col tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-3.5\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-2 tpl:text-[var(--tpl-primary)]\"\n >\n <component\n :is=\"blockTypeIcons[blockType]\"\n v-if=\"blockTypeIcons[blockType]\"\n :size=\"16\"\n :stroke-width=\"1.5\"\n />\n <Code v-else-if=\"isCustom\" :size=\"16\" :stroke-width=\"1.5\" />\n <h3\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ blockTypeLabel }}\n </h3>\n </div>\n <div class=\"tpl:flex tpl:gap-1\">\n <button\n class=\"tpl:flex tpl:size-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)] tpl:hover:text-[var(--tpl-text)]\"\n :title=\"t.toolbar.duplicate\"\n @click=\"emit('duplicate')\"\n >\n <Copy :size=\"14\" :stroke-width=\"2\" />\n </button>\n <button\n class=\"tpl:flex tpl:size-7 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-150 tpl:hover:border-[var(--tpl-danger)] tpl:hover:bg-[var(--tpl-danger-light)] tpl:hover:text-[var(--tpl-danger)]\"\n :title=\"t.toolbar.delete\"\n @click=\"emit('delete')\"\n >\n <Trash2 :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <template v-if=\"isCustom\">\n <CustomBlockToolbar\n :block=\"block as CustomBlock\"\n @update-field-values=\"emit('update', { fieldValues: $event })\"\n @update-data-source-fetched=\"\n emit('update', { dataSourceFetched: $event })\n \"\n />\n </template>\n\n <SectionToolbar\n v-else-if=\"blockType === 'section'\"\n :block=\"block as SectionBlock\"\n @update=\"handleUpdate\"\n />\n\n <TitleToolbar\n v-else-if=\"blockType === 'title'\"\n :block=\"block as TitleBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <!-- Paragraph block: no text-specific sidebar controls — all formatting is in the TipTap toolbar -->\n <template v-else-if=\"blockType === 'paragraph'\" />\n\n <ImageToolbar\n v-else-if=\"blockType === 'image'\"\n :block=\"block as ImageBlock\"\n @update=\"handleUpdate\"\n />\n\n <VideoToolbar\n v-else-if=\"blockType === 'video'\"\n :block=\"block as VideoBlock\"\n @update=\"handleUpdate\"\n />\n\n <ButtonToolbar\n v-else-if=\"blockType === 'button'\"\n :block=\"block as ButtonBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <DividerToolbar\n v-else-if=\"blockType === 'divider'\"\n :block=\"block as DividerBlock\"\n @update=\"handleUpdate\"\n />\n\n <SocialToolbar\n v-else-if=\"blockType === 'social'\"\n :block=\"block as SocialIconsBlock\"\n @update=\"handleUpdate\"\n />\n\n <MenuToolbar\n v-else-if=\"blockType === 'menu'\"\n :block=\"block as MenuBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <TableToolbar\n v-else-if=\"blockType === 'table'\"\n :block=\"block as TableBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <SpacerToolbar\n v-else-if=\"blockType === 'spacer'\"\n :block=\"block as SpacerBlock\"\n @update=\"handleUpdate\"\n />\n\n <HtmlToolbar\n v-else-if=\"blockType === 'html'\"\n :block=\"block as HtmlBlock\"\n @update=\"handleUpdate\"\n />\n\n <CountdownToolbar\n v-else-if=\"blockType === 'countdown'\"\n :block=\"block as CountdownBlock\"\n :font-families=\"fontFamilies\"\n @update=\"handleUpdate\"\n />\n\n <!-- Common block settings -->\n <CommonBlockSettings\n :block=\"block\"\n :is-first-section=\"blockType === 'paragraph'\"\n @update=\"handleUpdate\"\n />\n </div>\n </aside>\n</template>\n\n<style scoped>\n.tpl-collapsible {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-collapsible--open {\n grid-template-rows: 1fr;\n}\n\n.tpl-collapsible > div {\n overflow: hidden;\n}\n</style>\n","<script setup lang=\"ts\">\nimport TemplateSettingsPanel from \"./TemplateSettings.vue\";\nimport Toolbar from \"./Toolbar.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { ACCESSIBILITY_LINT_KEY } from \"../keys\";\nimport type { Block, TemplateSettings } from \"@templatical/types\";\nimport { Accessibility, LayoutTemplate, PanelTop, Settings } from \"@lucide/vue\";\nimport { computed, defineAsyncComponent, inject, ref, watch } from \"vue\";\n\nconst AccessibilityPanel = defineAsyncComponent(\n () => import(\"./sidebar/AccessibilityPanel.vue\"),\n);\n\nconst props = defineProps<{\n selectedBlock: Block | null;\n settings: TemplateSettings;\n shiftedLeft?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update-block\", updates: Partial<Block>): void;\n (e: \"delete-block\"): void;\n (e: \"duplicate-block\"): void;\n (e: \"update-settings\", settings: Partial<TemplateSettings>): void;\n}>();\n\nconst { t } = useI18n();\n\ntype Tab = \"content\" | \"settings\" | \"accessibility\";\nconst activeTab = ref<Tab>(\"content\");\n\nconst lint = inject(ACCESSIBILITY_LINT_KEY, null);\nconst a11yEnabled = computed(() => lint !== null);\nconst a11yIssueCount = computed(() => lint?.issues.value.length ?? 0);\n\nfunction tabClass(tab: Tab): string {\n const isActive = activeTab.value === tab;\n if (isActive) {\n return \"tpl:flex-1 tpl:text-[var(--tpl-primary)]\";\n }\n return \"tpl:shrink-0 tpl:text-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]\";\n}\n\nfunction tabStyle(tab: Tab): Record<string, string> {\n const isActive = activeTab.value === tab;\n if (isActive) {\n return {\n backgroundColor: \"var(--tpl-bg)\",\n boxShadow: \"var(--tpl-shadow-md)\",\n };\n }\n return { backgroundColor: \"transparent\" };\n}\n\nwatch(\n () => props.selectedBlock,\n (newBlock) => {\n if (newBlock) {\n activeTab.value = \"content\";\n }\n },\n);\n</script>\n\n<template>\n <aside\n :aria-label=\"t.landmarks.rightSidebar\"\n class=\"tpl-right-sidebar tpl:absolute tpl:top-14 tpl:bottom-0 tpl:z-40 tpl:flex tpl:w-[320px] tpl:flex-col tpl:bg-[var(--tpl-bg-elevated)] tpl:transition-all tpl:duration-200 tpl:border-l tpl:border-[var(--tpl-border)]\"\n :class=\"shiftedLeft ? 'tpl:right-[360px]' : 'tpl:right-0'\"\n >\n <div\n role=\"tablist\"\n class=\"tpl:relative tpl:flex tpl:gap-1 tpl:border-b tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-active)] tpl:p-1.5\"\n >\n <button\n id=\"tpl-tab-content\"\n role=\"tab\"\n :aria-selected=\"activeTab === 'content'\"\n aria-controls=\"tpl-tabpanel-content\"\n :aria-label=\"t.sidebar.content\"\n :title=\"t.sidebar.content\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"tabClass('content')\"\n :style=\"tabStyle('content')\"\n @click=\"activeTab = 'content'\"\n >\n <PanelTop :size=\"14\" :stroke-width=\"2\" />\n <span v-if=\"activeTab === 'content'\">{{ t.sidebar.content }}</span>\n </button>\n <button\n id=\"tpl-tab-settings\"\n role=\"tab\"\n :aria-selected=\"activeTab === 'settings'\"\n aria-controls=\"tpl-tabpanel-settings\"\n :aria-label=\"t.sidebar.settings\"\n :title=\"t.sidebar.settings\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"tabClass('settings')\"\n :style=\"tabStyle('settings')\"\n @click=\"activeTab = 'settings'\"\n >\n <Settings :size=\"14\" :stroke-width=\"1.5\" />\n <span v-if=\"activeTab === 'settings'\">{{ t.sidebar.settings }}</span>\n </button>\n <button\n v-if=\"a11yEnabled\"\n id=\"tpl-tab-accessibility\"\n role=\"tab\"\n :aria-selected=\"activeTab === 'accessibility'\"\n aria-controls=\"tpl-tabpanel-accessibility\"\n :aria-label=\"t.accessibility.panelTabLabel\"\n :title=\"t.accessibility.panelTabLabel\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"tabClass('accessibility')\"\n :style=\"tabStyle('accessibility')\"\n @click=\"activeTab = 'accessibility'\"\n >\n <Accessibility :size=\"14\" :stroke-width=\"1.5\" />\n <span v-if=\"activeTab === 'accessibility'\">\n {{ t.accessibility.panelTabLabel }}\n </span>\n <span\n v-if=\"a11yIssueCount > 0\"\n class=\"tpl:ml-1 tpl:rounded-full tpl:bg-[var(--tpl-bg-hover)] tpl:px-1.5 tpl:text-[10px]\"\n >\n {{ a11yIssueCount }}\n </span>\n </button>\n </div>\n\n <div\n v-if=\"activeTab === 'content'\"\n id=\"tpl-tabpanel-content\"\n role=\"tabpanel\"\n aria-labelledby=\"tpl-tab-content\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-y-auto\"\n >\n <Toolbar\n v-if=\"selectedBlock\"\n :block=\"selectedBlock\"\n @update=\"emit('update-block', $event)\"\n @delete=\"emit('delete-block')\"\n @duplicate=\"emit('duplicate-block')\"\n />\n <div\n v-else\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:px-6 tpl:py-10 tpl:text-center tpl:text-[var(--tpl-text-muted)]\"\n >\n <div class=\"tpl:mb-4 tpl:text-[var(--tpl-text-dim)]\">\n <LayoutTemplate :size=\"40\" :stroke-width=\"1.5\" />\n </div>\n <h3\n class=\"tpl:m-0 tpl:mb-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.sidebar.noSelection }}\n </h3>\n <p class=\"tpl:m-0 tpl:text-sm tpl:leading-normal\">\n {{ t.sidebar.noSelectionHint }}\n </p>\n </div>\n </div>\n\n <div\n v-if=\"activeTab === 'settings'\"\n id=\"tpl-tabpanel-settings\"\n role=\"tabpanel\"\n aria-labelledby=\"tpl-tab-settings\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-y-auto\"\n >\n <TemplateSettingsPanel\n :settings=\"settings\"\n @update=\"emit('update-settings', $event)\"\n />\n </div>\n\n <div\n v-if=\"activeTab === 'accessibility' && a11yEnabled\"\n id=\"tpl-tabpanel-accessibility\"\n role=\"tabpanel\"\n aria-labelledby=\"tpl-tab-accessibility\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-y-auto\"\n >\n <AccessibilityPanel />\n </div>\n </aside>\n</template>\n","<script setup lang=\"ts\">\nimport TemplateSettingsPanel from \"./TemplateSettings.vue\";\nimport Toolbar from \"./Toolbar.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { ACCESSIBILITY_LINT_KEY } from \"../keys\";\nimport type { Block, TemplateSettings } from \"@templatical/types\";\nimport { Accessibility, LayoutTemplate, PanelTop, Settings } from \"@lucide/vue\";\nimport { computed, defineAsyncComponent, inject, ref, watch } from \"vue\";\n\nconst AccessibilityPanel = defineAsyncComponent(\n () => import(\"./sidebar/AccessibilityPanel.vue\"),\n);\n\nconst props = defineProps<{\n selectedBlock: Block | null;\n settings: TemplateSettings;\n shiftedLeft?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update-block\", updates: Partial<Block>): void;\n (e: \"delete-block\"): void;\n (e: \"duplicate-block\"): void;\n (e: \"update-settings\", settings: Partial<TemplateSettings>): void;\n}>();\n\nconst { t } = useI18n();\n\ntype Tab = \"content\" | \"settings\" | \"accessibility\";\nconst activeTab = ref<Tab>(\"content\");\n\nconst lint = inject(ACCESSIBILITY_LINT_KEY, null);\nconst a11yEnabled = computed(() => lint !== null);\nconst a11yIssueCount = computed(() => lint?.issues.value.length ?? 0);\n\nfunction tabClass(tab: Tab): string {\n const isActive = activeTab.value === tab;\n if (isActive) {\n return \"tpl:flex-1 tpl:text-[var(--tpl-primary)]\";\n }\n return \"tpl:shrink-0 tpl:text-[var(--tpl-text-muted)] hover:tpl:text-[var(--tpl-text)]\";\n}\n\nfunction tabStyle(tab: Tab): Record<string, string> {\n const isActive = activeTab.value === tab;\n if (isActive) {\n return {\n backgroundColor: \"var(--tpl-bg)\",\n boxShadow: \"var(--tpl-shadow-md)\",\n };\n }\n return { backgroundColor: \"transparent\" };\n}\n\nwatch(\n () => props.selectedBlock,\n (newBlock) => {\n if (newBlock) {\n activeTab.value = \"content\";\n }\n },\n);\n</script>\n\n<template>\n <aside\n :aria-label=\"t.landmarks.rightSidebar\"\n class=\"tpl-right-sidebar tpl:absolute tpl:top-14 tpl:bottom-0 tpl:z-40 tpl:flex tpl:w-[320px] tpl:flex-col tpl:bg-[var(--tpl-bg-elevated)] tpl:transition-all tpl:duration-200 tpl:border-l tpl:border-[var(--tpl-border)]\"\n :class=\"shiftedLeft ? 'tpl:right-[360px]' : 'tpl:right-0'\"\n >\n <div\n role=\"tablist\"\n class=\"tpl:relative tpl:flex tpl:gap-1 tpl:border-b tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-active)] tpl:p-1.5\"\n >\n <button\n id=\"tpl-tab-content\"\n role=\"tab\"\n :aria-selected=\"activeTab === 'content'\"\n aria-controls=\"tpl-tabpanel-content\"\n :aria-label=\"t.sidebar.content\"\n :title=\"t.sidebar.content\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"tabClass('content')\"\n :style=\"tabStyle('content')\"\n @click=\"activeTab = 'content'\"\n >\n <PanelTop :size=\"14\" :stroke-width=\"2\" />\n <span v-if=\"activeTab === 'content'\">{{ t.sidebar.content }}</span>\n </button>\n <button\n id=\"tpl-tab-settings\"\n role=\"tab\"\n :aria-selected=\"activeTab === 'settings'\"\n aria-controls=\"tpl-tabpanel-settings\"\n :aria-label=\"t.sidebar.settings\"\n :title=\"t.sidebar.settings\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"tabClass('settings')\"\n :style=\"tabStyle('settings')\"\n @click=\"activeTab = 'settings'\"\n >\n <Settings :size=\"14\" :stroke-width=\"1.5\" />\n <span v-if=\"activeTab === 'settings'\">{{ t.sidebar.settings }}</span>\n </button>\n <button\n v-if=\"a11yEnabled\"\n id=\"tpl-tab-accessibility\"\n role=\"tab\"\n :aria-selected=\"activeTab === 'accessibility'\"\n aria-controls=\"tpl-tabpanel-accessibility\"\n :aria-label=\"t.accessibility.panelTabLabel\"\n :title=\"t.accessibility.panelTabLabel\"\n class=\"tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-3 tpl:py-2 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\"\n :class=\"tabClass('accessibility')\"\n :style=\"tabStyle('accessibility')\"\n @click=\"activeTab = 'accessibility'\"\n >\n <Accessibility :size=\"14\" :stroke-width=\"1.5\" />\n <span v-if=\"activeTab === 'accessibility'\">\n {{ t.accessibility.panelTabLabel }}\n </span>\n <span\n v-if=\"a11yIssueCount > 0\"\n class=\"tpl:ml-1 tpl:rounded-full tpl:bg-[var(--tpl-bg-hover)] tpl:px-1.5 tpl:text-[10px]\"\n >\n {{ a11yIssueCount }}\n </span>\n </button>\n </div>\n\n <div\n v-if=\"activeTab === 'content'\"\n id=\"tpl-tabpanel-content\"\n role=\"tabpanel\"\n aria-labelledby=\"tpl-tab-content\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-y-auto\"\n >\n <Toolbar\n v-if=\"selectedBlock\"\n :block=\"selectedBlock\"\n @update=\"emit('update-block', $event)\"\n @delete=\"emit('delete-block')\"\n @duplicate=\"emit('duplicate-block')\"\n />\n <div\n v-else\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:px-6 tpl:py-10 tpl:text-center tpl:text-[var(--tpl-text-muted)]\"\n >\n <div class=\"tpl:mb-4 tpl:text-[var(--tpl-text-dim)]\">\n <LayoutTemplate :size=\"40\" :stroke-width=\"1.5\" />\n </div>\n <h3\n class=\"tpl:m-0 tpl:mb-2 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.sidebar.noSelection }}\n </h3>\n <p class=\"tpl:m-0 tpl:text-sm tpl:leading-normal\">\n {{ t.sidebar.noSelectionHint }}\n </p>\n </div>\n </div>\n\n <div\n v-if=\"activeTab === 'settings'\"\n id=\"tpl-tabpanel-settings\"\n role=\"tabpanel\"\n aria-labelledby=\"tpl-tab-settings\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-y-auto\"\n >\n <TemplateSettingsPanel\n :settings=\"settings\"\n @update=\"emit('update-settings', $event)\"\n />\n </div>\n\n <div\n v-if=\"activeTab === 'accessibility' && a11yEnabled\"\n id=\"tpl-tabpanel-accessibility\"\n role=\"tabpanel\"\n aria-labelledby=\"tpl-tab-accessibility\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-y-auto\"\n >\n <AccessibilityPanel />\n </div>\n </aside>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { ViewportSize } from \"@templatical/types\";\nimport { Monitor, Smartphone, Tablet } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n viewport: ViewportSize;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", viewport: ViewportSize): void;\n}>();\n\nconst { t } = useI18n();\n\nconst viewports = computed(() => [\n { value: \"desktop\" as ViewportSize, label: t.viewport.desktop },\n { value: \"tablet\" as ViewportSize, label: t.viewport.tablet },\n { value: \"mobile\" as ViewportSize, label: t.viewport.mobile },\n]);\n\nconst pillOffset = computed(() => {\n const index = viewports.value.findIndex((vp) => vp.value === props.viewport);\n return `translateX(${index * 100}%)`;\n});\n</script>\n\n<template>\n <div\n role=\"radiogroup\"\n :aria-label=\"t.viewport.label\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${viewports.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill -->\n <div\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${viewports.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"vp in viewports\"\n :key=\"vp.value\"\n role=\"radio\"\n :aria-checked=\"viewport === vp.value\"\n :aria-label=\"vp.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n viewport === vp.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"vp.label\"\n @click=\"emit('change', vp.value)\"\n >\n <Monitor v-if=\"vp.value === 'desktop'\" :size=\"18\" :stroke-width=\"1.5\" />\n <Tablet\n v-else-if=\"vp.value === 'tablet'\"\n :size=\"18\"\n :stroke-width=\"1.5\"\n />\n <Smartphone v-else :size=\"18\" :stroke-width=\"1.5\" />\n <span>{{ vp.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { ViewportSize } from \"@templatical/types\";\nimport { Monitor, Smartphone, Tablet } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nconst props = defineProps<{\n viewport: ViewportSize;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", viewport: ViewportSize): void;\n}>();\n\nconst { t } = useI18n();\n\nconst viewports = computed(() => [\n { value: \"desktop\" as ViewportSize, label: t.viewport.desktop },\n { value: \"tablet\" as ViewportSize, label: t.viewport.tablet },\n { value: \"mobile\" as ViewportSize, label: t.viewport.mobile },\n]);\n\nconst pillOffset = computed(() => {\n const index = viewports.value.findIndex((vp) => vp.value === props.viewport);\n return `translateX(${index * 100}%)`;\n});\n</script>\n\n<template>\n <div\n role=\"radiogroup\"\n :aria-label=\"t.viewport.label\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${viewports.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill -->\n <div\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${viewports.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"vp in viewports\"\n :key=\"vp.value\"\n role=\"radio\"\n :aria-checked=\"viewport === vp.value\"\n :aria-label=\"vp.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n viewport === vp.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"vp.label\"\n @click=\"emit('change', vp.value)\"\n >\n <Monitor v-if=\"vp.value === 'desktop'\" :size=\"18\" :stroke-width=\"1.5\" />\n <Tablet\n v-else-if=\"vp.value === 'tablet'\"\n :size=\"18\"\n :stroke-width=\"1.5\"\n />\n <Smartphone v-else :size=\"18\" :stroke-width=\"1.5\" />\n <span>{{ vp.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Eye, EyeOff } from \"@lucide/vue\";\n\ndefineProps<{\n previewMode: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", previewMode: boolean): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <button\n class=\"tpl-preview-toggle tpl:relative tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:p-2 tpl:transition-all tpl:duration-150\"\n :style=\"{\n color: previewMode ? 'var(--tpl-primary)' : 'var(--tpl-text-muted)',\n backgroundColor: previewMode ? 'var(--tpl-primary-light)' : 'transparent',\n }\"\n :aria-label=\"previewMode ? t.previewMode.disable : t.previewMode.enable\"\n :title=\"previewMode ? t.previewMode.disable : t.previewMode.enable\"\n :aria-pressed=\"previewMode\"\n @click=\"emit('change', !previewMode)\"\n >\n <Transition\n enter-active-class=\"tpl-icon-enter-active\"\n leave-active-class=\"tpl-icon-leave-active\"\n enter-from-class=\"tpl-icon-enter-from\"\n leave-to-class=\"tpl-icon-leave-to\"\n mode=\"out-in\"\n >\n <Eye v-if=\"previewMode\" key=\"eye\" :size=\"18\" :stroke-width=\"1.5\" />\n <EyeOff v-else key=\"eye-off\" :size=\"18\" :stroke-width=\"1.5\" />\n </Transition>\n </button>\n</template>\n\n<style scoped>\n.tpl-icon-enter-active,\n.tpl-icon-leave-active {\n transition:\n opacity 120ms cubic-bezier(0.16, 1, 0.3, 1),\n transform 120ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-icon-enter-from {\n opacity: 0;\n transform: scale(0.8);\n}\n\n.tpl-icon-leave-to {\n opacity: 0;\n transform: scale(0.8);\n}\n\n.tpl-preview-toggle:hover {\n background-color: var(--tpl-bg-hover);\n}\n\n.tpl-preview-toggle:active {\n transform: scale(0.92);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Eye, EyeOff } from \"@lucide/vue\";\n\ndefineProps<{\n previewMode: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", previewMode: boolean): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <button\n class=\"tpl-preview-toggle tpl:relative tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:p-2 tpl:transition-all tpl:duration-150\"\n :style=\"{\n color: previewMode ? 'var(--tpl-primary)' : 'var(--tpl-text-muted)',\n backgroundColor: previewMode ? 'var(--tpl-primary-light)' : 'transparent',\n }\"\n :aria-label=\"previewMode ? t.previewMode.disable : t.previewMode.enable\"\n :title=\"previewMode ? t.previewMode.disable : t.previewMode.enable\"\n :aria-pressed=\"previewMode\"\n @click=\"emit('change', !previewMode)\"\n >\n <Transition\n enter-active-class=\"tpl-icon-enter-active\"\n leave-active-class=\"tpl-icon-leave-active\"\n enter-from-class=\"tpl-icon-enter-from\"\n leave-to-class=\"tpl-icon-leave-to\"\n mode=\"out-in\"\n >\n <Eye v-if=\"previewMode\" key=\"eye\" :size=\"18\" :stroke-width=\"1.5\" />\n <EyeOff v-else key=\"eye-off\" :size=\"18\" :stroke-width=\"1.5\" />\n </Transition>\n </button>\n</template>\n\n<style scoped>\n.tpl-icon-enter-active,\n.tpl-icon-leave-active {\n transition:\n opacity 120ms cubic-bezier(0.16, 1, 0.3, 1),\n transform 120ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-icon-enter-from {\n opacity: 0;\n transform: scale(0.8);\n}\n\n.tpl-icon-leave-to {\n opacity: 0;\n transform: scale(0.8);\n}\n\n.tpl-preview-toggle:hover {\n background-color: var(--tpl-bg-hover);\n}\n\n.tpl-preview-toggle:active {\n transform: scale(0.92);\n}\n</style>\n","<!--\n Canvas Dark Mode Preview Toggle\n\n This toggle simulates how the email template will look when the recipient's\n email client uses dark mode (e.g. Gmail, Outlook, Apple Mail in dark theme).\n It applies a CSS filter (invert + hue-rotate) to the canvas area only.\n\n This is NOT the editor UI theme toggle. The editor UI theme (light/dark/auto)\n is controlled externally via the `uiTheme` config option or `editor.setTheme()`.\n-->\n<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Moon, Sun } from \"@lucide/vue\";\n\ndefineProps<{\n darkMode: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", darkMode: boolean): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <button\n class=\"tpl-dark-mode-toggle tpl:relative tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:p-2 tpl:transition-all tpl:duration-150\"\n :style=\"{\n color: darkMode ? 'var(--tpl-primary)' : 'var(--tpl-text-muted)',\n backgroundColor: darkMode ? 'var(--tpl-primary-light)' : 'transparent',\n }\"\n :aria-label=\"darkMode ? t.darkMode.disable : t.darkMode.enable\"\n :title=\"darkMode ? t.darkMode.disable : t.darkMode.enable\"\n :aria-pressed=\"darkMode\"\n @click=\"emit('change', !darkMode)\"\n >\n <Transition\n enter-active-class=\"tpl-icon-enter-active\"\n leave-active-class=\"tpl-icon-leave-active\"\n enter-from-class=\"tpl-icon-enter-from\"\n leave-to-class=\"tpl-icon-leave-to\"\n mode=\"out-in\"\n >\n <Moon v-if=\"darkMode\" key=\"moon\" :size=\"18\" :stroke-width=\"1.5\" />\n <Sun v-else key=\"sun\" :size=\"18\" :stroke-width=\"1.5\" />\n </Transition>\n </button>\n</template>\n\n<style scoped>\n.tpl-icon-enter-active,\n.tpl-icon-leave-active {\n transition:\n opacity 120ms cubic-bezier(0.16, 1, 0.3, 1),\n transform 120ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-icon-enter-from {\n opacity: 0;\n transform: scale(0.8);\n}\n\n.tpl-icon-leave-to {\n opacity: 0;\n transform: scale(0.8);\n}\n\n.tpl-dark-mode-toggle:hover {\n background-color: var(--tpl-bg-hover);\n}\n\n.tpl-dark-mode-toggle:active {\n transform: scale(0.92);\n}\n</style>\n","<!--\n Canvas Dark Mode Preview Toggle\n\n This toggle simulates how the email template will look when the recipient's\n email client uses dark mode (e.g. Gmail, Outlook, Apple Mail in dark theme).\n It applies a CSS filter (invert + hue-rotate) to the canvas area only.\n\n This is NOT the editor UI theme toggle. The editor UI theme (light/dark/auto)\n is controlled externally via the `uiTheme` config option or `editor.setTheme()`.\n-->\n<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Moon, Sun } from \"@lucide/vue\";\n\ndefineProps<{\n darkMode: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"change\", darkMode: boolean): void;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <button\n class=\"tpl-dark-mode-toggle tpl:relative tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:p-2 tpl:transition-all tpl:duration-150\"\n :style=\"{\n color: darkMode ? 'var(--tpl-primary)' : 'var(--tpl-text-muted)',\n backgroundColor: darkMode ? 'var(--tpl-primary-light)' : 'transparent',\n }\"\n :aria-label=\"darkMode ? t.darkMode.disable : t.darkMode.enable\"\n :title=\"darkMode ? t.darkMode.disable : t.darkMode.enable\"\n :aria-pressed=\"darkMode\"\n @click=\"emit('change', !darkMode)\"\n >\n <Transition\n enter-active-class=\"tpl-icon-enter-active\"\n leave-active-class=\"tpl-icon-leave-active\"\n enter-from-class=\"tpl-icon-enter-from\"\n leave-to-class=\"tpl-icon-leave-to\"\n mode=\"out-in\"\n >\n <Moon v-if=\"darkMode\" key=\"moon\" :size=\"18\" :stroke-width=\"1.5\" />\n <Sun v-else key=\"sun\" :size=\"18\" :stroke-width=\"1.5\" />\n </Transition>\n </button>\n</template>\n\n<style scoped>\n.tpl-icon-enter-active,\n.tpl-icon-leave-active {\n transition:\n opacity 120ms cubic-bezier(0.16, 1, 0.3, 1),\n transform 120ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-icon-enter-from {\n opacity: 0;\n transform: scale(0.8);\n}\n\n.tpl-icon-leave-to {\n opacity: 0;\n transform: scale(0.8);\n}\n\n.tpl-dark-mode-toggle:hover {\n background-color: var(--tpl-bg-hover);\n}\n\n.tpl-dark-mode-toggle:active {\n transform: scale(0.92);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\n\ndefineProps<{\n /** Positioning classes for the footer (left/right offsets). */\n positionClass?: string | string[];\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <footer\n class=\"tpl:pointer-events-none tpl:absolute tpl:bottom-0 tpl:z-50 tpl:flex tpl:h-8 tpl:items-center tpl:justify-end tpl:pr-4 tpl:text-[9px] tpl:opacity-90 tpl:transition-all tpl:duration-300 tpl:text-[var(--tpl-text-dim)]\"\n :class=\"positionClass\"\n >\n <div\n class=\"tpl:pointer-events-auto tpl:flex tpl:items-center tpl:gap-1.5 tpl:rounded-tl-lg tpl:p-1\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-canvas-bg) 85%,\n transparent\n );\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n \"\n >\n <span>{{ t.footer.poweredBy }}</span>\n <a\n href=\"https://templatical.com\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"tpl:inline-flex tpl:items-center tpl:gap-1 tpl:font-medium tpl:transition-colors tpl:duration-150 hover:tpl:opacity-80 tpl:text-[var(--tpl-text-muted)]\"\n style=\"text-decoration: none\"\n >\n <img\n width=\"14\"\n height=\"14\"\n src=\"https://templatical.com/logo.svg\"\n alt=\"\"\n />\n Templatical\n </a>\n <span class=\"tpl:text-[var(--tpl-border)]\">·</span>\n <a\n href=\"https://github.com/templatical/sdk\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"tpl:transition-colors tpl:duration-150 hover:tpl:opacity-80 tpl:text-[var(--tpl-text-dim)]\"\n style=\"text-decoration: none\"\n >\n {{ t.footer.openSource }}\n </a>\n </div>\n </footer>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\n\ndefineProps<{\n /** Positioning classes for the footer (left/right offsets). */\n positionClass?: string | string[];\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <footer\n class=\"tpl:pointer-events-none tpl:absolute tpl:bottom-0 tpl:z-50 tpl:flex tpl:h-8 tpl:items-center tpl:justify-end tpl:pr-4 tpl:text-[9px] tpl:opacity-90 tpl:transition-all tpl:duration-300 tpl:text-[var(--tpl-text-dim)]\"\n :class=\"positionClass\"\n >\n <div\n class=\"tpl:pointer-events-auto tpl:flex tpl:items-center tpl:gap-1.5 tpl:rounded-tl-lg tpl:p-1\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-canvas-bg) 85%,\n transparent\n );\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n \"\n >\n <span>{{ t.footer.poweredBy }}</span>\n <a\n href=\"https://templatical.com\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"tpl:inline-flex tpl:items-center tpl:gap-1 tpl:font-medium tpl:transition-colors tpl:duration-150 hover:tpl:opacity-80 tpl:text-[var(--tpl-text-muted)]\"\n style=\"text-decoration: none\"\n >\n <img\n width=\"14\"\n height=\"14\"\n src=\"https://templatical.com/logo.svg\"\n alt=\"\"\n />\n Templatical\n </a>\n <span class=\"tpl:text-[var(--tpl-border)]\">·</span>\n <a\n href=\"https://github.com/templatical/sdk\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"tpl:transition-colors tpl:duration-150 hover:tpl:opacity-80 tpl:text-[var(--tpl-text-dim)]\"\n style=\"text-decoration: none\"\n >\n {{ t.footer.openSource }}\n </a>\n </div>\n </footer>\n</template>\n"],"mappings":"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"}
|