@templatical/editor 0.21.2 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/README.md +1 -1
  2. package/dist/{AiChatSidebar-K_AehWc-.js → AiChatSidebar-Bru6-ziQ.js} +1 -1
  3. package/dist/{AiFeatureMenu-CcqQDHzJ.js → AiFeatureMenu-CcqjRMFW.js} +1 -1
  4. package/dist/{SavedBlockPreviewCanvas-2-mzAnZE.js → BlockPreviewCanvas-MD9BGuXV.js} +36 -30
  5. package/dist/{CloudEditor-DOjGJwPe.js → CloudEditor-iK4vkpNm.js} +275 -279
  6. package/dist/{CollaboratorBar-Bj4NZiie.js → CollaboratorBar-C_1SF7Xf.js} +1 -1
  7. package/dist/{DesignReferenceSidebar-DNmK3dxp.js → DesignReferenceSidebar-BxPo0zyx.js} +1 -1
  8. package/dist/{MergeTagSuggestion-DMe4rqn0.js → MergeTagSuggestion-D9eCryMC.js} +2 -2
  9. package/dist/{ParagraphEditor--_zWfxps.js → ParagraphEditor-MKJYyWN9.js} +13 -13
  10. package/dist/{SaveBlockDialog-CYIEHABH.js → SaveBlockDialog-CFAJHP3v.js} +99 -98
  11. package/dist/{SavedBlocksBrowserModal-D5V5yF_3.js → SavedBlocksBrowserModal-C_-Xcm0Q.js} +4 -4
  12. package/dist/{SavedBlocksPanels-Dff38YXu.js → SavedBlocksPanels-CRf7zzFn.js} +1 -1
  13. package/dist/{SavedBlocksPickBar-rB6iyji6.js → SavedBlocksPickBar-C1FTJP-I.js} +1 -1
  14. package/dist/{SnapshotHistory-FtzDq8f0.js → SnapshotHistory-DWlQ3Eh0.js} +1 -1
  15. package/dist/TestEmailModal-D90-3Yex.js +162 -0
  16. package/dist/TestEmailPanel-B0u_Yd3X.js +33 -0
  17. package/dist/{TitleEditor-CPda5lh-.js → TitleEditor-DzsPxKv4.js} +5 -5
  18. package/dist/{TplModal-C7Koj5gM.js → TplModal-CykwWFhF.js} +1 -1
  19. package/dist/ViewportToggle-BjmbNoao.js +97 -0
  20. package/dist/{blockTypeIcons-CgwECCut.js → blockTypeIcons-CpKkI47V.js} +1 -1
  21. package/dist/bundle-stats.json +8 -8
  22. package/dist/{ca-BBgad6y5.js → ca-D47BxSGu.js} +14 -0
  23. package/dist/cdn/chunks/{AiChatSidebar-A1CuWGY0.js → AiChatSidebar-BMnsF8G6.js} +2 -2
  24. package/dist/cdn/chunks/{AiChatSidebar-A1CuWGY0.js.map → AiChatSidebar-BMnsF8G6.js.map} +1 -1
  25. package/dist/cdn/chunks/{AiFeatureMenu-Duwg413F.js → AiFeatureMenu-hWgWQg11.js} +2 -2
  26. package/dist/cdn/chunks/{AiFeatureMenu-Duwg413F.js.map → AiFeatureMenu-hWgWQg11.js.map} +1 -1
  27. package/dist/cdn/chunks/{SavedBlockPreviewCanvas-DZOMgNsz.js → BlockPreviewCanvas-eASrdFSN.js} +37 -31
  28. package/dist/cdn/chunks/BlockPreviewCanvas-eASrdFSN.js.map +1 -0
  29. package/dist/cdn/chunks/{CloudEditor-C0Y4RD4c.js → CloudEditor-S8zLHtKb.js} +253 -257
  30. package/dist/cdn/chunks/CloudEditor-S8zLHtKb.js.map +1 -0
  31. package/dist/cdn/chunks/{CollaboratorBar-BS3rJy6-.js → CollaboratorBar-mkJVAkSh.js} +2 -2
  32. package/dist/cdn/chunks/{CollaboratorBar-BS3rJy6-.js.map → CollaboratorBar-mkJVAkSh.js.map} +1 -1
  33. package/dist/cdn/chunks/{DesignReferenceSidebar-Dre99DTH.js → DesignReferenceSidebar-CM-k15pd.js} +2 -2
  34. package/dist/cdn/chunks/{DesignReferenceSidebar-Dre99DTH.js.map → DesignReferenceSidebar-CM-k15pd.js.map} +1 -1
  35. package/dist/cdn/chunks/{MergeTagSuggestion-D7TsECll.js → MergeTagSuggestion-BM349_QH.js} +2 -2
  36. package/dist/cdn/chunks/{MergeTagSuggestion-D7TsECll.js.map → MergeTagSuggestion-BM349_QH.js.map} +1 -1
  37. package/dist/cdn/chunks/{ParagraphEditor-CtaRa1vr.js → ParagraphEditor-J-1RLyi7.js} +14 -14
  38. package/dist/cdn/chunks/{ParagraphEditor-CtaRa1vr.js.map → ParagraphEditor-J-1RLyi7.js.map} +1 -1
  39. package/dist/cdn/chunks/{SaveBlockDialog-Bb3fdUEZ.js → SaveBlockDialog-Bi_Z3kuA.js} +100 -99
  40. package/dist/cdn/chunks/SaveBlockDialog-Bi_Z3kuA.js.map +1 -0
  41. package/dist/cdn/chunks/{SavedBlocksBrowserModal-CeUWcPv0.js → SavedBlocksBrowserModal-CesYxFsH.js} +4 -4
  42. package/dist/cdn/chunks/SavedBlocksBrowserModal-CesYxFsH.js.map +1 -0
  43. package/dist/cdn/chunks/{SavedBlocksPanels-D-pA_wfk.js → SavedBlocksPanels-BsSpJwrW.js} +2 -2
  44. package/dist/cdn/chunks/{SavedBlocksPanels-D-pA_wfk.js.map → SavedBlocksPanels-BsSpJwrW.js.map} +1 -1
  45. package/dist/cdn/chunks/{SavedBlocksPickBar-1rf1KpqS.js → SavedBlocksPickBar-CduR1Cpb.js} +2 -2
  46. package/dist/cdn/chunks/{SavedBlocksPickBar-1rf1KpqS.js.map → SavedBlocksPickBar-CduR1Cpb.js.map} +1 -1
  47. package/dist/cdn/chunks/{SnapshotHistory-Bf1uovti.js → SnapshotHistory-DO-gzr3i.js} +2 -2
  48. package/dist/cdn/chunks/{SnapshotHistory-Bf1uovti.js.map → SnapshotHistory-DO-gzr3i.js.map} +1 -1
  49. package/dist/cdn/chunks/TestEmailModal-CCWEVrE2.js +162 -0
  50. package/dist/cdn/chunks/TestEmailModal-CCWEVrE2.js.map +1 -0
  51. package/dist/cdn/chunks/TestEmailPanel-048m0kxX.js +35 -0
  52. package/dist/cdn/chunks/TestEmailPanel-048m0kxX.js.map +1 -0
  53. package/dist/cdn/chunks/{TitleEditor-BJ7LFykI.js → TitleEditor-J1vgU_J6.js} +6 -6
  54. package/dist/cdn/chunks/{TitleEditor-BJ7LFykI.js.map → TitleEditor-J1vgU_J6.js.map} +1 -1
  55. package/dist/cdn/chunks/{TplModal-CH9RzKF7.js → TplModal-BN53d9Se.js} +2 -2
  56. package/dist/cdn/chunks/{TplModal-CH9RzKF7.js.map → TplModal-BN53d9Se.js.map} +1 -1
  57. package/dist/cdn/chunks/ViewportToggle-CFmF-N4i.js +66 -0
  58. package/dist/cdn/chunks/ViewportToggle-CFmF-N4i.js.map +1 -0
  59. package/dist/cdn/chunks/{ca-BBgad6y5.js → ca-D47BxSGu.js} +15 -1
  60. package/dist/cdn/chunks/{ca-BBgad6y5.js.map → ca-D47BxSGu.js.map} +1 -1
  61. package/dist/cdn/chunks/{cloud-BJlbXGTJ.js → cloud-F-aVi9hj.js} +45 -41
  62. package/dist/cdn/chunks/cloud-F-aVi9hj.js.map +1 -0
  63. package/dist/{de-D7WbpFBR.js → cdn/chunks/de-CnnxBwh7.js} +16 -0
  64. package/dist/cdn/chunks/{de-D7WbpFBR.js.map → de-CnnxBwh7.js.map} +1 -1
  65. package/dist/{de-Bw6fB5JU.js → cdn/chunks/de-uVoSP1xF.js} +2 -9
  66. package/dist/cdn/chunks/de-uVoSP1xF.js.map +1 -0
  67. package/dist/cdn/chunks/emailFrameWidth-BmFCN2dp.js +8 -0
  68. package/dist/cdn/chunks/emailFrameWidth-BmFCN2dp.js.map +1 -0
  69. package/dist/{en-BrZ0uGK2.js → cdn/chunks/en-5gW-bYy2.js} +16 -0
  70. package/dist/cdn/chunks/{en-BrZ0uGK2.js.map → en-5gW-bYy2.js.map} +1 -1
  71. package/dist/{en-DAdHJz7h.js → cdn/chunks/en-CjhsJxb-.js} +2 -9
  72. package/dist/cdn/chunks/en-CjhsJxb-.js.map +1 -0
  73. package/dist/{es-tLSLysPs.js → cdn/chunks/es-sQ9SdI0l.js} +16 -0
  74. package/dist/cdn/chunks/{es-tLSLysPs.js.map → es-sQ9SdI0l.js.map} +1 -1
  75. package/dist/cdn/chunks/{extensions-ZX2oarW6.js → extensions-C49c51P1.js} +3 -3
  76. package/dist/cdn/chunks/{extensions-ZX2oarW6.js.map → extensions-C49c51P1.js.map} +1 -1
  77. package/dist/cdn/chunks/fr-DBZ5w21C.js +624 -0
  78. package/dist/cdn/chunks/fr-DBZ5w21C.js.map +1 -0
  79. package/dist/cdn/chunks/fr-Dswns199.js +89 -0
  80. package/dist/cdn/chunks/fr-Dswns199.js.map +1 -0
  81. package/dist/cdn/chunks/nl-CbcPJlrL.js +89 -0
  82. package/dist/cdn/chunks/nl-CbcPJlrL.js.map +1 -0
  83. package/dist/cdn/chunks/nl-DOL7Fnfo.js +624 -0
  84. package/dist/cdn/chunks/nl-DOL7Fnfo.js.map +1 -0
  85. package/dist/{pt-BR-w2sGzHNX.js → cdn/chunks/pt-BR-CTzHTqJD.js} +2 -9
  86. package/dist/cdn/chunks/pt-BR-CTzHTqJD.js.map +1 -0
  87. package/dist/cdn/chunks/{pt-BR-BWFokdTU.js → pt-BR-DRCdHX1d.js} +15 -1
  88. package/dist/cdn/chunks/{pt-BR-BWFokdTU.js.map → pt-BR-DRCdHX1d.js.map} +1 -1
  89. package/dist/cdn/chunks/{src-D6F_3XkE.js → src-CzoMNRq4.js} +19 -17
  90. package/dist/cdn/chunks/{src-D6F_3XkE.js.map → src-CzoMNRq4.js.map} +1 -1
  91. package/dist/cdn/chunks/{src-BuC_IHzN.js → src-SluTVSz_.js} +2 -2
  92. package/dist/cdn/chunks/{src-BuC_IHzN.js.map → src-SluTVSz_.js.map} +1 -1
  93. package/dist/cdn/chunks/{styles-DMOEiSX9.js → styles-BbfU2w9R.js} +617 -585
  94. package/dist/cdn/chunks/styles-BbfU2w9R.js.map +1 -0
  95. package/dist/cdn/chunks/{tiptap-BMZ-OENR.js → tiptap-DUTbZTeR.js} +5 -5
  96. package/dist/cdn/chunks/{tiptap-BMZ-OENR.js.map → tiptap-DUTbZTeR.js.map} +1 -1
  97. package/dist/cdn/chunks/{useEditorCore-BA6mspzD.js → useEditorCore-CzMhD6Gd.js} +3 -3
  98. package/dist/cdn/chunks/{useEditorCore-BA6mspzD.js.map → useEditorCore-CzMhD6Gd.js.map} +1 -1
  99. package/dist/cdn/editor.css +1 -1
  100. package/dist/cdn/editor.js +164 -145
  101. package/dist/cdn/editor.js.map +1 -1
  102. package/dist/{cloud-Du7mnkz2.js → cloud-CeZPgo1V.js} +41 -39
  103. package/dist/{cdn/chunks/de-D7WbpFBR.js → de-CnnxBwh7.js} +14 -2
  104. package/dist/{cdn/chunks/de-Bw6fB5JU.js → de-uVoSP1xF.js} +0 -11
  105. package/dist/{dist-CDO52Rxy.js → dist-3DHaYEiT.js} +2 -2
  106. package/dist/{dist-BEpCh2mG.js → dist-3HenmYIg.js} +1 -1
  107. package/dist/{dist-CrjzhIxB.js → dist-BCnJcAQc.js} +2 -2
  108. package/dist/{dist-BgPYC_8-.js → dist-B_etKYyh.js} +5 -5
  109. package/dist/{dist-CBzCU-a-.js → dist-CL_CfQ3Q.js} +2 -2
  110. package/dist/dist-CcQLJeWV.js +5 -0
  111. package/dist/{dist-CuXha2_7.js → dist-CclnU_v0.js} +2 -2
  112. package/dist/{dist-BkM8A17N.js → dist-DKWoZJAV.js} +2 -2
  113. package/dist/{dist-DYClLqy8.js → dist-DOtLGnLr.js} +2 -2
  114. package/dist/dist-DerdmERj.js +5 -0
  115. package/dist/{dist-BuNDAAVp.js → dist-Ih7ejWzn.js} +2 -2
  116. package/dist/{dist-CtcgoC_R.js → dist-bivrM-nS.js} +2 -2
  117. package/dist/emailFrameWidth-BmFCN2dp.js +6 -0
  118. package/dist/{cdn/chunks/en-BrZ0uGK2.js → en-5gW-bYy2.js} +14 -2
  119. package/dist/{cdn/chunks/en-DAdHJz7h.js → en-CjhsJxb-.js} +0 -11
  120. package/dist/{cdn/chunks/es-tLSLysPs.js → es-sQ9SdI0l.js} +14 -2
  121. package/dist/{extensions-ClY5Sh7S.js → extensions-3870a_3R.js} +3 -3
  122. package/dist/fr-DBZ5w21C.js +622 -0
  123. package/dist/index.d.ts +74 -0
  124. package/dist/nl-DOL7Fnfo.js +622 -0
  125. package/dist/{cdn/chunks/pt-BR-w2sGzHNX.js → pt-BR-CTzHTqJD.js} +0 -11
  126. package/dist/{pt-BR-BWFokdTU.js → pt-BR-DRCdHX1d.js} +14 -0
  127. package/dist/style.css +1 -1
  128. package/dist/{styles-PcOPXvLO.js → styles-BWTIgN5L.js} +672 -673
  129. package/dist/templatical-editor.js +128 -108
  130. package/dist/{useEditorCore-CT1slufs.js → useEditorCore-CuMMIfiS.js} +2 -2
  131. package/package.json +20 -20
  132. package/dist/TestEmailModal-DL2O1H8M.js +0 -95
  133. package/dist/cdn/chunks/CloudEditor-C0Y4RD4c.js.map +0 -1
  134. package/dist/cdn/chunks/SaveBlockDialog-Bb3fdUEZ.js.map +0 -1
  135. package/dist/cdn/chunks/SavedBlockPreviewCanvas-DZOMgNsz.js.map +0 -1
  136. package/dist/cdn/chunks/SavedBlocksBrowserModal-CeUWcPv0.js.map +0 -1
  137. package/dist/cdn/chunks/TestEmailModal-BJTDxFHh.js +0 -96
  138. package/dist/cdn/chunks/TestEmailModal-BJTDxFHh.js.map +0 -1
  139. package/dist/cdn/chunks/cloud-BJlbXGTJ.js.map +0 -1
  140. package/dist/cdn/chunks/de-Bw6fB5JU.js.map +0 -1
  141. package/dist/cdn/chunks/en-DAdHJz7h.js.map +0 -1
  142. package/dist/cdn/chunks/pt-BR-w2sGzHNX.js.map +0 -1
  143. package/dist/cdn/chunks/styles-DMOEiSX9.js.map +0 -1
  144. package/dist/dist-CweLDZrs.js +0 -5
  145. package/dist/dist-DOsdqAEZ.js +0 -5
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestEmailModal-CCWEVrE2.js","names":[],"sources":["../../../src/utils/validateEmailShape.ts","../../../src/components/TestEmailModal.vue","../../../src/components/TestEmailModal.vue"],"sourcesContent":["/**\n * Characters that cannot appear in a domain but routinely arrive attached to one.\n *\n * A **denylist**, not an allowlist, so unicode domains keep working — an\n * allowlist of `[a-z0-9.-]` would be the stricter, more obviously \"correct\"\n * choice and would reject them. What this actually targets is the punctuation\n * that comes from pasting out of a list or a mail client: `user@example.com,`\n * and `<user@example.com>`.\n */\nconst IMPOSSIBLE_DOMAIN_CHARS = /[,;<>()[\\]\\\\\"'`|/?#@!$%^&*+=~{}]/;\n\n/**\n * Does this string look like an email address?\n *\n * Deliberately loose: non-empty, no whitespace, exactly one `@`, a non-empty\n * local part, and a domain containing a dot with something either side and no\n * impossible characters. That is enough to catch the mistakes a typo actually\n * produces — a missing `@`, a trailing comma, a half-typed domain — before the\n * user pays for a round-trip.\n *\n * **Do not \"improve\" this into an RFC-ish regex.** Those reliably reject valid\n * addresses (plus-addressing, long new gTLDs, unicode domains) and the resulting\n * bug reports cost far more than the malformed addresses they catch. Only the\n * receiving mail server can really tell, and the sending backend has to validate\n * anyway — this is a fast-feedback affordance, not a gate. `validateEmailShape`\n * has its own test file pinning the addresses that must keep passing.\n */\nexport function looksLikeEmail(value: string): boolean {\n const trimmed = value.trim();\n if (trimmed.length === 0) return false;\n if (/\\s/.test(trimmed)) return false;\n\n const parts = trimmed.split(\"@\");\n if (parts.length !== 2) return false;\n\n const [local, domain] = parts;\n if (local.length === 0) return false;\n if (IMPOSSIBLE_DOMAIN_CHARS.test(domain)) return false;\n\n // A dot with at least one character either side, so `a@b` and `a@b.` fail but\n // `a@b.c` and `a@sub.example.museum` pass.\n const dot = domain.indexOf(\".\");\n return dot > 0 && dot < domain.length - 1;\n}\n","<script setup lang=\"ts\">\n/**\n * Send-a-test dialog, shared by the OSS and Cloud editors.\n *\n * The recipient control has three shapes, driven entirely by whether the\n * provider configured an allowlist:\n *\n * - **no allowlist** — free text, validated for shape only. This branch exists\n * for BYO senders; Cloud never takes it, because its list comes from a signed\n * JWT.\n * - **exactly one** — a read-only field. Preserved verbatim from the Cloud-only\n * version; there is nothing to choose, so offering an editable field would\n * imply otherwise.\n * - **several** — a picker of exactly those addresses.\n *\n * An empty allowlist never reaches here: the feature reports itself unavailable,\n * so no trigger renders. (The Cloud-only version fell through to `v-else` and\n * rendered a select with no options and a permanently disabled Send.)\n */\nimport TplModal from \"./TplModal.vue\";\n// Statically imported: this modal is already lazy-loaded as a whole, so the\n// preview rides its chunk rather than costing a second round-trip and flashing\n// an empty frame. Still shared with the saved-blocks surfaces, which lazy-load\n// it themselves.\nimport BlockPreviewCanvas from \"./BlockPreviewCanvas.vue\";\n// The editor header's own control, reused verbatim so the two viewport switches\n// are the same component rather than two things that resemble each other.\nimport ViewportToggle from \"./ViewportToggle.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { looksLikeEmail } from \"../utils/validateEmailShape\";\nimport { Check, LoaderCircle } from \"@lucide/vue\";\nimport { computed, inject, ref, watch } from \"vue\";\nimport type { ViewportSize } from \"@templatical/types\";\nimport { EDITOR_KEY } from \"../keys\";\nimport type { TestEmailError } from \"../composables/useTestEmailFeature\";\n\nconst props = defineProps<{\n visible: boolean;\n /** Omitted / undefined means unrestricted, so the field accepts free text. */\n allowedRecipients?: string[];\n defaultRecipient?: string;\n isSending: boolean;\n justSent: boolean;\n error: TestEmailError | null;\n}>();\n\nconst emit = defineEmits<{\n (e: \"send\", recipient: string): void;\n (e: \"close\"): void;\n}>();\n\nconst { t } = useI18n();\nconst editor = inject(EDITOR_KEY, null);\n\nconst recipient = ref(\"\");\n\nconst previewViewport = ref<ViewportSize>(\"desktop\");\n\n/**\n * Read live from the editor rather than passed in, so a preview left open while\n * a Cloud collaborator edits stays current. Empty without an editor (headless\n * mounts), which simply renders an empty frame.\n */\nconst previewBlocks = computed(() => editor?.content.value.blocks ?? []);\n\nconst hasAllowlist = computed(\n () =>\n props.allowedRecipients !== undefined && props.allowedRecipients.length > 0,\n);\nconst isSingleRecipient = computed(() => props.allowedRecipients?.length === 1);\n\n// Mounted behind a `v-if` on the same state that drives `visible`, so it can\n// mount with `visible` already true — without `immediate` the field would never\n// be seeded.\nwatch(\n () => props.visible,\n (visible) => {\n if (!visible) return;\n recipient.value =\n props.defaultRecipient ?? props.allowedRecipients?.[0] ?? \"\";\n },\n { immediate: true },\n);\n\n/** Shape-check free text only; a picker can't produce a malformed value. */\nconst isRecipientValid = computed(() =>\n hasAllowlist.value\n ? recipient.value.length > 0\n : looksLikeEmail(recipient.value),\n);\n\nconst canSend = computed(\n () => isRecipientValid.value && !props.isSending && !props.justSent,\n);\n\n/** Free text that's been typed into but isn't yet a plausible address. */\nconst showShapeHint = computed(\n () =>\n !hasAllowlist.value &&\n recipient.value.trim().length > 0 &&\n !isRecipientValid.value,\n);\n\nconst errorText = computed(() => {\n if (!props.error) return null;\n return props.error.kind === \"provider\"\n ? props.error.message\n : t.testEmail.recipientNotAllowed;\n});\n\nfunction handleSend(): void {\n if (!canSend.value) return;\n emit(\"send\", recipient.value.trim());\n}\n\nfunction handleClose(): void {\n // Sending is the one state that blocks dismissal; the success state closes\n // immediately rather than making the user wait out the timer.\n if (props.isSending) return;\n emit(\"close\");\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n handleSend();\n }\n if (event.key === \"Escape\") {\n handleClose();\n }\n}\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"handleClose\" @keydown=\"handleKeydown\">\n <!-- Grows to `max-w-2xl` only while the preview is open, so a 600px email\n renders at roughly true size — the same reason SaveBlockDialog picked\n that width. Collapsed, it stays the compact form it was. `max-h-[90vh]`\n with a `min-h-0 overflow-y-auto` preview region is the pattern the\n saved-blocks dialogs use: `min-h-0` is what lets a flex child shrink\n below its content and actually scroll. -->\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-busy=\"isSending\"\n aria-labelledby=\"tpl-test-email-title\"\n class=\"tpl-scale-in tpl:mx-4 tpl:flex tpl:max-h-[90vh] tpl:w-full tpl:max-w-2xl tpl:flex-col tpl:rounded-[var(--tpl-radius-lg)] tpl:p-5\"\n style=\"\n background-color: var(--tpl-bg-elevated);\n box-shadow: var(--tpl-shadow-xl);\n \"\n >\n <h3\n id=\"tpl-test-email-title\"\n class=\"tpl:mb-4 tpl:shrink-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.testEmail.title }}\n </h3>\n\n <!-- Recipient -->\n <div class=\"tpl:mb-3 tpl:shrink-0\">\n <label\n for=\"tpl-test-email-recipient\"\n class=\"tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.testEmail.recipientLabel }}\n </label>\n\n <!-- Exactly one allowed address: nothing to choose. -->\n <input\n v-if=\"isSingleRecipient\"\n id=\"tpl-test-email-recipient\"\n type=\"text\"\n :value=\"recipient\"\n disabled\n data-testid=\"test-email-recipient\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:opacity-70 tpl:shadow-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n />\n\n <!-- Several allowed addresses: pick one. -->\n <select\n v-else-if=\"hasAllowlist\"\n id=\"tpl-test-email-recipient\"\n v-model=\"recipient\"\n :disabled=\"isSending || justSent\"\n data-testid=\"test-email-recipient\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n >\n <option\n v-for=\"email in allowedRecipients\"\n :key=\"email\"\n :value=\"email\"\n >\n {{ email }}\n </option>\n </select>\n\n <!-- No allowlist: free text. -->\n <input\n v-else\n id=\"tpl-test-email-recipient\"\n v-model=\"recipient\"\n type=\"email\"\n autocomplete=\"email\"\n spellcheck=\"false\"\n :placeholder=\"t.testEmail.recipientPlaceholder\"\n :disabled=\"isSending || justSent\"\n data-testid=\"test-email-recipient\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n />\n\n <p\n v-if=\"showShapeHint\"\n class=\"tpl:mt-1.5 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.testEmail.invalidAddress }}\n </p>\n </div>\n\n <!-- Preview header: a label plus the viewport switch. Always shown — the\n preview is part of the dialog rather than something to opt into. -->\n <div class=\"tpl:mb-2 tpl:shrink-0\">\n <div class=\"tpl:flex tpl:items-center tpl:justify-between tpl:gap-2\">\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.testEmail.preview }}\n </span>\n\n <!-- Without this the preview would silently show only the desktop\n variant of any responsive block. -->\n <ViewportToggle\n :viewport=\"previewViewport\"\n @change=\"previewViewport = $event\"\n />\n </div>\n\n <p class=\"tpl:mt-1 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\">\n {{ t.testEmail.previewHint }}\n </p>\n </div>\n\n <!-- The one region allowed to grow: `flex-1 min-h-0` so it absorbs the\n remaining height and scrolls, leaving the form and actions fixed. -->\n <div\n id=\"tpl-test-email-preview\"\n data-testid=\"test-email-preview\"\n class=\"tpl:mb-3 tpl:min-h-0 tpl:flex-1 tpl:overflow-y-auto tpl:rounded-[var(--tpl-radius-sm)] tpl:p-3 tpl:bg-[var(--tpl-canvas-bg)]\"\n >\n <BlockPreviewCanvas\n :blocks=\"previewBlocks\"\n :viewport=\"previewViewport\"\n />\n </div>\n\n <!-- Success. Announced politely; the dialog closes itself shortly after. -->\n <p\n v-if=\"justSent\"\n role=\"status\"\n data-testid=\"test-email-success\"\n class=\"tpl:mb-3 tpl:flex tpl:shrink-0 tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-success,var(--tpl-primary))]\"\n >\n <Check :size=\"13\" :stroke-width=\"2.5\" />\n {{ t.testEmail.success }}\n </p>\n\n <!-- Failure. Stays open so the user can retry. -->\n <p\n v-else-if=\"errorText\"\n role=\"alert\"\n data-testid=\"test-email-error\"\n class=\"tpl:mb-3 tpl:shrink-0 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n {{ errorText }}\n </p>\n\n <!-- Actions -->\n <div class=\"tpl:flex tpl:shrink-0 tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n data-testid=\"test-email-cancel\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n :disabled=\"isSending\"\n :class=\"{ 'tpl:cursor-not-allowed tpl:opacity-50': isSending }\"\n @click=\"handleClose\"\n >\n {{ t.testEmail.cancel }}\n </button>\n <button\n type=\"button\"\n data-testid=\"test-email-send\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!canSend\"\n @click=\"handleSend\"\n >\n <span v-if=\"isSending\" class=\"tpl:flex tpl:items-center tpl:gap-1.5\">\n <LoaderCircle\n class=\"tpl:animate-spin\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n {{ t.testEmail.sending }}\n </span>\n <span v-else>\n {{ t.testEmail.send }}\n </span>\n </button>\n </div>\n </div>\n </TplModal>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Send-a-test dialog, shared by the OSS and Cloud editors.\n *\n * The recipient control has three shapes, driven entirely by whether the\n * provider configured an allowlist:\n *\n * - **no allowlist** — free text, validated for shape only. This branch exists\n * for BYO senders; Cloud never takes it, because its list comes from a signed\n * JWT.\n * - **exactly one** — a read-only field. Preserved verbatim from the Cloud-only\n * version; there is nothing to choose, so offering an editable field would\n * imply otherwise.\n * - **several** — a picker of exactly those addresses.\n *\n * An empty allowlist never reaches here: the feature reports itself unavailable,\n * so no trigger renders. (The Cloud-only version fell through to `v-else` and\n * rendered a select with no options and a permanently disabled Send.)\n */\nimport TplModal from \"./TplModal.vue\";\n// Statically imported: this modal is already lazy-loaded as a whole, so the\n// preview rides its chunk rather than costing a second round-trip and flashing\n// an empty frame. Still shared with the saved-blocks surfaces, which lazy-load\n// it themselves.\nimport BlockPreviewCanvas from \"./BlockPreviewCanvas.vue\";\n// The editor header's own control, reused verbatim so the two viewport switches\n// are the same component rather than two things that resemble each other.\nimport ViewportToggle from \"./ViewportToggle.vue\";\nimport { useI18n } from \"../composables/useI18n\";\nimport { looksLikeEmail } from \"../utils/validateEmailShape\";\nimport { Check, LoaderCircle } from \"@lucide/vue\";\nimport { computed, inject, ref, watch } from \"vue\";\nimport type { ViewportSize } from \"@templatical/types\";\nimport { EDITOR_KEY } from \"../keys\";\nimport type { TestEmailError } from \"../composables/useTestEmailFeature\";\n\nconst props = defineProps<{\n visible: boolean;\n /** Omitted / undefined means unrestricted, so the field accepts free text. */\n allowedRecipients?: string[];\n defaultRecipient?: string;\n isSending: boolean;\n justSent: boolean;\n error: TestEmailError | null;\n}>();\n\nconst emit = defineEmits<{\n (e: \"send\", recipient: string): void;\n (e: \"close\"): void;\n}>();\n\nconst { t } = useI18n();\nconst editor = inject(EDITOR_KEY, null);\n\nconst recipient = ref(\"\");\n\nconst previewViewport = ref<ViewportSize>(\"desktop\");\n\n/**\n * Read live from the editor rather than passed in, so a preview left open while\n * a Cloud collaborator edits stays current. Empty without an editor (headless\n * mounts), which simply renders an empty frame.\n */\nconst previewBlocks = computed(() => editor?.content.value.blocks ?? []);\n\nconst hasAllowlist = computed(\n () =>\n props.allowedRecipients !== undefined && props.allowedRecipients.length > 0,\n);\nconst isSingleRecipient = computed(() => props.allowedRecipients?.length === 1);\n\n// Mounted behind a `v-if` on the same state that drives `visible`, so it can\n// mount with `visible` already true — without `immediate` the field would never\n// be seeded.\nwatch(\n () => props.visible,\n (visible) => {\n if (!visible) return;\n recipient.value =\n props.defaultRecipient ?? props.allowedRecipients?.[0] ?? \"\";\n },\n { immediate: true },\n);\n\n/** Shape-check free text only; a picker can't produce a malformed value. */\nconst isRecipientValid = computed(() =>\n hasAllowlist.value\n ? recipient.value.length > 0\n : looksLikeEmail(recipient.value),\n);\n\nconst canSend = computed(\n () => isRecipientValid.value && !props.isSending && !props.justSent,\n);\n\n/** Free text that's been typed into but isn't yet a plausible address. */\nconst showShapeHint = computed(\n () =>\n !hasAllowlist.value &&\n recipient.value.trim().length > 0 &&\n !isRecipientValid.value,\n);\n\nconst errorText = computed(() => {\n if (!props.error) return null;\n return props.error.kind === \"provider\"\n ? props.error.message\n : t.testEmail.recipientNotAllowed;\n});\n\nfunction handleSend(): void {\n if (!canSend.value) return;\n emit(\"send\", recipient.value.trim());\n}\n\nfunction handleClose(): void {\n // Sending is the one state that blocks dismissal; the success state closes\n // immediately rather than making the user wait out the timer.\n if (props.isSending) return;\n emit(\"close\");\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n handleSend();\n }\n if (event.key === \"Escape\") {\n handleClose();\n }\n}\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"handleClose\" @keydown=\"handleKeydown\">\n <!-- Grows to `max-w-2xl` only while the preview is open, so a 600px email\n renders at roughly true size — the same reason SaveBlockDialog picked\n that width. Collapsed, it stays the compact form it was. `max-h-[90vh]`\n with a `min-h-0 overflow-y-auto` preview region is the pattern the\n saved-blocks dialogs use: `min-h-0` is what lets a flex child shrink\n below its content and actually scroll. -->\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-busy=\"isSending\"\n aria-labelledby=\"tpl-test-email-title\"\n class=\"tpl-scale-in tpl:mx-4 tpl:flex tpl:max-h-[90vh] tpl:w-full tpl:max-w-2xl tpl:flex-col tpl:rounded-[var(--tpl-radius-lg)] tpl:p-5\"\n style=\"\n background-color: var(--tpl-bg-elevated);\n box-shadow: var(--tpl-shadow-xl);\n \"\n >\n <h3\n id=\"tpl-test-email-title\"\n class=\"tpl:mb-4 tpl:shrink-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.testEmail.title }}\n </h3>\n\n <!-- Recipient -->\n <div class=\"tpl:mb-3 tpl:shrink-0\">\n <label\n for=\"tpl-test-email-recipient\"\n class=\"tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.testEmail.recipientLabel }}\n </label>\n\n <!-- Exactly one allowed address: nothing to choose. -->\n <input\n v-if=\"isSingleRecipient\"\n id=\"tpl-test-email-recipient\"\n type=\"text\"\n :value=\"recipient\"\n disabled\n data-testid=\"test-email-recipient\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:opacity-70 tpl:shadow-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n />\n\n <!-- Several allowed addresses: pick one. -->\n <select\n v-else-if=\"hasAllowlist\"\n id=\"tpl-test-email-recipient\"\n v-model=\"recipient\"\n :disabled=\"isSending || justSent\"\n data-testid=\"test-email-recipient\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n >\n <option\n v-for=\"email in allowedRecipients\"\n :key=\"email\"\n :value=\"email\"\n >\n {{ email }}\n </option>\n </select>\n\n <!-- No allowlist: free text. -->\n <input\n v-else\n id=\"tpl-test-email-recipient\"\n v-model=\"recipient\"\n type=\"email\"\n autocomplete=\"email\"\n spellcheck=\"false\"\n :placeholder=\"t.testEmail.recipientPlaceholder\"\n :disabled=\"isSending || justSent\"\n data-testid=\"test-email-recipient\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n />\n\n <p\n v-if=\"showShapeHint\"\n class=\"tpl:mt-1.5 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ t.testEmail.invalidAddress }}\n </p>\n </div>\n\n <!-- Preview header: a label plus the viewport switch. Always shown — the\n preview is part of the dialog rather than something to opt into. -->\n <div class=\"tpl:mb-2 tpl:shrink-0\">\n <div class=\"tpl:flex tpl:items-center tpl:justify-between tpl:gap-2\">\n <span\n class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.testEmail.preview }}\n </span>\n\n <!-- Without this the preview would silently show only the desktop\n variant of any responsive block. -->\n <ViewportToggle\n :viewport=\"previewViewport\"\n @change=\"previewViewport = $event\"\n />\n </div>\n\n <p class=\"tpl:mt-1 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\">\n {{ t.testEmail.previewHint }}\n </p>\n </div>\n\n <!-- The one region allowed to grow: `flex-1 min-h-0` so it absorbs the\n remaining height and scrolls, leaving the form and actions fixed. -->\n <div\n id=\"tpl-test-email-preview\"\n data-testid=\"test-email-preview\"\n class=\"tpl:mb-3 tpl:min-h-0 tpl:flex-1 tpl:overflow-y-auto tpl:rounded-[var(--tpl-radius-sm)] tpl:p-3 tpl:bg-[var(--tpl-canvas-bg)]\"\n >\n <BlockPreviewCanvas\n :blocks=\"previewBlocks\"\n :viewport=\"previewViewport\"\n />\n </div>\n\n <!-- Success. Announced politely; the dialog closes itself shortly after. -->\n <p\n v-if=\"justSent\"\n role=\"status\"\n data-testid=\"test-email-success\"\n class=\"tpl:mb-3 tpl:flex tpl:shrink-0 tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-success,var(--tpl-primary))]\"\n >\n <Check :size=\"13\" :stroke-width=\"2.5\" />\n {{ t.testEmail.success }}\n </p>\n\n <!-- Failure. Stays open so the user can retry. -->\n <p\n v-else-if=\"errorText\"\n role=\"alert\"\n data-testid=\"test-email-error\"\n class=\"tpl:mb-3 tpl:shrink-0 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n {{ errorText }}\n </p>\n\n <!-- Actions -->\n <div class=\"tpl:flex tpl:shrink-0 tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n data-testid=\"test-email-cancel\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n :disabled=\"isSending\"\n :class=\"{ 'tpl:cursor-not-allowed tpl:opacity-50': isSending }\"\n @click=\"handleClose\"\n >\n {{ t.testEmail.cancel }}\n </button>\n <button\n type=\"button\"\n data-testid=\"test-email-send\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!canSend\"\n @click=\"handleSend\"\n >\n <span v-if=\"isSending\" class=\"tpl:flex tpl:items-center tpl:gap-1.5\">\n <LoaderCircle\n class=\"tpl:animate-spin\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n {{ t.testEmail.sending }}\n </span>\n <span v-else>\n {{ t.testEmail.send }}\n </span>\n </button>\n </div>\n </div>\n </TplModal>\n</template>\n"],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { M as e, b as t, h as n, it as r, m as i, x as a } from "./draggable-D840DIZB.js";
2
+ //#endregion
3
+ //#region src/components/TestEmailPanel.vue
4
+ var o = /* @__PURE__ */ a({
5
+ __name: "TestEmailPanel",
6
+ props: { feature: {} },
7
+ setup(a) {
8
+ let o = a, s = t(() => import("./TestEmailModal-CCWEVrE2.js"));
9
+ function c(e) {
10
+ o.feature.send(e);
11
+ }
12
+ return (t, o) => a.feature.isModalOpen.value ? (e(), i(r(s), {
13
+ key: 0,
14
+ visible: a.feature.isModalOpen.value,
15
+ "allowed-recipients": a.feature.allowedRecipients.value,
16
+ "default-recipient": a.feature.defaultRecipient.value,
17
+ "is-sending": a.feature.isSending.value,
18
+ "just-sent": a.feature.justSent.value,
19
+ error: a.feature.error.value,
20
+ onSend: c,
21
+ onClose: o[0] ||= (e) => a.feature.close()
22
+ }, null, 8, [
23
+ "visible",
24
+ "allowed-recipients",
25
+ "default-recipient",
26
+ "is-sending",
27
+ "just-sent",
28
+ "error"
29
+ ])) : n("", !0);
30
+ }
31
+ });
32
+ //#endregion
33
+ export { o as default };
34
+
35
+ //# sourceMappingURL=TestEmailPanel-048m0kxX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestEmailPanel-048m0kxX.js","names":[],"sources":["../../../src/components/TestEmailPanel.vue","../../../src/components/TestEmailPanel.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Mount point for the test-email dialog, shared by the OSS and Cloud editors.\n *\n * The dialog is `defineAsyncComponent` behind a `v-if` on its open state, so its\n * chunk is fetched only when the user actually opens it — `defineAsyncComponent`\n * triggers its `import()` on first render, not at definition time. The editors in\n * turn lazy-load *this* wrapper and render it only when a `TestEmailProvider` is\n * configured, so a consumer without one downloads none of it.\n *\n * Never static-import `TestEmailModal` into `Editor.vue` / `CloudEditor.vue`:\n * that collapses it into the main entry for every consumer, including those who\n * never configure a sender. Guarded by `tests/cdn-chunk-granularity.test.ts`.\n */\nimport { defineAsyncComponent } from \"vue\";\nimport type { UseTestEmailFeatureReturn } from \"../composables/useTestEmailFeature\";\n\nconst props = defineProps<{\n feature: UseTestEmailFeatureReturn;\n}>();\n\nconst TestEmailModal = defineAsyncComponent(\n () => import(\"./TestEmailModal.vue\"),\n);\n\nfunction handleSend(recipient: string): void {\n void props.feature.send(recipient);\n}\n</script>\n\n<template>\n <TestEmailModal\n v-if=\"feature.isModalOpen.value\"\n :visible=\"feature.isModalOpen.value\"\n :allowed-recipients=\"feature.allowedRecipients.value\"\n :default-recipient=\"feature.defaultRecipient.value\"\n :is-sending=\"feature.isSending.value\"\n :just-sent=\"feature.justSent.value\"\n :error=\"feature.error.value\"\n @send=\"handleSend\"\n @close=\"feature.close()\"\n />\n</template>\n","<script setup lang=\"ts\">\n/**\n * Mount point for the test-email dialog, shared by the OSS and Cloud editors.\n *\n * The dialog is `defineAsyncComponent` behind a `v-if` on its open state, so its\n * chunk is fetched only when the user actually opens it — `defineAsyncComponent`\n * triggers its `import()` on first render, not at definition time. The editors in\n * turn lazy-load *this* wrapper and render it only when a `TestEmailProvider` is\n * configured, so a consumer without one downloads none of it.\n *\n * Never static-import `TestEmailModal` into `Editor.vue` / `CloudEditor.vue`:\n * that collapses it into the main entry for every consumer, including those who\n * never configure a sender. Guarded by `tests/cdn-chunk-granularity.test.ts`.\n */\nimport { defineAsyncComponent } from \"vue\";\nimport type { UseTestEmailFeatureReturn } from \"../composables/useTestEmailFeature\";\n\nconst props = defineProps<{\n feature: UseTestEmailFeatureReturn;\n}>();\n\nconst TestEmailModal = defineAsyncComponent(\n () => import(\"./TestEmailModal.vue\"),\n);\n\nfunction handleSend(recipient: string): void {\n void props.feature.send(recipient);\n}\n</script>\n\n<template>\n <TestEmailModal\n v-if=\"feature.isModalOpen.value\"\n :visible=\"feature.isModalOpen.value\"\n :allowed-recipients=\"feature.allowedRecipients.value\"\n :default-recipient=\"feature.defaultRecipient.value\"\n :is-sending=\"feature.isSending.value\"\n :just-sent=\"feature.justSent.value\"\n :error=\"feature.error.value\"\n @send=\"handleSend\"\n @close=\"feature.close()\"\n />\n</template>\n"],"mappings":";;;;;;;EAiBA,IAAM,IAAQ,GAIR,IAAiB,QACf,OAAO,+BACf;EAEA,SAAS,EAAW,GAAyB;GAC3C,EAAW,QAAQ,KAAK,CAAS;EACnC;mBAKU,EAAA,QAAQ,YAAY,SAAA,EAAA,GAD5B,EAUE,EAAA,CAAA,GAAA;;GARC,SAAS,EAAA,QAAQ,YAAY;GAC7B,sBAAoB,EAAA,QAAQ,kBAAkB;GAC9C,qBAAmB,EAAA,QAAQ,iBAAiB;GAC5C,cAAY,EAAA,QAAQ,UAAU;GAC9B,aAAW,EAAA,QAAQ,SAAS;GAC5B,OAAO,EAAA,QAAQ,MAAM;GACrB,QAAM;GACN,SAAK,AAAA,EAAA,QAAA,MAAE,EAAA,QAAQ,MAAK"}
@@ -1,5 +1,5 @@
1
1
  import { K as e, M as t, T as n, ct as r, d as i, f as a, g as o, h as s, it as c, m as l, ot as u, p as d, st as ee, u as f, v as p, x as m, y as h } from "./draggable-D840DIZB.js";
