@templatical/editor 0.26.2 → 0.26.3

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 (60) hide show
  1. package/dist/{AiFeatureMenu-BY6mubnR.js → AiFeatureMenu-Cy-PVrNT.js} +1 -1
  2. package/dist/{BlockPreviewCanvas-DbNOqSI1.js → BlockPreviewCanvas-BZKslwGI.js} +1 -1
  3. package/dist/{CloudEditor-DEnMNMbm.js → CloudEditor-CPRkQCRa.js} +4 -4
  4. package/dist/{CollaboratorBar-zvfvE7f-.js → CollaboratorBar-BGwbg9aN.js} +1 -1
  5. package/dist/{ParagraphEditor-CLvv5Ija.js → ParagraphEditor-6WN5jmXr.js} +4 -3
  6. package/dist/{SaveBlockDialog-fVp_PwZi.js → SaveBlockDialog-21vDlEBS.js} +3 -3
  7. package/dist/{SavedBlocksBrowserModal-KWlaiLlr.js → SavedBlocksBrowserModal-GMrajvfV.js} +4 -4
  8. package/dist/{SavedBlocksPanels-CQsZvWO_.js → SavedBlocksPanels-DEPmNhi6.js} +1 -1
  9. package/dist/{SavedBlocksPickBar-C3h8oL3r.js → SavedBlocksPickBar-Dk_IaMO9.js} +1 -1
  10. package/dist/{SnapshotHistory-DjA3Ej_i.js → SnapshotHistory-Cb4WW2gf.js} +1 -1
  11. package/dist/{TestEmailModal-EM0RP_9L.js → TestEmailModal-BHDKmd6B.js} +3 -3
  12. package/dist/{TestEmailPanel-B6goIAxT.js → TestEmailPanel-DubocPxf.js} +1 -1
  13. package/dist/{TitleEditor-JEaP9jVF.js → TitleEditor-Cy4B3UNe.js} +4 -3
  14. package/dist/{TplModal-DoneXIj_.js → TplModal-Dv9zgSWI.js} +1 -1
  15. package/dist/{blockTypeIcons-2_85L_CL.js → blockTypeIcons-C7wRSaT3.js} +1 -1
  16. package/dist/bundle-stats.json +5 -5
  17. package/dist/cdn/chunks/{AiFeatureMenu-Cwj8afsi.js → AiFeatureMenu-Cb9gaRBn.js} +2 -2
  18. package/dist/cdn/chunks/{AiFeatureMenu-Cwj8afsi.js.map → AiFeatureMenu-Cb9gaRBn.js.map} +1 -1
  19. package/dist/cdn/chunks/{BlockPreviewCanvas-DPtiMsVz.js → BlockPreviewCanvas-msj619wT.js} +2 -2
  20. package/dist/cdn/chunks/{BlockPreviewCanvas-DPtiMsVz.js.map → BlockPreviewCanvas-msj619wT.js.map} +1 -1
  21. package/dist/cdn/chunks/{CloudEditor-CQ830jJt.js → CloudEditor-DbyEsIxu.js} +5 -5
  22. package/dist/cdn/chunks/{CloudEditor-CQ830jJt.js.map → CloudEditor-DbyEsIxu.js.map} +1 -1
  23. package/dist/cdn/chunks/{CollaboratorBar-BF469_AN.js → CollaboratorBar-G_sZDZXw.js} +2 -2
  24. package/dist/cdn/chunks/{CollaboratorBar-BF469_AN.js.map → CollaboratorBar-G_sZDZXw.js.map} +1 -1
  25. package/dist/cdn/chunks/{ParagraphEditor-Bzcma_Lm.js → ParagraphEditor-CZ5Z6AgG.js} +5 -4
  26. package/dist/cdn/chunks/{ParagraphEditor-Bzcma_Lm.js.map → ParagraphEditor-CZ5Z6AgG.js.map} +1 -1
  27. package/dist/cdn/chunks/{SaveBlockDialog-BfNVeXXV.js → SaveBlockDialog-CVIVZEuy.js} +4 -4
  28. package/dist/cdn/chunks/{SaveBlockDialog-BfNVeXXV.js.map → SaveBlockDialog-CVIVZEuy.js.map} +1 -1
  29. package/dist/cdn/chunks/{SavedBlocksBrowserModal-C59D4G5C.js → SavedBlocksBrowserModal-BkQXEUE2.js} +4 -4
  30. package/dist/cdn/chunks/{SavedBlocksBrowserModal-C59D4G5C.js.map → SavedBlocksBrowserModal-BkQXEUE2.js.map} +1 -1
  31. package/dist/cdn/chunks/{SavedBlocksPanels-e4HyTZ8v.js → SavedBlocksPanels-CUtYoi5Z.js} +2 -2
  32. package/dist/cdn/chunks/{SavedBlocksPanels-e4HyTZ8v.js.map → SavedBlocksPanels-CUtYoi5Z.js.map} +1 -1
  33. package/dist/cdn/chunks/{SavedBlocksPickBar-DMHxPCM4.js → SavedBlocksPickBar-C80qJykl.js} +2 -2
  34. package/dist/cdn/chunks/{SavedBlocksPickBar-DMHxPCM4.js.map → SavedBlocksPickBar-C80qJykl.js.map} +1 -1
  35. package/dist/cdn/chunks/{SnapshotHistory-80tAU_VX.js → SnapshotHistory-DFinMrg_.js} +2 -2
  36. package/dist/cdn/chunks/{SnapshotHistory-80tAU_VX.js.map → SnapshotHistory-DFinMrg_.js.map} +1 -1
  37. package/dist/cdn/chunks/{TestEmailModal-DdWfAGNe.js → TestEmailModal-IxS-QjeH.js} +4 -4
  38. package/dist/cdn/chunks/{TestEmailModal-DdWfAGNe.js.map → TestEmailModal-IxS-QjeH.js.map} +1 -1
  39. package/dist/cdn/chunks/{TestEmailPanel-CZAG1gQE.js → TestEmailPanel-B8y7JabA.js} +2 -2
  40. package/dist/cdn/chunks/{TestEmailPanel-CZAG1gQE.js.map → TestEmailPanel-B8y7JabA.js.map} +1 -1
  41. package/dist/cdn/chunks/{TitleEditor-D0_LY2Ex.js → TitleEditor-C8WDXf4P.js} +5 -4
  42. package/dist/cdn/chunks/TitleEditor-C8WDXf4P.js.map +1 -0
  43. package/dist/cdn/chunks/{TplModal-iCmoKB2k.js → TplModal-Ce0QuHyo.js} +2 -2
  44. package/dist/cdn/chunks/{TplModal-iCmoKB2k.js.map → TplModal-Ce0QuHyo.js.map} +1 -1
  45. package/dist/cdn/chunks/{extensions-C7h52yuk.js → extensions-qhcN3RMT.js} +35 -21
  46. package/dist/cdn/chunks/extensions-qhcN3RMT.js.map +1 -0
  47. package/dist/cdn/chunks/{src-CUKUsRzX.js → src-BwbnXShT.js} +2 -2
  48. package/dist/cdn/chunks/{src-CUKUsRzX.js.map → src-BwbnXShT.js.map} +1 -1
  49. package/dist/cdn/chunks/{styles-CND3ey2g.js → styles-DLAfU4Uf.js} +4 -4
  50. package/dist/cdn/chunks/{styles-CND3ey2g.js.map → styles-DLAfU4Uf.js.map} +1 -1
  51. package/dist/cdn/chunks/{useEditorCore-CKQ9gmoo.js → useEditorCore-CcgxY-Gy.js} +3 -3
  52. package/dist/cdn/chunks/{useEditorCore-CKQ9gmoo.js.map → useEditorCore-CcgxY-Gy.js.map} +1 -1
  53. package/dist/cdn/editor.js +5 -5
  54. package/dist/{extensions-DzKNPITR.js → extensions-Cwg9S-0f.js} +34 -20
  55. package/dist/{styles-BK36D1sy.js → styles-Dx__pP9O.js} +3 -3
  56. package/dist/templatical-editor.js +5 -5
  57. package/dist/{useEditorCore-idmPLUeb.js → useEditorCore-P4UkhcyS.js} +2 -2
  58. package/package.json +7 -7
  59. package/dist/cdn/chunks/TitleEditor-D0_LY2Ex.js.map +0 -1
  60. package/dist/cdn/chunks/extensions-C7h52yuk.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { A as e, Dt as t, N as n, O as r, Ot as i, _ as a, c as o, d as s, l as c, r as l, s as u } from "./vue.runtime.esm-bundler-D1diBMsm.js";
2
- import "./useEditorCore-idmPLUeb.js";
2
+ import "./useEditorCore-P4UkhcyS.js";
3
3
  import { G as d, t as f } from "./keys-fwF3qZax.js";
4
4
  import { t as p } from "./image-up-Ceggh_R3.js";
5
5
  import { t as m } from "./shield-check-qje8Kufw.js";
@@ -1,6 +1,6 @@
1
1
  import { t as e } from "./rolldown-runtime-Dy4uBu1J.js";
2
2
  import { A as t, Dt as n, M as r, N as i, O as a, _ as o, c as s, d as c, k as l, l as u, nt as d, r as f, s as p, x as m } from "./vue.runtime.esm-bundler-D1diBMsm.js";
3
- import { a as h, b as g, d as _, f as v, g as y, h as b, i as x, l as S, m as C, n as w, p as T, r as E, v as D, y as O, z as k } from "./useEditorCore-idmPLUeb.js";
3
+ import { a as h, b as g, d as _, f as v, g as y, h as b, i as x, l as S, m as C, n as w, p as T, r as E, v as D, y as O, z as k } from "./useEditorCore-P4UkhcyS.js";
4
4
  import { W as A, f as j, g as M, k as N, s as P } from "./keys-fwF3qZax.js";
5
5
  import { n as F, t as I } from "./emailFrameWidth-BmFCN2dp.js";
6
6
  //#region src/components/blocks/PreviewSectionBlock.vue?vue&type=script&setup=true&lang.ts
