@templatical/editor 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AiChatSidebar-K_AehWc-.js → AiChatSidebar-Bru6-ziQ.js} +1 -1
- package/dist/{SavedBlockPreviewCanvas-DXkGDYph.js → BlockPreviewCanvas-MD9BGuXV.js} +35 -29
- package/dist/{CloudEditor-BVH7D1Ka.js → CloudEditor-iK4vkpNm.js} +274 -278
- package/dist/{DesignReferenceSidebar-DNmK3dxp.js → DesignReferenceSidebar-BxPo0zyx.js} +1 -1
- package/dist/{SaveBlockDialog-B_5KBaXW.js → SaveBlockDialog-CFAJHP3v.js} +98 -97
- package/dist/{SavedBlocksBrowserModal-DoJBoYPk.js → SavedBlocksBrowserModal-C_-Xcm0Q.js} +1 -1
- package/dist/{SavedBlocksPanels-3Af5dMaf.js → SavedBlocksPanels-CRf7zzFn.js} +1 -1
- package/dist/TestEmailModal-D90-3Yex.js +162 -0
- package/dist/TestEmailPanel-B0u_Yd3X.js +33 -0
- package/dist/ViewportToggle-BjmbNoao.js +97 -0
- package/dist/bundle-stats.json +7 -7
- package/dist/{cdn/chunks/ca-BBgad6y5.js → ca-D47BxSGu.js} +14 -2
- package/dist/cdn/chunks/{AiChatSidebar-A1CuWGY0.js → AiChatSidebar-BMnsF8G6.js} +2 -2
- package/dist/cdn/chunks/{AiChatSidebar-A1CuWGY0.js.map → AiChatSidebar-BMnsF8G6.js.map} +1 -1
- package/dist/cdn/chunks/{SavedBlockPreviewCanvas-DgLOKtR_.js → BlockPreviewCanvas-eASrdFSN.js} +36 -30
- package/dist/cdn/chunks/BlockPreviewCanvas-eASrdFSN.js.map +1 -0
- package/dist/cdn/chunks/{CloudEditor-FCS53UcZ.js → CloudEditor-S8zLHtKb.js} +253 -257
- package/dist/cdn/chunks/CloudEditor-S8zLHtKb.js.map +1 -0
- package/dist/cdn/chunks/{DesignReferenceSidebar-Dre99DTH.js → DesignReferenceSidebar-CM-k15pd.js} +2 -2
- package/dist/cdn/chunks/{DesignReferenceSidebar-Dre99DTH.js.map → DesignReferenceSidebar-CM-k15pd.js.map} +1 -1
- package/dist/cdn/chunks/{SaveBlockDialog-CSeD3gn9.js → SaveBlockDialog-Bi_Z3kuA.js} +100 -99
- package/dist/cdn/chunks/SaveBlockDialog-Bi_Z3kuA.js.map +1 -0
- package/dist/cdn/chunks/{SavedBlocksBrowserModal-B2x5WRPN.js → SavedBlocksBrowserModal-CesYxFsH.js} +2 -2
- package/dist/cdn/chunks/SavedBlocksBrowserModal-CesYxFsH.js.map +1 -0
- package/dist/cdn/chunks/{SavedBlocksPanels-BzMTZxf2.js → SavedBlocksPanels-BsSpJwrW.js} +2 -2
- package/dist/cdn/chunks/{SavedBlocksPanels-BzMTZxf2.js.map → SavedBlocksPanels-BsSpJwrW.js.map} +1 -1
- package/dist/cdn/chunks/TestEmailModal-CCWEVrE2.js +162 -0
- package/dist/cdn/chunks/TestEmailModal-CCWEVrE2.js.map +1 -0
- package/dist/cdn/chunks/TestEmailPanel-048m0kxX.js +35 -0
- package/dist/cdn/chunks/TestEmailPanel-048m0kxX.js.map +1 -0
- package/dist/cdn/chunks/ViewportToggle-CFmF-N4i.js +66 -0
- package/dist/cdn/chunks/ViewportToggle-CFmF-N4i.js.map +1 -0
- package/dist/{ca-BBgad6y5.js → cdn/chunks/ca-D47BxSGu.js} +16 -0
- package/dist/cdn/chunks/{ca-BBgad6y5.js.map → ca-D47BxSGu.js.map} +1 -1
- package/dist/cdn/chunks/{cloud-BJlbXGTJ.js → cloud-F-aVi9hj.js} +45 -41
- package/dist/cdn/chunks/cloud-F-aVi9hj.js.map +1 -0
- package/dist/{de-D7WbpFBR.js → cdn/chunks/de-CnnxBwh7.js} +16 -0
- package/dist/cdn/chunks/{de-D7WbpFBR.js.map → de-CnnxBwh7.js.map} +1 -1
- package/dist/{de-Bw6fB5JU.js → cdn/chunks/de-uVoSP1xF.js} +2 -9
- package/dist/cdn/chunks/de-uVoSP1xF.js.map +1 -0
- package/dist/cdn/chunks/emailFrameWidth-BmFCN2dp.js +8 -0
- package/dist/cdn/chunks/emailFrameWidth-BmFCN2dp.js.map +1 -0
- package/dist/cdn/chunks/{en-BrZ0uGK2.js → en-5gW-bYy2.js} +15 -1
- package/dist/cdn/chunks/{en-BrZ0uGK2.js.map → en-5gW-bYy2.js.map} +1 -1
- package/dist/{en-DAdHJz7h.js → cdn/chunks/en-CjhsJxb-.js} +2 -9
- package/dist/cdn/chunks/en-CjhsJxb-.js.map +1 -0
- package/dist/{es-tLSLysPs.js → cdn/chunks/es-sQ9SdI0l.js} +16 -0
- package/dist/cdn/chunks/{es-tLSLysPs.js.map → es-sQ9SdI0l.js.map} +1 -1
- package/dist/cdn/chunks/{fr-BMWOPztQ.js → fr-DBZ5w21C.js} +15 -1
- package/dist/cdn/chunks/{fr-BMWOPztQ.js.map → fr-DBZ5w21C.js.map} +1 -1
- package/dist/{nl-Dhctu7JL.js → cdn/chunks/nl-DOL7Fnfo.js} +16 -0
- package/dist/cdn/chunks/{nl-Dhctu7JL.js.map → nl-DOL7Fnfo.js.map} +1 -1
- package/dist/{pt-BR-w2sGzHNX.js → cdn/chunks/pt-BR-CTzHTqJD.js} +2 -9
- package/dist/cdn/chunks/pt-BR-CTzHTqJD.js.map +1 -0
- package/dist/cdn/chunks/{pt-BR-BWFokdTU.js → pt-BR-DRCdHX1d.js} +15 -1
- package/dist/cdn/chunks/{pt-BR-BWFokdTU.js.map → pt-BR-DRCdHX1d.js.map} +1 -1
- package/dist/cdn/chunks/{src-ByWL5K1f.js → src-CzoMNRq4.js} +17 -17
- package/dist/cdn/chunks/{src-ByWL5K1f.js.map → src-CzoMNRq4.js.map} +1 -1
- package/dist/cdn/chunks/{src-MA8DWI8O.js → src-SluTVSz_.js} +2 -2
- package/dist/cdn/chunks/{src-MA8DWI8O.js.map → src-SluTVSz_.js.map} +1 -1
- package/dist/cdn/chunks/{styles-BkXzQ4-n.js → styles-BbfU2w9R.js} +616 -584
- package/dist/cdn/chunks/styles-BbfU2w9R.js.map +1 -0
- package/dist/cdn/editor.css +1 -1
- package/dist/cdn/editor.js +164 -147
- package/dist/cdn/editor.js.map +1 -1
- package/dist/{cloud-Du7mnkz2.js → cloud-CeZPgo1V.js} +41 -39
- package/dist/{cdn/chunks/de-D7WbpFBR.js → de-CnnxBwh7.js} +14 -2
- package/dist/{cdn/chunks/de-Bw6fB5JU.js → de-uVoSP1xF.js} +0 -11
- package/dist/emailFrameWidth-BmFCN2dp.js +6 -0
- package/dist/{en-BrZ0uGK2.js → en-5gW-bYy2.js} +14 -0
- package/dist/{cdn/chunks/en-DAdHJz7h.js → en-CjhsJxb-.js} +0 -11
- package/dist/{cdn/chunks/es-tLSLysPs.js → es-sQ9SdI0l.js} +14 -2
- package/dist/{fr-BMWOPztQ.js → fr-DBZ5w21C.js} +14 -0
- package/dist/index.d.ts +74 -0
- package/dist/{cdn/chunks/nl-Dhctu7JL.js → nl-DOL7Fnfo.js} +14 -2
- package/dist/{cdn/chunks/pt-BR-w2sGzHNX.js → pt-BR-CTzHTqJD.js} +0 -11
- package/dist/{pt-BR-BWFokdTU.js → pt-BR-DRCdHX1d.js} +14 -0
- package/dist/style.css +1 -1
- package/dist/{styles-AxBd7CgB.js → styles-BWTIgN5L.js} +670 -671
- package/dist/templatical-editor.js +127 -109
- package/package.json +7 -7
- package/dist/TestEmailModal-DComAFpR.js +0 -95
- package/dist/cdn/chunks/CloudEditor-FCS53UcZ.js.map +0 -1
- package/dist/cdn/chunks/SaveBlockDialog-CSeD3gn9.js.map +0 -1
- package/dist/cdn/chunks/SavedBlockPreviewCanvas-DgLOKtR_.js.map +0 -1
- package/dist/cdn/chunks/SavedBlocksBrowserModal-B2x5WRPN.js.map +0 -1
- package/dist/cdn/chunks/TestEmailModal-DT37w7i_.js +0 -96
- package/dist/cdn/chunks/TestEmailModal-DT37w7i_.js.map +0 -1
- package/dist/cdn/chunks/cloud-BJlbXGTJ.js.map +0 -1
- package/dist/cdn/chunks/de-Bw6fB5JU.js.map +0 -1
- package/dist/cdn/chunks/en-DAdHJz7h.js.map +0 -1
- package/dist/cdn/chunks/pt-BR-w2sGzHNX.js.map +0 -1
- package/dist/cdn/chunks/styles-BkXzQ4-n.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DesignReferenceSidebar-Dre99DTH.js","names":[],"sources":["../../../src/cloud/components/DesignReferenceSidebar.vue","../../../src/cloud/components/DesignReferenceSidebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { MAX_UPLOAD_SIZE_BYTES } from \"../../constants/timeouts\";\nimport LoadingTrack from \"../../components/LoadingTrack.vue\";\nimport { useDesignReference } from \"@templatical/core/cloud\";\nimport { EDITOR_KEY, AUTH_MANAGER_KEY, requireInject } from \"../../keys\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\nimport type { TemplateContent } from \"@templatical/types\";\nimport {\n CircleAlert,\n FileImage,\n FileText,\n ImageUp,\n Upload,\n X,\n} from \"@lucide/vue\";\nimport { computed, onBeforeUnmount, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n hasExistingBlocks: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"apply\", content: TemplateContent): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\nconst editor = requireInject(EDITOR_KEY, \"DesignReferenceSidebar\");\nconst authManager = requireInject(AUTH_MANAGER_KEY, \"DesignReferenceSidebar\");\n\nconst designReference = useDesignReference({\n authManager,\n getTemplateId: () => editor.state.template?.id ?? null,\n onApply: (content) => emit(\"apply\", content),\n});\n\nconst fileInput = ref<HTMLInputElement | null>(null);\n\ntype SourceTab = \"image\" | \"pdf\";\n\nconst activeTab = ref<SourceTab>(\"image\");\nconst selectedFile = ref<File | null>(null);\nconst prompt = ref(\"\");\nconst filePreviewUrl = ref<string | null>(null);\nconst showConfirmation = ref(false);\nconst isDragging = ref(false);\n\nconst canGenerate = computed(() => {\n if (designReference.isGenerating.value) {\n return false;\n }\n\n return selectedFile.value !== null;\n});\n\nfunction selectTab(tab: SourceTab): void {\n activeTab.value = tab;\n clearFile();\n}\n\nfunction handleFileSelect(event: Event): void {\n const input = event.target as HTMLInputElement;\n const file = input.files?.[0];\n if (file) {\n setFile(file);\n }\n // Reset input so same file can be selected again\n input.value = \"\";\n}\n\nfunction setFile(file: File): void {\n // Validate file size (10MB)\n if (file.size > MAX_UPLOAD_SIZE_BYTES) {\n designReference.error.value = cloudT.designReference.fileTooLarge;\n return;\n }\n\n // Validate file type\n if (activeTab.value === \"image\") {\n const validTypes = [\"image/png\", \"image/jpeg\", \"image/jpg\", \"image/webp\"];\n if (!validTypes.includes(file.type)) {\n designReference.error.value = cloudT.designReference.invalidFileType;\n return;\n }\n } else if (activeTab.value === \"pdf\") {\n if (file.type !== \"application/pdf\") {\n designReference.error.value = cloudT.designReference.invalidFileType;\n return;\n }\n }\n\n selectedFile.value = file;\n designReference.error.value = null;\n\n // Create preview URL for images\n if (filePreviewUrl.value) {\n URL.revokeObjectURL(filePreviewUrl.value);\n }\n if (file.type.startsWith(\"image/\")) {\n filePreviewUrl.value = URL.createObjectURL(file);\n } else {\n filePreviewUrl.value = null;\n }\n}\n\nfunction clearFile(): void {\n if (filePreviewUrl.value) {\n URL.revokeObjectURL(filePreviewUrl.value);\n filePreviewUrl.value = null;\n }\n selectedFile.value = null;\n}\n\nfunction handleDragOver(event: DragEvent): void {\n event.preventDefault();\n isDragging.value = true;\n}\n\nfunction handleDragLeave(): void {\n isDragging.value = false;\n}\n\nfunction handleDrop(event: DragEvent): void {\n event.preventDefault();\n isDragging.value = false;\n\n const file = event.dataTransfer?.files?.[0];\n if (file) {\n setFile(file);\n }\n}\n\nfunction handleGenerate(): void {\n if (!canGenerate.value) {\n return;\n }\n\n // Show confirmation if there are existing blocks\n if (props.hasExistingBlocks && !showConfirmation.value) {\n showConfirmation.value = true;\n return;\n }\n\n showConfirmation.value = false;\n\n const input: {\n prompt?: string;\n imageUpload?: File;\n pdfUpload?: File;\n } = {};\n\n if (prompt.value.trim()) {\n input.prompt = prompt.value.trim();\n }\n\n if (activeTab.value === \"image\" && selectedFile.value) {\n input.imageUpload = selectedFile.value;\n } else if (activeTab.value === \"pdf\" && selectedFile.value) {\n input.pdfUpload = selectedFile.value;\n }\n\n designReference.generate(input);\n}\n\nfunction cancelConfirmation(): void {\n showConfirmation.value = false;\n}\n\n// Reset state when panel closes\nwatch(\n () => props.visible,\n (isVisible) => {\n if (!isVisible) {\n showConfirmation.value = false;\n }\n },\n);\n\nonBeforeUnmount(() => {\n if (filePreviewUrl.value) {\n URL.revokeObjectURL(filePreviewUrl.value);\n filePreviewUrl.value = null;\n }\n});\n</script>\n\n<template>\n <Transition\n enter-active-class=\"tpl-design-slide-enter-active\"\n enter-from-class=\"tpl:translate-x-full\"\n enter-to-class=\"tpl:translate-x-0\"\n leave-active-class=\"tpl-design-slide-leave-active\"\n leave-from-class=\"tpl:translate-x-0\"\n leave-to-class=\"tpl:translate-x-full\"\n >\n <div\n v-if=\"visible\"\n class=\"tpl-design-sidebar tpl:absolute tpl:top-14 tpl:right-0 tpl:bottom-0 tpl:z-panel tpl:flex tpl:w-[360px] tpl:flex-col tpl:border-l tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <!-- Header -->\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-3\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-primary)]\"\n >\n <ImageUp :size=\"13\" :stroke-width=\"2\" />\n <span>{{ cloudT.designReference.title }}</span>\n </div>\n <button\n class=\"tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:duration-150 tpl:text-[var(--tpl-text-muted)]\"\n @click=\"emit('close')\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <!-- Loading state -->\n <div\n v-if=\"designReference.isGenerating.value\"\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center\"\n >\n <div\n class=\"tpl:flex tpl:w-full tpl:flex-col tpl:items-center tpl:gap-3\"\n >\n <LoadingTrack />\n <p class=\"tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ cloudT.designReference.generating }}\n </p>\n </div>\n </div>\n\n <!-- Input form -->\n <div v-else class=\"tpl:flex tpl:flex-col tpl:gap-4\">\n <!-- Source tabs -->\n <div\n class=\"tpl:flex tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1 tpl:bg-[var(--tpl-bg-hover)]\"\n >\n <button\n class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-2 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150\"\n :style=\"{\n backgroundColor:\n activeTab === 'image' ? 'var(--tpl-bg)' : 'transparent',\n color:\n activeTab === 'image'\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n boxShadow: activeTab === 'image' ? 'var(--tpl-shadow)' : 'none',\n }\"\n @click=\"selectTab('image')\"\n >\n <FileImage :size=\"12\" :stroke-width=\"2\" />\n {{ cloudT.designReference.uploadImage }}\n </button>\n <button\n class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-2 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150\"\n :style=\"{\n backgroundColor:\n activeTab === 'pdf' ? 'var(--tpl-bg)' : 'transparent',\n color:\n activeTab === 'pdf'\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n boxShadow: activeTab === 'pdf' ? 'var(--tpl-shadow)' : 'none',\n }\"\n @click=\"selectTab('pdf')\"\n >\n <FileText :size=\"12\" :stroke-width=\"2\" />\n {{ cloudT.designReference.uploadPdf }}\n </button>\n </div>\n\n <!-- File upload area (image or pdf) -->\n <div>\n <!-- Preview -->\n <div v-if=\"selectedFile\" class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n class=\"tpl:relative tpl:overflow-hidden tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <!-- Image preview -->\n <img\n v-if=\"filePreviewUrl\"\n :src=\"filePreviewUrl\"\n :alt=\"selectedFile.name\"\n class=\"tpl:h-auto tpl:max-h-48 tpl:w-full tpl:object-contain\"\n />\n <!-- PDF preview (icon-based) -->\n <div\n v-else\n class=\"tpl:flex tpl:h-32 tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-2\"\n >\n <FileText\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\">\n {{ selectedFile.name }}\n </span>\n </div>\n <!-- Remove button -->\n <button\n class=\"tpl:absolute tpl:top-2 tpl:right-2 tpl:rounded-full tpl:p-1 tpl:transition-colors tpl:duration-150 tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:shadow-[var(--tpl-shadow)]\"\n @click=\"clearFile\"\n >\n <X :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Drop zone -->\n <div\n v-else\n class=\"tpl-design-dropzone tpl:flex tpl:cursor-pointer tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-2 tpl:rounded-[var(--tpl-radius)] tpl:border-2 tpl:border-dashed tpl:px-4 tpl:py-8 tpl:transition-colors tpl:duration-150\"\n :style=\"{\n borderColor: isDragging\n ? 'var(--tpl-primary)'\n : 'var(--tpl-border-light)',\n backgroundColor: isDragging\n ? 'var(--tpl-primary-light)'\n : 'var(--tpl-bg)',\n }\"\n @click=\"fileInput?.click()\"\n @dragover=\"handleDragOver\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop\"\n >\n <Upload\n :size=\"24\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <span\n class=\"tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.designReference.dropHint }}\n </span>\n <span\n class=\"tpl:text-center tpl:text-[11px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{\n activeTab === \"image\"\n ? cloudT.designReference.acceptedImages\n : cloudT.designReference.acceptedPdf\n }}\n </span>\n </div>\n <input\n ref=\"fileInput\"\n type=\"file\"\n class=\"tpl:hidden\"\n :accept=\"\n activeTab === 'image'\n ? 'image/png,image/jpeg,image/webp'\n : 'application/pdf'\n \"\n @change=\"handleFileSelect\"\n />\n </div>\n\n <!-- Prompt textarea -->\n <div class=\"tpl:flex tpl:flex-col tpl:gap-1.5\">\n <label\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.designReference.promptLabel }}\n </label>\n <textarea\n v-model=\"prompt\"\n class=\"tpl:min-h-[72px] tpl:w-full tpl:resize-none tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:px-3 tpl:py-2 tpl:font-sans tpl:text-sm tpl:outline-none tpl:transition-colors tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n :class=\"['tpl-design-prompt-input']\"\n :placeholder=\"cloudT.designReference.promptPlaceholder\"\n rows=\"3\"\n />\n </div>\n\n <!-- Confirmation overlay -->\n <div\n v-if=\"showConfirmation\"\n class=\"tpl:flex tpl:flex-col tpl:gap-2 tpl:rounded-[var(--tpl-radius)] tpl:px-3 tpl:py-3 tpl:bg-[var(--tpl-warning-light)] tpl:border tpl:border-[var(--tpl-warning)]\"\n >\n <p class=\"tpl:text-xs tpl:leading-snug tpl:text-[var(--tpl-text)]\">\n {{ cloudT.designReference.replaceWarning }}\n </p>\n <div class=\"tpl:flex tpl:gap-2\">\n <button\n class=\"tpl:flex-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150 tpl:text-[var(--tpl-text-muted)] tpl:border tpl:border-[var(--tpl-border)]\"\n style=\"background-color: transparent\"\n @click=\"cancelConfirmation\"\n >\n {{ cloudT.designReference.replaceCancel }}\n </button>\n <button\n class=\"tpl:flex-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n @click=\"handleGenerate\"\n >\n {{ cloudT.designReference.replaceConfirm }}\n </button>\n </div>\n </div>\n\n <!-- Error message -->\n <div\n v-if=\"designReference.error.value\"\n class=\"tpl:flex tpl:items-start tpl:gap-2 tpl:rounded-lg tpl:px-3 tpl:py-2 tpl:text-xs tpl:bg-[var(--tpl-danger-light)] tpl:text-[var(--tpl-danger)]\"\n >\n <CircleAlert\n :size=\"14\"\n :stroke-width=\"2\"\n class=\"tpl:mt-0.5 tpl:shrink-0\"\n />\n <span>{{ cloudT.designReference.error }}</span>\n </div>\n\n <!-- Generate button -->\n <button\n v-if=\"!showConfirmation\"\n class=\"tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-2 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!canGenerate\"\n @click=\"handleGenerate\"\n >\n <ImageUp :size=\"16\" :stroke-width=\"2\" />\n {{ cloudT.designReference.generate }}\n </button>\n\n <!-- AI disclaimer -->\n <p\n class=\"tpl:m-0 tpl:pt-1 tpl:text-center tpl:text-[11px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ cloudT.aiMenu.disclaimer }}\n </p>\n </div>\n </div>\n </div>\n </Transition>\n</template>\n\n<style scoped>\n.tpl-design-slide-enter-active {\n transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-design-slide-leave-active {\n transition: transform 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-design-prompt-input:focus {\n border-color: var(--tpl-primary);\n box-shadow: var(--tpl-ring);\n}\n\n.tpl-design-dropzone:hover {\n border-color: var(--tpl-primary) !important;\n background-color: var(--tpl-primary-light) !important;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { MAX_UPLOAD_SIZE_BYTES } from \"../../constants/timeouts\";\nimport LoadingTrack from \"../../components/LoadingTrack.vue\";\nimport { useDesignReference } from \"@templatical/core/cloud\";\nimport { EDITOR_KEY, AUTH_MANAGER_KEY, requireInject } from \"../../keys\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\nimport type { TemplateContent } from \"@templatical/types\";\nimport {\n CircleAlert,\n FileImage,\n FileText,\n ImageUp,\n Upload,\n X,\n} from \"@lucide/vue\";\nimport { computed, onBeforeUnmount, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n hasExistingBlocks: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"apply\", content: TemplateContent): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\nconst editor = requireInject(EDITOR_KEY, \"DesignReferenceSidebar\");\nconst authManager = requireInject(AUTH_MANAGER_KEY, \"DesignReferenceSidebar\");\n\nconst designReference = useDesignReference({\n authManager,\n getTemplateId: () => editor.state.template?.id ?? null,\n onApply: (content) => emit(\"apply\", content),\n});\n\nconst fileInput = ref<HTMLInputElement | null>(null);\n\ntype SourceTab = \"image\" | \"pdf\";\n\nconst activeTab = ref<SourceTab>(\"image\");\nconst selectedFile = ref<File | null>(null);\nconst prompt = ref(\"\");\nconst filePreviewUrl = ref<string | null>(null);\nconst showConfirmation = ref(false);\nconst isDragging = ref(false);\n\nconst canGenerate = computed(() => {\n if (designReference.isGenerating.value) {\n return false;\n }\n\n return selectedFile.value !== null;\n});\n\nfunction selectTab(tab: SourceTab): void {\n activeTab.value = tab;\n clearFile();\n}\n\nfunction handleFileSelect(event: Event): void {\n const input = event.target as HTMLInputElement;\n const file = input.files?.[0];\n if (file) {\n setFile(file);\n }\n // Reset input so same file can be selected again\n input.value = \"\";\n}\n\nfunction setFile(file: File): void {\n // Validate file size (10MB)\n if (file.size > MAX_UPLOAD_SIZE_BYTES) {\n designReference.error.value = cloudT.designReference.fileTooLarge;\n return;\n }\n\n // Validate file type\n if (activeTab.value === \"image\") {\n const validTypes = [\"image/png\", \"image/jpeg\", \"image/jpg\", \"image/webp\"];\n if (!validTypes.includes(file.type)) {\n designReference.error.value = cloudT.designReference.invalidFileType;\n return;\n }\n } else if (activeTab.value === \"pdf\") {\n if (file.type !== \"application/pdf\") {\n designReference.error.value = cloudT.designReference.invalidFileType;\n return;\n }\n }\n\n selectedFile.value = file;\n designReference.error.value = null;\n\n // Create preview URL for images\n if (filePreviewUrl.value) {\n URL.revokeObjectURL(filePreviewUrl.value);\n }\n if (file.type.startsWith(\"image/\")) {\n filePreviewUrl.value = URL.createObjectURL(file);\n } else {\n filePreviewUrl.value = null;\n }\n}\n\nfunction clearFile(): void {\n if (filePreviewUrl.value) {\n URL.revokeObjectURL(filePreviewUrl.value);\n filePreviewUrl.value = null;\n }\n selectedFile.value = null;\n}\n\nfunction handleDragOver(event: DragEvent): void {\n event.preventDefault();\n isDragging.value = true;\n}\n\nfunction handleDragLeave(): void {\n isDragging.value = false;\n}\n\nfunction handleDrop(event: DragEvent): void {\n event.preventDefault();\n isDragging.value = false;\n\n const file = event.dataTransfer?.files?.[0];\n if (file) {\n setFile(file);\n }\n}\n\nfunction handleGenerate(): void {\n if (!canGenerate.value) {\n return;\n }\n\n // Show confirmation if there are existing blocks\n if (props.hasExistingBlocks && !showConfirmation.value) {\n showConfirmation.value = true;\n return;\n }\n\n showConfirmation.value = false;\n\n const input: {\n prompt?: string;\n imageUpload?: File;\n pdfUpload?: File;\n } = {};\n\n if (prompt.value.trim()) {\n input.prompt = prompt.value.trim();\n }\n\n if (activeTab.value === \"image\" && selectedFile.value) {\n input.imageUpload = selectedFile.value;\n } else if (activeTab.value === \"pdf\" && selectedFile.value) {\n input.pdfUpload = selectedFile.value;\n }\n\n designReference.generate(input);\n}\n\nfunction cancelConfirmation(): void {\n showConfirmation.value = false;\n}\n\n// Reset state when panel closes\nwatch(\n () => props.visible,\n (isVisible) => {\n if (!isVisible) {\n showConfirmation.value = false;\n }\n },\n);\n\nonBeforeUnmount(() => {\n if (filePreviewUrl.value) {\n URL.revokeObjectURL(filePreviewUrl.value);\n filePreviewUrl.value = null;\n }\n});\n</script>\n\n<template>\n <Transition\n enter-active-class=\"tpl-design-slide-enter-active\"\n enter-from-class=\"tpl:translate-x-full\"\n enter-to-class=\"tpl:translate-x-0\"\n leave-active-class=\"tpl-design-slide-leave-active\"\n leave-from-class=\"tpl:translate-x-0\"\n leave-to-class=\"tpl:translate-x-full\"\n >\n <div\n v-if=\"visible\"\n class=\"tpl-design-sidebar tpl:absolute tpl:top-14 tpl:right-0 tpl:bottom-0 tpl:z-panel tpl:flex tpl:w-[360px] tpl:flex-col tpl:border-l tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <!-- Header -->\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-3\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-primary)]\"\n >\n <ImageUp :size=\"13\" :stroke-width=\"2\" />\n <span>{{ cloudT.designReference.title }}</span>\n </div>\n <button\n class=\"tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:duration-150 tpl:text-[var(--tpl-text-muted)]\"\n @click=\"emit('close')\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <!-- Loading state -->\n <div\n v-if=\"designReference.isGenerating.value\"\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center\"\n >\n <div\n class=\"tpl:flex tpl:w-full tpl:flex-col tpl:items-center tpl:gap-3\"\n >\n <LoadingTrack />\n <p class=\"tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ cloudT.designReference.generating }}\n </p>\n </div>\n </div>\n\n <!-- Input form -->\n <div v-else class=\"tpl:flex tpl:flex-col tpl:gap-4\">\n <!-- Source tabs -->\n <div\n class=\"tpl:flex tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1 tpl:bg-[var(--tpl-bg-hover)]\"\n >\n <button\n class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-2 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150\"\n :style=\"{\n backgroundColor:\n activeTab === 'image' ? 'var(--tpl-bg)' : 'transparent',\n color:\n activeTab === 'image'\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n boxShadow: activeTab === 'image' ? 'var(--tpl-shadow)' : 'none',\n }\"\n @click=\"selectTab('image')\"\n >\n <FileImage :size=\"12\" :stroke-width=\"2\" />\n {{ cloudT.designReference.uploadImage }}\n </button>\n <button\n class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-2 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150\"\n :style=\"{\n backgroundColor:\n activeTab === 'pdf' ? 'var(--tpl-bg)' : 'transparent',\n color:\n activeTab === 'pdf'\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n boxShadow: activeTab === 'pdf' ? 'var(--tpl-shadow)' : 'none',\n }\"\n @click=\"selectTab('pdf')\"\n >\n <FileText :size=\"12\" :stroke-width=\"2\" />\n {{ cloudT.designReference.uploadPdf }}\n </button>\n </div>\n\n <!-- File upload area (image or pdf) -->\n <div>\n <!-- Preview -->\n <div v-if=\"selectedFile\" class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n class=\"tpl:relative tpl:overflow-hidden tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <!-- Image preview -->\n <img\n v-if=\"filePreviewUrl\"\n :src=\"filePreviewUrl\"\n :alt=\"selectedFile.name\"\n class=\"tpl:h-auto tpl:max-h-48 tpl:w-full tpl:object-contain\"\n />\n <!-- PDF preview (icon-based) -->\n <div\n v-else\n class=\"tpl:flex tpl:h-32 tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-2\"\n >\n <FileText\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\">\n {{ selectedFile.name }}\n </span>\n </div>\n <!-- Remove button -->\n <button\n class=\"tpl:absolute tpl:top-2 tpl:right-2 tpl:rounded-full tpl:p-1 tpl:transition-colors tpl:duration-150 tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:shadow-[var(--tpl-shadow)]\"\n @click=\"clearFile\"\n >\n <X :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Drop zone -->\n <div\n v-else\n class=\"tpl-design-dropzone tpl:flex tpl:cursor-pointer tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-2 tpl:rounded-[var(--tpl-radius)] tpl:border-2 tpl:border-dashed tpl:px-4 tpl:py-8 tpl:transition-colors tpl:duration-150\"\n :style=\"{\n borderColor: isDragging\n ? 'var(--tpl-primary)'\n : 'var(--tpl-border-light)',\n backgroundColor: isDragging\n ? 'var(--tpl-primary-light)'\n : 'var(--tpl-bg)',\n }\"\n @click=\"fileInput?.click()\"\n @dragover=\"handleDragOver\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop\"\n >\n <Upload\n :size=\"24\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <span\n class=\"tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.designReference.dropHint }}\n </span>\n <span\n class=\"tpl:text-center tpl:text-[11px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{\n activeTab === \"image\"\n ? cloudT.designReference.acceptedImages\n : cloudT.designReference.acceptedPdf\n }}\n </span>\n </div>\n <input\n ref=\"fileInput\"\n type=\"file\"\n class=\"tpl:hidden\"\n :accept=\"\n activeTab === 'image'\n ? 'image/png,image/jpeg,image/webp'\n : 'application/pdf'\n \"\n @change=\"handleFileSelect\"\n />\n </div>\n\n <!-- Prompt textarea -->\n <div class=\"tpl:flex tpl:flex-col tpl:gap-1.5\">\n <label\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.designReference.promptLabel }}\n </label>\n <textarea\n v-model=\"prompt\"\n class=\"tpl:min-h-[72px] tpl:w-full tpl:resize-none tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:px-3 tpl:py-2 tpl:font-sans tpl:text-sm tpl:outline-none tpl:transition-colors tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n :class=\"['tpl-design-prompt-input']\"\n :placeholder=\"cloudT.designReference.promptPlaceholder\"\n rows=\"3\"\n />\n </div>\n\n <!-- Confirmation overlay -->\n <div\n v-if=\"showConfirmation\"\n class=\"tpl:flex tpl:flex-col tpl:gap-2 tpl:rounded-[var(--tpl-radius)] tpl:px-3 tpl:py-3 tpl:bg-[var(--tpl-warning-light)] tpl:border tpl:border-[var(--tpl-warning)]\"\n >\n <p class=\"tpl:text-xs tpl:leading-snug tpl:text-[var(--tpl-text)]\">\n {{ cloudT.designReference.replaceWarning }}\n </p>\n <div class=\"tpl:flex tpl:gap-2\">\n <button\n class=\"tpl:flex-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150 tpl:text-[var(--tpl-text-muted)] tpl:border tpl:border-[var(--tpl-border)]\"\n style=\"background-color: transparent\"\n @click=\"cancelConfirmation\"\n >\n {{ cloudT.designReference.replaceCancel }}\n </button>\n <button\n class=\"tpl:flex-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n @click=\"handleGenerate\"\n >\n {{ cloudT.designReference.replaceConfirm }}\n </button>\n </div>\n </div>\n\n <!-- Error message -->\n <div\n v-if=\"designReference.error.value\"\n class=\"tpl:flex tpl:items-start tpl:gap-2 tpl:rounded-lg tpl:px-3 tpl:py-2 tpl:text-xs tpl:bg-[var(--tpl-danger-light)] tpl:text-[var(--tpl-danger)]\"\n >\n <CircleAlert\n :size=\"14\"\n :stroke-width=\"2\"\n class=\"tpl:mt-0.5 tpl:shrink-0\"\n />\n <span>{{ cloudT.designReference.error }}</span>\n </div>\n\n <!-- Generate button -->\n <button\n v-if=\"!showConfirmation\"\n class=\"tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-2 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!canGenerate\"\n @click=\"handleGenerate\"\n >\n <ImageUp :size=\"16\" :stroke-width=\"2\" />\n {{ cloudT.designReference.generate }}\n </button>\n\n <!-- AI disclaimer -->\n <p\n class=\"tpl:m-0 tpl:pt-1 tpl:text-center tpl:text-[11px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ cloudT.aiMenu.disclaimer }}\n </p>\n </div>\n </div>\n </div>\n </Transition>\n</template>\n\n<style scoped>\n.tpl-design-slide-enter-active {\n transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-design-slide-leave-active {\n transition: transform 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-design-prompt-input:focus {\n border-color: var(--tpl-primary);\n box-shadow: var(--tpl-ring);\n}\n\n.tpl-design-dropzone:hover {\n border-color: var(--tpl-primary) !important;\n background-color: var(--tpl-primary-light) !important;\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DesignReferenceSidebar-CM-k15pd.js","names":[],"sources":["../../../src/cloud/components/DesignReferenceSidebar.vue","../../../src/cloud/components/DesignReferenceSidebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { MAX_UPLOAD_SIZE_BYTES } from \"../../constants/timeouts\";\nimport LoadingTrack from \"../../components/LoadingTrack.vue\";\nimport { useDesignReference } from \"@templatical/core/cloud\";\nimport { EDITOR_KEY, AUTH_MANAGER_KEY, requireInject } from \"../../keys\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\nimport type { TemplateContent } from \"@templatical/types\";\nimport {\n CircleAlert,\n FileImage,\n FileText,\n ImageUp,\n Upload,\n X,\n} from \"@lucide/vue\";\nimport { computed, onBeforeUnmount, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n hasExistingBlocks: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"apply\", content: TemplateContent): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\nconst editor = requireInject(EDITOR_KEY, \"DesignReferenceSidebar\");\nconst authManager = requireInject(AUTH_MANAGER_KEY, \"DesignReferenceSidebar\");\n\nconst designReference = useDesignReference({\n authManager,\n getTemplateId: () => editor.state.template?.id ?? null,\n onApply: (content) => emit(\"apply\", content),\n});\n\nconst fileInput = ref<HTMLInputElement | null>(null);\n\ntype SourceTab = \"image\" | \"pdf\";\n\nconst activeTab = ref<SourceTab>(\"image\");\nconst selectedFile = ref<File | null>(null);\nconst prompt = ref(\"\");\nconst filePreviewUrl = ref<string | null>(null);\nconst showConfirmation = ref(false);\nconst isDragging = ref(false);\n\nconst canGenerate = computed(() => {\n if (designReference.isGenerating.value) {\n return false;\n }\n\n return selectedFile.value !== null;\n});\n\nfunction selectTab(tab: SourceTab): void {\n activeTab.value = tab;\n clearFile();\n}\n\nfunction handleFileSelect(event: Event): void {\n const input = event.target as HTMLInputElement;\n const file = input.files?.[0];\n if (file) {\n setFile(file);\n }\n // Reset input so same file can be selected again\n input.value = \"\";\n}\n\nfunction setFile(file: File): void {\n // Validate file size (10MB)\n if (file.size > MAX_UPLOAD_SIZE_BYTES) {\n designReference.error.value = cloudT.designReference.fileTooLarge;\n return;\n }\n\n // Validate file type\n if (activeTab.value === \"image\") {\n const validTypes = [\"image/png\", \"image/jpeg\", \"image/jpg\", \"image/webp\"];\n if (!validTypes.includes(file.type)) {\n designReference.error.value = cloudT.designReference.invalidFileType;\n return;\n }\n } else if (activeTab.value === \"pdf\") {\n if (file.type !== \"application/pdf\") {\n designReference.error.value = cloudT.designReference.invalidFileType;\n return;\n }\n }\n\n selectedFile.value = file;\n designReference.error.value = null;\n\n // Create preview URL for images\n if (filePreviewUrl.value) {\n URL.revokeObjectURL(filePreviewUrl.value);\n }\n if (file.type.startsWith(\"image/\")) {\n filePreviewUrl.value = URL.createObjectURL(file);\n } else {\n filePreviewUrl.value = null;\n }\n}\n\nfunction clearFile(): void {\n if (filePreviewUrl.value) {\n URL.revokeObjectURL(filePreviewUrl.value);\n filePreviewUrl.value = null;\n }\n selectedFile.value = null;\n}\n\nfunction handleDragOver(event: DragEvent): void {\n event.preventDefault();\n isDragging.value = true;\n}\n\nfunction handleDragLeave(): void {\n isDragging.value = false;\n}\n\nfunction handleDrop(event: DragEvent): void {\n event.preventDefault();\n isDragging.value = false;\n\n const file = event.dataTransfer?.files?.[0];\n if (file) {\n setFile(file);\n }\n}\n\nfunction handleGenerate(): void {\n if (!canGenerate.value) {\n return;\n }\n\n // Show confirmation if there are existing blocks\n if (props.hasExistingBlocks && !showConfirmation.value) {\n showConfirmation.value = true;\n return;\n }\n\n showConfirmation.value = false;\n\n const input: {\n prompt?: string;\n imageUpload?: File;\n pdfUpload?: File;\n } = {};\n\n if (prompt.value.trim()) {\n input.prompt = prompt.value.trim();\n }\n\n if (activeTab.value === \"image\" && selectedFile.value) {\n input.imageUpload = selectedFile.value;\n } else if (activeTab.value === \"pdf\" && selectedFile.value) {\n input.pdfUpload = selectedFile.value;\n }\n\n designReference.generate(input);\n}\n\nfunction cancelConfirmation(): void {\n showConfirmation.value = false;\n}\n\n// Reset state when panel closes\nwatch(\n () => props.visible,\n (isVisible) => {\n if (!isVisible) {\n showConfirmation.value = false;\n }\n },\n);\n\nonBeforeUnmount(() => {\n if (filePreviewUrl.value) {\n URL.revokeObjectURL(filePreviewUrl.value);\n filePreviewUrl.value = null;\n }\n});\n</script>\n\n<template>\n <Transition\n enter-active-class=\"tpl-design-slide-enter-active\"\n enter-from-class=\"tpl:translate-x-full\"\n enter-to-class=\"tpl:translate-x-0\"\n leave-active-class=\"tpl-design-slide-leave-active\"\n leave-from-class=\"tpl:translate-x-0\"\n leave-to-class=\"tpl:translate-x-full\"\n >\n <div\n v-if=\"visible\"\n class=\"tpl-design-sidebar tpl:absolute tpl:top-14 tpl:right-0 tpl:bottom-0 tpl:z-panel tpl:flex tpl:w-[360px] tpl:flex-col tpl:border-l tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <!-- Header -->\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-3\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-primary)]\"\n >\n <ImageUp :size=\"13\" :stroke-width=\"2\" />\n <span>{{ cloudT.designReference.title }}</span>\n </div>\n <button\n class=\"tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:duration-150 tpl:text-[var(--tpl-text-muted)]\"\n @click=\"emit('close')\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <!-- Loading state -->\n <div\n v-if=\"designReference.isGenerating.value\"\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center\"\n >\n <div\n class=\"tpl:flex tpl:w-full tpl:flex-col tpl:items-center tpl:gap-3\"\n >\n <LoadingTrack />\n <p class=\"tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ cloudT.designReference.generating }}\n </p>\n </div>\n </div>\n\n <!-- Input form -->\n <div v-else class=\"tpl:flex tpl:flex-col tpl:gap-4\">\n <!-- Source tabs -->\n <div\n class=\"tpl:flex tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1 tpl:bg-[var(--tpl-bg-hover)]\"\n >\n <button\n class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-2 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150\"\n :style=\"{\n backgroundColor:\n activeTab === 'image' ? 'var(--tpl-bg)' : 'transparent',\n color:\n activeTab === 'image'\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n boxShadow: activeTab === 'image' ? 'var(--tpl-shadow)' : 'none',\n }\"\n @click=\"selectTab('image')\"\n >\n <FileImage :size=\"12\" :stroke-width=\"2\" />\n {{ cloudT.designReference.uploadImage }}\n </button>\n <button\n class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-2 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150\"\n :style=\"{\n backgroundColor:\n activeTab === 'pdf' ? 'var(--tpl-bg)' : 'transparent',\n color:\n activeTab === 'pdf'\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n boxShadow: activeTab === 'pdf' ? 'var(--tpl-shadow)' : 'none',\n }\"\n @click=\"selectTab('pdf')\"\n >\n <FileText :size=\"12\" :stroke-width=\"2\" />\n {{ cloudT.designReference.uploadPdf }}\n </button>\n </div>\n\n <!-- File upload area (image or pdf) -->\n <div>\n <!-- Preview -->\n <div v-if=\"selectedFile\" class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n class=\"tpl:relative tpl:overflow-hidden tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <!-- Image preview -->\n <img\n v-if=\"filePreviewUrl\"\n :src=\"filePreviewUrl\"\n :alt=\"selectedFile.name\"\n class=\"tpl:h-auto tpl:max-h-48 tpl:w-full tpl:object-contain\"\n />\n <!-- PDF preview (icon-based) -->\n <div\n v-else\n class=\"tpl:flex tpl:h-32 tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-2\"\n >\n <FileText\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\">\n {{ selectedFile.name }}\n </span>\n </div>\n <!-- Remove button -->\n <button\n class=\"tpl:absolute tpl:top-2 tpl:right-2 tpl:rounded-full tpl:p-1 tpl:transition-colors tpl:duration-150 tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:shadow-[var(--tpl-shadow)]\"\n @click=\"clearFile\"\n >\n <X :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Drop zone -->\n <div\n v-else\n class=\"tpl-design-dropzone tpl:flex tpl:cursor-pointer tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-2 tpl:rounded-[var(--tpl-radius)] tpl:border-2 tpl:border-dashed tpl:px-4 tpl:py-8 tpl:transition-colors tpl:duration-150\"\n :style=\"{\n borderColor: isDragging\n ? 'var(--tpl-primary)'\n : 'var(--tpl-border-light)',\n backgroundColor: isDragging\n ? 'var(--tpl-primary-light)'\n : 'var(--tpl-bg)',\n }\"\n @click=\"fileInput?.click()\"\n @dragover=\"handleDragOver\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop\"\n >\n <Upload\n :size=\"24\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <span\n class=\"tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.designReference.dropHint }}\n </span>\n <span\n class=\"tpl:text-center tpl:text-[11px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{\n activeTab === \"image\"\n ? cloudT.designReference.acceptedImages\n : cloudT.designReference.acceptedPdf\n }}\n </span>\n </div>\n <input\n ref=\"fileInput\"\n type=\"file\"\n class=\"tpl:hidden\"\n :accept=\"\n activeTab === 'image'\n ? 'image/png,image/jpeg,image/webp'\n : 'application/pdf'\n \"\n @change=\"handleFileSelect\"\n />\n </div>\n\n <!-- Prompt textarea -->\n <div class=\"tpl:flex tpl:flex-col tpl:gap-1.5\">\n <label\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.designReference.promptLabel }}\n </label>\n <textarea\n v-model=\"prompt\"\n class=\"tpl:min-h-[72px] tpl:w-full tpl:resize-none tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:px-3 tpl:py-2 tpl:font-sans tpl:text-sm tpl:outline-none tpl:transition-colors tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n :class=\"['tpl-design-prompt-input']\"\n :placeholder=\"cloudT.designReference.promptPlaceholder\"\n rows=\"3\"\n />\n </div>\n\n <!-- Confirmation overlay -->\n <div\n v-if=\"showConfirmation\"\n class=\"tpl:flex tpl:flex-col tpl:gap-2 tpl:rounded-[var(--tpl-radius)] tpl:px-3 tpl:py-3 tpl:bg-[var(--tpl-warning-light)] tpl:border tpl:border-[var(--tpl-warning)]\"\n >\n <p class=\"tpl:text-xs tpl:leading-snug tpl:text-[var(--tpl-text)]\">\n {{ cloudT.designReference.replaceWarning }}\n </p>\n <div class=\"tpl:flex tpl:gap-2\">\n <button\n class=\"tpl:flex-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150 tpl:text-[var(--tpl-text-muted)] tpl:border tpl:border-[var(--tpl-border)]\"\n style=\"background-color: transparent\"\n @click=\"cancelConfirmation\"\n >\n {{ cloudT.designReference.replaceCancel }}\n </button>\n <button\n class=\"tpl:flex-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n @click=\"handleGenerate\"\n >\n {{ cloudT.designReference.replaceConfirm }}\n </button>\n </div>\n </div>\n\n <!-- Error message -->\n <div\n v-if=\"designReference.error.value\"\n class=\"tpl:flex tpl:items-start tpl:gap-2 tpl:rounded-lg tpl:px-3 tpl:py-2 tpl:text-xs tpl:bg-[var(--tpl-danger-light)] tpl:text-[var(--tpl-danger)]\"\n >\n <CircleAlert\n :size=\"14\"\n :stroke-width=\"2\"\n class=\"tpl:mt-0.5 tpl:shrink-0\"\n />\n <span>{{ cloudT.designReference.error }}</span>\n </div>\n\n <!-- Generate button -->\n <button\n v-if=\"!showConfirmation\"\n class=\"tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-2 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!canGenerate\"\n @click=\"handleGenerate\"\n >\n <ImageUp :size=\"16\" :stroke-width=\"2\" />\n {{ cloudT.designReference.generate }}\n </button>\n\n <!-- AI disclaimer -->\n <p\n class=\"tpl:m-0 tpl:pt-1 tpl:text-center tpl:text-[11px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ cloudT.aiMenu.disclaimer }}\n </p>\n </div>\n </div>\n </div>\n </Transition>\n</template>\n\n<style scoped>\n.tpl-design-slide-enter-active {\n transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-design-slide-leave-active {\n transition: transform 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-design-prompt-input:focus {\n border-color: var(--tpl-primary);\n box-shadow: var(--tpl-ring);\n}\n\n.tpl-design-dropzone:hover {\n border-color: var(--tpl-primary) !important;\n background-color: var(--tpl-primary-light) !important;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { MAX_UPLOAD_SIZE_BYTES } from \"../../constants/timeouts\";\nimport LoadingTrack from \"../../components/LoadingTrack.vue\";\nimport { useDesignReference } from \"@templatical/core/cloud\";\nimport { EDITOR_KEY, AUTH_MANAGER_KEY, requireInject } from \"../../keys\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\nimport type { TemplateContent } from \"@templatical/types\";\nimport {\n CircleAlert,\n FileImage,\n FileText,\n ImageUp,\n Upload,\n X,\n} from \"@lucide/vue\";\nimport { computed, onBeforeUnmount, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n hasExistingBlocks: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"apply\", content: TemplateContent): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\nconst editor = requireInject(EDITOR_KEY, \"DesignReferenceSidebar\");\nconst authManager = requireInject(AUTH_MANAGER_KEY, \"DesignReferenceSidebar\");\n\nconst designReference = useDesignReference({\n authManager,\n getTemplateId: () => editor.state.template?.id ?? null,\n onApply: (content) => emit(\"apply\", content),\n});\n\nconst fileInput = ref<HTMLInputElement | null>(null);\n\ntype SourceTab = \"image\" | \"pdf\";\n\nconst activeTab = ref<SourceTab>(\"image\");\nconst selectedFile = ref<File | null>(null);\nconst prompt = ref(\"\");\nconst filePreviewUrl = ref<string | null>(null);\nconst showConfirmation = ref(false);\nconst isDragging = ref(false);\n\nconst canGenerate = computed(() => {\n if (designReference.isGenerating.value) {\n return false;\n }\n\n return selectedFile.value !== null;\n});\n\nfunction selectTab(tab: SourceTab): void {\n activeTab.value = tab;\n clearFile();\n}\n\nfunction handleFileSelect(event: Event): void {\n const input = event.target as HTMLInputElement;\n const file = input.files?.[0];\n if (file) {\n setFile(file);\n }\n // Reset input so same file can be selected again\n input.value = \"\";\n}\n\nfunction setFile(file: File): void {\n // Validate file size (10MB)\n if (file.size > MAX_UPLOAD_SIZE_BYTES) {\n designReference.error.value = cloudT.designReference.fileTooLarge;\n return;\n }\n\n // Validate file type\n if (activeTab.value === \"image\") {\n const validTypes = [\"image/png\", \"image/jpeg\", \"image/jpg\", \"image/webp\"];\n if (!validTypes.includes(file.type)) {\n designReference.error.value = cloudT.designReference.invalidFileType;\n return;\n }\n } else if (activeTab.value === \"pdf\") {\n if (file.type !== \"application/pdf\") {\n designReference.error.value = cloudT.designReference.invalidFileType;\n return;\n }\n }\n\n selectedFile.value = file;\n designReference.error.value = null;\n\n // Create preview URL for images\n if (filePreviewUrl.value) {\n URL.revokeObjectURL(filePreviewUrl.value);\n }\n if (file.type.startsWith(\"image/\")) {\n filePreviewUrl.value = URL.createObjectURL(file);\n } else {\n filePreviewUrl.value = null;\n }\n}\n\nfunction clearFile(): void {\n if (filePreviewUrl.value) {\n URL.revokeObjectURL(filePreviewUrl.value);\n filePreviewUrl.value = null;\n }\n selectedFile.value = null;\n}\n\nfunction handleDragOver(event: DragEvent): void {\n event.preventDefault();\n isDragging.value = true;\n}\n\nfunction handleDragLeave(): void {\n isDragging.value = false;\n}\n\nfunction handleDrop(event: DragEvent): void {\n event.preventDefault();\n isDragging.value = false;\n\n const file = event.dataTransfer?.files?.[0];\n if (file) {\n setFile(file);\n }\n}\n\nfunction handleGenerate(): void {\n if (!canGenerate.value) {\n return;\n }\n\n // Show confirmation if there are existing blocks\n if (props.hasExistingBlocks && !showConfirmation.value) {\n showConfirmation.value = true;\n return;\n }\n\n showConfirmation.value = false;\n\n const input: {\n prompt?: string;\n imageUpload?: File;\n pdfUpload?: File;\n } = {};\n\n if (prompt.value.trim()) {\n input.prompt = prompt.value.trim();\n }\n\n if (activeTab.value === \"image\" && selectedFile.value) {\n input.imageUpload = selectedFile.value;\n } else if (activeTab.value === \"pdf\" && selectedFile.value) {\n input.pdfUpload = selectedFile.value;\n }\n\n designReference.generate(input);\n}\n\nfunction cancelConfirmation(): void {\n showConfirmation.value = false;\n}\n\n// Reset state when panel closes\nwatch(\n () => props.visible,\n (isVisible) => {\n if (!isVisible) {\n showConfirmation.value = false;\n }\n },\n);\n\nonBeforeUnmount(() => {\n if (filePreviewUrl.value) {\n URL.revokeObjectURL(filePreviewUrl.value);\n filePreviewUrl.value = null;\n }\n});\n</script>\n\n<template>\n <Transition\n enter-active-class=\"tpl-design-slide-enter-active\"\n enter-from-class=\"tpl:translate-x-full\"\n enter-to-class=\"tpl:translate-x-0\"\n leave-active-class=\"tpl-design-slide-leave-active\"\n leave-from-class=\"tpl:translate-x-0\"\n leave-to-class=\"tpl:translate-x-full\"\n >\n <div\n v-if=\"visible\"\n class=\"tpl-design-sidebar tpl:absolute tpl:top-14 tpl:right-0 tpl:bottom-0 tpl:z-panel tpl:flex tpl:w-[360px] tpl:flex-col tpl:border-l tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)]\"\n >\n <!-- Header -->\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:border-[var(--tpl-border)] tpl:px-4 tpl:py-3\"\n >\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-primary)]\"\n >\n <ImageUp :size=\"13\" :stroke-width=\"2\" />\n <span>{{ cloudT.designReference.title }}</span>\n </div>\n <button\n class=\"tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:duration-150 tpl:text-[var(--tpl-text-muted)]\"\n @click=\"emit('close')\"\n >\n <X :size=\"14\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Content -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <!-- Loading state -->\n <div\n v-if=\"designReference.isGenerating.value\"\n class=\"tpl:flex tpl:h-full tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-3 tpl:text-center\"\n >\n <div\n class=\"tpl:flex tpl:w-full tpl:flex-col tpl:items-center tpl:gap-3\"\n >\n <LoadingTrack />\n <p class=\"tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ cloudT.designReference.generating }}\n </p>\n </div>\n </div>\n\n <!-- Input form -->\n <div v-else class=\"tpl:flex tpl:flex-col tpl:gap-4\">\n <!-- Source tabs -->\n <div\n class=\"tpl:flex tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1 tpl:bg-[var(--tpl-bg-hover)]\"\n >\n <button\n class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-2 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150\"\n :style=\"{\n backgroundColor:\n activeTab === 'image' ? 'var(--tpl-bg)' : 'transparent',\n color:\n activeTab === 'image'\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n boxShadow: activeTab === 'image' ? 'var(--tpl-shadow)' : 'none',\n }\"\n @click=\"selectTab('image')\"\n >\n <FileImage :size=\"12\" :stroke-width=\"2\" />\n {{ cloudT.designReference.uploadImage }}\n </button>\n <button\n class=\"tpl:flex tpl:flex-1 tpl:items-center tpl:justify-center tpl:gap-1.5 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-2 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150\"\n :style=\"{\n backgroundColor:\n activeTab === 'pdf' ? 'var(--tpl-bg)' : 'transparent',\n color:\n activeTab === 'pdf'\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n boxShadow: activeTab === 'pdf' ? 'var(--tpl-shadow)' : 'none',\n }\"\n @click=\"selectTab('pdf')\"\n >\n <FileText :size=\"12\" :stroke-width=\"2\" />\n {{ cloudT.designReference.uploadPdf }}\n </button>\n </div>\n\n <!-- File upload area (image or pdf) -->\n <div>\n <!-- Preview -->\n <div v-if=\"selectedFile\" class=\"tpl:flex tpl:flex-col tpl:gap-2\">\n <div\n class=\"tpl:relative tpl:overflow-hidden tpl:rounded-[var(--tpl-radius)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)]\"\n >\n <!-- Image preview -->\n <img\n v-if=\"filePreviewUrl\"\n :src=\"filePreviewUrl\"\n :alt=\"selectedFile.name\"\n class=\"tpl:h-auto tpl:max-h-48 tpl:w-full tpl:object-contain\"\n />\n <!-- PDF preview (icon-based) -->\n <div\n v-else\n class=\"tpl:flex tpl:h-32 tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-2\"\n >\n <FileText\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\">\n {{ selectedFile.name }}\n </span>\n </div>\n <!-- Remove button -->\n <button\n class=\"tpl:absolute tpl:top-2 tpl:right-2 tpl:rounded-full tpl:p-1 tpl:transition-colors tpl:duration-150 tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text-muted)] tpl:shadow-[var(--tpl-shadow)]\"\n @click=\"clearFile\"\n >\n <X :size=\"12\" :stroke-width=\"2\" />\n </button>\n </div>\n </div>\n\n <!-- Drop zone -->\n <div\n v-else\n class=\"tpl-design-dropzone tpl:flex tpl:cursor-pointer tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-2 tpl:rounded-[var(--tpl-radius)] tpl:border-2 tpl:border-dashed tpl:px-4 tpl:py-8 tpl:transition-colors tpl:duration-150\"\n :style=\"{\n borderColor: isDragging\n ? 'var(--tpl-primary)'\n : 'var(--tpl-border-light)',\n backgroundColor: isDragging\n ? 'var(--tpl-primary-light)'\n : 'var(--tpl-bg)',\n }\"\n @click=\"fileInput?.click()\"\n @dragover=\"handleDragOver\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop\"\n >\n <Upload\n :size=\"24\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <span\n class=\"tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.designReference.dropHint }}\n </span>\n <span\n class=\"tpl:text-center tpl:text-[11px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{\n activeTab === \"image\"\n ? cloudT.designReference.acceptedImages\n : cloudT.designReference.acceptedPdf\n }}\n </span>\n </div>\n <input\n ref=\"fileInput\"\n type=\"file\"\n class=\"tpl:hidden\"\n :accept=\"\n activeTab === 'image'\n ? 'image/png,image/jpeg,image/webp'\n : 'application/pdf'\n \"\n @change=\"handleFileSelect\"\n />\n </div>\n\n <!-- Prompt textarea -->\n <div class=\"tpl:flex tpl:flex-col tpl:gap-1.5\">\n <label\n class=\"tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.designReference.promptLabel }}\n </label>\n <textarea\n v-model=\"prompt\"\n class=\"tpl:min-h-[72px] tpl:w-full tpl:resize-none tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:px-3 tpl:py-2 tpl:font-sans tpl:text-sm tpl:outline-none tpl:transition-colors tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n :class=\"['tpl-design-prompt-input']\"\n :placeholder=\"cloudT.designReference.promptPlaceholder\"\n rows=\"3\"\n />\n </div>\n\n <!-- Confirmation overlay -->\n <div\n v-if=\"showConfirmation\"\n class=\"tpl:flex tpl:flex-col tpl:gap-2 tpl:rounded-[var(--tpl-radius)] tpl:px-3 tpl:py-3 tpl:bg-[var(--tpl-warning-light)] tpl:border tpl:border-[var(--tpl-warning)]\"\n >\n <p class=\"tpl:text-xs tpl:leading-snug tpl:text-[var(--tpl-text)]\">\n {{ cloudT.designReference.replaceWarning }}\n </p>\n <div class=\"tpl:flex tpl:gap-2\">\n <button\n class=\"tpl:flex-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150 tpl:text-[var(--tpl-text-muted)] tpl:border tpl:border-[var(--tpl-border)]\"\n style=\"background-color: transparent\"\n @click=\"cancelConfirmation\"\n >\n {{ cloudT.designReference.replaceCancel }}\n </button>\n <button\n class=\"tpl:flex-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n @click=\"handleGenerate\"\n >\n {{ cloudT.designReference.replaceConfirm }}\n </button>\n </div>\n </div>\n\n <!-- Error message -->\n <div\n v-if=\"designReference.error.value\"\n class=\"tpl:flex tpl:items-start tpl:gap-2 tpl:rounded-lg tpl:px-3 tpl:py-2 tpl:text-xs tpl:bg-[var(--tpl-danger-light)] tpl:text-[var(--tpl-danger)]\"\n >\n <CircleAlert\n :size=\"14\"\n :stroke-width=\"2\"\n class=\"tpl:mt-0.5 tpl:shrink-0\"\n />\n <span>{{ cloudT.designReference.error }}</span>\n </div>\n\n <!-- Generate button -->\n <button\n v-if=\"!showConfirmation\"\n class=\"tpl:flex tpl:w-full tpl:items-center tpl:justify-center tpl:gap-2 tpl:rounded-[var(--tpl-radius-sm)] tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!canGenerate\"\n @click=\"handleGenerate\"\n >\n <ImageUp :size=\"16\" :stroke-width=\"2\" />\n {{ cloudT.designReference.generate }}\n </button>\n\n <!-- AI disclaimer -->\n <p\n class=\"tpl:m-0 tpl:pt-1 tpl:text-center tpl:text-[11px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ cloudT.aiMenu.disclaimer }}\n </p>\n </div>\n </div>\n </div>\n </Transition>\n</template>\n\n<style scoped>\n.tpl-design-slide-enter-active {\n transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-design-slide-leave-active {\n transition: transform 200ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n.tpl-design-prompt-input:focus {\n border-color: var(--tpl-primary);\n box-shadow: var(--tpl-ring);\n}\n\n.tpl-design-dropzone:hover {\n border-color: var(--tpl-primary) !important;\n background-color: var(--tpl-primary-light) !important;\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { A as e, H as t, L as n, M as r, P as i, T as a, V as o, Z as s, c, ct as l, f as u, g as d, h as f, it as p, k as m, l as h, m as g, o as _, ot as v, p as y, st as b, t as x, u as S, v as C, x as w, y as T, z as E } from "./draggable-D840DIZB.js";
|
|
2
|
-
import { M as
|
|
3
|
-
import { B as
|
|
4
|
-
import { t as
|
|
5
|
-
import { Pt as
|
|
6
|
-
import {
|
|
7
|
-
import { t as
|
|
2
|
+
import { M as ee } from "./useEditorCore-CzMhD6Gd.js";
|
|
3
|
+
import { B as D, P as O, f as k, h as A } from "./keys-D7kUmfpE.js";
|
|
4
|
+
import { t as te } from "./useI18n-8Wd7G-cq.js";
|
|
5
|
+
import { Pt as j, jt as M, mt as N, rt as P } from "./icons-CYWw8Xfp.js";
|
|
6
|
+
import { n as F } from "./emailFrameWidth-BmFCN2dp.js";
|
|
7
|
+
import { t as I } from "./TplModal-BN53d9Se.js";
|
|
8
|
+
import { t as L } from "./BlockPreviewCanvas-eASrdFSN.js";
|
|
8
9
|
//#region src/components/SavedBlockPreviewRow.vue?vue&type=script&setup=true&lang.ts
|
|
9
|
-
var
|
|
10
|
+
var R = ["data-block-id"], z = ["aria-label", "title"], B = { class: "tpl:relative tpl:min-w-0 tpl:flex-1" }, V = {
|
|
10
11
|
key: 0,
|
|
11
12
|
"data-testid": "saved-blocks-preview-fade",
|
|
12
13
|
"aria-hidden": "true",
|
|
13
14
|
class: "tpl:pointer-events-none tpl:absolute tpl:inset-x-0 tpl:bottom-0 tpl:h-10",
|
|
14
15
|
style: { background: "linear-gradient(\n to bottom,\n transparent,\n var(--tpl-bg) 90%\n )" }
|
|
15
|
-
},
|
|
16
|
+
}, H = ["aria-label", "aria-expanded"], U = 240, W = /* @__PURE__ */ w({
|
|
16
17
|
__name: "SavedBlockPreviewRow",
|
|
17
18
|
props: {
|
|
18
19
|
block: {},
|
|
@@ -21,25 +22,25 @@ var z = ["data-block-id"], B = ["aria-label", "title"], V = { class: "tpl:relati
|
|
|
21
22
|
},
|
|
22
23
|
emits: ["move"],
|
|
23
24
|
setup(t, { emit: i }) {
|
|
24
|
-
let o = t, _ = i, { t: v, format: x } =
|
|
25
|
-
function
|
|
26
|
-
let e =
|
|
25
|
+
let o = t, _ = i, { t: v, format: x } = te(), S = a(k, []), w = a(A, null), E = u(() => F(w?.content.value.settings)), D = s(null), O = s(null), P = s(1), I = s(null), W = s(!1), G = null, K = u(() => I.value !== null && I.value > U), q = u(() => I.value === null ? null : W.value || !K.value ? I.value : U);
|
|
26
|
+
function J() {
|
|
27
|
+
let e = D.value, t = O.value;
|
|
27
28
|
if (!e || !t) return;
|
|
28
29
|
let n = e.clientWidth;
|
|
29
30
|
if (n <= 0) return;
|
|
30
|
-
let r = Math.min(1, n /
|
|
31
|
-
|
|
31
|
+
let r = Math.min(1, n / E.value);
|
|
32
|
+
P.value = r, I.value = t.offsetHeight * r;
|
|
32
33
|
}
|
|
33
34
|
e(() => {
|
|
34
|
-
|
|
35
|
+
J(), !(typeof ResizeObserver > "u") && (G = new ResizeObserver(() => J()), D.value && G.observe(D.value), O.value && G.observe(O.value));
|
|
35
36
|
}), m(() => {
|
|
36
|
-
|
|
37
|
+
G?.disconnect(), G = null;
|
|
37
38
|
});
|
|
38
|
-
let
|
|
39
|
-
block:
|
|
39
|
+
let Y = u(() => ee(o.block, v, S)), X = u(() => x(v.savedBlocks.reorderHandle, {
|
|
40
|
+
block: Y.value,
|
|
40
41
|
position: o.position,
|
|
41
42
|
total: o.total
|
|
42
|
-
})),
|
|
43
|
+
})), Z = u(() => x(W.value ? v.savedBlocks.collapsePreview : v.savedBlocks.expandPreview, { block: Y.value }));
|
|
43
44
|
return (e, i) => (r(), d("div", {
|
|
44
45
|
"data-testid": "saved-blocks-reorder-row",
|
|
45
46
|
"data-block-id": t.block.id,
|
|
@@ -49,67 +50,67 @@ var z = ["data-block-id"], B = ["aria-label", "title"], V = { class: "tpl:relati
|
|
|
49
50
|
type: "button",
|
|
50
51
|
"data-testid": "saved-blocks-reorder-handle",
|
|
51
52
|
class: "tpl-saved-block-reorder-handle tpl:mt-0.5 tpl:shrink-0 tpl:cursor-grab tpl:rounded tpl:border-none tpl:bg-transparent tpl:p-0.5 tpl:text-[var(--tpl-text-dim)]",
|
|
52
|
-
"aria-label":
|
|
53
|
+
"aria-label": X.value,
|
|
53
54
|
title: p(v).savedBlocks.reorderHint,
|
|
54
55
|
onKeydown: [i[0] ||= c(h((e) => _("move", -1), ["prevent"]), ["up"]), i[1] ||= c(h((e) => _("move", 1), ["prevent"]), ["down"])]
|
|
55
|
-
}, [T(p(
|
|
56
|
+
}, [T(p(N), {
|
|
56
57
|
size: 14,
|
|
57
58
|
"stroke-width": 1.5
|
|
58
|
-
})], 40,
|
|
59
|
+
})], 40, z), y("div", B, [
|
|
59
60
|
y("div", {
|
|
60
61
|
ref_key: "frameEl",
|
|
61
|
-
ref:
|
|
62
|
+
ref: D,
|
|
62
63
|
class: "tpl:overflow-hidden",
|
|
63
|
-
style: b({ height:
|
|
64
|
+
style: b({ height: q.value === null ? void 0 : `${q.value}px` })
|
|
64
65
|
}, [y("div", {
|
|
65
66
|
ref_key: "contentEl",
|
|
66
|
-
ref:
|
|
67
|
+
ref: O,
|
|
67
68
|
style: b({
|
|
68
|
-
width: `${
|
|
69
|
-
transform: `scale(${
|
|
69
|
+
width: `${E.value}px`,
|
|
70
|
+
transform: `scale(${P.value})`,
|
|
70
71
|
transformOrigin: "top left"
|
|
71
72
|
})
|
|
72
|
-
}, [T(
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
}, [T(L, { blocks: [t.block] }, null, 8, ["blocks"])], 4)], 4),
|
|
74
|
+
K.value && !W.value ? (r(), d("div", V)) : f("", !0),
|
|
75
|
+
K.value ? (r(), d("button", {
|
|
75
76
|
key: 1,
|
|
76
77
|
type: "button",
|
|
77
78
|
"data-testid": "saved-blocks-preview-toggle",
|
|
78
79
|
class: "tpl:absolute tpl:bottom-1 tpl:right-1 tpl:flex tpl:cursor-pointer tpl:items-center tpl:gap-0.5 tpl:rounded tpl:border tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-elevated)] tpl:text-[var(--tpl-text-muted)]",
|
|
79
|
-
"aria-label":
|
|
80
|
-
"aria-expanded":
|
|
81
|
-
onClick: i[2] ||= (e) =>
|
|
82
|
-
}, [(r(), g(n(p(
|
|
80
|
+
"aria-label": Z.value,
|
|
81
|
+
"aria-expanded": W.value,
|
|
82
|
+
onClick: i[2] ||= (e) => W.value = !W.value
|
|
83
|
+
}, [(r(), g(n(p(W.value ? M : j)), {
|
|
83
84
|
size: 11,
|
|
84
85
|
"stroke-width": 2
|
|
85
|
-
})), C(" " + l(
|
|
86
|
-
])], 8,
|
|
86
|
+
})), C(" " + l(W.value ? p(v).savedBlocks.collapse : p(v).savedBlocks.expand), 1)], 8, H)) : f("", !0)
|
|
87
|
+
])], 8, R));
|
|
87
88
|
}
|
|
88
|
-
}),
|
|
89
|
+
}), G = ["aria-busy"], K = {
|
|
89
90
|
id: "tpl-save-block-title",
|
|
90
91
|
class: "tpl:mb-4 tpl:shrink-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]"
|
|
91
|
-
},
|
|
92
|
+
}, q = { class: "tpl:mb-3 tpl:shrink-0" }, J = { class: "tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]" }, Y = ["placeholder", "disabled"], X = { class: "tpl:mb-3 tpl:shrink-0" }, Z = {
|
|
92
93
|
for: "tpl-save-block-category",
|
|
93
94
|
class: "tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]"
|
|
94
|
-
},
|
|
95
|
+
}, ne = ["placeholder", "disabled"], re = { id: "tpl-saved-block-categories" }, ie = ["value"], ae = {
|
|
95
96
|
"data-testid": "saved-blocks-save-summary",
|
|
96
97
|
class: "tpl:mb-1.5 tpl:shrink-0 tpl:text-xs tpl:text-[var(--tpl-text-muted)]"
|
|
97
|
-
},
|
|
98
|
+
}, oe = { class: "tpl:text-[var(--tpl-text-dim)]" }, se = {
|
|
98
99
|
key: 0,
|
|
99
100
|
class: "tpl:mb-1.5 tpl:shrink-0 tpl:text-[11px] tpl:text-[var(--tpl-text-dim)]"
|
|
100
|
-
},
|
|
101
|
+
}, ce = {
|
|
101
102
|
class: "tpl-sr-only",
|
|
102
103
|
role: "status",
|
|
103
104
|
"aria-live": "polite",
|
|
104
105
|
"aria-atomic": "true"
|
|
105
|
-
},
|
|
106
|
+
}, le = {
|
|
106
107
|
key: 1,
|
|
107
108
|
role: "alert",
|
|
108
109
|
class: "tpl:mb-3 tpl:shrink-0 tpl:text-xs tpl:text-[var(--tpl-danger)]"
|
|
109
|
-
},
|
|
110
|
+
}, ue = { class: "tpl:flex tpl:shrink-0 tpl:justify-end tpl:gap-2" }, de = ["disabled"], fe = ["disabled"], pe = {
|
|
110
111
|
key: 0,
|
|
111
112
|
class: "tpl:flex tpl:items-center tpl:gap-1.5"
|
|
112
|
-
},
|
|
113
|
+
}, me = { key: 1 }, Q = /* @__PURE__ */ w({
|
|
113
114
|
__name: "SaveBlockDialog",
|
|
114
115
|
props: {
|
|
115
116
|
visible: { type: Boolean },
|
|
@@ -117,55 +118,55 @@ var z = ["data-block-id"], B = ["aria-label", "title"], V = { class: "tpl:relati
|
|
|
117
118
|
},
|
|
118
119
|
emits: ["close", "saved"],
|
|
119
120
|
setup(e, { emit: n }) {
|
|
120
|
-
let c = e, m = n, { t: h, format: b } =
|
|
121
|
+
let c = e, m = n, { t: h, format: b } = te(), w = D(A, "SaveBlockDialog"), j = D(O, "SaveBlockDialog"), M = a(k, []), N = s(""), F = s(""), L = s(!1), R = s(null), z = s(""), B = s([]), V = u(() => {
|
|
121
122
|
let e = new Map(w.content.value.blocks.map((e) => [e.id, e]));
|
|
122
|
-
return
|
|
123
|
-
}),
|
|
124
|
-
get: () =>
|
|
123
|
+
return B.value.map((t) => e.get(t)).filter((e) => e !== void 0);
|
|
124
|
+
}), H = u({
|
|
125
|
+
get: () => V.value,
|
|
125
126
|
set: (e) => {
|
|
126
|
-
|
|
127
|
+
B.value = e.map((e) => e.id);
|
|
127
128
|
}
|
|
128
129
|
});
|
|
129
|
-
function
|
|
130
|
-
let n =
|
|
130
|
+
function U(e, t) {
|
|
131
|
+
let n = V.value.map((e) => e.id), r = n.indexOf(e), i = r + t;
|
|
131
132
|
if (r === -1 || i < 0 || i >= n.length) return;
|
|
132
|
-
[n[r], n[i]] = [n[i], n[r]],
|
|
133
|
-
let a =
|
|
134
|
-
|
|
135
|
-
block:
|
|
133
|
+
[n[r], n[i]] = [n[i], n[r]], B.value = n;
|
|
134
|
+
let a = V.value[i];
|
|
135
|
+
z.value = a ? b(h.savedBlocks.reorderAnnouncement, {
|
|
136
|
+
block: Q(a),
|
|
136
137
|
position: i + 1,
|
|
137
138
|
total: n.length
|
|
138
139
|
}) : "";
|
|
139
140
|
}
|
|
140
|
-
function
|
|
141
|
-
return
|
|
141
|
+
function Q(e) {
|
|
142
|
+
return ee(e, h, M);
|
|
142
143
|
}
|
|
143
|
-
let
|
|
144
|
+
let he = u(() => V.value.map(Q).join(", "));
|
|
144
145
|
E(() => c.visible, (e) => {
|
|
145
|
-
e && (
|
|
146
|
+
e && (N.value = "", F.value = "", R.value = null, z.value = "", B.value = [...c.pickedIds]);
|
|
146
147
|
}, { immediate: !0 });
|
|
147
|
-
let
|
|
148
|
+
let ge = u(() => N.value.trim().length > 0 && V.value.length > 0 && !L.value);
|
|
148
149
|
async function _e() {
|
|
149
|
-
if (!
|
|
150
|
-
let e =
|
|
150
|
+
if (!ge.value) return;
|
|
151
|
+
let e = V.value;
|
|
151
152
|
if (e.length !== 0) {
|
|
152
|
-
|
|
153
|
+
L.value = !0, R.value = null;
|
|
153
154
|
try {
|
|
154
|
-
await
|
|
155
|
+
await j.create(N.value.trim(), e, F.value.trim() || void 0), m("saved"), m("close");
|
|
155
156
|
} catch (e) {
|
|
156
|
-
|
|
157
|
+
R.value = e.message;
|
|
157
158
|
} finally {
|
|
158
|
-
|
|
159
|
+
L.value = !1;
|
|
159
160
|
}
|
|
160
161
|
}
|
|
161
162
|
}
|
|
162
163
|
function $() {
|
|
163
|
-
|
|
164
|
+
L.value || m("close");
|
|
164
165
|
}
|
|
165
166
|
function ve(e) {
|
|
166
167
|
e.key === "Enter" && !e.shiftKey && (e.preventDefault(), _e()), e.key === "Escape" && $();
|
|
167
168
|
}
|
|
168
|
-
return (n, a) => (r(), g(
|
|
169
|
+
return (n, a) => (r(), g(I, {
|
|
169
170
|
visible: e.visible,
|
|
170
171
|
onClose: $,
|
|
171
172
|
onKeydown: ve
|
|
@@ -173,7 +174,7 @@ var z = ["data-block-id"], B = ["aria-label", "title"], V = { class: "tpl:relati
|
|
|
173
174
|
default: o(() => [y("div", {
|
|
174
175
|
role: "dialog",
|
|
175
176
|
"aria-modal": "true",
|
|
176
|
-
"aria-busy":
|
|
177
|
+
"aria-busy": L.value,
|
|
177
178
|
"aria-labelledby": "tpl-save-block-title",
|
|
178
179
|
class: "tpl-scale-in tpl:mx-4 tpl:flex tpl:w-full tpl:max-w-2xl tpl:flex-col tpl:rounded-[var(--tpl-radius-lg)] tpl:p-5",
|
|
179
180
|
style: {
|
|
@@ -182,37 +183,37 @@ var z = ["data-block-id"], B = ["aria-label", "title"], V = { class: "tpl:relati
|
|
|
182
183
|
"max-height": "90vh"
|
|
183
184
|
}
|
|
184
185
|
}, [
|
|
185
|
-
y("h3",
|
|
186
|
-
y("div",
|
|
187
|
-
"onUpdate:modelValue": a[0] ||= (e) =>
|
|
186
|
+
y("h3", K, l(p(h).savedBlocks.saveAsBlock), 1),
|
|
187
|
+
y("div", q, [y("label", J, l(p(h).savedBlocks.name), 1), t(y("input", {
|
|
188
|
+
"onUpdate:modelValue": a[0] ||= (e) => N.value = e,
|
|
188
189
|
type: "text",
|
|
189
190
|
"data-testid": "saved-blocks-name-input",
|
|
190
191
|
placeholder: p(h).savedBlocks.namePlaceholder,
|
|
191
192
|
class: "tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]",
|
|
192
|
-
disabled:
|
|
193
|
-
}, null, 8,
|
|
194
|
-
y("div",
|
|
195
|
-
y("label",
|
|
193
|
+
disabled: L.value
|
|
194
|
+
}, null, 8, Y), [[_, N.value]])]),
|
|
195
|
+
y("div", X, [
|
|
196
|
+
y("label", Z, l(p(h).savedBlocks.category), 1),
|
|
196
197
|
t(y("input", {
|
|
197
198
|
id: "tpl-save-block-category",
|
|
198
|
-
"onUpdate:modelValue": a[1] ||= (e) =>
|
|
199
|
+
"onUpdate:modelValue": a[1] ||= (e) => F.value = e,
|
|
199
200
|
type: "text",
|
|
200
201
|
"data-testid": "saved-blocks-category-input",
|
|
201
202
|
list: "tpl-saved-block-categories",
|
|
202
203
|
placeholder: p(h).savedBlocks.categoryPlaceholder,
|
|
203
204
|
class: "tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]",
|
|
204
|
-
disabled:
|
|
205
|
-
}, null, 8,
|
|
206
|
-
y("datalist",
|
|
205
|
+
disabled: L.value
|
|
206
|
+
}, null, 8, ne), [[_, F.value]]),
|
|
207
|
+
y("datalist", re, [(r(!0), d(S, null, i(p(j).categories.value, (e) => (r(), d("option", {
|
|
207
208
|
key: e,
|
|
208
209
|
value: e
|
|
209
|
-
}, null, 8,
|
|
210
|
+
}, null, 8, ie))), 128))])
|
|
210
211
|
]),
|
|
211
|
-
y("p",
|
|
212
|
-
|
|
212
|
+
y("p", ae, [C(l(p(b)(p(h).savedBlocks.savingCount, { count: V.value.length })) + " ", 1), y("span", oe, l(he.value), 1)]),
|
|
213
|
+
V.value.length > 1 ? (r(), d("p", se, l(p(h).savedBlocks.reorderHint), 1)) : f("", !0),
|
|
213
214
|
T(p(x), {
|
|
214
|
-
modelValue:
|
|
215
|
-
"onUpdate:modelValue": a[2] ||= (e) =>
|
|
215
|
+
modelValue: H.value,
|
|
216
|
+
"onUpdate:modelValue": a[2] ||= (e) => H.value = e,
|
|
216
217
|
"data-testid": "saved-blocks-reorder-list",
|
|
217
218
|
animation: 150,
|
|
218
219
|
"ghost-class": "tpl-ghost",
|
|
@@ -221,12 +222,12 @@ var z = ["data-block-id"], B = ["aria-label", "title"], V = { class: "tpl:relati
|
|
|
221
222
|
"force-fallback": !0,
|
|
222
223
|
class: "tpl:mb-4 tpl:flex tpl:min-h-0 tpl:flex-1 tpl:flex-col tpl:gap-1.5 tpl:overflow-y-auto"
|
|
223
224
|
}, {
|
|
224
|
-
default: o(() => [(r(!0), d(S, null, i(
|
|
225
|
+
default: o(() => [(r(!0), d(S, null, i(H.value, (e, t) => (r(), g(W, {
|
|
225
226
|
key: e.id,
|
|
226
227
|
block: e,
|
|
227
228
|
position: t + 1,
|
|
228
|
-
total:
|
|
229
|
-
onMove: (t) =>
|
|
229
|
+
total: H.value.length,
|
|
230
|
+
onMove: (t) => U(e.id, t)
|
|
230
231
|
}, null, 8, [
|
|
231
232
|
"block",
|
|
232
233
|
"position",
|
|
@@ -235,29 +236,29 @@ var z = ["data-block-id"], B = ["aria-label", "title"], V = { class: "tpl:relati
|
|
|
235
236
|
]))), 128))]),
|
|
236
237
|
_: 1
|
|
237
238
|
}, 8, ["modelValue"]),
|
|
238
|
-
y("div",
|
|
239
|
-
|
|
240
|
-
y("div",
|
|
239
|
+
y("div", ce, l(z.value), 1),
|
|
240
|
+
R.value ? (r(), d("p", le, l(R.value), 1)) : f("", !0),
|
|
241
|
+
y("div", ue, [y("button", {
|
|
241
242
|
type: "button",
|
|
242
|
-
class: v(["tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]", { "tpl:cursor-not-allowed tpl:opacity-50":
|
|
243
|
-
disabled:
|
|
243
|
+
class: v(["tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]", { "tpl:cursor-not-allowed tpl:opacity-50": L.value }]),
|
|
244
|
+
disabled: L.value,
|
|
244
245
|
onClick: $
|
|
245
|
-
}, l(p(h).savedBlocks.cancel), 11,
|
|
246
|
+
}, l(p(h).savedBlocks.cancel), 11, de), y("button", {
|
|
246
247
|
type: "button",
|
|
247
248
|
class: "tpl:cursor-pointer tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]",
|
|
248
|
-
disabled: !
|
|
249
|
+
disabled: !ge.value,
|
|
249
250
|
onClick: _e
|
|
250
|
-
}, [
|
|
251
|
+
}, [L.value ? (r(), d("span", pe, [T(p(P), {
|
|
251
252
|
class: "tpl:animate-spin",
|
|
252
253
|
size: 12,
|
|
253
254
|
"stroke-width": 2
|
|
254
|
-
}), C(" " + l(p(h).savedBlocks.saving), 1)])) : (r(), d("span",
|
|
255
|
-
], 8,
|
|
255
|
+
}), C(" " + l(p(h).savedBlocks.saving), 1)])) : (r(), d("span", me, l(p(h).savedBlocks.save), 1))], 8, fe)])
|
|
256
|
+
], 8, G)]),
|
|
256
257
|
_: 1
|
|
257
258
|
}, 8, ["visible"]));
|
|
258
259
|
}
|
|
259
260
|
});
|
|
260
261
|
//#endregion
|
|
261
|
-
export {
|
|
262
|
+
export { Q as default };
|
|
262
263
|
|
|
263
|
-
//# sourceMappingURL=SaveBlockDialog-
|
|
264
|
+
//# sourceMappingURL=SaveBlockDialog-Bi_Z3kuA.js.map
|