2
- import { S as te, x as ne } from "./useEditorCore-BA6mspzD.js";
2
+ import { S as te, x as ne } from "./useEditorCore-CzMhD6Gd.js";
3
3
  import { D as re, L as g, w as _, z as v } from "./keys-D7kUmfpE.js";
4
4
  import { t as y } from "./useI18n-8Wd7G-cq.js";
5
5
  import { n as b } from "./usePopoverPosition-Bx0QewTv.js";
@@ -30,10 +30,10 @@ var w = { class: "tpl-text-editor-wrapper tpl:relative" }, T = ["data-tpl-theme"
30
30
  editorName: "TitleEditor",
31
31
  async loadExtensions({ mergeTags: e, syntax: t, triggerChar: n, autocompleteEnabled: r, suggestionEmptyText: i }) {
32
32
  let [{ Editor: a, EditorContent: o }, { default: s }, { default: c }, { MergeTagNode: l, MergeTagSuggestion: u, LogicMergeTagNode: d }] = await Promise.all([
33
- import("./tiptap-BMZ-OENR.js").then((e) => e.r),
34
- import("./tiptap-BMZ-OENR.js").then((e) => e.i),
35
- import("./tiptap-BMZ-OENR.js").then((e) => e.l),
36
- import("./extensions-ZX2oarW6.js")
33
+ import("./tiptap-DUTbZTeR.js").then((e) => e.r),
34
+ import("./tiptap-DUTbZTeR.js").then((e) => e.i),
35
+ import("./tiptap-DUTbZTeR.js").then((e) => e.l),
36
+ import("./extensions-C49c51P1.js")
37
37
  ]);
38
38
  return {
39
39
  TiptapEditor: a,
@@ -195,4 +195,4 @@ var w = { class: "tpl-text-editor-wrapper tpl:relative" }, T = ["data-tpl-theme"
195
195
  //#endregion
196
196
  export { P as default };
197
197
 
198
- //# sourceMappingURL=TitleEditor-BJ7LFykI.js.map
198
+ //# sourceMappingURL=TitleEditor-J1vgU_J6.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TitleEditor-BJ7LFykI.js","names":[],"sources":["../../../src/components/blocks/TitleEditor.vue","../../../src/components/blocks/TitleEditor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables\";\nimport { useRichTextEditor } from \"../../composables/useRichTextEditor\";\nimport { usePopoverRoot } from \"../../composables/usePopoverRoot\";\nimport type { TitleBlock as TitleBlockType } from \"@templatical/types\";\nimport {\n Bold,\n Braces,\n Italic,\n Link,\n LoaderCircle,\n ScanLine,\n} from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport {\n THEME_STYLES_KEY,\n UI_THEME_KEY,\n MERGE_TAG_PICKER_KEY,\n LOGIC_TAG_PICKER_KEY,\n} from \"../../keys\";\nimport RichTextLinkDialog from \"./RichTextLinkDialog.vue\";\nimport RichTextEditorContent from \"./RichTextEditorContent.vue\";\nimport { withLinkColor } from \"../../utils/linkColorExtension\";\n\nconst props = defineProps<{\n block: TitleBlockType;\n toolbarPosition: { top: number; left: number };\n}>();\n\nconst emit = defineEmits<{\n (e: \"done\"): void;\n}>();\n\nconst themeStyles = inject(THEME_STYLES_KEY, null);\nconst tplUiTheme = inject(UI_THEME_KEY, null);\nconst popoverRoot = usePopoverRoot();\n// Hide the floating toolbar while either built-in picker modal is open — see\n// ParagraphToolbar.vue for the same rationale.\nconst picker = inject(MERGE_TAG_PICKER_KEY, null);\nconst logicPicker = inject(LOGIC_TAG_PICKER_KEY, null);\nconst pickerIsOpen = computed(\n () => (picker?.isOpen.value ?? false) || (logicPicker?.isOpen.value ?? false),\n);\n\nconst { t } = useI18n();\n\nconst {\n editor,\n EditorContent,\n isLoading,\n initError,\n retry,\n showLinkDialog,\n linkUrl,\n linkColor,\n linkDialogRef,\n canRequestMergeTag,\n canInsertLogicTag,\n openLinkDialog,\n insertLink,\n removeLink,\n closeLinkDialog,\n handleLinkKeydown,\n handleAddMergeTag,\n handleAddLogicTag,\n} = useRichTextEditor({\n blockId: () => props.block.id,\n blockContent: () => props.block.content,\n onDone: () => emit(\"done\"),\n editorName: \"TitleEditor\",\n async loadExtensions({\n mergeTags,\n syntax,\n triggerChar,\n autocompleteEnabled,\n suggestionEmptyText,\n }) {\n const [\n { Editor: TiptapEditor, EditorContent: EC },\n { default: StarterKit },\n { default: LinkExt },\n { MergeTagNode, MergeTagSuggestion, LogicMergeTagNode },\n ] = await Promise.all([\n import(\"@tiptap/vue-3\"),\n import(\"@tiptap/starter-kit\"),\n import(\"@tiptap/extension-link\"),\n import(\"../../extensions\"),\n ]);\n\n return {\n TiptapEditor,\n EC,\n extensions: [\n StarterKit.configure({\n heading: false,\n codeBlock: false,\n blockquote: false,\n horizontalRule: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n strike: false,\n // StarterKit bundles @tiptap/extension-link (openOnClick: true).\n // Disable it so the LinkExt below (openOnClick: false) is the only\n // link extension — otherwise clicking a link while editing opens it.\n link: false,\n }),\n withLinkColor(LinkExt).configure({\n openOnClick: false,\n HTMLAttributes: {\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n }),\n MergeTagNode.configure({ mergeTags, syntax }),\n LogicMergeTagNode.configure({ syntax }),\n ...(autocompleteEnabled && triggerChar && mergeTags.length > 0\n ? [\n MergeTagSuggestion.configure({\n mergeTags,\n char: triggerChar,\n emptyText: suggestionEmptyText,\n popoverRoot,\n }),\n ]\n : []),\n ],\n };\n },\n});\n</script>\n\n<template>\n <div class=\"tpl-text-editor-wrapper tpl:relative\">\n <Teleport v-if=\"popoverRoot && !pickerIsOpen\" :to=\"popoverRoot\">\n <div\n :data-tpl-theme=\"tplUiTheme\"\n role=\"toolbar\"\n :aria-label=\"t.titleEditor.toolbar\"\n class=\"tpl tpl-text-toolbar tpl:absolute tpl:z-popover tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:shadow-lg\"\n :style=\"{\n ...themeStyles,\n top: `${toolbarPosition.top}px`,\n left: `${toolbarPosition.left}px`,\n transform: 'translateY(-100%)',\n }\"\n >\n <template v-if=\"!isLoading && editor\">\n <!-- Bold -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('bold'),\n }\"\n :aria-label=\"t.titleEditor.bold\"\n :title=\"t.titleEditor.bold\"\n @click=\"editor?.chain().focus().toggleBold().run()\"\n >\n <Bold :size=\"16\" :stroke-width=\"2.5\" />\n </button>\n <!-- Italic -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('italic'),\n }\"\n :aria-label=\"t.titleEditor.italic\"\n :title=\"t.titleEditor.italic\"\n @click=\"editor?.chain().focus().toggleItalic().run()\"\n >\n <Italic :size=\"16\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n aria-hidden=\"true\"\n ></span>\n <!-- Link -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('link'),\n }\"\n :aria-label=\"t.titleEditor.addLink\"\n :title=\"t.titleEditor.addLink\"\n @click=\"openLinkDialog\"\n >\n <Link :size=\"16\" :stroke-width=\"2\" />\n </button>\n <!-- Add Merge Tag -->\n <span\n v-if=\"canRequestMergeTag\"\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n ></span>\n <button\n v-if=\"canRequestMergeTag\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n @click=\"handleAddMergeTag\"\n >\n <ScanLine :size=\"16\" :stroke-width=\"2\" />\n {{ t.mergeTag.insertShort }}\n </button>\n <!-- Insert Logic -->\n <span\n v-if=\"canInsertLogicTag\"\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n ></span>\n <button\n v-if=\"canInsertLogicTag\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.logicTag.insert\"\n :title=\"t.logicTag.insert\"\n data-testid=\"insert-logic-button\"\n @click=\"handleAddLogicTag\"\n >\n <Braces :size=\"16\" :stroke-width=\"2\" />\n {{ t.logicTag.insertShort }}\n </button>\n </template>\n <template v-else>\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-2 tpl:px-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n <LoaderCircle class=\"tpl-spinner\" :size=\"14\" :stroke-width=\"2\" />\n {{ t.errors.editorLoading }}\n </div>\n </template>\n </div>\n </Teleport>\n\n <RichTextEditorContent\n :editor=\"editor\"\n :editor-content=\"EditorContent\"\n :is-loading=\"isLoading\"\n :init-error=\"initError\"\n @retry=\"retry\"\n />\n\n <RichTextLinkDialog\n :visible=\"showLinkDialog\"\n :is-editing-link=\"editor?.isActive('link') ?? false\"\n v-model:dialog-ref=\"linkDialogRef\"\n v-model:link-url=\"linkUrl\"\n v-model:link-color=\"linkColor\"\n @close=\"closeLinkDialog\"\n @insert=\"insertLink\"\n @remove=\"removeLink\"\n @keydown=\"handleLinkKeydown\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables\";\nimport { useRichTextEditor } from \"../../composables/useRichTextEditor\";\nimport { usePopoverRoot } from \"../../composables/usePopoverRoot\";\nimport type { TitleBlock as TitleBlockType } from \"@templatical/types\";\nimport {\n Bold,\n Braces,\n Italic,\n Link,\n LoaderCircle,\n ScanLine,\n} from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport {\n THEME_STYLES_KEY,\n UI_THEME_KEY,\n MERGE_TAG_PICKER_KEY,\n LOGIC_TAG_PICKER_KEY,\n} from \"../../keys\";\nimport RichTextLinkDialog from \"./RichTextLinkDialog.vue\";\nimport RichTextEditorContent from \"./RichTextEditorContent.vue\";\nimport { withLinkColor } from \"../../utils/linkColorExtension\";\n\nconst props = defineProps<{\n block: TitleBlockType;\n toolbarPosition: { top: number; left: number };\n}>();\n\nconst emit = defineEmits<{\n (e: \"done\"): void;\n}>();\n\nconst themeStyles = inject(THEME_STYLES_KEY, null);\nconst tplUiTheme = inject(UI_THEME_KEY, null);\nconst popoverRoot = usePopoverRoot();\n// Hide the floating toolbar while either built-in picker modal is open — see\n// ParagraphToolbar.vue for the same rationale.\nconst picker = inject(MERGE_TAG_PICKER_KEY, null);\nconst logicPicker = inject(LOGIC_TAG_PICKER_KEY, null);\nconst pickerIsOpen = computed(\n () => (picker?.isOpen.value ?? false) || (logicPicker?.isOpen.value ?? false),\n);\n\nconst { t } = useI18n();\n\nconst {\n editor,\n EditorContent,\n isLoading,\n initError,\n retry,\n showLinkDialog,\n linkUrl,\n linkColor,\n linkDialogRef,\n canRequestMergeTag,\n canInsertLogicTag,\n openLinkDialog,\n insertLink,\n removeLink,\n closeLinkDialog,\n handleLinkKeydown,\n handleAddMergeTag,\n handleAddLogicTag,\n} = useRichTextEditor({\n blockId: () => props.block.id,\n blockContent: () => props.block.content,\n onDone: () => emit(\"done\"),\n editorName: \"TitleEditor\",\n async loadExtensions({\n mergeTags,\n syntax,\n triggerChar,\n autocompleteEnabled,\n suggestionEmptyText,\n }) {\n const [\n { Editor: TiptapEditor, EditorContent: EC },\n { default: StarterKit },\n { default: LinkExt },\n { MergeTagNode, MergeTagSuggestion, LogicMergeTagNode },\n ] = await Promise.all([\n import(\"@tiptap/vue-3\"),\n import(\"@tiptap/starter-kit\"),\n import(\"@tiptap/extension-link\"),\n import(\"../../extensions\"),\n ]);\n\n return {\n TiptapEditor,\n EC,\n extensions: [\n StarterKit.configure({\n heading: false,\n codeBlock: false,\n blockquote: false,\n horizontalRule: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n strike: false,\n // StarterKit bundles @tiptap/extension-link (openOnClick: true).\n // Disable it so the LinkExt below (openOnClick: false) is the only\n // link extension — otherwise clicking a link while editing opens it.\n link: false,\n }),\n withLinkColor(LinkExt).configure({\n openOnClick: false,\n HTMLAttributes: {\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n }),\n MergeTagNode.configure({ mergeTags, syntax }),\n LogicMergeTagNode.configure({ syntax }),\n ...(autocompleteEnabled && triggerChar && mergeTags.length > 0\n ? [\n MergeTagSuggestion.configure({\n mergeTags,\n char: triggerChar,\n emptyText: suggestionEmptyText,\n popoverRoot,\n }),\n ]\n : []),\n ],\n };\n },\n});\n</script>\n\n<template>\n <div class=\"tpl-text-editor-wrapper tpl:relative\">\n <Teleport v-if=\"popoverRoot && !pickerIsOpen\" :to=\"popoverRoot\">\n <div\n :data-tpl-theme=\"tplUiTheme\"\n role=\"toolbar\"\n :aria-label=\"t.titleEditor.toolbar\"\n class=\"tpl tpl-text-toolbar tpl:absolute tpl:z-popover tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:shadow-lg\"\n :style=\"{\n ...themeStyles,\n top: `${toolbarPosition.top}px`,\n left: `${toolbarPosition.left}px`,\n transform: 'translateY(-100%)',\n }\"\n >\n <template v-if=\"!isLoading && editor\">\n <!-- Bold -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('bold'),\n }\"\n :aria-label=\"t.titleEditor.bold\"\n :title=\"t.titleEditor.bold\"\n @click=\"editor?.chain().focus().toggleBold().run()\"\n >\n <Bold :size=\"16\" :stroke-width=\"2.5\" />\n </button>\n <!-- Italic -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('italic'),\n }\"\n :aria-label=\"t.titleEditor.italic\"\n :title=\"t.titleEditor.italic\"\n @click=\"editor?.chain().focus().toggleItalic().run()\"\n >\n <Italic :size=\"16\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n aria-hidden=\"true\"\n ></span>\n <!-- Link -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('link'),\n }\"\n :aria-label=\"t.titleEditor.addLink\"\n :title=\"t.titleEditor.addLink\"\n @click=\"openLinkDialog\"\n >\n <Link :size=\"16\" :stroke-width=\"2\" />\n </button>\n <!-- Add Merge Tag -->\n <span\n v-if=\"canRequestMergeTag\"\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n ></span>\n <button\n v-if=\"canRequestMergeTag\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n @click=\"handleAddMergeTag\"\n >\n <ScanLine :size=\"16\" :stroke-width=\"2\" />\n {{ t.mergeTag.insertShort }}\n </button>\n <!-- Insert Logic -->\n <span\n v-if=\"canInsertLogicTag\"\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n ></span>\n <button\n v-if=\"canInsertLogicTag\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.logicTag.insert\"\n :title=\"t.logicTag.insert\"\n data-testid=\"insert-logic-button\"\n @click=\"handleAddLogicTag\"\n >\n <Braces :size=\"16\" :stroke-width=\"2\" />\n {{ t.logicTag.insertShort }}\n </button>\n </template>\n <template v-else>\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-2 tpl:px-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n <LoaderCircle class=\"tpl-spinner\" :size=\"14\" :stroke-width=\"2\" />\n {{ t.errors.editorLoading }}\n </div>\n </template>\n </div>\n </Teleport>\n\n <RichTextEditorContent\n :editor=\"editor\"\n :editor-content=\"EditorContent\"\n :is-loading=\"isLoading\"\n :init-error=\"initError\"\n @retry=\"retry\"\n />\n\n <RichTextLinkDialog\n :visible=\"showLinkDialog\"\n :is-editing-link=\"editor?.isActive('link') ?? false\"\n v-model:dialog-ref=\"linkDialogRef\"\n v-model:link-url=\"linkUrl\"\n v-model:link-color=\"linkColor\"\n @close=\"closeLinkDialog\"\n @insert=\"insertLink\"\n @remove=\"removeLink\"\n @keydown=\"handleLinkKeydown\"\n />\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"TitleEditor-J1vgU_J6.js","names":[],"sources":["../../../src/components/blocks/TitleEditor.vue","../../../src/components/blocks/TitleEditor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables\";\nimport { useRichTextEditor } from \"../../composables/useRichTextEditor\";\nimport { usePopoverRoot } from \"../../composables/usePopoverRoot\";\nimport type { TitleBlock as TitleBlockType } from \"@templatical/types\";\nimport {\n Bold,\n Braces,\n Italic,\n Link,\n LoaderCircle,\n ScanLine,\n} from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport {\n THEME_STYLES_KEY,\n UI_THEME_KEY,\n MERGE_TAG_PICKER_KEY,\n LOGIC_TAG_PICKER_KEY,\n} from \"../../keys\";\nimport RichTextLinkDialog from \"./RichTextLinkDialog.vue\";\nimport RichTextEditorContent from \"./RichTextEditorContent.vue\";\nimport { withLinkColor } from \"../../utils/linkColorExtension\";\n\nconst props = defineProps<{\n block: TitleBlockType;\n toolbarPosition: { top: number; left: number };\n}>();\n\nconst emit = defineEmits<{\n (e: \"done\"): void;\n}>();\n\nconst themeStyles = inject(THEME_STYLES_KEY, null);\nconst tplUiTheme = inject(UI_THEME_KEY, null);\nconst popoverRoot = usePopoverRoot();\n// Hide the floating toolbar while either built-in picker modal is open — see\n// ParagraphToolbar.vue for the same rationale.\nconst picker = inject(MERGE_TAG_PICKER_KEY, null);\nconst logicPicker = inject(LOGIC_TAG_PICKER_KEY, null);\nconst pickerIsOpen = computed(\n () => (picker?.isOpen.value ?? false) || (logicPicker?.isOpen.value ?? false),\n);\n\nconst { t } = useI18n();\n\nconst {\n editor,\n EditorContent,\n isLoading,\n initError,\n retry,\n showLinkDialog,\n linkUrl,\n linkColor,\n linkDialogRef,\n canRequestMergeTag,\n canInsertLogicTag,\n openLinkDialog,\n insertLink,\n removeLink,\n closeLinkDialog,\n handleLinkKeydown,\n handleAddMergeTag,\n handleAddLogicTag,\n} = useRichTextEditor({\n blockId: () => props.block.id,\n blockContent: () => props.block.content,\n onDone: () => emit(\"done\"),\n editorName: \"TitleEditor\",\n async loadExtensions({\n mergeTags,\n syntax,\n triggerChar,\n autocompleteEnabled,\n suggestionEmptyText,\n }) {\n const [\n { Editor: TiptapEditor, EditorContent: EC },\n { default: StarterKit },\n { default: LinkExt },\n { MergeTagNode, MergeTagSuggestion, LogicMergeTagNode },\n ] = await Promise.all([\n import(\"@tiptap/vue-3\"),\n import(\"@tiptap/starter-kit\"),\n import(\"@tiptap/extension-link\"),\n import(\"../../extensions\"),\n ]);\n\n return {\n TiptapEditor,\n EC,\n extensions: [\n StarterKit.configure({\n heading: false,\n codeBlock: false,\n blockquote: false,\n horizontalRule: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n strike: false,\n // StarterKit bundles @tiptap/extension-link (openOnClick: true).\n // Disable it so the LinkExt below (openOnClick: false) is the only\n // link extension — otherwise clicking a link while editing opens it.\n link: false,\n }),\n withLinkColor(LinkExt).configure({\n openOnClick: false,\n HTMLAttributes: {\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n }),\n MergeTagNode.configure({ mergeTags, syntax }),\n LogicMergeTagNode.configure({ syntax }),\n ...(autocompleteEnabled && triggerChar && mergeTags.length > 0\n ? [\n MergeTagSuggestion.configure({\n mergeTags,\n char: triggerChar,\n emptyText: suggestionEmptyText,\n popoverRoot,\n }),\n ]\n : []),\n ],\n };\n },\n});\n</script>\n\n<template>\n <div class=\"tpl-text-editor-wrapper tpl:relative\">\n <Teleport v-if=\"popoverRoot && !pickerIsOpen\" :to=\"popoverRoot\">\n <div\n :data-tpl-theme=\"tplUiTheme\"\n role=\"toolbar\"\n :aria-label=\"t.titleEditor.toolbar\"\n class=\"tpl tpl-text-toolbar tpl:absolute tpl:z-popover tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:shadow-lg\"\n :style=\"{\n ...themeStyles,\n top: `${toolbarPosition.top}px`,\n left: `${toolbarPosition.left}px`,\n transform: 'translateY(-100%)',\n }\"\n >\n <template v-if=\"!isLoading && editor\">\n <!-- Bold -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('bold'),\n }\"\n :aria-label=\"t.titleEditor.bold\"\n :title=\"t.titleEditor.bold\"\n @click=\"editor?.chain().focus().toggleBold().run()\"\n >\n <Bold :size=\"16\" :stroke-width=\"2.5\" />\n </button>\n <!-- Italic -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('italic'),\n }\"\n :aria-label=\"t.titleEditor.italic\"\n :title=\"t.titleEditor.italic\"\n @click=\"editor?.chain().focus().toggleItalic().run()\"\n >\n <Italic :size=\"16\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n aria-hidden=\"true\"\n ></span>\n <!-- Link -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('link'),\n }\"\n :aria-label=\"t.titleEditor.addLink\"\n :title=\"t.titleEditor.addLink\"\n @click=\"openLinkDialog\"\n >\n <Link :size=\"16\" :stroke-width=\"2\" />\n </button>\n <!-- Add Merge Tag -->\n <span\n v-if=\"canRequestMergeTag\"\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n ></span>\n <button\n v-if=\"canRequestMergeTag\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n @click=\"handleAddMergeTag\"\n >\n <ScanLine :size=\"16\" :stroke-width=\"2\" />\n {{ t.mergeTag.insertShort }}\n </button>\n <!-- Insert Logic -->\n <span\n v-if=\"canInsertLogicTag\"\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n ></span>\n <button\n v-if=\"canInsertLogicTag\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.logicTag.insert\"\n :title=\"t.logicTag.insert\"\n data-testid=\"insert-logic-button\"\n @click=\"handleAddLogicTag\"\n >\n <Braces :size=\"16\" :stroke-width=\"2\" />\n {{ t.logicTag.insertShort }}\n </button>\n </template>\n <template v-else>\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-2 tpl:px-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n <LoaderCircle class=\"tpl-spinner\" :size=\"14\" :stroke-width=\"2\" />\n {{ t.errors.editorLoading }}\n </div>\n </template>\n </div>\n </Teleport>\n\n <RichTextEditorContent\n :editor=\"editor\"\n :editor-content=\"EditorContent\"\n :is-loading=\"isLoading\"\n :init-error=\"initError\"\n @retry=\"retry\"\n />\n\n <RichTextLinkDialog\n :visible=\"showLinkDialog\"\n :is-editing-link=\"editor?.isActive('link') ?? false\"\n v-model:dialog-ref=\"linkDialogRef\"\n v-model:link-url=\"linkUrl\"\n v-model:link-color=\"linkColor\"\n @close=\"closeLinkDialog\"\n @insert=\"insertLink\"\n @remove=\"removeLink\"\n @keydown=\"handleLinkKeydown\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables\";\nimport { useRichTextEditor } from \"../../composables/useRichTextEditor\";\nimport { usePopoverRoot } from \"../../composables/usePopoverRoot\";\nimport type { TitleBlock as TitleBlockType } from \"@templatical/types\";\nimport {\n Bold,\n Braces,\n Italic,\n Link,\n LoaderCircle,\n ScanLine,\n} from \"@lucide/vue\";\nimport { computed, inject } from \"vue\";\nimport {\n THEME_STYLES_KEY,\n UI_THEME_KEY,\n MERGE_TAG_PICKER_KEY,\n LOGIC_TAG_PICKER_KEY,\n} from \"../../keys\";\nimport RichTextLinkDialog from \"./RichTextLinkDialog.vue\";\nimport RichTextEditorContent from \"./RichTextEditorContent.vue\";\nimport { withLinkColor } from \"../../utils/linkColorExtension\";\n\nconst props = defineProps<{\n block: TitleBlockType;\n toolbarPosition: { top: number; left: number };\n}>();\n\nconst emit = defineEmits<{\n (e: \"done\"): void;\n}>();\n\nconst themeStyles = inject(THEME_STYLES_KEY, null);\nconst tplUiTheme = inject(UI_THEME_KEY, null);\nconst popoverRoot = usePopoverRoot();\n// Hide the floating toolbar while either built-in picker modal is open — see\n// ParagraphToolbar.vue for the same rationale.\nconst picker = inject(MERGE_TAG_PICKER_KEY, null);\nconst logicPicker = inject(LOGIC_TAG_PICKER_KEY, null);\nconst pickerIsOpen = computed(\n () => (picker?.isOpen.value ?? false) || (logicPicker?.isOpen.value ?? false),\n);\n\nconst { t } = useI18n();\n\nconst {\n editor,\n EditorContent,\n isLoading,\n initError,\n retry,\n showLinkDialog,\n linkUrl,\n linkColor,\n linkDialogRef,\n canRequestMergeTag,\n canInsertLogicTag,\n openLinkDialog,\n insertLink,\n removeLink,\n closeLinkDialog,\n handleLinkKeydown,\n handleAddMergeTag,\n handleAddLogicTag,\n} = useRichTextEditor({\n blockId: () => props.block.id,\n blockContent: () => props.block.content,\n onDone: () => emit(\"done\"),\n editorName: \"TitleEditor\",\n async loadExtensions({\n mergeTags,\n syntax,\n triggerChar,\n autocompleteEnabled,\n suggestionEmptyText,\n }) {\n const [\n { Editor: TiptapEditor, EditorContent: EC },\n { default: StarterKit },\n { default: LinkExt },\n { MergeTagNode, MergeTagSuggestion, LogicMergeTagNode },\n ] = await Promise.all([\n import(\"@tiptap/vue-3\"),\n import(\"@tiptap/starter-kit\"),\n import(\"@tiptap/extension-link\"),\n import(\"../../extensions\"),\n ]);\n\n return {\n TiptapEditor,\n EC,\n extensions: [\n StarterKit.configure({\n heading: false,\n codeBlock: false,\n blockquote: false,\n horizontalRule: false,\n bulletList: false,\n orderedList: false,\n listItem: false,\n strike: false,\n // StarterKit bundles @tiptap/extension-link (openOnClick: true).\n // Disable it so the LinkExt below (openOnClick: false) is the only\n // link extension — otherwise clicking a link while editing opens it.\n link: false,\n }),\n withLinkColor(LinkExt).configure({\n openOnClick: false,\n HTMLAttributes: {\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n },\n }),\n MergeTagNode.configure({ mergeTags, syntax }),\n LogicMergeTagNode.configure({ syntax }),\n ...(autocompleteEnabled && triggerChar && mergeTags.length > 0\n ? [\n MergeTagSuggestion.configure({\n mergeTags,\n char: triggerChar,\n emptyText: suggestionEmptyText,\n popoverRoot,\n }),\n ]\n : []),\n ],\n };\n },\n});\n</script>\n\n<template>\n <div class=\"tpl-text-editor-wrapper tpl:relative\">\n <Teleport v-if=\"popoverRoot && !pickerIsOpen\" :to=\"popoverRoot\">\n <div\n :data-tpl-theme=\"tplUiTheme\"\n role=\"toolbar\"\n :aria-label=\"t.titleEditor.toolbar\"\n class=\"tpl tpl-text-toolbar tpl:absolute tpl:z-popover tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-2 tpl:shadow-lg\"\n :style=\"{\n ...themeStyles,\n top: `${toolbarPosition.top}px`,\n left: `${toolbarPosition.left}px`,\n transform: 'translateY(-100%)',\n }\"\n >\n <template v-if=\"!isLoading && editor\">\n <!-- Bold -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('bold'),\n }\"\n :aria-label=\"t.titleEditor.bold\"\n :title=\"t.titleEditor.bold\"\n @click=\"editor?.chain().focus().toggleBold().run()\"\n >\n <Bold :size=\"16\" :stroke-width=\"2.5\" />\n </button>\n <!-- Italic -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('italic'),\n }\"\n :aria-label=\"t.titleEditor.italic\"\n :title=\"t.titleEditor.italic\"\n @click=\"editor?.chain().focus().toggleItalic().run()\"\n >\n <Italic :size=\"16\" :stroke-width=\"2\" />\n </button>\n <span\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n aria-hidden=\"true\"\n ></span>\n <!-- Link -->\n <button\n type=\"button\"\n class=\"tpl-text-toolbar-btn\"\n :class=\"{\n 'tpl-text-toolbar-btn--active': editor?.isActive('link'),\n }\"\n :aria-label=\"t.titleEditor.addLink\"\n :title=\"t.titleEditor.addLink\"\n @click=\"openLinkDialog\"\n >\n <Link :size=\"16\" :stroke-width=\"2\" />\n </button>\n <!-- Add Merge Tag -->\n <span\n v-if=\"canRequestMergeTag\"\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n ></span>\n <button\n v-if=\"canRequestMergeTag\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n @click=\"handleAddMergeTag\"\n >\n <ScanLine :size=\"16\" :stroke-width=\"2\" />\n {{ t.mergeTag.insertShort }}\n </button>\n <!-- Insert Logic -->\n <span\n v-if=\"canInsertLogicTag\"\n class=\"tpl:mx-1.5 tpl:h-6 tpl:w-px tpl:bg-[var(--tpl-border)]\"\n ></span>\n <button\n v-if=\"canInsertLogicTag\"\n type=\"button\"\n class=\"tpl:flex tpl:h-8 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)] tpl:transition-all tpl:duration-150 tpl:hover:bg-[var(--tpl-bg-active)]\"\n :aria-label=\"t.logicTag.insert\"\n :title=\"t.logicTag.insert\"\n data-testid=\"insert-logic-button\"\n @click=\"handleAddLogicTag\"\n >\n <Braces :size=\"16\" :stroke-width=\"2\" />\n {{ t.logicTag.insertShort }}\n </button>\n </template>\n <template v-else>\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-2 tpl:px-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n <LoaderCircle class=\"tpl-spinner\" :size=\"14\" :stroke-width=\"2\" />\n {{ t.errors.editorLoading }}\n </div>\n </template>\n </div>\n </Teleport>\n\n <RichTextEditorContent\n :editor=\"editor\"\n :editor-content=\"EditorContent\"\n :is-loading=\"isLoading\"\n :init-error=\"initError\"\n @retry=\"retry\"\n />\n\n <RichTextLinkDialog\n :visible=\"showLinkDialog\"\n :is-editing-link=\"editor?.isActive('link') ?? false\"\n v-model:dialog-ref=\"linkDialogRef\"\n v-model:link-url=\"linkUrl\"\n v-model:link-color=\"linkColor\"\n @close=\"closeLinkDialog\"\n @insert=\"insertLink\"\n @remove=\"removeLink\"\n @keydown=\"handleLinkKeydown\"\n />\n </div>\n</template>\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { F as e, M as t, T as n, V as r, Z as i, d as a, f as o, g as s, h as c, it as l, l as u, m as d, n as f, p, x as m, y as h } from "./draggable-D840DIZB.js";
2
- import { D as g } from "./useEditorCore-BA6mspzD.js";
2
+ import { D as g } from "./useEditorCore-CzMhD6Gd.js";
3
3
  import { z as _ } from "./keys-D7kUmfpE.js";
4
4
  import { n as v } from "./usePopoverPosition-Bx0QewTv.js";
5
5
  //#region src/components/TplModal.vue?vue&type=script&setup=true&lang.ts
@@ -47,4 +47,4 @@ var y = ["data-tpl-theme"], b = /* @__PURE__ */ m({
47
47
  //#endregion
48
48
  export { b as t };
49
49
 
50
- //# sourceMappingURL=TplModal-CH9RzKF7.js.map
50
+ //# sourceMappingURL=TplModal-BN53d9Se.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TplModal-CH9RzKF7.js","names":[],"sources":["../../../src/components/TplModal.vue","../../../src/components/TplModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useFocusTrap } from \"../composables\";\nimport { usePopoverRoot } from \"../composables/usePopoverRoot\";\nimport { UI_THEME_KEY } from \"../keys\";\nimport { computed, inject, ref } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"keydown\", event: KeyboardEvent): void;\n}>();\n\nconst dialogRef = ref<HTMLElement | null>(null);\nconst isVisible = computed(() => props.visible);\nuseFocusTrap(dialogRef, isVisible);\n\nconst tplUiTheme = inject(UI_THEME_KEY);\nconst popoverRoot = usePopoverRoot();\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Escape\") {\n emit(\"close\");\n }\n emit(\"keydown\", event);\n}\n</script>\n\n<template>\n <Teleport v-if=\"popoverRoot\" :to=\"popoverRoot\">\n <Transition\n enter-active-class=\"tpl:transition tpl:duration-150\"\n enter-from-class=\"tpl:opacity-0\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition tpl:duration-100\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <div\n v-if=\"visible\"\n :data-tpl-theme=\"tplUiTheme\"\n class=\"tpl tpl:fixed tpl:inset-0 tpl:z-modal tpl:flex tpl:items-center tpl:justify-center\"\n style=\"\n background-color: var(--tpl-overlay);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n \"\n @click.self=\"emit('close')\"\n @keydown=\"handleKeydown\"\n >\n <div ref=\"dialogRef\">\n <slot />\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { useFocusTrap } from \"../composables\";\nimport { usePopoverRoot } from \"../composables/usePopoverRoot\";\nimport { UI_THEME_KEY } from \"../keys\";\nimport { computed, inject, ref } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"keydown\", event: KeyboardEvent): void;\n}>();\n\nconst dialogRef = ref<HTMLElement | null>(null);\nconst isVisible = computed(() => props.visible);\nuseFocusTrap(dialogRef, isVisible);\n\nconst tplUiTheme = inject(UI_THEME_KEY);\nconst popoverRoot = usePopoverRoot();\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Escape\") {\n emit(\"close\");\n }\n emit(\"keydown\", event);\n}\n</script>\n\n<template>\n <Teleport v-if=\"popoverRoot\" :to=\"popoverRoot\">\n <Transition\n enter-active-class=\"tpl:transition tpl:duration-150\"\n enter-from-class=\"tpl:opacity-0\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition tpl:duration-100\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <div\n v-if=\"visible\"\n :data-tpl-theme=\"tplUiTheme\"\n class=\"tpl tpl:fixed tpl:inset-0 tpl:z-modal tpl:flex tpl:items-center tpl:justify-center\"\n style=\"\n background-color: var(--tpl-overlay);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n \"\n @click.self=\"emit('close')\"\n @keydown=\"handleKeydown\"\n >\n <div ref=\"dialogRef\">\n <slot />\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n"],"mappings":";;;;;;;;;;EAMA,IAAM,IAAQ,GAIR,IAAO,GAKP,IAAY,EAAwB,IAAI;EAE9C,EAAa,GADK,QAAe,EAAM,OACf,CAAS;EAEjC,IAAM,IAAa,EAAO,CAAY,GAChC,IAAc,EAAe;EAEnC,SAAS,EAAc,GAA4B;GAIjD,AAHI,EAAM,QAAQ,YAChB,EAAK,OAAO,GAEd,EAAK,WAAW,CAAK;EACvB;mBAIkB,EAAA,CAAA,KAAA,EAAA,GAAhB,EA0BW,GAAA;;GA1BmB,IAAI,EAAA,CAAA;MAChC,EAwBa,GAAA;GAvBX,sBAAmB;GACnB,oBAAiB;GACjB,kBAAe;GACf,sBAAmB;GACnB,oBAAiB;GACjB,kBAAe;;oBAiBT,CAdE,EAAA,WAAA,EAAA,GADR,EAeM,OAAA;;IAbH,kBAAgB,EAAA,CAAA;IACjB,OAAM;IACN,OAAA;KAAA,oBAAA;KAAA,mBAAA;KAAA,2BAAA;IAAA;IAKC,SAAK,AAAA,EAAA,OAAA,GAAA,MAAO,EAAI,OAAA,GAAA,CAAA,MAAA,CAAA;IAChB,WAAS;OAEV,EAEM,OAAA;aAFG;IAAJ,KAAI;OACP,EAAQ,EAAA,QAAA,SAAA,CAAA,GAAA,GAAA,CAAA,GAAA,IAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"TplModal-BN53d9Se.js","names":[],"sources":["../../../src/components/TplModal.vue","../../../src/components/TplModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useFocusTrap } from \"../composables\";\nimport { usePopoverRoot } from \"../composables/usePopoverRoot\";\nimport { UI_THEME_KEY } from \"../keys\";\nimport { computed, inject, ref } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"keydown\", event: KeyboardEvent): void;\n}>();\n\nconst dialogRef = ref<HTMLElement | null>(null);\nconst isVisible = computed(() => props.visible);\nuseFocusTrap(dialogRef, isVisible);\n\nconst tplUiTheme = inject(UI_THEME_KEY);\nconst popoverRoot = usePopoverRoot();\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Escape\") {\n emit(\"close\");\n }\n emit(\"keydown\", event);\n}\n</script>\n\n<template>\n <Teleport v-if=\"popoverRoot\" :to=\"popoverRoot\">\n <Transition\n enter-active-class=\"tpl:transition tpl:duration-150\"\n enter-from-class=\"tpl:opacity-0\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition tpl:duration-100\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <div\n v-if=\"visible\"\n :data-tpl-theme=\"tplUiTheme\"\n class=\"tpl tpl:fixed tpl:inset-0 tpl:z-modal tpl:flex tpl:items-center tpl:justify-center\"\n style=\"\n background-color: var(--tpl-overlay);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n \"\n @click.self=\"emit('close')\"\n @keydown=\"handleKeydown\"\n >\n <div ref=\"dialogRef\">\n <slot />\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n","<script setup lang=\"ts\">\nimport { useFocusTrap } from \"../composables\";\nimport { usePopoverRoot } from \"../composables/usePopoverRoot\";\nimport { UI_THEME_KEY } from \"../keys\";\nimport { computed, inject, ref } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"keydown\", event: KeyboardEvent): void;\n}>();\n\nconst dialogRef = ref<HTMLElement | null>(null);\nconst isVisible = computed(() => props.visible);\nuseFocusTrap(dialogRef, isVisible);\n\nconst tplUiTheme = inject(UI_THEME_KEY);\nconst popoverRoot = usePopoverRoot();\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Escape\") {\n emit(\"close\");\n }\n emit(\"keydown\", event);\n}\n</script>\n\n<template>\n <Teleport v-if=\"popoverRoot\" :to=\"popoverRoot\">\n <Transition\n enter-active-class=\"tpl:transition tpl:duration-150\"\n enter-from-class=\"tpl:opacity-0\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition tpl:duration-100\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <div\n v-if=\"visible\"\n :data-tpl-theme=\"tplUiTheme\"\n class=\"tpl tpl:fixed tpl:inset-0 tpl:z-modal tpl:flex tpl:items-center tpl:justify-center\"\n style=\"\n background-color: var(--tpl-overlay);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n \"\n @click.self=\"emit('close')\"\n @keydown=\"handleKeydown\"\n >\n <div ref=\"dialogRef\">\n <slot />\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n"],"mappings":";;;;;;;;;;EAMA,IAAM,IAAQ,GAIR,IAAO,GAKP,IAAY,EAAwB,IAAI;EAE9C,EAAa,GADK,QAAe,EAAM,OACf,CAAS;EAEjC,IAAM,IAAa,EAAO,CAAY,GAChC,IAAc,EAAe;EAEnC,SAAS,EAAc,GAA4B;GAIjD,AAHI,EAAM,QAAQ,YAChB,EAAK,OAAO,GAEd,EAAK,WAAW,CAAK;EACvB;mBAIkB,EAAA,CAAA,KAAA,EAAA,GAAhB,EA0BW,GAAA;;GA1BmB,IAAI,EAAA,CAAA;MAChC,EAwBa,GAAA;GAvBX,sBAAmB;GACnB,oBAAiB;GACjB,kBAAe;GACf,sBAAmB;GACnB,oBAAiB;GACjB,kBAAe;;oBAiBT,CAdE,EAAA,WAAA,EAAA,GADR,EAeM,OAAA;;IAbH,kBAAgB,EAAA,CAAA;IACjB,OAAM;IACN,OAAA;KAAA,oBAAA;KAAA,mBAAA;KAAA,2BAAA;IAAA;IAKC,SAAK,AAAA,EAAA,OAAA,GAAA,MAAO,EAAI,OAAA,GAAA,CAAA,MAAA,CAAA;IAChB,WAAS;OAEV,EAEM,OAAA;aAFG;IAAJ,KAAI;OACP,EAAQ,EAAA,QAAA,SAAA,CAAA,GAAA,GAAA,CAAA,GAAA,IAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,CAAA"}
@@ -0,0 +1,66 @@
1
+ import { M as e, P as t, ct as n, f as r, g as i, it as a, m as o, p as s, st as c, u as l, x as u } from "./draggable-D840DIZB.js";
2
+ import { t as d } from "./useI18n-8Wd7G-cq.js";
3
+ import { E as f, Q as p } from "./icons-CYWw8Xfp.js";
4
+ //#region src/components/ViewportToggle.vue?vue&type=script&setup=true&lang.ts
5
+ var m = ["aria-label"], h = [
6
+ "aria-checked",
7
+ "aria-label",
8
+ "title",
9
+ "onClick"
10
+ ], g = /* @__PURE__ */ u({
11
+ __name: "ViewportToggle",
12
+ props: { viewport: {} },
13
+ emits: ["change"],
14
+ setup(u, { emit: g }) {
15
+ let _ = u, v = g, { t: y } = d(), b = r(() => [{
16
+ value: "desktop",
17
+ label: y.viewport.desktop
18
+ }, {
19
+ value: "mobile",
20
+ label: y.viewport.mobile
21
+ }]), x = r(() => `translateX(${b.value.findIndex((e) => e.value === _.viewport) * 100}%)`);
22
+ return (r, d) => (e(), i("div", {
23
+ role: "radiogroup",
24
+ "aria-label": a(y).viewport.label,
25
+ class: "tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1",
26
+ style: c({
27
+ gridTemplateColumns: `repeat(${b.value.length}, 1fr)`,
28
+ backgroundColor: "var(--tpl-bg-hover)"
29
+ })
30
+ }, [s("div", {
31
+ class: "tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]",
32
+ style: c({
33
+ left: "4px",
34
+ width: `calc((100% - 8px) / ${b.value.length})`,
35
+ transform: x.value,
36
+ backgroundColor: "var(--tpl-bg)",
37
+ boxShadow: "var(--tpl-shadow)",
38
+ transition: "transform 120ms cubic-bezier(0.16, 1, 0.3, 1)"
39
+ })
40
+ }, null, 4), (e(!0), i(l, null, t(b.value, (t) => (e(), i("button", {
41
+ key: t.value,
42
+ role: "radio",
43
+ "aria-checked": u.viewport === t.value,
44
+ "aria-label": t.label,
45
+ 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",
46
+ style: c({
47
+ color: u.viewport === t.value ? "var(--tpl-primary)" : "var(--tpl-text-muted)",
48
+ transition: "color 120ms cubic-bezier(0.16, 1, 0.3, 1)"
49
+ }),
50
+ title: t.label,
51
+ onClick: (e) => v("change", t.value)
52
+ }, [t.value === "desktop" ? (e(), o(a(p), {
53
+ key: 0,
54
+ size: 18,
55
+ "stroke-width": 1.5
56
+ })) : (e(), o(a(f), {
57
+ key: 1,
58
+ size: 18,
59
+ "stroke-width": 1.5
60
+ })), s("span", null, n(t.label), 1)], 12, h))), 128))], 12, m));
61
+ }
62
+ });
63
+ //#endregion
64
+ export { g as t };
65
+
66
+ //# sourceMappingURL=ViewportToggle-CFmF-N4i.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ViewportToggle-CFmF-N4i.js","names":[],"sources":["../../../src/components/ViewportToggle.vue","../../../src/components/ViewportToggle.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { ViewportSize } from \"@templatical/types\";\nimport { Monitor, Smartphone } 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: \"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 <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 } 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: \"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 <Smartphone v-else :size=\"18\" :stroke-width=\"1.5\" />\n <span>{{ vp.label }}</span>\n </button>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;EAMA,IAAM,IAAQ,GAIR,IAAO,GAIP,EAAE,SAAM,EAAQ,GAEhB,IAAY,QAAe,CAC/B;GAAE,OAAO;GAA2B,OAAO,EAAE,SAAS;EAAQ,GAC9D;GAAE,OAAO;GAA0B,OAAO,EAAE,SAAS;EAAO,CAC9D,CAAC,GAEK,IAAa,QAEV,cADO,EAAU,MAAM,WAAW,MAAO,EAAG,UAAU,EAAM,QAC9C,IAAQ,IAAI,GAClC;yBAIC,EA2CM,OAAA;GA1CJ,MAAK;GACJ,cAAY,EAAA,CAAA,CAAC,CAAC,SAAS;GACxB,OAAM;GACL,OAAK,EAAA;mCAAyC,EAAA,MAAU,OAAM;;;MAM/D,EAUO,OAAA;GATL,OAAM;GACL,OAAK,EAAA;;kCAA+D,EAAA,MAAU,OAAM;eAAwB,EAAA;;;;;uBAU/G,EAoBS,GAAA,MAAA,EAnBM,EAAA,QAAN,YADT,EAoBS,UAAA;GAlBN,KAAK,EAAG;GACT,MAAK;GACJ,gBAAc,EAAA,aAAa,EAAG;GAC9B,cAAY,EAAG;GAChB,OAAM;GACL,OAAK,EAAA;WAA6B,EAAA,aAAa,EAAG,QAAA,uBAAA;;;GAOlD,OAAO,EAAG;GACV,UAAK,MAAE,EAAI,UAAW,EAAG,KAAK;MAEhB,EAAG,UAAK,aAAA,EAAA,GAAvB,EAAwE,EAAA,CAAA,GAAA;;GAAhC,MAAM;GAAK,gBAAc;cACjE,EAAoD,EAAA,CAAA,GAAA;;GAAhC,MAAM;GAAK,gBAAc;OAC7C,EAA2B,QAAA,MAAA,EAAlB,EAAG,KAAK,GAAA,CAAA,CAAA,GAAA,IAAA,CAAA"}
@@ -569,6 +569,20 @@ var e = {
569
569
  daysAgo: "fa {days} d"
570
570
  }
571
571
  },
572
+ testEmail: {
573
+ title: "Envia un correu de prova",
574
+ recipientLabel: "Destinatari",
575
+ recipientPlaceholder: "tu@exemple.cat",
576
+ invalidAddress: "Introduïu una adreça de correu vàlida.",
577
+ preview: "Previsualització",
578
+ previewHint: "Les etiquetes de combinació es mostren sense resoldre — el vostre backend les omple.",
579
+ recipientNotAllowed: "Aquesta adreça no és a la llista de destinataris permesos.",
580
+ send: "Envia",
581
+ sending: "S'està enviant...",
582
+ cancel: "Cancel·la",
583
+ success: "Correu de prova enviat correctament",
584
+ button: "Prova"
585
+ },
572
586
  sidebarNav: {
573
587
  browseSavedBlocks: "Explora els blocs desats",
574
588
  expandSidebar: "Expandeix la barra lateral de blocs",
@@ -607,4 +621,4 @@ var e = {
607
621
  //#endregion
608
622
  export { e as default };
609
623
 
610
- //# sourceMappingURL=ca-BBgad6y5.js.map
624
+ //# sourceMappingURL=ca-D47BxSGu.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ca-BBgad6y5.js","names":[],"sources":["../../../src/i18n/locales/ca.ts"],"sourcesContent":["import type en from \"./en\";\n\nconst ca: typeof en = {\n // Footer (OSS only)\n footer: {\n poweredBy: \"Amb la tecnologia de\",\n openSource: \"Codi obert\",\n },\n\n // History (undo/redo)\n history: {\n undo: \"Desfer\",\n redo: \"Refer\",\n collabWarning: \"Desfer pot afectar els canvis recents dels col·laboradors\",\n },\n\n // Viewport toggle\n viewport: {\n label: \"Àrea de visualització\",\n desktop: \"Escriptori\",\n mobile: \"Mòbil\",\n },\n\n // Dark mode preview\n darkMode: {\n enable: \"Vista prèvia del mode fosc\",\n disable: \"Vista prèvia del mode clar\",\n },\n\n // Preview mode\n previewMode: {\n enable: \"Mode de vista prèvia\",\n disable: \"Surt de la vista prèvia\",\n },\n\n // Sidebar - Block types\n blocks: {\n section: \"Secció\",\n image: \"Imatge\",\n title: \"Títol\",\n paragraph: \"Paràgraf\",\n button: \"Botó\",\n divider: \"Divisor\",\n video: \"Vídeo\",\n social: \"Xarxes socials\",\n spacer: \"Espaiador\",\n html: \"HTML\",\n menu: \"Menú\",\n table: \"Taula\",\n countdown: \"Compta enrere\",\n },\n\n // Right sidebar\n sidebar: {\n content: \"Contingut\",\n settings: \"Configuració\",\n noSelection: \"Cap element seleccionat\",\n noSelectionHint: \"Selecciona un bloc al llenç per editar-lo\",\n },\n\n // Toolbar - Common\n toolbar: {\n duplicate: \"Duplica\",\n delete: \"Elimina\",\n },\n\n // Title editor toolbar\n titleEditor: {\n toolbar: \"Format del títol\",\n bold: \"Negreta (Ctrl+B)\",\n italic: \"Cursiva (Ctrl+I)\",\n addLink: \"Afegeix un enllaç\",\n },\n\n // Paragraph editor toolbar\n paragraphEditor: {\n toolbar: \"Format del text\",\n bold: \"Negreta (Ctrl+B)\",\n italic: \"Cursiva (Ctrl+I)\",\n underline: \"Subratllat (Ctrl+U)\",\n strikethrough: \"Text ratllat\",\n subscript: \"Subíndex\",\n superscript: \"Superíndex\",\n addLink: \"Afegeix un enllaç\",\n bulletList: \"Llista de vinyetes\",\n numberedList: \"Llista numerada\",\n alignLeft: \"Alinea a l'esquerra\",\n alignCenter: \"Alinea al centre\",\n alignRight: \"Alinea a la dreta\",\n clearFormatting: \"Neteja el format\",\n insertEmoji: \"Insereix un emoji\",\n fontFamily: \"Tipus de lletra\",\n defaultFont: \"Per defecte\",\n fontSize: \"Mida de la font\",\n defaultSize: \"Per defecte\",\n textColor: \"Color del text\",\n highlightColor: \"Color de ressaltat\",\n lineHeight: \"Interlínia\",\n letterSpacing: \"Espaiat de lletres\",\n emojiItemLabel: \"Insereix l'emoji {emoji}\",\n closeEmojiPicker: \"Tanca el selector d'emojis\",\n },\n\n // Block actions (BlockWrapper)\n blockActions: {\n drag: \"Arrossega per reordenar, o prem Espai per moure amb el teclat\",\n dragLifted:\n \"Movent {block}. Utilitza les fletxes amunt i avall per reposicionar, Espai o Retorn per deixar anar, Esc per cancel·lar.\",\n duplicate: \"Duplica el bloc\",\n delete: \"Elimina el bloc\",\n hiddenOnViewport: \"Ocult a {viewport}\",\n saveAsBlock: \"Desa com a bloc\",\n conditionToggle: \"Alterna la condició de visualització\",\n comments: \"Comentaris ({count})\",\n lifted: \"{block} aixecat. Posició {position} de {total}.\",\n moved: \"{block} mogut a la posició {position} de {total}.\",\n dropped: \"{block} deixat anar a la posició {position} de {total}.\",\n cancelled:\n \"Moviment cancel·lat. {block} ha tornat a la posició {position}.\",\n },\n\n // Toolbar - Section\n section: {\n dropHere: \"Deixa anar els blocs aquí\",\n columns: \"Columnes\",\n column1: \"1 columna\",\n column2: \"2 columnes\",\n column3: \"3 columnes\",\n ratio12: \"Proporció 1:2\",\n ratio21: \"Proporció 2:1\",\n borderRadius: \"Radi de la vora\",\n wrapperEnable: \"Afegeix un contenidor\",\n stackOnMobile: \"Apila en mòbil\",\n },\n\n // Text editor link dialog\n linkDialog: {\n editLink: \"Edita l'enllaç\",\n insertLink: \"Insereix l'enllaç\",\n updateLink: \"Actualitza l'enllaç\",\n removeLink: \"Elimina l'enllaç\",\n cancel: \"Cancel·la\",\n urlPlaceholder: \"https://example.com\",\n urlLabel: \"URL\",\n colorLabel: \"Color de l'enllaç\",\n },\n\n // Toolbar - Title\n title: {\n level: \"Nivell d'encapçalament\",\n heading1: \"Encapçalament 1 (36px)\",\n heading2: \"Encapçalament 2 (28px)\",\n heading3: \"Encapçalament 3 (22px)\",\n heading4: \"Encapçalament 4 (18px)\",\n fontFamily: \"Tipus de lletra\",\n inheritFont: \"Utilitza la font de la plantilla\",\n color: \"Color\",\n align: \"Alineació\",\n alignLeft: \"Esquerra\",\n alignCenter: \"Centre\",\n alignRight: \"Dreta\",\n },\n\n // Emoji picker\n emoji: {\n smileys: \"Emoticones\",\n gestures: \"Gestos\",\n objects: \"Objectes\",\n },\n\n // Toolbar - Image\n image: {\n imageUrl: \"URL de la imatge\",\n imageUrlPlaceholder: \"https://...\",\n altText: \"Text alternatiu\",\n altTextPlaceholder: \"Descripció de la imatge\",\n width: \"Amplada\",\n fullWidth: \"Amplada completa\",\n widthCustom: \"Personalitzada\",\n linkUrl: \"URL de l'enllaç\",\n openInNewTab: \"Obre en una pestanya nova\",\n placeholderUrl: \"Imatge de mostra\",\n placeholderUrlPlaceholder: \"https://... (només per al disseny)\",\n placeholderUrlTooltip:\n \"Com que la URL de la imatge utilitza una etiqueta de fusió, podeu proporcionar una imatge real aquí per previsualitzar el disseny mentre dissenyeu. Això no s'inclou a la sortida final.\",\n clickToAdd: \"Feu clic per afegir la URL de la imatge\",\n browseMedia: \"Explora multimèdia\",\n dropToUpload: \"Deixeu anar la imatge per pujar-la\",\n uploading: \"Pujant…\",\n decorative: \"Imatge decorativa\",\n decorativeHint:\n \"Ocult per als lectors de pantalla. Utilitzeu-lo només per a espaiadors i detalls visuals.\",\n },\n\n // Toolbar - Video\n video: {\n videoUrl: \"URL del vídeo\",\n videoUrlPlaceholder: \"https://youtube.com/...\",\n youtube: \"YouTube\",\n vimeo: \"Vimeo\",\n detected: \"Vídeo detectat: la miniatura es generarà automàticament\",\n openInNewTab: \"Obre en una pestanya nova\",\n customThumbnail: \"Miniatura personalitzada\",\n optional: \"(opcional)\",\n thumbnailPlaceholder: \"Generat automàticament des de la URL del vídeo\",\n altText: \"Text alternatiu\",\n altTextPlaceholder: \"Descripció del vídeo\",\n width: \"Amplada\",\n fullWidth: \"Amplada completa\",\n placeholderUrl: \"Miniatura de mostra\",\n placeholderUrlPlaceholder: \"https://... (només per al disseny)\",\n placeholderUrlTooltip:\n \"Com que la URL del vídeo utilitza una etiqueta de fusió, podeu proporcionar una miniatura real aquí per previsualitzar el disseny mentre dissenyeu. Això no s'inclou a la sortida final.\",\n addVideo: \"Afegeix una URL de vídeo\",\n },\n\n // Toolbar - Button\n button: {\n fontFamily: \"Tipus de lletra\",\n inheritFont: \"Utilitza la font de la plantilla\",\n text: \"Text\",\n url: \"URL\",\n urlPlaceholder: \"https://...\",\n openInNewTab: \"Obre en una pestanya nova\",\n background: \"Fons\",\n textColor: \"Color del text\",\n borderRadius: \"Radi de la vora\",\n fontSize: \"Mida de la font\",\n width: \"Amplada\",\n widthAuto: \"Ajusta al contingut\",\n fullWidth: \"Amplada completa\",\n widthCustom: \"Personalitzada\",\n },\n\n // Toolbar - Divider\n divider: {\n style: \"Estil\",\n solid: \"Sòlid\",\n dashed: \"Amb guions\",\n dotted: \"Amb punts\",\n color: \"Color\",\n thickness: \"Gruix\",\n },\n\n // Toolbar - Social Icons\n social: {\n icons: \"Icones\",\n addIcon: \"Afegeix una icona\",\n addIcons: \"Afegeix icones de xarxes socials\",\n removeIcon: \"Elimina\",\n platform: \"Plataforma\",\n url: \"URL\",\n urlPlaceholder: \"https://...\",\n style: \"Estil\",\n styleSolid: \"Sòlid\",\n styleOutlined: \"Contornejat\",\n styleRounded: \"Arrodonit\",\n styleSquare: \"Quadrat\",\n styleCircle: \"Cercle\",\n size: \"Mida\",\n sizeSmall: \"S\",\n sizeMedium: \"M\",\n sizeLarge: \"L\",\n spacing: \"Espaiat\",\n align: \"Alineació\",\n platforms: {\n facebook: \"Facebook\",\n twitter: \"X (Twitter)\",\n instagram: \"Instagram\",\n linkedin: \"LinkedIn\",\n youtube: \"YouTube\",\n tiktok: \"TikTok\",\n pinterest: \"Pinterest\",\n email: \"Correu electrònic\",\n website: \"Lloc web\",\n whatsapp: \"WhatsApp\",\n telegram: \"Telegram\",\n discord: \"Discord\",\n snapchat: \"Snapchat\",\n reddit: \"Reddit\",\n github: \"GitHub\",\n dribbble: \"Dribbble\",\n behance: \"Behance\",\n },\n },\n\n // Toolbar - Menu\n menu: {\n items: \"Elements del menú\",\n addItem: \"Afegeix un element\",\n removeItem: \"Elimina\",\n text: \"Text\",\n url: \"URL\",\n urlPlaceholder: \"https://example.com\",\n openInNewTab: \"Obre en una pestanya nova\",\n bold: \"Negreta\",\n underline: \"Subratllat\",\n color: \"Color\",\n linkColor: \"Color de l'enllaç\",\n fontSize: \"Mida de la font\",\n fontFamily: \"Tipus de lletra\",\n separator: \"Separador\",\n separatorColor: \"Color del separador\",\n spacing: \"Espaiat\",\n textAlign: \"Alineació\",\n addLinks: \"Afegeix enllaços al menú\",\n },\n\n // Toolbar - Table\n table: {\n dimensions: \"Dimensions\",\n rows: \"Files\",\n columns: \"Columnes\",\n addRow: \"Afegeix una fila\",\n removeRow: \"Elimina la fila\",\n addColumn: \"Afegeix una columna\",\n removeColumn: \"Elimina la columna\",\n hasHeaderRow: \"Fila de capçalera\",\n headerBackgroundColor: \"Fons de la capçalera\",\n noHeaderBg: \"Sense fons\",\n borderColor: \"Color de la vora\",\n borderWidth: \"Gruix de la vora\",\n cellPadding: \"Farciment de la cel·la\",\n fontFamily: \"Tipus de lletra\",\n fontSize: \"Mida de la font\",\n color: \"Color del text\",\n textAlign: \"Alineació\",\n cellPlaceholder: \"Introdueix el text...\",\n empty: \"Afegeix una taula\",\n },\n\n // Toolbar - Spacer\n spacer: {\n height: \"Alçada\",\n },\n\n // Toolbar - Countdown\n countdown: {\n targetDate: \"Data límit\",\n timezone: \"Zona horària\",\n display: \"Visualització\",\n days: \"Dies\",\n hours: \"Hores\",\n minutes: \"Minuts\",\n seconds: \"Segons\",\n separator: \"Separador\",\n fontFamily: \"Tipus de lletra\",\n inheritFont: \"Per defecte\",\n digitFontSize: \"Mida dels dígits\",\n digitColor: \"Color dels dígits\",\n labelColor: \"Color de l'etiqueta\",\n labelFontSize: \"Mida de l'etiqueta\",\n background: \"Fons\",\n labels: \"Etiquetes\",\n expiry: \"Missatge de caducitat\",\n expiredMessagePlaceholder: \"Aquesta oferta ha caducat\",\n expiredImageUrl: \"URL de la imatge caducada\",\n hideOnExpiry: \"Oculta quan hagi caducat\",\n setDate: \"Defineix una data límit al panell de configuració\",\n hidden: \"Ocult (caducat)\",\n },\n\n // Custom Blocks\n customBlocks: {\n definitionNotFound:\n \"Tipus de bloc desconegut: aquest bloc no està registrat\",\n renderError:\n \"No s'ha pogut renderitzar aquest bloc. Comproveu si hi ha errors a la plantilla del bloc.\",\n fields: {\n required: \"Obligatori\",\n addItem: \"Afegeix un element\",\n removeItem: \"Elimina\",\n maxItemsReached: \"S'ha arribat al límit màxim d'elements\",\n minItemsRequired: \"Es requereix un mínim de {count} elements\",\n },\n toolbar: {\n noDefinition:\n \"Enregistreu aquest tipus de bloc a la configuració de l'SDK per editar-ne les propietats\",\n },\n dataSource: {\n fetchButton: \"Carrega el contingut\",\n changeButton: \"Canvia\",\n fetching: \"Carregant...\",\n readOnlyTooltip: \"Aquest valor es carrega des de la vostra font de dades\",\n fetchError: \"No s'ha pogut carregar el contingut\",\n },\n },\n\n // Toolbar - HTML\n html: {\n content: \"Contingut HTML\",\n preview: \"Bloc HTML personalitzat\",\n empty: \"Afegeix contingut HTML al panell\",\n sanitizationHint:\n \"L'HTML s'exporta tal qual i no es depura — assegura't que el contingut sigui segur.\",\n },\n\n // Toolbar - Common block settings\n blockSettings: {\n spacing: \"Espaiat\",\n padding: \"Farciment\",\n background: \"Fons\",\n color: \"Color\",\n display: \"Visualització\",\n showOnDesktop: \"Mostra a l'escriptori\",\n showOnMobile: \"Mostra al mòbil\",\n hiddenOnDevice: \"Ocult a {device}\",\n displayCondition: \"Condició de visualització\",\n selectCondition: \"Selecciona una condició\",\n removeCondition: \"Elimina la condició\",\n noCondition: \"Sempre visible\",\n conditionApplied: \"Condició aplicada\",\n customCondition: \"Condició personalitzada\",\n customConditionLabel: \"Nom de la condició\",\n customConditionBefore: \"Abans (lògica d'obertura)\",\n customConditionAfter: \"Després (lògica de tancament)\",\n applyCondition: \"Aplica\",\n cancelCondition: \"Cancel·la\",\n customBadge: \"Personalitzat\",\n restoreHiddenBlocks: \"Mostra tots els blocs ocults\",\n },\n\n // Template settings\n templateSettings: {\n layout: \"Disposició\",\n widthPreset: \"Amplada predefinida\",\n customWidth: \"Amplada personalitzada\",\n appearance: \"Aparença\",\n backgroundColor: \"Color de fons\",\n textColor: \"Color del text\",\n linkColor: \"Color de l'enllaç\",\n linkUnderline: \"Subratllar enllaços\",\n fontFamily: \"Tipus de lletra\",\n preheaderText: \"Text de la precapçalera\",\n preheaderTextPlaceholder:\n \"Text de vista prèvia que es mostra després de la línia de l'assumpte a la bústia...\",\n preheaderTextHint:\n \"Aquest text apareix després de la línia de l'assumpte a les vistes prèvies dels clients de correu. Admet etiquetes de fusió.\",\n language: \"Idioma\",\n contentLocale: \"Idioma del contingut\",\n contentLocaleHint:\n \"Codi BCP 47 (p. ex. ca, en, de). Estableix l'atribut lang del correu renderitzat perquè els lectors de pantalla pronunciïn el contingut correctament.\",\n tips: \"Consells\",\n tip1: \"600px és l'amplada estàndard per a les plantilles de correu electrònic\",\n tip2: \"Utilitzeu fonts segures per a la web per obtenir la millor compatibilitat\",\n tip3: \"Els fons clars funcionen millor per a la llegibilitat\",\n },\n\n // Spacing control\n spacingControl: {\n lockAll: \"Bloqueja tots els costats\",\n unlock: \"Desbloqueja els costats\",\n top: \"Superior\",\n right: \"Dret\",\n bottom: \"Inferior\",\n left: \"Esquerre\",\n decreaseTop: \"Disminueix superior\",\n increaseTop: \"Augmenta superior\",\n decreaseLeft: \"Disminueix esquerre\",\n increaseLeft: \"Augmenta esquerre\",\n decreaseRight: \"Disminueix dret\",\n increaseRight: \"Augmenta dret\",\n decreaseBottom: \"Disminueix inferior\",\n increaseBottom: \"Augmenta inferior\",\n },\n\n // Color Picker\n colorPicker: {\n pickColor: \"Tria un color\",\n hexValue: \"Valor del color en Hex\",\n notSet: \"No definit\",\n clear: \"Neteja el color\",\n presetColors: \"Colors predefinits\",\n },\n\n // Merge Tag\n mergeTag: {\n clickToEdit: \"Feu clic per editar\",\n remove: \"Elimina l'etiqueta de fusió\",\n insert: \"Insereix l'etiqueta de fusió\",\n insertShort: \"Etiqueta de fusió\",\n editValue: \"Edita el valor de l'etiqueta de fusió\",\n deleteMergeTag: \"Elimina l'etiqueta de fusió\",\n suggestionEmpty: \"No hi ha etiquetes de fusió coincidents\",\n picker: {\n title: \"Insereix l'etiqueta de fusió\",\n searchPlaceholder: \"Cerca etiquetes de fusió\",\n searchAriaLabel: \"Cerca etiquetes de fusió\",\n noResults: \"No hi ha etiquetes de fusió coincidents\",\n empty: \"No hi ha etiquetes de fusió configurades\",\n otherGroup: \"Altres\",\n cancel: \"Cancel·la\",\n close: \"Tanca\",\n groupCount: \"{count}\",\n },\n },\n\n // Logic tags (standalone, separate from merge tags)\n logicTag: {\n insert: \"Insereix lògica\",\n insertShort: \"Lògica\",\n picker: {\n title: \"Insereix lògica\",\n searchPlaceholder: \"Cerca etiquetes de lògica\",\n searchAriaLabel: \"Cerca etiquetes de lògica\",\n noResults: \"No hi ha etiquetes de lògica coincidents\",\n empty: \"No hi ha etiquetes de lògica configurades\",\n otherGroup: \"Altres\",\n close: \"Tanca\",\n },\n },\n\n // Canvas\n canvas: {\n noBlocks: \"Encara no hi ha blocs\",\n dragHint: \"Comenceu des de zero arrossegant blocs des de la barra lateral\",\n dropHere: \"Deixeu anar aquí\",\n aiHintChat: \"o deixeu que\",\n aiHintChatSuffix:\n \"generi una plantilla completa per a vosaltres en pocs segons\",\n aiHintDesign:\n \"Teniu un disseny existent? Pugeu una captura de pantalla, una imatge o un PDF i\",\n aiHintDesignSuffix: \"el recrearà a l'instant\",\n },\n\n // Media Library (cloud)\n mediaLibrary: {\n title: \"Biblioteca multimèdia\",\n searchPlaceholder: \"Cerca fitxers...\",\n allFiles: \"Tots els fitxers\",\n filterAll: \"Tots els tipus\",\n filterImages: \"Imatges\",\n filterDocuments: \"Documents\",\n filterVideos: \"Vídeos\",\n filterAudio: \"Àudio\",\n newFolder: \"Nova carpeta\",\n folderName: \"Nom de la carpeta\",\n noFiles: \"No s'han trobat fitxers\",\n dropOrClick: \"Deixeu anar els fitxers aquí o feu clic per pujar-los\",\n acceptedFormats: \"Imatges, PDF, Vídeo, Àudio, Documents (màx. 10 MB)\",\n uploading: \"Pujant...\",\n uploadingProgress: \"Pujant {current} de {total}...\",\n selectImage: \"Selecciona la imatge\",\n selectFile: \"Selecciona el fitxer\",\n deleteSelected: \"Elimina\",\n copyUrl: \"Copia la URL\",\n copied: \"Copiat!\",\n browseMedia: \"Explora la biblioteca multimèdia\",\n renameFolder: \"Canvia el nom de la carpeta\",\n addSubfolder: \"Afegeix una subcarpeta\",\n subfolderName: \"Nom de la subcarpeta\",\n sortNewest: \"Els més nous primer\",\n sortOldest: \"Els més antics primer\",\n sortNameAsc: \"Nom A-Z\",\n sortNameDesc: \"Nom Z-A\",\n sortSizeAsc: \"Els més petits primer\",\n sortSizeDesc: \"Els més grans primer\",\n moveSelected: \"Mou\",\n moveToRoot: \"Tots els fitxers\",\n currentFolder: \"(actual)\",\n confirmDelete: \"Voleu eliminar aquest fitxer?\",\n renameFile: \"Canvia el nom\",\n editFile: \"Edita el fitxer\",\n fileName: \"Nom del fitxer\",\n altText: \"Text alternatiu\",\n altTextPlaceholder: \"Descriviu aquesta imatge per a l'accessibilitat\",\n saveChanges: \"Desa\",\n cancel: \"Cancel·la\",\n frequentlyUsed: \"Utilitzat freqüentment\",\n deleteWarningTitle: \"Elimina el fitxer\",\n deleteWarningMessage:\n \"Aquest fitxer s'eliminarà permanentment i no es podrà recuperar.\",\n deleteWarningUsageNote:\n \"Els fitxers següents s'utilitzen en plantilles. Eliminar-los pot trencar aquestes plantilles.\",\n deleteAnyway: \"Elimina el fitxer de totes maneres\",\n usedInTemplates: \"Utilitzat en {count} plantilla/es\",\n viewGrid: \"Vista de quadrícula\",\n viewList: \"Vista de llista\",\n showFolders: \"Mostra les carpetes\",\n hideFolders: \"Oculta les carpetes\",\n importFromUrl: \"Importa des d'una URL\",\n importUrlPlaceholder: \"https://example.com/image.jpg\",\n import: \"Importa\",\n importing: \"Important...\",\n importError: \"No s'ha pogut importar des de la URL\",\n conversionLabel: \"Mida\",\n conversionOriginal: \"Original\",\n conversionSmall: \"Petita (150px)\",\n conversionMedium: \"Mitjana (600px)\",\n conversionLarge: \"Gran (1200px)\",\n replaceFile: \"Substitueix el fitxer\",\n replaceWarningTitle: \"Substitueix el fitxer\",\n replaceWarningMessage:\n \"Esteu a punt de substituir aquest fitxer. El substitut ha de tenir la mateixa extensió de fitxer ({extension}).\",\n replaceWarningUsageNote:\n \"Aquest fitxer s'utilitza en {count} plantilla/es. Substituir-lo actualitzarà totes les referències.\",\n replaceSelectFile: \"Selecciona el fitxer de substitució\",\n replace: \"Substitueix\",\n replacing: \"Substituint...\",\n replaceError: \"No s'ha pogut substituir el fitxer\",\n saving: \"Desant...\",\n cropAspectRatio: \"Relació d'aspecte\",\n cropFree: \"Lliure\",\n cropSquare: \"1:1\",\n cropLandscape43: \"4:3\",\n cropLandscape169: \"16:9\",\n cropOriginal: \"Original\",\n cropMaxWidth: \"Amplada màxima\",\n cropMaxHeight: \"Alçada màxima\",\n cropOutputSize: \"Mida de sortida\",\n cropPixels: \"px\",\n cropOptional: \"(opcional)\",\n storageTooltip: \"{used} de {total} utilitzats (queden {remaining})\",\n },\n\n // Sidebar\n savedBlocks: {\n title: \"Blocs desats\",\n saveAsBlock: \"Desa com a bloc\",\n name: \"Nom\",\n namePlaceholder: \"p. ex. Encapçalament, Peu de pàgina, CTA...\",\n save: \"Desa el bloc\",\n saving: \"S'està desant...\",\n cancel: \"Cancel·la\",\n empty: \"Encara no hi ha blocs desats\",\n emptyHint:\n \"Desa blocs de les teves plantilles per reutilitzar-los més tard.\",\n noResults: \"Cap bloc desat coincideix amb la teva cerca\",\n search: \"Cerca blocs desats...\",\n loading: \"S'estan carregant els blocs desats…\",\n category: \"Categoria\",\n categoryPlaceholder: \"p. ex. Capçaleres, Promocions... (opcional)\",\n allCategories: \"Totes les categories\",\n filterByCategory: \"Filtra per categoria\",\n insert: \"Insereix\",\n rename: \"Canvia el nom\",\n delete: \"Elimina\",\n deleteConfirm: \"Vols eliminar aquest bloc desat?\",\n blockCount: \"{count} bloc(s)\",\n browse: \"Explora els blocs desats\",\n selectToPreview: \"Selecciona un bloc desat per previsualitzar-lo\",\n insertAtBeginning: \"Al principi\",\n insertAfterBlock: \"Després de {block}\",\n insertAtEnd: \"Al final\",\n insertPosition: \"Posició d'inserció\",\n close: \"Tanca\",\n pickToolbar: \"Selecció de bloc desat\",\n pickCount: \"{count} bloc(s) seleccionat(s)\",\n pickHint: \"Fes clic als blocs per afegir-los o treure'ls\",\n savingCount: \"S'estan desant {count} bloc(s):\",\n reorderHint: \"Arrossega per reordenar: els blocs es desen en aquest ordre\",\n reorderHandle: \"Reordena {block}, posició {position} de {total}\",\n reorderAnnouncement: \"{block} mogut a la posició {position} de {total}\",\n expand: \"Amplia\",\n collapse: \"Redueix\",\n expandPreview: \"Amplia la vista prèvia de {block}\",\n collapsePreview: \"Redueix la vista prèvia de {block}\",\n time: {\n justNow: \"Ara mateix\",\n minutesAgo: \"fa {minutes} min\",\n hoursAgo: \"fa {hours} h\",\n daysAgo: \"fa {days} d\",\n },\n },\n sidebarNav: {\n browseSavedBlocks: \"Explora els blocs desats\",\n expandSidebar: \"Expandeix la barra lateral de blocs\",\n palette: \"Paleta de blocs\",\n insertBlock: \"Insereix un bloc de tipus {block}\",\n },\n\n // Landmark region labels for assistive technology\n landmarks: {\n canvas: \"Llenç del correu\",\n blockToolbar: \"Propietats del bloc\",\n rightSidebar: \"Propietats del bloc i configuració de la plantilla\",\n reorderAnnouncements: \"Avisos de reordenació de blocs\",\n },\n\n // Design Reference (cloud)\n errors: {\n editorLoading: \"Carregant l'editor...\",\n editorLoadFailed: \"No s'ha pogut carregar l'editor.\",\n retry: \"Torna-ho a provar\",\n },\n\n issues: {\n panelTitle: \"Problemes\",\n panelTabLabel: \"Problemes\",\n groupErrors: \"Errors\",\n groupWarnings: \"Avisos\",\n groupInfo: \"Informació\",\n jump: \"Vés al bloc\",\n fix: \"Corregeix\",\n emptyState: \"Sense problemes, tot correcte.\",\n badgeError: \"Té errors\",\n badgeWarning: \"Té avisos\",\n issueCountTooltip: \"{count} problema/es\",\n },\n\n smallScreen: {\n title: \"Cal una pantalla més gran\",\n message:\n \"L'editor necessita més espai del que ofereix aquesta pantalla. Obriu-lo en una tauleta o ordinador d'escriptori per començar a editar.\",\n },\n};\n\nexport default ca;\n"],"mappings":"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"}
1
+ {"version":3,"file":"ca-D47BxSGu.js","names":[],"sources":["../../../src/i18n/locales/ca.ts"],"sourcesContent":["import type en from \"./en\";\n\nconst ca: typeof en = {\n // Footer (OSS only)\n footer: {\n poweredBy: \"Amb la tecnologia de\",\n openSource: \"Codi obert\",\n },\n\n // History (undo/redo)\n history: {\n undo: \"Desfer\",\n redo: \"Refer\",\n collabWarning: \"Desfer pot afectar els canvis recents dels col·laboradors\",\n },\n\n // Viewport toggle\n viewport: {\n label: \"Àrea de visualització\",\n desktop: \"Escriptori\",\n mobile: \"Mòbil\",\n },\n\n // Dark mode preview\n darkMode: {\n enable: \"Vista prèvia del mode fosc\",\n disable: \"Vista prèvia del mode clar\",\n },\n\n // Preview mode\n previewMode: {\n enable: \"Mode de vista prèvia\",\n disable: \"Surt de la vista prèvia\",\n },\n\n // Sidebar - Block types\n blocks: {\n section: \"Secció\",\n image: \"Imatge\",\n title: \"Títol\",\n paragraph: \"Paràgraf\",\n button: \"Botó\",\n divider: \"Divisor\",\n video: \"Vídeo\",\n social: \"Xarxes socials\",\n spacer: \"Espaiador\",\n html: \"HTML\",\n menu: \"Menú\",\n table: \"Taula\",\n countdown: \"Compta enrere\",\n },\n\n // Right sidebar\n sidebar: {\n content: \"Contingut\",\n settings: \"Configuració\",\n noSelection: \"Cap element seleccionat\",\n noSelectionHint: \"Selecciona un bloc al llenç per editar-lo\",\n },\n\n // Toolbar - Common\n toolbar: {\n duplicate: \"Duplica\",\n delete: \"Elimina\",\n },\n\n // Title editor toolbar\n titleEditor: {\n toolbar: \"Format del títol\",\n bold: \"Negreta (Ctrl+B)\",\n italic: \"Cursiva (Ctrl+I)\",\n addLink: \"Afegeix un enllaç\",\n },\n\n // Paragraph editor toolbar\n paragraphEditor: {\n toolbar: \"Format del text\",\n bold: \"Negreta (Ctrl+B)\",\n italic: \"Cursiva (Ctrl+I)\",\n underline: \"Subratllat (Ctrl+U)\",\n strikethrough: \"Text ratllat\",\n subscript: \"Subíndex\",\n superscript: \"Superíndex\",\n addLink: \"Afegeix un enllaç\",\n bulletList: \"Llista de vinyetes\",\n numberedList: \"Llista numerada\",\n alignLeft: \"Alinea a l'esquerra\",\n alignCenter: \"Alinea al centre\",\n alignRight: \"Alinea a la dreta\",\n clearFormatting: \"Neteja el format\",\n insertEmoji: \"Insereix un emoji\",\n fontFamily: \"Tipus de lletra\",\n defaultFont: \"Per defecte\",\n fontSize: \"Mida de la font\",\n defaultSize: \"Per defecte\",\n textColor: \"Color del text\",\n highlightColor: \"Color de ressaltat\",\n lineHeight: \"Interlínia\",\n letterSpacing: \"Espaiat de lletres\",\n emojiItemLabel: \"Insereix l'emoji {emoji}\",\n closeEmojiPicker: \"Tanca el selector d'emojis\",\n },\n\n // Block actions (BlockWrapper)\n blockActions: {\n drag: \"Arrossega per reordenar, o prem Espai per moure amb el teclat\",\n dragLifted:\n \"Movent {block}. Utilitza les fletxes amunt i avall per reposicionar, Espai o Retorn per deixar anar, Esc per cancel·lar.\",\n duplicate: \"Duplica el bloc\",\n delete: \"Elimina el bloc\",\n hiddenOnViewport: \"Ocult a {viewport}\",\n saveAsBlock: \"Desa com a bloc\",\n conditionToggle: \"Alterna la condició de visualització\",\n comments: \"Comentaris ({count})\",\n lifted: \"{block} aixecat. Posició {position} de {total}.\",\n moved: \"{block} mogut a la posició {position} de {total}.\",\n dropped: \"{block} deixat anar a la posició {position} de {total}.\",\n cancelled:\n \"Moviment cancel·lat. {block} ha tornat a la posició {position}.\",\n },\n\n // Toolbar - Section\n section: {\n dropHere: \"Deixa anar els blocs aquí\",\n columns: \"Columnes\",\n column1: \"1 columna\",\n column2: \"2 columnes\",\n column3: \"3 columnes\",\n ratio12: \"Proporció 1:2\",\n ratio21: \"Proporció 2:1\",\n borderRadius: \"Radi de la vora\",\n wrapperEnable: \"Afegeix un contenidor\",\n stackOnMobile: \"Apila en mòbil\",\n },\n\n // Text editor link dialog\n linkDialog: {\n editLink: \"Edita l'enllaç\",\n insertLink: \"Insereix l'enllaç\",\n updateLink: \"Actualitza l'enllaç\",\n removeLink: \"Elimina l'enllaç\",\n cancel: \"Cancel·la\",\n urlPlaceholder: \"https://example.com\",\n urlLabel: \"URL\",\n colorLabel: \"Color de l'enllaç\",\n },\n\n // Toolbar - Title\n title: {\n level: \"Nivell d'encapçalament\",\n heading1: \"Encapçalament 1 (36px)\",\n heading2: \"Encapçalament 2 (28px)\",\n heading3: \"Encapçalament 3 (22px)\",\n heading4: \"Encapçalament 4 (18px)\",\n fontFamily: \"Tipus de lletra\",\n inheritFont: \"Utilitza la font de la plantilla\",\n color: \"Color\",\n align: \"Alineació\",\n alignLeft: \"Esquerra\",\n alignCenter: \"Centre\",\n alignRight: \"Dreta\",\n },\n\n // Emoji picker\n emoji: {\n smileys: \"Emoticones\",\n gestures: \"Gestos\",\n objects: \"Objectes\",\n },\n\n // Toolbar - Image\n image: {\n imageUrl: \"URL de la imatge\",\n imageUrlPlaceholder: \"https://...\",\n altText: \"Text alternatiu\",\n altTextPlaceholder: \"Descripció de la imatge\",\n width: \"Amplada\",\n fullWidth: \"Amplada completa\",\n widthCustom: \"Personalitzada\",\n linkUrl: \"URL de l'enllaç\",\n openInNewTab: \"Obre en una pestanya nova\",\n placeholderUrl: \"Imatge de mostra\",\n placeholderUrlPlaceholder: \"https://... (només per al disseny)\",\n placeholderUrlTooltip:\n \"Com que la URL de la imatge utilitza una etiqueta de fusió, podeu proporcionar una imatge real aquí per previsualitzar el disseny mentre dissenyeu. Això no s'inclou a la sortida final.\",\n clickToAdd: \"Feu clic per afegir la URL de la imatge\",\n browseMedia: \"Explora multimèdia\",\n dropToUpload: \"Deixeu anar la imatge per pujar-la\",\n uploading: \"Pujant…\",\n decorative: \"Imatge decorativa\",\n decorativeHint:\n \"Ocult per als lectors de pantalla. Utilitzeu-lo només per a espaiadors i detalls visuals.\",\n },\n\n // Toolbar - Video\n video: {\n videoUrl: \"URL del vídeo\",\n videoUrlPlaceholder: \"https://youtube.com/...\",\n youtube: \"YouTube\",\n vimeo: \"Vimeo\",\n detected: \"Vídeo detectat: la miniatura es generarà automàticament\",\n openInNewTab: \"Obre en una pestanya nova\",\n customThumbnail: \"Miniatura personalitzada\",\n optional: \"(opcional)\",\n thumbnailPlaceholder: \"Generat automàticament des de la URL del vídeo\",\n altText: \"Text alternatiu\",\n altTextPlaceholder: \"Descripció del vídeo\",\n width: \"Amplada\",\n fullWidth: \"Amplada completa\",\n placeholderUrl: \"Miniatura de mostra\",\n placeholderUrlPlaceholder: \"https://... (només per al disseny)\",\n placeholderUrlTooltip:\n \"Com que la URL del vídeo utilitza una etiqueta de fusió, podeu proporcionar una miniatura real aquí per previsualitzar el disseny mentre dissenyeu. Això no s'inclou a la sortida final.\",\n addVideo: \"Afegeix una URL de vídeo\",\n },\n\n // Toolbar - Button\n button: {\n fontFamily: \"Tipus de lletra\",\n inheritFont: \"Utilitza la font de la plantilla\",\n text: \"Text\",\n url: \"URL\",\n urlPlaceholder: \"https://...\",\n openInNewTab: \"Obre en una pestanya nova\",\n background: \"Fons\",\n textColor: \"Color del text\",\n borderRadius: \"Radi de la vora\",\n fontSize: \"Mida de la font\",\n width: \"Amplada\",\n widthAuto: \"Ajusta al contingut\",\n fullWidth: \"Amplada completa\",\n widthCustom: \"Personalitzada\",\n },\n\n // Toolbar - Divider\n divider: {\n style: \"Estil\",\n solid: \"Sòlid\",\n dashed: \"Amb guions\",\n dotted: \"Amb punts\",\n color: \"Color\",\n thickness: \"Gruix\",\n },\n\n // Toolbar - Social Icons\n social: {\n icons: \"Icones\",\n addIcon: \"Afegeix una icona\",\n addIcons: \"Afegeix icones de xarxes socials\",\n removeIcon: \"Elimina\",\n platform: \"Plataforma\",\n url: \"URL\",\n urlPlaceholder: \"https://...\",\n style: \"Estil\",\n styleSolid: \"Sòlid\",\n styleOutlined: \"Contornejat\",\n styleRounded: \"Arrodonit\",\n styleSquare: \"Quadrat\",\n styleCircle: \"Cercle\",\n size: \"Mida\",\n sizeSmall: \"S\",\n sizeMedium: \"M\",\n sizeLarge: \"L\",\n spacing: \"Espaiat\",\n align: \"Alineació\",\n platforms: {\n facebook: \"Facebook\",\n twitter: \"X (Twitter)\",\n instagram: \"Instagram\",\n linkedin: \"LinkedIn\",\n youtube: \"YouTube\",\n tiktok: \"TikTok\",\n pinterest: \"Pinterest\",\n email: \"Correu electrònic\",\n website: \"Lloc web\",\n whatsapp: \"WhatsApp\",\n telegram: \"Telegram\",\n discord: \"Discord\",\n snapchat: \"Snapchat\",\n reddit: \"Reddit\",\n github: \"GitHub\",\n dribbble: \"Dribbble\",\n behance: \"Behance\",\n },\n },\n\n // Toolbar - Menu\n menu: {\n items: \"Elements del menú\",\n addItem: \"Afegeix un element\",\n removeItem: \"Elimina\",\n text: \"Text\",\n url: \"URL\",\n urlPlaceholder: \"https://example.com\",\n openInNewTab: \"Obre en una pestanya nova\",\n bold: \"Negreta\",\n underline: \"Subratllat\",\n color: \"Color\",\n linkColor: \"Color de l'enllaç\",\n fontSize: \"Mida de la font\",\n fontFamily: \"Tipus de lletra\",\n separator: \"Separador\",\n separatorColor: \"Color del separador\",\n spacing: \"Espaiat\",\n textAlign: \"Alineació\",\n addLinks: \"Afegeix enllaços al menú\",\n },\n\n // Toolbar - Table\n table: {\n dimensions: \"Dimensions\",\n rows: \"Files\",\n columns: \"Columnes\",\n addRow: \"Afegeix una fila\",\n removeRow: \"Elimina la fila\",\n addColumn: \"Afegeix una columna\",\n removeColumn: \"Elimina la columna\",\n hasHeaderRow: \"Fila de capçalera\",\n headerBackgroundColor: \"Fons de la capçalera\",\n noHeaderBg: \"Sense fons\",\n borderColor: \"Color de la vora\",\n borderWidth: \"Gruix de la vora\",\n cellPadding: \"Farciment de la cel·la\",\n fontFamily: \"Tipus de lletra\",\n fontSize: \"Mida de la font\",\n color: \"Color del text\",\n textAlign: \"Alineació\",\n cellPlaceholder: \"Introdueix el text...\",\n empty: \"Afegeix una taula\",\n },\n\n // Toolbar - Spacer\n spacer: {\n height: \"Alçada\",\n },\n\n // Toolbar - Countdown\n countdown: {\n targetDate: \"Data límit\",\n timezone: \"Zona horària\",\n display: \"Visualització\",\n days: \"Dies\",\n hours: \"Hores\",\n minutes: \"Minuts\",\n seconds: \"Segons\",\n separator: \"Separador\",\n fontFamily: \"Tipus de lletra\",\n inheritFont: \"Per defecte\",\n digitFontSize: \"Mida dels dígits\",\n digitColor: \"Color dels dígits\",\n labelColor: \"Color de l'etiqueta\",\n labelFontSize: \"Mida de l'etiqueta\",\n background: \"Fons\",\n labels: \"Etiquetes\",\n expiry: \"Missatge de caducitat\",\n expiredMessagePlaceholder: \"Aquesta oferta ha caducat\",\n expiredImageUrl: \"URL de la imatge caducada\",\n hideOnExpiry: \"Oculta quan hagi caducat\",\n setDate: \"Defineix una data límit al panell de configuració\",\n hidden: \"Ocult (caducat)\",\n },\n\n // Custom Blocks\n customBlocks: {\n definitionNotFound:\n \"Tipus de bloc desconegut: aquest bloc no està registrat\",\n renderError:\n \"No s'ha pogut renderitzar aquest bloc. Comproveu si hi ha errors a la plantilla del bloc.\",\n fields: {\n required: \"Obligatori\",\n addItem: \"Afegeix un element\",\n removeItem: \"Elimina\",\n maxItemsReached: \"S'ha arribat al límit màxim d'elements\",\n minItemsRequired: \"Es requereix un mínim de {count} elements\",\n },\n toolbar: {\n noDefinition:\n \"Enregistreu aquest tipus de bloc a la configuració de l'SDK per editar-ne les propietats\",\n },\n dataSource: {\n fetchButton: \"Carrega el contingut\",\n changeButton: \"Canvia\",\n fetching: \"Carregant...\",\n readOnlyTooltip: \"Aquest valor es carrega des de la vostra font de dades\",\n fetchError: \"No s'ha pogut carregar el contingut\",\n },\n },\n\n // Toolbar - HTML\n html: {\n content: \"Contingut HTML\",\n preview: \"Bloc HTML personalitzat\",\n empty: \"Afegeix contingut HTML al panell\",\n sanitizationHint:\n \"L'HTML s'exporta tal qual i no es depura — assegura't que el contingut sigui segur.\",\n },\n\n // Toolbar - Common block settings\n blockSettings: {\n spacing: \"Espaiat\",\n padding: \"Farciment\",\n background: \"Fons\",\n color: \"Color\",\n display: \"Visualització\",\n showOnDesktop: \"Mostra a l'escriptori\",\n showOnMobile: \"Mostra al mòbil\",\n hiddenOnDevice: \"Ocult a {device}\",\n displayCondition: \"Condició de visualització\",\n selectCondition: \"Selecciona una condició\",\n removeCondition: \"Elimina la condició\",\n noCondition: \"Sempre visible\",\n conditionApplied: \"Condició aplicada\",\n customCondition: \"Condició personalitzada\",\n customConditionLabel: \"Nom de la condició\",\n customConditionBefore: \"Abans (lògica d'obertura)\",\n customConditionAfter: \"Després (lògica de tancament)\",\n applyCondition: \"Aplica\",\n cancelCondition: \"Cancel·la\",\n customBadge: \"Personalitzat\",\n restoreHiddenBlocks: \"Mostra tots els blocs ocults\",\n },\n\n // Template settings\n templateSettings: {\n layout: \"Disposició\",\n widthPreset: \"Amplada predefinida\",\n customWidth: \"Amplada personalitzada\",\n appearance: \"Aparença\",\n backgroundColor: \"Color de fons\",\n textColor: \"Color del text\",\n linkColor: \"Color de l'enllaç\",\n linkUnderline: \"Subratllar enllaços\",\n fontFamily: \"Tipus de lletra\",\n preheaderText: \"Text de la precapçalera\",\n preheaderTextPlaceholder:\n \"Text de vista prèvia que es mostra després de la línia de l'assumpte a la bústia...\",\n preheaderTextHint:\n \"Aquest text apareix després de la línia de l'assumpte a les vistes prèvies dels clients de correu. Admet etiquetes de fusió.\",\n language: \"Idioma\",\n contentLocale: \"Idioma del contingut\",\n contentLocaleHint:\n \"Codi BCP 47 (p. ex. ca, en, de). Estableix l'atribut lang del correu renderitzat perquè els lectors de pantalla pronunciïn el contingut correctament.\",\n tips: \"Consells\",\n tip1: \"600px és l'amplada estàndard per a les plantilles de correu electrònic\",\n tip2: \"Utilitzeu fonts segures per a la web per obtenir la millor compatibilitat\",\n tip3: \"Els fons clars funcionen millor per a la llegibilitat\",\n },\n\n // Spacing control\n spacingControl: {\n lockAll: \"Bloqueja tots els costats\",\n unlock: \"Desbloqueja els costats\",\n top: \"Superior\",\n right: \"Dret\",\n bottom: \"Inferior\",\n left: \"Esquerre\",\n decreaseTop: \"Disminueix superior\",\n increaseTop: \"Augmenta superior\",\n decreaseLeft: \"Disminueix esquerre\",\n increaseLeft: \"Augmenta esquerre\",\n decreaseRight: \"Disminueix dret\",\n increaseRight: \"Augmenta dret\",\n decreaseBottom: \"Disminueix inferior\",\n increaseBottom: \"Augmenta inferior\",\n },\n\n // Color Picker\n colorPicker: {\n pickColor: \"Tria un color\",\n hexValue: \"Valor del color en Hex\",\n notSet: \"No definit\",\n clear: \"Neteja el color\",\n presetColors: \"Colors predefinits\",\n },\n\n // Merge Tag\n mergeTag: {\n clickToEdit: \"Feu clic per editar\",\n remove: \"Elimina l'etiqueta de fusió\",\n insert: \"Insereix l'etiqueta de fusió\",\n insertShort: \"Etiqueta de fusió\",\n editValue: \"Edita el valor de l'etiqueta de fusió\",\n deleteMergeTag: \"Elimina l'etiqueta de fusió\",\n suggestionEmpty: \"No hi ha etiquetes de fusió coincidents\",\n picker: {\n title: \"Insereix l'etiqueta de fusió\",\n searchPlaceholder: \"Cerca etiquetes de fusió\",\n searchAriaLabel: \"Cerca etiquetes de fusió\",\n noResults: \"No hi ha etiquetes de fusió coincidents\",\n empty: \"No hi ha etiquetes de fusió configurades\",\n otherGroup: \"Altres\",\n cancel: \"Cancel·la\",\n close: \"Tanca\",\n groupCount: \"{count}\",\n },\n },\n\n // Logic tags (standalone, separate from merge tags)\n logicTag: {\n insert: \"Insereix lògica\",\n insertShort: \"Lògica\",\n picker: {\n title: \"Insereix lògica\",\n searchPlaceholder: \"Cerca etiquetes de lògica\",\n searchAriaLabel: \"Cerca etiquetes de lògica\",\n noResults: \"No hi ha etiquetes de lògica coincidents\",\n empty: \"No hi ha etiquetes de lògica configurades\",\n otherGroup: \"Altres\",\n close: \"Tanca\",\n },\n },\n\n // Canvas\n canvas: {\n noBlocks: \"Encara no hi ha blocs\",\n dragHint: \"Comenceu des de zero arrossegant blocs des de la barra lateral\",\n dropHere: \"Deixeu anar aquí\",\n aiHintChat: \"o deixeu que\",\n aiHintChatSuffix:\n \"generi una plantilla completa per a vosaltres en pocs segons\",\n aiHintDesign:\n \"Teniu un disseny existent? Pugeu una captura de pantalla, una imatge o un PDF i\",\n aiHintDesignSuffix: \"el recrearà a l'instant\",\n },\n\n // Media Library (cloud)\n mediaLibrary: {\n title: \"Biblioteca multimèdia\",\n searchPlaceholder: \"Cerca fitxers...\",\n allFiles: \"Tots els fitxers\",\n filterAll: \"Tots els tipus\",\n filterImages: \"Imatges\",\n filterDocuments: \"Documents\",\n filterVideos: \"Vídeos\",\n filterAudio: \"Àudio\",\n newFolder: \"Nova carpeta\",\n folderName: \"Nom de la carpeta\",\n noFiles: \"No s'han trobat fitxers\",\n dropOrClick: \"Deixeu anar els fitxers aquí o feu clic per pujar-los\",\n acceptedFormats: \"Imatges, PDF, Vídeo, Àudio, Documents (màx. 10 MB)\",\n uploading: \"Pujant...\",\n uploadingProgress: \"Pujant {current} de {total}...\",\n selectImage: \"Selecciona la imatge\",\n selectFile: \"Selecciona el fitxer\",\n deleteSelected: \"Elimina\",\n copyUrl: \"Copia la URL\",\n copied: \"Copiat!\",\n browseMedia: \"Explora la biblioteca multimèdia\",\n renameFolder: \"Canvia el nom de la carpeta\",\n addSubfolder: \"Afegeix una subcarpeta\",\n subfolderName: \"Nom de la subcarpeta\",\n sortNewest: \"Els més nous primer\",\n sortOldest: \"Els més antics primer\",\n sortNameAsc: \"Nom A-Z\",\n sortNameDesc: \"Nom Z-A\",\n sortSizeAsc: \"Els més petits primer\",\n sortSizeDesc: \"Els més grans primer\",\n moveSelected: \"Mou\",\n moveToRoot: \"Tots els fitxers\",\n currentFolder: \"(actual)\",\n confirmDelete: \"Voleu eliminar aquest fitxer?\",\n renameFile: \"Canvia el nom\",\n editFile: \"Edita el fitxer\",\n fileName: \"Nom del fitxer\",\n altText: \"Text alternatiu\",\n altTextPlaceholder: \"Descriviu aquesta imatge per a l'accessibilitat\",\n saveChanges: \"Desa\",\n cancel: \"Cancel·la\",\n frequentlyUsed: \"Utilitzat freqüentment\",\n deleteWarningTitle: \"Elimina el fitxer\",\n deleteWarningMessage:\n \"Aquest fitxer s'eliminarà permanentment i no es podrà recuperar.\",\n deleteWarningUsageNote:\n \"Els fitxers següents s'utilitzen en plantilles. Eliminar-los pot trencar aquestes plantilles.\",\n deleteAnyway: \"Elimina el fitxer de totes maneres\",\n usedInTemplates: \"Utilitzat en {count} plantilla/es\",\n viewGrid: \"Vista de quadrícula\",\n viewList: \"Vista de llista\",\n showFolders: \"Mostra les carpetes\",\n hideFolders: \"Oculta les carpetes\",\n importFromUrl: \"Importa des d'una URL\",\n importUrlPlaceholder: \"https://example.com/image.jpg\",\n import: \"Importa\",\n importing: \"Important...\",\n importError: \"No s'ha pogut importar des de la URL\",\n conversionLabel: \"Mida\",\n conversionOriginal: \"Original\",\n conversionSmall: \"Petita (150px)\",\n conversionMedium: \"Mitjana (600px)\",\n conversionLarge: \"Gran (1200px)\",\n replaceFile: \"Substitueix el fitxer\",\n replaceWarningTitle: \"Substitueix el fitxer\",\n replaceWarningMessage:\n \"Esteu a punt de substituir aquest fitxer. El substitut ha de tenir la mateixa extensió de fitxer ({extension}).\",\n replaceWarningUsageNote:\n \"Aquest fitxer s'utilitza en {count} plantilla/es. Substituir-lo actualitzarà totes les referències.\",\n replaceSelectFile: \"Selecciona el fitxer de substitució\",\n replace: \"Substitueix\",\n replacing: \"Substituint...\",\n replaceError: \"No s'ha pogut substituir el fitxer\",\n saving: \"Desant...\",\n cropAspectRatio: \"Relació d'aspecte\",\n cropFree: \"Lliure\",\n cropSquare: \"1:1\",\n cropLandscape43: \"4:3\",\n cropLandscape169: \"16:9\",\n cropOriginal: \"Original\",\n cropMaxWidth: \"Amplada màxima\",\n cropMaxHeight: \"Alçada màxima\",\n cropOutputSize: \"Mida de sortida\",\n cropPixels: \"px\",\n cropOptional: \"(opcional)\",\n storageTooltip: \"{used} de {total} utilitzats (queden {remaining})\",\n },\n\n // Sidebar\n savedBlocks: {\n title: \"Blocs desats\",\n saveAsBlock: \"Desa com a bloc\",\n name: \"Nom\",\n namePlaceholder: \"p. ex. Encapçalament, Peu de pàgina, CTA...\",\n save: \"Desa el bloc\",\n saving: \"S'està desant...\",\n cancel: \"Cancel·la\",\n empty: \"Encara no hi ha blocs desats\",\n emptyHint:\n \"Desa blocs de les teves plantilles per reutilitzar-los més tard.\",\n noResults: \"Cap bloc desat coincideix amb la teva cerca\",\n search: \"Cerca blocs desats...\",\n loading: \"S'estan carregant els blocs desats…\",\n category: \"Categoria\",\n categoryPlaceholder: \"p. ex. Capçaleres, Promocions... (opcional)\",\n allCategories: \"Totes les categories\",\n filterByCategory: \"Filtra per categoria\",\n insert: \"Insereix\",\n rename: \"Canvia el nom\",\n delete: \"Elimina\",\n deleteConfirm: \"Vols eliminar aquest bloc desat?\",\n blockCount: \"{count} bloc(s)\",\n browse: \"Explora els blocs desats\",\n selectToPreview: \"Selecciona un bloc desat per previsualitzar-lo\",\n insertAtBeginning: \"Al principi\",\n insertAfterBlock: \"Després de {block}\",\n insertAtEnd: \"Al final\",\n insertPosition: \"Posició d'inserció\",\n close: \"Tanca\",\n pickToolbar: \"Selecció de bloc desat\",\n pickCount: \"{count} bloc(s) seleccionat(s)\",\n pickHint: \"Fes clic als blocs per afegir-los o treure'ls\",\n savingCount: \"S'estan desant {count} bloc(s):\",\n reorderHint: \"Arrossega per reordenar: els blocs es desen en aquest ordre\",\n reorderHandle: \"Reordena {block}, posició {position} de {total}\",\n reorderAnnouncement: \"{block} mogut a la posició {position} de {total}\",\n expand: \"Amplia\",\n collapse: \"Redueix\",\n expandPreview: \"Amplia la vista prèvia de {block}\",\n collapsePreview: \"Redueix la vista prèvia de {block}\",\n time: {\n justNow: \"Ara mateix\",\n minutesAgo: \"fa {minutes} min\",\n hoursAgo: \"fa {hours} h\",\n daysAgo: \"fa {days} d\",\n },\n },\n testEmail: {\n title: \"Envia un correu de prova\",\n recipientLabel: \"Destinatari\",\n recipientPlaceholder: \"tu@exemple.cat\",\n invalidAddress: \"Introduïu una adreça de correu vàlida.\",\n preview: \"Previsualització\",\n previewHint:\n \"Les etiquetes de combinació es mostren sense resoldre — el vostre backend les omple.\",\n recipientNotAllowed:\n \"Aquesta adreça no és a la llista de destinataris permesos.\",\n send: \"Envia\",\n sending: \"S'està enviant...\",\n cancel: \"Cancel·la\",\n success: \"Correu de prova enviat correctament\",\n button: \"Prova\",\n },\n sidebarNav: {\n browseSavedBlocks: \"Explora els blocs desats\",\n expandSidebar: \"Expandeix la barra lateral de blocs\",\n palette: \"Paleta de blocs\",\n insertBlock: \"Insereix un bloc de tipus {block}\",\n },\n\n // Landmark region labels for assistive technology\n landmarks: {\n canvas: \"Llenç del correu\",\n blockToolbar: \"Propietats del bloc\",\n rightSidebar: \"Propietats del bloc i configuració de la plantilla\",\n reorderAnnouncements: \"Avisos de reordenació de blocs\",\n },\n\n // Design Reference (cloud)\n errors: {\n editorLoading: \"Carregant l'editor...\",\n editorLoadFailed: \"No s'ha pogut carregar l'editor.\",\n retry: \"Torna-ho a provar\",\n },\n\n issues: {\n panelTitle: \"Problemes\",\n panelTabLabel: \"Problemes\",\n groupErrors: \"Errors\",\n groupWarnings: \"Avisos\",\n groupInfo: \"Informació\",\n jump: \"Vés al bloc\",\n fix: \"Corregeix\",\n emptyState: \"Sense problemes, tot correcte.\",\n badgeError: \"Té errors\",\n badgeWarning: \"Té avisos\",\n issueCountTooltip: \"{count} problema/es\",\n },\n\n smallScreen: {\n title: \"Cal una pantalla més gran\",\n message:\n \"L'editor necessita més espai del que ofereix aquesta pantalla. Obriu-lo en una tauleta o ordinador d'escriptori per començar a editar.\",\n },\n};\n\nexport default ca;\n"],"mappings":"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"}