@@ -2,7 +2,7 @@ import { A as e, D as t, Dt as n, E as r, Et as i, I as a, K as o, O as s, Ot as
2
2
  import { c as E, o as D, t as O } from "./runtime-dom.esm-bundler-CepEmvwF.js";
3
3
  import { n as k } from "./upload-sVqqppuv.js";
4
4
  import { M as A } from "./dist-B1kLn1rs.js";
5
- import { E as j, V as M, t as N, x as P } from "./useEditorCore-idmPLUeb.js";
5
+ import { E as j, V as M, t as N, x as P } from "./useEditorCore-P4UkhcyS.js";
6
6
  import { m as F } from "./dist-BpKxrUi2.js";
7
7
  import { s as I } from "./usePopoverPosition-C0dPEF4X.js";
8
8
  import { M as L, c as ee, d as R, i as z, l as B, t as V, z as te } from "./keys-fwF3qZax.js";
@@ -10,7 +10,7 @@ import { t as H } from "./useI18n-DoVKdCTH.js";
10
10
  import { t as U } from "./createLucideIcon-C8WzBYCv.js";
11
11
  import { t as W } from "./check-On4TSac-.js";
12
12
  import { t as G } from "./circle-alert-cilwzafX.js";
13
- import { a as K, c as ne, d as q, f as re, h as ie, i as J, l as ae, m as oe, n as se, o as ce, p as le, r as ue, s as de, t as fe, u as pe } from "./styles-BK36D1sy.js";
13
+ import { a as K, c as ne, d as q, f as re, h as ie, i as J, l as ae, m as oe, n as se, o as ce, p as le, r as ue, s as de, t as fe, u as pe } from "./styles-Dx__pP9O.js";
14
14
  import { t as me } from "./clock-Dt0APH92.js";
15
15
  import { t as Y } from "./loader-circle-bO7o7_HD.js";
16
16
  import { t as X } from "./message-circle-BEkAk65X.js";
@@ -637,7 +637,7 @@ var Je = ["aria-label"], Ye = { class: "tpl:flex tpl:max-h-[80vh] tpl:w-full tpl
637
637
  },
638
638
  emits: ["save"],
639
639
  setup(e) {
640
- let t = h(() => import("./CollaboratorBar-zvfvE7f-.js")), r = h(() => import("./SnapshotHistory-DjA3Ej_i.js")), a = h(() => import("./AiFeatureMenu-BY6mubnR.js")), { t: o } = H(), { t: u, format: d } = Q();
640
+ let t = h(() => import("./CollaboratorBar-BGwbg9aN.js")), r = h(() => import("./SnapshotHistory-Cb4WW2gf.js")), a = h(() => import("./AiFeatureMenu-Cy-PVrNT.js")), { t: o } = H(), { t: u, format: d } = Q();
641
641
  return (f, h) => (s(), m("header", it, [
642
642
  p("div", at, [e.featureFlags.templateLimit.value === null ? w("", !0) : (s(), m("span", ot, c(b(d)(b(u).header.templatesUsed, {
643
643
  used: e.featureFlags.templateCount.value,
@@ -939,7 +939,7 @@ var Je = ["aria-label"], Ye = { class: "tpl:flex tpl:max-h-[80vh] tpl:w-full tpl
939
939
  },
940
940
  emits: ["ready"],
941
941
  setup(e, { expose: a, emit: o }) {
942
- let d = h(() => import("./SavedBlocksPanels-CQsZvWO_.js")), f = h(() => import("./TestEmailPanel-B6goIAxT.js")), x = e;
942
+ let d = h(() => import("./SavedBlocksPanels-DEPmNhi6.js")), f = h(() => import("./TestEmailPanel-DubocPxf.js")), x = e;
943
943
  _(B, x.cloudTranslations);
944
944
  let S = o, C = u(null), k = u(null), A = We({
945
945
  config: x.config,
@@ -1,5 +1,5 @@
1
1
  import { A as e, Dt as t, O as n, Ot as r, _ as i, c as a, d as o, l as s, nt as c, r as l, s as u, u as d } from "./vue.runtime.esm-bundler-D1diBMsm.js";
2
- import "./useEditorCore-idmPLUeb.js";
2
+ import "./useEditorCore-P4UkhcyS.js";
3
3
  import { t as f } from "./createLucideIcon-C8WzBYCv.js";
4
4
  import { n as p } from "./useCloudI18n-DKVm6PCa.js";
5
5
  import { t as m } from "./readableTextColor-f8Kykfnh.js";
@@ -1,6 +1,6 @@
1
1
  import { A as e, Dt as t, Et as n, N as r, O as i, Ot as a, W as o, Y as s, _ as c, c as l, d as u, h as d, i as f, l as p, m, nt as h, r as g, s as _, u as v, x as y } from "./vue.runtime.esm-bundler-D1diBMsm.js";
2
2
  import { c as b, s as x } from "./runtime-dom.esm-bundler-CepEmvwF.js";
3
- import { C as S, D as C, O as w, S as T } from "./useEditorCore-idmPLUeb.js";
3
+ import { C as S, D as C, O as w, S as T } from "./useEditorCore-P4UkhcyS.js";
4
4
  import { n as E, s as D } from "./usePopoverPosition-C0dPEF4X.js";
5
5
  import { G as O, O as k, T as A, U as ee, V as te, g as ne, v as re } from "./keys-fwF3qZax.js";
6
6
  import { t as j } from "./useI18n-DoVKdCTH.js";
@@ -641,7 +641,7 @@ var De = ["data-tpl-theme", "aria-label"], $ = { class: "tpl:flex tpl:items-cent
641
641
  onDone: () => r("done"),
642
642
  editorName: "ParagraphEditor",
643
643
  async loadExtensions({ mergeTags: e, syntax: t, triggerChar: n, autocompleteEnabled: r, suggestionEmptyText: i }) {
644
- let [{ Editor: o, EditorContent: s }, { default: c }, { default: l }, { default: u }, { default: d }, { default: f }, { default: p }, { TextStyle: m }, { default: h }, { default: g }, { default: _ }, { MergeTagNode: v, MergeTagSuggestion: y, LogicMergeTagNode: b, FontSize: x, LineHeight: C, LetterSpacing: w }] = await Promise.all([
644
+ let [{ Editor: o, EditorContent: s }, { default: c }, { default: l }, { default: u }, { default: d }, { default: f }, { default: p }, { TextStyle: m }, { default: h }, { default: g }, { default: _ }, { MergeTagNode: v, MergeTagSuggestion: y, LogicMergeTagNode: b, FontSize: x, LineHeight: C, LetterSpacing: w, LineBoundaryKeys: T }] = await Promise.all([
645
645
  import("./dist-Cl8J0IES.js").then((e) => e.r),
646
646
  import("./dist-DMJRSouD.js"),
647
647
  import("./dist-M7m1lYRe.js"),
@@ -653,7 +653,7 @@ var De = ["data-tpl-theme", "aria-label"], $ = { class: "tpl:flex tpl:items-cent
653
653
  import("./dist-8vlm9kfO.js"),
654
654
  import("./dist-6YJAWMuf.js"),
655
655
  import("./dist-C08jiemd.js"),
656
- import("./extensions-DzKNPITR.js")
656
+ import("./extensions-Cwg9S-0f.js")
657
657
  ]);
658
658
  return {
659
659
  TiptapEditor: o,
@@ -690,6 +690,7 @@ var De = ["data-tpl-theme", "aria-label"], $ = { class: "tpl:flex tpl:items-cent
690
690
  syntax: t
691
691
  }),
692
692
  b.configure({ syntax: t }),
693
+ T,
693
694
  ...r && n && e.length > 0 ? [y.configure({
694
695
  mergeTags: e,
695
696
  char: n,
@@ -1,14 +1,14 @@
1
1
  import { A as e, Dt as t, E as n, Et as r, I as i, N as a, O as o, Ot as s, R as c, T as l, Y as u, _ as d, c as f, d as p, h as m, l as h, m as g, nt as _, r as v, s as y, u as b, x, z as S } from "./vue.runtime.esm-bundler-D1diBMsm.js";
2
2
  import { a as C, c as w, s as T } from "./runtime-dom.esm-bundler-CepEmvwF.js";
3
- import { H as E, P as D, c as O } from "./useEditorCore-idmPLUeb.js";
3
+ import { H as E, P as D, c as O } from "./useEditorCore-P4UkhcyS.js";
4
4
  import { G as k, R as A, g as j, p as M } from "./keys-fwF3qZax.js";
5
5
  import { t as ee } from "./useI18n-DoVKdCTH.js";
6
6
  import { t as te } from "./chevron-down-69EiBGoB.js";
7
7
  import { t as N } from "./chevron-up-Dh3MG1Dw.js";
8
8
  import { t as P } from "./loader-circle-bO7o7_HD.js";
9
9
  import { n as F } from "./emailFrameWidth-BmFCN2dp.js";
10
- import { t as I } from "./TplModal-DoneXIj_.js";
11
- import { t as L } from "./BlockPreviewCanvas-DbNOqSI1.js";
10
+ import { t as I } from "./TplModal-Dv9zgSWI.js";
11
+ import { t as L } from "./BlockPreviewCanvas-BZKslwGI.js";
12
12
  //#region src/components/SavedBlockPreviewRow.vue?vue&type=script&setup=true&lang.ts
13
13
  var R = ["data-block-id"], z = ["aria-label", "title"], B = { class: "tpl:relative tpl:min-w-0 tpl:flex-1" }, V = {
14
14
  key: 0,
@@ -1,13 +1,13 @@
1
1
  import { A as e, Dt as t, Et as n, I as r, N as i, O as a, Ot as o, R as ee, Y as s, _ as c, c as l, d as u, g as te, h as d, l as f, nt as p, r as m, s as h, u as g, w as ne, z as _ } from "./vue.runtime.esm-bundler-D1diBMsm.js";
2
2
  import { a as v, c as y, i as b, s as x } from "./runtime-dom.esm-bundler-CepEmvwF.js";
3
- import "./useEditorCore-idmPLUeb.js";
3
+ import "./useEditorCore-P4UkhcyS.js";
4
4
  import { G as S, R as re, g as ie } from "./keys-fwF3qZax.js";
5
5
  import { t as ae } from "./useI18n-DoVKdCTH.js";
6
- import { n as oe, r as C, t as w } from "./blockTypeIcons-2_85L_CL.js";
6
+ import { n as oe, r as C, t as w } from "./blockTypeIcons-C7wRSaT3.js";
7
7
  import { t as se } from "./pencil-t6m0EtKL.js";
8
8
  import { t as ce } from "./trash-2-CklAFCmO.js";
9
9
  import { t as le } from "./x-Mby7KlhS.js";
10
- import { t as ue } from "./TplModal-DoneXIj_.js";
10
+ import { t as ue } from "./TplModal-Dv9zgSWI.js";
11
11
  import { t as de } from "./formatRelativeTime-CtUU-QZ8.js";
12
12
  //#region src/components/SavedBlocksBrowserModal.vue?vue&type=script&setup=true&lang.ts
13
13
  var fe = {
@@ -65,7 +65,7 @@ var fe = {
65
65
  props: { visible: { type: Boolean } },
66
66
  emits: ["close", "insert"],
67
67
  setup(c, { emit: A }) {
68
- let et = c, j = A, tt = te(() => import("./BlockPreviewCanvas-DbNOqSI1.js").then((e) => e.n)), { t: M, format: N } = ae(), P = S(re, "SavedBlocksBrowserModal"), F = S(ie, "SavedBlocksBrowserModal"), I = s(""), L = s(""), R = s(null), z = s(null), B = s(null), V = s(""), H = s(""), U = s(null), W = s("end"), G = h(() => {
68
+ let et = c, j = A, tt = te(() => import("./BlockPreviewCanvas-BZKslwGI.js").then((e) => e.n)), { t: M, format: N } = ae(), P = S(re, "SavedBlocksBrowserModal"), F = S(ie, "SavedBlocksBrowserModal"), I = s(""), L = s(""), R = s(null), z = s(null), B = s(null), V = s(""), H = s(""), U = s(null), W = s("end"), G = h(() => {
69
69
  let e = I.value.trim().toLowerCase(), t = L.value;
70
70
  return P.savedBlocks.value.filter((n) => !(e && !n.name.toLowerCase().includes(e) || t && n.category !== t));
71
71
  }), K = h(() => P.isLoading.value && P.savedBlocks.value.length === 0), nt = h(() => I.value.trim().length > 0 || L.value !== ""), q = h(() => R.value ? P.savedBlocks.value.find((e) => e.id === R.value) ?? null : null), rt = h(() => {
@@ -5,7 +5,7 @@ var c = /* @__PURE__ */ t({
5
5
  __name: "SavedBlocksPanels",
6
6
  props: { feature: {} },
7
7
  setup(t) {
8
- let c = t, l = r(() => import("./SavedBlocksPickBar-C3h8oL3r.js")), u = r(() => import("./SaveBlockDialog-fVp_PwZi.js")), d = r(() => import("./SavedBlocksBrowserModal-KWlaiLlr.js"));
8
+ let c = t, l = r(() => import("./SavedBlocksPickBar-Dk_IaMO9.js")), u = r(() => import("./SaveBlockDialog-21vDlEBS.js")), d = r(() => import("./SavedBlocksBrowserModal-GMrajvfV.js"));
9
9
  function f(e, t) {
10
10
  c.feature.insert(e, t);
11
11
  }
@@ -1,5 +1,5 @@
1
1
  import { O as e, Ot as t, _ as n, c as r, d as i, h as a, m as o, nt as s } from "./vue.runtime.esm-bundler-D1diBMsm.js";
2
- import { R as c } from "./useEditorCore-idmPLUeb.js";
2
+ import { R as c } from "./useEditorCore-P4UkhcyS.js";
3
3
  import { t as l } from "./useI18n-DoVKdCTH.js";
4
4
  import { t as u } from "./x-Mby7KlhS.js";
5
5
  //#region src/components/SavedBlocksPickBar.vue?vue&type=script&setup=true&lang.ts
@@ -1,6 +1,6 @@
1
1
  import { A as e, Et as t, I as n, O as r, Ot as i, R as a, Y as o, _ as s, c, d as l, h as u, nt as d, r as f, s as p, u as m } from "./vue.runtime.esm-bundler-D1diBMsm.js";
2
2
  import { c as h, t as g } from "./runtime-dom.esm-bundler-CepEmvwF.js";
3
- import "./useEditorCore-idmPLUeb.js";
3
+ import "./useEditorCore-P4UkhcyS.js";
4
4
  import { s as _ } from "./usePopoverPosition-C0dPEF4X.js";
5
5
  import { t as v } from "./useI18n-DoVKdCTH.js";
6
6
  import { t as y } from "./createLucideIcon-C8WzBYCv.js";
@@ -1,14 +1,14 @@
1
1
  import { A as e, Et as t, I as n, O as r, Ot as i, R as a, Y as o, _ as s, c, d as l, h as u, l as d, m as f, nt as p, r as m, s as h, u as g, x as _, z as v } from "./vue.runtime.esm-bundler-D1diBMsm.js";
2
2
  import { a as ee, i as te } from "./runtime-dom.esm-bundler-CepEmvwF.js";
3
3
  import { j as ne } from "./dist-B1kLn1rs.js";
4
- import { W as re } from "./useEditorCore-idmPLUeb.js";
4
+ import { W as re } from "./useEditorCore-P4UkhcyS.js";
5
5
  import { E as ie, L as ae, g as oe, k as se } from "./keys-fwF3qZax.js";
6
6
  import { t as ce } from "./useI18n-DoVKdCTH.js";
7
7
  import { t as le } from "./check-On4TSac-.js";
8
8
  import { t as ue } from "./loader-circle-bO7o7_HD.js";
9
9
  import { n as de, t as fe } from "./MergeTagModeToggle-P-UguQHc.js";
10
- import { t as pe } from "./TplModal-DoneXIj_.js";
11
- import { t as me } from "./BlockPreviewCanvas-DbNOqSI1.js";
10
+ import { t as pe } from "./TplModal-Dv9zgSWI.js";
11
+ import { t as me } from "./BlockPreviewCanvas-BZKslwGI.js";
12
12
  //#region src/utils/validateEmailShape.ts
13
13
  var y = /[,;<>()[\]\\"'`|/?#@!$%^&*+=~{}]/;
14
14
  function he(e) {
@@ -5,7 +5,7 @@ var o = /* @__PURE__ */ t({
5
5
  __name: "TestEmailPanel",
6
6
  props: { feature: {} },
7
7
  setup(t) {
8
- let o = t, s = n(() => import("./TestEmailModal-EM0RP_9L.js"));
8
+ let o = t, s = n(() => import("./TestEmailModal-BHDKmd6B.js"));
9
9
  function c(e) {
10
10
  o.feature.send(e);
11
11
  }
@@ -1,5 +1,5 @@
1
1
  import { Dt as e, Et as t, O as n, Ot as r, W as i, _ as a, c as o, d as s, h as c, i as l, l as u, m as d, nt as f, r as p, s as m, u as h, x as g } from "./vue.runtime.esm-bundler-D1diBMsm.js";
2
- import { C as ee, S as te } from "./useEditorCore-idmPLUeb.js";
2
+ import { C as ee, S as te } from "./useEditorCore-P4UkhcyS.js";
3
3
  import { n as ne } from "./usePopoverPosition-C0dPEF4X.js";
4
4
  import { O as _, T as v, U as y, V as b } from "./keys-fwF3qZax.js";
5
5
  import { t as re } from "./useI18n-DoVKdCTH.js";
@@ -30,11 +30,11 @@ var w = { class: "tpl-text-editor-wrapper tpl:relative" }, T = ["data-tpl-theme"
30
30
  onDone: () => I("done"),
31
31
  editorName: "TitleEditor",
32
32
  async loadExtensions({ mergeTags: e, syntax: t, triggerChar: n, autocompleteEnabled: r, suggestionEmptyText: i }) {
33
- let [{ Editor: a, EditorContent: o }, { default: s }, { default: c }, { MergeTagNode: l, MergeTagSuggestion: u, LogicMergeTagNode: d }] = await Promise.all([
33
+ let [{ Editor: a, EditorContent: o }, { default: s }, { default: c }, { MergeTagNode: l, MergeTagSuggestion: u, LogicMergeTagNode: d, LineBoundaryKeys: f }] = await Promise.all([
34
34
  import("./dist-Cl8J0IES.js").then((e) => e.r),
35
35
  import("./dist-DMJRSouD.js"),
36
36
  import("./dist-M7m1lYRe.js"),
37
- import("./extensions-DzKNPITR.js")
37
+ import("./extensions-Cwg9S-0f.js")
38
38
  ]);
39
39
  return {
40
40
  TiptapEditor: a,
@@ -63,6 +63,7 @@ var w = { class: "tpl-text-editor-wrapper tpl:relative" }, T = ["data-tpl-theme"
63
63
  syntax: t
64
64
  }),
65
65
  d.configure({ syntax: t }),
66
+ f,
66
67
  ...r && n && e.length > 0 ? [u.configure({
67
68
  mergeTags: e,
68
69
  char: n,
@@ -1,6 +1,6 @@
1
1
  import { Dt as e, O as t, R as n, Y as r, _ as i, c as a, d as o, h as s, i as c, j as l, l as u, nt as d, s as f, u as p, x as m } from "./vue.runtime.esm-bundler-D1diBMsm.js";
2
2
  import { c as h, t as g } from "./runtime-dom.esm-bundler-CepEmvwF.js";
3
- import { O as _ } from "./useEditorCore-idmPLUeb.js";
3
+ import { O as _ } from "./useEditorCore-P4UkhcyS.js";
4
4
  import { n as v } from "./usePopoverPosition-C0dPEF4X.js";
5
5
  import { U as y, V as b } from "./keys-fwF3qZax.js";
6
6
  //#region src/components/TplModal.vue?vue&type=script&setup=true&lang.ts
@@ -1,4 +1,4 @@
1
- import { A as e, L as t, M as n, N as r, j as i } from "./useEditorCore-idmPLUeb.js";
1
+ import { A as e, L as t, M as n, N as r, j as i } from "./useEditorCore-P4UkhcyS.js";
2
2
  import { t as a } from "./createLucideIcon-C8WzBYCv.js";
3
3
  var o = a("columns-3", [
4
4
  ["rect", {
@@ -1,10 +1,10 @@
1
1
  {
2
- "version": "0.26.2",
3
- "initialGzipBytes": 297146,
2
+ "version": "0.26.3",
3
+ "initialGzipBytes": 297148,
4
4
  "initialRawBytes": 1068314,
5
5
  "initialFileCount": 41,
6
- "lazyGzipBytes": 210024,
7
- "lazyRawBytes": 685534,
6
+ "lazyGzipBytes": 210277,
7
+ "lazyRawBytes": 686125,
8
8
  "lazyFileCount": 54,
9
- "generatedAt": "2026-08-19T09:57:51.761Z"
9
+ "generatedAt": "2026-08-19T15:22:31.506Z"
10
10
  }
@@ -1,5 +1,5 @@
1
1
  import { L as e, M as t, P as n, ct as r, f as i, g as a, lt as o, m as s, p as c, u as l, x as u } from "./draggable-DCs9qL-b.js";
2
- import "./useEditorCore-CKQ9gmoo.js";
2
+ import "./useEditorCore-CcgxY-Gy.js";
3
3
  import { G as d, t as f } from "./keys-Cmc2Yl3D.js";
4
4
  import { E as p, k as m, pt as h } from "./icons-CvYN6zrM.js";
5
5
  import { t as g } from "./_plugin-vue_export-helper-B3ysoDQm.js";
@@ -60,4 +60,4 @@ var v = {
60
60
  //#endregion
61
61
  export { C as default };
62
62
 
63
- //# sourceMappingURL=AiFeatureMenu-Cwj8afsi.js.map
63
+ //# sourceMappingURL=AiFeatureMenu-Cb9gaRBn.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AiFeatureMenu-Cwj8afsi.js","names":[],"sources":["../../../src/cloud/components/AiFeatureMenu.vue","../../../src/cloud/components/AiFeatureMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useCloudI18nStrict } from \"../../composables\";\nimport { AI_CONFIG_KEY, requireInject } from \"../../keys\";\nimport type { AiConfig } from \"@templatical/types\";\nimport { ImageUp, ShieldCheck, Sparkles } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nexport type AiFeature = \"ai-chat\" | \"design-reference\" | \"scoring\";\n\ndefineProps<{\n activeFeature: AiFeature | null;\n}>();\n\nconst emit = defineEmits<{\n (e: \"select\", feature: AiFeature): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n\nconst aiConfig = requireInject(AI_CONFIG_KEY, \"AiFeatureMenu\");\n\nconst configKeyMap: Record<AiFeature, keyof AiConfig> = {\n \"ai-chat\": \"chat\",\n \"design-reference\": \"designToTemplate\",\n scoring: \"scoring\",\n};\n\nconst allFeatures: { key: AiFeature; icon: typeof Sparkles }[] = [\n { key: \"ai-chat\", icon: Sparkles },\n { key: \"design-reference\", icon: ImageUp },\n { key: \"scoring\", icon: ShieldCheck },\n];\n\nconst features = computed(() =>\n allFeatures.filter((f) => aiConfig.isFeatureEnabled(configKeyMap[f.key])),\n);\n\nfunction getTitle(key: AiFeature): string {\n if (key === \"ai-chat\") return cloudT.aiMenu.aiAssistant;\n if (key === \"design-reference\") return cloudT.aiMenu.designToTemplate;\n return cloudT.aiMenu.templateScore;\n}\n\nfunction getDescription(key: AiFeature): string {\n if (key === \"ai-chat\") return cloudT.aiMenu.aiAssistantDesc;\n if (key === \"design-reference\") return cloudT.aiMenu.designToTemplateDesc;\n return cloudT.aiMenu.templateScoreDesc;\n}\n</script>\n\n<template>\n <div\n class=\"tpl-ai-feature-menu tpl:w-[280px] tpl:overflow-hidden tpl:rounded-[var(--tpl-radius)] tpl:py-1 tpl:bg-[var(--tpl-bg-elevated)] tpl:border tpl:border-[var(--tpl-border)] tpl:shadow-[var(--tpl-shadow-lg)]\"\n style=\"backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px)\"\n >\n <button\n v-for=\"feature in features\"\n :key=\"feature.key\"\n class=\"tpl-ai-feature-menu-item tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-start tpl:gap-3 tpl:border-none tpl:px-3 tpl:py-2.5 tpl:text-left tpl:transition-colors tpl:duration-100\"\n :style=\"{\n backgroundColor:\n activeFeature === feature.key\n ? 'var(--tpl-primary-light)'\n : 'transparent',\n }\"\n @click=\"emit('select', feature.key)\"\n >\n <div\n class=\"tpl:mt-0.5 tpl:flex tpl:size-7 tpl:shrink-0 tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n backgroundColor:\n activeFeature === feature.key\n ? 'var(--tpl-primary)'\n : 'var(--tpl-bg-active)',\n color:\n activeFeature === feature.key\n ? 'var(--tpl-bg)'\n : 'var(--tpl-text-muted)',\n }\"\n >\n <component :is=\"feature.icon\" :size=\"15\" :stroke-width=\"2\" />\n </div>\n <div class=\"tpl:flex tpl:min-w-0 tpl:flex-col tpl:gap-0.5\">\n <span class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text)]\">\n {{ getTitle(feature.key) }}\n </span>\n <span\n class=\"tpl:text-xs tpl:leading-snug tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ getDescription(feature.key) }}\n </span>\n </div>\n </button>\n </div>\n</template>\n\n<style scoped>\n.tpl-ai-feature-menu-item:hover {\n background-color: var(--tpl-bg-hover) !important;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useCloudI18nStrict } from \"../../composables\";\nimport { AI_CONFIG_KEY, requireInject } from \"../../keys\";\nimport type { AiConfig } from \"@templatical/types\";\nimport { ImageUp, ShieldCheck, Sparkles } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nexport type AiFeature = \"ai-chat\" | \"design-reference\" | \"scoring\";\n\ndefineProps<{\n activeFeature: AiFeature | null;\n}>();\n\nconst emit = defineEmits<{\n (e: \"select\", feature: AiFeature): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n\nconst aiConfig = requireInject(AI_CONFIG_KEY, \"AiFeatureMenu\");\n\nconst configKeyMap: Record<AiFeature, keyof AiConfig> = {\n \"ai-chat\": \"chat\",\n \"design-reference\": \"designToTemplate\",\n scoring: \"scoring\",\n};\n\nconst allFeatures: { key: AiFeature; icon: typeof Sparkles }[] = [\n { key: \"ai-chat\", icon: Sparkles },\n { key: \"design-reference\", icon: ImageUp },\n { key: \"scoring\", icon: ShieldCheck },\n];\n\nconst features = computed(() =>\n allFeatures.filter((f) => aiConfig.isFeatureEnabled(configKeyMap[f.key])),\n);\n\nfunction getTitle(key: AiFeature): string {\n if (key === \"ai-chat\") return cloudT.aiMenu.aiAssistant;\n if (key === \"design-reference\") return cloudT.aiMenu.designToTemplate;\n return cloudT.aiMenu.templateScore;\n}\n\nfunction getDescription(key: AiFeature): string {\n if (key === \"ai-chat\") return cloudT.aiMenu.aiAssistantDesc;\n if (key === \"design-reference\") return cloudT.aiMenu.designToTemplateDesc;\n return cloudT.aiMenu.templateScoreDesc;\n}\n</script>\n\n<template>\n <div\n class=\"tpl-ai-feature-menu tpl:w-[280px] tpl:overflow-hidden tpl:rounded-[var(--tpl-radius)] tpl:py-1 tpl:bg-[var(--tpl-bg-elevated)] tpl:border tpl:border-[var(--tpl-border)] tpl:shadow-[var(--tpl-shadow-lg)]\"\n style=\"backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px)\"\n >\n <button\n v-for=\"feature in features\"\n :key=\"feature.key\"\n class=\"tpl-ai-feature-menu-item tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-start tpl:gap-3 tpl:border-none tpl:px-3 tpl:py-2.5 tpl:text-left tpl:transition-colors tpl:duration-100\"\n :style=\"{\n backgroundColor:\n activeFeature === feature.key\n ? 'var(--tpl-primary-light)'\n : 'transparent',\n }\"\n @click=\"emit('select', feature.key)\"\n >\n <div\n class=\"tpl:mt-0.5 tpl:flex tpl:size-7 tpl:shrink-0 tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n backgroundColor:\n activeFeature === feature.key\n ? 'var(--tpl-primary)'\n : 'var(--tpl-bg-active)',\n color:\n activeFeature === feature.key\n ? 'var(--tpl-bg)'\n : 'var(--tpl-text-muted)',\n }\"\n >\n <component :is=\"feature.icon\" :size=\"15\" :stroke-width=\"2\" />\n </div>\n <div class=\"tpl:flex tpl:min-w-0 tpl:flex-col tpl:gap-0.5\">\n <span class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text)]\">\n {{ getTitle(feature.key) }}\n </span>\n <span\n class=\"tpl:text-xs tpl:leading-snug tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ getDescription(feature.key) }}\n </span>\n </div>\n </button>\n </div>\n</template>\n\n<style scoped>\n.tpl-ai-feature-menu-item:hover {\n background-color: var(--tpl-bg-hover) !important;\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;EAaA,IAAM,IAAO,GAIP,EAAE,GAAG,MAAW,EAAmB,GAEnC,IAAW,EAAc,GAAe,eAAe,GAEvD,IAAkD;GACtD,WAAW;GACX,oBAAoB;GACpB,SAAS;EACX,GAEM,IAA2D;GAC/D;IAAE,KAAK;IAAW,MAAM;GAAS;GACjC;IAAE,KAAK;IAAoB,MAAM;GAAQ;GACzC;IAAE,KAAK;IAAW,MAAM;GAAY;EACtC,GAEM,IAAW,QACf,EAAY,QAAQ,MAAM,EAAS,iBAAiB,EAAa,EAAE,IAAI,CAAC,CAC1E;EAEA,SAAS,EAAS,GAAwB;GAGxC,OAFI,MAAQ,YAAkB,EAAO,OAAO,cACxC,MAAQ,qBAA2B,EAAO,OAAO,mBAC9C,EAAO,OAAO;EACvB;EAEA,SAAS,EAAe,GAAwB;GAG9C,OAFI,MAAQ,YAAkB,EAAO,OAAO,kBACxC,MAAQ,qBAA2B,EAAO,OAAO,uBAC9C,EAAO,OAAO;EACvB;oBAIE,EAAA,GAAA,EA0CM,OA1CN,GA0CM,EAtCJ,EAAA,EAAA,GAAA,EAqCS,GAAA,MAAA,EApCW,EAAA,QAAX,OADT,EAAA,GAAA,EAqCS,UAAA;GAnCN,KAAK,EAAQ;GACd,OAAM;GACL,OAAK,EAAA,EAAA,iBAAuC,EAAA,kBAAkB,EAAQ,MAAA,6BAAA,cAAA,CAAA;GAMtE,UAAK,MAAE,EAAI,UAAW,EAAQ,GAAG;EAElC,GAAA,CAAA,EAcM,OAAA;GAbJ,OAAM;GACL,OAAK,EAAA;IAA2C,iBAAA,EAAA,kBAAkB,EAAQ,MAAA,uBAAA;IAA8G,OAAA,EAAA,kBAAkB,EAAQ,MAAA,kBAAA;;EAWnN,GAAA,EAAA,EAAA,GAAA,EAA6D,EAA7C,EAAQ,IAAI,GAAA;GAAG,MAAM;GAAK,gBAAc;EAE1D,CAAA,EAAA,GAAA,CAAA,GAAA,EASM,OATN,GASM,CARJ,EAEO,QAFP,GAEO,EADF,EAAS,EAAQ,GAAG,CAAA,GAAA,CAAA,GAEzB,EAIO,QAJP,GAIO,EADF,EAAe,EAAQ,GAAG,CAAA,GAAA,CAAA,CAAA,CAAA,CAAA,GAAA,IAAA,CAAA"}
1
+ {"version":3,"file":"AiFeatureMenu-Cb9gaRBn.js","names":[],"sources":["../../../src/cloud/components/AiFeatureMenu.vue","../../../src/cloud/components/AiFeatureMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useCloudI18nStrict } from \"../../composables\";\nimport { AI_CONFIG_KEY, requireInject } from \"../../keys\";\nimport type { AiConfig } from \"@templatical/types\";\nimport { ImageUp, ShieldCheck, Sparkles } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nexport type AiFeature = \"ai-chat\" | \"design-reference\" | \"scoring\";\n\ndefineProps<{\n activeFeature: AiFeature | null;\n}>();\n\nconst emit = defineEmits<{\n (e: \"select\", feature: AiFeature): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n\nconst aiConfig = requireInject(AI_CONFIG_KEY, \"AiFeatureMenu\");\n\nconst configKeyMap: Record<AiFeature, keyof AiConfig> = {\n \"ai-chat\": \"chat\",\n \"design-reference\": \"designToTemplate\",\n scoring: \"scoring\",\n};\n\nconst allFeatures: { key: AiFeature; icon: typeof Sparkles }[] = [\n { key: \"ai-chat\", icon: Sparkles },\n { key: \"design-reference\", icon: ImageUp },\n { key: \"scoring\", icon: ShieldCheck },\n];\n\nconst features = computed(() =>\n allFeatures.filter((f) => aiConfig.isFeatureEnabled(configKeyMap[f.key])),\n);\n\nfunction getTitle(key: AiFeature): string {\n if (key === \"ai-chat\") return cloudT.aiMenu.aiAssistant;\n if (key === \"design-reference\") return cloudT.aiMenu.designToTemplate;\n return cloudT.aiMenu.templateScore;\n}\n\nfunction getDescription(key: AiFeature): string {\n if (key === \"ai-chat\") return cloudT.aiMenu.aiAssistantDesc;\n if (key === \"design-reference\") return cloudT.aiMenu.designToTemplateDesc;\n return cloudT.aiMenu.templateScoreDesc;\n}\n</script>\n\n<template>\n <div\n class=\"tpl-ai-feature-menu tpl:w-[280px] tpl:overflow-hidden tpl:rounded-[var(--tpl-radius)] tpl:py-1 tpl:bg-[var(--tpl-bg-elevated)] tpl:border tpl:border-[var(--tpl-border)] tpl:shadow-[var(--tpl-shadow-lg)]\"\n style=\"backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px)\"\n >\n <button\n v-for=\"feature in features\"\n :key=\"feature.key\"\n class=\"tpl-ai-feature-menu-item tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-start tpl:gap-3 tpl:border-none tpl:px-3 tpl:py-2.5 tpl:text-left tpl:transition-colors tpl:duration-100\"\n :style=\"{\n backgroundColor:\n activeFeature === feature.key\n ? 'var(--tpl-primary-light)'\n : 'transparent',\n }\"\n @click=\"emit('select', feature.key)\"\n >\n <div\n class=\"tpl:mt-0.5 tpl:flex tpl:size-7 tpl:shrink-0 tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n backgroundColor:\n activeFeature === feature.key\n ? 'var(--tpl-primary)'\n : 'var(--tpl-bg-active)',\n color:\n activeFeature === feature.key\n ? 'var(--tpl-bg)'\n : 'var(--tpl-text-muted)',\n }\"\n >\n <component :is=\"feature.icon\" :size=\"15\" :stroke-width=\"2\" />\n </div>\n <div class=\"tpl:flex tpl:min-w-0 tpl:flex-col tpl:gap-0.5\">\n <span class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text)]\">\n {{ getTitle(feature.key) }}\n </span>\n <span\n class=\"tpl:text-xs tpl:leading-snug tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ getDescription(feature.key) }}\n </span>\n </div>\n </button>\n </div>\n</template>\n\n<style scoped>\n.tpl-ai-feature-menu-item:hover {\n background-color: var(--tpl-bg-hover) !important;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useCloudI18nStrict } from \"../../composables\";\nimport { AI_CONFIG_KEY, requireInject } from \"../../keys\";\nimport type { AiConfig } from \"@templatical/types\";\nimport { ImageUp, ShieldCheck, Sparkles } from \"@lucide/vue\";\nimport { computed } from \"vue\";\n\nexport type AiFeature = \"ai-chat\" | \"design-reference\" | \"scoring\";\n\ndefineProps<{\n activeFeature: AiFeature | null;\n}>();\n\nconst emit = defineEmits<{\n (e: \"select\", feature: AiFeature): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n\nconst aiConfig = requireInject(AI_CONFIG_KEY, \"AiFeatureMenu\");\n\nconst configKeyMap: Record<AiFeature, keyof AiConfig> = {\n \"ai-chat\": \"chat\",\n \"design-reference\": \"designToTemplate\",\n scoring: \"scoring\",\n};\n\nconst allFeatures: { key: AiFeature; icon: typeof Sparkles }[] = [\n { key: \"ai-chat\", icon: Sparkles },\n { key: \"design-reference\", icon: ImageUp },\n { key: \"scoring\", icon: ShieldCheck },\n];\n\nconst features = computed(() =>\n allFeatures.filter((f) => aiConfig.isFeatureEnabled(configKeyMap[f.key])),\n);\n\nfunction getTitle(key: AiFeature): string {\n if (key === \"ai-chat\") return cloudT.aiMenu.aiAssistant;\n if (key === \"design-reference\") return cloudT.aiMenu.designToTemplate;\n return cloudT.aiMenu.templateScore;\n}\n\nfunction getDescription(key: AiFeature): string {\n if (key === \"ai-chat\") return cloudT.aiMenu.aiAssistantDesc;\n if (key === \"design-reference\") return cloudT.aiMenu.designToTemplateDesc;\n return cloudT.aiMenu.templateScoreDesc;\n}\n</script>\n\n<template>\n <div\n class=\"tpl-ai-feature-menu tpl:w-[280px] tpl:overflow-hidden tpl:rounded-[var(--tpl-radius)] tpl:py-1 tpl:bg-[var(--tpl-bg-elevated)] tpl:border tpl:border-[var(--tpl-border)] tpl:shadow-[var(--tpl-shadow-lg)]\"\n style=\"backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px)\"\n >\n <button\n v-for=\"feature in features\"\n :key=\"feature.key\"\n class=\"tpl-ai-feature-menu-item tpl:flex tpl:w-full tpl:cursor-pointer tpl:items-start tpl:gap-3 tpl:border-none tpl:px-3 tpl:py-2.5 tpl:text-left tpl:transition-colors tpl:duration-100\"\n :style=\"{\n backgroundColor:\n activeFeature === feature.key\n ? 'var(--tpl-primary-light)'\n : 'transparent',\n }\"\n @click=\"emit('select', feature.key)\"\n >\n <div\n class=\"tpl:mt-0.5 tpl:flex tpl:size-7 tpl:shrink-0 tpl:items-center tpl:justify-center tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n backgroundColor:\n activeFeature === feature.key\n ? 'var(--tpl-primary)'\n : 'var(--tpl-bg-active)',\n color:\n activeFeature === feature.key\n ? 'var(--tpl-bg)'\n : 'var(--tpl-text-muted)',\n }\"\n >\n <component :is=\"feature.icon\" :size=\"15\" :stroke-width=\"2\" />\n </div>\n <div class=\"tpl:flex tpl:min-w-0 tpl:flex-col tpl:gap-0.5\">\n <span class=\"tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text)]\">\n {{ getTitle(feature.key) }}\n </span>\n <span\n class=\"tpl:text-xs tpl:leading-snug tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ getDescription(feature.key) }}\n </span>\n </div>\n </button>\n </div>\n</template>\n\n<style scoped>\n.tpl-ai-feature-menu-item:hover {\n background-color: var(--tpl-bg-hover) !important;\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;EAaA,IAAM,IAAO,GAIP,EAAE,GAAG,MAAW,EAAmB,GAEnC,IAAW,EAAc,GAAe,eAAe,GAEvD,IAAkD;GACtD,WAAW;GACX,oBAAoB;GACpB,SAAS;EACX,GAEM,IAA2D;GAC/D;IAAE,KAAK;IAAW,MAAM;GAAS;GACjC;IAAE,KAAK;IAAoB,MAAM;GAAQ;GACzC;IAAE,KAAK;IAAW,MAAM;GAAY;EACtC,GAEM,IAAW,QACf,EAAY,QAAQ,MAAM,EAAS,iBAAiB,EAAa,EAAE,IAAI,CAAC,CAC1E;EAEA,SAAS,EAAS,GAAwB;GAGxC,OAFI,MAAQ,YAAkB,EAAO,OAAO,cACxC,MAAQ,qBAA2B,EAAO,OAAO,mBAC9C,EAAO,OAAO;EACvB;EAEA,SAAS,EAAe,GAAwB;GAG9C,OAFI,MAAQ,YAAkB,EAAO,OAAO,kBACxC,MAAQ,qBAA2B,EAAO,OAAO,uBAC9C,EAAO,OAAO;EACvB;oBAIE,EAAA,GAAA,EA0CM,OA1CN,GA0CM,EAtCJ,EAAA,EAAA,GAAA,EAqCS,GAAA,MAAA,EApCW,EAAA,QAAX,OADT,EAAA,GAAA,EAqCS,UAAA;GAnCN,KAAK,EAAQ;GACd,OAAM;GACL,OAAK,EAAA,EAAA,iBAAuC,EAAA,kBAAkB,EAAQ,MAAA,6BAAA,cAAA,CAAA;GAMtE,UAAK,MAAE,EAAI,UAAW,EAAQ,GAAG;EAElC,GAAA,CAAA,EAcM,OAAA;GAbJ,OAAM;GACL,OAAK,EAAA;IAA2C,iBAAA,EAAA,kBAAkB,EAAQ,MAAA,uBAAA;IAA8G,OAAA,EAAA,kBAAkB,EAAQ,MAAA,kBAAA;;EAWnN,GAAA,EAAA,EAAA,GAAA,EAA6D,EAA7C,EAAQ,IAAI,GAAA;GAAG,MAAM;GAAK,gBAAc;EAE1D,CAAA,EAAA,GAAA,CAAA,GAAA,EASM,OATN,GASM,CARJ,EAEO,QAFP,GAEO,EADF,EAAS,EAAQ,GAAG,CAAA,GAAA,CAAA,GAEzB,EAIO,QAJP,GAIO,EADF,EAAe,EAAQ,GAAG,CAAA,GAAA,CAAA,CAAA,CAAA,CAAA,GAAA,IAAA,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import { n as e } from "./rolldown-runtime-B0aSnxlc.js";
2
2
  import { I as t, L as n, M as r, N as i, P as a, T as o, ct as s, f as c, g as l, it as u, m as d, p as f, u as p, x as m } from "./draggable-DCs9qL-b.js";
3
- import { _ as h, a as g, c as _, d as v, f as y, h as b, i as x, k as S, m as C, n as w, p as T, r as E, u as D, v as O, y as k } from "./useEditorCore-CKQ9gmoo.js";
3
+ import { _ as h, a as g, c as _, d as v, f as y, h as b, i as x, k as S, m as C, n as w, p as T, r as E, u as D, v as O, y as k } from "./useEditorCore-CcgxY-Gy.js";
4
4
  import { W as A, f as j, g as M, k as N, s as P } from "./keys-Cmc2Yl3D.js";
5
5
  import { n as F, t as I } from "./emailFrameWidth-BmFCN2dp.js";
6
6
  //#region src/components/blocks/PreviewSectionBlock.vue?vue&type=script&setup=true&lang.ts
@@ -120,4 +120,4 @@ var L = { class: "tpl:w-full" }, R = { class: "tpl:flex tpl:gap-0" }, z = /* @__
120
120
  //#endregion
121
121
  export { V as n, H as t };
122
122
 
123
- //# sourceMappingURL=BlockPreviewCanvas-DPtiMsVz.js.map
123
+ //# sourceMappingURL=BlockPreviewCanvas-msj619wT.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BlockPreviewCanvas-DPtiMsVz.js","names":[],"sources":["../../../src/components/blocks/PreviewSectionBlock.vue","../../../src/components/blocks/PreviewSectionBlock.vue","../../../src/components/BlockPreviewCanvas.vue","../../../src/components/BlockPreviewCanvas.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport ButtonBlock from \"./ButtonBlock.vue\";\nimport CustomBlock from \"./CustomBlock.vue\";\nimport DividerBlock from \"./DividerBlock.vue\";\nimport HtmlBlock from \"./HtmlBlock.vue\";\nimport ImageBlock from \"./ImageBlock.vue\";\nimport MenuBlock from \"./MenuBlock.vue\";\nimport SocialIconsBlock from \"./SocialIconsBlock.vue\";\nimport SpacerBlock from \"./SpacerBlock.vue\";\nimport TableBlock from \"./TableBlock.vue\";\nimport TitleBlock from \"./TitleBlock.vue\";\nimport ParagraphBlock from \"./ParagraphBlock.vue\";\nimport VideoBlock from \"./VideoBlock.vue\";\nimport {\n resolveBlockComponent,\n getBlockWrapperStyle,\n} from \"../../utils/blockComponentResolver\";\nimport type {\n Block,\n SectionBlock as SectionBlockType,\n} from \"@templatical/types\";\nimport { computed, inject, type Component } from \"vue\";\nimport { BLOCK_REGISTRY_KEY } from \"../../keys\";\n\nconst previewBlockComponentMap: Record<string, Component> = {\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n video: VideoBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n custom: CustomBlock,\n};\n\nconst props = defineProps<{\n block: SectionBlockType;\n viewport: \"desktop\";\n}>();\n\ndefineOptions({ name: \"PreviewSectionBlock\" });\n\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY, null);\n\nconst columnWidths = computed(() => {\n switch (props.block.columns) {\n case \"2\":\n return [\"50%\", \"50%\"];\n case \"3\":\n return [\"33.33%\", \"33.33%\", \"33.33%\"];\n case \"1-2\":\n return [\"33.33%\", \"66.67%\"];\n case \"2-1\":\n return [\"66.67%\", \"33.33%\"];\n default:\n return [\"100%\"];\n }\n});\n\nconst columns = computed(() => {\n const count = columnWidths.value.length;\n const children = [...props.block.children];\n while (children.length < count) {\n children.push([]);\n }\n return children.slice(0, count);\n});\n\nfunction getColumnBlocks(colIndex: number): Block[] {\n return columns.value[colIndex] || [];\n}\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, previewBlockComponentMap);\n}\n</script>\n\n<template>\n <div class=\"tpl:w-full\">\n <div class=\"tpl:flex tpl:gap-0\">\n <div\n v-for=\"(_, colIndex) in columns\"\n :key=\"colIndex\"\n :style=\"{ width: columnWidths[colIndex] }\"\n >\n <div\n v-for=\"childBlock in getColumnBlocks(colIndex)\"\n :key=\"childBlock.id\"\n :style=\"getBlockWrapperStyle(childBlock)\"\n >\n <!-- Recursive self-reference for nested sections -->\n <PreviewSectionBlock\n v-if=\"childBlock.type === 'section'\"\n :block=\"childBlock\"\n viewport=\"desktop\"\n />\n <component\n :is=\"getBlockComponent(childBlock)\"\n v-else\n :block=\"childBlock\"\n viewport=\"desktop\"\n />\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ButtonBlock from \"./ButtonBlock.vue\";\nimport CustomBlock from \"./CustomBlock.vue\";\nimport DividerBlock from \"./DividerBlock.vue\";\nimport HtmlBlock from \"./HtmlBlock.vue\";\nimport ImageBlock from \"./ImageBlock.vue\";\nimport MenuBlock from \"./MenuBlock.vue\";\nimport SocialIconsBlock from \"./SocialIconsBlock.vue\";\nimport SpacerBlock from \"./SpacerBlock.vue\";\nimport TableBlock from \"./TableBlock.vue\";\nimport TitleBlock from \"./TitleBlock.vue\";\nimport ParagraphBlock from \"./ParagraphBlock.vue\";\nimport VideoBlock from \"./VideoBlock.vue\";\nimport {\n resolveBlockComponent,\n getBlockWrapperStyle,\n} from \"../../utils/blockComponentResolver\";\nimport type {\n Block,\n SectionBlock as SectionBlockType,\n} from \"@templatical/types\";\nimport { computed, inject, type Component } from \"vue\";\nimport { BLOCK_REGISTRY_KEY } from \"../../keys\";\n\nconst previewBlockComponentMap: Record<string, Component> = {\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n video: VideoBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n custom: CustomBlock,\n};\n\nconst props = defineProps<{\n block: SectionBlockType;\n viewport: \"desktop\";\n}>();\n\ndefineOptions({ name: \"PreviewSectionBlock\" });\n\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY, null);\n\nconst columnWidths = computed(() => {\n switch (props.block.columns) {\n case \"2\":\n return [\"50%\", \"50%\"];\n case \"3\":\n return [\"33.33%\", \"33.33%\", \"33.33%\"];\n case \"1-2\":\n return [\"33.33%\", \"66.67%\"];\n case \"2-1\":\n return [\"66.67%\", \"33.33%\"];\n default:\n return [\"100%\"];\n }\n});\n\nconst columns = computed(() => {\n const count = columnWidths.value.length;\n const children = [...props.block.children];\n while (children.length < count) {\n children.push([]);\n }\n return children.slice(0, count);\n});\n\nfunction getColumnBlocks(colIndex: number): Block[] {\n return columns.value[colIndex] || [];\n}\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, previewBlockComponentMap);\n}\n</script>\n\n<template>\n <div class=\"tpl:w-full\">\n <div class=\"tpl:flex tpl:gap-0\">\n <div\n v-for=\"(_, colIndex) in columns\"\n :key=\"colIndex\"\n :style=\"{ width: columnWidths[colIndex] }\"\n >\n <div\n v-for=\"childBlock in getColumnBlocks(colIndex)\"\n :key=\"childBlock.id\"\n :style=\"getBlockWrapperStyle(childBlock)\"\n >\n <!-- Recursive self-reference for nested sections -->\n <PreviewSectionBlock\n v-if=\"childBlock.type === 'section'\"\n :block=\"childBlock\"\n viewport=\"desktop\"\n />\n <component\n :is=\"getBlockComponent(childBlock)\"\n v-else\n :block=\"childBlock\"\n viewport=\"desktop\"\n />\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ButtonBlock from \"./blocks/ButtonBlock.vue\";\nimport CustomBlock from \"./blocks/CustomBlock.vue\";\nimport DividerBlock from \"./blocks/DividerBlock.vue\";\nimport HtmlBlock from \"./blocks/HtmlBlock.vue\";\nimport ImageBlock from \"./blocks/ImageBlock.vue\";\nimport MenuBlock from \"./blocks/MenuBlock.vue\";\nimport PreviewSectionBlock from \"./blocks/PreviewSectionBlock.vue\";\nimport SocialIconsBlock from \"./blocks/SocialIconsBlock.vue\";\nimport SpacerBlock from \"./blocks/SpacerBlock.vue\";\nimport TableBlock from \"./blocks/TableBlock.vue\";\nimport TitleBlock from \"./blocks/TitleBlock.vue\";\nimport ParagraphBlock from \"./blocks/ParagraphBlock.vue\";\nimport VideoBlock from \"./blocks/VideoBlock.vue\";\nimport {\n BLOCK_REGISTRY_KEY,\n CONDITION_PREVIEW_KEY,\n EDITOR_KEY,\n MERGE_TAG_SAMPLE_MODE_KEY,\n USE_MERGE_TAG_SAMPLES_KEY,\n} from \"../keys\";\nimport {\n resolveBlockComponent,\n getBlockWrapperStyle,\n getDocumentStyle,\n} from \"../utils/blockComponentResolver\";\nimport {\n EMAIL_FRAME_WIDTH_TRANSITION,\n getEmailFrameWidth,\n} from \"../utils/emailFrameWidth\";\nimport type { Block, ViewportSize } from \"@templatical/types\";\nimport { computed, inject, provide, type Component } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n blocks: Block[];\n /**\n * Which viewport to render. Defaults to `desktop`, so the saved-blocks\n * surfaces — which have no viewport control — behave exactly as before.\n * The test-email dialog passes the user's choice so responsive blocks show\n * the variant a recipient on that device would receive.\n */\n viewport?: ViewportSize;\n /**\n * Whether the hand-toggled display-condition filter still applies here.\n * `false` where a resolver owns the preview: it has already evaluated every\n * condition against real data, so layering a manual hide on top would veto\n * the answer that was asked for.\n *\n * A prop rather than an injected `PREVIEW_RESOLUTION_KEY` read, because the\n * test-email dialog builds its *own* resolution instance (keyed to the\n * selected recipient) — injecting would consult the shared canvas instance\n * and get the wrong answer.\n */\n applyConditionFilter?: boolean;\n }>(),\n { viewport: \"desktop\", applyConditionFilter: true },\n);\n\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY);\nconst editor = inject(EDITOR_KEY, null);\nconst conditionPreview = inject(CONDITION_PREVIEW_KEY, null);\n\n/**\n * This canvas is only ever a preview — nothing here is editable — so it honours\n * the user's mode directly, with no `previewMode` gate of the kind `Canvas.vue`\n * needs. Re-provided rather than left to inherit, because a `BlockPreviewCanvas`\n * inside a dialog may sit under an editing `Canvas` that provides `false`.\n */\nconst mergeTagSampleMode = inject(MERGE_TAG_SAMPLE_MODE_KEY, null);\nprovide(\n USE_MERGE_TAG_SAMPLES_KEY,\n computed(() => mergeTagSampleMode?.value ?? false),\n);\n\n/**\n * Blocks a display condition currently excludes, so a preview never shows\n * content the recipient won't get. The canvas does the same via\n * `v-show=\"!conditionPreview?.isHidden(block.id)\"`.\n *\n * A no-op for the saved-blocks browser: entries there carry their *stored* ids,\n * which aren't in the current template, so nothing is tracked as hidden. It\n * bites where it should — the save dialog and the test-email preview, both of\n * which show blocks that are live on the canvas.\n *\n * Skipped entirely when `applyConditionFilter` is false — see the prop.\n */\nconst visibleBlocks = computed(() =>\n props.applyConditionFilter\n ? props.blocks.filter((block) => !conditionPreview?.isHidden(block.id))\n : props.blocks,\n);\n\n/**\n * Frame width, from the one helper the canvas and the scaled preview rows also\n * use — so all three agree by construction rather than by three constants that\n * happen to match. A template with a custom body width now previews at that\n * width instead of a flat 600.\n */\nconst frameWidth = computed(() =>\n getEmailFrameWidth(editor?.content.value.settings, props.viewport),\n);\n\n/**\n * The same document-level style the canvas applies, so a previewed block\n * renders identically to the canvas one: without it the font falls back to the\n * editor UI's and the link rules hit their unset defaults, dropping the\n * underline and the link colour.\n *\n * Read from the *current* template even in the saved-blocks browser, where the\n * previewed content came from elsewhere — a saved block stores only `Block[]`,\n * and the current settings are what it will actually look like once inserted.\n */\nconst documentStyle = computed(() =>\n editor ? getDocumentStyle(editor.content.value.settings) : {},\n);\n\nconst previewComponentMap: Record<string, Component> = {\n section: PreviewSectionBlock,\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n video: VideoBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n custom: CustomBlock,\n};\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, previewComponentMap);\n}\n</script>\n\n<template>\n <div\n data-testid=\"block-preview-canvas\"\n class=\"tpl:pointer-events-none tpl:mx-auto tpl:select-none tpl:rounded-lg\"\n :style=\"{\n width: `${frameWidth}px`,\n transition: EMAIL_FRAME_WIDTH_TRANSITION,\n backgroundColor: 'var(--tpl-canvas-bg)',\n boxShadow: 'var(--tpl-shadow-sm)',\n ...documentStyle,\n }\"\n >\n <div\n v-for=\"block in visibleBlocks\"\n :key=\"block.id\"\n :style=\"getBlockWrapperStyle(block)\"\n >\n <component\n :is=\"getBlockComponent(block)\"\n :block=\"block\"\n :viewport=\"viewport\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ButtonBlock from \"./blocks/ButtonBlock.vue\";\nimport CustomBlock from \"./blocks/CustomBlock.vue\";\nimport DividerBlock from \"./blocks/DividerBlock.vue\";\nimport HtmlBlock from \"./blocks/HtmlBlock.vue\";\nimport ImageBlock from \"./blocks/ImageBlock.vue\";\nimport MenuBlock from \"./blocks/MenuBlock.vue\";\nimport PreviewSectionBlock from \"./blocks/PreviewSectionBlock.vue\";\nimport SocialIconsBlock from \"./blocks/SocialIconsBlock.vue\";\nimport SpacerBlock from \"./blocks/SpacerBlock.vue\";\nimport TableBlock from \"./blocks/TableBlock.vue\";\nimport TitleBlock from \"./blocks/TitleBlock.vue\";\nimport ParagraphBlock from \"./blocks/ParagraphBlock.vue\";\nimport VideoBlock from \"./blocks/VideoBlock.vue\";\nimport {\n BLOCK_REGISTRY_KEY,\n CONDITION_PREVIEW_KEY,\n EDITOR_KEY,\n MERGE_TAG_SAMPLE_MODE_KEY,\n USE_MERGE_TAG_SAMPLES_KEY,\n} from \"../keys\";\nimport {\n resolveBlockComponent,\n getBlockWrapperStyle,\n getDocumentStyle,\n} from \"../utils/blockComponentResolver\";\nimport {\n EMAIL_FRAME_WIDTH_TRANSITION,\n getEmailFrameWidth,\n} from \"../utils/emailFrameWidth\";\nimport type { Block, ViewportSize } from \"@templatical/types\";\nimport { computed, inject, provide, type Component } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n blocks: Block[];\n /**\n * Which viewport to render. Defaults to `desktop`, so the saved-blocks\n * surfaces — which have no viewport control — behave exactly as before.\n * The test-email dialog passes the user's choice so responsive blocks show\n * the variant a recipient on that device would receive.\n */\n viewport?: ViewportSize;\n /**\n * Whether the hand-toggled display-condition filter still applies here.\n * `false` where a resolver owns the preview: it has already evaluated every\n * condition against real data, so layering a manual hide on top would veto\n * the answer that was asked for.\n *\n * A prop rather than an injected `PREVIEW_RESOLUTION_KEY` read, because the\n * test-email dialog builds its *own* resolution instance (keyed to the\n * selected recipient) — injecting would consult the shared canvas instance\n * and get the wrong answer.\n */\n applyConditionFilter?: boolean;\n }>(),\n { viewport: \"desktop\", applyConditionFilter: true },\n);\n\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY);\nconst editor = inject(EDITOR_KEY, null);\nconst conditionPreview = inject(CONDITION_PREVIEW_KEY, null);\n\n/**\n * This canvas is only ever a preview — nothing here is editable — so it honours\n * the user's mode directly, with no `previewMode` gate of the kind `Canvas.vue`\n * needs. Re-provided rather than left to inherit, because a `BlockPreviewCanvas`\n * inside a dialog may sit under an editing `Canvas` that provides `false`.\n */\nconst mergeTagSampleMode = inject(MERGE_TAG_SAMPLE_MODE_KEY, null);\nprovide(\n USE_MERGE_TAG_SAMPLES_KEY,\n computed(() => mergeTagSampleMode?.value ?? false),\n);\n\n/**\n * Blocks a display condition currently excludes, so a preview never shows\n * content the recipient won't get. The canvas does the same via\n * `v-show=\"!conditionPreview?.isHidden(block.id)\"`.\n *\n * A no-op for the saved-blocks browser: entries there carry their *stored* ids,\n * which aren't in the current template, so nothing is tracked as hidden. It\n * bites where it should — the save dialog and the test-email preview, both of\n * which show blocks that are live on the canvas.\n *\n * Skipped entirely when `applyConditionFilter` is false — see the prop.\n */\nconst visibleBlocks = computed(() =>\n props.applyConditionFilter\n ? props.blocks.filter((block) => !conditionPreview?.isHidden(block.id))\n : props.blocks,\n);\n\n/**\n * Frame width, from the one helper the canvas and the scaled preview rows also\n * use — so all three agree by construction rather than by three constants that\n * happen to match. A template with a custom body width now previews at that\n * width instead of a flat 600.\n */\nconst frameWidth = computed(() =>\n getEmailFrameWidth(editor?.content.value.settings, props.viewport),\n);\n\n/**\n * The same document-level style the canvas applies, so a previewed block\n * renders identically to the canvas one: without it the font falls back to the\n * editor UI's and the link rules hit their unset defaults, dropping the\n * underline and the link colour.\n *\n * Read from the *current* template even in the saved-blocks browser, where the\n * previewed content came from elsewhere — a saved block stores only `Block[]`,\n * and the current settings are what it will actually look like once inserted.\n */\nconst documentStyle = computed(() =>\n editor ? getDocumentStyle(editor.content.value.settings) : {},\n);\n\nconst previewComponentMap: Record<string, Component> = {\n section: PreviewSectionBlock,\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n video: VideoBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n custom: CustomBlock,\n};\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, previewComponentMap);\n}\n</script>\n\n<template>\n <div\n data-testid=\"block-preview-canvas\"\n class=\"tpl:pointer-events-none tpl:mx-auto tpl:select-none tpl:rounded-lg\"\n :style=\"{\n width: `${frameWidth}px`,\n transition: EMAIL_FRAME_WIDTH_TRANSITION,\n backgroundColor: 'var(--tpl-canvas-bg)',\n boxShadow: 'var(--tpl-shadow-sm)',\n ...documentStyle,\n }\"\n >\n <div\n v-for=\"block in visibleBlocks\"\n :key=\"block.id\"\n :style=\"getBlockWrapperStyle(block)\"\n >\n <component\n :is=\"getBlockComponent(block)\"\n :block=\"block\"\n :viewport=\"viewport\"\n />\n </div>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"BlockPreviewCanvas-msj619wT.js","names":[],"sources":["../../../src/components/blocks/PreviewSectionBlock.vue","../../../src/components/blocks/PreviewSectionBlock.vue","../../../src/components/BlockPreviewCanvas.vue","../../../src/components/BlockPreviewCanvas.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport ButtonBlock from \"./ButtonBlock.vue\";\nimport CustomBlock from \"./CustomBlock.vue\";\nimport DividerBlock from \"./DividerBlock.vue\";\nimport HtmlBlock from \"./HtmlBlock.vue\";\nimport ImageBlock from \"./ImageBlock.vue\";\nimport MenuBlock from \"./MenuBlock.vue\";\nimport SocialIconsBlock from \"./SocialIconsBlock.vue\";\nimport SpacerBlock from \"./SpacerBlock.vue\";\nimport TableBlock from \"./TableBlock.vue\";\nimport TitleBlock from \"./TitleBlock.vue\";\nimport ParagraphBlock from \"./ParagraphBlock.vue\";\nimport VideoBlock from \"./VideoBlock.vue\";\nimport {\n resolveBlockComponent,\n getBlockWrapperStyle,\n} from \"../../utils/blockComponentResolver\";\nimport type {\n Block,\n SectionBlock as SectionBlockType,\n} from \"@templatical/types\";\nimport { computed, inject, type Component } from \"vue\";\nimport { BLOCK_REGISTRY_KEY } from \"../../keys\";\n\nconst previewBlockComponentMap: Record<string, Component> = {\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n video: VideoBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n custom: CustomBlock,\n};\n\nconst props = defineProps<{\n block: SectionBlockType;\n viewport: \"desktop\";\n}>();\n\ndefineOptions({ name: \"PreviewSectionBlock\" });\n\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY, null);\n\nconst columnWidths = computed(() => {\n switch (props.block.columns) {\n case \"2\":\n return [\"50%\", \"50%\"];\n case \"3\":\n return [\"33.33%\", \"33.33%\", \"33.33%\"];\n case \"1-2\":\n return [\"33.33%\", \"66.67%\"];\n case \"2-1\":\n return [\"66.67%\", \"33.33%\"];\n default:\n return [\"100%\"];\n }\n});\n\nconst columns = computed(() => {\n const count = columnWidths.value.length;\n const children = [...props.block.children];\n while (children.length < count) {\n children.push([]);\n }\n return children.slice(0, count);\n});\n\nfunction getColumnBlocks(colIndex: number): Block[] {\n return columns.value[colIndex] || [];\n}\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, previewBlockComponentMap);\n}\n</script>\n\n<template>\n <div class=\"tpl:w-full\">\n <div class=\"tpl:flex tpl:gap-0\">\n <div\n v-for=\"(_, colIndex) in columns\"\n :key=\"colIndex\"\n :style=\"{ width: columnWidths[colIndex] }\"\n >\n <div\n v-for=\"childBlock in getColumnBlocks(colIndex)\"\n :key=\"childBlock.id\"\n :style=\"getBlockWrapperStyle(childBlock)\"\n >\n <!-- Recursive self-reference for nested sections -->\n <PreviewSectionBlock\n v-if=\"childBlock.type === 'section'\"\n :block=\"childBlock\"\n viewport=\"desktop\"\n />\n <component\n :is=\"getBlockComponent(childBlock)\"\n v-else\n :block=\"childBlock\"\n viewport=\"desktop\"\n />\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ButtonBlock from \"./ButtonBlock.vue\";\nimport CustomBlock from \"./CustomBlock.vue\";\nimport DividerBlock from \"./DividerBlock.vue\";\nimport HtmlBlock from \"./HtmlBlock.vue\";\nimport ImageBlock from \"./ImageBlock.vue\";\nimport MenuBlock from \"./MenuBlock.vue\";\nimport SocialIconsBlock from \"./SocialIconsBlock.vue\";\nimport SpacerBlock from \"./SpacerBlock.vue\";\nimport TableBlock from \"./TableBlock.vue\";\nimport TitleBlock from \"./TitleBlock.vue\";\nimport ParagraphBlock from \"./ParagraphBlock.vue\";\nimport VideoBlock from \"./VideoBlock.vue\";\nimport {\n resolveBlockComponent,\n getBlockWrapperStyle,\n} from \"../../utils/blockComponentResolver\";\nimport type {\n Block,\n SectionBlock as SectionBlockType,\n} from \"@templatical/types\";\nimport { computed, inject, type Component } from \"vue\";\nimport { BLOCK_REGISTRY_KEY } from \"../../keys\";\n\nconst previewBlockComponentMap: Record<string, Component> = {\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n video: VideoBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n custom: CustomBlock,\n};\n\nconst props = defineProps<{\n block: SectionBlockType;\n viewport: \"desktop\";\n}>();\n\ndefineOptions({ name: \"PreviewSectionBlock\" });\n\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY, null);\n\nconst columnWidths = computed(() => {\n switch (props.block.columns) {\n case \"2\":\n return [\"50%\", \"50%\"];\n case \"3\":\n return [\"33.33%\", \"33.33%\", \"33.33%\"];\n case \"1-2\":\n return [\"33.33%\", \"66.67%\"];\n case \"2-1\":\n return [\"66.67%\", \"33.33%\"];\n default:\n return [\"100%\"];\n }\n});\n\nconst columns = computed(() => {\n const count = columnWidths.value.length;\n const children = [...props.block.children];\n while (children.length < count) {\n children.push([]);\n }\n return children.slice(0, count);\n});\n\nfunction getColumnBlocks(colIndex: number): Block[] {\n return columns.value[colIndex] || [];\n}\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, previewBlockComponentMap);\n}\n</script>\n\n<template>\n <div class=\"tpl:w-full\">\n <div class=\"tpl:flex tpl:gap-0\">\n <div\n v-for=\"(_, colIndex) in columns\"\n :key=\"colIndex\"\n :style=\"{ width: columnWidths[colIndex] }\"\n >\n <div\n v-for=\"childBlock in getColumnBlocks(colIndex)\"\n :key=\"childBlock.id\"\n :style=\"getBlockWrapperStyle(childBlock)\"\n >\n <!-- Recursive self-reference for nested sections -->\n <PreviewSectionBlock\n v-if=\"childBlock.type === 'section'\"\n :block=\"childBlock\"\n viewport=\"desktop\"\n />\n <component\n :is=\"getBlockComponent(childBlock)\"\n v-else\n :block=\"childBlock\"\n viewport=\"desktop\"\n />\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ButtonBlock from \"./blocks/ButtonBlock.vue\";\nimport CustomBlock from \"./blocks/CustomBlock.vue\";\nimport DividerBlock from \"./blocks/DividerBlock.vue\";\nimport HtmlBlock from \"./blocks/HtmlBlock.vue\";\nimport ImageBlock from \"./blocks/ImageBlock.vue\";\nimport MenuBlock from \"./blocks/MenuBlock.vue\";\nimport PreviewSectionBlock from \"./blocks/PreviewSectionBlock.vue\";\nimport SocialIconsBlock from \"./blocks/SocialIconsBlock.vue\";\nimport SpacerBlock from \"./blocks/SpacerBlock.vue\";\nimport TableBlock from \"./blocks/TableBlock.vue\";\nimport TitleBlock from \"./blocks/TitleBlock.vue\";\nimport ParagraphBlock from \"./blocks/ParagraphBlock.vue\";\nimport VideoBlock from \"./blocks/VideoBlock.vue\";\nimport {\n BLOCK_REGISTRY_KEY,\n CONDITION_PREVIEW_KEY,\n EDITOR_KEY,\n MERGE_TAG_SAMPLE_MODE_KEY,\n USE_MERGE_TAG_SAMPLES_KEY,\n} from \"../keys\";\nimport {\n resolveBlockComponent,\n getBlockWrapperStyle,\n getDocumentStyle,\n} from \"../utils/blockComponentResolver\";\nimport {\n EMAIL_FRAME_WIDTH_TRANSITION,\n getEmailFrameWidth,\n} from \"../utils/emailFrameWidth\";\nimport type { Block, ViewportSize } from \"@templatical/types\";\nimport { computed, inject, provide, type Component } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n blocks: Block[];\n /**\n * Which viewport to render. Defaults to `desktop`, so the saved-blocks\n * surfaces — which have no viewport control — behave exactly as before.\n * The test-email dialog passes the user's choice so responsive blocks show\n * the variant a recipient on that device would receive.\n */\n viewport?: ViewportSize;\n /**\n * Whether the hand-toggled display-condition filter still applies here.\n * `false` where a resolver owns the preview: it has already evaluated every\n * condition against real data, so layering a manual hide on top would veto\n * the answer that was asked for.\n *\n * A prop rather than an injected `PREVIEW_RESOLUTION_KEY` read, because the\n * test-email dialog builds its *own* resolution instance (keyed to the\n * selected recipient) — injecting would consult the shared canvas instance\n * and get the wrong answer.\n */\n applyConditionFilter?: boolean;\n }>(),\n { viewport: \"desktop\", applyConditionFilter: true },\n);\n\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY);\nconst editor = inject(EDITOR_KEY, null);\nconst conditionPreview = inject(CONDITION_PREVIEW_KEY, null);\n\n/**\n * This canvas is only ever a preview — nothing here is editable — so it honours\n * the user's mode directly, with no `previewMode` gate of the kind `Canvas.vue`\n * needs. Re-provided rather than left to inherit, because a `BlockPreviewCanvas`\n * inside a dialog may sit under an editing `Canvas` that provides `false`.\n */\nconst mergeTagSampleMode = inject(MERGE_TAG_SAMPLE_MODE_KEY, null);\nprovide(\n USE_MERGE_TAG_SAMPLES_KEY,\n computed(() => mergeTagSampleMode?.value ?? false),\n);\n\n/**\n * Blocks a display condition currently excludes, so a preview never shows\n * content the recipient won't get. The canvas does the same via\n * `v-show=\"!conditionPreview?.isHidden(block.id)\"`.\n *\n * A no-op for the saved-blocks browser: entries there carry their *stored* ids,\n * which aren't in the current template, so nothing is tracked as hidden. It\n * bites where it should — the save dialog and the test-email preview, both of\n * which show blocks that are live on the canvas.\n *\n * Skipped entirely when `applyConditionFilter` is false — see the prop.\n */\nconst visibleBlocks = computed(() =>\n props.applyConditionFilter\n ? props.blocks.filter((block) => !conditionPreview?.isHidden(block.id))\n : props.blocks,\n);\n\n/**\n * Frame width, from the one helper the canvas and the scaled preview rows also\n * use — so all three agree by construction rather than by three constants that\n * happen to match. A template with a custom body width now previews at that\n * width instead of a flat 600.\n */\nconst frameWidth = computed(() =>\n getEmailFrameWidth(editor?.content.value.settings, props.viewport),\n);\n\n/**\n * The same document-level style the canvas applies, so a previewed block\n * renders identically to the canvas one: without it the font falls back to the\n * editor UI's and the link rules hit their unset defaults, dropping the\n * underline and the link colour.\n *\n * Read from the *current* template even in the saved-blocks browser, where the\n * previewed content came from elsewhere — a saved block stores only `Block[]`,\n * and the current settings are what it will actually look like once inserted.\n */\nconst documentStyle = computed(() =>\n editor ? getDocumentStyle(editor.content.value.settings) : {},\n);\n\nconst previewComponentMap: Record<string, Component> = {\n section: PreviewSectionBlock,\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n video: VideoBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n custom: CustomBlock,\n};\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, previewComponentMap);\n}\n</script>\n\n<template>\n <div\n data-testid=\"block-preview-canvas\"\n class=\"tpl:pointer-events-none tpl:mx-auto tpl:select-none tpl:rounded-lg\"\n :style=\"{\n width: `${frameWidth}px`,\n transition: EMAIL_FRAME_WIDTH_TRANSITION,\n backgroundColor: 'var(--tpl-canvas-bg)',\n boxShadow: 'var(--tpl-shadow-sm)',\n ...documentStyle,\n }\"\n >\n <div\n v-for=\"block in visibleBlocks\"\n :key=\"block.id\"\n :style=\"getBlockWrapperStyle(block)\"\n >\n <component\n :is=\"getBlockComponent(block)\"\n :block=\"block\"\n :viewport=\"viewport\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ButtonBlock from \"./blocks/ButtonBlock.vue\";\nimport CustomBlock from \"./blocks/CustomBlock.vue\";\nimport DividerBlock from \"./blocks/DividerBlock.vue\";\nimport HtmlBlock from \"./blocks/HtmlBlock.vue\";\nimport ImageBlock from \"./blocks/ImageBlock.vue\";\nimport MenuBlock from \"./blocks/MenuBlock.vue\";\nimport PreviewSectionBlock from \"./blocks/PreviewSectionBlock.vue\";\nimport SocialIconsBlock from \"./blocks/SocialIconsBlock.vue\";\nimport SpacerBlock from \"./blocks/SpacerBlock.vue\";\nimport TableBlock from \"./blocks/TableBlock.vue\";\nimport TitleBlock from \"./blocks/TitleBlock.vue\";\nimport ParagraphBlock from \"./blocks/ParagraphBlock.vue\";\nimport VideoBlock from \"./blocks/VideoBlock.vue\";\nimport {\n BLOCK_REGISTRY_KEY,\n CONDITION_PREVIEW_KEY,\n EDITOR_KEY,\n MERGE_TAG_SAMPLE_MODE_KEY,\n USE_MERGE_TAG_SAMPLES_KEY,\n} from \"../keys\";\nimport {\n resolveBlockComponent,\n getBlockWrapperStyle,\n getDocumentStyle,\n} from \"../utils/blockComponentResolver\";\nimport {\n EMAIL_FRAME_WIDTH_TRANSITION,\n getEmailFrameWidth,\n} from \"../utils/emailFrameWidth\";\nimport type { Block, ViewportSize } from \"@templatical/types\";\nimport { computed, inject, provide, type Component } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n blocks: Block[];\n /**\n * Which viewport to render. Defaults to `desktop`, so the saved-blocks\n * surfaces — which have no viewport control — behave exactly as before.\n * The test-email dialog passes the user's choice so responsive blocks show\n * the variant a recipient on that device would receive.\n */\n viewport?: ViewportSize;\n /**\n * Whether the hand-toggled display-condition filter still applies here.\n * `false` where a resolver owns the preview: it has already evaluated every\n * condition against real data, so layering a manual hide on top would veto\n * the answer that was asked for.\n *\n * A prop rather than an injected `PREVIEW_RESOLUTION_KEY` read, because the\n * test-email dialog builds its *own* resolution instance (keyed to the\n * selected recipient) — injecting would consult the shared canvas instance\n * and get the wrong answer.\n */\n applyConditionFilter?: boolean;\n }>(),\n { viewport: \"desktop\", applyConditionFilter: true },\n);\n\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY);\nconst editor = inject(EDITOR_KEY, null);\nconst conditionPreview = inject(CONDITION_PREVIEW_KEY, null);\n\n/**\n * This canvas is only ever a preview — nothing here is editable — so it honours\n * the user's mode directly, with no `previewMode` gate of the kind `Canvas.vue`\n * needs. Re-provided rather than left to inherit, because a `BlockPreviewCanvas`\n * inside a dialog may sit under an editing `Canvas` that provides `false`.\n */\nconst mergeTagSampleMode = inject(MERGE_TAG_SAMPLE_MODE_KEY, null);\nprovide(\n USE_MERGE_TAG_SAMPLES_KEY,\n computed(() => mergeTagSampleMode?.value ?? false),\n);\n\n/**\n * Blocks a display condition currently excludes, so a preview never shows\n * content the recipient won't get. The canvas does the same via\n * `v-show=\"!conditionPreview?.isHidden(block.id)\"`.\n *\n * A no-op for the saved-blocks browser: entries there carry their *stored* ids,\n * which aren't in the current template, so nothing is tracked as hidden. It\n * bites where it should — the save dialog and the test-email preview, both of\n * which show blocks that are live on the canvas.\n *\n * Skipped entirely when `applyConditionFilter` is false — see the prop.\n */\nconst visibleBlocks = computed(() =>\n props.applyConditionFilter\n ? props.blocks.filter((block) => !conditionPreview?.isHidden(block.id))\n : props.blocks,\n);\n\n/**\n * Frame width, from the one helper the canvas and the scaled preview rows also\n * use — so all three agree by construction rather than by three constants that\n * happen to match. A template with a custom body width now previews at that\n * width instead of a flat 600.\n */\nconst frameWidth = computed(() =>\n getEmailFrameWidth(editor?.content.value.settings, props.viewport),\n);\n\n/**\n * The same document-level style the canvas applies, so a previewed block\n * renders identically to the canvas one: without it the font falls back to the\n * editor UI's and the link rules hit their unset defaults, dropping the\n * underline and the link colour.\n *\n * Read from the *current* template even in the saved-blocks browser, where the\n * previewed content came from elsewhere — a saved block stores only `Block[]`,\n * and the current settings are what it will actually look like once inserted.\n */\nconst documentStyle = computed(() =>\n editor ? getDocumentStyle(editor.content.value.settings) : {},\n);\n\nconst previewComponentMap: Record<string, Component> = {\n section: PreviewSectionBlock,\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n video: VideoBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n custom: CustomBlock,\n};\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, previewComponentMap);\n}\n</script>\n\n<template>\n <div\n data-testid=\"block-preview-canvas\"\n class=\"tpl:pointer-events-none tpl:mx-auto tpl:select-none tpl:rounded-lg\"\n :style=\"{\n width: `${frameWidth}px`,\n transition: EMAIL_FRAME_WIDTH_TRANSITION,\n backgroundColor: 'var(--tpl-canvas-bg)',\n boxShadow: 'var(--tpl-shadow-sm)',\n ...documentStyle,\n }\"\n >\n <div\n v-for=\"block in visibleBlocks\"\n :key=\"block.id\"\n :style=\"getBlockWrapperStyle(block)\"\n >\n <component\n :is=\"getBlockComponent(block)\"\n :block=\"block\"\n :viewport=\"viewport\"\n />\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { $ as e, A as t, H as n, J as r, M as i, N as a, P as o, V as s, Z as c, _ as l, b as u, ct as d, f, g as p, h as m, it as h, j as g, l as _, lt as v, m as y, n as b, p as x, s as S, st as C, u as w, v as T, x as E, y as D, z as O } from "./draggable-DCs9qL-b.js";
2
2
  import { t as k } from "./timeouts-SsLMC4a3.js";
3
3
  import { H as A } from "./src-B_QJduQD.js";
4
- import { T as j, b as M, j as ee, t as N } from "./useEditorCore-CKQ9gmoo.js";
4
+ import { T as j, b as M, j as ee, t as N } from "./useEditorCore-CcgxY-Gy.js";
5
5
  import { _ as P } from "./dist-C6z8AjBC.js";
6
6
  import { t as F } from "./dist-B2HKGHnc.js";
7
7
  import { M as I, c as te, d as L, i as R, l as z, t as B, z as ne } from "./keys-Cmc2Yl3D.js";
@@ -9,7 +9,7 @@ import { t as V } from "./useI18n-UMs0F71x.js";
9
9
  import { E as H, F as U, I as W, Lt as G, M as K, Mt as q, it as J, kt as Y, tt as re, u as ie } from "./icons-CvYN6zrM.js";
10
10
  import { t as ae } from "./_plugin-vue_export-helper-B3ysoDQm.js";
11
11
  import { n as X } from "./useCloudI18n-8wkJtuJD.js";
12
- import { a as Z, c as oe, d as Q, f as se, i as ce, l as le, m as ue, n as de, o as fe, p as pe, r as me, s as he, t as ge, u as _e } from "./styles-CND3ey2g.js";
12
+ import { a as Z, c as oe, d as Q, f as se, i as ce, l as le, m as ue, n as de, o as fe, p as pe, r as me, s as he, t as ge, u as _e } from "./styles-DLAfU4Uf.js";
13
13
  import { d as $ } from "./ColorPicker-W_UyiVGc.js";
14
14
  import { n as ve, t as ye } from "./MergeTagModeToggle-DQ79SqQc.js";
15
15
  import { C as be, a as xe, c as Se, d as Ce, f as we, g as Te, h as Ee, i as De, l as Oe, n as ke, o as Ae, p as je, r as Me, s as Ne, t as Pe, u as Fe, v as Ie, y as Le } from "./cloud-DcC8bPwn.js";
@@ -613,7 +613,7 @@ var qe = ["aria-label"], Je = { class: "tpl:flex tpl:max-h-[80vh] tpl:w-full tpl
613
613
  },
614
614
  emits: ["save"],
615
615
  setup(e) {
616
- let t = u(() => import("./CollaboratorBar-BF469_AN.js")), n = u(() => import("./SnapshotHistory-80tAU_VX.js")), r = u(() => import("./AiFeatureMenu-Cwj8afsi.js")), { t: a } = V(), { t: o, format: c } = X();
616
+ let t = u(() => import("./CollaboratorBar-G_sZDZXw.js")), n = u(() => import("./SnapshotHistory-DFinMrg_.js")), r = u(() => import("./AiFeatureMenu-Cb9gaRBn.js")), { t: a } = V(), { t: o, format: c } = X();
617
617
  return (l, u) => (i(), p("header", rt, [
618
618
  x("div", it, [e.featureFlags.templateLimit.value === null ? m("", !0) : (i(), p("span", at, v(h(c)(h(o).header.templatesUsed, {
619
619
  used: e.featureFlags.templateCount.value,
@@ -915,7 +915,7 @@ var qe = ["aria-label"], Je = { class: "tpl:flex tpl:max-h-[80vh] tpl:w-full tpl
915
915
  },
916
916
  emits: ["ready"],
917
917
  setup(e, { expose: r, emit: o }) {
918
- let l = u(() => import("./SavedBlocksPanels-e4HyTZ8v.js")), f = u(() => import("./TestEmailPanel-CZAG1gQE.js")), w = e;
918
+ let l = u(() => import("./SavedBlocksPanels-CUtYoi5Z.js")), f = u(() => import("./TestEmailPanel-B8y7JabA.js")), w = e;
919
919
  a(z, w.cloudTranslations);
920
920
  let E = o, O = c(null), k = c(null), A = Ue({
921
921
  config: w.config,
@@ -1163,4 +1163,4 @@ var qe = ["aria-label"], Je = { class: "tpl:flex tpl:max-h-[80vh] tpl:w-full tpl
1163
1163
  //#endregion
1164
1164
  export { zt as default };
1165
1165
 
1166
- //# sourceMappingURL=CloudEditor-CQ830jJt.js.map
1166
+ //# sourceMappingURL=CloudEditor-DbyEsIxu.js.map