@templatical/editor 0.39.2 → 0.39.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/dist/{AiChatSidebar-pOxCZWz9.js → AiChatSidebar-5qqrS6uy.js} +69 -63
  2. package/dist/{AiFeatureMenu-Bljk6Qjv.js → AiFeatureMenu-BBO_Ojlp.js} +1 -1
  3. package/dist/{BlockPreviewCanvas-DXGjUECG.js → BlockPreviewCanvas-BMC16_QU.js} +1 -1
  4. package/dist/{CloudHeaderExtras-DaocwNmy.js → CloudHeaderExtras-Cg6uF-yQ.js} +1 -1
  5. package/dist/{CloudPanels-CyZAnSJY.js → CloudPanels-D1Gg4qrG.js} +80 -66
  6. package/dist/{CollaboratorBar-C7Zca3g1.js → CollaboratorBar-Cmn0LccS.js} +3 -3
  7. package/dist/{CommentsPanel-CNOb7YCm.js → CommentsPanel-twNhYUqZ.js} +1 -1
  8. package/dist/{CommentsSidebar-VmwjU9va.js → CommentsSidebar-DDO0Qx8U.js} +122 -112
  9. package/dist/{CountdownToolbar-C2kfS6Oy.js → CountdownToolbar-nvHxu8Gu.js} +1 -1
  10. package/dist/DesignReferenceSidebar-C8ohNhOd.js +280 -0
  11. package/dist/{IssuesPanel-DL2Xr14_.js → IssuesPanel-Ck0EGoV5.js} +4 -4
  12. package/dist/{LoadingTrack-C2jKCU2d.js → LoadingTrack-C1Epulqq.js} +1 -1
  13. package/dist/{MediaEditModal-D4GEq2md.js → MediaEditModal-BFWmufJK.js} +3 -3
  14. package/dist/{MediaPanels-sapgKtxu.js → MediaPanels-BmDda5rY.js} +1 -1
  15. package/dist/{ParagraphEditor-DkkPSexG.js → ParagraphEditor-D6FRxr8Y.js} +2 -2
  16. package/dist/{RestoreVersionDialog-BgueZnZr.js → RestoreVersionDialog--b0qfNaK.js} +1 -1
  17. package/dist/{SaveBlockDialog-BuTuVlLr.js → SaveBlockDialog-C0BdEGeL.js} +6 -6
  18. package/dist/{SavedBlocksBrowserModal-Srhix8BB.js → SavedBlocksBrowserModal-BzDgXtr4.js} +8 -8
  19. package/dist/{SavedBlocksPanels-B2iScCS-.js → SavedBlocksPanels-DN-bDZzy.js} +1 -1
  20. package/dist/SavedBlocksPickBar-pEi9Et8Q.js +53 -0
  21. package/dist/{TemplateScoringPanel-C2Rdlyn6.js → TemplateScoringPanel-B1wcfE0w.js} +62 -56
  22. package/dist/{TestEmailModal-DJ5q_u4N.js → TestEmailModal-maM8SLBV.js} +6 -6
  23. package/dist/{TestEmailPanel-CP24UGst.js → TestEmailPanel-8iAZYpCK.js} +1 -1
  24. package/dist/{TitleEditor-QBBOXtxx.js → TitleEditor-L26QXwna.js} +1 -1
  25. package/dist/{Toolbar-COD2TW20.js → Toolbar-B2kDLFhn.js} +314 -314
  26. package/dist/{TplModal-C8xGjC1l.js → TplModal-DVMS_nAn.js} +1 -1
  27. package/dist/{VersionHistoryPanels-BrK6na4A.js → VersionHistoryPanels-D8f8KvKX.js} +1 -1
  28. package/dist/bundle-stats.json +6 -6
  29. package/dist/{ca-YlPDhElv.js → ca-Bqeygp1_.js} +1 -0
  30. package/dist/cdn/chunks/{AiChatSidebar-CjSEoK4x.js → AiChatSidebar-ctfIU4i0.js} +67 -61
  31. package/dist/cdn/chunks/AiChatSidebar-ctfIU4i0.js.map +1 -0
  32. package/dist/cdn/chunks/{AiFeatureMenu-l0K5TOSn.js → AiFeatureMenu-ByygdH_9.js} +2 -2
  33. package/dist/cdn/chunks/{AiFeatureMenu-l0K5TOSn.js.map → AiFeatureMenu-ByygdH_9.js.map} +1 -1
  34. package/dist/cdn/chunks/{BlockPreviewCanvas-lQrAdgG3.js → BlockPreviewCanvas-DeS7Sa--.js} +2 -2
  35. package/dist/cdn/chunks/{BlockPreviewCanvas-lQrAdgG3.js.map → BlockPreviewCanvas-DeS7Sa--.js.map} +1 -1
  36. package/dist/cdn/chunks/{CloudHeaderExtras-CXNe7Q-4.js → CloudHeaderExtras-BxsMpvVr.js} +2 -2
  37. package/dist/cdn/chunks/{CloudHeaderExtras-CXNe7Q-4.js.map → CloudHeaderExtras-BxsMpvVr.js.map} +1 -1
  38. package/dist/cdn/chunks/{CloudPanels-bHhh9AUA.js → CloudPanels-DmxmqAS_.js} +78 -64
  39. package/dist/cdn/chunks/CloudPanels-DmxmqAS_.js.map +1 -0
  40. package/dist/cdn/chunks/{CollaboratorBar-DgqcaGGI.js → CollaboratorBar-B8ttqAqX.js} +4 -4
  41. package/dist/cdn/chunks/CollaboratorBar-B8ttqAqX.js.map +1 -0
  42. package/dist/cdn/chunks/{CommentsPanel-Cy4TlTHC.js → CommentsPanel-C8ibhaiK.js} +2 -2
  43. package/dist/cdn/chunks/{CommentsPanel-Cy4TlTHC.js.map → CommentsPanel-C8ibhaiK.js.map} +1 -1
  44. package/dist/cdn/chunks/{CommentsSidebar-C7oAP3Mx.js → CommentsSidebar-CybWfZ3V.js} +118 -108
  45. package/dist/cdn/chunks/CommentsSidebar-CybWfZ3V.js.map +1 -0
  46. package/dist/cdn/chunks/{CountdownToolbar-CN9h4nlC.js → CountdownToolbar-DRESXiVD.js} +2 -2
  47. package/dist/cdn/chunks/{CountdownToolbar-CN9h4nlC.js.map → CountdownToolbar-DRESXiVD.js.map} +1 -1
  48. package/dist/cdn/chunks/DesignReferenceSidebar-CtaoY-hl.js +230 -0
  49. package/dist/cdn/chunks/DesignReferenceSidebar-CtaoY-hl.js.map +1 -0
  50. package/dist/cdn/chunks/{IssuesPanel-DdFk-Blf.js → IssuesPanel-C6RlIxoi.js} +5 -5
  51. package/dist/cdn/chunks/IssuesPanel-C6RlIxoi.js.map +1 -0
  52. package/dist/cdn/chunks/{LoadingTrack-Ba5xh5HD.js → LoadingTrack-_7DH_5aw.js} +2 -2
  53. package/dist/cdn/chunks/{LoadingTrack-Ba5xh5HD.js.map → LoadingTrack-_7DH_5aw.js.map} +1 -1
  54. package/dist/cdn/chunks/{MediaEditModal-BBZRcYKe.js → MediaEditModal-CZBZviu2.js} +4 -4
  55. package/dist/cdn/chunks/MediaEditModal-CZBZviu2.js.map +1 -0
  56. package/dist/cdn/chunks/{MediaPanels-CSBCsFwT.js → MediaPanels-Bq5b-XJM.js} +2 -2
  57. package/dist/cdn/chunks/{MediaPanels-CSBCsFwT.js.map → MediaPanels-Bq5b-XJM.js.map} +1 -1
  58. package/dist/cdn/chunks/{ParagraphEditor-DO4RzP8l.js → ParagraphEditor-R2ph_UOo.js} +3 -3
  59. package/dist/cdn/chunks/ParagraphEditor-R2ph_UOo.js.map +1 -0
  60. package/dist/cdn/chunks/{RestoreVersionDialog-CzZ_sFnz.js → RestoreVersionDialog-C-dwbb7T.js} +2 -2
  61. package/dist/cdn/chunks/{RestoreVersionDialog-CzZ_sFnz.js.map → RestoreVersionDialog-C-dwbb7T.js.map} +1 -1
  62. package/dist/cdn/chunks/{SaveBlockDialog-LgxYrXyx.js → SaveBlockDialog-C_BPjVv6.js} +7 -7
  63. package/dist/cdn/chunks/SaveBlockDialog-C_BPjVv6.js.map +1 -0
  64. package/dist/cdn/chunks/{SavedBlocksBrowserModal-CdeSsTpb.js → SavedBlocksBrowserModal-CrrGEfFb.js} +9 -9
  65. package/dist/cdn/chunks/SavedBlocksBrowserModal-CrrGEfFb.js.map +1 -0
  66. package/dist/cdn/chunks/{SavedBlocksPanels-B5hakNJj.js → SavedBlocksPanels-DfTaMPAr.js} +2 -2
  67. package/dist/cdn/chunks/{SavedBlocksPanels-B5hakNJj.js.map → SavedBlocksPanels-DfTaMPAr.js.map} +1 -1
  68. package/dist/cdn/chunks/SavedBlocksPickBar-DziwHYSu.js +55 -0
  69. package/dist/cdn/chunks/SavedBlocksPickBar-DziwHYSu.js.map +1 -0
  70. package/dist/cdn/chunks/{TemplateScoringPanel--JxR6CsQ.js → TemplateScoringPanel-CR6RG6dZ.js} +95 -89
  71. package/dist/cdn/chunks/TemplateScoringPanel-CR6RG6dZ.js.map +1 -0
  72. package/dist/cdn/chunks/{TestEmailModal-BAuDxJtD.js → TestEmailModal-DId9Og-N.js} +7 -7
  73. package/dist/cdn/chunks/TestEmailModal-DId9Og-N.js.map +1 -0
  74. package/dist/cdn/chunks/{TestEmailPanel-GUGZisLp.js → TestEmailPanel-B8LwXmeX.js} +2 -2
  75. package/dist/cdn/chunks/{TestEmailPanel-GUGZisLp.js.map → TestEmailPanel-B8LwXmeX.js.map} +1 -1
  76. package/dist/cdn/chunks/{TitleEditor-uKu33Nwr.js → TitleEditor-C7KMzX1e.js} +2 -2
  77. package/dist/cdn/chunks/{TitleEditor-uKu33Nwr.js.map → TitleEditor-C7KMzX1e.js.map} +1 -1
  78. package/dist/cdn/chunks/{Toolbar-dxDYwN5E.js → Toolbar-DqFhWxgW.js} +310 -310
  79. package/dist/cdn/chunks/Toolbar-DqFhWxgW.js.map +1 -0
  80. package/dist/cdn/chunks/{TplModal-DuwS2ifK.js → TplModal-BKqDFLq3.js} +2 -2
  81. package/dist/cdn/chunks/{TplModal-DuwS2ifK.js.map → TplModal-BKqDFLq3.js.map} +1 -1
  82. package/dist/cdn/chunks/{VersionHistoryPanels-GeR6MStB.js → VersionHistoryPanels-DcCFFDK6.js} +2 -2
  83. package/dist/cdn/chunks/{VersionHistoryPanels-GeR6MStB.js.map → VersionHistoryPanels-DcCFFDK6.js.map} +1 -1
  84. package/dist/cdn/chunks/{ca-YlPDhElv.js → ca-Bqeygp1_.js} +2 -1
  85. package/dist/cdn/chunks/{ca-YlPDhElv.js.map → ca-Bqeygp1_.js.map} +1 -1
  86. package/dist/cdn/chunks/{de-CONjnzrF.js → de-CY6SlqBx.js} +2 -1
  87. package/dist/cdn/chunks/{de-CONjnzrF.js.map → de-CY6SlqBx.js.map} +1 -1
  88. package/dist/{de-BytZwgn6.js → cdn/chunks/de-mMY5yQJu.js} +5 -0
  89. package/dist/cdn/chunks/de-mMY5yQJu.js.map +1 -0
  90. package/dist/cdn/chunks/{editor-modal-C-yBlj2C.js → editor-modal-zxvWz8R6.js} +4 -3
  91. package/dist/cdn/chunks/editor-modal-zxvWz8R6.js.map +1 -0
  92. package/dist/cdn/chunks/{en-BAMooiOF.js → en-8V-2kDcp.js} +2 -1
  93. package/dist/cdn/chunks/{en-BAMooiOF.js.map → en-8V-2kDcp.js.map} +1 -1
  94. package/dist/cdn/chunks/en-BcQ9ovQu.js +2 -0
  95. package/dist/{en-DyOSV0lY.js → cdn/chunks/en-ChVeGQBY.js} +5 -0
  96. package/dist/cdn/chunks/en-ChVeGQBY.js.map +1 -0
  97. package/dist/cdn/chunks/{es-I47lHs2m.js → es-Cmn1rT3l.js} +2 -1
  98. package/dist/cdn/chunks/{es-I47lHs2m.js.map → es-Cmn1rT3l.js.map} +1 -1
  99. package/dist/cdn/chunks/{fr-B8MIKjT4.js → fr-2yDa4zXM.js} +2 -1
  100. package/dist/cdn/chunks/{fr-B8MIKjT4.js.map → fr-2yDa4zXM.js.map} +1 -1
  101. package/dist/cdn/chunks/{nl-CBLxcb-0.js → nl-Bzk2zRI8.js} +2 -1
  102. package/dist/cdn/chunks/{nl-CBLxcb-0.js.map → nl-Bzk2zRI8.js.map} +1 -1
  103. package/dist/cdn/chunks/{pt-BR-C1WLSGDh.js → pt-BR-CoCwRUi0.js} +4 -1
  104. package/dist/cdn/chunks/pt-BR-CoCwRUi0.js.map +1 -0
  105. package/dist/cdn/chunks/{pt-BR-CMSRfX7t.js → pt-BR-DqtSCTUi.js} +2 -1
  106. package/dist/cdn/chunks/{pt-BR-CMSRfX7t.js.map → pt-BR-DqtSCTUi.js.map} +1 -1
  107. package/dist/cdn/chunks/{src-O5Sp_qJ7.js → src-CqYYKcCI.js} +2 -2
  108. package/dist/cdn/chunks/{src-O5Sp_qJ7.js.map → src-CqYYKcCI.js.map} +1 -1
  109. package/dist/cdn/chunks/{useEditorCore-BHmIp1hW.js → useEditorCore-CXYI14ux.js} +14 -14
  110. package/dist/cdn/chunks/{useEditorCore-BHmIp1hW.js.map → useEditorCore-CXYI14ux.js.map} +1 -1
  111. package/dist/cdn/editor.css +1 -1
  112. package/dist/cdn/editor.js +15 -15
  113. package/dist/cdn/editor.js.map +1 -1
  114. package/dist/{de-CONjnzrF.js → de-CY6SlqBx.js} +1 -0
  115. package/dist/{cdn/chunks/de-BytZwgn6.js → de-mMY5yQJu.js} +3 -2
  116. package/dist/{editor-modal-oD1qNz_P.js → editor-modal-Czq1I8oC.js} +3 -2
  117. package/dist/{en-BAMooiOF.js → en-8V-2kDcp.js} +1 -0
  118. package/dist/{cdn/chunks/en-DyOSV0lY.js → en-ChVeGQBY.js} +3 -2
  119. package/dist/en-DQqmtndQ.js +2 -0
  120. package/dist/{es-I47lHs2m.js → es-Cmn1rT3l.js} +1 -0
  121. package/dist/{fr-B8MIKjT4.js → fr-2yDa4zXM.js} +1 -0
  122. package/dist/{nl-CBLxcb-0.js → nl-Bzk2zRI8.js} +1 -0
  123. package/dist/{pt-BR-C1WLSGDh.js → pt-BR-CoCwRUi0.js} +3 -0
  124. package/dist/{pt-BR-CMSRfX7t.js → pt-BR-DqtSCTUi.js} +1 -0
  125. package/dist/style.css +1 -1
  126. package/dist/templatical-editor.js +14 -14
  127. package/dist/{useEditorCore-C5APBjjV.js → useEditorCore-BUVbQgwb.js} +13 -13
  128. package/package.json +7 -7
  129. package/dist/DesignReferenceSidebar-QC4gEUWp.js +0 -273
  130. package/dist/SavedBlocksPickBar-cI7bDQQi.js +0 -52
  131. package/dist/cdn/chunks/AiChatSidebar-CjSEoK4x.js.map +0 -1
  132. package/dist/cdn/chunks/CloudPanels-bHhh9AUA.js.map +0 -1
  133. package/dist/cdn/chunks/CollaboratorBar-DgqcaGGI.js.map +0 -1
  134. package/dist/cdn/chunks/CommentsSidebar-C7oAP3Mx.js.map +0 -1
  135. package/dist/cdn/chunks/DesignReferenceSidebar-Bjo0M-AY.js +0 -223
  136. package/dist/cdn/chunks/DesignReferenceSidebar-Bjo0M-AY.js.map +0 -1
  137. package/dist/cdn/chunks/IssuesPanel-DdFk-Blf.js.map +0 -1
  138. package/dist/cdn/chunks/MediaEditModal-BBZRcYKe.js.map +0 -1
  139. package/dist/cdn/chunks/ParagraphEditor-DO4RzP8l.js.map +0 -1
  140. package/dist/cdn/chunks/SaveBlockDialog-LgxYrXyx.js.map +0 -1
  141. package/dist/cdn/chunks/SavedBlocksBrowserModal-CdeSsTpb.js.map +0 -1
  142. package/dist/cdn/chunks/SavedBlocksPickBar-Cl6PLwwV.js +0 -54
  143. package/dist/cdn/chunks/SavedBlocksPickBar-Cl6PLwwV.js.map +0 -1
  144. package/dist/cdn/chunks/TemplateScoringPanel--JxR6CsQ.js.map +0 -1
  145. package/dist/cdn/chunks/TestEmailModal-BAuDxJtD.js.map +0 -1
  146. package/dist/cdn/chunks/Toolbar-dxDYwN5E.js.map +0 -1
  147. package/dist/cdn/chunks/de-BytZwgn6.js.map +0 -1
  148. package/dist/cdn/chunks/editor-modal-C-yBlj2C.js.map +0 -1
  149. package/dist/cdn/chunks/en-DYtFUE2l.js +0 -2
  150. package/dist/cdn/chunks/en-DyOSV0lY.js.map +0 -1
  151. package/dist/cdn/chunks/pt-BR-C1WLSGDh.js.map +0 -1
  152. package/dist/en-CM5fTFSo.js +0 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DesignReferenceSidebar-CtaoY-hl.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 { primaryBtnClass } from \"../../constants/styleConstants\";\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 role=\"complementary\"\n :aria-label=\"cloudT.designReference.title\"\n class=\"tpl-design-sidebar tpl:absolute tpl:top-14 tpl:right-0 tpl:bottom-0 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 type=\"button\"\n class=\"tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-muted)]\"\n :aria-label=\"cloudT.designReference.close\"\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\"\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\"\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: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\"\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-[10px] 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:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)] tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)]\"\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: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 type=\"button\"\n :class=\"[primaryBtnClass, 'tpl:flex-1']\"\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 type=\"button\"\n :class=\"[primaryBtnClass, 'tpl:w-full']\"\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-[10px] 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 { primaryBtnClass } from \"../../constants/styleConstants\";\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 role=\"complementary\"\n :aria-label=\"cloudT.designReference.title\"\n class=\"tpl-design-sidebar tpl:absolute tpl:top-14 tpl:right-0 tpl:bottom-0 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 type=\"button\"\n class=\"tpl:rounded-md tpl:p-0.5 tpl:transition-colors tpl:text-[var(--tpl-text-muted)]\"\n :aria-label=\"cloudT.designReference.close\"\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\"\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\"\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: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\"\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-[10px] 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:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)] tpl:focus:border-[var(--tpl-primary)] tpl:focus:shadow-[var(--tpl-ring)]\"\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: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 type=\"button\"\n :class=\"[primaryBtnClass, 'tpl:flex-1']\"\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 type=\"button\"\n :class=\"[primaryBtnClass, 'tpl:w-full']\"\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-[10px] 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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"}
