@templatical/editor
Advanced tools
| 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 _, m as v, n as y, ot 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-D840DIZB.js"; | ||
| import { t as k } from "./timeouts-Bmi_yePw.js"; | ||
| import { F as A } from "./src-BKI1x1k7.js"; | ||
| import { T as j, b as M, j as N, t as P } from "./useEditorCore-BA6mspzD.js"; | ||
| import { _ as F } from "./dist-CiBEt0VR.js"; | ||
| import { t as I } from "./dist-CxTt8FRE.js"; | ||
| import { A as L, F as R, c as z, r as B, s as V, t as H, u as ee } from "./keys-D7kUmfpE.js"; | ||
| import { t as U } from "./useI18n-8Wd7G-cq.js"; | ||
| import { At as W, Dt as G, F as te, Ft as K, P as q, et as J, j as ne, rt as Y, u as re, w as ie } from "./icons-CYWw8Xfp.js"; | ||
| import { t as ae } from "./_plugin-vue_export-helper-B3ysoDQm.js"; | ||
| import { n as X } from "./useCloudI18n-BjNENqkM.js"; | ||
| import { a as Z, c as oe, d as se, f as Q, i as ce, l as le, n as ue, o as de, p as fe, r as pe, s as me, t as he, u as ge } from "./styles-DMOEiSX9.js"; | ||
| import { d as $ } from "./ColorPicker-DZSzZ5ZP.js"; | ||
| import { S as _e, _ as ve, a as ye, c as be, d as xe, f as Se, h as Ce, i as we, l as Te, m as Ee, n as De, o as Oe, r as ke, s as Ae, t as je, u as Me, v as Ne } from "./cloud-BJlbXGTJ.js"; | ||
| //#region src/cloud/composables/useSnapshotPreview.ts | ||
| function Pe(t) { | ||
| let { authManager: n, editor: i, history: a, conditionPreview: o, autoSave: s, onError: l } = t, u = e(null), d = c(null), p = c(null), m = !1; | ||
| r(() => { | ||
| m = !0; | ||
| }); | ||
| let h = f(() => d.value !== null), g = f(() => u.value?.snapshots.value ?? []), _ = f(() => u.value?.isLoading.value ?? !1), v = f(() => u.value?.isRestoring.value ?? !1); | ||
| function y() { | ||
| i.state.template?.id && !u.value && (u.value = Oe({ | ||
| authManager: n, | ||
| templateId: i.state.template.id, | ||
| onRestore: b, | ||
| onError: l | ||
| }), u.value.loadSnapshots()); | ||
| } | ||
| function b(e) { | ||
| i.setContent(e.content, !1), a.clear(), o.reset(); | ||
| } | ||
| async function x(e) { | ||
| if (!m) { | ||
| if (d.value) { | ||
| d.value = e, i.setContent(e.content, !1); | ||
| return; | ||
| } | ||
| i.state.isDirty && i.hasTemplate() && (await i.createSnapshot(), m) || (p.value = structuredClone(i.content.value), s?.pause(), d.value = e, i.setContent(e.content, !1)); | ||
| } | ||
| } | ||
| async function S() { | ||
| if (!(!d.value || !u.value)) try { | ||
| if (await u.value.restoreSnapshot(d.value.id), m || (await u.value.loadSnapshots(), m)) return; | ||
| } catch (e) { | ||
| throw !m && p.value && i.setContent(p.value, !1), e; | ||
| } finally { | ||
| m || (d.value = null, p.value = null, s?.resume()); | ||
| } | ||
| } | ||
| function C() { | ||
| !d.value || !p.value || (i.setContent(p.value, !1), d.value = null, p.value = null, s?.resume()); | ||
| } | ||
| async function w() { | ||
| m || u.value && await u.value.loadSnapshots(); | ||
| } | ||
| return { | ||
| snapshotHistoryInstance: u, | ||
| previewingSnapshot: d, | ||
| contentBeforePreview: p, | ||
| isPreviewingSnapshot: h, | ||
| snapshotHistorySnapshots: g, | ||
| snapshotHistoryIsLoading: _, | ||
| snapshotHistoryIsRestoring: v, | ||
| initSnapshotHistory: y, | ||
| handleRestore: b, | ||
| handleSnapshotNavigate: x, | ||
| confirmRestoreSnapshot: S, | ||
| cancelPreview: C, | ||
| loadSnapshotHistory: w | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudPanelState.ts | ||
| function Fe() { | ||
| let e = c(null), t = f({ | ||
| get: () => e.value === "ai-chat", | ||
| set: (t) => e.value = t ? "ai-chat" : null | ||
| }), n = f({ | ||
| get: () => e.value === "scoring", | ||
| set: (t) => e.value = t ? "scoring" : null | ||
| }), r = f({ | ||
| get: () => e.value === "design-reference", | ||
| set: (t) => e.value = t ? "design-reference" : null | ||
| }), i = f({ | ||
| get: () => e.value === "comments", | ||
| set: (t) => e.value = t ? "comments" : null | ||
| }), a = c(!1), o = c(!1), s = c(void 0), l = c(!1), u = c(null), d = f(() => e.value !== null), p = f(() => { | ||
| let t = e.value; | ||
| return t === "ai-chat" || t === "design-reference" || t === "scoring" ? t : null; | ||
| }), m = f(() => l.value || e.value === "ai-chat" || e.value === "design-reference" || e.value === "scoring"); | ||
| function h() { | ||
| l.value = !l.value; | ||
| } | ||
| function g(t) { | ||
| l.value = !1, e.value = e.value === t ? null : t; | ||
| } | ||
| return I(u, () => { | ||
| l.value = !1; | ||
| }), { | ||
| activePanel: e, | ||
| aiChatOpen: t, | ||
| scoringPanelOpen: n, | ||
| designReferenceOpen: r, | ||
| commentsOpen: i, | ||
| testEmailModalOpen: a, | ||
| mediaLibraryOpen: o, | ||
| mediaLibraryAccept: s, | ||
| aiMenuOpen: l, | ||
| aiMenuRef: u, | ||
| rightPanelOpen: d, | ||
| activeAiFeature: p, | ||
| aiButtonActive: m, | ||
| toggleAiMenu: h, | ||
| handleAiFeatureSelect: g | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCollabUndoWarning.ts | ||
| function Ie(e) { | ||
| let { isCollaborationEnabled: t, getCollaboratorCount: n, canUndo: r } = e, i = c(!1), a = c(!1), { start: o } = F(() => { | ||
| a.value = !1; | ||
| }, k, { immediate: !1 }); | ||
| function s() { | ||
| i.value || !t.value || n() === 0 || !r.value || (i.value = !0, a.value = !0, o()); | ||
| } | ||
| return { | ||
| collabUndoWarningVisible: a, | ||
| showCollabUndoWarning: s | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudFeatureFlags.ts | ||
| function Le(e) { | ||
| let { planConfigInstance: t, aiConfig: n, editor: r } = e, i = f(() => t.hasFeature("ai_generation") && n.hasAnyMenuFeature.value), a = f(() => t.hasFeature("test_email")), o = f(() => !!r.state.template?.id), s = f(() => t.hasFeature("white_label")), l = f(() => t.config.value?.limits.max_templates ?? null), u = f(() => t.config.value?.template_count ?? 0), d = c(!1), p = c("idle"), m = c(""), { start: h } = F(() => { | ||
| p.value = "idle"; | ||
| }, 3e3, { immediate: !1 }); | ||
| return { | ||
| canUseAiGeneration: i, | ||
| canSendTestEmail: a, | ||
| hasTemplateSaved: o, | ||
| isWhiteLabeled: s, | ||
| templateLimit: l, | ||
| templateCount: u, | ||
| isSaveExporting: d, | ||
| saveStatus: p, | ||
| saveErrorMessage: m, | ||
| startSaveStatusClear: h | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudMediaLibrary.ts | ||
| function Re(e) { | ||
| let { onRequestMedia: t, mediaLibraryOpen: n, mediaLibraryAccept: i, authManager: a, getMediaConfig: o, onError: s } = e, c = null; | ||
| function l(e) { | ||
| return { | ||
| url: e.url, | ||
| alt: e.alt_text || void 0 | ||
| }; | ||
| } | ||
| async function u(e) { | ||
| let r = e?.accept ?? ["images"]; | ||
| if (e?.files?.length) { | ||
| if (t) { | ||
| let n = await t({ | ||
| accept: r, | ||
| files: e.files | ||
| }); | ||
| return n ? l(n) : null; | ||
| } | ||
| return d(e.files[0]); | ||
| } | ||
| if (t) { | ||
| let e = await t({ accept: r }); | ||
| return e ? l(e) : null; | ||
| } | ||
| return c &&= (c(null), null), i.value = r, n.value = !0, new Promise((e) => { | ||
| c = (t) => { | ||
| e(t); | ||
| }; | ||
| }); | ||
| } | ||
| async function d(e) { | ||
| let t = f(e); | ||
| if (t) return s?.(t), null; | ||
| try { | ||
| let { MediaApiClient: t } = await import("./src-D6F_3XkE.js"); | ||
| return l(await new t(a).uploadMedia(e)); | ||
| } catch (e) { | ||
| return s?.(e), null; | ||
| } | ||
| } | ||
| function f(e) { | ||
| let t = o?.() ?? null; | ||
| if (!t) return null; | ||
| let n = t.categories?.images?.mime_types ?? []; | ||
| return n.length > 0 && !n.includes(e.type) ? /* @__PURE__ */ Error(`Unsupported image type: ${e.type || "unknown"}. Accepted: ${n.join(", ")}.`) : t.max_file_size > 0 && e.size > t.max_file_size ? /* @__PURE__ */ Error(`Image is too large (${e.size} bytes). Maximum allowed is ${t.max_file_size} bytes.`) : null; | ||
| } | ||
| function p(e) { | ||
| n.value = !1, c?.(l(e)), c = null; | ||
| } | ||
| function m() { | ||
| n.value = !1, c?.(null), c = null; | ||
| } | ||
| return r(() => { | ||
| c &&= (c(null), null); | ||
| }), { | ||
| handleRequestMedia: u, | ||
| handleMediaSelect: p, | ||
| handleMediaLibraryClose: m | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudInitialization.ts | ||
| function ze(e) { | ||
| let { config: t, translations: n, fontsManager: r, emit: i, getCommentsSidebar: o } = e, s = c(!0), l = c(!1), u = c(null), d = !1, p = { value: null }, m = null, h = null, g = new _e({ | ||
| ...t.auth, | ||
| onError: t.onError | ||
| }), _ = ke({ | ||
| authManager: g, | ||
| onError: t.onError | ||
| }), v = c(/* @__PURE__ */ new Map()), y = ve({ | ||
| authManager: g, | ||
| defaultFontFamily: t.fonts?.defaultFont, | ||
| templateDefaults: t.templateDefaults, | ||
| onError: t.onError, | ||
| lockedBlocks: v | ||
| }), b = be({ | ||
| authManager: g, | ||
| onError: t.onError | ||
| }); | ||
| t.mcp?.enabled && je({ | ||
| editor: y, | ||
| channel: b.channel, | ||
| onOperation: t.mcp.onOperation | ||
| }); | ||
| let x = null; | ||
| t.collaboration?.enabled && (x = Me({ | ||
| authManager: g, | ||
| editor: y, | ||
| channel: b.channel, | ||
| onError: t.onError, | ||
| onCollaboratorJoined: t.collaboration.onCollaboratorJoined, | ||
| onCollaboratorLeft: t.collaboration.onCollaboratorLeft, | ||
| onBlockLocked: t.collaboration.onBlockLocked, | ||
| onBlockUnlocked: t.collaboration.onBlockUnlocked | ||
| }), O(() => x.lockedBlocks.value, (e) => { | ||
| v.value = e; | ||
| }, { immediate: !0 }), Te(y, x)); | ||
| let S = f(() => !!t.collaboration?.enabled && _.hasFeature("collaboration")), C = P({ | ||
| editor: y, | ||
| config: { | ||
| uiTheme: t.uiTheme, | ||
| theme: void 0, | ||
| blockDefaults: t.blockDefaults, | ||
| templateDefaults: t.templateDefaults, | ||
| customBlocks: [], | ||
| paletteBlocks: t.paletteBlocks, | ||
| htmlBlockPreview: t.htmlBlockPreview, | ||
| colors: t.colors, | ||
| mergeTags: t.mergeTags, | ||
| logicTags: t.logicTags, | ||
| displayConditions: t.displayConditions, | ||
| onRequestMedia: null, | ||
| lint: Q(t), | ||
| onSave: () => { | ||
| p.value?.().catch((e) => { | ||
| t.onError?.(e); | ||
| }); | ||
| } | ||
| }, | ||
| translations: n, | ||
| fontsManager: r, | ||
| historyOptions: x ? { isRemoteOperation: () => x._isProcessingRemoteOperation() } : void 0, | ||
| autoSaveOptions: { | ||
| onChange: async () => { | ||
| y.hasTemplate() && (await y.createSnapshot(), m?.snapshotHistoryInstance.value?.loadSnapshots()); | ||
| }, | ||
| debounce: t.autoSaveDebounce ?? 5e3, | ||
| enabled: () => t.autoSave !== !1 && _.hasFeature("auto_save") | ||
| }, | ||
| themeExtraStyles: () => ({ "--tpl-drop-text": `"${n.canvas.dropHere}"` }), | ||
| keyboardOptions: { onBeforeUndo: () => h?.showCollabUndoWarning() }, | ||
| editorRoot: e.editorRoot, | ||
| containerEl: e.containerEl | ||
| }), w = Ie({ | ||
| isCollaborationEnabled: S, | ||
| getCollaboratorCount: () => x?.collaborators.value.length ?? 0, | ||
| canUndo: C.history.canUndo | ||
| }); | ||
| h = w; | ||
| let T = Pe({ | ||
| authManager: g, | ||
| editor: y, | ||
| history: C.history, | ||
| conditionPreview: C.conditionPreview, | ||
| autoSave: C.autoSave, | ||
| onError: t.onError | ||
| }); | ||
| m = T; | ||
| let E = Fe(), D = Ce(t.ai), k = Le({ | ||
| planConfigInstance: _, | ||
| aiConfig: D, | ||
| editor: y | ||
| }), A = Re({ | ||
| onRequestMedia: t.onRequestMedia, | ||
| mediaLibraryOpen: E.mediaLibraryOpen, | ||
| mediaLibraryAccept: E.mediaLibraryAccept, | ||
| authManager: g, | ||
| getMediaConfig: () => _.config.value?.media ?? null, | ||
| onError: t.onError | ||
| }); | ||
| j({ | ||
| onBlockMove: y.moveBlock, | ||
| onBlockAdd: y.addBlock | ||
| }); | ||
| let M = we({ | ||
| authManager: g, | ||
| getFontsConfig: () => t.fonts, | ||
| canUseCustomFonts: () => _.hasFeature("custom_fonts") | ||
| }), F = ye({ | ||
| authManager: g, | ||
| getTemplateId: () => y.state.template?.id ?? null, | ||
| save: () => y.save(), | ||
| exportHtml: (e) => M.exportHtml(e), | ||
| onError: t.onError, | ||
| isAuthReady: l, | ||
| onBeforeTestEmail: t.onBeforeTestEmail | ||
| }), I = Se({ | ||
| authManager: g, | ||
| getTemplateId: () => y.state.template?.id ?? null, | ||
| getSocketId: () => b.getSocketId(), | ||
| onComment: t.onComment, | ||
| onError: t.onError, | ||
| isAuthReady: l, | ||
| hasCommentingFeature: () => t.commenting !== !1 && _.hasFeature("commenting") | ||
| }); | ||
| xe({ | ||
| comments: I, | ||
| channel: b.channel | ||
| }); | ||
| let z = typeof t.savedBlocks == "object" && t.savedBlocks !== null ? t.savedBlocks : null, U = fe({ | ||
| provider: z ?? Ae(g), | ||
| editor: y, | ||
| onError: t.onError, | ||
| isAvailable: z ? () => !0 : () => t.savedBlocks !== !1 && _.hasFeature("saved_modules") | ||
| }), W = Ee({ | ||
| authManager: g, | ||
| getTemplateId: () => y.state.template?.id ?? null | ||
| }); | ||
| function G(e) { | ||
| E.commentsOpen.value = !0, queueMicrotask(() => { | ||
| o()?.filterByBlock(e); | ||
| }); | ||
| } | ||
| a(L, A.handleRequestMedia), a(B, g), a(H, D), a(ee, I), a(R, W), a(V, { | ||
| plan: _, | ||
| ai: D, | ||
| comments: { | ||
| getBlockCount: (e) => I.commentCountByBlock.value.get(e) ?? 0, | ||
| openForBlock: G | ||
| }, | ||
| savedBlocks: U.capability | ||
| }); | ||
| function te(e) { | ||
| _.hasFeature("theme_customization") && (C.themeOverrides.value = e); | ||
| } | ||
| function K(e) { | ||
| y.setUiTheme(e); | ||
| } | ||
| async function q() { | ||
| s.value = !0, u.value = null; | ||
| try { | ||
| if (await g.initialize(), d) return; | ||
| l.value = !0; | ||
| let e = await De({ authManager: g }); | ||
| if (d) return; | ||
| if (!e.api.ok) throw Error("Health check failed: API is not reachable"); | ||
| if (!e.auth.ok) throw Error(`Health check failed: authentication error${e.auth.error ? ` - ${e.auth.error}` : ""}`); | ||
| if (e.websocket.ok || N.warn("WebSocket health check failed:", e.websocket.error ?? "unknown error", "-- real-time features will be disabled."), await _.fetchConfig(), d) return; | ||
| r.setCustomFontsEnabled(_.hasFeature("custom_fonts")), t.customBlocks?.length && _.hasFeature("custom_blocks") && C.registerCustomBlocks(t.customBlocks), t.theme && _.hasFeature("theme_customization") && (C.themeOverrides.value = t.theme), i("ready"); | ||
| } catch (e) { | ||
| if (d) return; | ||
| let n = e instanceof Error ? e : Error("Initialization failed", { cause: e }); | ||
| u.value = n, t.onError?.(n); | ||
| } finally { | ||
| d || (s.value = !1); | ||
| } | ||
| } | ||
| function J() { | ||
| d = !0, r.cleanupFontLinks(), b.disconnect(), C.destroy(), t.onUnmount?.(); | ||
| } | ||
| return { | ||
| isInitializing: s, | ||
| isAuthReady: l, | ||
| initError: u, | ||
| isDestroyed: () => d, | ||
| authManager: g, | ||
| planConfigInstance: _, | ||
| websocket: b, | ||
| collaboration: x, | ||
| isCollaborationEnabled: S, | ||
| editor: y, | ||
| core: C, | ||
| aiConfig: D, | ||
| featureFlags: k, | ||
| mediaLib: A, | ||
| exporter: M, | ||
| testEmail: F, | ||
| commentsInstance: I, | ||
| savedBlocks: U, | ||
| scoringInstance: W, | ||
| panelState: E, | ||
| snapshotPreview: T, | ||
| collabWarning: w, | ||
| onSaveHook: p, | ||
| initialize: q, | ||
| destroy: J, | ||
| setThemeOverrides: te, | ||
| setUiTheme: K, | ||
| openCommentsForBlock: G | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/utils/preRenderCustomBlocks.ts | ||
| async function Be(e, t) { | ||
| let n = async (e) => { | ||
| if (A(e)) { | ||
| let n = e; | ||
| try { | ||
| n.renderedHtml = await t.renderCustomBlock(n); | ||
| } catch { | ||
| n.renderedHtml = `<!-- Custom block render error: ${n.customType} -->`; | ||
| } | ||
| } | ||
| if (e.type === "section" && "children" in e) { | ||
| let t = e; | ||
| for (let e of t.children) for (let t of e) await n(t); | ||
| } | ||
| }; | ||
| for (let t of e.blocks) await n(t); | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudLifecycle.ts | ||
| function Ve(e) { | ||
| let { config: t, editor: n, websocket: r, planConfigInstance: i, snapshotPreview: a, core: o, exporter: s, featureFlags: c, isDestroyed: l } = e; | ||
| function u() { | ||
| return Ne(i.config.value.websocket); | ||
| } | ||
| async function d(e) { | ||
| let i = await n.create(e); | ||
| return l() ? i : (t.onCreate?.(i), a.initSnapshotHistory(), r.connect(i.id, u()), i); | ||
| } | ||
| async function f(e) { | ||
| let i = await n.load(e); | ||
| return l() ? i : (t.onLoad?.(i), a.initSnapshotHistory(), r.connect(i.id, u()), i); | ||
| } | ||
| async function p() { | ||
| c.isSaveExporting.value = !0, c.saveStatus.value = "idle"; | ||
| try { | ||
| if (await Be(n.content.value, o.registry), l()) throw Error("Component unmounted during save"); | ||
| let e = await n.save(); | ||
| if (l()) throw Error("Component unmounted during save"); | ||
| a.initSnapshotHistory(), a.snapshotHistoryInstance.value?.loadSnapshots(); | ||
| let r = await s.exportHtml(e.id); | ||
| if (l()) throw Error("Component unmounted during save"); | ||
| let i = { | ||
| templateId: e.id, | ||
| html: r.html, | ||
| mjml: r.mjml, | ||
| content: e.content | ||
| }; | ||
| return t.onSave?.(i), c.saveStatus.value = "saved", c.startSaveStatusClear(), i; | ||
| } catch (e) { | ||
| throw l() || (c.saveStatus.value = "error", c.saveErrorMessage.value = e instanceof Error ? e.message : "Save failed"), e; | ||
| } finally { | ||
| l() || (c.isSaveExporting.value = !1); | ||
| } | ||
| } | ||
| return { | ||
| createTemplate: d, | ||
| loadTemplate: f, | ||
| saveTemplate: p | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudSaveGate.ts | ||
| function He(e) { | ||
| let t = c(!1), n = null, r = f(() => e.planConfig.value?.accessibility?.blockOnError === !0), i = f(() => r.value ? e.issues.value.filter((e) => e.severity === "error") : []), a = f(() => i.value.length > 0); | ||
| async function o(e) { | ||
| return a.value ? (n = e, t.value = !0, !1) : (await e(), !0); | ||
| } | ||
| async function s() { | ||
| let e = n; | ||
| n = null, t.value = !1, e && await e(); | ||
| } | ||
| function l() { | ||
| n = null, t.value = !1; | ||
| } | ||
| return { | ||
| shouldBlock: a, | ||
| blockingIssues: i, | ||
| modalOpen: t, | ||
| tryRunSave: o, | ||
| confirmAndSave: s, | ||
| cancel: l | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/components/CloudSaveGateModal.vue?vue&type=script&setup=true&lang.ts | ||
| var Ue = ["aria-label"], We = { class: "tpl:flex tpl:max-h-[80vh] tpl:w-full tpl:max-w-md tpl:flex-col tpl:gap-4 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:p-5 tpl:shadow-[var(--tpl-shadow-md)]" }, Ge = { class: "tpl:flex tpl:items-center tpl:gap-2" }, Ke = { class: "tpl:m-0 tpl:text-base tpl:font-semibold tpl:text-[var(--tpl-text)]" }, qe = { class: "tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-muted)]" }, Je = { class: "tpl:m-0 tpl:flex tpl:max-h-64 tpl:list-none tpl:flex-col tpl:gap-1.5 tpl:overflow-y-auto tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-2" }, Ye = { class: "tpl:text-xs tpl:text-[var(--tpl-text)]" }, Xe = { class: "tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]" }, Ze = { class: "tpl:flex tpl:justify-end tpl:gap-2" }, Qe = /* @__PURE__ */ E({ | ||
| __name: "CloudSaveGateModal", | ||
| props: { | ||
| open: { type: Boolean }, | ||
| issues: {} | ||
| }, | ||
| emits: ["cancel", "confirm"], | ||
| setup(e, { emit: t }) { | ||
| let n = t, { t: r } = X(); | ||
| return (t, a) => (i(), v(y, { | ||
| "enter-active-class": "tpl:transition-opacity tpl:duration-150", | ||
| "leave-active-class": "tpl:transition-opacity tpl:duration-150", | ||
| "enter-from-class": "tpl:opacity-0", | ||
| "leave-to-class": "tpl:opacity-0" | ||
| }, { | ||
| default: s(() => [e.open ? (i(), p("div", { | ||
| key: 0, | ||
| role: "dialog", | ||
| "aria-modal": "true", | ||
| "aria-label": h(r).saveGate.title, | ||
| class: "tpl:fixed tpl:inset-0 tpl:z-50 tpl:flex tpl:items-center tpl:justify-center tpl:bg-black/40 tpl:p-6", | ||
| onClick: a[2] ||= _((e) => n("cancel"), ["self"]) | ||
| }, [x("div", We, [ | ||
| x("header", Ge, [D(h(re), { | ||
| size: 18, | ||
| "stroke-width": 2, | ||
| class: "tpl:text-[var(--tpl-warning)]" | ||
| }), x("h2", Ke, d(h(r).saveGate.title), 1)]), | ||
| x("p", qe, d(h(r).saveGate.body), 1), | ||
| x("ul", Je, [(i(!0), p(w, null, o(e.issues, (e) => (i(), p("li", { | ||
| key: `${e.ruleId}-${e.blockId ?? "template"}`, | ||
| class: "tpl:flex tpl:flex-col tpl:gap-0.5 tpl:rounded tpl:px-2 tpl:py-1.5" | ||
| }, [x("span", Ye, d(e.message), 1), x("span", Xe, d(e.ruleId), 1)]))), 128))]), | ||
| x("footer", Ze, [x("button", { | ||
| type: "button", | ||
| class: "tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]", | ||
| onClick: a[0] ||= (e) => n("cancel") | ||
| }, d(h(r).saveGate.cancel), 1), x("button", { | ||
| type: "button", | ||
| class: "tpl:rounded-md tpl:bg-[var(--tpl-danger)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-white", | ||
| onClick: a[1] ||= (e) => n("confirm") | ||
| }, d(h(r).saveGate.confirm), 1)]) | ||
| ])], 8, Ue)) : m("", !0)]), | ||
| _: 1 | ||
| })); | ||
| } | ||
| }), $e = { | ||
| class: "tpl-header tpl:absolute tpl:top-0 tpl:right-0 tpl:left-0 tpl:z-50 tpl:grid tpl:h-14 tpl:grid-cols-[1fr_auto_1fr] tpl:items-center tpl:px-4", | ||
| style: { | ||
| "background-color": "color-mix(in srgb, var(--tpl-bg) 80%, transparent)", | ||
| "backdrop-filter": "blur(12px)", | ||
| "-webkit-backdrop-filter": "blur(12px)", | ||
| "box-shadow": "var(--tpl-shadow-md)", | ||
| "border-bottom": "1px solid var(--tpl-border)" | ||
| } | ||
| }, et = { class: "tpl-header-left tpl:flex tpl:min-w-[200px] tpl:items-center tpl:gap-3" }, tt = { | ||
| key: 0, | ||
| class: "tpl:text-xs tpl:opacity-60 tpl:text-[var(--tpl-text-muted)]" | ||
| }, nt = { class: "tpl-header-center tpl:flex tpl:items-center tpl:justify-center tpl:gap-10" }, rt = { class: "tpl-header-right tpl:flex tpl:min-w-[200px] tpl:items-center tpl:justify-end tpl:gap-3" }, it = ["data-tooltip"], at = { | ||
| key: 1, | ||
| "aria-live": "polite", | ||
| class: "tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-success)]" | ||
| }, ot = { | ||
| key: 2, | ||
| "aria-live": "polite", | ||
| class: "tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-text-muted)]" | ||
| }, st = ["aria-label", "aria-expanded"], ct = { | ||
| key: 0, | ||
| class: "tpl:inline-flex tpl:size-4.5 tpl:items-center tpl:justify-center tpl:rounded-full tpl:text-[10px] tpl:font-semibold tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]" | ||
| }, lt = ["aria-expanded"], ut = { | ||
| key: 0, | ||
| class: "tpl:absolute tpl:right-0 tpl:top-full tpl:z-50 tpl:mt-1 tpl:origin-top-right" | ||
| }, dt = ["disabled"], ft = ["disabled"], pt = /* @__PURE__ */ E({ | ||
| __name: "CloudHeader", | ||
| props: { | ||
| editor: {}, | ||
| core: {}, | ||
| featureFlags: {}, | ||
| panelState: {}, | ||
| snapshotPreview: {}, | ||
| commentsInstance: {}, | ||
| testEmail: {}, | ||
| websocket: {}, | ||
| collaboration: {}, | ||
| isCollaborationEnabled: { type: Boolean }, | ||
| isSaveDisabled: { type: Boolean }, | ||
| isSaving: { type: Boolean } | ||
| }, | ||
| emits: ["save"], | ||
| setup(e) { | ||
| let t = u(() => import("./CollaboratorBar-BS3rJy6-.js")), n = u(() => import("./SnapshotHistory-Bf1uovti.js")), r = u(() => import("./AiFeatureMenu-Duwg413F.js")), { t: a, format: o } = X(); | ||
| return (c, l) => (i(), p("header", $e, [ | ||
| x("div", et, [e.featureFlags.templateLimit.value === null ? m("", !0) : (i(), p("span", tt, d(h(o)(h(a).header.templatesUsed, { | ||
| used: e.featureFlags.templateCount.value, | ||
| max: e.featureFlags.templateLimit.value | ||
| })), 1))]), | ||
| x("div", nt, [ | ||
| D(de, { | ||
| viewport: e.editor.state.viewport, | ||
| onChange: e.editor.setViewport | ||
| }, null, 8, ["viewport", "onChange"]), | ||
| D(ce, { | ||
| "dark-mode": e.editor.state.darkMode, | ||
| onChange: e.editor.setDarkMode | ||
| }, null, 8, ["dark-mode", "onChange"]), | ||
| D(Z, { | ||
| "preview-mode": e.editor.state.previewMode, | ||
| onChange: e.editor.setPreviewMode | ||
| }, null, 8, ["preview-mode", "onChange"]), | ||
| e.collaboration && e.isCollaborationEnabled ? (i(), v(h(t), { | ||
| key: 0, | ||
| collaborators: e.collaboration.collaborators.value, | ||
| "is-connected": e.websocket.isConnected.value | ||
| }, null, 8, ["collaborators", "is-connected"])) : m("", !0), | ||
| e.snapshotPreview.snapshotHistoryInstance.value ? (i(), v(h(n), { | ||
| key: 1, | ||
| snapshots: e.snapshotPreview.snapshotHistorySnapshots.value, | ||
| "is-loading": e.snapshotPreview.snapshotHistoryIsLoading.value, | ||
| "is-restoring": e.snapshotPreview.snapshotHistoryIsRestoring.value, | ||
| onLoad: e.snapshotPreview.loadSnapshotHistory, | ||
| onNavigate: e.snapshotPreview.handleSnapshotNavigate | ||
| }, null, 8, [ | ||
| "snapshots", | ||
| "is-loading", | ||
| "is-restoring", | ||
| "onLoad", | ||
| "onNavigate" | ||
| ])) : m("", !0) | ||
| ]), | ||
| x("div", rt, [ | ||
| e.featureFlags.saveStatus.value === "error" ? (i(), p("div", { | ||
| key: 0, | ||
| "aria-live": "assertive", | ||
| class: "tpl-tooltip tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-danger)]", | ||
| "data-tooltip": e.featureFlags.saveErrorMessage.value | ||
| }, [D(h(W), { | ||
| size: 12, | ||
| "stroke-width": 2.5 | ||
| }), T(" " + d(h(a).header.saveFailed), 1)], 8, it)) : e.featureFlags.saveStatus.value === "saved" ? (i(), p("div", at, [D(h(K), { | ||
| size: 12, | ||
| "stroke-width": 2.5 | ||
| }), T(" " + d(h(a).header.saved), 1)])) : e.editor.state.isDirty ? (i(), p("div", ot, [l[4] ||= x("span", { class: "tpl-pulse tpl:size-1.5 tpl:rounded-full tpl:bg-[var(--tpl-primary)]" }, null, -1), T(" " + d(h(a).header.unsaved), 1)])) : m("", !0), | ||
| e.commentsInstance.isEnabled.value && e.featureFlags.hasTemplateSaved.value ? (i(), p("button", { | ||
| key: 3, | ||
| "aria-label": e.commentsInstance.unresolvedCount.value > 0 ? `${h(a).comments.button} (${e.commentsInstance.unresolvedCount.value})` : h(a).comments.button, | ||
| "aria-expanded": e.panelState.commentsOpen.value, | ||
| class: b(h($)), | ||
| style: C({ | ||
| backgroundColor: e.panelState.commentsOpen.value ? "var(--tpl-primary)" : "transparent", | ||
| color: e.panelState.commentsOpen.value ? "var(--tpl-bg)" : "var(--tpl-primary)", | ||
| borderColor: "var(--tpl-primary)" | ||
| }), | ||
| onClick: l[0] ||= (t) => e.panelState.commentsOpen.value = !e.panelState.commentsOpen.value | ||
| }, [ | ||
| D(h(J), { | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| }), | ||
| T(" " + d(h(a).comments.button) + " ", 1), | ||
| e.commentsInstance.unresolvedCount.value > 0 && !e.panelState.commentsOpen.value ? (i(), p("span", ct, d(e.commentsInstance.unresolvedCount.value), 1)) : m("", !0) | ||
| ], 14, st)) : m("", !0), | ||
| e.featureFlags.canUseAiGeneration.value && e.featureFlags.hasTemplateSaved.value ? (i(), p("div", { | ||
| key: 4, | ||
| ref: (t) => e.panelState.aiMenuRef.value = t, | ||
| class: "tpl:relative" | ||
| }, [x("button", { | ||
| "aria-expanded": e.panelState.aiMenuOpen.value, | ||
| class: b(["tpl-ai-btn tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-4 tpl:py-2 tpl:text-sm tpl:font-semibold tpl:whitespace-nowrap tpl:transition-all tpl:duration-200", e.panelState.aiButtonActive.value ? "tpl-ai-btn--active" : "tpl-ai-btn--idle"]), | ||
| onClick: l[1] ||= _((...t) => e.panelState.toggleAiMenu && e.panelState.toggleAiMenu(...t), ["stop"]) | ||
| }, [D(h(ie), { | ||
| size: 16, | ||
| "stroke-width": 2, | ||
| class: "tpl-ai-btn-icon" | ||
| }), T(" " + d(h(a).aiChat.button), 1)], 10, lt), D(y, { | ||
| "enter-active-class": "tpl:transition-all tpl:duration-150 tpl:ease-out", | ||
| "enter-from-class": "tpl:scale-95 tpl:opacity-0", | ||
| "enter-to-class": "tpl:scale-100 tpl:opacity-100", | ||
| "leave-active-class": "tpl:transition-all tpl:duration-100 tpl:ease-in", | ||
| "leave-from-class": "tpl:scale-100 tpl:opacity-100", | ||
| "leave-to-class": "tpl:scale-95 tpl:opacity-0" | ||
| }, { | ||
| default: s(() => [e.panelState.aiMenuOpen.value ? (i(), p("div", ut, [D(h(r), { | ||
| "active-feature": e.panelState.activeAiFeature.value, | ||
| onSelect: e.panelState.handleAiFeatureSelect | ||
| }, null, 8, ["active-feature", "onSelect"])])) : m("", !0)]), | ||
| _: 1 | ||
| })], 512)) : m("", !0), | ||
| e.testEmail.isEnabled.value && e.featureFlags.canSendTestEmail.value ? (i(), p("button", { | ||
| key: 5, | ||
| class: b(h($)), | ||
| style: { | ||
| "background-color": "transparent", | ||
| color: "var(--tpl-primary)", | ||
| "border-color": "var(--tpl-primary)" | ||
| }, | ||
| disabled: e.testEmail.isSending.value || !e.featureFlags.hasTemplateSaved.value, | ||
| onClick: l[2] ||= (t) => e.panelState.testEmailModalOpen.value = !0 | ||
| }, [e.testEmail.isSending.value ? (i(), v(h(Y), { | ||
| key: 1, | ||
| class: "tpl-spinner", | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| })) : (i(), v(h(ne), { | ||
| key: 0, | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| })), T(" " + d(h(a).testEmail.button), 1)], 10, dt)) : m("", !0), | ||
| x("button", { | ||
| class: b(h($)), | ||
| style: { | ||
| "background-color": "transparent", | ||
| color: "var(--tpl-primary)", | ||
| "border-color": "var(--tpl-primary)" | ||
| }, | ||
| disabled: e.isSaveDisabled, | ||
| onClick: l[3] ||= (e) => c.$emit("save") | ||
| }, [e.isSaving ? (i(), v(h(Y), { | ||
| key: 1, | ||
| class: "tpl-spinner", | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| })) : (i(), v(h(q), { | ||
| key: 0, | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| })), T(" " + d(e.isSaving ? h(a).header.saving : h(a).header.save), 1)], 10, ft) | ||
| ]) | ||
| ])); | ||
| } | ||
| }), mt = /* @__PURE__ */ E({ | ||
| __name: "CloudPanels", | ||
| props: { | ||
| config: {}, | ||
| editor: {}, | ||
| core: {}, | ||
| panelState: {}, | ||
| planConfigInstance: {}, | ||
| testEmail: {}, | ||
| mediaLib: {} | ||
| }, | ||
| emits: ["send-test-email"], | ||
| setup(e, { expose: t, emit: n }) { | ||
| let r = u(() => import("./AiChatSidebar-A1CuWGY0.js")), a = u(() => import("./CommentsSidebar-DHdRiGPX.js")), o = u(() => import("./DesignReferenceSidebar-Dre99DTH.js")), s = u(() => import("./TemplateScoringPanel-CTk8WwxU.js")), l = u(() => import("./TestEmailModal-BJTDxFHh.js")), d = u(async () => { | ||
| try { | ||
| return (await import("./src-D6F_3XkE.js")).MediaLibraryModal; | ||
| } catch { | ||
| throw Error("[Templatical] Cloud media library requires the optional peer dependency '@templatical/media-library'. Please install it."); | ||
| } | ||
| }), f = n; | ||
| function m(e, t, n) { | ||
| t.history.record(), n.setContent(e), t.conditionPreview.reset(); | ||
| } | ||
| let g = c(null); | ||
| function _(e) { | ||
| g.value?.filterByBlock(e); | ||
| } | ||
| return t({ filterCommentsByBlock: _ }), (t, n) => (i(), p(w, null, [ | ||
| D(h(r), { | ||
| visible: e.panelState.aiChatOpen.value, | ||
| "on-apply": (t) => m(t, e.core, e.editor), | ||
| onClose: n[0] ||= (t) => e.panelState.aiChatOpen.value = !1 | ||
| }, null, 8, ["visible", "on-apply"]), | ||
| D(h(s), { | ||
| visible: e.panelState.scoringPanelOpen.value, | ||
| onClose: n[1] ||= (t) => e.panelState.scoringPanelOpen.value = !1 | ||
| }, null, 8, ["visible"]), | ||
| D(h(o), { | ||
| visible: e.panelState.designReferenceOpen.value, | ||
| "has-existing-blocks": e.editor.content.value.blocks.length > 0, | ||
| onClose: n[2] ||= (t) => e.panelState.designReferenceOpen.value = !1, | ||
| onApply: n[3] ||= (t) => m(t, e.core, e.editor) | ||
| }, null, 8, ["visible", "has-existing-blocks"]), | ||
| D(h(a), { | ||
| ref_key: "commentsSidebar", | ||
| ref: g, | ||
| visible: e.panelState.commentsOpen.value, | ||
| onClose: n[4] ||= (t) => e.panelState.commentsOpen.value = !1 | ||
| }, null, 8, ["visible"]), | ||
| D(h(l), { | ||
| visible: e.panelState.testEmailModalOpen.value, | ||
| "allowed-emails": e.testEmail.allowedEmails.value, | ||
| "is-sending": e.testEmail.isSending.value, | ||
| error: e.testEmail.error.value, | ||
| onSend: n[5] ||= (e) => f("send-test-email", e), | ||
| onClose: n[6] ||= (t) => e.panelState.testEmailModalOpen.value = !1 | ||
| }, null, 8, [ | ||
| "visible", | ||
| "allowed-emails", | ||
| "is-sending", | ||
| "error" | ||
| ]), | ||
| D(h(d), { | ||
| visible: e.panelState.mediaLibraryOpen.value, | ||
| accept: e.panelState.mediaLibraryAccept.value, | ||
| "popover-target": e.core.popoverRoot.value, | ||
| onSelect: e.mediaLib.handleMediaSelect, | ||
| onClose: e.mediaLib.handleMediaLibraryClose | ||
| }, null, 8, [ | ||
| "visible", | ||
| "accept", | ||
| "popover-target", | ||
| "onSelect", | ||
| "onClose" | ||
| ]) | ||
| ], 64)); | ||
| } | ||
| }), ht = { | ||
| key: 0, | ||
| class: "tpl-loading tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:bg-[var(--tpl-bg)]" | ||
| }, gt = { class: "tpl:flex tpl:flex-1 tpl:overflow-hidden" }, _t = { class: "tpl:flex tpl:w-12 tpl:shrink-0 tpl:flex-col tpl:items-center tpl:gap-4 tpl:py-5 tpl:border-r tpl:border-[var(--tpl-border)]" }, vt = /* @__PURE__ */ E({ | ||
| __name: "CloudLoadingOverlay", | ||
| props: { visible: { type: Boolean } }, | ||
| setup(e) { | ||
| return (t, n) => e.visible ? (i(), p("div", ht, [n[1] ||= l("<div class=\"tpl:flex tpl:h-14 tpl:shrink-0 tpl:items-center tpl:justify-between tpl:px-4 tpl:border-b tpl:border-[var(--tpl-border)]\"><div class=\"tpl-shimmer tpl:h-5 tpl:w-28 tpl:rounded-[var(--tpl-radius-sm)]\"></div><div class=\"tpl:flex tpl:gap-3\"><div class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"></div><div class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"></div></div></div>", 1), x("div", gt, [x("div", _t, [(i(), p(w, null, o(5, (e) => x("div", { | ||
| key: e, | ||
| class: "tpl-shimmer tpl:size-7 tpl:rounded-[var(--tpl-radius-sm)]" | ||
| })), 64))]), n[0] ||= l("<div class=\"tpl:flex tpl:flex-1 tpl:items-start tpl:justify-center tpl:overflow-auto tpl:p-8 tpl:bg-[var(--tpl-canvas-bg)]\"><div class=\"tpl:w-full tpl:max-w-[600px] tpl:rounded-[var(--tpl-radius)] tpl:p-6 tpl:bg-[var(--tpl-bg)] tpl:shadow-[var(--tpl-shadow-sm)]\"><div class=\"tpl:space-y-2 tpl:py-4\"><div class=\"tpl-shimmer tpl:h-3 tpl:w-3/4 tpl:rounded\"></div><div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div><div class=\"tpl-shimmer tpl:h-3 tpl:w-5/6 tpl:rounded\"></div></div><div class=\"tpl:py-4\"><div class=\"tpl-shimmer tpl:h-44 tpl:w-full tpl:rounded-[var(--tpl-radius-sm)]\"></div></div><div class=\"tpl:space-y-2 tpl:py-4\"><div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div><div class=\"tpl-shimmer tpl:h-3 tpl:w-2/3 tpl:rounded\"></div></div><div class=\"tpl:flex tpl:justify-center tpl:py-4\"><div class=\"tpl-shimmer tpl:h-10 tpl:w-36 tpl:rounded-[var(--tpl-radius-sm)]\"></div></div><div class=\"tpl:space-y-2 tpl:py-4\"><div class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/2 tpl:rounded\"></div><div class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/3 tpl:rounded\"></div></div></div></div><div class=\"tpl:flex tpl:w-[320px] tpl:shrink-0 tpl:flex-col tpl:gap-4 tpl:p-4 tpl:border-l tpl:border-[var(--tpl-border)]\"><div class=\"tpl-shimmer tpl:h-8 tpl:rounded-[var(--tpl-radius-sm)]\"></div><div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div><div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div></div>", 2)])])) : m("", !0); | ||
| } | ||
| }), yt = { | ||
| key: 0, | ||
| role: "alert", | ||
| class: "tpl-error tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-6 tpl:px-8 tpl:bg-[var(--tpl-bg)]" | ||
| }, bt = { class: "tpl:flex tpl:size-16 tpl:items-center tpl:justify-center tpl:rounded-full tpl:bg-[var(--tpl-danger-light)]" }, xt = { class: "tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:text-center" }, St = { class: "tpl:text-lg tpl:font-semibold tpl:text-[var(--tpl-text)]" }, Ct = { class: "tpl:max-w-md tpl:text-sm tpl:text-[var(--tpl-text-muted)]" }, wt = /* @__PURE__ */ E({ | ||
| __name: "CloudErrorOverlay", | ||
| props: { | ||
| error: {}, | ||
| visible: { type: Boolean } | ||
| }, | ||
| emits: ["retry"], | ||
| setup(e, { emit: t }) { | ||
| let n = t, { t: r } = X(); | ||
| function a(e) { | ||
| return "isUnauthorized" in e && e.isUnauthorized ? r.error.authFailed : "isNotFound" in e && e.isNotFound ? r.error.templateNotFound : r.error.defaultMessage; | ||
| } | ||
| function o(e) { | ||
| return "isNotFound" in e && !!e.isNotFound; | ||
| } | ||
| return (t, s) => e.visible && e.error ? (i(), p("div", yt, [ | ||
| x("div", bt, [D(h(W), { | ||
| size: 32, | ||
| "stroke-width": 1.5, | ||
| class: "tpl:text-[var(--tpl-danger)]" | ||
| })]), | ||
| x("div", xt, [x("h2", St, d(h(r).error.title), 1), x("p", Ct, d(a(e.error)), 1)]), | ||
| o(e.error) ? m("", !0) : (i(), p("button", { | ||
| key: 0, | ||
| class: "tpl-btn tpl-btn-primary tpl:inline-flex tpl:items-center tpl:gap-2 tpl:rounded-md tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]", | ||
| onClick: s[0] ||= (e) => n("retry") | ||
| }, d(h(r).error.retry), 1)) | ||
| ])) : m("", !0); | ||
| } | ||
| }), Tt = { | ||
| key: 0, | ||
| class: "tpl-preview-banner tpl:absolute tpl:top-14 tpl:right-0 tpl:left-0 tpl:z-40 tpl:flex tpl:items-center tpl:justify-center tpl:gap-4 tpl:px-4 tpl:py-3 tpl:bg-[var(--tpl-primary-light)] tpl:border-b tpl:border-[var(--tpl-primary)]" | ||
| }, Et = { class: "tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]" }, Dt = { class: "tpl:flex tpl:items-center tpl:gap-2" }, Ot = /* @__PURE__ */ E({ | ||
| __name: "SnapshotPreviewBanner", | ||
| props: { visible: { type: Boolean } }, | ||
| emits: ["cancel", "confirm"], | ||
| setup(e, { emit: t }) { | ||
| let n = t, { t: r } = X(); | ||
| return (t, a) => e.visible ? (i(), p("div", Tt, [x("div", Et, [D(h(G), { | ||
| size: 18, | ||
| "stroke-width": 2, | ||
| class: "tpl:text-[var(--tpl-primary)]" | ||
| }), x("span", null, d(h(r).snapshotPreview.message), 1)]), x("div", Dt, [x("button", { | ||
| class: "tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-150 tpl:text-[var(--tpl-text-muted)] tpl:border tpl:border-[var(--tpl-border)]", | ||
| style: { "background-color": "transparent" }, | ||
| onClick: a[0] ||= (e) => n("cancel") | ||
| }, d(h(r).snapshotPreview.cancel), 1), x("button", { | ||
| class: "tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]", | ||
| onClick: a[1] ||= (e) => n("confirm") | ||
| }, d(h(r).snapshotPreview.restore), 1)])])) : m("", !0); | ||
| } | ||
| }), kt = { | ||
| key: 0, | ||
| role: "status", | ||
| "aria-live": "polite", | ||
| class: "tpl:absolute tpl:top-16 tpl:left-1/2 tpl:z-toast tpl:-translate-x-1/2 tpl:rounded-[var(--tpl-radius)] tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:shadow-lg", | ||
| style: { | ||
| "background-color": "var(--tpl-warning-light)", | ||
| color: "var(--tpl-text)", | ||
| border: "1px solid var(--tpl-warning)" | ||
| } | ||
| }, At = /* @__PURE__ */ E({ | ||
| __name: "CollabUndoToast", | ||
| props: { visible: { type: Boolean } }, | ||
| setup(e) { | ||
| let { t } = U(); | ||
| return (n, r) => e.visible ? (i(), p("div", kt, d(h(t).history.collabWarning), 1)) : m("", !0); | ||
| } | ||
| }), jt = ["data-tpl-theme"], Mt = { class: "tpl:sticky tpl:top-0 tpl:z-40 tpl:h-0" }, Nt = { class: "tpl-main tpl:flex tpl:justify-center tpl:p-8" }, Pt = ["aria-label"], Ft = /*#__PURE__*/ ae(/* @__PURE__ */ E({ | ||
| __name: "CloudEditor", | ||
| props: { | ||
| config: {}, | ||
| translations: {}, | ||
| cloudTranslations: {}, | ||
| fontsManager: {}, | ||
| shadowRoot: {} | ||
| }, | ||
| emits: ["ready"], | ||
| setup(e, { expose: r, emit: o }) { | ||
| let l = u(() => import("./SavedBlocksPanels-D-pA_wfk.js")), f = e; | ||
| a(z, f.cloudTranslations); | ||
| let w = o, E = c(null), O = c(null), k = ze({ | ||
| config: f.config, | ||
| translations: f.translations, | ||
| fontsManager: f.fontsManager, | ||
| emit: w, | ||
| getCommentsSidebar: () => E.value ? { filterByBlock: E.value.filterCommentsByBlock } : null, | ||
| editorRoot: f.shadowRoot, | ||
| containerEl: O | ||
| }), { isInitializing: A, isAuthReady: j, initError: N, planConfigInstance: P, websocket: F, collaboration: I, isCollaborationEnabled: L, editor: R, core: B, featureFlags: V, mediaLib: H, exporter: ee, testEmail: U, commentsInstance: W, savedBlocks: G, panelState: K, snapshotPreview: q, collabWarning: J, setThemeOverrides: ne, setUiTheme: Y } = k, { showNotice: re } = M(() => f.config.smallScreenNotice); | ||
| async function ie(e) { | ||
| try { | ||
| await U.sendTestEmail(e), K.testEmailModalOpen.value = !1; | ||
| } catch {} | ||
| } | ||
| async function ae() { | ||
| try { | ||
| await q.confirmRestoreSnapshot(); | ||
| } catch {} | ||
| } | ||
| let X = Ve({ | ||
| config: f.config, | ||
| editor: R, | ||
| websocket: F, | ||
| planConfigInstance: P, | ||
| snapshotPreview: q, | ||
| core: B, | ||
| exporter: ee, | ||
| featureFlags: V, | ||
| isDestroyed: k.isDestroyed | ||
| }), Z = He({ | ||
| issues: B.templateLint ? B.templateLint.issues : c([]), | ||
| planConfig: P.config | ||
| }); | ||
| async function Q() { | ||
| await Z.tryRunSave(() => X.saveTemplate().catch((e) => f.config.onError?.(e))); | ||
| } | ||
| return k.onSaveHook.value = Q, t(() => { | ||
| k.initialize(); | ||
| }), g(() => { | ||
| k.destroy(); | ||
| }), r({ | ||
| getContent: () => R.content.value, | ||
| setContent: (e) => R.setContent(e), | ||
| setTheme: Y, | ||
| setThemeOverrides: ne, | ||
| create: X.createTemplate, | ||
| load: X.loadTemplate, | ||
| save: X.saveTemplate, | ||
| sendTestEmail: U.sendTestEmail | ||
| }), (t, r) => (i(), p("div", { | ||
| ref_key: "rootEl", | ||
| ref: O, | ||
| class: b(["tpl tpl:relative tpl:h-full tpl:overflow-hidden", { "tpl:dark": h(R).state.darkMode }]), | ||
| "data-tpl-theme": h(B).resolvedTheme.value, | ||
| style: C(h(B).themeStyles.value), | ||
| onDragover: r[6] ||= _(() => {}, ["prevent"]), | ||
| onDrop: r[7] ||= _(() => {}, ["prevent"]) | ||
| }, [ | ||
| D(ge), | ||
| D(y, { | ||
| "enter-active-class": "tpl:transition-opacity tpl:duration-200", | ||
| "enter-from-class": "tpl:opacity-100", | ||
| "enter-to-class": "tpl:opacity-100", | ||
| "leave-active-class": "tpl:transition-opacity tpl:duration-300", | ||
| "leave-from-class": "tpl:opacity-100", | ||
| "leave-to-class": "tpl:opacity-0" | ||
| }, { | ||
| default: s(() => [D(vt, { visible: h(A) || h(R).state.isLoading }, null, 8, ["visible"])]), | ||
| _: 1 | ||
| }), | ||
| D(y, { | ||
| "enter-active-class": "tpl:transition-opacity tpl:duration-200", | ||
| "enter-from-class": "tpl:opacity-0", | ||
| "enter-to-class": "tpl:opacity-100", | ||
| "leave-active-class": "tpl:transition-opacity tpl:duration-300", | ||
| "leave-from-class": "tpl:opacity-100", | ||
| "leave-to-class": "tpl:opacity-0" | ||
| }, { | ||
| default: s(() => [D(wt, { | ||
| error: h(N), | ||
| visible: !!h(N) && !h(A), | ||
| onRetry: h(k).initialize | ||
| }, null, 8, [ | ||
| "error", | ||
| "visible", | ||
| "onRetry" | ||
| ])]), | ||
| _: 1 | ||
| }), | ||
| D(pt, { | ||
| editor: h(R), | ||
| core: h(B), | ||
| "feature-flags": h(V), | ||
| "panel-state": h(K), | ||
| "snapshot-preview": h(q), | ||
| "comments-instance": h(W), | ||
| "test-email": h(U), | ||
| websocket: h(F), | ||
| collaboration: h(I), | ||
| "is-collaboration-enabled": h(L), | ||
| "is-saving": h(R).state.isSaving || h(V).isSaveExporting.value, | ||
| "is-save-disabled": h(R).state.isSaving || h(V).isSaveExporting.value || !h(R).state.isDirty, | ||
| onSave: Q | ||
| }, null, 8, [ | ||
| "editor", | ||
| "core", | ||
| "feature-flags", | ||
| "panel-state", | ||
| "snapshot-preview", | ||
| "comments-instance", | ||
| "test-email", | ||
| "websocket", | ||
| "collaboration", | ||
| "is-collaboration-enabled", | ||
| "is-saving", | ||
| "is-save-disabled" | ||
| ]), | ||
| D(Qe, { | ||
| open: h(Z).modalOpen.value, | ||
| issues: h(Z).blockingIssues.value, | ||
| onCancel: h(Z).cancel, | ||
| onConfirm: h(Z).confirmAndSave | ||
| }, null, 8, [ | ||
| "open", | ||
| "issues", | ||
| "onCancel", | ||
| "onConfirm" | ||
| ]), | ||
| D(Ot, { | ||
| visible: h(q).isPreviewingSnapshot.value, | ||
| onCancel: h(q).cancelPreview, | ||
| onConfirm: ae | ||
| }, null, 8, ["visible", "onCancel"]), | ||
| D(y, { | ||
| "enter-active-class": "tpl:transition-all tpl:duration-200 tpl:ease-out", | ||
| "enter-from-class": "tpl:translate-y-[-8px] tpl:opacity-0", | ||
| "enter-to-class": "tpl:translate-y-0 tpl:opacity-100", | ||
| "leave-active-class": "tpl:transition-all tpl:duration-300 tpl:ease-in", | ||
| "leave-from-class": "tpl:translate-y-0 tpl:opacity-100", | ||
| "leave-to-class": "tpl:translate-y-[-8px] tpl:opacity-0" | ||
| }, { | ||
| default: s(() => [D(At, { visible: h(J).collabUndoWarningVisible.value }, null, 8, ["visible"])]), | ||
| _: 1 | ||
| }), | ||
| n(D(le, null, null, 512), [[S, !h(R).state.previewMode]]), | ||
| x("div", { | ||
| class: b(["tpl-body tpl:absolute tpl:bottom-0 tpl:overflow-auto", [h(R).state.previewMode ? "tpl:left-0 tpl:right-0" : h(K).rightPanelOpen.value ? "tpl:left-12 tpl:right-[680px]" : "tpl:left-12 tpl:right-[320px]", h(q).isPreviewingSnapshot.value ? "tpl:top-[104px]" : "tpl:top-14"]]), | ||
| style: { | ||
| transition: "all 300ms cubic-bezier(0.34, 1.56, 0.64, 1)", | ||
| "background-color": "var(--tpl-canvas-bg)" | ||
| } | ||
| }, [x("div", Mt, [D(y, { name: "tpl-restore-btn" }, { | ||
| default: s(() => [h(B).conditionPreview.hasHiddenBlocks.value ? (i(), p("button", { | ||
| key: 0, | ||
| class: "tpl:absolute tpl:left-1/2 tpl:top-2 tpl:-translate-x-1/2 tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-full tpl:border tpl:px-3.5 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:whitespace-nowrap tpl:shadow-md tpl:hover:opacity-80", | ||
| style: { | ||
| "background-color": "var(--tpl-warning-light)", | ||
| color: "var(--tpl-warning)", | ||
| "border-color": "var(--tpl-warning)", | ||
| "backdrop-filter": "blur(8px)" | ||
| }, | ||
| onClick: r[0] ||= (e) => h(B).conditionPreview.reset() | ||
| }, [D(h(te), { | ||
| size: 13, | ||
| "stroke-width": 2 | ||
| }), T(" " + d(h(B).t.blockSettings.restoreHiddenBlocks), 1)])) : m("", !0)]), | ||
| _: 1 | ||
| })]), x("main", Nt, [D(se, { | ||
| viewport: h(R).state.viewport, | ||
| content: h(R).content.value, | ||
| "selected-block-id": h(R).state.selectedBlockId, | ||
| "dark-mode": h(R).state.darkMode, | ||
| "preview-mode": h(R).state.previewMode, | ||
| "locked-blocks": h(I)?.lockedBlocks.value ?? void 0, | ||
| onSelectBlock: h(R).selectBlock, | ||
| onOpenAiChat: r[1] ||= (e) => h(K).aiChatOpen.value = !0, | ||
| onOpenDesignReference: r[2] ||= (e) => h(K).designReferenceOpen.value = !0 | ||
| }, null, 8, [ | ||
| "viewport", | ||
| "content", | ||
| "selected-block-id", | ||
| "dark-mode", | ||
| "preview-mode", | ||
| "locked-blocks", | ||
| "onSelectBlock" | ||
| ])])], 2), | ||
| e.config.branding !== !1 && !h(V).isWhiteLabeled.value ? (i(), v(pe, { | ||
| key: 0, | ||
| "position-class": [h(R).state.previewMode ? "tpl:left-0 tpl:right-0" : h(K).rightPanelOpen.value ? "tpl:left-12 tpl:right-[680px]" : "tpl:left-12 tpl:right-[320px]"] | ||
| }, null, 8, ["position-class"])) : m("", !0), | ||
| x("div", { | ||
| class: "tpl-sr-only", | ||
| role: "status", | ||
| "aria-live": "polite", | ||
| "aria-atomic": "true", | ||
| "aria-label": h(B).t.landmarks.reorderAnnouncements | ||
| }, d(h(B).keyboardReorder.announcement.value), 9, Pt), | ||
| n(D(oe, { | ||
| "selected-block": h(R).selectedBlock.value, | ||
| settings: h(R).content.value.settings, | ||
| "shifted-left": h(K).rightPanelOpen.value, | ||
| onUpdateBlock: r[3] ||= (e) => h(R).updateBlock(h(R).selectedBlock.value.id, e), | ||
| onDeleteBlock: r[4] ||= (e) => h(B).blockActions.deleteBlock(h(R).selectedBlock.value.id), | ||
| onDuplicateBlock: r[5] ||= (e) => h(B).blockActions.duplicateBlock(h(R).selectedBlock.value), | ||
| onUpdateSettings: h(R).updateSettings | ||
| }, null, 8, [ | ||
| "selected-block", | ||
| "settings", | ||
| "shifted-left", | ||
| "onUpdateSettings" | ||
| ]), [[S, !h(R).state.previewMode]]), | ||
| !h(A) && h(j) ? (i(), v(mt, { | ||
| key: 1, | ||
| ref_key: "cloudPanelsRef", | ||
| ref: E, | ||
| config: f.config, | ||
| editor: h(R), | ||
| core: h(B), | ||
| "panel-state": h(K), | ||
| "plan-config-instance": h(P), | ||
| "test-email": h(U), | ||
| "media-lib": h(H), | ||
| onSendTestEmail: ie | ||
| }, null, 8, [ | ||
| "config", | ||
| "editor", | ||
| "core", | ||
| "panel-state", | ||
| "plan-config-instance", | ||
| "test-email", | ||
| "media-lib" | ||
| ])) : m("", !0), | ||
| h(G).isAvailable.value ? (i(), v(h(l), { | ||
| key: 2, | ||
| feature: h(G) | ||
| }, null, 8, ["feature"])) : m("", !0), | ||
| x("div", { | ||
| ref: (e) => h(B).popoverRoot.value = e, | ||
| class: "tpl-popover-root" | ||
| }, null, 512), | ||
| D(ue), | ||
| D(he), | ||
| h(re) ? (i(), v(me, { key: 3 })) : m("", !0) | ||
| ], 46, jt)); | ||
| } | ||
| }), [["__scopeId", "data-v-04940787"]]); | ||
| //#endregion | ||
| export { Ft as default }; | ||
| //# sourceMappingURL=CloudEditor-C0Y4RD4c.js.map |
Sorry, the diff of this file is too big to display
| import { B as e, F as t, G as n, H as r, I as i, J as a, K as o, L as s, P as c, R as l, U as u, V as d, W as f, z as p } from "./src-BKI1x1k7.js"; | ||
| //#endregion | ||
| //#region ../renderer/src/render-context.ts | ||
| var m = `https://cdn.jsdelivr.net/npm/@templatical/renderer@${{ | ||
| name: "@templatical/renderer", | ||
| description: "Render Templatical email templates to MJML", | ||
| version: "0.21.2", | ||
| bugs: "https://github.com/templatical/sdk/issues", | ||
| dependencies: { "@templatical/types": "workspace:*" }, | ||
| devDependencies: { | ||
| "@resvg/resvg-js": "^2.6.2", | ||
| mjml: "^5.4.0", | ||
| typescript: "^6.0.3", | ||
| vitest: "^4.1.10" | ||
| }, | ||
| exports: { ".": { | ||
| types: "./dist/index.d.ts", | ||
| import: "./dist/index.js" | ||
| } }, | ||
| files: ["dist", "assets"], | ||
| homepage: "https://templatical.com", | ||
| keywords: [ | ||
| "email", | ||
| "email-template", | ||
| "html-email", | ||
| "mjml", | ||
| "renderer", | ||
| "templatical" | ||
| ], | ||
| license: "MIT", | ||
| module: "./dist/index.js", | ||
| publishConfig: { access: "public" }, | ||
| repository: { | ||
| type: "git", | ||
| url: "git+https://github.com/templatical/sdk.git", | ||
| directory: "packages/renderer" | ||
| }, | ||
| scripts: { | ||
| build: "tsdown && node scripts/rasterize-social.mjs", | ||
| test: "vitest run --config vitest.config.ts", | ||
| typecheck: "tsc --noEmit" | ||
| }, | ||
| type: "module", | ||
| types: "./dist/index.d.ts" | ||
| }.version}/assets/social`, h = { | ||
| arial: "Arial, sans-serif", | ||
| helvetica: "Helvetica, sans-serif", | ||
| georgia: "Georgia, serif", | ||
| "times new roman": "'Times New Roman', serif", | ||
| verdana: "Verdana, sans-serif", | ||
| "trebuchet ms": "'Trebuchet MS', sans-serif", | ||
| "courier new": "'Courier New', monospace", | ||
| tahoma: "Tahoma, sans-serif" | ||
| }, g = class e { | ||
| containerWidth; | ||
| customFonts; | ||
| defaultFallbackFont; | ||
| allowHtmlBlocks; | ||
| customBlockHtml; | ||
| socialIconsBaseUrl; | ||
| constructor(e, t, n, r, i = /* @__PURE__ */ new Map(), a = m) { | ||
| this.containerWidth = e, this.customFonts = t, this.defaultFallbackFont = n, this.allowHtmlBlocks = r, this.customBlockHtml = i, this.socialIconsBaseUrl = a; | ||
| } | ||
| withContainerWidth(t) { | ||
| return new e(t, this.customFonts, this.defaultFallbackFont, this.allowHtmlBlocks, this.customBlockHtml, this.socialIconsBaseUrl); | ||
| } | ||
| resolveFontFamily(e) { | ||
| for (let t of this.customFonts) if (t.name.toLowerCase() === e.toLowerCase()) { | ||
| let e = t.fallback ?? this.defaultFallbackFont; | ||
| return `'${t.name}', ${e}`; | ||
| } | ||
| return h[e.toLowerCase()] || e; | ||
| } | ||
| }, _ = { | ||
| "&": "&", | ||
| "<": "<", | ||
| ">": ">", | ||
| "\"": """, | ||
| "'": "'" | ||
| }, v = /[&<>"']/g; | ||
| function y(e) { | ||
| return e === "" ? "" : e.replace(v, (e) => _[e] ?? e); | ||
| } | ||
| function b(e) { | ||
| return e === "" ? "" : e.replace(v, (e) => _[e] ?? e); | ||
| } | ||
| function x(e) { | ||
| return e === "" ? "" : b(e).replace(/[;{}\r\n]/g, ""); | ||
| } | ||
| function S(e) { | ||
| return e === "" ? "" : C(e, (e) => w(e, "data-merge-tag") ?? w(e, "data-logic-merge-tag")); | ||
| } | ||
| function C(e, t) { | ||
| let n = "", r = 0; | ||
| for (; r < e.length;) { | ||
| let i = e.indexOf("<span", r); | ||
| if (i === -1) { | ||
| n += e.substring(r); | ||
| break; | ||
| } | ||
| let a = e[i + 5]; | ||
| if (a !== ">" && a !== " " && a !== " " && a !== "\n" && a !== "\r" && a !== "/") { | ||
| n += e.substring(r, i + 5), r = i + 5; | ||
| continue; | ||
| } | ||
| let o = e.indexOf(">", i + 5); | ||
| if (o === -1) { | ||
| n += e.substring(r); | ||
| break; | ||
| } | ||
| let s = e.indexOf("</span>", o + 1); | ||
| if (s === -1) { | ||
| n += e.substring(r); | ||
| break; | ||
| } | ||
| let c = t(e.substring(i + 5, o)); | ||
| if (c === null) { | ||
| n += e.substring(r, i + 5), r = i + 5; | ||
| continue; | ||
| } | ||
| n += e.substring(r, i), n += c, r = s + 7; | ||
| } | ||
| return n; | ||
| } | ||
| function w(e, t) { | ||
| let n = RegExp(`(?:^|\\s)${t}="([^"<>]*)"`).exec(e); | ||
| return n ? n[1] : null; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/padding.ts | ||
| function T(e) { | ||
| return `${e.top}px ${e.right}px ${e.bottom}px ${e.left}px`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/utils.ts | ||
| function E(e, t) { | ||
| return e ? ` ${t === "native" ? "background-color" : "container-background-color"}="${e}"` : ""; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/visibility.ts | ||
| function D(e) { | ||
| let t = e.visibility; | ||
| return t ? !t.desktop && !t.mobile : !1; | ||
| } | ||
| function O(e) { | ||
| let t = k(e); | ||
| return t === "" ? "" : ` css-class="${t}"`; | ||
| } | ||
| function k(e) { | ||
| let t = e.visibility; | ||
| if (!t) return ""; | ||
| let n = []; | ||
| return t.desktop || n.push("tpl-hide-desktop"), t.mobile || n.push("tpl-hide-mobile"), n.join(" "); | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/title.ts | ||
| function A(e, t) { | ||
| if (D(e)) return ""; | ||
| let n = T(e.styles.padding), r = E(e.styles.backgroundColor, "container"), i = j(S(e.content)), o = a[e.level] ?? a[2], s = e.color ? `\n color="${b(e.color)}"` : "", c = e.textAlign, l = ee(e.fontFamily, t), u = O(e), d = `h${a[e.level] ? e.level : 2}`; | ||
| return `<mj-text | ||
| font-size="${o}px"${s} | ||
| align="${c}" | ||
| line-height="1.3" | ||
| padding="${n}"${r}${l}${u} | ||
| ><${d} style="margin:0;font-size:inherit;color:inherit;line-height:inherit">${i}</${d}></mj-text>`; | ||
| } | ||
| function j(e) { | ||
| let t = e.match(/^\s*<p\b[^>]*>([\s\S]*)<\/p>\s*$/); | ||
| return !t || /<\/p>\s*<p\b/i.test(t[1]) ? e : t[1]; | ||
| } | ||
| function ee(e, t) { | ||
| return e ? ` font-family="${t.resolveFontFamily(e)}"` : ""; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/paragraph.ts | ||
| function te(e, t) { | ||
| if (D(e) || e.content.replace(/<\/?p\b[^<>]*>/gi, "").trim() === "") return ""; | ||
| let n = T(e.styles.padding), r = E(e.styles.backgroundColor, "container"), i = S(e.content); | ||
| return `<mj-text | ||
| line-height="1.5" | ||
| padding="${n}"${r}${O(e)} | ||
| >${i}</mj-text>`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/image.ts | ||
| function ne(e, t) { | ||
| if (D(e) || e.src === "") return ""; | ||
| let n = T(e.styles.padding), r = E(e.styles.backgroundColor, "container"), i = e.width === "full" ? t.containerWidth + "px" : e.width + "px", a = O(e), o = ""; | ||
| e.linkUrl && (o = ` href="${b(e.linkUrl)}"`, e.linkOpenInNewTab && (o += " target=\"_blank\" rel=\"noopener\"")); | ||
| let s = b(e.src), c = e.decorative === !0; | ||
| return `<mj-image | ||
| src="${s}" | ||
| alt="${c ? "" : b(e.alt)}" | ||
| width="${i}" | ||
| align="${e.align}" | ||
| padding="${n}"${r}${o}${a}${c ? " role=\"presentation\"" : ""} | ||
| />`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/button.ts | ||
| function M(e, t) { | ||
| if (D(e)) return ""; | ||
| let n = T(e.styles.padding), r = E(e.styles.backgroundColor, "container"), i = T(e.buttonPadding), a = e.url === "" ? "" : b(e.url), o = a === "" ? "" : ` href="${a}"`, s = b(e.backgroundColor), c = b(e.textColor), l = e.fontSize, u = e.borderRadius, d = y(e.text); | ||
| return `<mj-button${o}${e.openInNewTab ? " target=\"_blank\" rel=\"noopener\"" : ""} | ||
| background-color="${s}" | ||
| color="${c}" | ||
| font-size="${l}px" | ||
| font-weight="bold" | ||
| border-radius="${u}px" | ||
| inner-padding="${i}" | ||
| padding="${n}"${r}${N(e.fontFamily, t)}${P(e.width)}${O(e)} | ||
| >${d}</mj-button>`; | ||
| } | ||
| function N(e, t) { | ||
| return e ? ` font-family="${t.resolveFontFamily(e)}"` : ""; | ||
| } | ||
| function P(e) { | ||
| return e === void 0 ? "" : ` width="${e === "full" ? "100%" : `${e}px`}"`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/divider.ts | ||
| function F(e, t) { | ||
| if (D(e)) return ""; | ||
| let n = T(e.styles.padding), r = e.styles.backgroundColor ? ` container-background-color="${b(e.styles.backgroundColor)}"` : "", i = e.width === "full" ? "100%" : e.width + "px"; | ||
| return `<mj-divider | ||
| border-width="${e.thickness}px" | ||
| border-style="${e.lineStyle}" | ||
| border-color="${b(e.color)}" | ||
| width="${i}" | ||
| padding="${n}"${r}${O(e)} | ||
| />`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/spacer.ts | ||
| function I(e, t) { | ||
| return D(e) ? "" : `<mj-spacer height="${e.height}px" padding="0"${e.styles.backgroundColor ? ` container-background-color="${b(e.styles.backgroundColor)}"` : ""}${O(e)} />`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/html.ts | ||
| function L(e, t) { | ||
| if (D(e) || !t.allowHtmlBlocks) return ""; | ||
| let n = e.content; | ||
| if (n === "") return ""; | ||
| let r = O(e); | ||
| return `<mj-text padding="${T(e.styles.padding)}"${E(e.styles.backgroundColor, "container")}${r}> | ||
| ${n} | ||
| </mj-text>`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/social.ts | ||
| function R(e, t) { | ||
| if (D(e)) return ""; | ||
| let n = e.icons; | ||
| if (n.length === 0) return ""; | ||
| let r = T(e.styles.padding), i = e.styles.backgroundColor ? ` container-background-color="${b(e.styles.backgroundColor)}"` : "", a = O(e), o = e.align, s = e.iconSize, c = e.iconStyle, l = e.spacing, u; | ||
| switch (s) { | ||
| case "small": | ||
| u = 24; | ||
| break; | ||
| case "large": | ||
| u = 48; | ||
| break; | ||
| default: | ||
| u = 32; | ||
| break; | ||
| } | ||
| let d; | ||
| switch (c) { | ||
| case "circle": | ||
| d = "50%"; | ||
| break; | ||
| case "rounded": | ||
| d = "8px"; | ||
| break; | ||
| case "square": | ||
| d = "0"; | ||
| break; | ||
| default: | ||
| d = "4px"; | ||
| break; | ||
| } | ||
| let f = n.length; | ||
| return `<mj-social | ||
| mode="horizontal" | ||
| align="${o}" | ||
| icon-padding="0" | ||
| padding="${r}"${i}${a} | ||
| > | ||
| ${n.map((e, n) => { | ||
| let r = e.platform, i = b(e.url), a = `${t.socialIconsBaseUrl}/${c}/${r}.png`, o = n === f - 1 ? 0 : l; | ||
| return `<mj-social-element src="${a}" href="${i}" icon-size="${u}px" padding="0 ${o}px 0 0" border-radius="${d}" background-color="transparent"></mj-social-element>`; | ||
| }).join("\n")} | ||
| </mj-social>`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/menu.ts | ||
| function z(e, t) { | ||
| if (D(e) || e.items.length === 0) return ""; | ||
| let n = T(e.styles.padding), r = E(e.styles.backgroundColor, "container"), i = O(e), a = H(e.fontFamily, t), o = e.textAlign; | ||
| return `<mj-text | ||
| font-size="${e.fontSize}px"${e.color ? `\n color="${b(e.color)}"` : ""} | ||
| align="${o}" | ||
| line-height="1.5" | ||
| padding="${n}"${r}${a}${i} | ||
| >${B(e)}</mj-text>`; | ||
| } | ||
| function B(e) { | ||
| let t = e.items, n = y(e.separator), r = x(e.separatorColor), i = e.spacing, a = e.linkColor ?? e.color, o = [], s = t.length; | ||
| for (let e = 0; e < s; e++) o.push(V(t[e], a)), e < s - 1 && o.push(`<span style="color: ${r}; padding: 0 ${i}px;">${n}</span>`); | ||
| return o.join(""); | ||
| } | ||
| function V(e, t) { | ||
| let n = y(e.text), r = b(e.url), i = e.color ?? t, a = e.openInNewTab ? " target=\"_blank\" rel=\"noopener\"" : "", o = ["text-decoration: none"]; | ||
| return i && o.unshift(`color: ${x(i)}`), e.bold && o.push("font-weight: bold"), e.underline && o.push("text-decoration: underline"), `<a href="${r}" style="${o.join("; ")}"${a}>${n}</a>`; | ||
| } | ||
| function H(e, t) { | ||
| return e ? ` font-family="${t.resolveFontFamily(e)}"` : ""; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/table.ts | ||
| function U(e, t) { | ||
| if (D(e) || e.rows.length === 0) return ""; | ||
| let n = T(e.styles.padding), r = E(e.styles.backgroundColor, "container"), i = O(e), a = q(e.fontFamily, t); | ||
| return `<mj-text | ||
| font-size="${e.fontSize}px"${e.color ? `\n color="${b(e.color)}"` : ""} | ||
| align="${e.textAlign}" | ||
| line-height="1.5" | ||
| padding="${n}"${r}${a}${i} | ||
| >${W(e)}</mj-text>`; | ||
| } | ||
| function W(e) { | ||
| let t = x(e.borderColor), n = e.borderWidth, r = ""; | ||
| for (let i = 0; i < e.rows.length; i++) { | ||
| let a = e.rows[i]; | ||
| r += G(a, e, e.hasHeaderRow && i === 0, t, n); | ||
| } | ||
| return `<table style="width: 100%; border-collapse: collapse;">${r}</table>`; | ||
| } | ||
| function G(e, t, n, r, i) { | ||
| let a = ""; | ||
| for (let o of e.cells) a += K(o, t, n, r, i); | ||
| return `<tr>${a}</tr>`; | ||
| } | ||
| function K(e, t, n, r, i) { | ||
| let a = t.cellPadding, o = [`border: ${i}px solid ${r}`, `padding: ${a}px`]; | ||
| n && (o.push("font-weight: bold"), t.headerBackgroundColor && o.push(`background-color: ${x(t.headerBackgroundColor)}`)); | ||
| let s = o.join("; "), c = y(S(e.content)), l = n ? "th" : "td"; | ||
| return `<${l} style="${s}">${c}</${l}>`; | ||
| } | ||
| function q(e, t) { | ||
| return e ? ` font-family="${t.resolveFontFamily(e)}"` : ""; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/custom.ts | ||
| function re(e, t) { | ||
| if (D(e)) return ""; | ||
| let n = t.customBlockHtml.get(e.id) ?? e.renderedHtml; | ||
| if (!n || n === "") return ""; | ||
| let r = O(e); | ||
| return `<mj-text padding="${T(e.styles.padding)}"${E(e.styles.backgroundColor, "container")}${r}> | ||
| ${n} | ||
| </mj-text>`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/columns.ts | ||
| function J(e) { | ||
| switch (e) { | ||
| case "2": return ["50%", "50%"]; | ||
| case "3": return [ | ||
| "33.33%", | ||
| "33.33%", | ||
| "33.34%" | ||
| ]; | ||
| case "1-2": return ["33.33%", "66.67%"]; | ||
| case "2-1": return ["66.67%", "33.33%"]; | ||
| default: return ["100%"]; | ||
| } | ||
| } | ||
| function Y(e, t) { | ||
| switch (e) { | ||
| case "2": return [t * .5, t * .5]; | ||
| case "3": return [ | ||
| t / 3, | ||
| t / 3, | ||
| t / 3 | ||
| ]; | ||
| case "1-2": return [t / 3, t * 2 / 3]; | ||
| case "2-1": return [t * 2 / 3, t / 3]; | ||
| default: return [t]; | ||
| } | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/display-condition.ts | ||
| function X(e, t) { | ||
| if (t === "") return ""; | ||
| let n = e.displayCondition; | ||
| return n ? `<mj-raw>${n.before}</mj-raw> | ||
| ` + t + ` | ||
| <mj-raw>${n.after}</mj-raw>` : t; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/section.ts | ||
| function ie(e, t, n) { | ||
| if (D(e)) return ""; | ||
| let r = e.columns, i = J(r), a = Y(r, t.containerWidth), o = T(e.styles.padding), s = E(e.styles.backgroundColor, "native"), c = O(e), l = e.borderRadius && e.borderRadius > 0 ? ` border-radius="${e.borderRadius}px"` : "", u = e.children, f = []; | ||
| for (let e = 0; e < u.length; e++) { | ||
| let r = u[e], o = i[e] ?? "100%", s = Math.floor(a[e] ?? t.containerWidth), c = ae(r, t.allowHtmlBlocks).filter((e) => !d(e)), l = t.withContainerWidth(s), p = c.map((e) => X(e, n(e, l))).filter((e) => e !== "").join("\n"); | ||
| f.push(`<mj-column width="${o}"> | ||
| ${p === "" ? "<mj-text> </mj-text>" : p} | ||
| </mj-column>`); | ||
| } | ||
| let p = f.join("\n"); | ||
| return `<mj-section${s} padding="${o}"${l}${c}> | ||
| ${e.stackOnMobile === !1 && f.length > 1 ? `<mj-group> | ||
| ${p} | ||
| </mj-group>` : p} | ||
| </mj-section>`; | ||
| } | ||
| function ae(e, t) { | ||
| return t ? e : e.filter((e) => e.type !== "html"); | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/video.ts | ||
| function oe(e, t) { | ||
| if (t) return t; | ||
| if (!e) return null; | ||
| for (let t of [/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/, /youtube\.com\/shorts\/([a-zA-Z0-9_-]{11})/]) { | ||
| let n = e.match(t); | ||
| if (n) return `https://img.youtube.com/vi/${n[1]}/maxresdefault.jpg`; | ||
| } | ||
| let n = e.match(/vimeo\.com\/(?:video\/)?(\d+)/); | ||
| return n ? `https://vumbnail.com/${n[1]}.jpg` : null; | ||
| } | ||
| function se(e, t) { | ||
| if (D(e)) return ""; | ||
| let n = oe(e.url, e.thumbnailUrl); | ||
| if (!n) return ""; | ||
| let r = T(e.styles.padding), i = E(e.styles.backgroundColor, "container"), a = e.width === "full" ? t.containerWidth + "px" : e.width + "px", o = O(e); | ||
| return `<mj-image | ||
| src="${b(n)}" | ||
| alt="${b(e.alt)}" | ||
| width="${a}" | ||
| align="${e.align}" | ||
| padding="${r}" | ||
| href="${b(e.url)}" | ||
| target="_blank" | ||
| rel="noopener"${i}${o} | ||
| />`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/index.ts | ||
| function Z(a, m) { | ||
| return d(a) ? ie(a, m, Z) : n(a) ? A(a, m) : e(a) ? te(a, m) : l(a) ? ne(a, m) : c(a) ? M(a, m) : i(a) ? F(a, m) : u(a) ? I(a, m) : s(a) ? L(a, m) : r(a) ? R(a, m) : p(a) ? z(a, m) : f(a) ? U(a, m) : o(a) ? se(a, m) : t(a) ? re(a, m) : ""; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/index.ts | ||
| async function ce(e, t) { | ||
| let n = t?.customFonts ?? [], r = t?.defaultFallbackFont ?? "Arial, sans-serif", i = t?.allowHtmlBlocks ?? !0, a = fe(t?.socialIconsBaseUrl ?? m), o = await he(e, t?.renderCustomBlock), s = ge(e, t?.getCustomBlockStylesheet), c = new g(e.settings.width, n, r, i, o, a), l = Q(e.blocks, i), u = c.resolveFontFamily(e.settings.fontFamily), d = e.settings.backgroundColor, f = e.settings.textColor ? ` color="${b(e.settings.textColor)}"` : "", p = e.settings.linkColor ? x(e.settings.linkColor) : "inherit", h = e.settings.linkUnderline ? "underline" : "none", _ = l.map((e) => le(e, c)).filter((e) => e !== "").join("\n"), v = me(n), y = pe(e.settings.preheaderText); | ||
| return `<mjml lang="${b(e.settings.locale)}"> | ||
| <mj-head>${y} | ||
| <mj-attributes> | ||
| <mj-all font-family="${u}" /> | ||
| <mj-text font-size="14px"${f} /> | ||
| <mj-section padding="0" /> | ||
| <mj-column padding="0" /> | ||
| <mj-image fluid-on-mobile="true" /> | ||
| </mj-attributes>${v} | ||
| <mj-style> | ||
| a { color: ${p}; text-decoration: ${h}; } | ||
| @media only screen and (max-width: 480px) { | ||
| .tpl-hide-mobile { display: none !important; mso-hide: all !important; } | ||
| } | ||
| @media only screen and (min-width: 481px) { | ||
| .tpl-hide-desktop { display: none !important; mso-hide: all !important; } | ||
| } | ||
| </mj-style>${_e(s)} | ||
| </mj-head> | ||
| <mj-body width="${c.containerWidth}px" background-color="${d}"> | ||
| ${_} | ||
| </mj-body> | ||
| </mjml>`; | ||
| } | ||
| function le(e, t) { | ||
| if (d(e)) { | ||
| let n = Z(e, t); | ||
| return X(e, e.wrapper && n !== "" ? ue(n, e.wrapper) : n); | ||
| } | ||
| return X(e, de(Z(e, t))); | ||
| } | ||
| function ue(e, t) { | ||
| return `<mj-wrapper${E(t.backgroundColor, "native")}${` padding="${t.padding ? T(t.padding) : "0"}"`}${t.borderRadius && t.borderRadius > 0 ? ` border-radius="${t.borderRadius}px"` : ""}> | ||
| ${e} | ||
| </mj-wrapper>`; | ||
| } | ||
| function de(e) { | ||
| return e === "" ? "" : `<mj-section> | ||
| <mj-column> | ||
| ${e} | ||
| </mj-column> | ||
| </mj-section>`; | ||
| } | ||
| function fe(e) { | ||
| return e.endsWith("/") ? e.slice(0, -1) : e; | ||
| } | ||
| function pe(e) { | ||
| if (!e) return ""; | ||
| let t = e.trim(); | ||
| return t === "" ? "" : `\n <mj-preview>${y(t)}</mj-preview>`; | ||
| } | ||
| function me(e) { | ||
| return e.length === 0 ? "" : e.map((e) => `\n <mj-font name="${b(e.name)}" href="${b(e.url)}" />`).join(""); | ||
| } | ||
| function Q(e, t) { | ||
| return t ? e : e.filter((e) => e.type !== "html"); | ||
| } | ||
| async function he(e, t) { | ||
| let n = /* @__PURE__ */ new Map(); | ||
| if (!t) return n; | ||
| let r = []; | ||
| if ($(e.blocks, r), r.length === 0) return n; | ||
| let i = await Promise.all(r.map((e) => t(e))); | ||
| for (let e = 0; e < r.length; e++) n.set(r[e].id, i[e]); | ||
| return n; | ||
| } | ||
| function $(e, n) { | ||
| for (let r of e) { | ||
| if (t(r)) { | ||
| n.push(r); | ||
| continue; | ||
| } | ||
| if (d(r)) for (let e of r.children) $(e, n); | ||
| } | ||
| } | ||
| function ge(e, t) { | ||
| if (!t) return []; | ||
| let n = []; | ||
| if ($(e.blocks, n), n.length === 0) return []; | ||
| let r = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Set(), a = []; | ||
| for (let e of n) { | ||
| if (r.has(e.customType)) continue; | ||
| r.add(e.customType); | ||
| let n = t(e.customType); | ||
| if (!n) continue; | ||
| let o = n.trim(); | ||
| o === "" || i.has(o) || (i.add(o), a.push(o)); | ||
| } | ||
| return a; | ||
| } | ||
| function _e(e) { | ||
| return e.length === 0 ? "" : e.map((e) => `\n <mj-style>\n${e}\n </mj-style>`).join(""); | ||
| } | ||
| //#endregion | ||
| export { m as DEFAULT_SOCIAL_ICONS_BASE_URL, g as RenderContext, S as convertMergeTagsToValues, b as escapeAttr, y as escapeHtml, O as getCssClassAttr, k as getCssClasses, J as getWidthPercentages, Y as getWidthPixels, D as isHiddenOnAll, Z as renderBlock, ce as renderToMjml, T as toPaddingString }; | ||
| //# sourceMappingURL=src-BuC_IHzN.js.map |
Sorry, the diff of this file is too big to display
| 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 c, R as l, Y as u, Z as d, _ as f, c as p, d as m, g as h, h as g, k as _, l as v, m as y, nt as b, p as x, r as S, s as C, u as w, z as T } from "./vue.runtime.esm-bundler-B0xoGkDv.js"; | ||
| import { c as E, o as D, t as O } from "./runtime-dom.esm-bundler-GdMI8WqG.js"; | ||
| import { n as k } from "./upload-BwwE0U6G.js"; | ||
| import { A } from "./dist-BJbqgyPn.js"; | ||
| import { E as j, V as M, t as N, x as P } from "./useEditorCore-CT1slufs.js"; | ||
| import { m as F } from "./dist-DYv_Pttj.js"; | ||
| import { s as I } from "./usePopoverPosition-BO2LNy5n.js"; | ||
| import { A as L, F as R, c as z, r as B, s as V, t as H, u as ee } from "./keys-CHPcp-Nz.js"; | ||
| import { t as U } from "./useI18n-D2L9Mkik.js"; | ||
| import { t as W } from "./createLucideIcon-JWHwTI6N.js"; | ||
| import { t as G } from "./check-BpQDKiPz.js"; | ||
| import { t as K } from "./circle-alert-CgtE46ND.js"; | ||
| import { a as q, c as te, d as ne, f as re, i as ie, 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-PcOPXvLO.js"; | ||
| import { t as me } from "./clock-BzKLLW5L.js"; | ||
| import { t as J } from "./loader-circle-C-Z6kgI6.js"; | ||
| import { t as Y } from "./message-circle-CEm5qckV.js"; | ||
| import { t as X } from "./send-B70aQDrA.js"; | ||
| import { t as Z } from "./sparkles-DVlm1Ipp.js"; | ||
| import { t as he } from "./triangle-alert-Djg6IG8N.js"; | ||
| import { t as ge } from "./_plugin-vue_export-helper-B3ysoDQm.js"; | ||
| import { n as Q } from "./useCloudI18n-BmiygEM4.js"; | ||
| import { d as $ } from "./ColorPicker-s9GHXjZs.js"; | ||
| import { _ as _e, c as ve, f as ye, g as be, h as xe, i as Se, l as Ce, m as we, n as Te, o as Ee, p as De, r as Oe, s as ke, t as Ae, u as je, v as Me, y as Ne } from "./cloud-Du7mnkz2.js"; | ||
| var Pe = W("save", [ | ||
| ["path", { | ||
| d: "M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z", | ||
| key: "1c8476" | ||
| }], | ||
| ["path", { | ||
| d: "M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7", | ||
| key: "1ydtos" | ||
| }], | ||
| ["path", { | ||
| d: "M7 3v4a1 1 0 0 0 1 1h7", | ||
| key: "t51u73" | ||
| }] | ||
| ]); | ||
| //#endregion | ||
| //#region src/cloud/composables/useSnapshotPreview.ts | ||
| function Fe(e) { | ||
| let { authManager: t, editor: n, history: r, conditionPreview: i, autoSave: a, onError: s } = e, c = d(null), l = u(null), f = u(null), p = !1; | ||
| o(() => { | ||
| p = !0; | ||
| }); | ||
| let m = C(() => l.value !== null), h = C(() => c.value?.snapshots.value ?? []), g = C(() => c.value?.isLoading.value ?? !1), _ = C(() => c.value?.isRestoring.value ?? !1); | ||
| function v() { | ||
| n.state.template?.id && !c.value && (c.value = be({ | ||
| authManager: t, | ||
| templateId: n.state.template.id, | ||
| onRestore: y, | ||
| onError: s | ||
| }), c.value.loadSnapshots()); | ||
| } | ||
| function y(e) { | ||
| n.setContent(e.content, !1), r.clear(), i.reset(); | ||
| } | ||
| async function b(e) { | ||
| if (!p) { | ||
| if (l.value) { | ||
| l.value = e, n.setContent(e.content, !1); | ||
| return; | ||
| } | ||
| n.state.isDirty && n.hasTemplate() && (await n.createSnapshot(), p) || (f.value = structuredClone(n.content.value), a?.pause(), l.value = e, n.setContent(e.content, !1)); | ||
| } | ||
| } | ||
| async function x() { | ||
| if (!(!l.value || !c.value)) try { | ||
| if (await c.value.restoreSnapshot(l.value.id), p || (await c.value.loadSnapshots(), p)) return; | ||
| } catch (e) { | ||
| throw !p && f.value && n.setContent(f.value, !1), e; | ||
| } finally { | ||
| p || (l.value = null, f.value = null, a?.resume()); | ||
| } | ||
| } | ||
| function S() { | ||
| !l.value || !f.value || (n.setContent(f.value, !1), l.value = null, f.value = null, a?.resume()); | ||
| } | ||
| async function w() { | ||
| p || c.value && await c.value.loadSnapshots(); | ||
| } | ||
| return { | ||
| snapshotHistoryInstance: c, | ||
| previewingSnapshot: l, | ||
| contentBeforePreview: f, | ||
| isPreviewingSnapshot: m, | ||
| snapshotHistorySnapshots: h, | ||
| snapshotHistoryIsLoading: g, | ||
| snapshotHistoryIsRestoring: _, | ||
| initSnapshotHistory: v, | ||
| handleRestore: y, | ||
| handleSnapshotNavigate: b, | ||
| confirmRestoreSnapshot: x, | ||
| cancelPreview: S, | ||
| loadSnapshotHistory: w | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudPanelState.ts | ||
| function Ie() { | ||
| let e = u(null), t = C({ | ||
| get: () => e.value === "ai-chat", | ||
| set: (t) => e.value = t ? "ai-chat" : null | ||
| }), n = C({ | ||
| get: () => e.value === "scoring", | ||
| set: (t) => e.value = t ? "scoring" : null | ||
| }), r = C({ | ||
| get: () => e.value === "design-reference", | ||
| set: (t) => e.value = t ? "design-reference" : null | ||
| }), i = C({ | ||
| get: () => e.value === "comments", | ||
| set: (t) => e.value = t ? "comments" : null | ||
| }), a = u(!1), o = u(!1), s = u(void 0), c = u(!1), l = u(null), d = C(() => e.value !== null), f = C(() => { | ||
| let t = e.value; | ||
| return t === "ai-chat" || t === "design-reference" || t === "scoring" ? t : null; | ||
| }), p = C(() => c.value || e.value === "ai-chat" || e.value === "design-reference" || e.value === "scoring"); | ||
| function m() { | ||
| c.value = !c.value; | ||
| } | ||
| function h(t) { | ||
| c.value = !1, e.value = e.value === t ? null : t; | ||
| } | ||
| return I(l, () => { | ||
| c.value = !1; | ||
| }), { | ||
| activePanel: e, | ||
| aiChatOpen: t, | ||
| scoringPanelOpen: n, | ||
| designReferenceOpen: r, | ||
| commentsOpen: i, | ||
| testEmailModalOpen: a, | ||
| mediaLibraryOpen: o, | ||
| mediaLibraryAccept: s, | ||
| aiMenuOpen: c, | ||
| aiMenuRef: l, | ||
| rightPanelOpen: d, | ||
| activeAiFeature: f, | ||
| aiButtonActive: p, | ||
| toggleAiMenu: m, | ||
| handleAiFeatureSelect: h | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCollabUndoWarning.ts | ||
| function Le(e) { | ||
| let { isCollaborationEnabled: t, getCollaboratorCount: n, canUndo: r } = e, i = u(!1), a = u(!1), { start: o } = F(() => { | ||
| a.value = !1; | ||
| }, k, { immediate: !1 }); | ||
| function s() { | ||
| i.value || !t.value || n() === 0 || !r.value || (i.value = !0, a.value = !0, o()); | ||
| } | ||
| return { | ||
| collabUndoWarningVisible: a, | ||
| showCollabUndoWarning: s | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudFeatureFlags.ts | ||
| function Re(e) { | ||
| let { planConfigInstance: t, aiConfig: n, editor: r } = e, i = C(() => t.hasFeature("ai_generation") && n.hasAnyMenuFeature.value), a = C(() => t.hasFeature("test_email")), o = C(() => !!r.state.template?.id), s = C(() => t.hasFeature("white_label")), c = C(() => t.config.value?.limits.max_templates ?? null), l = C(() => t.config.value?.template_count ?? 0), d = u(!1), f = u("idle"), p = u(""), { start: m } = F(() => { | ||
| f.value = "idle"; | ||
| }, 3e3, { immediate: !1 }); | ||
| return { | ||
| canUseAiGeneration: i, | ||
| canSendTestEmail: a, | ||
| hasTemplateSaved: o, | ||
| isWhiteLabeled: s, | ||
| templateLimit: c, | ||
| templateCount: l, | ||
| isSaveExporting: d, | ||
| saveStatus: f, | ||
| saveErrorMessage: p, | ||
| startSaveStatusClear: m | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudMediaLibrary.ts | ||
| function ze(e) { | ||
| let { onRequestMedia: t, mediaLibraryOpen: n, mediaLibraryAccept: r, authManager: i, getMediaConfig: a, onError: s } = e, c = null; | ||
| function l(e) { | ||
| return { | ||
| url: e.url, | ||
| alt: e.alt_text || void 0 | ||
| }; | ||
| } | ||
| async function u(e) { | ||
| let i = e?.accept ?? ["images"]; | ||
| if (e?.files?.length) { | ||
| if (t) { | ||
| let n = await t({ | ||
| accept: i, | ||
| files: e.files | ||
| }); | ||
| return n ? l(n) : null; | ||
| } | ||
| return d(e.files[0]); | ||
| } | ||
| if (t) { | ||
| let e = await t({ accept: i }); | ||
| return e ? l(e) : null; | ||
| } | ||
| return c &&= (c(null), null), r.value = i, n.value = !0, new Promise((e) => { | ||
| c = (t) => { | ||
| e(t); | ||
| }; | ||
| }); | ||
| } | ||
| async function d(e) { | ||
| let t = f(e); | ||
| if (t) return s?.(t), null; | ||
| try { | ||
| let { MediaApiClient: t } = await import("@templatical/media-library"); | ||
| return l(await new t(i).uploadMedia(e)); | ||
| } catch (e) { | ||
| return s?.(e), null; | ||
| } | ||
| } | ||
| function f(e) { | ||
| let t = a?.() ?? null; | ||
| if (!t) return null; | ||
| let n = t.categories?.images?.mime_types ?? []; | ||
| return n.length > 0 && !n.includes(e.type) ? /* @__PURE__ */ Error(`Unsupported image type: ${e.type || "unknown"}. Accepted: ${n.join(", ")}.`) : t.max_file_size > 0 && e.size > t.max_file_size ? /* @__PURE__ */ Error(`Image is too large (${e.size} bytes). Maximum allowed is ${t.max_file_size} bytes.`) : null; | ||
| } | ||
| function p(e) { | ||
| n.value = !1, c?.(l(e)), c = null; | ||
| } | ||
| function m() { | ||
| n.value = !1, c?.(null), c = null; | ||
| } | ||
| return o(() => { | ||
| c &&= (c(null), null); | ||
| }), { | ||
| handleRequestMedia: u, | ||
| handleMediaSelect: p, | ||
| handleMediaLibraryClose: m | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudInitialization.ts | ||
| function Be(e) { | ||
| let { config: t, translations: n, fontsManager: r, emit: i, getCommentsSidebar: o } = e, s = u(!0), c = u(!1), l = u(null), d = !1, f = { value: null }, p = null, m = null, h = new Ae({ | ||
| ...t.auth, | ||
| onError: t.onError | ||
| }), g = xe({ | ||
| authManager: h, | ||
| onError: t.onError | ||
| }), v = u(/* @__PURE__ */ new Map()), y = ye({ | ||
| authManager: h, | ||
| defaultFontFamily: t.fonts?.defaultFont, | ||
| templateDefaults: t.templateDefaults, | ||
| onError: t.onError, | ||
| lockedBlocks: v | ||
| }), b = Ne({ | ||
| authManager: h, | ||
| onError: t.onError | ||
| }); | ||
| t.mcp?.enabled && we({ | ||
| editor: y, | ||
| channel: b.channel, | ||
| onOperation: t.mcp.onOperation | ||
| }); | ||
| let x = null; | ||
| t.collaboration?.enabled && (x = ke({ | ||
| authManager: h, | ||
| editor: y, | ||
| channel: b.channel, | ||
| onError: t.onError, | ||
| onCollaboratorJoined: t.collaboration.onCollaboratorJoined, | ||
| onCollaboratorLeft: t.collaboration.onCollaboratorLeft, | ||
| onBlockLocked: t.collaboration.onBlockLocked, | ||
| onBlockUnlocked: t.collaboration.onBlockUnlocked | ||
| }), a(() => x.lockedBlocks.value, (e) => { | ||
| v.value = e; | ||
| }, { immediate: !0 }), ve(y, x)); | ||
| let S = C(() => !!t.collaboration?.enabled && g.hasFeature("collaboration")), w = N({ | ||
| editor: y, | ||
| config: { | ||
| uiTheme: t.uiTheme, | ||
| theme: void 0, | ||
| blockDefaults: t.blockDefaults, | ||
| templateDefaults: t.templateDefaults, | ||
| customBlocks: [], | ||
| paletteBlocks: t.paletteBlocks, | ||
| htmlBlockPreview: t.htmlBlockPreview, | ||
| colors: t.colors, | ||
| mergeTags: t.mergeTags, | ||
| logicTags: t.logicTags, | ||
| displayConditions: t.displayConditions, | ||
| onRequestMedia: null, | ||
| lint: re(t), | ||
| onSave: () => { | ||
| f.value?.().catch((e) => { | ||
| t.onError?.(e); | ||
| }); | ||
| } | ||
| }, | ||
| translations: n, | ||
| fontsManager: r, | ||
| historyOptions: x ? { isRemoteOperation: () => x._isProcessingRemoteOperation() } : void 0, | ||
| autoSaveOptions: { | ||
| onChange: async () => { | ||
| y.hasTemplate() && (await y.createSnapshot(), p?.snapshotHistoryInstance.value?.loadSnapshots()); | ||
| }, | ||
| debounce: t.autoSaveDebounce ?? 5e3, | ||
| enabled: () => t.autoSave !== !1 && g.hasFeature("auto_save") | ||
| }, | ||
| themeExtraStyles: () => ({ "--tpl-drop-text": `"${n.canvas.dropHere}"` }), | ||
| keyboardOptions: { onBeforeUndo: () => m?.showCollabUndoWarning() }, | ||
| editorRoot: e.editorRoot, | ||
| containerEl: e.containerEl | ||
| }), T = Le({ | ||
| isCollaborationEnabled: S, | ||
| getCollaboratorCount: () => x?.collaborators.value.length ?? 0, | ||
| canUndo: w.history.canUndo | ||
| }); | ||
| m = T; | ||
| let E = Fe({ | ||
| authManager: h, | ||
| editor: y, | ||
| history: w.history, | ||
| conditionPreview: w.conditionPreview, | ||
| autoSave: w.autoSave, | ||
| onError: t.onError | ||
| }); | ||
| p = E; | ||
| let D = Ie(), O = Ee(t.ai), k = Re({ | ||
| planConfigInstance: g, | ||
| aiConfig: O, | ||
| editor: y | ||
| }), A = ze({ | ||
| onRequestMedia: t.onRequestMedia, | ||
| mediaLibraryOpen: D.mediaLibraryOpen, | ||
| mediaLibraryAccept: D.mediaLibraryAccept, | ||
| authManager: h, | ||
| getMediaConfig: () => g.config.value?.media ?? null, | ||
| onError: t.onError | ||
| }); | ||
| j({ | ||
| onBlockMove: y.moveBlock, | ||
| onBlockAdd: y.addBlock | ||
| }); | ||
| let P = De({ | ||
| authManager: h, | ||
| getFontsConfig: () => t.fonts, | ||
| canUseCustomFonts: () => g.hasFeature("custom_fonts") | ||
| }), F = Me({ | ||
| authManager: h, | ||
| getTemplateId: () => y.state.template?.id ?? null, | ||
| save: () => y.save(), | ||
| exportHtml: (e) => P.exportHtml(e), | ||
| onError: t.onError, | ||
| isAuthReady: c, | ||
| onBeforeTestEmail: t.onBeforeTestEmail | ||
| }), I = je({ | ||
| authManager: h, | ||
| getTemplateId: () => y.state.template?.id ?? null, | ||
| getSocketId: () => b.getSocketId(), | ||
| onComment: t.onComment, | ||
| onError: t.onError, | ||
| isAuthReady: c, | ||
| hasCommentingFeature: () => t.commenting !== !1 && g.hasFeature("commenting") | ||
| }); | ||
| Ce({ | ||
| comments: I, | ||
| channel: b.channel | ||
| }); | ||
| let z = typeof t.savedBlocks == "object" && t.savedBlocks !== null ? t.savedBlocks : null, U = le({ | ||
| provider: z ?? Te(h), | ||
| editor: y, | ||
| onError: t.onError, | ||
| isAvailable: z ? () => !0 : () => t.savedBlocks !== !1 && g.hasFeature("saved_modules") | ||
| }), W = _e({ | ||
| authManager: h, | ||
| getTemplateId: () => y.state.template?.id ?? null | ||
| }); | ||
| function G(e) { | ||
| D.commentsOpen.value = !0, queueMicrotask(() => { | ||
| o()?.filterByBlock(e); | ||
| }); | ||
| } | ||
| _(L, A.handleRequestMedia), _(B, h), _(H, O), _(ee, I), _(R, W), _(V, { | ||
| plan: g, | ||
| ai: O, | ||
| comments: { | ||
| getBlockCount: (e) => I.commentCountByBlock.value.get(e) ?? 0, | ||
| openForBlock: G | ||
| }, | ||
| savedBlocks: U.capability | ||
| }); | ||
| function K(e) { | ||
| g.hasFeature("theme_customization") && (w.themeOverrides.value = e); | ||
| } | ||
| function q(e) { | ||
| y.setUiTheme(e); | ||
| } | ||
| async function te() { | ||
| s.value = !0, l.value = null; | ||
| try { | ||
| if (await h.initialize(), d) return; | ||
| c.value = !0; | ||
| let e = await Oe({ authManager: h }); | ||
| if (d) return; | ||
| if (!e.api.ok) throw Error("Health check failed: API is not reachable"); | ||
| if (!e.auth.ok) throw Error(`Health check failed: authentication error${e.auth.error ? ` - ${e.auth.error}` : ""}`); | ||
| if (e.websocket.ok || M.warn("WebSocket health check failed:", e.websocket.error ?? "unknown error", "-- real-time features will be disabled."), await g.fetchConfig(), d) return; | ||
| r.setCustomFontsEnabled(g.hasFeature("custom_fonts")), t.customBlocks?.length && g.hasFeature("custom_blocks") && w.registerCustomBlocks(t.customBlocks), t.theme && g.hasFeature("theme_customization") && (w.themeOverrides.value = t.theme), i("ready"); | ||
| } catch (e) { | ||
| if (d) return; | ||
| let n = e instanceof Error ? e : Error("Initialization failed", { cause: e }); | ||
| l.value = n, t.onError?.(n); | ||
| } finally { | ||
| d || (s.value = !1); | ||
| } | ||
| } | ||
| function ne() { | ||
| d = !0, r.cleanupFontLinks(), b.disconnect(), w.destroy(), t.onUnmount?.(); | ||
| } | ||
| return { | ||
| isInitializing: s, | ||
| isAuthReady: c, | ||
| initError: l, | ||
| isDestroyed: () => d, | ||
| authManager: h, | ||
| planConfigInstance: g, | ||
| websocket: b, | ||
| collaboration: x, | ||
| isCollaborationEnabled: S, | ||
| editor: y, | ||
| core: w, | ||
| aiConfig: O, | ||
| featureFlags: k, | ||
| mediaLib: A, | ||
| exporter: P, | ||
| testEmail: F, | ||
| commentsInstance: I, | ||
| savedBlocks: U, | ||
| scoringInstance: W, | ||
| panelState: D, | ||
| snapshotPreview: E, | ||
| collabWarning: T, | ||
| onSaveHook: f, | ||
| initialize: te, | ||
| destroy: ne, | ||
| setThemeOverrides: K, | ||
| setUiTheme: q, | ||
| openCommentsForBlock: G | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/utils/preRenderCustomBlocks.ts | ||
| async function Ve(e, t) { | ||
| let n = async (e) => { | ||
| if (A(e)) { | ||
| let n = e; | ||
| try { | ||
| n.renderedHtml = await t.renderCustomBlock(n); | ||
| } catch { | ||
| n.renderedHtml = `<!-- Custom block render error: ${n.customType} -->`; | ||
| } | ||
| } | ||
| if (e.type === "section" && "children" in e) { | ||
| let t = e; | ||
| for (let e of t.children) for (let t of e) await n(t); | ||
| } | ||
| }; | ||
| for (let t of e.blocks) await n(t); | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudLifecycle.ts | ||
| function He(e) { | ||
| let { config: t, editor: n, websocket: r, planConfigInstance: i, snapshotPreview: a, core: o, exporter: s, featureFlags: c, isDestroyed: l } = e; | ||
| function u() { | ||
| return Se(i.config.value.websocket); | ||
| } | ||
| async function d(e) { | ||
| let i = await n.create(e); | ||
| return l() ? i : (t.onCreate?.(i), a.initSnapshotHistory(), r.connect(i.id, u()), i); | ||
| } | ||
| async function f(e) { | ||
| let i = await n.load(e); | ||
| return l() ? i : (t.onLoad?.(i), a.initSnapshotHistory(), r.connect(i.id, u()), i); | ||
| } | ||
| async function p() { | ||
| c.isSaveExporting.value = !0, c.saveStatus.value = "idle"; | ||
| try { | ||
| if (await Ve(n.content.value, o.registry), l()) throw Error("Component unmounted during save"); | ||
| let e = await n.save(); | ||
| if (l()) throw Error("Component unmounted during save"); | ||
| a.initSnapshotHistory(), a.snapshotHistoryInstance.value?.loadSnapshots(); | ||
| let r = await s.exportHtml(e.id); | ||
| if (l()) throw Error("Component unmounted during save"); | ||
| let i = { | ||
| templateId: e.id, | ||
| html: r.html, | ||
| mjml: r.mjml, | ||
| content: e.content | ||
| }; | ||
| return t.onSave?.(i), c.saveStatus.value = "saved", c.startSaveStatusClear(), i; | ||
| } catch (e) { | ||
| throw l() || (c.saveStatus.value = "error", c.saveErrorMessage.value = e instanceof Error ? e.message : "Save failed"), e; | ||
| } finally { | ||
| l() || (c.isSaveExporting.value = !1); | ||
| } | ||
| } | ||
| return { | ||
| createTemplate: d, | ||
| loadTemplate: f, | ||
| saveTemplate: p | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudSaveGate.ts | ||
| function Ue(e) { | ||
| let t = u(!1), n = null, r = C(() => e.planConfig.value?.accessibility?.blockOnError === !0), i = C(() => r.value ? e.issues.value.filter((e) => e.severity === "error") : []), a = C(() => i.value.length > 0); | ||
| async function o(e) { | ||
| return a.value ? (n = e, t.value = !0, !1) : (await e(), !0); | ||
| } | ||
| async function s() { | ||
| let e = n; | ||
| n = null, t.value = !1, e && await e(); | ||
| } | ||
| function c() { | ||
| n = null, t.value = !1; | ||
| } | ||
| return { | ||
| shouldBlock: a, | ||
| blockingIssues: i, | ||
| modalOpen: t, | ||
| tryRunSave: o, | ||
| confirmAndSave: s, | ||
| cancel: c | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/components/CloudSaveGateModal.vue?vue&type=script&setup=true&lang.ts | ||
| var We = ["aria-label"], Ge = { class: "tpl:flex tpl:max-h-[80vh] tpl:w-full tpl:max-w-md tpl:flex-col tpl:gap-4 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:p-5 tpl:shadow-[var(--tpl-shadow-md)]" }, Ke = { class: "tpl:flex tpl:items-center tpl:gap-2" }, qe = { class: "tpl:m-0 tpl:text-base tpl:font-semibold tpl:text-[var(--tpl-text)]" }, Je = { class: "tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-muted)]" }, Ye = { class: "tpl:m-0 tpl:flex tpl:max-h-64 tpl:list-none tpl:flex-col tpl:gap-1.5 tpl:overflow-y-auto tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-2" }, Xe = { class: "tpl:text-xs tpl:text-[var(--tpl-text)]" }, Ze = { class: "tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]" }, Qe = { class: "tpl:flex tpl:justify-end tpl:gap-2" }, $e = /* @__PURE__ */ f({ | ||
| __name: "CloudSaveGateModal", | ||
| props: { | ||
| open: { type: Boolean }, | ||
| issues: {} | ||
| }, | ||
| emits: ["cancel", "confirm"], | ||
| setup(t, { emit: n }) { | ||
| let r = n, { t: i } = Q(); | ||
| return (n, a) => (s(), v(O, { | ||
| "enter-active-class": "tpl:transition-opacity tpl:duration-150", | ||
| "leave-active-class": "tpl:transition-opacity tpl:duration-150", | ||
| "enter-from-class": "tpl:opacity-0", | ||
| "leave-to-class": "tpl:opacity-0" | ||
| }, { | ||
| default: l(() => [t.open ? (s(), m("div", { | ||
| key: 0, | ||
| role: "dialog", | ||
| "aria-modal": "true", | ||
| "aria-label": b(i).saveGate.title, | ||
| class: "tpl:fixed tpl:inset-0 tpl:z-50 tpl:flex tpl:items-center tpl:justify-center tpl:bg-black/40 tpl:p-6", | ||
| onClick: a[2] ||= E((e) => r("cancel"), ["self"]) | ||
| }, [p("div", Ge, [ | ||
| p("header", Ke, [g(b(he), { | ||
| size: 18, | ||
| "stroke-width": 2, | ||
| class: "tpl:text-[var(--tpl-warning)]" | ||
| }), p("h2", qe, c(b(i).saveGate.title), 1)]), | ||
| p("p", Je, c(b(i).saveGate.body), 1), | ||
| p("ul", Ye, [(s(!0), m(S, null, e(t.issues, (e) => (s(), m("li", { | ||
| key: `${e.ruleId}-${e.blockId ?? "template"}`, | ||
| class: "tpl:flex tpl:flex-col tpl:gap-0.5 tpl:rounded tpl:px-2 tpl:py-1.5" | ||
| }, [p("span", Xe, c(e.message), 1), p("span", Ze, c(e.ruleId), 1)]))), 128))]), | ||
| p("footer", Qe, [p("button", { | ||
| type: "button", | ||
| class: "tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]", | ||
| onClick: a[0] ||= (e) => r("cancel") | ||
| }, c(b(i).saveGate.cancel), 1), p("button", { | ||
| type: "button", | ||
| class: "tpl:rounded-md tpl:bg-[var(--tpl-danger)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-white", | ||
| onClick: a[1] ||= (e) => r("confirm") | ||
| }, c(b(i).saveGate.confirm), 1)]) | ||
| ])], 8, We)) : w("", !0)]), | ||
| _: 1 | ||
| })); | ||
| } | ||
| }), et = { | ||
| class: "tpl-header tpl:absolute tpl:top-0 tpl:right-0 tpl:left-0 tpl:z-50 tpl:grid tpl:h-14 tpl:grid-cols-[1fr_auto_1fr] tpl:items-center tpl:px-4", | ||
| style: { | ||
| "background-color": "color-mix(in srgb, var(--tpl-bg) 80%, transparent)", | ||
| "backdrop-filter": "blur(12px)", | ||
| "-webkit-backdrop-filter": "blur(12px)", | ||
| "box-shadow": "var(--tpl-shadow-md)", | ||
| "border-bottom": "1px solid var(--tpl-border)" | ||
| } | ||
| }, tt = { class: "tpl-header-left tpl:flex tpl:min-w-[200px] tpl:items-center tpl:gap-3" }, nt = { | ||
| key: 0, | ||
| class: "tpl:text-xs tpl:opacity-60 tpl:text-[var(--tpl-text-muted)]" | ||
| }, rt = { class: "tpl-header-center tpl:flex tpl:items-center tpl:justify-center tpl:gap-10" }, it = { class: "tpl-header-right tpl:flex tpl:min-w-[200px] tpl:items-center tpl:justify-end tpl:gap-3" }, at = ["data-tooltip"], ot = { | ||
| key: 1, | ||
| "aria-live": "polite", | ||
| class: "tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-success)]" | ||
| }, st = { | ||
| key: 2, | ||
| "aria-live": "polite", | ||
| class: "tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-text-muted)]" | ||
| }, ct = ["aria-label", "aria-expanded"], lt = { | ||
| key: 0, | ||
| class: "tpl:inline-flex tpl:size-4.5 tpl:items-center tpl:justify-center tpl:rounded-full tpl:text-[10px] tpl:font-semibold tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]" | ||
| }, ut = ["aria-expanded"], dt = { | ||
| key: 0, | ||
| class: "tpl:absolute tpl:right-0 tpl:top-full tpl:z-50 tpl:mt-1 tpl:origin-top-right" | ||
| }, ft = ["disabled"], pt = ["disabled"], mt = /* @__PURE__ */ f({ | ||
| __name: "CloudHeader", | ||
| props: { | ||
| editor: {}, | ||
| core: {}, | ||
| featureFlags: {}, | ||
| panelState: {}, | ||
| snapshotPreview: {}, | ||
| commentsInstance: {}, | ||
| testEmail: {}, | ||
| websocket: {}, | ||
| collaboration: {}, | ||
| isCollaborationEnabled: { type: Boolean }, | ||
| isSaveDisabled: { type: Boolean }, | ||
| isSaving: { type: Boolean } | ||
| }, | ||
| emits: ["save"], | ||
| setup(e) { | ||
| let t = h(() => import("./CollaboratorBar-Bj4NZiie.js")), r = h(() => import("./SnapshotHistory-FtzDq8f0.js")), a = h(() => import("./AiFeatureMenu-CcqQDHzJ.js")), { t: o, format: u } = Q(); | ||
| return (d, f) => (s(), m("header", et, [ | ||
| p("div", tt, [e.featureFlags.templateLimit.value === null ? w("", !0) : (s(), m("span", nt, c(b(u)(b(o).header.templatesUsed, { | ||
| used: e.featureFlags.templateCount.value, | ||
| max: e.featureFlags.templateLimit.value | ||
| })), 1))]), | ||
| p("div", rt, [ | ||
| g(ce, { | ||
| viewport: e.editor.state.viewport, | ||
| onChange: e.editor.setViewport | ||
| }, null, 8, ["viewport", "onChange"]), | ||
| g(ie, { | ||
| "dark-mode": e.editor.state.darkMode, | ||
| onChange: e.editor.setDarkMode | ||
| }, null, 8, ["dark-mode", "onChange"]), | ||
| g(q, { | ||
| "preview-mode": e.editor.state.previewMode, | ||
| onChange: e.editor.setPreviewMode | ||
| }, null, 8, ["preview-mode", "onChange"]), | ||
| e.collaboration && e.isCollaborationEnabled ? (s(), v(b(t), { | ||
| key: 0, | ||
| collaborators: e.collaboration.collaborators.value, | ||
| "is-connected": e.websocket.isConnected.value | ||
| }, null, 8, ["collaborators", "is-connected"])) : w("", !0), | ||
| e.snapshotPreview.snapshotHistoryInstance.value ? (s(), v(b(r), { | ||
| key: 1, | ||
| snapshots: e.snapshotPreview.snapshotHistorySnapshots.value, | ||
| "is-loading": e.snapshotPreview.snapshotHistoryIsLoading.value, | ||
| "is-restoring": e.snapshotPreview.snapshotHistoryIsRestoring.value, | ||
| onLoad: e.snapshotPreview.loadSnapshotHistory, | ||
| onNavigate: e.snapshotPreview.handleSnapshotNavigate | ||
| }, null, 8, [ | ||
| "snapshots", | ||
| "is-loading", | ||
| "is-restoring", | ||
| "onLoad", | ||
| "onNavigate" | ||
| ])) : w("", !0) | ||
| ]), | ||
| p("div", it, [ | ||
| e.featureFlags.saveStatus.value === "error" ? (s(), m("div", { | ||
| key: 0, | ||
| "aria-live": "assertive", | ||
| class: "tpl-tooltip tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-danger)]", | ||
| "data-tooltip": e.featureFlags.saveErrorMessage.value | ||
| }, [g(b(K), { | ||
| size: 12, | ||
| "stroke-width": 2.5 | ||
| }), y(" " + c(b(o).header.saveFailed), 1)], 8, at)) : e.featureFlags.saveStatus.value === "saved" ? (s(), m("div", ot, [g(b(G), { | ||
| size: 12, | ||
| "stroke-width": 2.5 | ||
| }), y(" " + c(b(o).header.saved), 1)])) : e.editor.state.isDirty ? (s(), m("div", st, [f[4] ||= p("span", { class: "tpl-pulse tpl:size-1.5 tpl:rounded-full tpl:bg-[var(--tpl-primary)]" }, null, -1), y(" " + c(b(o).header.unsaved), 1)])) : w("", !0), | ||
| e.commentsInstance.isEnabled.value && e.featureFlags.hasTemplateSaved.value ? (s(), m("button", { | ||
| key: 3, | ||
| "aria-label": e.commentsInstance.unresolvedCount.value > 0 ? `${b(o).comments.button} (${e.commentsInstance.unresolvedCount.value})` : b(o).comments.button, | ||
| "aria-expanded": e.panelState.commentsOpen.value, | ||
| class: i(b($)), | ||
| style: n({ | ||
| backgroundColor: e.panelState.commentsOpen.value ? "var(--tpl-primary)" : "transparent", | ||
| color: e.panelState.commentsOpen.value ? "var(--tpl-bg)" : "var(--tpl-primary)", | ||
| borderColor: "var(--tpl-primary)" | ||
| }), | ||
| onClick: f[0] ||= (t) => e.panelState.commentsOpen.value = !e.panelState.commentsOpen.value | ||
| }, [ | ||
| g(b(Y), { | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| }), | ||
| y(" " + c(b(o).comments.button) + " ", 1), | ||
| e.commentsInstance.unresolvedCount.value > 0 && !e.panelState.commentsOpen.value ? (s(), m("span", lt, c(e.commentsInstance.unresolvedCount.value), 1)) : w("", !0) | ||
| ], 14, ct)) : w("", !0), | ||
| e.featureFlags.canUseAiGeneration.value && e.featureFlags.hasTemplateSaved.value ? (s(), m("div", { | ||
| key: 4, | ||
| ref: (t) => e.panelState.aiMenuRef.value = t, | ||
| class: "tpl:relative" | ||
| }, [p("button", { | ||
| "aria-expanded": e.panelState.aiMenuOpen.value, | ||
| class: i(["tpl-ai-btn tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-4 tpl:py-2 tpl:text-sm tpl:font-semibold tpl:whitespace-nowrap tpl:transition-all tpl:duration-200", e.panelState.aiButtonActive.value ? "tpl-ai-btn--active" : "tpl-ai-btn--idle"]), | ||
| onClick: f[1] ||= E((...t) => e.panelState.toggleAiMenu && e.panelState.toggleAiMenu(...t), ["stop"]) | ||
| }, [g(b(Z), { | ||
| size: 16, | ||
| "stroke-width": 2, | ||
| class: "tpl-ai-btn-icon" | ||
| }), y(" " + c(b(o).aiChat.button), 1)], 10, ut), g(O, { | ||
| "enter-active-class": "tpl:transition-all tpl:duration-150 tpl:ease-out", | ||
| "enter-from-class": "tpl:scale-95 tpl:opacity-0", | ||
| "enter-to-class": "tpl:scale-100 tpl:opacity-100", | ||
| "leave-active-class": "tpl:transition-all tpl:duration-100 tpl:ease-in", | ||
| "leave-from-class": "tpl:scale-100 tpl:opacity-100", | ||
| "leave-to-class": "tpl:scale-95 tpl:opacity-0" | ||
| }, { | ||
| default: l(() => [e.panelState.aiMenuOpen.value ? (s(), m("div", dt, [g(b(a), { | ||
| "active-feature": e.panelState.activeAiFeature.value, | ||
| onSelect: e.panelState.handleAiFeatureSelect | ||
| }, null, 8, ["active-feature", "onSelect"])])) : w("", !0)]), | ||
| _: 1 | ||
| })], 512)) : w("", !0), | ||
| e.testEmail.isEnabled.value && e.featureFlags.canSendTestEmail.value ? (s(), m("button", { | ||
| key: 5, | ||
| class: i(b($)), | ||
| style: { | ||
| "background-color": "transparent", | ||
| color: "var(--tpl-primary)", | ||
| "border-color": "var(--tpl-primary)" | ||
| }, | ||
| disabled: e.testEmail.isSending.value || !e.featureFlags.hasTemplateSaved.value, | ||
| onClick: f[2] ||= (t) => e.panelState.testEmailModalOpen.value = !0 | ||
| }, [e.testEmail.isSending.value ? (s(), v(b(J), { | ||
| key: 1, | ||
| class: "tpl-spinner", | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| })) : (s(), v(b(X), { | ||
| key: 0, | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| })), y(" " + c(b(o).testEmail.button), 1)], 10, ft)) : w("", !0), | ||
| p("button", { | ||
| class: i(b($)), | ||
| style: { | ||
| "background-color": "transparent", | ||
| color: "var(--tpl-primary)", | ||
| "border-color": "var(--tpl-primary)" | ||
| }, | ||
| disabled: e.isSaveDisabled, | ||
| onClick: f[3] ||= (e) => d.$emit("save") | ||
| }, [e.isSaving ? (s(), v(b(J), { | ||
| key: 1, | ||
| class: "tpl-spinner", | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| })) : (s(), v(b(Pe), { | ||
| key: 0, | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| })), y(" " + c(e.isSaving ? b(o).header.saving : b(o).header.save), 1)], 10, pt) | ||
| ]) | ||
| ])); | ||
| } | ||
| }), ht = /* @__PURE__ */ f({ | ||
| __name: "CloudPanels", | ||
| props: { | ||
| config: {}, | ||
| editor: {}, | ||
| core: {}, | ||
| panelState: {}, | ||
| planConfigInstance: {}, | ||
| testEmail: {}, | ||
| mediaLib: {} | ||
| }, | ||
| emits: ["send-test-email"], | ||
| setup(e, { expose: t, emit: n }) { | ||
| let r = h(() => import("./AiChatSidebar-K_AehWc-.js")), i = h(() => import("./CommentsSidebar-DvNEdugN.js")), a = h(() => import("./DesignReferenceSidebar-DNmK3dxp.js")), o = h(() => import("./TemplateScoringPanel-ifGGGWs5.js")), c = h(() => import("./TestEmailModal-DL2O1H8M.js")), l = h(async () => { | ||
| try { | ||
| return (await import("@templatical/media-library")).MediaLibraryModal; | ||
| } catch { | ||
| throw Error("[Templatical] Cloud media library requires the optional peer dependency '@templatical/media-library'. Please install it."); | ||
| } | ||
| }), d = n; | ||
| function f(e, t, n) { | ||
| t.history.record(), n.setContent(e), t.conditionPreview.reset(); | ||
| } | ||
| let p = u(null); | ||
| function _(e) { | ||
| p.value?.filterByBlock(e); | ||
| } | ||
| return t({ filterCommentsByBlock: _ }), (t, n) => (s(), m(S, null, [ | ||
| g(b(r), { | ||
| visible: e.panelState.aiChatOpen.value, | ||
| "on-apply": (t) => f(t, e.core, e.editor), | ||
| onClose: n[0] ||= (t) => e.panelState.aiChatOpen.value = !1 | ||
| }, null, 8, ["visible", "on-apply"]), | ||
| g(b(o), { | ||
| visible: e.panelState.scoringPanelOpen.value, | ||
| onClose: n[1] ||= (t) => e.panelState.scoringPanelOpen.value = !1 | ||
| }, null, 8, ["visible"]), | ||
| g(b(a), { | ||
| visible: e.panelState.designReferenceOpen.value, | ||
| "has-existing-blocks": e.editor.content.value.blocks.length > 0, | ||
| onClose: n[2] ||= (t) => e.panelState.designReferenceOpen.value = !1, | ||
| onApply: n[3] ||= (t) => f(t, e.core, e.editor) | ||
| }, null, 8, ["visible", "has-existing-blocks"]), | ||
| g(b(i), { | ||
| ref_key: "commentsSidebar", | ||
| ref: p, | ||
| visible: e.panelState.commentsOpen.value, | ||
| onClose: n[4] ||= (t) => e.panelState.commentsOpen.value = !1 | ||
| }, null, 8, ["visible"]), | ||
| g(b(c), { | ||
| visible: e.panelState.testEmailModalOpen.value, | ||
| "allowed-emails": e.testEmail.allowedEmails.value, | ||
| "is-sending": e.testEmail.isSending.value, | ||
| error: e.testEmail.error.value, | ||
| onSend: n[5] ||= (e) => d("send-test-email", e), | ||
| onClose: n[6] ||= (t) => e.panelState.testEmailModalOpen.value = !1 | ||
| }, null, 8, [ | ||
| "visible", | ||
| "allowed-emails", | ||
| "is-sending", | ||
| "error" | ||
| ]), | ||
| g(b(l), { | ||
| visible: e.panelState.mediaLibraryOpen.value, | ||
| accept: e.panelState.mediaLibraryAccept.value, | ||
| "popover-target": e.core.popoverRoot.value, | ||
| onSelect: e.mediaLib.handleMediaSelect, | ||
| onClose: e.mediaLib.handleMediaLibraryClose | ||
| }, null, 8, [ | ||
| "visible", | ||
| "accept", | ||
| "popover-target", | ||
| "onSelect", | ||
| "onClose" | ||
| ]) | ||
| ], 64)); | ||
| } | ||
| }), gt = { | ||
| key: 0, | ||
| class: "tpl-loading tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:bg-[var(--tpl-bg)]" | ||
| }, _t = { class: "tpl:flex tpl:flex-1 tpl:overflow-hidden" }, vt = { class: "tpl:flex tpl:w-12 tpl:shrink-0 tpl:flex-col tpl:items-center tpl:gap-4 tpl:py-5 tpl:border-r tpl:border-[var(--tpl-border)]" }, yt = /* @__PURE__ */ f({ | ||
| __name: "CloudLoadingOverlay", | ||
| props: { visible: { type: Boolean } }, | ||
| setup(t) { | ||
| return (n, r) => t.visible ? (s(), m("div", gt, [r[1] ||= x("<div class=\"tpl:flex tpl:h-14 tpl:shrink-0 tpl:items-center tpl:justify-between tpl:px-4 tpl:border-b tpl:border-[var(--tpl-border)]\"><div class=\"tpl-shimmer tpl:h-5 tpl:w-28 tpl:rounded-[var(--tpl-radius-sm)]\"></div><div class=\"tpl:flex tpl:gap-3\"><div class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"></div><div class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"></div></div></div>", 1), p("div", _t, [p("div", vt, [(s(), m(S, null, e(5, (e) => p("div", { | ||
| key: e, | ||
| class: "tpl-shimmer tpl:size-7 tpl:rounded-[var(--tpl-radius-sm)]" | ||
| })), 64))]), r[0] ||= x("<div class=\"tpl:flex tpl:flex-1 tpl:items-start tpl:justify-center tpl:overflow-auto tpl:p-8 tpl:bg-[var(--tpl-canvas-bg)]\"><div class=\"tpl:w-full tpl:max-w-[600px] tpl:rounded-[var(--tpl-radius)] tpl:p-6 tpl:bg-[var(--tpl-bg)] tpl:shadow-[var(--tpl-shadow-sm)]\"><div class=\"tpl:space-y-2 tpl:py-4\"><div class=\"tpl-shimmer tpl:h-3 tpl:w-3/4 tpl:rounded\"></div><div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div><div class=\"tpl-shimmer tpl:h-3 tpl:w-5/6 tpl:rounded\"></div></div><div class=\"tpl:py-4\"><div class=\"tpl-shimmer tpl:h-44 tpl:w-full tpl:rounded-[var(--tpl-radius-sm)]\"></div></div><div class=\"tpl:space-y-2 tpl:py-4\"><div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div><div class=\"tpl-shimmer tpl:h-3 tpl:w-2/3 tpl:rounded\"></div></div><div class=\"tpl:flex tpl:justify-center tpl:py-4\"><div class=\"tpl-shimmer tpl:h-10 tpl:w-36 tpl:rounded-[var(--tpl-radius-sm)]\"></div></div><div class=\"tpl:space-y-2 tpl:py-4\"><div class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/2 tpl:rounded\"></div><div class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/3 tpl:rounded\"></div></div></div></div><div class=\"tpl:flex tpl:w-[320px] tpl:shrink-0 tpl:flex-col tpl:gap-4 tpl:p-4 tpl:border-l tpl:border-[var(--tpl-border)]\"><div class=\"tpl-shimmer tpl:h-8 tpl:rounded-[var(--tpl-radius-sm)]\"></div><div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div><div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div></div>", 2)])])) : w("", !0); | ||
| } | ||
| }), bt = { | ||
| key: 0, | ||
| role: "alert", | ||
| class: "tpl-error tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-6 tpl:px-8 tpl:bg-[var(--tpl-bg)]" | ||
| }, xt = { class: "tpl:flex tpl:size-16 tpl:items-center tpl:justify-center tpl:rounded-full tpl:bg-[var(--tpl-danger-light)]" }, St = { class: "tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:text-center" }, Ct = { class: "tpl:text-lg tpl:font-semibold tpl:text-[var(--tpl-text)]" }, wt = { class: "tpl:max-w-md tpl:text-sm tpl:text-[var(--tpl-text-muted)]" }, Tt = /* @__PURE__ */ f({ | ||
| __name: "CloudErrorOverlay", | ||
| props: { | ||
| error: {}, | ||
| visible: { type: Boolean } | ||
| }, | ||
| emits: ["retry"], | ||
| setup(e, { emit: t }) { | ||
| let n = t, { t: r } = Q(); | ||
| function i(e) { | ||
| return "isUnauthorized" in e && e.isUnauthorized ? r.error.authFailed : "isNotFound" in e && e.isNotFound ? r.error.templateNotFound : r.error.defaultMessage; | ||
| } | ||
| function a(e) { | ||
| return "isNotFound" in e && !!e.isNotFound; | ||
| } | ||
| return (t, o) => e.visible && e.error ? (s(), m("div", bt, [ | ||
| p("div", xt, [g(b(K), { | ||
| size: 32, | ||
| "stroke-width": 1.5, | ||
| class: "tpl:text-[var(--tpl-danger)]" | ||
| })]), | ||
| p("div", St, [p("h2", Ct, c(b(r).error.title), 1), p("p", wt, c(i(e.error)), 1)]), | ||
| a(e.error) ? w("", !0) : (s(), m("button", { | ||
| key: 0, | ||
| class: "tpl-btn tpl-btn-primary tpl:inline-flex tpl:items-center tpl:gap-2 tpl:rounded-md tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]", | ||
| onClick: o[0] ||= (e) => n("retry") | ||
| }, c(b(r).error.retry), 1)) | ||
| ])) : w("", !0); | ||
| } | ||
| }), Et = { | ||
| key: 0, | ||
| class: "tpl-preview-banner tpl:absolute tpl:top-14 tpl:right-0 tpl:left-0 tpl:z-40 tpl:flex tpl:items-center tpl:justify-center tpl:gap-4 tpl:px-4 tpl:py-3 tpl:bg-[var(--tpl-primary-light)] tpl:border-b tpl:border-[var(--tpl-primary)]" | ||
| }, Dt = { class: "tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]" }, Ot = { class: "tpl:flex tpl:items-center tpl:gap-2" }, kt = /* @__PURE__ */ f({ | ||
| __name: "SnapshotPreviewBanner", | ||
| props: { visible: { type: Boolean } }, | ||
| emits: ["cancel", "confirm"], | ||
| setup(e, { emit: t }) { | ||
| let n = t, { t: r } = Q(); | ||
| return (t, i) => e.visible ? (s(), m("div", Et, [p("div", Dt, [g(b(me), { | ||
| size: 18, | ||
| "stroke-width": 2, | ||
| class: "tpl:text-[var(--tpl-primary)]" | ||
| }), p("span", null, c(b(r).snapshotPreview.message), 1)]), p("div", Ot, [p("button", { | ||
| class: "tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-150 tpl:text-[var(--tpl-text-muted)] tpl:border tpl:border-[var(--tpl-border)]", | ||
| style: { "background-color": "transparent" }, | ||
| onClick: i[0] ||= (e) => n("cancel") | ||
| }, c(b(r).snapshotPreview.cancel), 1), p("button", { | ||
| class: "tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]", | ||
| onClick: i[1] ||= (e) => n("confirm") | ||
| }, c(b(r).snapshotPreview.restore), 1)])])) : w("", !0); | ||
| } | ||
| }), At = { | ||
| key: 0, | ||
| role: "status", | ||
| "aria-live": "polite", | ||
| class: "tpl:absolute tpl:top-16 tpl:left-1/2 tpl:z-toast tpl:-translate-x-1/2 tpl:rounded-[var(--tpl-radius)] tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:shadow-lg", | ||
| style: { | ||
| "background-color": "var(--tpl-warning-light)", | ||
| color: "var(--tpl-text)", | ||
| border: "1px solid var(--tpl-warning)" | ||
| } | ||
| }, jt = /* @__PURE__ */ f({ | ||
| __name: "CollabUndoToast", | ||
| props: { visible: { type: Boolean } }, | ||
| setup(e) { | ||
| let { t } = U(); | ||
| return (n, r) => e.visible ? (s(), m("div", At, c(b(t).history.collabWarning), 1)) : w("", !0); | ||
| } | ||
| }), Mt = ["data-tpl-theme"], Nt = { class: "tpl:sticky tpl:top-0 tpl:z-40 tpl:h-0" }, Pt = { class: "tpl-main tpl:flex tpl:justify-center tpl:p-8" }, Ft = ["aria-label"], It = /*#__PURE__*/ ge(/* @__PURE__ */ f({ | ||
| __name: "CloudEditor", | ||
| props: { | ||
| config: {}, | ||
| translations: {}, | ||
| cloudTranslations: {}, | ||
| fontsManager: {}, | ||
| shadowRoot: {} | ||
| }, | ||
| emits: ["ready"], | ||
| setup(e, { expose: a, emit: o }) { | ||
| let d = h(() => import("./SavedBlocksPanels-Dff38YXu.js")), f = e; | ||
| _(z, f.cloudTranslations); | ||
| let x = o, S = u(null), C = u(null), k = Be({ | ||
| config: f.config, | ||
| translations: f.translations, | ||
| fontsManager: f.fontsManager, | ||
| emit: x, | ||
| getCommentsSidebar: () => S.value ? { filterByBlock: S.value.filterCommentsByBlock } : null, | ||
| editorRoot: f.shadowRoot, | ||
| containerEl: C | ||
| }), { isInitializing: A, isAuthReady: j, initError: M, planConfigInstance: N, websocket: F, collaboration: I, isCollaborationEnabled: L, editor: R, core: B, featureFlags: V, mediaLib: H, exporter: ee, testEmail: U, commentsInstance: W, savedBlocks: G, panelState: K, snapshotPreview: q, collabWarning: re, setThemeOverrides: ie, setUiTheme: ce } = k, { showNotice: le } = P(() => f.config.smallScreenNotice); | ||
| async function me(e) { | ||
| try { | ||
| await U.sendTestEmail(e), K.testEmailModalOpen.value = !1; | ||
| } catch {} | ||
| } | ||
| async function J() { | ||
| try { | ||
| await q.confirmRestoreSnapshot(); | ||
| } catch {} | ||
| } | ||
| let Y = He({ | ||
| config: f.config, | ||
| editor: R, | ||
| websocket: F, | ||
| planConfigInstance: N, | ||
| snapshotPreview: q, | ||
| core: B, | ||
| exporter: ee, | ||
| featureFlags: V, | ||
| isDestroyed: k.isDestroyed | ||
| }), X = Ue({ | ||
| issues: B.templateLint ? B.templateLint.issues : u([]), | ||
| planConfig: N.config | ||
| }); | ||
| async function Z() { | ||
| await X.tryRunSave(() => Y.saveTemplate().catch((e) => f.config.onError?.(e))); | ||
| } | ||
| return k.onSaveHook.value = Z, r(() => { | ||
| k.initialize(); | ||
| }), t(() => { | ||
| k.destroy(); | ||
| }), a({ | ||
| getContent: () => R.content.value, | ||
| setContent: (e) => R.setContent(e), | ||
| setTheme: ce, | ||
| setThemeOverrides: ie, | ||
| create: Y.createTemplate, | ||
| load: Y.loadTemplate, | ||
| save: Y.saveTemplate, | ||
| sendTestEmail: U.sendTestEmail | ||
| }), (t, r) => (s(), m("div", { | ||
| ref_key: "rootEl", | ||
| ref: C, | ||
| class: i(["tpl tpl:relative tpl:h-full tpl:overflow-hidden", { "tpl:dark": b(R).state.darkMode }]), | ||
| "data-tpl-theme": b(B).resolvedTheme.value, | ||
| style: n(b(B).themeStyles.value), | ||
| onDragover: r[6] ||= E(() => {}, ["prevent"]), | ||
| onDrop: r[7] ||= E(() => {}, ["prevent"]) | ||
| }, [ | ||
| g(pe), | ||
| g(O, { | ||
| "enter-active-class": "tpl:transition-opacity tpl:duration-200", | ||
| "enter-from-class": "tpl:opacity-100", | ||
| "enter-to-class": "tpl:opacity-100", | ||
| "leave-active-class": "tpl:transition-opacity tpl:duration-300", | ||
| "leave-from-class": "tpl:opacity-100", | ||
| "leave-to-class": "tpl:opacity-0" | ||
| }, { | ||
| default: l(() => [g(yt, { visible: b(A) || b(R).state.isLoading }, null, 8, ["visible"])]), | ||
| _: 1 | ||
| }), | ||
| g(O, { | ||
| "enter-active-class": "tpl:transition-opacity tpl:duration-200", | ||
| "enter-from-class": "tpl:opacity-0", | ||
| "enter-to-class": "tpl:opacity-100", | ||
| "leave-active-class": "tpl:transition-opacity tpl:duration-300", | ||
| "leave-from-class": "tpl:opacity-100", | ||
| "leave-to-class": "tpl:opacity-0" | ||
| }, { | ||
| default: l(() => [g(Tt, { | ||
| error: b(M), | ||
| visible: !!b(M) && !b(A), | ||
| onRetry: b(k).initialize | ||
| }, null, 8, [ | ||
| "error", | ||
| "visible", | ||
| "onRetry" | ||
| ])]), | ||
| _: 1 | ||
| }), | ||
| g(mt, { | ||
| editor: b(R), | ||
| core: b(B), | ||
| "feature-flags": b(V), | ||
| "panel-state": b(K), | ||
| "snapshot-preview": b(q), | ||
| "comments-instance": b(W), | ||
| "test-email": b(U), | ||
| websocket: b(F), | ||
| collaboration: b(I), | ||
| "is-collaboration-enabled": b(L), | ||
| "is-saving": b(R).state.isSaving || b(V).isSaveExporting.value, | ||
| "is-save-disabled": b(R).state.isSaving || b(V).isSaveExporting.value || !b(R).state.isDirty, | ||
| onSave: Z | ||
| }, null, 8, [ | ||
| "editor", | ||
| "core", | ||
| "feature-flags", | ||
| "panel-state", | ||
| "snapshot-preview", | ||
| "comments-instance", | ||
| "test-email", | ||
| "websocket", | ||
| "collaboration", | ||
| "is-collaboration-enabled", | ||
| "is-saving", | ||
| "is-save-disabled" | ||
| ]), | ||
| g($e, { | ||
| open: b(X).modalOpen.value, | ||
| issues: b(X).blockingIssues.value, | ||
| onCancel: b(X).cancel, | ||
| onConfirm: b(X).confirmAndSave | ||
| }, null, 8, [ | ||
| "open", | ||
| "issues", | ||
| "onCancel", | ||
| "onConfirm" | ||
| ]), | ||
| g(kt, { | ||
| visible: b(q).isPreviewingSnapshot.value, | ||
| onCancel: b(q).cancelPreview, | ||
| onConfirm: J | ||
| }, null, 8, ["visible", "onCancel"]), | ||
| g(O, { | ||
| "enter-active-class": "tpl:transition-all tpl:duration-200 tpl:ease-out", | ||
| "enter-from-class": "tpl:translate-y-[-8px] tpl:opacity-0", | ||
| "enter-to-class": "tpl:translate-y-0 tpl:opacity-100", | ||
| "leave-active-class": "tpl:transition-all tpl:duration-300 tpl:ease-in", | ||
| "leave-from-class": "tpl:translate-y-0 tpl:opacity-100", | ||
| "leave-to-class": "tpl:translate-y-[-8px] tpl:opacity-0" | ||
| }, { | ||
| default: l(() => [g(jt, { visible: b(re).collabUndoWarningVisible.value }, null, 8, ["visible"])]), | ||
| _: 1 | ||
| }), | ||
| T(g(ae, null, null, 512), [[D, !b(R).state.previewMode]]), | ||
| p("div", { | ||
| class: i(["tpl-body tpl:absolute tpl:bottom-0 tpl:overflow-auto", [b(R).state.previewMode ? "tpl:left-0 tpl:right-0" : b(K).rightPanelOpen.value ? "tpl:left-12 tpl:right-[680px]" : "tpl:left-12 tpl:right-[320px]", b(q).isPreviewingSnapshot.value ? "tpl:top-[104px]" : "tpl:top-14"]]), | ||
| style: { | ||
| transition: "all 300ms cubic-bezier(0.34, 1.56, 0.64, 1)", | ||
| "background-color": "var(--tpl-canvas-bg)" | ||
| } | ||
| }, [p("div", Nt, [g(O, { name: "tpl-restore-btn" }, { | ||
| default: l(() => [b(B).conditionPreview.hasHiddenBlocks.value ? (s(), m("button", { | ||
| key: 0, | ||
| class: "tpl:absolute tpl:left-1/2 tpl:top-2 tpl:-translate-x-1/2 tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-full tpl:border tpl:px-3.5 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:whitespace-nowrap tpl:shadow-md tpl:hover:opacity-80", | ||
| style: { | ||
| "background-color": "var(--tpl-warning-light)", | ||
| color: "var(--tpl-warning)", | ||
| "border-color": "var(--tpl-warning)", | ||
| "backdrop-filter": "blur(8px)" | ||
| }, | ||
| onClick: r[0] ||= (e) => b(B).conditionPreview.reset() | ||
| }, [g(b(oe), { | ||
| size: 13, | ||
| "stroke-width": 2 | ||
| }), y(" " + c(b(B).t.blockSettings.restoreHiddenBlocks), 1)])) : w("", !0)]), | ||
| _: 1 | ||
| })]), p("main", Pt, [g(ne, { | ||
| viewport: b(R).state.viewport, | ||
| content: b(R).content.value, | ||
| "selected-block-id": b(R).state.selectedBlockId, | ||
| "dark-mode": b(R).state.darkMode, | ||
| "preview-mode": b(R).state.previewMode, | ||
| "locked-blocks": b(I)?.lockedBlocks.value ?? void 0, | ||
| onSelectBlock: b(R).selectBlock, | ||
| onOpenAiChat: r[1] ||= (e) => b(K).aiChatOpen.value = !0, | ||
| onOpenDesignReference: r[2] ||= (e) => b(K).designReferenceOpen.value = !0 | ||
| }, null, 8, [ | ||
| "viewport", | ||
| "content", | ||
| "selected-block-id", | ||
| "dark-mode", | ||
| "preview-mode", | ||
| "locked-blocks", | ||
| "onSelectBlock" | ||
| ])])], 2), | ||
| e.config.branding !== !1 && !b(V).isWhiteLabeled.value ? (s(), v(ue, { | ||
| key: 0, | ||
| "position-class": [b(R).state.previewMode ? "tpl:left-0 tpl:right-0" : b(K).rightPanelOpen.value ? "tpl:left-12 tpl:right-[680px]" : "tpl:left-12 tpl:right-[320px]"] | ||
| }, null, 8, ["position-class"])) : w("", !0), | ||
| p("div", { | ||
| class: "tpl-sr-only", | ||
| role: "status", | ||
| "aria-live": "polite", | ||
| "aria-atomic": "true", | ||
| "aria-label": b(B).t.landmarks.reorderAnnouncements | ||
| }, c(b(B).keyboardReorder.announcement.value), 9, Ft), | ||
| T(g(te, { | ||
| "selected-block": b(R).selectedBlock.value, | ||
| settings: b(R).content.value.settings, | ||
| "shifted-left": b(K).rightPanelOpen.value, | ||
| onUpdateBlock: r[3] ||= (e) => b(R).updateBlock(b(R).selectedBlock.value.id, e), | ||
| onDeleteBlock: r[4] ||= (e) => b(B).blockActions.deleteBlock(b(R).selectedBlock.value.id), | ||
| onDuplicateBlock: r[5] ||= (e) => b(B).blockActions.duplicateBlock(b(R).selectedBlock.value), | ||
| onUpdateSettings: b(R).updateSettings | ||
| }, null, 8, [ | ||
| "selected-block", | ||
| "settings", | ||
| "shifted-left", | ||
| "onUpdateSettings" | ||
| ]), [[D, !b(R).state.previewMode]]), | ||
| !b(A) && b(j) ? (s(), v(ht, { | ||
| key: 1, | ||
| ref_key: "cloudPanelsRef", | ||
| ref: S, | ||
| config: f.config, | ||
| editor: b(R), | ||
| core: b(B), | ||
| "panel-state": b(K), | ||
| "plan-config-instance": b(N), | ||
| "test-email": b(U), | ||
| "media-lib": b(H), | ||
| onSendTestEmail: me | ||
| }, null, 8, [ | ||
| "config", | ||
| "editor", | ||
| "core", | ||
| "panel-state", | ||
| "plan-config-instance", | ||
| "test-email", | ||
| "media-lib" | ||
| ])) : w("", !0), | ||
| b(G).isAvailable.value ? (s(), v(b(d), { | ||
| key: 2, | ||
| feature: b(G) | ||
| }, null, 8, ["feature"])) : w("", !0), | ||
| p("div", { | ||
| ref: (e) => b(B).popoverRoot.value = e, | ||
| class: "tpl-popover-root" | ||
| }, null, 512), | ||
| g(se), | ||
| g(fe), | ||
| b(le) ? (s(), v(de, { key: 3 })) : w("", !0) | ||
| ], 46, Mt)); | ||
| } | ||
| }), [["__scopeId", "data-v-04940787"]]); | ||
| //#endregion | ||
| export { It as default }; |
| { | ||
| "version": "0.21.1", | ||
| "initialGzipBytes": 286973, | ||
| "version": "0.21.2", | ||
| "initialGzipBytes": 286975, | ||
| "initialRawBytes": 1033853, | ||
| "initialFileCount": 38, | ||
| "lazyGzipBytes": 190248, | ||
| "lazyRawBytes": 624400, | ||
| "lazyGzipBytes": 190290, | ||
| "lazyRawBytes": 624509, | ||
| "lazyFileCount": 52, | ||
| "generatedAt": "2026-07-29T19:31:48.705Z" | ||
| "generatedAt": "2026-07-30T09:39:43.642Z" | ||
| } |
+15
-1
@@ -261,3 +261,17 @@ import type { AiConfig } from '@templatical/types'; | ||
| templateDefaults?: TemplateDefaults; | ||
| savedBlocks?: boolean; | ||
| /** | ||
| * Reusable saved blocks. | ||
| * | ||
| * - **omitted / `true`** — backed by Templatical Cloud, gated on the | ||
| * `saved_modules` plan feature. | ||
| * - **`false`** — off entirely; no UI renders. | ||
| * - **a {@link SavedBlocksProvider}** — backed by *your* store instead of | ||
| * Cloud's, and **not plan-gated**, because the plan feature licenses Cloud's | ||
| * storage rather than the editor's UI. | ||
| * | ||
| * The provider form is the same type `init()` takes, so moving an OSS | ||
| * integration to Cloud means deleting this key (to adopt Cloud's store) or | ||
| * leaving it exactly as-is (to keep your own) — never rewriting it. | ||
| */ | ||
| savedBlocks?: boolean | SavedBlocksProvider; | ||
| autoSave?: boolean; | ||
@@ -264,0 +278,0 @@ autoSaveDebounce?: number; |
+7
-7
| { | ||
| "name": "@templatical/editor", | ||
| "description": "Vue 3 visual drag-and-drop email editor powered by Templatical", | ||
| "version": "0.21.1", | ||
| "version": "0.21.2", | ||
| "bugs": "https://github.com/templatical/sdk/issues", | ||
@@ -35,5 +35,5 @@ "devDependencies": { | ||
| "vue-tsc": "^3.3.8", | ||
| "@templatical/core": "0.21.1", | ||
| "@templatical/quality": "0.21.1", | ||
| "@templatical/types": "0.21.1" | ||
| "@templatical/core": "0.21.2", | ||
| "@templatical/quality": "0.21.2", | ||
| "@templatical/types": "0.21.2" | ||
| }, | ||
@@ -66,5 +66,5 @@ "exports": { | ||
| "peerDependencies": { | ||
| "@templatical/media-library": "0.21.1", | ||
| "@templatical/quality": "0.21.1", | ||
| "@templatical/renderer": "0.21.1" | ||
| "@templatical/media-library": "0.21.2", | ||
| "@templatical/quality": "0.21.2", | ||
| "@templatical/renderer": "0.21.2" | ||
| }, | ||
@@ -71,0 +71,0 @@ "peerDependenciesMeta": { |
| 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 _, m as v, n as y, ot 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-D840DIZB.js"; | ||
| import { t as k } from "./timeouts-Bmi_yePw.js"; | ||
| import { F as A } from "./src-BKI1x1k7.js"; | ||
| import { T as j, b as M, j as N, t as P } from "./useEditorCore-BA6mspzD.js"; | ||
| import { _ as F } from "./dist-CiBEt0VR.js"; | ||
| import { t as I } from "./dist-CxTt8FRE.js"; | ||
| import { A as L, F as R, c as z, r as B, s as V, t as H, u as U } from "./keys-D7kUmfpE.js"; | ||
| import { t as W } from "./useI18n-8Wd7G-cq.js"; | ||
| import { At as G, Dt as K, F as ee, Ft as q, P as J, et as te, j as ne, rt as Y, u as re, w as ie } from "./icons-CYWw8Xfp.js"; | ||
| import { t as ae } from "./_plugin-vue_export-helper-B3ysoDQm.js"; | ||
| import { n as X } from "./useCloudI18n-BjNENqkM.js"; | ||
| import { a as Z, c as oe, d as se, f as Q, i as ce, l as le, n as ue, o as de, p as fe, r as pe, s as me, t as he, u as ge } from "./styles-DMOEiSX9.js"; | ||
| import { d as $ } from "./ColorPicker-DZSzZ5ZP.js"; | ||
| import { S as _e, _ as ve, a as ye, c as be, d as xe, f as Se, h as Ce, i as we, l as Te, m as Ee, n as De, o as Oe, r as ke, s as Ae, t as je, u as Me, v as Ne } from "./cloud-BJlbXGTJ.js"; | ||
| //#region src/cloud/composables/useSnapshotPreview.ts | ||
| function Pe(t) { | ||
| let { authManager: n, editor: i, history: a, conditionPreview: o, autoSave: s, onError: l } = t, u = e(null), d = c(null), p = c(null), m = !1; | ||
| r(() => { | ||
| m = !0; | ||
| }); | ||
| let h = f(() => d.value !== null), g = f(() => u.value?.snapshots.value ?? []), _ = f(() => u.value?.isLoading.value ?? !1), v = f(() => u.value?.isRestoring.value ?? !1); | ||
| function y() { | ||
| i.state.template?.id && !u.value && (u.value = Oe({ | ||
| authManager: n, | ||
| templateId: i.state.template.id, | ||
| onRestore: b, | ||
| onError: l | ||
| }), u.value.loadSnapshots()); | ||
| } | ||
| function b(e) { | ||
| i.setContent(e.content, !1), a.clear(), o.reset(); | ||
| } | ||
| async function x(e) { | ||
| if (!m) { | ||
| if (d.value) { | ||
| d.value = e, i.setContent(e.content, !1); | ||
| return; | ||
| } | ||
| i.state.isDirty && i.hasTemplate() && (await i.createSnapshot(), m) || (p.value = structuredClone(i.content.value), s?.pause(), d.value = e, i.setContent(e.content, !1)); | ||
| } | ||
| } | ||
| async function S() { | ||
| if (!(!d.value || !u.value)) try { | ||
| if (await u.value.restoreSnapshot(d.value.id), m || (await u.value.loadSnapshots(), m)) return; | ||
| } catch (e) { | ||
| throw !m && p.value && i.setContent(p.value, !1), e; | ||
| } finally { | ||
| m || (d.value = null, p.value = null, s?.resume()); | ||
| } | ||
| } | ||
| function C() { | ||
| !d.value || !p.value || (i.setContent(p.value, !1), d.value = null, p.value = null, s?.resume()); | ||
| } | ||
| async function w() { | ||
| m || u.value && await u.value.loadSnapshots(); | ||
| } | ||
| return { | ||
| snapshotHistoryInstance: u, | ||
| previewingSnapshot: d, | ||
| contentBeforePreview: p, | ||
| isPreviewingSnapshot: h, | ||
| snapshotHistorySnapshots: g, | ||
| snapshotHistoryIsLoading: _, | ||
| snapshotHistoryIsRestoring: v, | ||
| initSnapshotHistory: y, | ||
| handleRestore: b, | ||
| handleSnapshotNavigate: x, | ||
| confirmRestoreSnapshot: S, | ||
| cancelPreview: C, | ||
| loadSnapshotHistory: w | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudPanelState.ts | ||
| function Fe() { | ||
| let e = c(null), t = f({ | ||
| get: () => e.value === "ai-chat", | ||
| set: (t) => e.value = t ? "ai-chat" : null | ||
| }), n = f({ | ||
| get: () => e.value === "scoring", | ||
| set: (t) => e.value = t ? "scoring" : null | ||
| }), r = f({ | ||
| get: () => e.value === "design-reference", | ||
| set: (t) => e.value = t ? "design-reference" : null | ||
| }), i = f({ | ||
| get: () => e.value === "comments", | ||
| set: (t) => e.value = t ? "comments" : null | ||
| }), a = c(!1), o = c(!1), s = c(void 0), l = c(!1), u = c(null), d = f(() => e.value !== null), p = f(() => { | ||
| let t = e.value; | ||
| return t === "ai-chat" || t === "design-reference" || t === "scoring" ? t : null; | ||
| }), m = f(() => l.value || e.value === "ai-chat" || e.value === "design-reference" || e.value === "scoring"); | ||
| function h() { | ||
| l.value = !l.value; | ||
| } | ||
| function g(t) { | ||
| l.value = !1, e.value = e.value === t ? null : t; | ||
| } | ||
| return I(u, () => { | ||
| l.value = !1; | ||
| }), { | ||
| activePanel: e, | ||
| aiChatOpen: t, | ||
| scoringPanelOpen: n, | ||
| designReferenceOpen: r, | ||
| commentsOpen: i, | ||
| testEmailModalOpen: a, | ||
| mediaLibraryOpen: o, | ||
| mediaLibraryAccept: s, | ||
| aiMenuOpen: l, | ||
| aiMenuRef: u, | ||
| rightPanelOpen: d, | ||
| activeAiFeature: p, | ||
| aiButtonActive: m, | ||
| toggleAiMenu: h, | ||
| handleAiFeatureSelect: g | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCollabUndoWarning.ts | ||
| function Ie(e) { | ||
| let { isCollaborationEnabled: t, getCollaboratorCount: n, canUndo: r } = e, i = c(!1), a = c(!1), { start: o } = F(() => { | ||
| a.value = !1; | ||
| }, k, { immediate: !1 }); | ||
| function s() { | ||
| i.value || !t.value || n() === 0 || !r.value || (i.value = !0, a.value = !0, o()); | ||
| } | ||
| return { | ||
| collabUndoWarningVisible: a, | ||
| showCollabUndoWarning: s | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudFeatureFlags.ts | ||
| function Le(e) { | ||
| let { planConfigInstance: t, aiConfig: n, editor: r } = e, i = f(() => t.hasFeature("ai_generation") && n.hasAnyMenuFeature.value), a = f(() => t.hasFeature("test_email")), o = f(() => !!r.state.template?.id), s = f(() => t.hasFeature("white_label")), l = f(() => t.config.value?.limits.max_templates ?? null), u = f(() => t.config.value?.template_count ?? 0), d = c(!1), p = c("idle"), m = c(""), { start: h } = F(() => { | ||
| p.value = "idle"; | ||
| }, 3e3, { immediate: !1 }); | ||
| return { | ||
| canUseAiGeneration: i, | ||
| canSendTestEmail: a, | ||
| hasTemplateSaved: o, | ||
| isWhiteLabeled: s, | ||
| templateLimit: l, | ||
| templateCount: u, | ||
| isSaveExporting: d, | ||
| saveStatus: p, | ||
| saveErrorMessage: m, | ||
| startSaveStatusClear: h | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudMediaLibrary.ts | ||
| function Re(e) { | ||
| let { onRequestMedia: t, mediaLibraryOpen: n, mediaLibraryAccept: i, authManager: a, getMediaConfig: o, onError: s } = e, c = null; | ||
| function l(e) { | ||
| return { | ||
| url: e.url, | ||
| alt: e.alt_text || void 0 | ||
| }; | ||
| } | ||
| async function u(e) { | ||
| let r = e?.accept ?? ["images"]; | ||
| if (e?.files?.length) { | ||
| if (t) { | ||
| let n = await t({ | ||
| accept: r, | ||
| files: e.files | ||
| }); | ||
| return n ? l(n) : null; | ||
| } | ||
| return d(e.files[0]); | ||
| } | ||
| if (t) { | ||
| let e = await t({ accept: r }); | ||
| return e ? l(e) : null; | ||
| } | ||
| return c &&= (c(null), null), i.value = r, n.value = !0, new Promise((e) => { | ||
| c = (t) => { | ||
| e(t); | ||
| }; | ||
| }); | ||
| } | ||
| async function d(e) { | ||
| let t = f(e); | ||
| if (t) return s?.(t), null; | ||
| try { | ||
| let { MediaApiClient: t } = await import("./src-D6F_3XkE.js"); | ||
| return l(await new t(a).uploadMedia(e)); | ||
| } catch (e) { | ||
| return s?.(e), null; | ||
| } | ||
| } | ||
| function f(e) { | ||
| let t = o?.() ?? null; | ||
| if (!t) return null; | ||
| let n = t.categories?.images?.mime_types ?? []; | ||
| return n.length > 0 && !n.includes(e.type) ? /* @__PURE__ */ Error(`Unsupported image type: ${e.type || "unknown"}. Accepted: ${n.join(", ")}.`) : t.max_file_size > 0 && e.size > t.max_file_size ? /* @__PURE__ */ Error(`Image is too large (${e.size} bytes). Maximum allowed is ${t.max_file_size} bytes.`) : null; | ||
| } | ||
| function p(e) { | ||
| n.value = !1, c?.(l(e)), c = null; | ||
| } | ||
| function m() { | ||
| n.value = !1, c?.(null), c = null; | ||
| } | ||
| return r(() => { | ||
| c &&= (c(null), null); | ||
| }), { | ||
| handleRequestMedia: u, | ||
| handleMediaSelect: p, | ||
| handleMediaLibraryClose: m | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudInitialization.ts | ||
| function ze(e) { | ||
| let { config: t, translations: n, fontsManager: r, emit: i, getCommentsSidebar: o } = e, s = c(!0), l = c(!1), u = c(null), d = !1, p = { value: null }, m = null, h = null, g = new _e({ | ||
| ...t.auth, | ||
| onError: t.onError | ||
| }), _ = ke({ | ||
| authManager: g, | ||
| onError: t.onError | ||
| }), v = c(/* @__PURE__ */ new Map()), y = ve({ | ||
| authManager: g, | ||
| defaultFontFamily: t.fonts?.defaultFont, | ||
| templateDefaults: t.templateDefaults, | ||
| onError: t.onError, | ||
| lockedBlocks: v | ||
| }), b = be({ | ||
| authManager: g, | ||
| onError: t.onError | ||
| }); | ||
| t.mcp?.enabled && je({ | ||
| editor: y, | ||
| channel: b.channel, | ||
| onOperation: t.mcp.onOperation | ||
| }); | ||
| let x = null; | ||
| t.collaboration?.enabled && (x = Me({ | ||
| authManager: g, | ||
| editor: y, | ||
| channel: b.channel, | ||
| onError: t.onError, | ||
| onCollaboratorJoined: t.collaboration.onCollaboratorJoined, | ||
| onCollaboratorLeft: t.collaboration.onCollaboratorLeft, | ||
| onBlockLocked: t.collaboration.onBlockLocked, | ||
| onBlockUnlocked: t.collaboration.onBlockUnlocked | ||
| }), O(() => x.lockedBlocks.value, (e) => { | ||
| v.value = e; | ||
| }, { immediate: !0 }), Te(y, x)); | ||
| let S = f(() => !!t.collaboration?.enabled && _.hasFeature("collaboration")), C = P({ | ||
| editor: y, | ||
| config: { | ||
| uiTheme: t.uiTheme, | ||
| theme: void 0, | ||
| blockDefaults: t.blockDefaults, | ||
| templateDefaults: t.templateDefaults, | ||
| customBlocks: [], | ||
| paletteBlocks: t.paletteBlocks, | ||
| htmlBlockPreview: t.htmlBlockPreview, | ||
| colors: t.colors, | ||
| mergeTags: t.mergeTags, | ||
| logicTags: t.logicTags, | ||
| displayConditions: t.displayConditions, | ||
| onRequestMedia: null, | ||
| lint: Q(t), | ||
| onSave: () => { | ||
| p.value?.().catch((e) => { | ||
| t.onError?.(e); | ||
| }); | ||
| } | ||
| }, | ||
| translations: n, | ||
| fontsManager: r, | ||
| historyOptions: x ? { isRemoteOperation: () => x._isProcessingRemoteOperation() } : void 0, | ||
| autoSaveOptions: { | ||
| onChange: async () => { | ||
| y.hasTemplate() && (await y.createSnapshot(), m?.snapshotHistoryInstance.value?.loadSnapshots()); | ||
| }, | ||
| debounce: t.autoSaveDebounce ?? 5e3, | ||
| enabled: () => t.autoSave !== !1 && _.hasFeature("auto_save") | ||
| }, | ||
| themeExtraStyles: () => ({ "--tpl-drop-text": `"${n.canvas.dropHere}"` }), | ||
| keyboardOptions: { onBeforeUndo: () => h?.showCollabUndoWarning() }, | ||
| editorRoot: e.editorRoot, | ||
| containerEl: e.containerEl | ||
| }), w = Ie({ | ||
| isCollaborationEnabled: S, | ||
| getCollaboratorCount: () => x?.collaborators.value.length ?? 0, | ||
| canUndo: C.history.canUndo | ||
| }); | ||
| h = w; | ||
| let T = Pe({ | ||
| authManager: g, | ||
| editor: y, | ||
| history: C.history, | ||
| conditionPreview: C.conditionPreview, | ||
| autoSave: C.autoSave, | ||
| onError: t.onError | ||
| }); | ||
| m = T; | ||
| let E = Fe(), D = Ce(t.ai), k = Le({ | ||
| planConfigInstance: _, | ||
| aiConfig: D, | ||
| editor: y | ||
| }), A = Re({ | ||
| onRequestMedia: t.onRequestMedia, | ||
| mediaLibraryOpen: E.mediaLibraryOpen, | ||
| mediaLibraryAccept: E.mediaLibraryAccept, | ||
| authManager: g, | ||
| getMediaConfig: () => _.config.value?.media ?? null, | ||
| onError: t.onError | ||
| }); | ||
| j({ | ||
| onBlockMove: y.moveBlock, | ||
| onBlockAdd: y.addBlock | ||
| }); | ||
| let M = we({ | ||
| authManager: g, | ||
| getFontsConfig: () => t.fonts, | ||
| canUseCustomFonts: () => _.hasFeature("custom_fonts") | ||
| }), F = ye({ | ||
| authManager: g, | ||
| getTemplateId: () => y.state.template?.id ?? null, | ||
| save: () => y.save(), | ||
| exportHtml: (e) => M.exportHtml(e), | ||
| onError: t.onError, | ||
| isAuthReady: l, | ||
| onBeforeTestEmail: t.onBeforeTestEmail | ||
| }), I = Se({ | ||
| authManager: g, | ||
| getTemplateId: () => y.state.template?.id ?? null, | ||
| getSocketId: () => b.getSocketId(), | ||
| onComment: t.onComment, | ||
| onError: t.onError, | ||
| isAuthReady: l, | ||
| hasCommentingFeature: () => t.commenting !== !1 && _.hasFeature("commenting") | ||
| }); | ||
| xe({ | ||
| comments: I, | ||
| channel: b.channel | ||
| }); | ||
| let z = fe({ | ||
| provider: Ae(g), | ||
| editor: y, | ||
| onError: t.onError, | ||
| isAvailable: () => t.savedBlocks !== !1 && _.hasFeature("saved_modules") | ||
| }), W = Ee({ | ||
| authManager: g, | ||
| getTemplateId: () => y.state.template?.id ?? null | ||
| }); | ||
| function G(e) { | ||
| E.commentsOpen.value = !0, queueMicrotask(() => { | ||
| o()?.filterByBlock(e); | ||
| }); | ||
| } | ||
| a(L, A.handleRequestMedia), a(B, g), a(H, D), a(U, I), a(R, W), a(V, { | ||
| plan: _, | ||
| ai: D, | ||
| comments: { | ||
| getBlockCount: (e) => I.commentCountByBlock.value.get(e) ?? 0, | ||
| openForBlock: G | ||
| }, | ||
| savedBlocks: z.capability | ||
| }); | ||
| function K(e) { | ||
| _.hasFeature("theme_customization") && (C.themeOverrides.value = e); | ||
| } | ||
| function ee(e) { | ||
| y.setUiTheme(e); | ||
| } | ||
| async function q() { | ||
| s.value = !0, u.value = null; | ||
| try { | ||
| if (await g.initialize(), d) return; | ||
| l.value = !0; | ||
| let e = await De({ authManager: g }); | ||
| if (d) return; | ||
| if (!e.api.ok) throw Error("Health check failed: API is not reachable"); | ||
| if (!e.auth.ok) throw Error(`Health check failed: authentication error${e.auth.error ? ` - ${e.auth.error}` : ""}`); | ||
| if (e.websocket.ok || N.warn("WebSocket health check failed:", e.websocket.error ?? "unknown error", "-- real-time features will be disabled."), await _.fetchConfig(), d) return; | ||
| r.setCustomFontsEnabled(_.hasFeature("custom_fonts")), t.customBlocks?.length && _.hasFeature("custom_blocks") && C.registerCustomBlocks(t.customBlocks), t.theme && _.hasFeature("theme_customization") && (C.themeOverrides.value = t.theme), i("ready"); | ||
| } catch (e) { | ||
| if (d) return; | ||
| let n = e instanceof Error ? e : Error("Initialization failed", { cause: e }); | ||
| u.value = n, t.onError?.(n); | ||
| } finally { | ||
| d || (s.value = !1); | ||
| } | ||
| } | ||
| function J() { | ||
| d = !0, r.cleanupFontLinks(), b.disconnect(), C.destroy(), t.onUnmount?.(); | ||
| } | ||
| return { | ||
| isInitializing: s, | ||
| isAuthReady: l, | ||
| initError: u, | ||
| isDestroyed: () => d, | ||
| authManager: g, | ||
| planConfigInstance: _, | ||
| websocket: b, | ||
| collaboration: x, | ||
| isCollaborationEnabled: S, | ||
| editor: y, | ||
| core: C, | ||
| aiConfig: D, | ||
| featureFlags: k, | ||
| mediaLib: A, | ||
| exporter: M, | ||
| testEmail: F, | ||
| commentsInstance: I, | ||
| savedBlocks: z, | ||
| scoringInstance: W, | ||
| panelState: E, | ||
| snapshotPreview: T, | ||
| collabWarning: w, | ||
| onSaveHook: p, | ||
| initialize: q, | ||
| destroy: J, | ||
| setThemeOverrides: K, | ||
| setUiTheme: ee, | ||
| openCommentsForBlock: G | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/utils/preRenderCustomBlocks.ts | ||
| async function Be(e, t) { | ||
| let n = async (e) => { | ||
| if (A(e)) { | ||
| let n = e; | ||
| try { | ||
| n.renderedHtml = await t.renderCustomBlock(n); | ||
| } catch { | ||
| n.renderedHtml = `<!-- Custom block render error: ${n.customType} -->`; | ||
| } | ||
| } | ||
| if (e.type === "section" && "children" in e) { | ||
| let t = e; | ||
| for (let e of t.children) for (let t of e) await n(t); | ||
| } | ||
| }; | ||
| for (let t of e.blocks) await n(t); | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudLifecycle.ts | ||
| function Ve(e) { | ||
| let { config: t, editor: n, websocket: r, planConfigInstance: i, snapshotPreview: a, core: o, exporter: s, featureFlags: c, isDestroyed: l } = e; | ||
| function u() { | ||
| return Ne(i.config.value.websocket); | ||
| } | ||
| async function d(e) { | ||
| let i = await n.create(e); | ||
| return l() ? i : (t.onCreate?.(i), a.initSnapshotHistory(), r.connect(i.id, u()), i); | ||
| } | ||
| async function f(e) { | ||
| let i = await n.load(e); | ||
| return l() ? i : (t.onLoad?.(i), a.initSnapshotHistory(), r.connect(i.id, u()), i); | ||
| } | ||
| async function p() { | ||
| c.isSaveExporting.value = !0, c.saveStatus.value = "idle"; | ||
| try { | ||
| if (await Be(n.content.value, o.registry), l()) throw Error("Component unmounted during save"); | ||
| let e = await n.save(); | ||
| if (l()) throw Error("Component unmounted during save"); | ||
| a.initSnapshotHistory(), a.snapshotHistoryInstance.value?.loadSnapshots(); | ||
| let r = await s.exportHtml(e.id); | ||
| if (l()) throw Error("Component unmounted during save"); | ||
| let i = { | ||
| templateId: e.id, | ||
| html: r.html, | ||
| mjml: r.mjml, | ||
| content: e.content | ||
| }; | ||
| return t.onSave?.(i), c.saveStatus.value = "saved", c.startSaveStatusClear(), i; | ||
| } catch (e) { | ||
| throw l() || (c.saveStatus.value = "error", c.saveErrorMessage.value = e instanceof Error ? e.message : "Save failed"), e; | ||
| } finally { | ||
| l() || (c.isSaveExporting.value = !1); | ||
| } | ||
| } | ||
| return { | ||
| createTemplate: d, | ||
| loadTemplate: f, | ||
| saveTemplate: p | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudSaveGate.ts | ||
| function He(e) { | ||
| let t = c(!1), n = null, r = f(() => e.planConfig.value?.accessibility?.blockOnError === !0), i = f(() => r.value ? e.issues.value.filter((e) => e.severity === "error") : []), a = f(() => i.value.length > 0); | ||
| async function o(e) { | ||
| return a.value ? (n = e, t.value = !0, !1) : (await e(), !0); | ||
| } | ||
| async function s() { | ||
| let e = n; | ||
| n = null, t.value = !1, e && await e(); | ||
| } | ||
| function l() { | ||
| n = null, t.value = !1; | ||
| } | ||
| return { | ||
| shouldBlock: a, | ||
| blockingIssues: i, | ||
| modalOpen: t, | ||
| tryRunSave: o, | ||
| confirmAndSave: s, | ||
| cancel: l | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/components/CloudSaveGateModal.vue?vue&type=script&setup=true&lang.ts | ||
| var Ue = ["aria-label"], We = { class: "tpl:flex tpl:max-h-[80vh] tpl:w-full tpl:max-w-md tpl:flex-col tpl:gap-4 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:p-5 tpl:shadow-[var(--tpl-shadow-md)]" }, Ge = { class: "tpl:flex tpl:items-center tpl:gap-2" }, Ke = { class: "tpl:m-0 tpl:text-base tpl:font-semibold tpl:text-[var(--tpl-text)]" }, qe = { class: "tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-muted)]" }, Je = { class: "tpl:m-0 tpl:flex tpl:max-h-64 tpl:list-none tpl:flex-col tpl:gap-1.5 tpl:overflow-y-auto tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-2" }, Ye = { class: "tpl:text-xs tpl:text-[var(--tpl-text)]" }, Xe = { class: "tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]" }, Ze = { class: "tpl:flex tpl:justify-end tpl:gap-2" }, Qe = /* @__PURE__ */ E({ | ||
| __name: "CloudSaveGateModal", | ||
| props: { | ||
| open: { type: Boolean }, | ||
| issues: {} | ||
| }, | ||
| emits: ["cancel", "confirm"], | ||
| setup(e, { emit: t }) { | ||
| let n = t, { t: r } = X(); | ||
| return (t, a) => (i(), v(y, { | ||
| "enter-active-class": "tpl:transition-opacity tpl:duration-150", | ||
| "leave-active-class": "tpl:transition-opacity tpl:duration-150", | ||
| "enter-from-class": "tpl:opacity-0", | ||
| "leave-to-class": "tpl:opacity-0" | ||
| }, { | ||
| default: s(() => [e.open ? (i(), p("div", { | ||
| key: 0, | ||
| role: "dialog", | ||
| "aria-modal": "true", | ||
| "aria-label": h(r).saveGate.title, | ||
| class: "tpl:fixed tpl:inset-0 tpl:z-50 tpl:flex tpl:items-center tpl:justify-center tpl:bg-black/40 tpl:p-6", | ||
| onClick: a[2] ||= _((e) => n("cancel"), ["self"]) | ||
| }, [x("div", We, [ | ||
| x("header", Ge, [D(h(re), { | ||
| size: 18, | ||
| "stroke-width": 2, | ||
| class: "tpl:text-[var(--tpl-warning)]" | ||
| }), x("h2", Ke, d(h(r).saveGate.title), 1)]), | ||
| x("p", qe, d(h(r).saveGate.body), 1), | ||
| x("ul", Je, [(i(!0), p(w, null, o(e.issues, (e) => (i(), p("li", { | ||
| key: `${e.ruleId}-${e.blockId ?? "template"}`, | ||
| class: "tpl:flex tpl:flex-col tpl:gap-0.5 tpl:rounded tpl:px-2 tpl:py-1.5" | ||
| }, [x("span", Ye, d(e.message), 1), x("span", Xe, d(e.ruleId), 1)]))), 128))]), | ||
| x("footer", Ze, [x("button", { | ||
| type: "button", | ||
| class: "tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]", | ||
| onClick: a[0] ||= (e) => n("cancel") | ||
| }, d(h(r).saveGate.cancel), 1), x("button", { | ||
| type: "button", | ||
| class: "tpl:rounded-md tpl:bg-[var(--tpl-danger)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-white", | ||
| onClick: a[1] ||= (e) => n("confirm") | ||
| }, d(h(r).saveGate.confirm), 1)]) | ||
| ])], 8, Ue)) : m("", !0)]), | ||
| _: 1 | ||
| })); | ||
| } | ||
| }), $e = { | ||
| class: "tpl-header tpl:absolute tpl:top-0 tpl:right-0 tpl:left-0 tpl:z-50 tpl:grid tpl:h-14 tpl:grid-cols-[1fr_auto_1fr] tpl:items-center tpl:px-4", | ||
| style: { | ||
| "background-color": "color-mix(in srgb, var(--tpl-bg) 80%, transparent)", | ||
| "backdrop-filter": "blur(12px)", | ||
| "-webkit-backdrop-filter": "blur(12px)", | ||
| "box-shadow": "var(--tpl-shadow-md)", | ||
| "border-bottom": "1px solid var(--tpl-border)" | ||
| } | ||
| }, et = { class: "tpl-header-left tpl:flex tpl:min-w-[200px] tpl:items-center tpl:gap-3" }, tt = { | ||
| key: 0, | ||
| class: "tpl:text-xs tpl:opacity-60 tpl:text-[var(--tpl-text-muted)]" | ||
| }, nt = { class: "tpl-header-center tpl:flex tpl:items-center tpl:justify-center tpl:gap-10" }, rt = { class: "tpl-header-right tpl:flex tpl:min-w-[200px] tpl:items-center tpl:justify-end tpl:gap-3" }, it = ["data-tooltip"], at = { | ||
| key: 1, | ||
| "aria-live": "polite", | ||
| class: "tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-success)]" | ||
| }, ot = { | ||
| key: 2, | ||
| "aria-live": "polite", | ||
| class: "tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-text-muted)]" | ||
| }, st = ["aria-label", "aria-expanded"], ct = { | ||
| key: 0, | ||
| class: "tpl:inline-flex tpl:size-4.5 tpl:items-center tpl:justify-center tpl:rounded-full tpl:text-[10px] tpl:font-semibold tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]" | ||
| }, lt = ["aria-expanded"], ut = { | ||
| key: 0, | ||
| class: "tpl:absolute tpl:right-0 tpl:top-full tpl:z-50 tpl:mt-1 tpl:origin-top-right" | ||
| }, dt = ["disabled"], ft = ["disabled"], pt = /* @__PURE__ */ E({ | ||
| __name: "CloudHeader", | ||
| props: { | ||
| editor: {}, | ||
| core: {}, | ||
| featureFlags: {}, | ||
| panelState: {}, | ||
| snapshotPreview: {}, | ||
| commentsInstance: {}, | ||
| testEmail: {}, | ||
| websocket: {}, | ||
| collaboration: {}, | ||
| isCollaborationEnabled: { type: Boolean }, | ||
| isSaveDisabled: { type: Boolean }, | ||
| isSaving: { type: Boolean } | ||
| }, | ||
| emits: ["save"], | ||
| setup(e) { | ||
| let t = u(() => import("./CollaboratorBar-BS3rJy6-.js")), n = u(() => import("./SnapshotHistory-Bf1uovti.js")), r = u(() => import("./AiFeatureMenu-Duwg413F.js")), { t: a, format: o } = X(); | ||
| return (c, l) => (i(), p("header", $e, [ | ||
| x("div", et, [e.featureFlags.templateLimit.value === null ? m("", !0) : (i(), p("span", tt, d(h(o)(h(a).header.templatesUsed, { | ||
| used: e.featureFlags.templateCount.value, | ||
| max: e.featureFlags.templateLimit.value | ||
| })), 1))]), | ||
| x("div", nt, [ | ||
| D(de, { | ||
| viewport: e.editor.state.viewport, | ||
| onChange: e.editor.setViewport | ||
| }, null, 8, ["viewport", "onChange"]), | ||
| D(ce, { | ||
| "dark-mode": e.editor.state.darkMode, | ||
| onChange: e.editor.setDarkMode | ||
| }, null, 8, ["dark-mode", "onChange"]), | ||
| D(Z, { | ||
| "preview-mode": e.editor.state.previewMode, | ||
| onChange: e.editor.setPreviewMode | ||
| }, null, 8, ["preview-mode", "onChange"]), | ||
| e.collaboration && e.isCollaborationEnabled ? (i(), v(h(t), { | ||
| key: 0, | ||
| collaborators: e.collaboration.collaborators.value, | ||
| "is-connected": e.websocket.isConnected.value | ||
| }, null, 8, ["collaborators", "is-connected"])) : m("", !0), | ||
| e.snapshotPreview.snapshotHistoryInstance.value ? (i(), v(h(n), { | ||
| key: 1, | ||
| snapshots: e.snapshotPreview.snapshotHistorySnapshots.value, | ||
| "is-loading": e.snapshotPreview.snapshotHistoryIsLoading.value, | ||
| "is-restoring": e.snapshotPreview.snapshotHistoryIsRestoring.value, | ||
| onLoad: e.snapshotPreview.loadSnapshotHistory, | ||
| onNavigate: e.snapshotPreview.handleSnapshotNavigate | ||
| }, null, 8, [ | ||
| "snapshots", | ||
| "is-loading", | ||
| "is-restoring", | ||
| "onLoad", | ||
| "onNavigate" | ||
| ])) : m("", !0) | ||
| ]), | ||
| x("div", rt, [ | ||
| e.featureFlags.saveStatus.value === "error" ? (i(), p("div", { | ||
| key: 0, | ||
| "aria-live": "assertive", | ||
| class: "tpl-tooltip tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-danger)]", | ||
| "data-tooltip": e.featureFlags.saveErrorMessage.value | ||
| }, [D(h(G), { | ||
| size: 12, | ||
| "stroke-width": 2.5 | ||
| }), T(" " + d(h(a).header.saveFailed), 1)], 8, it)) : e.featureFlags.saveStatus.value === "saved" ? (i(), p("div", at, [D(h(q), { | ||
| size: 12, | ||
| "stroke-width": 2.5 | ||
| }), T(" " + d(h(a).header.saved), 1)])) : e.editor.state.isDirty ? (i(), p("div", ot, [l[4] ||= x("span", { class: "tpl-pulse tpl:size-1.5 tpl:rounded-full tpl:bg-[var(--tpl-primary)]" }, null, -1), T(" " + d(h(a).header.unsaved), 1)])) : m("", !0), | ||
| e.commentsInstance.isEnabled.value && e.featureFlags.hasTemplateSaved.value ? (i(), p("button", { | ||
| key: 3, | ||
| "aria-label": e.commentsInstance.unresolvedCount.value > 0 ? `${h(a).comments.button} (${e.commentsInstance.unresolvedCount.value})` : h(a).comments.button, | ||
| "aria-expanded": e.panelState.commentsOpen.value, | ||
| class: b(h($)), | ||
| style: C({ | ||
| backgroundColor: e.panelState.commentsOpen.value ? "var(--tpl-primary)" : "transparent", | ||
| color: e.panelState.commentsOpen.value ? "var(--tpl-bg)" : "var(--tpl-primary)", | ||
| borderColor: "var(--tpl-primary)" | ||
| }), | ||
| onClick: l[0] ||= (t) => e.panelState.commentsOpen.value = !e.panelState.commentsOpen.value | ||
| }, [ | ||
| D(h(te), { | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| }), | ||
| T(" " + d(h(a).comments.button) + " ", 1), | ||
| e.commentsInstance.unresolvedCount.value > 0 && !e.panelState.commentsOpen.value ? (i(), p("span", ct, d(e.commentsInstance.unresolvedCount.value), 1)) : m("", !0) | ||
| ], 14, st)) : m("", !0), | ||
| e.featureFlags.canUseAiGeneration.value && e.featureFlags.hasTemplateSaved.value ? (i(), p("div", { | ||
| key: 4, | ||
| ref: (t) => e.panelState.aiMenuRef.value = t, | ||
| class: "tpl:relative" | ||
| }, [x("button", { | ||
| "aria-expanded": e.panelState.aiMenuOpen.value, | ||
| class: b(["tpl-ai-btn tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-4 tpl:py-2 tpl:text-sm tpl:font-semibold tpl:whitespace-nowrap tpl:transition-all tpl:duration-200", e.panelState.aiButtonActive.value ? "tpl-ai-btn--active" : "tpl-ai-btn--idle"]), | ||
| onClick: l[1] ||= _((...t) => e.panelState.toggleAiMenu && e.panelState.toggleAiMenu(...t), ["stop"]) | ||
| }, [D(h(ie), { | ||
| size: 16, | ||
| "stroke-width": 2, | ||
| class: "tpl-ai-btn-icon" | ||
| }), T(" " + d(h(a).aiChat.button), 1)], 10, lt), D(y, { | ||
| "enter-active-class": "tpl:transition-all tpl:duration-150 tpl:ease-out", | ||
| "enter-from-class": "tpl:scale-95 tpl:opacity-0", | ||
| "enter-to-class": "tpl:scale-100 tpl:opacity-100", | ||
| "leave-active-class": "tpl:transition-all tpl:duration-100 tpl:ease-in", | ||
| "leave-from-class": "tpl:scale-100 tpl:opacity-100", | ||
| "leave-to-class": "tpl:scale-95 tpl:opacity-0" | ||
| }, { | ||
| default: s(() => [e.panelState.aiMenuOpen.value ? (i(), p("div", ut, [D(h(r), { | ||
| "active-feature": e.panelState.activeAiFeature.value, | ||
| onSelect: e.panelState.handleAiFeatureSelect | ||
| }, null, 8, ["active-feature", "onSelect"])])) : m("", !0)]), | ||
| _: 1 | ||
| })], 512)) : m("", !0), | ||
| e.testEmail.isEnabled.value && e.featureFlags.canSendTestEmail.value ? (i(), p("button", { | ||
| key: 5, | ||
| class: b(h($)), | ||
| style: { | ||
| "background-color": "transparent", | ||
| color: "var(--tpl-primary)", | ||
| "border-color": "var(--tpl-primary)" | ||
| }, | ||
| disabled: e.testEmail.isSending.value || !e.featureFlags.hasTemplateSaved.value, | ||
| onClick: l[2] ||= (t) => e.panelState.testEmailModalOpen.value = !0 | ||
| }, [e.testEmail.isSending.value ? (i(), v(h(Y), { | ||
| key: 1, | ||
| class: "tpl-spinner", | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| })) : (i(), v(h(ne), { | ||
| key: 0, | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| })), T(" " + d(h(a).testEmail.button), 1)], 10, dt)) : m("", !0), | ||
| x("button", { | ||
| class: b(h($)), | ||
| style: { | ||
| "background-color": "transparent", | ||
| color: "var(--tpl-primary)", | ||
| "border-color": "var(--tpl-primary)" | ||
| }, | ||
| disabled: e.isSaveDisabled, | ||
| onClick: l[3] ||= (e) => c.$emit("save") | ||
| }, [e.isSaving ? (i(), v(h(Y), { | ||
| key: 1, | ||
| class: "tpl-spinner", | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| })) : (i(), v(h(J), { | ||
| key: 0, | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| })), T(" " + d(e.isSaving ? h(a).header.saving : h(a).header.save), 1)], 10, ft) | ||
| ]) | ||
| ])); | ||
| } | ||
| }), mt = /* @__PURE__ */ E({ | ||
| __name: "CloudPanels", | ||
| props: { | ||
| config: {}, | ||
| editor: {}, | ||
| core: {}, | ||
| panelState: {}, | ||
| planConfigInstance: {}, | ||
| testEmail: {}, | ||
| mediaLib: {} | ||
| }, | ||
| emits: ["send-test-email"], | ||
| setup(e, { expose: t, emit: n }) { | ||
| let r = u(() => import("./AiChatSidebar-A1CuWGY0.js")), a = u(() => import("./CommentsSidebar-DHdRiGPX.js")), o = u(() => import("./DesignReferenceSidebar-Dre99DTH.js")), s = u(() => import("./TemplateScoringPanel-CTk8WwxU.js")), l = u(() => import("./TestEmailModal-BJTDxFHh.js")), d = u(async () => { | ||
| try { | ||
| return (await import("./src-D6F_3XkE.js")).MediaLibraryModal; | ||
| } catch { | ||
| throw Error("[Templatical] Cloud media library requires the optional peer dependency '@templatical/media-library'. Please install it."); | ||
| } | ||
| }), f = n; | ||
| function m(e, t, n) { | ||
| t.history.record(), n.setContent(e), t.conditionPreview.reset(); | ||
| } | ||
| let g = c(null); | ||
| function _(e) { | ||
| g.value?.filterByBlock(e); | ||
| } | ||
| return t({ filterCommentsByBlock: _ }), (t, n) => (i(), p(w, null, [ | ||
| D(h(r), { | ||
| visible: e.panelState.aiChatOpen.value, | ||
| "on-apply": (t) => m(t, e.core, e.editor), | ||
| onClose: n[0] ||= (t) => e.panelState.aiChatOpen.value = !1 | ||
| }, null, 8, ["visible", "on-apply"]), | ||
| D(h(s), { | ||
| visible: e.panelState.scoringPanelOpen.value, | ||
| onClose: n[1] ||= (t) => e.panelState.scoringPanelOpen.value = !1 | ||
| }, null, 8, ["visible"]), | ||
| D(h(o), { | ||
| visible: e.panelState.designReferenceOpen.value, | ||
| "has-existing-blocks": e.editor.content.value.blocks.length > 0, | ||
| onClose: n[2] ||= (t) => e.panelState.designReferenceOpen.value = !1, | ||
| onApply: n[3] ||= (t) => m(t, e.core, e.editor) | ||
| }, null, 8, ["visible", "has-existing-blocks"]), | ||
| D(h(a), { | ||
| ref_key: "commentsSidebar", | ||
| ref: g, | ||
| visible: e.panelState.commentsOpen.value, | ||
| onClose: n[4] ||= (t) => e.panelState.commentsOpen.value = !1 | ||
| }, null, 8, ["visible"]), | ||
| D(h(l), { | ||
| visible: e.panelState.testEmailModalOpen.value, | ||
| "allowed-emails": e.testEmail.allowedEmails.value, | ||
| "is-sending": e.testEmail.isSending.value, | ||
| error: e.testEmail.error.value, | ||
| onSend: n[5] ||= (e) => f("send-test-email", e), | ||
| onClose: n[6] ||= (t) => e.panelState.testEmailModalOpen.value = !1 | ||
| }, null, 8, [ | ||
| "visible", | ||
| "allowed-emails", | ||
| "is-sending", | ||
| "error" | ||
| ]), | ||
| D(h(d), { | ||
| visible: e.panelState.mediaLibraryOpen.value, | ||
| accept: e.panelState.mediaLibraryAccept.value, | ||
| "popover-target": e.core.popoverRoot.value, | ||
| onSelect: e.mediaLib.handleMediaSelect, | ||
| onClose: e.mediaLib.handleMediaLibraryClose | ||
| }, null, 8, [ | ||
| "visible", | ||
| "accept", | ||
| "popover-target", | ||
| "onSelect", | ||
| "onClose" | ||
| ]) | ||
| ], 64)); | ||
| } | ||
| }), ht = { | ||
| key: 0, | ||
| class: "tpl-loading tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:bg-[var(--tpl-bg)]" | ||
| }, gt = { class: "tpl:flex tpl:flex-1 tpl:overflow-hidden" }, _t = { class: "tpl:flex tpl:w-12 tpl:shrink-0 tpl:flex-col tpl:items-center tpl:gap-4 tpl:py-5 tpl:border-r tpl:border-[var(--tpl-border)]" }, vt = /* @__PURE__ */ E({ | ||
| __name: "CloudLoadingOverlay", | ||
| props: { visible: { type: Boolean } }, | ||
| setup(e) { | ||
| return (t, n) => e.visible ? (i(), p("div", ht, [n[1] ||= l("<div class=\"tpl:flex tpl:h-14 tpl:shrink-0 tpl:items-center tpl:justify-between tpl:px-4 tpl:border-b tpl:border-[var(--tpl-border)]\"><div class=\"tpl-shimmer tpl:h-5 tpl:w-28 tpl:rounded-[var(--tpl-radius-sm)]\"></div><div class=\"tpl:flex tpl:gap-3\"><div class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"></div><div class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"></div></div></div>", 1), x("div", gt, [x("div", _t, [(i(), p(w, null, o(5, (e) => x("div", { | ||
| key: e, | ||
| class: "tpl-shimmer tpl:size-7 tpl:rounded-[var(--tpl-radius-sm)]" | ||
| })), 64))]), n[0] ||= l("<div class=\"tpl:flex tpl:flex-1 tpl:items-start tpl:justify-center tpl:overflow-auto tpl:p-8 tpl:bg-[var(--tpl-canvas-bg)]\"><div class=\"tpl:w-full tpl:max-w-[600px] tpl:rounded-[var(--tpl-radius)] tpl:p-6 tpl:bg-[var(--tpl-bg)] tpl:shadow-[var(--tpl-shadow-sm)]\"><div class=\"tpl:space-y-2 tpl:py-4\"><div class=\"tpl-shimmer tpl:h-3 tpl:w-3/4 tpl:rounded\"></div><div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div><div class=\"tpl-shimmer tpl:h-3 tpl:w-5/6 tpl:rounded\"></div></div><div class=\"tpl:py-4\"><div class=\"tpl-shimmer tpl:h-44 tpl:w-full tpl:rounded-[var(--tpl-radius-sm)]\"></div></div><div class=\"tpl:space-y-2 tpl:py-4\"><div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div><div class=\"tpl-shimmer tpl:h-3 tpl:w-2/3 tpl:rounded\"></div></div><div class=\"tpl:flex tpl:justify-center tpl:py-4\"><div class=\"tpl-shimmer tpl:h-10 tpl:w-36 tpl:rounded-[var(--tpl-radius-sm)]\"></div></div><div class=\"tpl:space-y-2 tpl:py-4\"><div class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/2 tpl:rounded\"></div><div class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/3 tpl:rounded\"></div></div></div></div><div class=\"tpl:flex tpl:w-[320px] tpl:shrink-0 tpl:flex-col tpl:gap-4 tpl:p-4 tpl:border-l tpl:border-[var(--tpl-border)]\"><div class=\"tpl-shimmer tpl:h-8 tpl:rounded-[var(--tpl-radius-sm)]\"></div><div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div><div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div></div>", 2)])])) : m("", !0); | ||
| } | ||
| }), yt = { | ||
| key: 0, | ||
| role: "alert", | ||
| class: "tpl-error tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-6 tpl:px-8 tpl:bg-[var(--tpl-bg)]" | ||
| }, bt = { class: "tpl:flex tpl:size-16 tpl:items-center tpl:justify-center tpl:rounded-full tpl:bg-[var(--tpl-danger-light)]" }, xt = { class: "tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:text-center" }, St = { class: "tpl:text-lg tpl:font-semibold tpl:text-[var(--tpl-text)]" }, Ct = { class: "tpl:max-w-md tpl:text-sm tpl:text-[var(--tpl-text-muted)]" }, wt = /* @__PURE__ */ E({ | ||
| __name: "CloudErrorOverlay", | ||
| props: { | ||
| error: {}, | ||
| visible: { type: Boolean } | ||
| }, | ||
| emits: ["retry"], | ||
| setup(e, { emit: t }) { | ||
| let n = t, { t: r } = X(); | ||
| function a(e) { | ||
| return "isUnauthorized" in e && e.isUnauthorized ? r.error.authFailed : "isNotFound" in e && e.isNotFound ? r.error.templateNotFound : r.error.defaultMessage; | ||
| } | ||
| function o(e) { | ||
| return "isNotFound" in e && !!e.isNotFound; | ||
| } | ||
| return (t, s) => e.visible && e.error ? (i(), p("div", yt, [ | ||
| x("div", bt, [D(h(G), { | ||
| size: 32, | ||
| "stroke-width": 1.5, | ||
| class: "tpl:text-[var(--tpl-danger)]" | ||
| })]), | ||
| x("div", xt, [x("h2", St, d(h(r).error.title), 1), x("p", Ct, d(a(e.error)), 1)]), | ||
| o(e.error) ? m("", !0) : (i(), p("button", { | ||
| key: 0, | ||
| class: "tpl-btn tpl-btn-primary tpl:inline-flex tpl:items-center tpl:gap-2 tpl:rounded-md tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]", | ||
| onClick: s[0] ||= (e) => n("retry") | ||
| }, d(h(r).error.retry), 1)) | ||
| ])) : m("", !0); | ||
| } | ||
| }), Tt = { | ||
| key: 0, | ||
| class: "tpl-preview-banner tpl:absolute tpl:top-14 tpl:right-0 tpl:left-0 tpl:z-40 tpl:flex tpl:items-center tpl:justify-center tpl:gap-4 tpl:px-4 tpl:py-3 tpl:bg-[var(--tpl-primary-light)] tpl:border-b tpl:border-[var(--tpl-primary)]" | ||
| }, Et = { class: "tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]" }, Dt = { class: "tpl:flex tpl:items-center tpl:gap-2" }, Ot = /* @__PURE__ */ E({ | ||
| __name: "SnapshotPreviewBanner", | ||
| props: { visible: { type: Boolean } }, | ||
| emits: ["cancel", "confirm"], | ||
| setup(e, { emit: t }) { | ||
| let n = t, { t: r } = X(); | ||
| return (t, a) => e.visible ? (i(), p("div", Tt, [x("div", Et, [D(h(K), { | ||
| size: 18, | ||
| "stroke-width": 2, | ||
| class: "tpl:text-[var(--tpl-primary)]" | ||
| }), x("span", null, d(h(r).snapshotPreview.message), 1)]), x("div", Dt, [x("button", { | ||
| class: "tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-150 tpl:text-[var(--tpl-text-muted)] tpl:border tpl:border-[var(--tpl-border)]", | ||
| style: { "background-color": "transparent" }, | ||
| onClick: a[0] ||= (e) => n("cancel") | ||
| }, d(h(r).snapshotPreview.cancel), 1), x("button", { | ||
| class: "tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]", | ||
| onClick: a[1] ||= (e) => n("confirm") | ||
| }, d(h(r).snapshotPreview.restore), 1)])])) : m("", !0); | ||
| } | ||
| }), kt = { | ||
| key: 0, | ||
| role: "status", | ||
| "aria-live": "polite", | ||
| class: "tpl:absolute tpl:top-16 tpl:left-1/2 tpl:z-toast tpl:-translate-x-1/2 tpl:rounded-[var(--tpl-radius)] tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:shadow-lg", | ||
| style: { | ||
| "background-color": "var(--tpl-warning-light)", | ||
| color: "var(--tpl-text)", | ||
| border: "1px solid var(--tpl-warning)" | ||
| } | ||
| }, At = /* @__PURE__ */ E({ | ||
| __name: "CollabUndoToast", | ||
| props: { visible: { type: Boolean } }, | ||
| setup(e) { | ||
| let { t } = W(); | ||
| return (n, r) => e.visible ? (i(), p("div", kt, d(h(t).history.collabWarning), 1)) : m("", !0); | ||
| } | ||
| }), jt = ["data-tpl-theme"], Mt = { class: "tpl:sticky tpl:top-0 tpl:z-40 tpl:h-0" }, Nt = { class: "tpl-main tpl:flex tpl:justify-center tpl:p-8" }, Pt = ["aria-label"], Ft = /*#__PURE__*/ ae(/* @__PURE__ */ E({ | ||
| __name: "CloudEditor", | ||
| props: { | ||
| config: {}, | ||
| translations: {}, | ||
| cloudTranslations: {}, | ||
| fontsManager: {}, | ||
| shadowRoot: {} | ||
| }, | ||
| emits: ["ready"], | ||
| setup(e, { expose: r, emit: o }) { | ||
| let l = u(() => import("./SavedBlocksPanels-D-pA_wfk.js")), f = e; | ||
| a(z, f.cloudTranslations); | ||
| let w = o, E = c(null), O = c(null), k = ze({ | ||
| config: f.config, | ||
| translations: f.translations, | ||
| fontsManager: f.fontsManager, | ||
| emit: w, | ||
| getCommentsSidebar: () => E.value ? { filterByBlock: E.value.filterCommentsByBlock } : null, | ||
| editorRoot: f.shadowRoot, | ||
| containerEl: O | ||
| }), { isInitializing: A, isAuthReady: j, initError: N, planConfigInstance: P, websocket: F, collaboration: I, isCollaborationEnabled: L, editor: R, core: B, featureFlags: V, mediaLib: H, exporter: U, testEmail: W, commentsInstance: G, savedBlocks: K, panelState: q, snapshotPreview: J, collabWarning: te, setThemeOverrides: ne, setUiTheme: Y } = k, { showNotice: re } = M(() => f.config.smallScreenNotice); | ||
| async function ie(e) { | ||
| try { | ||
| await W.sendTestEmail(e), q.testEmailModalOpen.value = !1; | ||
| } catch {} | ||
| } | ||
| async function ae() { | ||
| try { | ||
| await J.confirmRestoreSnapshot(); | ||
| } catch {} | ||
| } | ||
| let X = Ve({ | ||
| config: f.config, | ||
| editor: R, | ||
| websocket: F, | ||
| planConfigInstance: P, | ||
| snapshotPreview: J, | ||
| core: B, | ||
| exporter: U, | ||
| featureFlags: V, | ||
| isDestroyed: k.isDestroyed | ||
| }), Z = He({ | ||
| issues: B.templateLint ? B.templateLint.issues : c([]), | ||
| planConfig: P.config | ||
| }); | ||
| async function Q() { | ||
| await Z.tryRunSave(() => X.saveTemplate().catch((e) => f.config.onError?.(e))); | ||
| } | ||
| return k.onSaveHook.value = Q, t(() => { | ||
| k.initialize(); | ||
| }), g(() => { | ||
| k.destroy(); | ||
| }), r({ | ||
| getContent: () => R.content.value, | ||
| setContent: (e) => R.setContent(e), | ||
| setTheme: Y, | ||
| setThemeOverrides: ne, | ||
| create: X.createTemplate, | ||
| load: X.loadTemplate, | ||
| save: X.saveTemplate, | ||
| sendTestEmail: W.sendTestEmail | ||
| }), (t, r) => (i(), p("div", { | ||
| ref_key: "rootEl", | ||
| ref: O, | ||
| class: b(["tpl tpl:relative tpl:h-full tpl:overflow-hidden", { "tpl:dark": h(R).state.darkMode }]), | ||
| "data-tpl-theme": h(B).resolvedTheme.value, | ||
| style: C(h(B).themeStyles.value), | ||
| onDragover: r[6] ||= _(() => {}, ["prevent"]), | ||
| onDrop: r[7] ||= _(() => {}, ["prevent"]) | ||
| }, [ | ||
| D(ge), | ||
| D(y, { | ||
| "enter-active-class": "tpl:transition-opacity tpl:duration-200", | ||
| "enter-from-class": "tpl:opacity-100", | ||
| "enter-to-class": "tpl:opacity-100", | ||
| "leave-active-class": "tpl:transition-opacity tpl:duration-300", | ||
| "leave-from-class": "tpl:opacity-100", | ||
| "leave-to-class": "tpl:opacity-0" | ||
| }, { | ||
| default: s(() => [D(vt, { visible: h(A) || h(R).state.isLoading }, null, 8, ["visible"])]), | ||
| _: 1 | ||
| }), | ||
| D(y, { | ||
| "enter-active-class": "tpl:transition-opacity tpl:duration-200", | ||
| "enter-from-class": "tpl:opacity-0", | ||
| "enter-to-class": "tpl:opacity-100", | ||
| "leave-active-class": "tpl:transition-opacity tpl:duration-300", | ||
| "leave-from-class": "tpl:opacity-100", | ||
| "leave-to-class": "tpl:opacity-0" | ||
| }, { | ||
| default: s(() => [D(wt, { | ||
| error: h(N), | ||
| visible: !!h(N) && !h(A), | ||
| onRetry: h(k).initialize | ||
| }, null, 8, [ | ||
| "error", | ||
| "visible", | ||
| "onRetry" | ||
| ])]), | ||
| _: 1 | ||
| }), | ||
| D(pt, { | ||
| editor: h(R), | ||
| core: h(B), | ||
| "feature-flags": h(V), | ||
| "panel-state": h(q), | ||
| "snapshot-preview": h(J), | ||
| "comments-instance": h(G), | ||
| "test-email": h(W), | ||
| websocket: h(F), | ||
| collaboration: h(I), | ||
| "is-collaboration-enabled": h(L), | ||
| "is-saving": h(R).state.isSaving || h(V).isSaveExporting.value, | ||
| "is-save-disabled": h(R).state.isSaving || h(V).isSaveExporting.value || !h(R).state.isDirty, | ||
| onSave: Q | ||
| }, null, 8, [ | ||
| "editor", | ||
| "core", | ||
| "feature-flags", | ||
| "panel-state", | ||
| "snapshot-preview", | ||
| "comments-instance", | ||
| "test-email", | ||
| "websocket", | ||
| "collaboration", | ||
| "is-collaboration-enabled", | ||
| "is-saving", | ||
| "is-save-disabled" | ||
| ]), | ||
| D(Qe, { | ||
| open: h(Z).modalOpen.value, | ||
| issues: h(Z).blockingIssues.value, | ||
| onCancel: h(Z).cancel, | ||
| onConfirm: h(Z).confirmAndSave | ||
| }, null, 8, [ | ||
| "open", | ||
| "issues", | ||
| "onCancel", | ||
| "onConfirm" | ||
| ]), | ||
| D(Ot, { | ||
| visible: h(J).isPreviewingSnapshot.value, | ||
| onCancel: h(J).cancelPreview, | ||
| onConfirm: ae | ||
| }, null, 8, ["visible", "onCancel"]), | ||
| D(y, { | ||
| "enter-active-class": "tpl:transition-all tpl:duration-200 tpl:ease-out", | ||
| "enter-from-class": "tpl:translate-y-[-8px] tpl:opacity-0", | ||
| "enter-to-class": "tpl:translate-y-0 tpl:opacity-100", | ||
| "leave-active-class": "tpl:transition-all tpl:duration-300 tpl:ease-in", | ||
| "leave-from-class": "tpl:translate-y-0 tpl:opacity-100", | ||
| "leave-to-class": "tpl:translate-y-[-8px] tpl:opacity-0" | ||
| }, { | ||
| default: s(() => [D(At, { visible: h(te).collabUndoWarningVisible.value }, null, 8, ["visible"])]), | ||
| _: 1 | ||
| }), | ||
| n(D(le, null, null, 512), [[S, !h(R).state.previewMode]]), | ||
| x("div", { | ||
| class: b(["tpl-body tpl:absolute tpl:bottom-0 tpl:overflow-auto", [h(R).state.previewMode ? "tpl:left-0 tpl:right-0" : h(q).rightPanelOpen.value ? "tpl:left-12 tpl:right-[680px]" : "tpl:left-12 tpl:right-[320px]", h(J).isPreviewingSnapshot.value ? "tpl:top-[104px]" : "tpl:top-14"]]), | ||
| style: { | ||
| transition: "all 300ms cubic-bezier(0.34, 1.56, 0.64, 1)", | ||
| "background-color": "var(--tpl-canvas-bg)" | ||
| } | ||
| }, [x("div", Mt, [D(y, { name: "tpl-restore-btn" }, { | ||
| default: s(() => [h(B).conditionPreview.hasHiddenBlocks.value ? (i(), p("button", { | ||
| key: 0, | ||
| class: "tpl:absolute tpl:left-1/2 tpl:top-2 tpl:-translate-x-1/2 tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-full tpl:border tpl:px-3.5 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:whitespace-nowrap tpl:shadow-md tpl:hover:opacity-80", | ||
| style: { | ||
| "background-color": "var(--tpl-warning-light)", | ||
| color: "var(--tpl-warning)", | ||
| "border-color": "var(--tpl-warning)", | ||
| "backdrop-filter": "blur(8px)" | ||
| }, | ||
| onClick: r[0] ||= (e) => h(B).conditionPreview.reset() | ||
| }, [D(h(ee), { | ||
| size: 13, | ||
| "stroke-width": 2 | ||
| }), T(" " + d(h(B).t.blockSettings.restoreHiddenBlocks), 1)])) : m("", !0)]), | ||
| _: 1 | ||
| })]), x("main", Nt, [D(se, { | ||
| viewport: h(R).state.viewport, | ||
| content: h(R).content.value, | ||
| "selected-block-id": h(R).state.selectedBlockId, | ||
| "dark-mode": h(R).state.darkMode, | ||
| "preview-mode": h(R).state.previewMode, | ||
| "locked-blocks": h(I)?.lockedBlocks.value ?? void 0, | ||
| onSelectBlock: h(R).selectBlock, | ||
| onOpenAiChat: r[1] ||= (e) => h(q).aiChatOpen.value = !0, | ||
| onOpenDesignReference: r[2] ||= (e) => h(q).designReferenceOpen.value = !0 | ||
| }, null, 8, [ | ||
| "viewport", | ||
| "content", | ||
| "selected-block-id", | ||
| "dark-mode", | ||
| "preview-mode", | ||
| "locked-blocks", | ||
| "onSelectBlock" | ||
| ])])], 2), | ||
| e.config.branding !== !1 && !h(V).isWhiteLabeled.value ? (i(), v(pe, { | ||
| key: 0, | ||
| "position-class": [h(R).state.previewMode ? "tpl:left-0 tpl:right-0" : h(q).rightPanelOpen.value ? "tpl:left-12 tpl:right-[680px]" : "tpl:left-12 tpl:right-[320px]"] | ||
| }, null, 8, ["position-class"])) : m("", !0), | ||
| x("div", { | ||
| class: "tpl-sr-only", | ||
| role: "status", | ||
| "aria-live": "polite", | ||
| "aria-atomic": "true", | ||
| "aria-label": h(B).t.landmarks.reorderAnnouncements | ||
| }, d(h(B).keyboardReorder.announcement.value), 9, Pt), | ||
| n(D(oe, { | ||
| "selected-block": h(R).selectedBlock.value, | ||
| settings: h(R).content.value.settings, | ||
| "shifted-left": h(q).rightPanelOpen.value, | ||
| onUpdateBlock: r[3] ||= (e) => h(R).updateBlock(h(R).selectedBlock.value.id, e), | ||
| onDeleteBlock: r[4] ||= (e) => h(B).blockActions.deleteBlock(h(R).selectedBlock.value.id), | ||
| onDuplicateBlock: r[5] ||= (e) => h(B).blockActions.duplicateBlock(h(R).selectedBlock.value), | ||
| onUpdateSettings: h(R).updateSettings | ||
| }, null, 8, [ | ||
| "selected-block", | ||
| "settings", | ||
| "shifted-left", | ||
| "onUpdateSettings" | ||
| ]), [[S, !h(R).state.previewMode]]), | ||
| !h(A) && h(j) ? (i(), v(mt, { | ||
| key: 1, | ||
| ref_key: "cloudPanelsRef", | ||
| ref: E, | ||
| config: f.config, | ||
| editor: h(R), | ||
| core: h(B), | ||
| "panel-state": h(q), | ||
| "plan-config-instance": h(P), | ||
| "test-email": h(W), | ||
| "media-lib": h(H), | ||
| onSendTestEmail: ie | ||
| }, null, 8, [ | ||
| "config", | ||
| "editor", | ||
| "core", | ||
| "panel-state", | ||
| "plan-config-instance", | ||
| "test-email", | ||
| "media-lib" | ||
| ])) : m("", !0), | ||
| h(K).isAvailable.value ? (i(), v(h(l), { | ||
| key: 2, | ||
| feature: h(K) | ||
| }, null, 8, ["feature"])) : m("", !0), | ||
| x("div", { | ||
| ref: (e) => h(B).popoverRoot.value = e, | ||
| class: "tpl-popover-root" | ||
| }, null, 512), | ||
| D(ue), | ||
| D(he), | ||
| h(re) ? (i(), v(me, { key: 3 })) : m("", !0) | ||
| ], 46, jt)); | ||
| } | ||
| }), [["__scopeId", "data-v-04940787"]]); | ||
| //#endregion | ||
| export { Ft as default }; | ||
| //# sourceMappingURL=CloudEditor-B3hqbwfP.js.map |
Sorry, the diff of this file is too big to display
| import { B as e, F as t, G as n, H as r, I as i, J as a, K as o, L as s, P as c, R as l, U as u, V as d, W as f, z as p } from "./src-BKI1x1k7.js"; | ||
| //#endregion | ||
| //#region ../renderer/src/render-context.ts | ||
| var m = `https://cdn.jsdelivr.net/npm/@templatical/renderer@${{ | ||
| name: "@templatical/renderer", | ||
| description: "Render Templatical email templates to MJML", | ||
| version: "0.21.1", | ||
| bugs: "https://github.com/templatical/sdk/issues", | ||
| dependencies: { "@templatical/types": "workspace:*" }, | ||
| devDependencies: { | ||
| "@resvg/resvg-js": "^2.6.2", | ||
| mjml: "^5.4.0", | ||
| typescript: "^6.0.3", | ||
| vitest: "^4.1.10" | ||
| }, | ||
| exports: { ".": { | ||
| types: "./dist/index.d.ts", | ||
| import: "./dist/index.js" | ||
| } }, | ||
| files: ["dist", "assets"], | ||
| homepage: "https://templatical.com", | ||
| keywords: [ | ||
| "email", | ||
| "email-template", | ||
| "html-email", | ||
| "mjml", | ||
| "renderer", | ||
| "templatical" | ||
| ], | ||
| license: "MIT", | ||
| module: "./dist/index.js", | ||
| publishConfig: { access: "public" }, | ||
| repository: { | ||
| type: "git", | ||
| url: "git+https://github.com/templatical/sdk.git", | ||
| directory: "packages/renderer" | ||
| }, | ||
| scripts: { | ||
| build: "tsdown && node scripts/rasterize-social.mjs", | ||
| test: "vitest run --config vitest.config.ts", | ||
| typecheck: "tsc --noEmit" | ||
| }, | ||
| type: "module", | ||
| types: "./dist/index.d.ts" | ||
| }.version}/assets/social`, h = { | ||
| arial: "Arial, sans-serif", | ||
| helvetica: "Helvetica, sans-serif", | ||
| georgia: "Georgia, serif", | ||
| "times new roman": "'Times New Roman', serif", | ||
| verdana: "Verdana, sans-serif", | ||
| "trebuchet ms": "'Trebuchet MS', sans-serif", | ||
| "courier new": "'Courier New', monospace", | ||
| tahoma: "Tahoma, sans-serif" | ||
| }, g = class e { | ||
| containerWidth; | ||
| customFonts; | ||
| defaultFallbackFont; | ||
| allowHtmlBlocks; | ||
| customBlockHtml; | ||
| socialIconsBaseUrl; | ||
| constructor(e, t, n, r, i = /* @__PURE__ */ new Map(), a = m) { | ||
| this.containerWidth = e, this.customFonts = t, this.defaultFallbackFont = n, this.allowHtmlBlocks = r, this.customBlockHtml = i, this.socialIconsBaseUrl = a; | ||
| } | ||
| withContainerWidth(t) { | ||
| return new e(t, this.customFonts, this.defaultFallbackFont, this.allowHtmlBlocks, this.customBlockHtml, this.socialIconsBaseUrl); | ||
| } | ||
| resolveFontFamily(e) { | ||
| for (let t of this.customFonts) if (t.name.toLowerCase() === e.toLowerCase()) { | ||
| let e = t.fallback ?? this.defaultFallbackFont; | ||
| return `'${t.name}', ${e}`; | ||
| } | ||
| return h[e.toLowerCase()] || e; | ||
| } | ||
| }, _ = { | ||
| "&": "&", | ||
| "<": "<", | ||
| ">": ">", | ||
| "\"": """, | ||
| "'": "'" | ||
| }, v = /[&<>"']/g; | ||
| function y(e) { | ||
| return e === "" ? "" : e.replace(v, (e) => _[e] ?? e); | ||
| } | ||
| function b(e) { | ||
| return e === "" ? "" : e.replace(v, (e) => _[e] ?? e); | ||
| } | ||
| function x(e) { | ||
| return e === "" ? "" : b(e).replace(/[;{}\r\n]/g, ""); | ||
| } | ||
| function S(e) { | ||
| return e === "" ? "" : C(e, (e) => w(e, "data-merge-tag") ?? w(e, "data-logic-merge-tag")); | ||
| } | ||
| function C(e, t) { | ||
| let n = "", r = 0; | ||
| for (; r < e.length;) { | ||
| let i = e.indexOf("<span", r); | ||
| if (i === -1) { | ||
| n += e.substring(r); | ||
| break; | ||
| } | ||
| let a = e[i + 5]; | ||
| if (a !== ">" && a !== " " && a !== " " && a !== "\n" && a !== "\r" && a !== "/") { | ||
| n += e.substring(r, i + 5), r = i + 5; | ||
| continue; | ||
| } | ||
| let o = e.indexOf(">", i + 5); | ||
| if (o === -1) { | ||
| n += e.substring(r); | ||
| break; | ||
| } | ||
| let s = e.indexOf("</span>", o + 1); | ||
| if (s === -1) { | ||
| n += e.substring(r); | ||
| break; | ||
| } | ||
| let c = t(e.substring(i + 5, o)); | ||
| if (c === null) { | ||
| n += e.substring(r, i + 5), r = i + 5; | ||
| continue; | ||
| } | ||
| n += e.substring(r, i), n += c, r = s + 7; | ||
| } | ||
| return n; | ||
| } | ||
| function w(e, t) { | ||
| let n = RegExp(`(?:^|\\s)${t}="([^"<>]*)"`).exec(e); | ||
| return n ? n[1] : null; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/padding.ts | ||
| function T(e) { | ||
| return `${e.top}px ${e.right}px ${e.bottom}px ${e.left}px`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/utils.ts | ||
| function E(e, t) { | ||
| return e ? ` ${t === "native" ? "background-color" : "container-background-color"}="${e}"` : ""; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/visibility.ts | ||
| function D(e) { | ||
| let t = e.visibility; | ||
| return t ? !t.desktop && !t.mobile : !1; | ||
| } | ||
| function O(e) { | ||
| let t = k(e); | ||
| return t === "" ? "" : ` css-class="${t}"`; | ||
| } | ||
| function k(e) { | ||
| let t = e.visibility; | ||
| if (!t) return ""; | ||
| let n = []; | ||
| return t.desktop || n.push("tpl-hide-desktop"), t.mobile || n.push("tpl-hide-mobile"), n.join(" "); | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/title.ts | ||
| function A(e, t) { | ||
| if (D(e)) return ""; | ||
| let n = T(e.styles.padding), r = E(e.styles.backgroundColor, "container"), i = j(S(e.content)), o = a[e.level] ?? a[2], s = e.color ? `\n color="${b(e.color)}"` : "", c = e.textAlign, l = ee(e.fontFamily, t), u = O(e), d = `h${a[e.level] ? e.level : 2}`; | ||
| return `<mj-text | ||
| font-size="${o}px"${s} | ||
| align="${c}" | ||
| line-height="1.3" | ||
| padding="${n}"${r}${l}${u} | ||
| ><${d} style="margin:0;font-size:inherit;color:inherit;line-height:inherit">${i}</${d}></mj-text>`; | ||
| } | ||
| function j(e) { | ||
| let t = e.match(/^\s*<p\b[^>]*>([\s\S]*)<\/p>\s*$/); | ||
| return !t || /<\/p>\s*<p\b/i.test(t[1]) ? e : t[1]; | ||
| } | ||
| function ee(e, t) { | ||
| return e ? ` font-family="${t.resolveFontFamily(e)}"` : ""; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/paragraph.ts | ||
| function te(e, t) { | ||
| if (D(e) || e.content.replace(/<\/?p\b[^<>]*>/gi, "").trim() === "") return ""; | ||
| let n = T(e.styles.padding), r = E(e.styles.backgroundColor, "container"), i = S(e.content); | ||
| return `<mj-text | ||
| line-height="1.5" | ||
| padding="${n}"${r}${O(e)} | ||
| >${i}</mj-text>`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/image.ts | ||
| function ne(e, t) { | ||
| if (D(e) || e.src === "") return ""; | ||
| let n = T(e.styles.padding), r = E(e.styles.backgroundColor, "container"), i = e.width === "full" ? t.containerWidth + "px" : e.width + "px", a = O(e), o = ""; | ||
| e.linkUrl && (o = ` href="${b(e.linkUrl)}"`, e.linkOpenInNewTab && (o += " target=\"_blank\" rel=\"noopener\"")); | ||
| let s = b(e.src), c = e.decorative === !0; | ||
| return `<mj-image | ||
| src="${s}" | ||
| alt="${c ? "" : b(e.alt)}" | ||
| width="${i}" | ||
| align="${e.align}" | ||
| padding="${n}"${r}${o}${a}${c ? " role=\"presentation\"" : ""} | ||
| />`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/button.ts | ||
| function M(e, t) { | ||
| if (D(e)) return ""; | ||
| let n = T(e.styles.padding), r = E(e.styles.backgroundColor, "container"), i = T(e.buttonPadding), a = e.url === "" ? "" : b(e.url), o = a === "" ? "" : ` href="${a}"`, s = b(e.backgroundColor), c = b(e.textColor), l = e.fontSize, u = e.borderRadius, d = y(e.text); | ||
| return `<mj-button${o}${e.openInNewTab ? " target=\"_blank\" rel=\"noopener\"" : ""} | ||
| background-color="${s}" | ||
| color="${c}" | ||
| font-size="${l}px" | ||
| font-weight="bold" | ||
| border-radius="${u}px" | ||
| inner-padding="${i}" | ||
| padding="${n}"${r}${N(e.fontFamily, t)}${P(e.width)}${O(e)} | ||
| >${d}</mj-button>`; | ||
| } | ||
| function N(e, t) { | ||
| return e ? ` font-family="${t.resolveFontFamily(e)}"` : ""; | ||
| } | ||
| function P(e) { | ||
| return e === void 0 ? "" : ` width="${e === "full" ? "100%" : `${e}px`}"`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/divider.ts | ||
| function F(e, t) { | ||
| if (D(e)) return ""; | ||
| let n = T(e.styles.padding), r = e.styles.backgroundColor ? ` container-background-color="${b(e.styles.backgroundColor)}"` : "", i = e.width === "full" ? "100%" : e.width + "px"; | ||
| return `<mj-divider | ||
| border-width="${e.thickness}px" | ||
| border-style="${e.lineStyle}" | ||
| border-color="${b(e.color)}" | ||
| width="${i}" | ||
| padding="${n}"${r}${O(e)} | ||
| />`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/spacer.ts | ||
| function I(e, t) { | ||
| return D(e) ? "" : `<mj-spacer height="${e.height}px" padding="0"${e.styles.backgroundColor ? ` container-background-color="${b(e.styles.backgroundColor)}"` : ""}${O(e)} />`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/html.ts | ||
| function L(e, t) { | ||
| if (D(e) || !t.allowHtmlBlocks) return ""; | ||
| let n = e.content; | ||
| if (n === "") return ""; | ||
| let r = O(e); | ||
| return `<mj-text padding="${T(e.styles.padding)}"${E(e.styles.backgroundColor, "container")}${r}> | ||
| ${n} | ||
| </mj-text>`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/social.ts | ||
| function R(e, t) { | ||
| if (D(e)) return ""; | ||
| let n = e.icons; | ||
| if (n.length === 0) return ""; | ||
| let r = T(e.styles.padding), i = e.styles.backgroundColor ? ` container-background-color="${b(e.styles.backgroundColor)}"` : "", a = O(e), o = e.align, s = e.iconSize, c = e.iconStyle, l = e.spacing, u; | ||
| switch (s) { | ||
| case "small": | ||
| u = 24; | ||
| break; | ||
| case "large": | ||
| u = 48; | ||
| break; | ||
| default: | ||
| u = 32; | ||
| break; | ||
| } | ||
| let d; | ||
| switch (c) { | ||
| case "circle": | ||
| d = "50%"; | ||
| break; | ||
| case "rounded": | ||
| d = "8px"; | ||
| break; | ||
| case "square": | ||
| d = "0"; | ||
| break; | ||
| default: | ||
| d = "4px"; | ||
| break; | ||
| } | ||
| let f = n.length; | ||
| return `<mj-social | ||
| mode="horizontal" | ||
| align="${o}" | ||
| icon-padding="0" | ||
| padding="${r}"${i}${a} | ||
| > | ||
| ${n.map((e, n) => { | ||
| let r = e.platform, i = b(e.url), a = `${t.socialIconsBaseUrl}/${c}/${r}.png`, o = n === f - 1 ? 0 : l; | ||
| return `<mj-social-element src="${a}" href="${i}" icon-size="${u}px" padding="0 ${o}px 0 0" border-radius="${d}" background-color="transparent"></mj-social-element>`; | ||
| }).join("\n")} | ||
| </mj-social>`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/menu.ts | ||
| function z(e, t) { | ||
| if (D(e) || e.items.length === 0) return ""; | ||
| let n = T(e.styles.padding), r = E(e.styles.backgroundColor, "container"), i = O(e), a = H(e.fontFamily, t), o = e.textAlign; | ||
| return `<mj-text | ||
| font-size="${e.fontSize}px"${e.color ? `\n color="${b(e.color)}"` : ""} | ||
| align="${o}" | ||
| line-height="1.5" | ||
| padding="${n}"${r}${a}${i} | ||
| >${B(e)}</mj-text>`; | ||
| } | ||
| function B(e) { | ||
| let t = e.items, n = y(e.separator), r = x(e.separatorColor), i = e.spacing, a = e.linkColor ?? e.color, o = [], s = t.length; | ||
| for (let e = 0; e < s; e++) o.push(V(t[e], a)), e < s - 1 && o.push(`<span style="color: ${r}; padding: 0 ${i}px;">${n}</span>`); | ||
| return o.join(""); | ||
| } | ||
| function V(e, t) { | ||
| let n = y(e.text), r = b(e.url), i = e.color ?? t, a = e.openInNewTab ? " target=\"_blank\" rel=\"noopener\"" : "", o = ["text-decoration: none"]; | ||
| return i && o.unshift(`color: ${x(i)}`), e.bold && o.push("font-weight: bold"), e.underline && o.push("text-decoration: underline"), `<a href="${r}" style="${o.join("; ")}"${a}>${n}</a>`; | ||
| } | ||
| function H(e, t) { | ||
| return e ? ` font-family="${t.resolveFontFamily(e)}"` : ""; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/table.ts | ||
| function U(e, t) { | ||
| if (D(e) || e.rows.length === 0) return ""; | ||
| let n = T(e.styles.padding), r = E(e.styles.backgroundColor, "container"), i = O(e), a = q(e.fontFamily, t); | ||
| return `<mj-text | ||
| font-size="${e.fontSize}px"${e.color ? `\n color="${b(e.color)}"` : ""} | ||
| align="${e.textAlign}" | ||
| line-height="1.5" | ||
| padding="${n}"${r}${a}${i} | ||
| >${W(e)}</mj-text>`; | ||
| } | ||
| function W(e) { | ||
| let t = x(e.borderColor), n = e.borderWidth, r = ""; | ||
| for (let i = 0; i < e.rows.length; i++) { | ||
| let a = e.rows[i]; | ||
| r += G(a, e, e.hasHeaderRow && i === 0, t, n); | ||
| } | ||
| return `<table style="width: 100%; border-collapse: collapse;">${r}</table>`; | ||
| } | ||
| function G(e, t, n, r, i) { | ||
| let a = ""; | ||
| for (let o of e.cells) a += K(o, t, n, r, i); | ||
| return `<tr>${a}</tr>`; | ||
| } | ||
| function K(e, t, n, r, i) { | ||
| let a = t.cellPadding, o = [`border: ${i}px solid ${r}`, `padding: ${a}px`]; | ||
| n && (o.push("font-weight: bold"), t.headerBackgroundColor && o.push(`background-color: ${x(t.headerBackgroundColor)}`)); | ||
| let s = o.join("; "), c = y(S(e.content)), l = n ? "th" : "td"; | ||
| return `<${l} style="${s}">${c}</${l}>`; | ||
| } | ||
| function q(e, t) { | ||
| return e ? ` font-family="${t.resolveFontFamily(e)}"` : ""; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/custom.ts | ||
| function re(e, t) { | ||
| if (D(e)) return ""; | ||
| let n = t.customBlockHtml.get(e.id) ?? e.renderedHtml; | ||
| if (!n || n === "") return ""; | ||
| let r = O(e); | ||
| return `<mj-text padding="${T(e.styles.padding)}"${E(e.styles.backgroundColor, "container")}${r}> | ||
| ${n} | ||
| </mj-text>`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/columns.ts | ||
| function J(e) { | ||
| switch (e) { | ||
| case "2": return ["50%", "50%"]; | ||
| case "3": return [ | ||
| "33.33%", | ||
| "33.33%", | ||
| "33.34%" | ||
| ]; | ||
| case "1-2": return ["33.33%", "66.67%"]; | ||
| case "2-1": return ["66.67%", "33.33%"]; | ||
| default: return ["100%"]; | ||
| } | ||
| } | ||
| function Y(e, t) { | ||
| switch (e) { | ||
| case "2": return [t * .5, t * .5]; | ||
| case "3": return [ | ||
| t / 3, | ||
| t / 3, | ||
| t / 3 | ||
| ]; | ||
| case "1-2": return [t / 3, t * 2 / 3]; | ||
| case "2-1": return [t * 2 / 3, t / 3]; | ||
| default: return [t]; | ||
| } | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/display-condition.ts | ||
| function X(e, t) { | ||
| if (t === "") return ""; | ||
| let n = e.displayCondition; | ||
| return n ? `<mj-raw>${n.before}</mj-raw> | ||
| ` + t + ` | ||
| <mj-raw>${n.after}</mj-raw>` : t; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/section.ts | ||
| function ie(e, t, n) { | ||
| if (D(e)) return ""; | ||
| let r = e.columns, i = J(r), a = Y(r, t.containerWidth), o = T(e.styles.padding), s = E(e.styles.backgroundColor, "native"), c = O(e), l = e.borderRadius && e.borderRadius > 0 ? ` border-radius="${e.borderRadius}px"` : "", u = e.children, f = []; | ||
| for (let e = 0; e < u.length; e++) { | ||
| let r = u[e], o = i[e] ?? "100%", s = Math.floor(a[e] ?? t.containerWidth), c = ae(r, t.allowHtmlBlocks).filter((e) => !d(e)), l = t.withContainerWidth(s), p = c.map((e) => X(e, n(e, l))).filter((e) => e !== "").join("\n"); | ||
| f.push(`<mj-column width="${o}"> | ||
| ${p === "" ? "<mj-text> </mj-text>" : p} | ||
| </mj-column>`); | ||
| } | ||
| let p = f.join("\n"); | ||
| return `<mj-section${s} padding="${o}"${l}${c}> | ||
| ${e.stackOnMobile === !1 && f.length > 1 ? `<mj-group> | ||
| ${p} | ||
| </mj-group>` : p} | ||
| </mj-section>`; | ||
| } | ||
| function ae(e, t) { | ||
| return t ? e : e.filter((e) => e.type !== "html"); | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/video.ts | ||
| function oe(e, t) { | ||
| if (t) return t; | ||
| if (!e) return null; | ||
| for (let t of [/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/, /youtube\.com\/shorts\/([a-zA-Z0-9_-]{11})/]) { | ||
| let n = e.match(t); | ||
| if (n) return `https://img.youtube.com/vi/${n[1]}/maxresdefault.jpg`; | ||
| } | ||
| let n = e.match(/vimeo\.com\/(?:video\/)?(\d+)/); | ||
| return n ? `https://vumbnail.com/${n[1]}.jpg` : null; | ||
| } | ||
| function se(e, t) { | ||
| if (D(e)) return ""; | ||
| let n = oe(e.url, e.thumbnailUrl); | ||
| if (!n) return ""; | ||
| let r = T(e.styles.padding), i = E(e.styles.backgroundColor, "container"), a = e.width === "full" ? t.containerWidth + "px" : e.width + "px", o = O(e); | ||
| return `<mj-image | ||
| src="${b(n)}" | ||
| alt="${b(e.alt)}" | ||
| width="${a}" | ||
| align="${e.align}" | ||
| padding="${r}" | ||
| href="${b(e.url)}" | ||
| target="_blank" | ||
| rel="noopener"${i}${o} | ||
| />`; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/renderers/index.ts | ||
| function Z(a, m) { | ||
| return d(a) ? ie(a, m, Z) : n(a) ? A(a, m) : e(a) ? te(a, m) : l(a) ? ne(a, m) : c(a) ? M(a, m) : i(a) ? F(a, m) : u(a) ? I(a, m) : s(a) ? L(a, m) : r(a) ? R(a, m) : p(a) ? z(a, m) : f(a) ? U(a, m) : o(a) ? se(a, m) : t(a) ? re(a, m) : ""; | ||
| } | ||
| //#endregion | ||
| //#region ../renderer/src/index.ts | ||
| async function ce(e, t) { | ||
| let n = t?.customFonts ?? [], r = t?.defaultFallbackFont ?? "Arial, sans-serif", i = t?.allowHtmlBlocks ?? !0, a = fe(t?.socialIconsBaseUrl ?? m), o = await he(e, t?.renderCustomBlock), s = ge(e, t?.getCustomBlockStylesheet), c = new g(e.settings.width, n, r, i, o, a), l = Q(e.blocks, i), u = c.resolveFontFamily(e.settings.fontFamily), d = e.settings.backgroundColor, f = e.settings.textColor ? ` color="${b(e.settings.textColor)}"` : "", p = e.settings.linkColor ? x(e.settings.linkColor) : "inherit", h = e.settings.linkUnderline ? "underline" : "none", _ = l.map((e) => le(e, c)).filter((e) => e !== "").join("\n"), v = me(n), y = pe(e.settings.preheaderText); | ||
| return `<mjml lang="${b(e.settings.locale)}"> | ||
| <mj-head>${y} | ||
| <mj-attributes> | ||
| <mj-all font-family="${u}" /> | ||
| <mj-text font-size="14px"${f} /> | ||
| <mj-section padding="0" /> | ||
| <mj-column padding="0" /> | ||
| <mj-image fluid-on-mobile="true" /> | ||
| </mj-attributes>${v} | ||
| <mj-style> | ||
| a { color: ${p}; text-decoration: ${h}; } | ||
| @media only screen and (max-width: 480px) { | ||
| .tpl-hide-mobile { display: none !important; mso-hide: all !important; } | ||
| } | ||
| @media only screen and (min-width: 481px) { | ||
| .tpl-hide-desktop { display: none !important; mso-hide: all !important; } | ||
| } | ||
| </mj-style>${_e(s)} | ||
| </mj-head> | ||
| <mj-body width="${c.containerWidth}px" background-color="${d}"> | ||
| ${_} | ||
| </mj-body> | ||
| </mjml>`; | ||
| } | ||
| function le(e, t) { | ||
| if (d(e)) { | ||
| let n = Z(e, t); | ||
| return X(e, e.wrapper && n !== "" ? ue(n, e.wrapper) : n); | ||
| } | ||
| return X(e, de(Z(e, t))); | ||
| } | ||
| function ue(e, t) { | ||
| return `<mj-wrapper${E(t.backgroundColor, "native")}${` padding="${t.padding ? T(t.padding) : "0"}"`}${t.borderRadius && t.borderRadius > 0 ? ` border-radius="${t.borderRadius}px"` : ""}> | ||
| ${e} | ||
| </mj-wrapper>`; | ||
| } | ||
| function de(e) { | ||
| return e === "" ? "" : `<mj-section> | ||
| <mj-column> | ||
| ${e} | ||
| </mj-column> | ||
| </mj-section>`; | ||
| } | ||
| function fe(e) { | ||
| return e.endsWith("/") ? e.slice(0, -1) : e; | ||
| } | ||
| function pe(e) { | ||
| if (!e) return ""; | ||
| let t = e.trim(); | ||
| return t === "" ? "" : `\n <mj-preview>${y(t)}</mj-preview>`; | ||
| } | ||
| function me(e) { | ||
| return e.length === 0 ? "" : e.map((e) => `\n <mj-font name="${b(e.name)}" href="${b(e.url)}" />`).join(""); | ||
| } | ||
| function Q(e, t) { | ||
| return t ? e : e.filter((e) => e.type !== "html"); | ||
| } | ||
| async function he(e, t) { | ||
| let n = /* @__PURE__ */ new Map(); | ||
| if (!t) return n; | ||
| let r = []; | ||
| if ($(e.blocks, r), r.length === 0) return n; | ||
| let i = await Promise.all(r.map((e) => t(e))); | ||
| for (let e = 0; e < r.length; e++) n.set(r[e].id, i[e]); | ||
| return n; | ||
| } | ||
| function $(e, n) { | ||
| for (let r of e) { | ||
| if (t(r)) { | ||
| n.push(r); | ||
| continue; | ||
| } | ||
| if (d(r)) for (let e of r.children) $(e, n); | ||
| } | ||
| } | ||
| function ge(e, t) { | ||
| if (!t) return []; | ||
| let n = []; | ||
| if ($(e.blocks, n), n.length === 0) return []; | ||
| let r = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Set(), a = []; | ||
| for (let e of n) { | ||
| if (r.has(e.customType)) continue; | ||
| r.add(e.customType); | ||
| let n = t(e.customType); | ||
| if (!n) continue; | ||
| let o = n.trim(); | ||
| o === "" || i.has(o) || (i.add(o), a.push(o)); | ||
| } | ||
| return a; | ||
| } | ||
| function _e(e) { | ||
| return e.length === 0 ? "" : e.map((e) => `\n <mj-style>\n${e}\n </mj-style>`).join(""); | ||
| } | ||
| //#endregion | ||
| export { m as DEFAULT_SOCIAL_ICONS_BASE_URL, g as RenderContext, S as convertMergeTagsToValues, b as escapeAttr, y as escapeHtml, O as getCssClassAttr, k as getCssClasses, J as getWidthPercentages, Y as getWidthPixels, D as isHiddenOnAll, Z as renderBlock, ce as renderToMjml, T as toPaddingString }; | ||
| //# sourceMappingURL=src-DYji-wXB.js.map |
Sorry, the diff of this file is too big to display
| 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 c, R as l, Y as u, Z as d, _ as f, c as p, d as m, g as h, h as g, k as _, l as v, m as y, nt as b, p as x, r as S, s as C, u as w, z as T } from "./vue.runtime.esm-bundler-B0xoGkDv.js"; | ||
| import { c as E, o as D, t as O } from "./runtime-dom.esm-bundler-GdMI8WqG.js"; | ||
| import { n as k } from "./upload-BwwE0U6G.js"; | ||
| import { A } from "./dist-BJbqgyPn.js"; | ||
| import { E as j, V as M, t as N, x as P } from "./useEditorCore-CT1slufs.js"; | ||
| import { m as F } from "./dist-DYv_Pttj.js"; | ||
| import { s as I } from "./usePopoverPosition-BO2LNy5n.js"; | ||
| import { A as L, F as R, c as z, r as B, s as V, t as H, u as ee } from "./keys-CHPcp-Nz.js"; | ||
| import { t as U } from "./useI18n-D2L9Mkik.js"; | ||
| import { t as W } from "./createLucideIcon-JWHwTI6N.js"; | ||
| import { t as G } from "./check-BpQDKiPz.js"; | ||
| import { t as K } from "./circle-alert-CgtE46ND.js"; | ||
| import { a as q, c as te, d as ne, f as re, i as ie, 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-PcOPXvLO.js"; | ||
| import { t as me } from "./clock-BzKLLW5L.js"; | ||
| import { t as J } from "./loader-circle-C-Z6kgI6.js"; | ||
| import { t as Y } from "./message-circle-CEm5qckV.js"; | ||
| import { t as X } from "./send-B70aQDrA.js"; | ||
| import { t as Z } from "./sparkles-DVlm1Ipp.js"; | ||
| import { t as he } from "./triangle-alert-Djg6IG8N.js"; | ||
| import { t as ge } from "./_plugin-vue_export-helper-B3ysoDQm.js"; | ||
| import { n as Q } from "./useCloudI18n-BmiygEM4.js"; | ||
| import { d as $ } from "./ColorPicker-s9GHXjZs.js"; | ||
| import { _ as _e, c as ve, f as ye, g as be, h as xe, i as Se, l as Ce, m as we, n as Te, o as Ee, p as De, r as Oe, s as ke, t as Ae, u as je, v as Me, y as Ne } from "./cloud-Du7mnkz2.js"; | ||
| var Pe = W("save", [ | ||
| ["path", { | ||
| d: "M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z", | ||
| key: "1c8476" | ||
| }], | ||
| ["path", { | ||
| d: "M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7", | ||
| key: "1ydtos" | ||
| }], | ||
| ["path", { | ||
| d: "M7 3v4a1 1 0 0 0 1 1h7", | ||
| key: "t51u73" | ||
| }] | ||
| ]); | ||
| //#endregion | ||
| //#region src/cloud/composables/useSnapshotPreview.ts | ||
| function Fe(e) { | ||
| let { authManager: t, editor: n, history: r, conditionPreview: i, autoSave: a, onError: s } = e, c = d(null), l = u(null), f = u(null), p = !1; | ||
| o(() => { | ||
| p = !0; | ||
| }); | ||
| let m = C(() => l.value !== null), h = C(() => c.value?.snapshots.value ?? []), g = C(() => c.value?.isLoading.value ?? !1), _ = C(() => c.value?.isRestoring.value ?? !1); | ||
| function v() { | ||
| n.state.template?.id && !c.value && (c.value = be({ | ||
| authManager: t, | ||
| templateId: n.state.template.id, | ||
| onRestore: y, | ||
| onError: s | ||
| }), c.value.loadSnapshots()); | ||
| } | ||
| function y(e) { | ||
| n.setContent(e.content, !1), r.clear(), i.reset(); | ||
| } | ||
| async function b(e) { | ||
| if (!p) { | ||
| if (l.value) { | ||
| l.value = e, n.setContent(e.content, !1); | ||
| return; | ||
| } | ||
| n.state.isDirty && n.hasTemplate() && (await n.createSnapshot(), p) || (f.value = structuredClone(n.content.value), a?.pause(), l.value = e, n.setContent(e.content, !1)); | ||
| } | ||
| } | ||
| async function x() { | ||
| if (!(!l.value || !c.value)) try { | ||
| if (await c.value.restoreSnapshot(l.value.id), p || (await c.value.loadSnapshots(), p)) return; | ||
| } catch (e) { | ||
| throw !p && f.value && n.setContent(f.value, !1), e; | ||
| } finally { | ||
| p || (l.value = null, f.value = null, a?.resume()); | ||
| } | ||
| } | ||
| function S() { | ||
| !l.value || !f.value || (n.setContent(f.value, !1), l.value = null, f.value = null, a?.resume()); | ||
| } | ||
| async function w() { | ||
| p || c.value && await c.value.loadSnapshots(); | ||
| } | ||
| return { | ||
| snapshotHistoryInstance: c, | ||
| previewingSnapshot: l, | ||
| contentBeforePreview: f, | ||
| isPreviewingSnapshot: m, | ||
| snapshotHistorySnapshots: h, | ||
| snapshotHistoryIsLoading: g, | ||
| snapshotHistoryIsRestoring: _, | ||
| initSnapshotHistory: v, | ||
| handleRestore: y, | ||
| handleSnapshotNavigate: b, | ||
| confirmRestoreSnapshot: x, | ||
| cancelPreview: S, | ||
| loadSnapshotHistory: w | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudPanelState.ts | ||
| function Ie() { | ||
| let e = u(null), t = C({ | ||
| get: () => e.value === "ai-chat", | ||
| set: (t) => e.value = t ? "ai-chat" : null | ||
| }), n = C({ | ||
| get: () => e.value === "scoring", | ||
| set: (t) => e.value = t ? "scoring" : null | ||
| }), r = C({ | ||
| get: () => e.value === "design-reference", | ||
| set: (t) => e.value = t ? "design-reference" : null | ||
| }), i = C({ | ||
| get: () => e.value === "comments", | ||
| set: (t) => e.value = t ? "comments" : null | ||
| }), a = u(!1), o = u(!1), s = u(void 0), c = u(!1), l = u(null), d = C(() => e.value !== null), f = C(() => { | ||
| let t = e.value; | ||
| return t === "ai-chat" || t === "design-reference" || t === "scoring" ? t : null; | ||
| }), p = C(() => c.value || e.value === "ai-chat" || e.value === "design-reference" || e.value === "scoring"); | ||
| function m() { | ||
| c.value = !c.value; | ||
| } | ||
| function h(t) { | ||
| c.value = !1, e.value = e.value === t ? null : t; | ||
| } | ||
| return I(l, () => { | ||
| c.value = !1; | ||
| }), { | ||
| activePanel: e, | ||
| aiChatOpen: t, | ||
| scoringPanelOpen: n, | ||
| designReferenceOpen: r, | ||
| commentsOpen: i, | ||
| testEmailModalOpen: a, | ||
| mediaLibraryOpen: o, | ||
| mediaLibraryAccept: s, | ||
| aiMenuOpen: c, | ||
| aiMenuRef: l, | ||
| rightPanelOpen: d, | ||
| activeAiFeature: f, | ||
| aiButtonActive: p, | ||
| toggleAiMenu: m, | ||
| handleAiFeatureSelect: h | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCollabUndoWarning.ts | ||
| function Le(e) { | ||
| let { isCollaborationEnabled: t, getCollaboratorCount: n, canUndo: r } = e, i = u(!1), a = u(!1), { start: o } = F(() => { | ||
| a.value = !1; | ||
| }, k, { immediate: !1 }); | ||
| function s() { | ||
| i.value || !t.value || n() === 0 || !r.value || (i.value = !0, a.value = !0, o()); | ||
| } | ||
| return { | ||
| collabUndoWarningVisible: a, | ||
| showCollabUndoWarning: s | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudFeatureFlags.ts | ||
| function Re(e) { | ||
| let { planConfigInstance: t, aiConfig: n, editor: r } = e, i = C(() => t.hasFeature("ai_generation") && n.hasAnyMenuFeature.value), a = C(() => t.hasFeature("test_email")), o = C(() => !!r.state.template?.id), s = C(() => t.hasFeature("white_label")), c = C(() => t.config.value?.limits.max_templates ?? null), l = C(() => t.config.value?.template_count ?? 0), d = u(!1), f = u("idle"), p = u(""), { start: m } = F(() => { | ||
| f.value = "idle"; | ||
| }, 3e3, { immediate: !1 }); | ||
| return { | ||
| canUseAiGeneration: i, | ||
| canSendTestEmail: a, | ||
| hasTemplateSaved: o, | ||
| isWhiteLabeled: s, | ||
| templateLimit: c, | ||
| templateCount: l, | ||
| isSaveExporting: d, | ||
| saveStatus: f, | ||
| saveErrorMessage: p, | ||
| startSaveStatusClear: m | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudMediaLibrary.ts | ||
| function ze(e) { | ||
| let { onRequestMedia: t, mediaLibraryOpen: n, mediaLibraryAccept: r, authManager: i, getMediaConfig: a, onError: s } = e, c = null; | ||
| function l(e) { | ||
| return { | ||
| url: e.url, | ||
| alt: e.alt_text || void 0 | ||
| }; | ||
| } | ||
| async function u(e) { | ||
| let i = e?.accept ?? ["images"]; | ||
| if (e?.files?.length) { | ||
| if (t) { | ||
| let n = await t({ | ||
| accept: i, | ||
| files: e.files | ||
| }); | ||
| return n ? l(n) : null; | ||
| } | ||
| return d(e.files[0]); | ||
| } | ||
| if (t) { | ||
| let e = await t({ accept: i }); | ||
| return e ? l(e) : null; | ||
| } | ||
| return c &&= (c(null), null), r.value = i, n.value = !0, new Promise((e) => { | ||
| c = (t) => { | ||
| e(t); | ||
| }; | ||
| }); | ||
| } | ||
| async function d(e) { | ||
| let t = f(e); | ||
| if (t) return s?.(t), null; | ||
| try { | ||
| let { MediaApiClient: t } = await import("@templatical/media-library"); | ||
| return l(await new t(i).uploadMedia(e)); | ||
| } catch (e) { | ||
| return s?.(e), null; | ||
| } | ||
| } | ||
| function f(e) { | ||
| let t = a?.() ?? null; | ||
| if (!t) return null; | ||
| let n = t.categories?.images?.mime_types ?? []; | ||
| return n.length > 0 && !n.includes(e.type) ? /* @__PURE__ */ Error(`Unsupported image type: ${e.type || "unknown"}. Accepted: ${n.join(", ")}.`) : t.max_file_size > 0 && e.size > t.max_file_size ? /* @__PURE__ */ Error(`Image is too large (${e.size} bytes). Maximum allowed is ${t.max_file_size} bytes.`) : null; | ||
| } | ||
| function p(e) { | ||
| n.value = !1, c?.(l(e)), c = null; | ||
| } | ||
| function m() { | ||
| n.value = !1, c?.(null), c = null; | ||
| } | ||
| return o(() => { | ||
| c &&= (c(null), null); | ||
| }), { | ||
| handleRequestMedia: u, | ||
| handleMediaSelect: p, | ||
| handleMediaLibraryClose: m | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudInitialization.ts | ||
| function Be(e) { | ||
| let { config: t, translations: n, fontsManager: r, emit: i, getCommentsSidebar: o } = e, s = u(!0), c = u(!1), l = u(null), d = !1, f = { value: null }, p = null, m = null, h = new Ae({ | ||
| ...t.auth, | ||
| onError: t.onError | ||
| }), g = xe({ | ||
| authManager: h, | ||
| onError: t.onError | ||
| }), v = u(/* @__PURE__ */ new Map()), y = ye({ | ||
| authManager: h, | ||
| defaultFontFamily: t.fonts?.defaultFont, | ||
| templateDefaults: t.templateDefaults, | ||
| onError: t.onError, | ||
| lockedBlocks: v | ||
| }), b = Ne({ | ||
| authManager: h, | ||
| onError: t.onError | ||
| }); | ||
| t.mcp?.enabled && we({ | ||
| editor: y, | ||
| channel: b.channel, | ||
| onOperation: t.mcp.onOperation | ||
| }); | ||
| let x = null; | ||
| t.collaboration?.enabled && (x = ke({ | ||
| authManager: h, | ||
| editor: y, | ||
| channel: b.channel, | ||
| onError: t.onError, | ||
| onCollaboratorJoined: t.collaboration.onCollaboratorJoined, | ||
| onCollaboratorLeft: t.collaboration.onCollaboratorLeft, | ||
| onBlockLocked: t.collaboration.onBlockLocked, | ||
| onBlockUnlocked: t.collaboration.onBlockUnlocked | ||
| }), a(() => x.lockedBlocks.value, (e) => { | ||
| v.value = e; | ||
| }, { immediate: !0 }), ve(y, x)); | ||
| let S = C(() => !!t.collaboration?.enabled && g.hasFeature("collaboration")), w = N({ | ||
| editor: y, | ||
| config: { | ||
| uiTheme: t.uiTheme, | ||
| theme: void 0, | ||
| blockDefaults: t.blockDefaults, | ||
| templateDefaults: t.templateDefaults, | ||
| customBlocks: [], | ||
| paletteBlocks: t.paletteBlocks, | ||
| htmlBlockPreview: t.htmlBlockPreview, | ||
| colors: t.colors, | ||
| mergeTags: t.mergeTags, | ||
| logicTags: t.logicTags, | ||
| displayConditions: t.displayConditions, | ||
| onRequestMedia: null, | ||
| lint: re(t), | ||
| onSave: () => { | ||
| f.value?.().catch((e) => { | ||
| t.onError?.(e); | ||
| }); | ||
| } | ||
| }, | ||
| translations: n, | ||
| fontsManager: r, | ||
| historyOptions: x ? { isRemoteOperation: () => x._isProcessingRemoteOperation() } : void 0, | ||
| autoSaveOptions: { | ||
| onChange: async () => { | ||
| y.hasTemplate() && (await y.createSnapshot(), p?.snapshotHistoryInstance.value?.loadSnapshots()); | ||
| }, | ||
| debounce: t.autoSaveDebounce ?? 5e3, | ||
| enabled: () => t.autoSave !== !1 && g.hasFeature("auto_save") | ||
| }, | ||
| themeExtraStyles: () => ({ "--tpl-drop-text": `"${n.canvas.dropHere}"` }), | ||
| keyboardOptions: { onBeforeUndo: () => m?.showCollabUndoWarning() }, | ||
| editorRoot: e.editorRoot, | ||
| containerEl: e.containerEl | ||
| }), T = Le({ | ||
| isCollaborationEnabled: S, | ||
| getCollaboratorCount: () => x?.collaborators.value.length ?? 0, | ||
| canUndo: w.history.canUndo | ||
| }); | ||
| m = T; | ||
| let E = Fe({ | ||
| authManager: h, | ||
| editor: y, | ||
| history: w.history, | ||
| conditionPreview: w.conditionPreview, | ||
| autoSave: w.autoSave, | ||
| onError: t.onError | ||
| }); | ||
| p = E; | ||
| let D = Ie(), O = Ee(t.ai), k = Re({ | ||
| planConfigInstance: g, | ||
| aiConfig: O, | ||
| editor: y | ||
| }), A = ze({ | ||
| onRequestMedia: t.onRequestMedia, | ||
| mediaLibraryOpen: D.mediaLibraryOpen, | ||
| mediaLibraryAccept: D.mediaLibraryAccept, | ||
| authManager: h, | ||
| getMediaConfig: () => g.config.value?.media ?? null, | ||
| onError: t.onError | ||
| }); | ||
| j({ | ||
| onBlockMove: y.moveBlock, | ||
| onBlockAdd: y.addBlock | ||
| }); | ||
| let P = De({ | ||
| authManager: h, | ||
| getFontsConfig: () => t.fonts, | ||
| canUseCustomFonts: () => g.hasFeature("custom_fonts") | ||
| }), F = Me({ | ||
| authManager: h, | ||
| getTemplateId: () => y.state.template?.id ?? null, | ||
| save: () => y.save(), | ||
| exportHtml: (e) => P.exportHtml(e), | ||
| onError: t.onError, | ||
| isAuthReady: c, | ||
| onBeforeTestEmail: t.onBeforeTestEmail | ||
| }), I = je({ | ||
| authManager: h, | ||
| getTemplateId: () => y.state.template?.id ?? null, | ||
| getSocketId: () => b.getSocketId(), | ||
| onComment: t.onComment, | ||
| onError: t.onError, | ||
| isAuthReady: c, | ||
| hasCommentingFeature: () => t.commenting !== !1 && g.hasFeature("commenting") | ||
| }); | ||
| Ce({ | ||
| comments: I, | ||
| channel: b.channel | ||
| }); | ||
| let z = le({ | ||
| provider: Te(h), | ||
| editor: y, | ||
| onError: t.onError, | ||
| isAvailable: () => t.savedBlocks !== !1 && g.hasFeature("saved_modules") | ||
| }), U = _e({ | ||
| authManager: h, | ||
| getTemplateId: () => y.state.template?.id ?? null | ||
| }); | ||
| function W(e) { | ||
| D.commentsOpen.value = !0, queueMicrotask(() => { | ||
| o()?.filterByBlock(e); | ||
| }); | ||
| } | ||
| _(L, A.handleRequestMedia), _(B, h), _(H, O), _(ee, I), _(R, U), _(V, { | ||
| plan: g, | ||
| ai: O, | ||
| comments: { | ||
| getBlockCount: (e) => I.commentCountByBlock.value.get(e) ?? 0, | ||
| openForBlock: W | ||
| }, | ||
| savedBlocks: z.capability | ||
| }); | ||
| function G(e) { | ||
| g.hasFeature("theme_customization") && (w.themeOverrides.value = e); | ||
| } | ||
| function K(e) { | ||
| y.setUiTheme(e); | ||
| } | ||
| async function q() { | ||
| s.value = !0, l.value = null; | ||
| try { | ||
| if (await h.initialize(), d) return; | ||
| c.value = !0; | ||
| let e = await Oe({ authManager: h }); | ||
| if (d) return; | ||
| if (!e.api.ok) throw Error("Health check failed: API is not reachable"); | ||
| if (!e.auth.ok) throw Error(`Health check failed: authentication error${e.auth.error ? ` - ${e.auth.error}` : ""}`); | ||
| if (e.websocket.ok || M.warn("WebSocket health check failed:", e.websocket.error ?? "unknown error", "-- real-time features will be disabled."), await g.fetchConfig(), d) return; | ||
| r.setCustomFontsEnabled(g.hasFeature("custom_fonts")), t.customBlocks?.length && g.hasFeature("custom_blocks") && w.registerCustomBlocks(t.customBlocks), t.theme && g.hasFeature("theme_customization") && (w.themeOverrides.value = t.theme), i("ready"); | ||
| } catch (e) { | ||
| if (d) return; | ||
| let n = e instanceof Error ? e : Error("Initialization failed", { cause: e }); | ||
| l.value = n, t.onError?.(n); | ||
| } finally { | ||
| d || (s.value = !1); | ||
| } | ||
| } | ||
| function te() { | ||
| d = !0, r.cleanupFontLinks(), b.disconnect(), w.destroy(), t.onUnmount?.(); | ||
| } | ||
| return { | ||
| isInitializing: s, | ||
| isAuthReady: c, | ||
| initError: l, | ||
| isDestroyed: () => d, | ||
| authManager: h, | ||
| planConfigInstance: g, | ||
| websocket: b, | ||
| collaboration: x, | ||
| isCollaborationEnabled: S, | ||
| editor: y, | ||
| core: w, | ||
| aiConfig: O, | ||
| featureFlags: k, | ||
| mediaLib: A, | ||
| exporter: P, | ||
| testEmail: F, | ||
| commentsInstance: I, | ||
| savedBlocks: z, | ||
| scoringInstance: U, | ||
| panelState: D, | ||
| snapshotPreview: E, | ||
| collabWarning: T, | ||
| onSaveHook: f, | ||
| initialize: q, | ||
| destroy: te, | ||
| setThemeOverrides: G, | ||
| setUiTheme: K, | ||
| openCommentsForBlock: W | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/utils/preRenderCustomBlocks.ts | ||
| async function Ve(e, t) { | ||
| let n = async (e) => { | ||
| if (A(e)) { | ||
| let n = e; | ||
| try { | ||
| n.renderedHtml = await t.renderCustomBlock(n); | ||
| } catch { | ||
| n.renderedHtml = `<!-- Custom block render error: ${n.customType} -->`; | ||
| } | ||
| } | ||
| if (e.type === "section" && "children" in e) { | ||
| let t = e; | ||
| for (let e of t.children) for (let t of e) await n(t); | ||
| } | ||
| }; | ||
| for (let t of e.blocks) await n(t); | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudLifecycle.ts | ||
| function He(e) { | ||
| let { config: t, editor: n, websocket: r, planConfigInstance: i, snapshotPreview: a, core: o, exporter: s, featureFlags: c, isDestroyed: l } = e; | ||
| function u() { | ||
| return Se(i.config.value.websocket); | ||
| } | ||
| async function d(e) { | ||
| let i = await n.create(e); | ||
| return l() ? i : (t.onCreate?.(i), a.initSnapshotHistory(), r.connect(i.id, u()), i); | ||
| } | ||
| async function f(e) { | ||
| let i = await n.load(e); | ||
| return l() ? i : (t.onLoad?.(i), a.initSnapshotHistory(), r.connect(i.id, u()), i); | ||
| } | ||
| async function p() { | ||
| c.isSaveExporting.value = !0, c.saveStatus.value = "idle"; | ||
| try { | ||
| if (await Ve(n.content.value, o.registry), l()) throw Error("Component unmounted during save"); | ||
| let e = await n.save(); | ||
| if (l()) throw Error("Component unmounted during save"); | ||
| a.initSnapshotHistory(), a.snapshotHistoryInstance.value?.loadSnapshots(); | ||
| let r = await s.exportHtml(e.id); | ||
| if (l()) throw Error("Component unmounted during save"); | ||
| let i = { | ||
| templateId: e.id, | ||
| html: r.html, | ||
| mjml: r.mjml, | ||
| content: e.content | ||
| }; | ||
| return t.onSave?.(i), c.saveStatus.value = "saved", c.startSaveStatusClear(), i; | ||
| } catch (e) { | ||
| throw l() || (c.saveStatus.value = "error", c.saveErrorMessage.value = e instanceof Error ? e.message : "Save failed"), e; | ||
| } finally { | ||
| l() || (c.isSaveExporting.value = !1); | ||
| } | ||
| } | ||
| return { | ||
| createTemplate: d, | ||
| loadTemplate: f, | ||
| saveTemplate: p | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/composables/useCloudSaveGate.ts | ||
| function Ue(e) { | ||
| let t = u(!1), n = null, r = C(() => e.planConfig.value?.accessibility?.blockOnError === !0), i = C(() => r.value ? e.issues.value.filter((e) => e.severity === "error") : []), a = C(() => i.value.length > 0); | ||
| async function o(e) { | ||
| return a.value ? (n = e, t.value = !0, !1) : (await e(), !0); | ||
| } | ||
| async function s() { | ||
| let e = n; | ||
| n = null, t.value = !1, e && await e(); | ||
| } | ||
| function c() { | ||
| n = null, t.value = !1; | ||
| } | ||
| return { | ||
| shouldBlock: a, | ||
| blockingIssues: i, | ||
| modalOpen: t, | ||
| tryRunSave: o, | ||
| confirmAndSave: s, | ||
| cancel: c | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region src/cloud/components/CloudSaveGateModal.vue?vue&type=script&setup=true&lang.ts | ||
| var We = ["aria-label"], Ge = { class: "tpl:flex tpl:max-h-[80vh] tpl:w-full tpl:max-w-md tpl:flex-col tpl:gap-4 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:p-5 tpl:shadow-[var(--tpl-shadow-md)]" }, Ke = { class: "tpl:flex tpl:items-center tpl:gap-2" }, qe = { class: "tpl:m-0 tpl:text-base tpl:font-semibold tpl:text-[var(--tpl-text)]" }, Je = { class: "tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-muted)]" }, Ye = { class: "tpl:m-0 tpl:flex tpl:max-h-64 tpl:list-none tpl:flex-col tpl:gap-1.5 tpl:overflow-y-auto tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-2" }, Xe = { class: "tpl:text-xs tpl:text-[var(--tpl-text)]" }, Ze = { class: "tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]" }, Qe = { class: "tpl:flex tpl:justify-end tpl:gap-2" }, $e = /* @__PURE__ */ f({ | ||
| __name: "CloudSaveGateModal", | ||
| props: { | ||
| open: { type: Boolean }, | ||
| issues: {} | ||
| }, | ||
| emits: ["cancel", "confirm"], | ||
| setup(t, { emit: n }) { | ||
| let r = n, { t: i } = Q(); | ||
| return (n, a) => (s(), v(O, { | ||
| "enter-active-class": "tpl:transition-opacity tpl:duration-150", | ||
| "leave-active-class": "tpl:transition-opacity tpl:duration-150", | ||
| "enter-from-class": "tpl:opacity-0", | ||
| "leave-to-class": "tpl:opacity-0" | ||
| }, { | ||
| default: l(() => [t.open ? (s(), m("div", { | ||
| key: 0, | ||
| role: "dialog", | ||
| "aria-modal": "true", | ||
| "aria-label": b(i).saveGate.title, | ||
| class: "tpl:fixed tpl:inset-0 tpl:z-50 tpl:flex tpl:items-center tpl:justify-center tpl:bg-black/40 tpl:p-6", | ||
| onClick: a[2] ||= E((e) => r("cancel"), ["self"]) | ||
| }, [p("div", Ge, [ | ||
| p("header", Ke, [g(b(he), { | ||
| size: 18, | ||
| "stroke-width": 2, | ||
| class: "tpl:text-[var(--tpl-warning)]" | ||
| }), p("h2", qe, c(b(i).saveGate.title), 1)]), | ||
| p("p", Je, c(b(i).saveGate.body), 1), | ||
| p("ul", Ye, [(s(!0), m(S, null, e(t.issues, (e) => (s(), m("li", { | ||
| key: `${e.ruleId}-${e.blockId ?? "template"}`, | ||
| class: "tpl:flex tpl:flex-col tpl:gap-0.5 tpl:rounded tpl:px-2 tpl:py-1.5" | ||
| }, [p("span", Xe, c(e.message), 1), p("span", Ze, c(e.ruleId), 1)]))), 128))]), | ||
| p("footer", Qe, [p("button", { | ||
| type: "button", | ||
| class: "tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]", | ||
| onClick: a[0] ||= (e) => r("cancel") | ||
| }, c(b(i).saveGate.cancel), 1), p("button", { | ||
| type: "button", | ||
| class: "tpl:rounded-md tpl:bg-[var(--tpl-danger)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-white", | ||
| onClick: a[1] ||= (e) => r("confirm") | ||
| }, c(b(i).saveGate.confirm), 1)]) | ||
| ])], 8, We)) : w("", !0)]), | ||
| _: 1 | ||
| })); | ||
| } | ||
| }), et = { | ||
| class: "tpl-header tpl:absolute tpl:top-0 tpl:right-0 tpl:left-0 tpl:z-50 tpl:grid tpl:h-14 tpl:grid-cols-[1fr_auto_1fr] tpl:items-center tpl:px-4", | ||
| style: { | ||
| "background-color": "color-mix(in srgb, var(--tpl-bg) 80%, transparent)", | ||
| "backdrop-filter": "blur(12px)", | ||
| "-webkit-backdrop-filter": "blur(12px)", | ||
| "box-shadow": "var(--tpl-shadow-md)", | ||
| "border-bottom": "1px solid var(--tpl-border)" | ||
| } | ||
| }, tt = { class: "tpl-header-left tpl:flex tpl:min-w-[200px] tpl:items-center tpl:gap-3" }, nt = { | ||
| key: 0, | ||
| class: "tpl:text-xs tpl:opacity-60 tpl:text-[var(--tpl-text-muted)]" | ||
| }, rt = { class: "tpl-header-center tpl:flex tpl:items-center tpl:justify-center tpl:gap-10" }, it = { class: "tpl-header-right tpl:flex tpl:min-w-[200px] tpl:items-center tpl:justify-end tpl:gap-3" }, at = ["data-tooltip"], ot = { | ||
| key: 1, | ||
| "aria-live": "polite", | ||
| class: "tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-success)]" | ||
| }, st = { | ||
| key: 2, | ||
| "aria-live": "polite", | ||
| class: "tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-text-muted)]" | ||
| }, ct = ["aria-label", "aria-expanded"], lt = { | ||
| key: 0, | ||
| class: "tpl:inline-flex tpl:size-4.5 tpl:items-center tpl:justify-center tpl:rounded-full tpl:text-[10px] tpl:font-semibold tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]" | ||
| }, ut = ["aria-expanded"], dt = { | ||
| key: 0, | ||
| class: "tpl:absolute tpl:right-0 tpl:top-full tpl:z-50 tpl:mt-1 tpl:origin-top-right" | ||
| }, ft = ["disabled"], pt = ["disabled"], mt = /* @__PURE__ */ f({ | ||
| __name: "CloudHeader", | ||
| props: { | ||
| editor: {}, | ||
| core: {}, | ||
| featureFlags: {}, | ||
| panelState: {}, | ||
| snapshotPreview: {}, | ||
| commentsInstance: {}, | ||
| testEmail: {}, | ||
| websocket: {}, | ||
| collaboration: {}, | ||
| isCollaborationEnabled: { type: Boolean }, | ||
| isSaveDisabled: { type: Boolean }, | ||
| isSaving: { type: Boolean } | ||
| }, | ||
| emits: ["save"], | ||
| setup(e) { | ||
| let t = h(() => import("./CollaboratorBar-Bj4NZiie.js")), r = h(() => import("./SnapshotHistory-FtzDq8f0.js")), a = h(() => import("./AiFeatureMenu-CcqQDHzJ.js")), { t: o, format: u } = Q(); | ||
| return (d, f) => (s(), m("header", et, [ | ||
| p("div", tt, [e.featureFlags.templateLimit.value === null ? w("", !0) : (s(), m("span", nt, c(b(u)(b(o).header.templatesUsed, { | ||
| used: e.featureFlags.templateCount.value, | ||
| max: e.featureFlags.templateLimit.value | ||
| })), 1))]), | ||
| p("div", rt, [ | ||
| g(ce, { | ||
| viewport: e.editor.state.viewport, | ||
| onChange: e.editor.setViewport | ||
| }, null, 8, ["viewport", "onChange"]), | ||
| g(ie, { | ||
| "dark-mode": e.editor.state.darkMode, | ||
| onChange: e.editor.setDarkMode | ||
| }, null, 8, ["dark-mode", "onChange"]), | ||
| g(q, { | ||
| "preview-mode": e.editor.state.previewMode, | ||
| onChange: e.editor.setPreviewMode | ||
| }, null, 8, ["preview-mode", "onChange"]), | ||
| e.collaboration && e.isCollaborationEnabled ? (s(), v(b(t), { | ||
| key: 0, | ||
| collaborators: e.collaboration.collaborators.value, | ||
| "is-connected": e.websocket.isConnected.value | ||
| }, null, 8, ["collaborators", "is-connected"])) : w("", !0), | ||
| e.snapshotPreview.snapshotHistoryInstance.value ? (s(), v(b(r), { | ||
| key: 1, | ||
| snapshots: e.snapshotPreview.snapshotHistorySnapshots.value, | ||
| "is-loading": e.snapshotPreview.snapshotHistoryIsLoading.value, | ||
| "is-restoring": e.snapshotPreview.snapshotHistoryIsRestoring.value, | ||
| onLoad: e.snapshotPreview.loadSnapshotHistory, | ||
| onNavigate: e.snapshotPreview.handleSnapshotNavigate | ||
| }, null, 8, [ | ||
| "snapshots", | ||
| "is-loading", | ||
| "is-restoring", | ||
| "onLoad", | ||
| "onNavigate" | ||
| ])) : w("", !0) | ||
| ]), | ||
| p("div", it, [ | ||
| e.featureFlags.saveStatus.value === "error" ? (s(), m("div", { | ||
| key: 0, | ||
| "aria-live": "assertive", | ||
| class: "tpl-tooltip tpl-status tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:text-[var(--tpl-danger)]", | ||
| "data-tooltip": e.featureFlags.saveErrorMessage.value | ||
| }, [g(b(K), { | ||
| size: 12, | ||
| "stroke-width": 2.5 | ||
| }), y(" " + c(b(o).header.saveFailed), 1)], 8, at)) : e.featureFlags.saveStatus.value === "saved" ? (s(), m("div", ot, [g(b(G), { | ||
| size: 12, | ||
| "stroke-width": 2.5 | ||
| }), y(" " + c(b(o).header.saved), 1)])) : e.editor.state.isDirty ? (s(), m("div", st, [f[4] ||= p("span", { class: "tpl-pulse tpl:size-1.5 tpl:rounded-full tpl:bg-[var(--tpl-primary)]" }, null, -1), y(" " + c(b(o).header.unsaved), 1)])) : w("", !0), | ||
| e.commentsInstance.isEnabled.value && e.featureFlags.hasTemplateSaved.value ? (s(), m("button", { | ||
| key: 3, | ||
| "aria-label": e.commentsInstance.unresolvedCount.value > 0 ? `${b(o).comments.button} (${e.commentsInstance.unresolvedCount.value})` : b(o).comments.button, | ||
| "aria-expanded": e.panelState.commentsOpen.value, | ||
| class: i(b($)), | ||
| style: n({ | ||
| backgroundColor: e.panelState.commentsOpen.value ? "var(--tpl-primary)" : "transparent", | ||
| color: e.panelState.commentsOpen.value ? "var(--tpl-bg)" : "var(--tpl-primary)", | ||
| borderColor: "var(--tpl-primary)" | ||
| }), | ||
| onClick: f[0] ||= (t) => e.panelState.commentsOpen.value = !e.panelState.commentsOpen.value | ||
| }, [ | ||
| g(b(Y), { | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| }), | ||
| y(" " + c(b(o).comments.button) + " ", 1), | ||
| e.commentsInstance.unresolvedCount.value > 0 && !e.panelState.commentsOpen.value ? (s(), m("span", lt, c(e.commentsInstance.unresolvedCount.value), 1)) : w("", !0) | ||
| ], 14, ct)) : w("", !0), | ||
| e.featureFlags.canUseAiGeneration.value && e.featureFlags.hasTemplateSaved.value ? (s(), m("div", { | ||
| key: 4, | ||
| ref: (t) => e.panelState.aiMenuRef.value = t, | ||
| class: "tpl:relative" | ||
| }, [p("button", { | ||
| "aria-expanded": e.panelState.aiMenuOpen.value, | ||
| class: i(["tpl-ai-btn tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:px-4 tpl:py-2 tpl:text-sm tpl:font-semibold tpl:whitespace-nowrap tpl:transition-all tpl:duration-200", e.panelState.aiButtonActive.value ? "tpl-ai-btn--active" : "tpl-ai-btn--idle"]), | ||
| onClick: f[1] ||= E((...t) => e.panelState.toggleAiMenu && e.panelState.toggleAiMenu(...t), ["stop"]) | ||
| }, [g(b(Z), { | ||
| size: 16, | ||
| "stroke-width": 2, | ||
| class: "tpl-ai-btn-icon" | ||
| }), y(" " + c(b(o).aiChat.button), 1)], 10, ut), g(O, { | ||
| "enter-active-class": "tpl:transition-all tpl:duration-150 tpl:ease-out", | ||
| "enter-from-class": "tpl:scale-95 tpl:opacity-0", | ||
| "enter-to-class": "tpl:scale-100 tpl:opacity-100", | ||
| "leave-active-class": "tpl:transition-all tpl:duration-100 tpl:ease-in", | ||
| "leave-from-class": "tpl:scale-100 tpl:opacity-100", | ||
| "leave-to-class": "tpl:scale-95 tpl:opacity-0" | ||
| }, { | ||
| default: l(() => [e.panelState.aiMenuOpen.value ? (s(), m("div", dt, [g(b(a), { | ||
| "active-feature": e.panelState.activeAiFeature.value, | ||
| onSelect: e.panelState.handleAiFeatureSelect | ||
| }, null, 8, ["active-feature", "onSelect"])])) : w("", !0)]), | ||
| _: 1 | ||
| })], 512)) : w("", !0), | ||
| e.testEmail.isEnabled.value && e.featureFlags.canSendTestEmail.value ? (s(), m("button", { | ||
| key: 5, | ||
| class: i(b($)), | ||
| style: { | ||
| "background-color": "transparent", | ||
| color: "var(--tpl-primary)", | ||
| "border-color": "var(--tpl-primary)" | ||
| }, | ||
| disabled: e.testEmail.isSending.value || !e.featureFlags.hasTemplateSaved.value, | ||
| onClick: f[2] ||= (t) => e.panelState.testEmailModalOpen.value = !0 | ||
| }, [e.testEmail.isSending.value ? (s(), v(b(J), { | ||
| key: 1, | ||
| class: "tpl-spinner", | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| })) : (s(), v(b(X), { | ||
| key: 0, | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| })), y(" " + c(b(o).testEmail.button), 1)], 10, ft)) : w("", !0), | ||
| p("button", { | ||
| class: i(b($)), | ||
| style: { | ||
| "background-color": "transparent", | ||
| color: "var(--tpl-primary)", | ||
| "border-color": "var(--tpl-primary)" | ||
| }, | ||
| disabled: e.isSaveDisabled, | ||
| onClick: f[3] ||= (e) => d.$emit("save") | ||
| }, [e.isSaving ? (s(), v(b(J), { | ||
| key: 1, | ||
| class: "tpl-spinner", | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| })) : (s(), v(b(Pe), { | ||
| key: 0, | ||
| size: 16, | ||
| "stroke-width": 2 | ||
| })), y(" " + c(e.isSaving ? b(o).header.saving : b(o).header.save), 1)], 10, pt) | ||
| ]) | ||
| ])); | ||
| } | ||
| }), ht = /* @__PURE__ */ f({ | ||
| __name: "CloudPanels", | ||
| props: { | ||
| config: {}, | ||
| editor: {}, | ||
| core: {}, | ||
| panelState: {}, | ||
| planConfigInstance: {}, | ||
| testEmail: {}, | ||
| mediaLib: {} | ||
| }, | ||
| emits: ["send-test-email"], | ||
| setup(e, { expose: t, emit: n }) { | ||
| let r = h(() => import("./AiChatSidebar-K_AehWc-.js")), i = h(() => import("./CommentsSidebar-DvNEdugN.js")), a = h(() => import("./DesignReferenceSidebar-DNmK3dxp.js")), o = h(() => import("./TemplateScoringPanel-ifGGGWs5.js")), c = h(() => import("./TestEmailModal-DL2O1H8M.js")), l = h(async () => { | ||
| try { | ||
| return (await import("@templatical/media-library")).MediaLibraryModal; | ||
| } catch { | ||
| throw Error("[Templatical] Cloud media library requires the optional peer dependency '@templatical/media-library'. Please install it."); | ||
| } | ||
| }), d = n; | ||
| function f(e, t, n) { | ||
| t.history.record(), n.setContent(e), t.conditionPreview.reset(); | ||
| } | ||
| let p = u(null); | ||
| function _(e) { | ||
| p.value?.filterByBlock(e); | ||
| } | ||
| return t({ filterCommentsByBlock: _ }), (t, n) => (s(), m(S, null, [ | ||
| g(b(r), { | ||
| visible: e.panelState.aiChatOpen.value, | ||
| "on-apply": (t) => f(t, e.core, e.editor), | ||
| onClose: n[0] ||= (t) => e.panelState.aiChatOpen.value = !1 | ||
| }, null, 8, ["visible", "on-apply"]), | ||
| g(b(o), { | ||
| visible: e.panelState.scoringPanelOpen.value, | ||
| onClose: n[1] ||= (t) => e.panelState.scoringPanelOpen.value = !1 | ||
| }, null, 8, ["visible"]), | ||
| g(b(a), { | ||
| visible: e.panelState.designReferenceOpen.value, | ||
| "has-existing-blocks": e.editor.content.value.blocks.length > 0, | ||
| onClose: n[2] ||= (t) => e.panelState.designReferenceOpen.value = !1, | ||
| onApply: n[3] ||= (t) => f(t, e.core, e.editor) | ||
| }, null, 8, ["visible", "has-existing-blocks"]), | ||
| g(b(i), { | ||
| ref_key: "commentsSidebar", | ||
| ref: p, | ||
| visible: e.panelState.commentsOpen.value, | ||
| onClose: n[4] ||= (t) => e.panelState.commentsOpen.value = !1 | ||
| }, null, 8, ["visible"]), | ||
| g(b(c), { | ||
| visible: e.panelState.testEmailModalOpen.value, | ||
| "allowed-emails": e.testEmail.allowedEmails.value, | ||
| "is-sending": e.testEmail.isSending.value, | ||
| error: e.testEmail.error.value, | ||
| onSend: n[5] ||= (e) => d("send-test-email", e), | ||
| onClose: n[6] ||= (t) => e.panelState.testEmailModalOpen.value = !1 | ||
| }, null, 8, [ | ||
| "visible", | ||
| "allowed-emails", | ||
| "is-sending", | ||
| "error" | ||
| ]), | ||
| g(b(l), { | ||
| visible: e.panelState.mediaLibraryOpen.value, | ||
| accept: e.panelState.mediaLibraryAccept.value, | ||
| "popover-target": e.core.popoverRoot.value, | ||
| onSelect: e.mediaLib.handleMediaSelect, | ||
| onClose: e.mediaLib.handleMediaLibraryClose | ||
| }, null, 8, [ | ||
| "visible", | ||
| "accept", | ||
| "popover-target", | ||
| "onSelect", | ||
| "onClose" | ||
| ]) | ||
| ], 64)); | ||
| } | ||
| }), gt = { | ||
| key: 0, | ||
| class: "tpl-loading tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:bg-[var(--tpl-bg)]" | ||
| }, _t = { class: "tpl:flex tpl:flex-1 tpl:overflow-hidden" }, vt = { class: "tpl:flex tpl:w-12 tpl:shrink-0 tpl:flex-col tpl:items-center tpl:gap-4 tpl:py-5 tpl:border-r tpl:border-[var(--tpl-border)]" }, yt = /* @__PURE__ */ f({ | ||
| __name: "CloudLoadingOverlay", | ||
| props: { visible: { type: Boolean } }, | ||
| setup(t) { | ||
| return (n, r) => t.visible ? (s(), m("div", gt, [r[1] ||= x("<div class=\"tpl:flex tpl:h-14 tpl:shrink-0 tpl:items-center tpl:justify-between tpl:px-4 tpl:border-b tpl:border-[var(--tpl-border)]\"><div class=\"tpl-shimmer tpl:h-5 tpl:w-28 tpl:rounded-[var(--tpl-radius-sm)]\"></div><div class=\"tpl:flex tpl:gap-3\"><div class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"></div><div class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"></div></div></div>", 1), p("div", _t, [p("div", vt, [(s(), m(S, null, e(5, (e) => p("div", { | ||
| key: e, | ||
| class: "tpl-shimmer tpl:size-7 tpl:rounded-[var(--tpl-radius-sm)]" | ||
| })), 64))]), r[0] ||= x("<div class=\"tpl:flex tpl:flex-1 tpl:items-start tpl:justify-center tpl:overflow-auto tpl:p-8 tpl:bg-[var(--tpl-canvas-bg)]\"><div class=\"tpl:w-full tpl:max-w-[600px] tpl:rounded-[var(--tpl-radius)] tpl:p-6 tpl:bg-[var(--tpl-bg)] tpl:shadow-[var(--tpl-shadow-sm)]\"><div class=\"tpl:space-y-2 tpl:py-4\"><div class=\"tpl-shimmer tpl:h-3 tpl:w-3/4 tpl:rounded\"></div><div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div><div class=\"tpl-shimmer tpl:h-3 tpl:w-5/6 tpl:rounded\"></div></div><div class=\"tpl:py-4\"><div class=\"tpl-shimmer tpl:h-44 tpl:w-full tpl:rounded-[var(--tpl-radius-sm)]\"></div></div><div class=\"tpl:space-y-2 tpl:py-4\"><div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div><div class=\"tpl-shimmer tpl:h-3 tpl:w-2/3 tpl:rounded\"></div></div><div class=\"tpl:flex tpl:justify-center tpl:py-4\"><div class=\"tpl-shimmer tpl:h-10 tpl:w-36 tpl:rounded-[var(--tpl-radius-sm)]\"></div></div><div class=\"tpl:space-y-2 tpl:py-4\"><div class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/2 tpl:rounded\"></div><div class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/3 tpl:rounded\"></div></div></div></div><div class=\"tpl:flex tpl:w-[320px] tpl:shrink-0 tpl:flex-col tpl:gap-4 tpl:p-4 tpl:border-l tpl:border-[var(--tpl-border)]\"><div class=\"tpl-shimmer tpl:h-8 tpl:rounded-[var(--tpl-radius-sm)]\"></div><div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div><div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div></div>", 2)])])) : w("", !0); | ||
| } | ||
| }), bt = { | ||
| key: 0, | ||
| role: "alert", | ||
| class: "tpl-error tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-6 tpl:px-8 tpl:bg-[var(--tpl-bg)]" | ||
| }, xt = { class: "tpl:flex tpl:size-16 tpl:items-center tpl:justify-center tpl:rounded-full tpl:bg-[var(--tpl-danger-light)]" }, St = { class: "tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:text-center" }, Ct = { class: "tpl:text-lg tpl:font-semibold tpl:text-[var(--tpl-text)]" }, wt = { class: "tpl:max-w-md tpl:text-sm tpl:text-[var(--tpl-text-muted)]" }, Tt = /* @__PURE__ */ f({ | ||
| __name: "CloudErrorOverlay", | ||
| props: { | ||
| error: {}, | ||
| visible: { type: Boolean } | ||
| }, | ||
| emits: ["retry"], | ||
| setup(e, { emit: t }) { | ||
| let n = t, { t: r } = Q(); | ||
| function i(e) { | ||
| return "isUnauthorized" in e && e.isUnauthorized ? r.error.authFailed : "isNotFound" in e && e.isNotFound ? r.error.templateNotFound : r.error.defaultMessage; | ||
| } | ||
| function a(e) { | ||
| return "isNotFound" in e && !!e.isNotFound; | ||
| } | ||
| return (t, o) => e.visible && e.error ? (s(), m("div", bt, [ | ||
| p("div", xt, [g(b(K), { | ||
| size: 32, | ||
| "stroke-width": 1.5, | ||
| class: "tpl:text-[var(--tpl-danger)]" | ||
| })]), | ||
| p("div", St, [p("h2", Ct, c(b(r).error.title), 1), p("p", wt, c(i(e.error)), 1)]), | ||
| a(e.error) ? w("", !0) : (s(), m("button", { | ||
| key: 0, | ||
| class: "tpl-btn tpl-btn-primary tpl:inline-flex tpl:items-center tpl:gap-2 tpl:rounded-md tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]", | ||
| onClick: o[0] ||= (e) => n("retry") | ||
| }, c(b(r).error.retry), 1)) | ||
| ])) : w("", !0); | ||
| } | ||
| }), Et = { | ||
| key: 0, | ||
| class: "tpl-preview-banner tpl:absolute tpl:top-14 tpl:right-0 tpl:left-0 tpl:z-40 tpl:flex tpl:items-center tpl:justify-center tpl:gap-4 tpl:px-4 tpl:py-3 tpl:bg-[var(--tpl-primary-light)] tpl:border-b tpl:border-[var(--tpl-primary)]" | ||
| }, Dt = { class: "tpl:flex tpl:items-center tpl:gap-2 tpl:text-sm tpl:text-[var(--tpl-text)]" }, Ot = { class: "tpl:flex tpl:items-center tpl:gap-2" }, kt = /* @__PURE__ */ f({ | ||
| __name: "SnapshotPreviewBanner", | ||
| props: { visible: { type: Boolean } }, | ||
| emits: ["cancel", "confirm"], | ||
| setup(e, { emit: t }) { | ||
| let n = t, { t: r } = Q(); | ||
| return (t, i) => e.visible ? (s(), m("div", Et, [p("div", Dt, [g(b(me), { | ||
| size: 18, | ||
| "stroke-width": 2, | ||
| class: "tpl:text-[var(--tpl-primary)]" | ||
| }), p("span", null, c(b(r).snapshotPreview.message), 1)]), p("div", Ot, [p("button", { | ||
| class: "tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-150 tpl:text-[var(--tpl-text-muted)] tpl:border tpl:border-[var(--tpl-border)]", | ||
| style: { "background-color": "transparent" }, | ||
| onClick: i[0] ||= (e) => n("cancel") | ||
| }, c(b(r).snapshotPreview.cancel), 1), p("button", { | ||
| class: "tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]", | ||
| onClick: i[1] ||= (e) => n("confirm") | ||
| }, c(b(r).snapshotPreview.restore), 1)])])) : w("", !0); | ||
| } | ||
| }), At = { | ||
| key: 0, | ||
| role: "status", | ||
| "aria-live": "polite", | ||
| class: "tpl:absolute tpl:top-16 tpl:left-1/2 tpl:z-toast tpl:-translate-x-1/2 tpl:rounded-[var(--tpl-radius)] tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:shadow-lg", | ||
| style: { | ||
| "background-color": "var(--tpl-warning-light)", | ||
| color: "var(--tpl-text)", | ||
| border: "1px solid var(--tpl-warning)" | ||
| } | ||
| }, jt = /* @__PURE__ */ f({ | ||
| __name: "CollabUndoToast", | ||
| props: { visible: { type: Boolean } }, | ||
| setup(e) { | ||
| let { t } = U(); | ||
| return (n, r) => e.visible ? (s(), m("div", At, c(b(t).history.collabWarning), 1)) : w("", !0); | ||
| } | ||
| }), Mt = ["data-tpl-theme"], Nt = { class: "tpl:sticky tpl:top-0 tpl:z-40 tpl:h-0" }, Pt = { class: "tpl-main tpl:flex tpl:justify-center tpl:p-8" }, Ft = ["aria-label"], It = /*#__PURE__*/ ge(/* @__PURE__ */ f({ | ||
| __name: "CloudEditor", | ||
| props: { | ||
| config: {}, | ||
| translations: {}, | ||
| cloudTranslations: {}, | ||
| fontsManager: {}, | ||
| shadowRoot: {} | ||
| }, | ||
| emits: ["ready"], | ||
| setup(e, { expose: a, emit: o }) { | ||
| let d = h(() => import("./SavedBlocksPanels-Dff38YXu.js")), f = e; | ||
| _(z, f.cloudTranslations); | ||
| let x = o, S = u(null), C = u(null), k = Be({ | ||
| config: f.config, | ||
| translations: f.translations, | ||
| fontsManager: f.fontsManager, | ||
| emit: x, | ||
| getCommentsSidebar: () => S.value ? { filterByBlock: S.value.filterCommentsByBlock } : null, | ||
| editorRoot: f.shadowRoot, | ||
| containerEl: C | ||
| }), { isInitializing: A, isAuthReady: j, initError: M, planConfigInstance: N, websocket: F, collaboration: I, isCollaborationEnabled: L, editor: R, core: B, featureFlags: V, mediaLib: H, exporter: ee, testEmail: U, commentsInstance: W, savedBlocks: G, panelState: K, snapshotPreview: q, collabWarning: re, setThemeOverrides: ie, setUiTheme: ce } = k, { showNotice: le } = P(() => f.config.smallScreenNotice); | ||
| async function me(e) { | ||
| try { | ||
| await U.sendTestEmail(e), K.testEmailModalOpen.value = !1; | ||
| } catch {} | ||
| } | ||
| async function J() { | ||
| try { | ||
| await q.confirmRestoreSnapshot(); | ||
| } catch {} | ||
| } | ||
| let Y = He({ | ||
| config: f.config, | ||
| editor: R, | ||
| websocket: F, | ||
| planConfigInstance: N, | ||
| snapshotPreview: q, | ||
| core: B, | ||
| exporter: ee, | ||
| featureFlags: V, | ||
| isDestroyed: k.isDestroyed | ||
| }), X = Ue({ | ||
| issues: B.templateLint ? B.templateLint.issues : u([]), | ||
| planConfig: N.config | ||
| }); | ||
| async function Z() { | ||
| await X.tryRunSave(() => Y.saveTemplate().catch((e) => f.config.onError?.(e))); | ||
| } | ||
| return k.onSaveHook.value = Z, r(() => { | ||
| k.initialize(); | ||
| }), t(() => { | ||
| k.destroy(); | ||
| }), a({ | ||
| getContent: () => R.content.value, | ||
| setContent: (e) => R.setContent(e), | ||
| setTheme: ce, | ||
| setThemeOverrides: ie, | ||
| create: Y.createTemplate, | ||
| load: Y.loadTemplate, | ||
| save: Y.saveTemplate, | ||
| sendTestEmail: U.sendTestEmail | ||
| }), (t, r) => (s(), m("div", { | ||
| ref_key: "rootEl", | ||
| ref: C, | ||
| class: i(["tpl tpl:relative tpl:h-full tpl:overflow-hidden", { "tpl:dark": b(R).state.darkMode }]), | ||
| "data-tpl-theme": b(B).resolvedTheme.value, | ||
| style: n(b(B).themeStyles.value), | ||
| onDragover: r[6] ||= E(() => {}, ["prevent"]), | ||
| onDrop: r[7] ||= E(() => {}, ["prevent"]) | ||
| }, [ | ||
| g(pe), | ||
| g(O, { | ||
| "enter-active-class": "tpl:transition-opacity tpl:duration-200", | ||
| "enter-from-class": "tpl:opacity-100", | ||
| "enter-to-class": "tpl:opacity-100", | ||
| "leave-active-class": "tpl:transition-opacity tpl:duration-300", | ||
| "leave-from-class": "tpl:opacity-100", | ||
| "leave-to-class": "tpl:opacity-0" | ||
| }, { | ||
| default: l(() => [g(yt, { visible: b(A) || b(R).state.isLoading }, null, 8, ["visible"])]), | ||
| _: 1 | ||
| }), | ||
| g(O, { | ||
| "enter-active-class": "tpl:transition-opacity tpl:duration-200", | ||
| "enter-from-class": "tpl:opacity-0", | ||
| "enter-to-class": "tpl:opacity-100", | ||
| "leave-active-class": "tpl:transition-opacity tpl:duration-300", | ||
| "leave-from-class": "tpl:opacity-100", | ||
| "leave-to-class": "tpl:opacity-0" | ||
| }, { | ||
| default: l(() => [g(Tt, { | ||
| error: b(M), | ||
| visible: !!b(M) && !b(A), | ||
| onRetry: b(k).initialize | ||
| }, null, 8, [ | ||
| "error", | ||
| "visible", | ||
| "onRetry" | ||
| ])]), | ||
| _: 1 | ||
| }), | ||
| g(mt, { | ||
| editor: b(R), | ||
| core: b(B), | ||
| "feature-flags": b(V), | ||
| "panel-state": b(K), | ||
| "snapshot-preview": b(q), | ||
| "comments-instance": b(W), | ||
| "test-email": b(U), | ||
| websocket: b(F), | ||
| collaboration: b(I), | ||
| "is-collaboration-enabled": b(L), | ||
| "is-saving": b(R).state.isSaving || b(V).isSaveExporting.value, | ||
| "is-save-disabled": b(R).state.isSaving || b(V).isSaveExporting.value || !b(R).state.isDirty, | ||
| onSave: Z | ||
| }, null, 8, [ | ||
| "editor", | ||
| "core", | ||
| "feature-flags", | ||
| "panel-state", | ||
| "snapshot-preview", | ||
| "comments-instance", | ||
| "test-email", | ||
| "websocket", | ||
| "collaboration", | ||
| "is-collaboration-enabled", | ||
| "is-saving", | ||
| "is-save-disabled" | ||
| ]), | ||
| g($e, { | ||
| open: b(X).modalOpen.value, | ||
| issues: b(X).blockingIssues.value, | ||
| onCancel: b(X).cancel, | ||
| onConfirm: b(X).confirmAndSave | ||
| }, null, 8, [ | ||
| "open", | ||
| "issues", | ||
| "onCancel", | ||
| "onConfirm" | ||
| ]), | ||
| g(kt, { | ||
| visible: b(q).isPreviewingSnapshot.value, | ||
| onCancel: b(q).cancelPreview, | ||
| onConfirm: J | ||
| }, null, 8, ["visible", "onCancel"]), | ||
| g(O, { | ||
| "enter-active-class": "tpl:transition-all tpl:duration-200 tpl:ease-out", | ||
| "enter-from-class": "tpl:translate-y-[-8px] tpl:opacity-0", | ||
| "enter-to-class": "tpl:translate-y-0 tpl:opacity-100", | ||
| "leave-active-class": "tpl:transition-all tpl:duration-300 tpl:ease-in", | ||
| "leave-from-class": "tpl:translate-y-0 tpl:opacity-100", | ||
| "leave-to-class": "tpl:translate-y-[-8px] tpl:opacity-0" | ||
| }, { | ||
| default: l(() => [g(jt, { visible: b(re).collabUndoWarningVisible.value }, null, 8, ["visible"])]), | ||
| _: 1 | ||
| }), | ||
| T(g(ae, null, null, 512), [[D, !b(R).state.previewMode]]), | ||
| p("div", { | ||
| class: i(["tpl-body tpl:absolute tpl:bottom-0 tpl:overflow-auto", [b(R).state.previewMode ? "tpl:left-0 tpl:right-0" : b(K).rightPanelOpen.value ? "tpl:left-12 tpl:right-[680px]" : "tpl:left-12 tpl:right-[320px]", b(q).isPreviewingSnapshot.value ? "tpl:top-[104px]" : "tpl:top-14"]]), | ||
| style: { | ||
| transition: "all 300ms cubic-bezier(0.34, 1.56, 0.64, 1)", | ||
| "background-color": "var(--tpl-canvas-bg)" | ||
| } | ||
| }, [p("div", Nt, [g(O, { name: "tpl-restore-btn" }, { | ||
| default: l(() => [b(B).conditionPreview.hasHiddenBlocks.value ? (s(), m("button", { | ||
| key: 0, | ||
| class: "tpl:absolute tpl:left-1/2 tpl:top-2 tpl:-translate-x-1/2 tpl:inline-flex tpl:items-center tpl:gap-1.5 tpl:rounded-full tpl:border tpl:px-3.5 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:whitespace-nowrap tpl:shadow-md tpl:hover:opacity-80", | ||
| style: { | ||
| "background-color": "var(--tpl-warning-light)", | ||
| color: "var(--tpl-warning)", | ||
| "border-color": "var(--tpl-warning)", | ||
| "backdrop-filter": "blur(8px)" | ||
| }, | ||
| onClick: r[0] ||= (e) => b(B).conditionPreview.reset() | ||
| }, [g(b(oe), { | ||
| size: 13, | ||
| "stroke-width": 2 | ||
| }), y(" " + c(b(B).t.blockSettings.restoreHiddenBlocks), 1)])) : w("", !0)]), | ||
| _: 1 | ||
| })]), p("main", Pt, [g(ne, { | ||
| viewport: b(R).state.viewport, | ||
| content: b(R).content.value, | ||
| "selected-block-id": b(R).state.selectedBlockId, | ||
| "dark-mode": b(R).state.darkMode, | ||
| "preview-mode": b(R).state.previewMode, | ||
| "locked-blocks": b(I)?.lockedBlocks.value ?? void 0, | ||
| onSelectBlock: b(R).selectBlock, | ||
| onOpenAiChat: r[1] ||= (e) => b(K).aiChatOpen.value = !0, | ||
| onOpenDesignReference: r[2] ||= (e) => b(K).designReferenceOpen.value = !0 | ||
| }, null, 8, [ | ||
| "viewport", | ||
| "content", | ||
| "selected-block-id", | ||
| "dark-mode", | ||
| "preview-mode", | ||
| "locked-blocks", | ||
| "onSelectBlock" | ||
| ])])], 2), | ||
| e.config.branding !== !1 && !b(V).isWhiteLabeled.value ? (s(), v(ue, { | ||
| key: 0, | ||
| "position-class": [b(R).state.previewMode ? "tpl:left-0 tpl:right-0" : b(K).rightPanelOpen.value ? "tpl:left-12 tpl:right-[680px]" : "tpl:left-12 tpl:right-[320px]"] | ||
| }, null, 8, ["position-class"])) : w("", !0), | ||
| p("div", { | ||
| class: "tpl-sr-only", | ||
| role: "status", | ||
| "aria-live": "polite", | ||
| "aria-atomic": "true", | ||
| "aria-label": b(B).t.landmarks.reorderAnnouncements | ||
| }, c(b(B).keyboardReorder.announcement.value), 9, Ft), | ||
| T(g(te, { | ||
| "selected-block": b(R).selectedBlock.value, | ||
| settings: b(R).content.value.settings, | ||
| "shifted-left": b(K).rightPanelOpen.value, | ||
| onUpdateBlock: r[3] ||= (e) => b(R).updateBlock(b(R).selectedBlock.value.id, e), | ||
| onDeleteBlock: r[4] ||= (e) => b(B).blockActions.deleteBlock(b(R).selectedBlock.value.id), | ||
| onDuplicateBlock: r[5] ||= (e) => b(B).blockActions.duplicateBlock(b(R).selectedBlock.value), | ||
| onUpdateSettings: b(R).updateSettings | ||
| }, null, 8, [ | ||
| "selected-block", | ||
| "settings", | ||
| "shifted-left", | ||
| "onUpdateSettings" | ||
| ]), [[D, !b(R).state.previewMode]]), | ||
| !b(A) && b(j) ? (s(), v(ht, { | ||
| key: 1, | ||
| ref_key: "cloudPanelsRef", | ||
| ref: S, | ||
| config: f.config, | ||
| editor: b(R), | ||
| core: b(B), | ||
| "panel-state": b(K), | ||
| "plan-config-instance": b(N), | ||
| "test-email": b(U), | ||
| "media-lib": b(H), | ||
| onSendTestEmail: me | ||
| }, null, 8, [ | ||
| "config", | ||
| "editor", | ||
| "core", | ||
| "panel-state", | ||
| "plan-config-instance", | ||
| "test-email", | ||
| "media-lib" | ||
| ])) : w("", !0), | ||
| b(G).isAvailable.value ? (s(), v(b(d), { | ||
| key: 2, | ||
| feature: b(G) | ||
| }, null, 8, ["feature"])) : w("", !0), | ||
| p("div", { | ||
| ref: (e) => b(B).popoverRoot.value = e, | ||
| class: "tpl-popover-root" | ||
| }, null, 512), | ||
| g(se), | ||
| g(fe), | ||
| b(le) ? (s(), v(de, { key: 3 })) : w("", !0) | ||
| ], 46, Mt)); | ||
| } | ||
| }), [["__scopeId", "data-v-04940787"]]); | ||
| //#endregion | ||
| export { It as default }; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
10468214
0.02%107326
0.01%