@@ -31,7 +31,7 @@ var E = { class: "tpl:flex tpl:flex-col tpl:gap-4 tpl:p-4" }, D = { class: "tpl:
31
31
  f("h3", O, m(a(z).issues.panelTitle), 1),
32
32
  q.value > 0 ? (n(), l("span", {
33
33
  key: 0,
34
- class: "tpl:ml-auto tpl:rounded-full tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-0.5 tpl:text-[11px] tpl:text-[var(--tpl-text-muted)]",
34
+ class: "tpl:ml-auto tpl:rounded-full tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-0.5 tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]",
35
35
  title: a(B)(a(z).issues.issueCountTooltip, { count: q.value })
36
36
  }, m(q.value), 9, k)) : u("", !0)
37
37
  ]), !a(V) || a(V).unavailable.value ? (n(), l("div", A)) : q.value === 0 ? (n(), l("div", j, m(a(z).issues.emptyState), 1)) : (n(!0), l(p, { key: 2 }, t([
@@ -58,7 +58,7 @@ var E = { class: "tpl:flex tpl:flex-col tpl:gap-4 tpl:p-4" }, D = { class: "tpl:
58
58
  }
59
59
  ], (e) => (n(), l("section", { key: e.key }, [e.items.length > 0 ? (n(), l("header", {
60
60
  key: 0,
61
- class: s(["tpl:mb-2 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-[11px] tpl:font-semibold tpl:uppercase tpl:tracking-wide", e.tone])
61
+ class: s(["tpl:mb-2 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:font-semibold tpl:uppercase tpl:tracking-wide", e.tone])
62
62
  }, [
63
63
  (n(), d(r(e.icon), {
64
64
  size: 12,
@@ -76,7 +76,7 @@ var E = { class: "tpl:flex tpl:flex-col tpl:gap-4 tpl:p-4" }, D = { class: "tpl:
76
76
  key: 0,
77
77
  type: "button",
78
78
  "data-testid": "issue-jump",
79
- class: "tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-text)]",
79
+ class: "tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]",
80
80
  onClick: (t) => J(e)
81
81
  }, [o(a(y), {
82
82
  size: 10,
@@ -84,7 +84,7 @@ var E = { class: "tpl:flex tpl:flex-col tpl:gap-4 tpl:p-4" }, D = { class: "tpl:
84
84
  }), h(" " + m(a(z).issues.jump), 1)], 8, L)) : u("", !0), e.fix ? (n(), l("button", {
85
85
  key: 1,
86
86
  type: "button",
87
- class: "tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-md tpl:bg-[var(--tpl-primary)] tpl:px-2 tpl:py-1 tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-on-primary)]",
87
+ class: "tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-md tpl:bg-[var(--tpl-primary)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-on-primary)]",
88
88
  title: e.fix.description,
89
89
  onClick: (t) => Y(e)
90
90
  }, [o(a(S), {
@@ -97,4 +97,4 @@ var E = { class: "tpl:flex tpl:flex-col tpl:gap-4 tpl:p-4" }, D = { class: "tpl:
97
97
  //#endregion
98
98
  export { z as default };
99
99
 
100
- //# sourceMappingURL=IssuesPanel-DdFk-Blf.js.map
100
+ //# sourceMappingURL=IssuesPanel-C6RlIxoi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IssuesPanel-C6RlIxoi.js","names":[],"sources":["../../../src/components/sidebar/IssuesPanel.vue","../../../src/components/sidebar/IssuesPanel.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport {\n ListChecks,\n AlertCircle,\n AlertTriangle,\n Info,\n ArrowRight,\n Wrench,\n} from \"@lucide/vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { useScrollToBlock } from \"../../composables/useScrollToBlock\";\nimport { TEMPLATE_LINT_KEY, EDITOR_KEY } from \"../../keys\";\nimport type { LintIssue } from \"../../composables/useTemplateLint\";\n\nconst { t, format } = useI18n();\nconst lint = inject(TEMPLATE_LINT_KEY, null);\nconst editor = inject(EDITOR_KEY, null);\nconst scrollToBlock = useScrollToBlock();\n\nconst errors = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"error\"),\n);\nconst warnings = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"warning\"),\n);\nconst infos = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"info\"),\n);\n\nconst totalCount = computed(\n () => errors.value.length + warnings.value.length + infos.value.length,\n);\n\nfunction jumpTo(issue: LintIssue): void {\n if (!editor) return;\n if (!issue.blockId) return;\n editor.selectBlock(issue.blockId);\n // Selecting is invisible on its own — on a long template the block sits\n // below the fold and the canvas stays put, so \"Jump\" appears to do nothing.\n scrollToBlock(issue.blockId);\n}\n\nfunction applyFix(issue: LintIssue): void {\n lint?.applyFix(issue);\n}\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:flex-col tpl:gap-4 tpl:p-4\">\n <header class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <ListChecks :size=\"16\" :stroke-width=\"1.5\" />\n <h3\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.issues.panelTitle }}\n </h3>\n <span\n v-if=\"totalCount > 0\"\n class=\"tpl:ml-auto tpl:rounded-full tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-0.5 tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n :title=\"format(t.issues.issueCountTooltip, { count: totalCount })\"\n >\n {{ totalCount }}\n </span>\n </header>\n\n <div\n v-if=\"!lint || lint.unavailable.value\"\n class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n <!-- Quality package not available; render nothing visible beyond a hint. -->\n </div>\n\n <div\n v-else-if=\"totalCount === 0\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.issues.emptyState }}\n </div>\n\n <template v-else>\n <section\n v-for=\"group in [\n {\n key: 'errors',\n title: t.issues.groupErrors,\n icon: AlertCircle,\n items: errors,\n tone: 'tpl:text-[var(--tpl-danger)]',\n },\n {\n key: 'warnings',\n title: t.issues.groupWarnings,\n icon: AlertTriangle,\n items: warnings,\n tone: 'tpl:text-[var(--tpl-warning)]',\n },\n {\n key: 'info',\n title: t.issues.groupInfo,\n icon: Info,\n items: infos,\n tone: 'tpl:text-[var(--tpl-text-muted)]',\n },\n ]\"\n :key=\"group.key\"\n >\n <header\n v-if=\"group.items.length > 0\"\n class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:font-semibold tpl:uppercase tpl:tracking-wide\"\n :class=\"group.tone\"\n >\n <component :is=\"group.icon\" :size=\"12\" :stroke-width=\"2\" />\n {{ group.title }}\n <span class=\"tpl:ml-auto tpl:font-normal tpl:opacity-70\">\n {{ group.items.length }}\n </span>\n </header>\n <ul\n v-if=\"group.items.length > 0\"\n class=\"tpl:m-0 tpl:mb-3 tpl:flex tpl:list-none tpl:flex-col tpl:gap-2 tpl:p-0\"\n >\n <li\n v-for=\"issue in group.items\"\n :key=\"`${issue.ruleId}-${issue.blockId ?? 'template'}`\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3\"\n >\n <p class=\"tpl:m-0 tpl:mb-2 tpl:text-xs tpl:text-[var(--tpl-text)]\">\n {{ issue.message }}\n </p>\n <p\n class=\"tpl:m-0 tpl:mb-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ issue.ruleId }}\n </p>\n <div class=\"tpl:flex tpl:gap-1.5\">\n <button\n v-if=\"issue.blockId\"\n type=\"button\"\n data-testid=\"issue-jump\"\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n @click=\"jumpTo(issue)\"\n >\n <ArrowRight :size=\"10\" :stroke-width=\"2\" />\n {{ t.issues.jump }}\n </button>\n <button\n v-if=\"issue.fix\"\n type=\"button\"\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-md tpl:bg-[var(--tpl-primary)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-on-primary)]\"\n :title=\"issue.fix.description\"\n @click=\"applyFix(issue)\"\n >\n <Wrench :size=\"10\" :stroke-width=\"2\" />\n {{ t.issues.fix }}\n </button>\n </div>\n </li>\n </ul>\n </section>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport {\n ListChecks,\n AlertCircle,\n AlertTriangle,\n Info,\n ArrowRight,\n Wrench,\n} from \"@lucide/vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { useScrollToBlock } from \"../../composables/useScrollToBlock\";\nimport { TEMPLATE_LINT_KEY, EDITOR_KEY } from \"../../keys\";\nimport type { LintIssue } from \"../../composables/useTemplateLint\";\n\nconst { t, format } = useI18n();\nconst lint = inject(TEMPLATE_LINT_KEY, null);\nconst editor = inject(EDITOR_KEY, null);\nconst scrollToBlock = useScrollToBlock();\n\nconst errors = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"error\"),\n);\nconst warnings = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"warning\"),\n);\nconst infos = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"info\"),\n);\n\nconst totalCount = computed(\n () => errors.value.length + warnings.value.length + infos.value.length,\n);\n\nfunction jumpTo(issue: LintIssue): void {\n if (!editor) return;\n if (!issue.blockId) return;\n editor.selectBlock(issue.blockId);\n // Selecting is invisible on its own — on a long template the block sits\n // below the fold and the canvas stays put, so \"Jump\" appears to do nothing.\n scrollToBlock(issue.blockId);\n}\n\nfunction applyFix(issue: LintIssue): void {\n lint?.applyFix(issue);\n}\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:flex-col tpl:gap-4 tpl:p-4\">\n <header class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <ListChecks :size=\"16\" :stroke-width=\"1.5\" />\n <h3\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.issues.panelTitle }}\n </h3>\n <span\n v-if=\"totalCount > 0\"\n class=\"tpl:ml-auto tpl:rounded-full tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-0.5 tpl:text-[10px] tpl:text-[var(--tpl-text-muted)]\"\n :title=\"format(t.issues.issueCountTooltip, { count: totalCount })\"\n >\n {{ totalCount }}\n </span>\n </header>\n\n <div\n v-if=\"!lint || lint.unavailable.value\"\n class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n <!-- Quality package not available; render nothing visible beyond a hint. -->\n </div>\n\n <div\n v-else-if=\"totalCount === 0\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.issues.emptyState }}\n </div>\n\n <template v-else>\n <section\n v-for=\"group in [\n {\n key: 'errors',\n title: t.issues.groupErrors,\n icon: AlertCircle,\n items: errors,\n tone: 'tpl:text-[var(--tpl-danger)]',\n },\n {\n key: 'warnings',\n title: t.issues.groupWarnings,\n icon: AlertTriangle,\n items: warnings,\n tone: 'tpl:text-[var(--tpl-warning)]',\n },\n {\n key: 'info',\n title: t.issues.groupInfo,\n icon: Info,\n items: infos,\n tone: 'tpl:text-[var(--tpl-text-muted)]',\n },\n ]\"\n :key=\"group.key\"\n >\n <header\n v-if=\"group.items.length > 0\"\n class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-xs tpl:font-semibold tpl:uppercase tpl:tracking-wide\"\n :class=\"group.tone\"\n >\n <component :is=\"group.icon\" :size=\"12\" :stroke-width=\"2\" />\n {{ group.title }}\n <span class=\"tpl:ml-auto tpl:font-normal tpl:opacity-70\">\n {{ group.items.length }}\n </span>\n </header>\n <ul\n v-if=\"group.items.length > 0\"\n class=\"tpl:m-0 tpl:mb-3 tpl:flex tpl:list-none tpl:flex-col tpl:gap-2 tpl:p-0\"\n >\n <li\n v-for=\"issue in group.items\"\n :key=\"`${issue.ruleId}-${issue.blockId ?? 'template'}`\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3\"\n >\n <p class=\"tpl:m-0 tpl:mb-2 tpl:text-xs tpl:text-[var(--tpl-text)]\">\n {{ issue.message }}\n </p>\n <p\n class=\"tpl:m-0 tpl:mb-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ issue.ruleId }}\n </p>\n <div class=\"tpl:flex tpl:gap-1.5\">\n <button\n v-if=\"issue.blockId\"\n type=\"button\"\n data-testid=\"issue-jump\"\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n @click=\"jumpTo(issue)\"\n >\n <ArrowRight :size=\"10\" :stroke-width=\"2\" />\n {{ t.issues.jump }}\n </button>\n <button\n v-if=\"issue.fix\"\n type=\"button\"\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-md tpl:bg-[var(--tpl-primary)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-on-primary)]\"\n :title=\"issue.fix.description\"\n @click=\"applyFix(issue)\"\n >\n <Wrench :size=\"10\" :stroke-width=\"2\" />\n {{ t.issues.fix }}\n </button>\n </div>\n </li>\n </ul>\n </section>\n </template>\n </div>\n</template>\n"],"mappings":"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"}
@@ -5,8 +5,8 @@ var r = {}, i = { class: "tpl-loading-track" };
5
5
  function a(n, r) {
6
6
  return e(), t("div", i);
7
7
  }
8
- var o = /*#__PURE__*/ n(r, [["render", a], ["__scopeId", "data-v-c3d50014"]]);
8
+ var o = /*#__PURE__*/ n(r, [["render", a], ["__scopeId", "data-v-e61f3b63"]]);
9
9
  //#endregion
10
10
  export { o as t };
11
11
 
12
- //# sourceMappingURL=LoadingTrack-Ba5xh5HD.js.map
12
+ //# sourceMappingURL=LoadingTrack-_7DH_5aw.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LoadingTrack-Ba5xh5HD.js","names":[],"sources":["../../../src/components/LoadingTrack.vue"],"sourcesContent":["<template>\n <div class=\"tpl-loading-track\"></div>\n</template>\n\n<style scoped>\n.tpl-loading-track {\n height: 3px;\n width: 100%;\n border-radius: 9999px;\n background: linear-gradient(\n 90deg,\n transparent 0%,\n color-mix(in srgb, var(--tpl-primary) 10%, transparent) 15%,\n color-mix(in srgb, var(--tpl-primary) 30%, transparent) 30%,\n var(--tpl-primary) 50%,\n color-mix(in srgb, var(--tpl-primary) 30%, transparent) 70%,\n color-mix(in srgb, var(--tpl-primary) 10%, transparent) 85%,\n transparent 100%\n );\n background-size: 200% 100%;\n animation: tpl-loading-flow 3s ease-in-out infinite;\n}\n\n@keyframes tpl-loading-flow {\n 0% {\n background-position: 100% 0;\n }\n 100% {\n background-position: -100% 0;\n }\n}\n/* The bar communicates \"working\" by its presence and its amber fill; the sweep\n is decoration, so reduced motion holds it still rather than removing it. */\n@media (prefers-reduced-motion: reduce) {\n .tpl-loading-track-fill {\n animation: none;\n background-position: 50% 0;\n }\n}\n</style>\n"],"mappings":";;;YACO,IAAA,EAAA,OAAM,oBAAmB;;CAA9B,OAAA,EAAA,GAAA,EAAqC,OAArC,CAAqC"}
1
+ {"version":3,"file":"LoadingTrack-_7DH_5aw.js","names":[],"sources":["../../../src/components/LoadingTrack.vue"],"sourcesContent":["<template>\n <div class=\"tpl-loading-track\"></div>\n</template>\n\n<style scoped>\n.tpl-loading-track {\n height: 3px;\n width: 100%;\n border-radius: 9999px;\n background: linear-gradient(\n 90deg,\n transparent 0%,\n color-mix(in srgb, var(--tpl-primary) 10%, transparent) 15%,\n color-mix(in srgb, var(--tpl-primary) 30%, transparent) 30%,\n var(--tpl-primary) 50%,\n color-mix(in srgb, var(--tpl-primary) 30%, transparent) 70%,\n color-mix(in srgb, var(--tpl-primary) 10%, transparent) 85%,\n transparent 100%\n );\n background-size: 200% 100%;\n animation: tpl-loading-flow 3s ease-in-out infinite;\n}\n\n@keyframes tpl-loading-flow {\n 0% {\n background-position: 100% 0;\n }\n 100% {\n background-position: -100% 0;\n }\n}\n/* The bar communicates \"working\" by its presence and its amber fill; the sweep\n is decoration, so reduced motion holds it still rather than removing it. */\n@media (prefers-reduced-motion: reduce) {\n .tpl-loading-track {\n animation: none;\n background-position: 50% 0;\n }\n}\n</style>\n"],"mappings":";;;YACO,IAAA,EAAA,OAAM,oBAAmB;;CAA9B,OAAA,EAAA,GAAA,EAAqC,OAArC,CAAqC"}
@@ -1,6 +1,6 @@
1
1
  import { B as e, E as t, F as n, H as r, I as i, L as a, N as o, O as s, Q as c, R as l, S as u, U as d, at as f, b as p, ct as m, d as h, f as g, g as _, h as v, l as y, lt as ee, m as b, n as te, o as ne, p as x, s as re, u as ie, ut as S, y as ae } from "./draggable-IB4x6w2O.js";
2
2
  import { t as oe } from "./_plugin-vue_export-helper-B3ysoDQm.js";
3
- import { a as se, n as ce, o as le, r as ue, t as de } from "./editor-modal-C-yBlj2C.js";
3
+ import { a as se, n as ce, o as le, r as ue, t as de } from "./editor-modal-zxvWz8R6.js";
4
4
  //#region ../media-library/src/composables/useImageCrop.ts
5
5
  var fe = {
6
6
  free: void 0,
@@ -3349,7 +3349,7 @@ var Vt = ["data-tpl-theme"], Ht = { class: "tpl:shrink-0 tpl:p-5 tpl:pb-4" }, Ut
3349
3349
  ].includes(s.item.mimeType ?? "") : !1), xe = g(() => E.value === "original" ? k.value : fe[E.value]), F = g(() => A.value ? ge(A.value.width, A.value.height, ye.value, be.value) : null);
3350
3350
  e(() => s.visible, (e) => {
3351
3351
  e && s.item && (w.value = s.item.filename ?? "", ve.value = s.item.alt ?? "", E.value = "free", ye.value = void 0, be.value = void 0, D.value = "", O.value = "", k.value = void 0, A.value = null, j.value = !1, N.value = !1, s.item.width && s.item.height && (k.value = s.item.width / s.item.height));
3352
- });
3352
+ }, { immediate: !0 });
3353
3353
  function I(e) {
3354
3354
  e.coordinates && (A.value = {
3355
3355
  width: Math.round(e.coordinates.width),
@@ -3522,8 +3522,8 @@ var Vt = ["data-tpl-theme"], Ht = { class: "tpl:shrink-0 tpl:p-5 tpl:pb-4" }, Ut
3522
3522
  _: 1
3523
3523
  })], 8, ["to"]));
3524
3524
  }
3525
- }), [["__scopeId", "data-v-6fc59551"]]);
3525
+ }), [["__scopeId", "data-v-51052195"]]);
3526
3526
  //#endregion
3527
3527
  export { xn as default };
3528
3528
 
3529
- //# sourceMappingURL=MediaEditModal-BBZRcYKe.js.map
3529
+ //# sourceMappingURL=MediaEditModal-CZBZviu2.js.map