@templatical/editor 0.29.1 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/dist/{AiChatSidebar-DsYJwCLT.js → AiChatSidebar-Bq99wU01.js} +1 -1
  2. package/dist/{AiFeatureMenu-CehKC-dh.js → AiFeatureMenu-F-C59LAV.js} +1 -1
  3. package/dist/{BlockPreviewCanvas-BQZ7aTPr.js → BlockPreviewCanvas-CqU1kwmG.js} +1 -1
  4. package/dist/{CloudHeaderExtras-CaRdlYpY.js → CloudHeaderExtras-vnGV_mLD.js} +1 -1
  5. package/dist/{CloudPanels-CYqHk1Az.js → CloudPanels-Dv-MD1Tc.js} +1 -1
  6. package/dist/{CollaboratorBar-BZi12Mut.js → CollaboratorBar-DmssyJoB.js} +1 -1
  7. package/dist/{CountdownToolbar-ByQVLqFF.js → CountdownToolbar-SwmE7CIk.js} +16 -16
  8. package/dist/{DesignReferenceSidebar-B9anB-Q0.js → DesignReferenceSidebar-9cLTzOqP.js} +1 -1
  9. package/dist/{NumberWithSuffix-C-k0BP1m.js → FieldRow-0-UbQR4a.js} +23 -16
  10. package/dist/{MergeTagModeToggle-sBEGugix.js → MergeTagModeToggle-DtD_Xl9w.js} +1 -1
  11. package/dist/{ParagraphEditor-gbp5gbFv.js → ParagraphEditor-C0NbqDmV.js} +2 -2
  12. package/dist/{RestoreVersionDialog-DIHpGfFr.js → RestoreVersionDialog-DCX0rJiV.js} +1 -1
  13. package/dist/{SaveBlockDialog-QSj7tf1p.js → SaveBlockDialog-e5LtQRVk.js} +3 -3
  14. package/dist/{SavedBlocksBrowserModal-Bz2xaBzJ.js → SavedBlocksBrowserModal-ZQIQoARO.js} +4 -4
  15. package/dist/{SavedBlocksPanels-RNgOnK9j.js → SavedBlocksPanels-pGZWjSmp.js} +1 -1
  16. package/dist/{SavedBlocksPickBar-CRmd3sD9.js → SavedBlocksPickBar-OZaOG1Si.js} +1 -1
  17. package/dist/{TestEmailModal-DclmELUc.js → TestEmailModal-PVa1VL2p.js} +5 -5
  18. package/dist/{TestEmailPanel-4i4n9Kup.js → TestEmailPanel-RekBdAtO.js} +1 -1
  19. package/dist/{TitleEditor-DMAeSWx3.js → TitleEditor-Cs2tELCL.js} +2 -2
  20. package/dist/{TplModal-CjuKT36J.js → TplModal-Bw1i8_eE.js} +1 -1
  21. package/dist/{VersionHistoryPanels-MqgexkhW.js → VersionHistoryPanels-TqTC4ofA.js} +1 -1
  22. package/dist/{blockTypeIcons-ejNais7n.js → blockTypeIcons-C7WedEOG.js} +1 -1
  23. package/dist/bundle-stats.json +6 -6
  24. package/dist/{cdn/chunks/ca-DY0AYOLG.js → ca-DZSrLNXK.js} +2 -2
  25. package/dist/cdn/chunks/{AiChatSidebar-BE97qqqb.js → AiChatSidebar-DmZ1Nvxo.js} +2 -2
  26. package/dist/cdn/chunks/{AiChatSidebar-BE97qqqb.js.map → AiChatSidebar-DmZ1Nvxo.js.map} +1 -1
  27. package/dist/cdn/chunks/{AiFeatureMenu-CNP54HPc.js → AiFeatureMenu-D4tax9j3.js} +2 -2
  28. package/dist/cdn/chunks/{AiFeatureMenu-CNP54HPc.js.map → AiFeatureMenu-D4tax9j3.js.map} +1 -1
  29. package/dist/cdn/chunks/{BlockPreviewCanvas-CUEZIwJ4.js → BlockPreviewCanvas-DDOqFJlT.js} +2 -2
  30. package/dist/cdn/chunks/{BlockPreviewCanvas-CUEZIwJ4.js.map → BlockPreviewCanvas-DDOqFJlT.js.map} +1 -1
  31. package/dist/cdn/chunks/{CloudHeaderExtras-Bz--_dKu.js → CloudHeaderExtras-g-_SFPna.js} +2 -2
  32. package/dist/cdn/chunks/{CloudHeaderExtras-Bz--_dKu.js.map → CloudHeaderExtras-g-_SFPna.js.map} +1 -1
  33. package/dist/cdn/chunks/{CloudPanels-BXcdYgy0.js → CloudPanels-Cnh07_2G.js} +3 -3
  34. package/dist/cdn/chunks/{CloudPanels-BXcdYgy0.js.map → CloudPanels-Cnh07_2G.js.map} +1 -1
  35. package/dist/cdn/chunks/{CollaboratorBar-CvvoIrKS.js → CollaboratorBar-CLGB0prG.js} +2 -2
  36. package/dist/cdn/chunks/{CollaboratorBar-CvvoIrKS.js.map → CollaboratorBar-CLGB0prG.js.map} +1 -1
  37. package/dist/cdn/chunks/{CountdownToolbar-BD90NpY1.js → CountdownToolbar-CO6xLgYc.js} +17 -17
  38. package/dist/cdn/chunks/{CountdownToolbar-BD90NpY1.js.map → CountdownToolbar-CO6xLgYc.js.map} +1 -1
  39. package/dist/cdn/chunks/{DesignReferenceSidebar-DY-laxO5.js → DesignReferenceSidebar-DRGqm33h.js} +2 -2
  40. package/dist/cdn/chunks/{DesignReferenceSidebar-DY-laxO5.js.map → DesignReferenceSidebar-DRGqm33h.js.map} +1 -1
  41. package/dist/cdn/chunks/{NumberWithSuffix-DOG0zHvY.js → FieldRow-CbcpsxZ4.js} +24 -17
  42. package/dist/cdn/chunks/FieldRow-CbcpsxZ4.js.map +1 -0
  43. package/dist/cdn/chunks/{MergeTagModeToggle-BRW3qErZ.js → MergeTagModeToggle-DZtnCj0F.js} +2 -2
  44. package/dist/cdn/chunks/{MergeTagModeToggle-BRW3qErZ.js.map → MergeTagModeToggle-DZtnCj0F.js.map} +1 -1
  45. package/dist/cdn/chunks/{ParagraphEditor-CPVnO48U.js → ParagraphEditor-BalgLjBt.js} +3 -3
  46. package/dist/cdn/chunks/{ParagraphEditor-CPVnO48U.js.map → ParagraphEditor-BalgLjBt.js.map} +1 -1
  47. package/dist/cdn/chunks/{RestoreVersionDialog--Blx-VKu.js → RestoreVersionDialog-DGOXQ0ou.js} +2 -2
  48. package/dist/cdn/chunks/{RestoreVersionDialog--Blx-VKu.js.map → RestoreVersionDialog-DGOXQ0ou.js.map} +1 -1
  49. package/dist/cdn/chunks/{SaveBlockDialog-DXQ2g_38.js → SaveBlockDialog-Dod3jX9X.js} +4 -4
  50. package/dist/cdn/chunks/{SaveBlockDialog-DXQ2g_38.js.map → SaveBlockDialog-Dod3jX9X.js.map} +1 -1
  51. package/dist/cdn/chunks/{SavedBlocksBrowserModal-DWBvVkuk.js → SavedBlocksBrowserModal-BysGB8zb.js} +4 -4
  52. package/dist/cdn/chunks/{SavedBlocksBrowserModal-DWBvVkuk.js.map → SavedBlocksBrowserModal-BysGB8zb.js.map} +1 -1
  53. package/dist/cdn/chunks/{SavedBlocksPanels-GgdpiPUq.js → SavedBlocksPanels-DJ4fceEM.js} +2 -2
  54. package/dist/cdn/chunks/{SavedBlocksPanels-GgdpiPUq.js.map → SavedBlocksPanels-DJ4fceEM.js.map} +1 -1
  55. package/dist/cdn/chunks/{SavedBlocksPickBar-Dyb5xi1B.js → SavedBlocksPickBar-DGgbqAsb.js} +2 -2
  56. package/dist/cdn/chunks/{SavedBlocksPickBar-Dyb5xi1B.js.map → SavedBlocksPickBar-DGgbqAsb.js.map} +1 -1
  57. package/dist/cdn/chunks/{TestEmailModal-B9SmAIKZ.js → TestEmailModal-DvZFnhWa.js} +6 -6
  58. package/dist/cdn/chunks/{TestEmailModal-B9SmAIKZ.js.map → TestEmailModal-DvZFnhWa.js.map} +1 -1
  59. package/dist/cdn/chunks/{TestEmailPanel-Q6alElB1.js → TestEmailPanel-BdxpMiWc.js} +2 -2
  60. package/dist/cdn/chunks/{TestEmailPanel-Q6alElB1.js.map → TestEmailPanel-BdxpMiWc.js.map} +1 -1
  61. package/dist/cdn/chunks/{TitleEditor-Tudoac5f.js → TitleEditor-CfHWnThg.js} +3 -3
  62. package/dist/cdn/chunks/{TitleEditor-Tudoac5f.js.map → TitleEditor-CfHWnThg.js.map} +1 -1
  63. package/dist/cdn/chunks/{TplModal-BNJIFnyj.js → TplModal-BMTW5bEN.js} +2 -2
  64. package/dist/cdn/chunks/{TplModal-BNJIFnyj.js.map → TplModal-BMTW5bEN.js.map} +1 -1
  65. package/dist/cdn/chunks/{VersionHistoryPanels-Crw5JA89.js → VersionHistoryPanels-2l_FLtix.js} +2 -2
  66. package/dist/cdn/chunks/{VersionHistoryPanels-Crw5JA89.js.map → VersionHistoryPanels-2l_FLtix.js.map} +1 -1
  67. package/dist/{ca-DY0AYOLG.js → cdn/chunks/ca-DZSrLNXK.js} +4 -0
  68. package/dist/cdn/chunks/ca-DZSrLNXK.js.map +1 -0
  69. package/dist/cdn/chunks/{cloud-C29LPXYn.js → cloud-CITqEHEc.js} +2 -2
  70. package/dist/cdn/chunks/{cloud-C29LPXYn.js.map → cloud-CITqEHEc.js.map} +1 -1
  71. package/dist/cdn/chunks/{createCloudRuntime-C6MJN7M6.js → createCloudRuntime-jLhuyKds.js} +4 -4
  72. package/dist/cdn/chunks/{createCloudRuntime-C6MJN7M6.js.map → createCloudRuntime-jLhuyKds.js.map} +1 -1
  73. package/dist/cdn/chunks/{de-CYLWugqb.js → de-DbayqpK-.js} +3 -1
  74. package/dist/cdn/chunks/{de-CYLWugqb.js.map → de-DbayqpK-.js.map} +1 -1
  75. package/dist/cdn/chunks/en-CwXyUb6-.js +2 -0
  76. package/dist/cdn/chunks/{en-C3TM249M.js → en-l0ib5X0f.js} +3 -1
  77. package/dist/cdn/chunks/en-l0ib5X0f.js.map +1 -0
  78. package/dist/{es-D1SF4nCR.js → cdn/chunks/es-CMQ-5PJP.js} +4 -0
  79. package/dist/cdn/chunks/es-CMQ-5PJP.js.map +1 -0
  80. package/dist/cdn/chunks/{extensions-BQefRevf.js → extensions-D30V0hzj.js} +3 -3
  81. package/dist/cdn/chunks/{extensions-BQefRevf.js.map → extensions-D30V0hzj.js.map} +1 -1
  82. package/dist/cdn/chunks/{fr-CdLhJ7kw.js → fr-DNXmZWAq.js} +3 -1
  83. package/dist/cdn/chunks/fr-DNXmZWAq.js.map +1 -0
  84. package/dist/{nl-BvsLoyKp.js → cdn/chunks/nl-BylRRHYJ.js} +4 -0
  85. package/dist/cdn/chunks/nl-BylRRHYJ.js.map +1 -0
  86. package/dist/cdn/chunks/{preRenderCustomBlocks-CbA3RcAy.js → preRenderCustomBlocks-DyCIgmqJ.js} +2 -2
  87. package/dist/cdn/chunks/{preRenderCustomBlocks-CbA3RcAy.js.map → preRenderCustomBlocks-DyCIgmqJ.js.map} +1 -1
  88. package/dist/cdn/chunks/{pt-BR-CAGS5YXp.js → pt-BR-BVoJhpie.js} +3 -1
  89. package/dist/cdn/chunks/pt-BR-BVoJhpie.js.map +1 -0
  90. package/dist/cdn/chunks/{src-fxaI2EQc.js → src-65H3RNcb.js} +3 -3
  91. package/dist/cdn/chunks/{src-fxaI2EQc.js.map → src-65H3RNcb.js.map} +1 -1
  92. package/dist/cdn/chunks/{src-Cx70q30o.js → src-6J-yyZoD.js} +2 -2
  93. package/dist/cdn/chunks/{src-Cx70q30o.js.map → src-6J-yyZoD.js.map} +1 -1
  94. package/dist/cdn/chunks/{src-B_QJduQD.js → src-BD60gqPW.js} +72 -67
  95. package/dist/cdn/chunks/src-BD60gqPW.js.map +1 -0
  96. package/dist/cdn/chunks/{src-CjSrA_o4.js → src-CLTXM4PM.js} +166 -136
  97. package/dist/cdn/chunks/src-CLTXM4PM.js.map +1 -0
  98. package/dist/cdn/chunks/{useEditorCore-o2emEjgF.js → useEditorCore-BQeEwEQ9.js} +351 -356
  99. package/dist/cdn/chunks/{useEditorCore-o2emEjgF.js.map → useEditorCore-BQeEwEQ9.js.map} +1 -1
  100. package/dist/cdn/chunks/{useMergeTag-BWt1DQS1.js → useMergeTag-lWo7So18.js} +2 -2
  101. package/dist/cdn/chunks/{useMergeTag-BWt1DQS1.js.map → useMergeTag-lWo7So18.js.map} +1 -1
  102. package/dist/cdn/editor.css +1 -1
  103. package/dist/cdn/editor.js +941 -909
  104. package/dist/cdn/editor.js.map +1 -1
  105. package/dist/{cloud-CHHQWdaR.js → cloud-DwJ8ebNl.js} +1 -1
  106. package/dist/{createCloudRuntime-WwKt9mgb.js → createCloudRuntime-CdXQdl34.js} +2 -2
  107. package/dist/{de-CYLWugqb.js → de-DbayqpK-.js} +2 -0
  108. package/dist/{dist-B1kLn1rs.js → dist-CzVIVbSa.js} +90 -85
  109. package/dist/{en-C3TM249M.js → en-l0ib5X0f.js} +2 -0
  110. package/dist/{cdn/chunks/es-D1SF4nCR.js → es-CMQ-5PJP.js} +2 -2
  111. package/dist/{extensions-B4gwZ36u.js → extensions-Bjsdx6Vm.js} +12 -12
  112. package/dist/{fr-CdLhJ7kw.js → fr-DNXmZWAq.js} +2 -0
  113. package/dist/{cdn/chunks/nl-BvsLoyKp.js → nl-BylRRHYJ.js} +2 -2
  114. package/dist/{preRenderCustomBlocks-BebjKtmw.js → preRenderCustomBlocks-2KyBJocY.js} +1 -1
  115. package/dist/{pt-BR-CAGS5YXp.js → pt-BR-BVoJhpie.js} +2 -0
  116. package/dist/style.css +1 -1
  117. package/dist/templatical-editor.js +983 -951
  118. package/dist/{useEditorCore-CJp84vJY.js → useEditorCore-Du9Vx-qr.js} +868 -873
  119. package/dist/{useMergeTag-D1Rsn4BO.js → useMergeTag-Cx36rtrt.js} +3 -3
  120. package/package.json +7 -7
  121. package/dist/cdn/chunks/NumberWithSuffix-DOG0zHvY.js.map +0 -1
  122. package/dist/cdn/chunks/ca-DY0AYOLG.js.map +0 -1
  123. package/dist/cdn/chunks/en-C3TM249M.js.map +0 -1
  124. package/dist/cdn/chunks/en-DIwntrh3.js +0 -2
  125. package/dist/cdn/chunks/es-D1SF4nCR.js.map +0 -1
  126. package/dist/cdn/chunks/fr-CdLhJ7kw.js.map +0 -1
  127. package/dist/cdn/chunks/nl-BvsLoyKp.js.map +0 -1
  128. package/dist/cdn/chunks/pt-BR-CAGS5YXp.js.map +0 -1
  129. package/dist/cdn/chunks/src-B_QJduQD.js.map +0 -1
  130. package/dist/cdn/chunks/src-CjSrA_o4.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"CloudPanels-BXcdYgy0.js","names":[],"sources":["../../../src/cloud/components/CloudErrorOverlay.vue","../../../src/cloud/components/CloudErrorOverlay.vue","../../../src/cloud/components/CloudLoadingOverlay.vue","../../../src/cloud/components/CloudLoadingOverlay.vue","../../../src/cloud/components/CloudSaveGateModal.vue","../../../src/cloud/components/CloudSaveGateModal.vue","../../../src/cloud/components/CollabUndoToast.vue","../../../src/cloud/components/CollabUndoToast.vue","../../../src/cloud/components/CloudPanels.vue","../../../src/cloud/components/CloudPanels.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { CircleAlert } from \"@lucide/vue\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\n\ndefineProps<{\n error: Error | null;\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"retry\"): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n\nfunction getErrorMessage(error: Error): string {\n if (\n \"isUnauthorized\" in error &&\n (error as { isUnauthorized: boolean }).isUnauthorized\n ) {\n return cloudT.error.authFailed;\n }\n if (\"isNotFound\" in error && (error as { isNotFound: boolean }).isNotFound) {\n return cloudT.error.templateNotFound;\n }\n return cloudT.error.defaultMessage;\n}\n\nfunction isNotFoundError(error: Error): boolean {\n return (\n \"isNotFound\" in error && !!(error as { isNotFound: boolean }).isNotFound\n );\n}\n</script>\n\n<template>\n <div\n v-if=\"visible && error\"\n role=\"alert\"\n class=\"tpl-error tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-6 tpl:px-8 tpl:bg-[var(--tpl-bg)]\"\n >\n <div\n class=\"tpl:flex tpl:size-16 tpl:items-center tpl:justify-center tpl:rounded-full tpl:bg-[var(--tpl-danger-light)]\"\n >\n <CircleAlert\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-danger)]\"\n />\n </div>\n <div\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:text-center\"\n >\n <h2 class=\"tpl:text-lg tpl:font-semibold tpl:text-[var(--tpl-text)]\">\n {{ cloudT.error.title }}\n </h2>\n <p class=\"tpl:max-w-md tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ getErrorMessage(error) }}\n </p>\n </div>\n <button\n v-if=\"!isNotFoundError(error)\"\n class=\"tpl-btn tpl-btn-primary tpl:inline-flex tpl:items-center tpl:gap-2 tpl:rounded-md tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:shadow-[var(--tpl-shadow-sm)] tpl:transition-all tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-on-primary)]\"\n @click=\"emit('retry')\"\n >\n {{ cloudT.error.retry }}\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { CircleAlert } from \"@lucide/vue\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\n\ndefineProps<{\n error: Error | null;\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"retry\"): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n\nfunction getErrorMessage(error: Error): string {\n if (\n \"isUnauthorized\" in error &&\n (error as { isUnauthorized: boolean }).isUnauthorized\n ) {\n return cloudT.error.authFailed;\n }\n if (\"isNotFound\" in error && (error as { isNotFound: boolean }).isNotFound) {\n return cloudT.error.templateNotFound;\n }\n return cloudT.error.defaultMessage;\n}\n\nfunction isNotFoundError(error: Error): boolean {\n return (\n \"isNotFound\" in error && !!(error as { isNotFound: boolean }).isNotFound\n );\n}\n</script>\n\n<template>\n <div\n v-if=\"visible && error\"\n role=\"alert\"\n class=\"tpl-error tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-6 tpl:px-8 tpl:bg-[var(--tpl-bg)]\"\n >\n <div\n class=\"tpl:flex tpl:size-16 tpl:items-center tpl:justify-center tpl:rounded-full tpl:bg-[var(--tpl-danger-light)]\"\n >\n <CircleAlert\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-danger)]\"\n />\n </div>\n <div\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:text-center\"\n >\n <h2 class=\"tpl:text-lg tpl:font-semibold tpl:text-[var(--tpl-text)]\">\n {{ cloudT.error.title }}\n </h2>\n <p class=\"tpl:max-w-md tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ getErrorMessage(error) }}\n </p>\n </div>\n <button\n v-if=\"!isNotFoundError(error)\"\n class=\"tpl-btn tpl-btn-primary tpl:inline-flex tpl:items-center tpl:gap-2 tpl:rounded-md tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:shadow-[var(--tpl-shadow-sm)] tpl:transition-all tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-on-primary)]\"\n @click=\"emit('retry')\"\n >\n {{ cloudT.error.retry }}\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\ndefineProps<{\n visible: boolean;\n}>();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n class=\"tpl-loading tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:bg-[var(--tpl-bg)]\"\n >\n <!-- Skeleton header -->\n <div\n class=\"tpl:flex tpl:h-14 tpl:shrink-0 tpl:items-center tpl:justify-between tpl:px-4 tpl:border-b tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl-shimmer tpl:h-5 tpl:w-28 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div class=\"tpl:flex tpl:gap-3\">\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n </div>\n <!-- Skeleton body -->\n <div class=\"tpl:flex tpl:flex-1 tpl:overflow-hidden\">\n <!-- Left sidebar rail -->\n <div\n class=\"tpl:flex tpl:w-12 tpl:shrink-0 tpl:flex-col tpl:items-center tpl:gap-4 tpl:py-5 tpl:border-r tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-for=\"n in 5\"\n :key=\"n\"\n class=\"tpl-shimmer tpl:size-7 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <!-- Canvas area -->\n <div\n class=\"tpl:flex tpl:flex-1 tpl:items-start tpl:justify-center tpl:overflow-auto tpl:p-8 tpl:bg-[var(--tpl-canvas-bg)]\"\n >\n <div\n class=\"tpl:w-full tpl:max-w-[600px] tpl:rounded-[var(--tpl-radius)] tpl:p-6 tpl:bg-[var(--tpl-bg)] tpl:shadow-[var(--tpl-shadow-sm)]\"\n >\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-3/4 tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-5/6 tpl:rounded\"></div>\n </div>\n <div class=\"tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:h-44 tpl:w-full tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-2/3 tpl:rounded\"></div>\n </div>\n <div class=\"tpl:flex tpl:justify-center tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:h-10 tpl:w-36 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/2 tpl:rounded\"\n ></div>\n <div\n class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/3 tpl:rounded\"\n ></div>\n </div>\n </div>\n </div>\n <!-- Right panel -->\n <div\n class=\"tpl:flex tpl:w-[320px] tpl:shrink-0 tpl:flex-col tpl:gap-4 tpl:p-4 tpl:border-l tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div>\n <div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\ndefineProps<{\n visible: boolean;\n}>();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n class=\"tpl-loading tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:bg-[var(--tpl-bg)]\"\n >\n <!-- Skeleton header -->\n <div\n class=\"tpl:flex tpl:h-14 tpl:shrink-0 tpl:items-center tpl:justify-between tpl:px-4 tpl:border-b tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl-shimmer tpl:h-5 tpl:w-28 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div class=\"tpl:flex tpl:gap-3\">\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n </div>\n <!-- Skeleton body -->\n <div class=\"tpl:flex tpl:flex-1 tpl:overflow-hidden\">\n <!-- Left sidebar rail -->\n <div\n class=\"tpl:flex tpl:w-12 tpl:shrink-0 tpl:flex-col tpl:items-center tpl:gap-4 tpl:py-5 tpl:border-r tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-for=\"n in 5\"\n :key=\"n\"\n class=\"tpl-shimmer tpl:size-7 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <!-- Canvas area -->\n <div\n class=\"tpl:flex tpl:flex-1 tpl:items-start tpl:justify-center tpl:overflow-auto tpl:p-8 tpl:bg-[var(--tpl-canvas-bg)]\"\n >\n <div\n class=\"tpl:w-full tpl:max-w-[600px] tpl:rounded-[var(--tpl-radius)] tpl:p-6 tpl:bg-[var(--tpl-bg)] tpl:shadow-[var(--tpl-shadow-sm)]\"\n >\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-3/4 tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-5/6 tpl:rounded\"></div>\n </div>\n <div class=\"tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:h-44 tpl:w-full tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-2/3 tpl:rounded\"></div>\n </div>\n <div class=\"tpl:flex tpl:justify-center tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:h-10 tpl:w-36 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/2 tpl:rounded\"\n ></div>\n <div\n class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/3 tpl:rounded\"\n ></div>\n </div>\n </div>\n </div>\n <!-- Right panel -->\n <div\n class=\"tpl:flex tpl:w-[320px] tpl:shrink-0 tpl:flex-col tpl:gap-4 tpl:p-4 tpl:border-l tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div>\n <div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { AlertTriangle } from \"@lucide/vue\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\nimport type { LintIssue } from \"../../composables/useTemplateLint\";\n\ndefineProps<{\n open: boolean;\n issues: LintIssue[];\n}>();\n\nconst emit = defineEmits<{\n (e: \"cancel\"): void;\n (e: \"confirm\"): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n</script>\n\n<template>\n <Transition\n enter-active-class=\"tpl:transition-opacity\"\n leave-active-class=\"tpl:transition-opacity\"\n enter-from-class=\"tpl:opacity-0\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <div\n v-if=\"open\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"cloudT.saveGate.title\"\n class=\"tpl:fixed tpl:inset-0 tpl:z-50 tpl:flex tpl:items-center tpl:justify-center tpl:bg-black/40 tpl:p-6\"\n @click.self=\"emit('cancel')\"\n >\n <div\n class=\"tpl:flex tpl:max-h-[80%] tpl:w-full tpl:max-w-md tpl:flex-col tpl:gap-4 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-5 tpl:shadow-[var(--tpl-shadow-md)]\"\n >\n <header class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <AlertTriangle\n :size=\"18\"\n :stroke-width=\"2\"\n class=\"tpl:text-[var(--tpl-warning)]\"\n />\n <h2\n class=\"tpl:m-0 tpl:text-base tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.saveGate.title }}\n </h2>\n </header>\n\n <p class=\"tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ cloudT.saveGate.body }}\n </p>\n\n <ul\n class=\"tpl:m-0 tpl:flex tpl:max-h-64 tpl:list-none tpl:flex-col tpl:gap-1.5 tpl:overflow-y-auto tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-2\"\n >\n <li\n v-for=\"issue in issues\"\n :key=\"`${issue.ruleId}-${issue.blockId ?? 'template'}`\"\n class=\"tpl:flex tpl:flex-col tpl:gap-0.5 tpl:rounded tpl:px-2 tpl:py-1.5\"\n >\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\">\n {{ issue.message }}\n </span>\n <span\n class=\"tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ issue.ruleId }}\n </span>\n </li>\n </ul>\n\n <footer class=\"tpl:flex tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n @click=\"emit('cancel')\"\n >\n {{ cloudT.saveGate.cancel }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:bg-[var(--tpl-danger)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-bg)]\"\n @click=\"emit('confirm')\"\n >\n {{ cloudT.saveGate.confirm }}\n </button>\n </footer>\n </div>\n </div>\n </Transition>\n</template>\n","<script setup lang=\"ts\">\nimport { AlertTriangle } from \"@lucide/vue\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\nimport type { LintIssue } from \"../../composables/useTemplateLint\";\n\ndefineProps<{\n open: boolean;\n issues: LintIssue[];\n}>();\n\nconst emit = defineEmits<{\n (e: \"cancel\"): void;\n (e: \"confirm\"): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n</script>\n\n<template>\n <Transition\n enter-active-class=\"tpl:transition-opacity\"\n leave-active-class=\"tpl:transition-opacity\"\n enter-from-class=\"tpl:opacity-0\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <div\n v-if=\"open\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"cloudT.saveGate.title\"\n class=\"tpl:fixed tpl:inset-0 tpl:z-50 tpl:flex tpl:items-center tpl:justify-center tpl:bg-black/40 tpl:p-6\"\n @click.self=\"emit('cancel')\"\n >\n <div\n class=\"tpl:flex tpl:max-h-[80%] tpl:w-full tpl:max-w-md tpl:flex-col tpl:gap-4 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-5 tpl:shadow-[var(--tpl-shadow-md)]\"\n >\n <header class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <AlertTriangle\n :size=\"18\"\n :stroke-width=\"2\"\n class=\"tpl:text-[var(--tpl-warning)]\"\n />\n <h2\n class=\"tpl:m-0 tpl:text-base tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.saveGate.title }}\n </h2>\n </header>\n\n <p class=\"tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ cloudT.saveGate.body }}\n </p>\n\n <ul\n class=\"tpl:m-0 tpl:flex tpl:max-h-64 tpl:list-none tpl:flex-col tpl:gap-1.5 tpl:overflow-y-auto tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-2\"\n >\n <li\n v-for=\"issue in issues\"\n :key=\"`${issue.ruleId}-${issue.blockId ?? 'template'}`\"\n class=\"tpl:flex tpl:flex-col tpl:gap-0.5 tpl:rounded tpl:px-2 tpl:py-1.5\"\n >\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\">\n {{ issue.message }}\n </span>\n <span\n class=\"tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ issue.ruleId }}\n </span>\n </li>\n </ul>\n\n <footer class=\"tpl:flex tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n @click=\"emit('cancel')\"\n >\n {{ cloudT.saveGate.cancel }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:bg-[var(--tpl-danger)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-bg)]\"\n @click=\"emit('confirm')\"\n >\n {{ cloudT.saveGate.confirm }}\n </button>\n </footer>\n </div>\n </div>\n </Transition>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\n\ndefineProps<{\n visible: boolean;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n role=\"status\"\n aria-live=\"polite\"\n class=\"tpl:absolute tpl:top-16 tpl:left-1/2 tpl:z-toast tpl:-translate-x-1/2 tpl:rounded-[var(--tpl-radius)] tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:shadow-[var(--tpl-shadow-lg)]\"\n style=\"\n background-color: var(--tpl-warning-light);\n color: var(--tpl-text);\n border: 1px solid var(--tpl-warning);\n \"\n >\n {{ t.history.collabWarning }}\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\n\ndefineProps<{\n visible: boolean;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n role=\"status\"\n aria-live=\"polite\"\n class=\"tpl:absolute tpl:top-16 tpl:left-1/2 tpl:z-toast tpl:-translate-x-1/2 tpl:rounded-[var(--tpl-radius)] tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:shadow-[var(--tpl-shadow-lg)]\"\n style=\"\n background-color: var(--tpl-warning-light);\n color: var(--tpl-text);\n border: 1px solid var(--tpl-warning);\n \"\n >\n {{ t.history.collabWarning }}\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { defineAsyncComponent } from \"vue\";\nimport type { TemplateContent } from \"@templatical/types\";\nimport type { UseEditorReturn } from \"@templatical/core\";\n\nimport type { UseEditorCoreReturn } from \"../../composables/useEditorCore\";\nimport type { CloudAttachment, CloudReady, CloudRuntime } from \"../runtime\";\n\nimport CloudErrorOverlay from \"./CloudErrorOverlay.vue\";\nimport CloudLoadingOverlay from \"./CloudLoadingOverlay.vue\";\nimport CloudSaveGateModal from \"./CloudSaveGateModal.vue\";\nimport CollabUndoToast from \"./CollabUndoToast.vue\";\n\nconst AiChatSidebar = defineAsyncComponent(() => import(\"./AiChatSidebar.vue\"));\nconst DesignReferenceSidebar = defineAsyncComponent(\n () => import(\"./DesignReferenceSidebar.vue\"),\n);\nconst TemplateScoringPanel = defineAsyncComponent(\n () => import(\"./TemplateScoringPanel.vue\"),\n);\nconst MediaLibraryModal = defineAsyncComponent(async () => {\n // try/catch downgrades Webpack's \"Module not found\" from error to warning\n // when the optional peer isn't installed. Cloud consumers always install it.\n try {\n const m = await import(\"@templatical/media-library\");\n return m.MediaLibraryModal;\n } catch {\n throw new Error(\n \"[Templatical] Cloud media library requires the optional peer dependency '@templatical/media-library'. Please install it.\",\n );\n }\n});\n\n/**\n * Every piece of Cloud chrome that renders outside the header.\n *\n * `Editor.vue` mounts this behind one lazy `defineAsyncComponent` gated on a\n * cloud runtime being present, so an OSS consumer downloads none of it — the\n * `SavedBlocksPanels` / `TestEmailPanel` pattern.\n *\n * The overlays live here rather than in the shared editor deliberately: `init()`\n * cannot fail after it mounts, and OSS should not grow the ability.\n *\n * The comments sidebar is deliberately not here: it is shared, as `CommentsPanel`\n * in `Editor.vue` over a `CommentsProvider`. Its filter target lives on the shared\n * feature rather than being relayed through this component, so it survives the\n * lazy panel's mount without a runtime hop.\n */\nconst props = defineProps<{\n editor: UseEditorReturn;\n core: UseEditorCoreReturn;\n runtime: CloudRuntime;\n cloud: CloudAttachment;\n ready: CloudReady;\n /**\n * The editor's `init({ locale })`, forwarded to `MediaLibraryModal` so\n * `@templatical/media-library` loads its own strings in the same language.\n * A locale rather than translations: that package owns its copy, and the\n * editor's own chunk carries none of it.\n */\n locale?: string;\n}>();\n\nfunction applyContent(content: TemplateContent): void {\n props.core.history.record();\n props.editor.setContent(content);\n props.core.conditionPreview.reset();\n}\n</script>\n\n<template>\n <!-- A template create / load in flight. Cloud reads its content from the\n server, so there is a real gap where the canvas shows the wrong thing. -->\n <Transition\n enter-active-class=\"tpl:transition-opacity tpl:duration-200\"\n enter-from-class=\"tpl:opacity-100\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition-opacity tpl:duration-300\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <CloudLoadingOverlay :visible=\"editor.state.isLoading\" />\n </Transition>\n\n <!-- Session failure — an auth refresh that could not renew the token. The\n *bootstrap* has no overlay any more: it runs before the mount, so a\n failure there rejects `initCloud()` instead of mounting a dead editor. -->\n <Transition\n enter-active-class=\"tpl:transition-opacity tpl:duration-200\"\n enter-from-class=\"tpl:opacity-0\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition-opacity tpl:duration-300\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <CloudErrorOverlay\n :error=\"ready.sessionError.value\"\n :visible=\"!!ready.sessionError.value\"\n @retry=\"ready.retry\"\n />\n </Transition>\n\n <CloudSaveGateModal\n :open=\"ready.saveGate.modalOpen.value\"\n :issues=\"ready.saveGate.blockingIssues.value\"\n @cancel=\"ready.saveGate.cancel\"\n @confirm=\"ready.saveGate.confirmAndSave\"\n />\n\n <Transition\n enter-active-class=\"tpl:transition-all tpl:duration-200 tpl:ease-out\"\n enter-from-class=\"tpl:translate-y-[-8px] tpl:opacity-0\"\n enter-to-class=\"tpl:translate-y-0 tpl:opacity-100\"\n leave-active-class=\"tpl:transition-all tpl:duration-300 tpl:ease-in\"\n leave-from-class=\"tpl:translate-y-0 tpl:opacity-100\"\n leave-to-class=\"tpl:translate-y-[-8px] tpl:opacity-0\"\n >\n <CollabUndoToast\n :visible=\"ready.collabWarning.collabUndoWarningVisible.value\"\n />\n </Transition>\n\n <AiChatSidebar\n :visible=\"cloud.panelState.aiChatOpen.value\"\n :on-apply=\"applyContent\"\n @close=\"cloud.panelState.aiChatOpen.value = false\"\n />\n\n <TemplateScoringPanel\n :visible=\"cloud.panelState.scoringPanelOpen.value\"\n @close=\"cloud.panelState.scoringPanelOpen.value = false\"\n />\n\n <DesignReferenceSidebar\n :visible=\"cloud.panelState.designReferenceOpen.value\"\n :has-existing-blocks=\"editor.content.value.blocks.length > 0\"\n @close=\"cloud.panelState.designReferenceOpen.value = false\"\n @apply=\"applyContent\"\n />\n\n <!-- `auth-manager` / `project-id` / `plan-config` are props, not injections.\n An injection would have to match the modal's key identity; a mismatch\n arrives as `undefined` with no error and the browser opens inert. -->\n <MediaLibraryModal\n :visible=\"cloud.panelState.mediaLibraryOpen.value\"\n :accept=\"cloud.panelState.mediaLibraryAccept.value\"\n :popover-target=\"core.popoverRoot.value\"\n :locale=\"locale\"\n :ui-theme=\"core.resolvedTheme.value\"\n :auth-manager=\"cloud.mediaBrowser.authManager\"\n :project-id=\"cloud.mediaBrowser.projectId\"\n :plan-config=\"cloud.mediaBrowser.planConfig\"\n @select=\"cloud.mediaLib.handleMediaSelect\"\n @close=\"cloud.mediaLib.handleMediaLibraryClose\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { defineAsyncComponent } from \"vue\";\nimport type { TemplateContent } from \"@templatical/types\";\nimport type { UseEditorReturn } from \"@templatical/core\";\n\nimport type { UseEditorCoreReturn } from \"../../composables/useEditorCore\";\nimport type { CloudAttachment, CloudReady, CloudRuntime } from \"../runtime\";\n\nimport CloudErrorOverlay from \"./CloudErrorOverlay.vue\";\nimport CloudLoadingOverlay from \"./CloudLoadingOverlay.vue\";\nimport CloudSaveGateModal from \"./CloudSaveGateModal.vue\";\nimport CollabUndoToast from \"./CollabUndoToast.vue\";\n\nconst AiChatSidebar = defineAsyncComponent(() => import(\"./AiChatSidebar.vue\"));\nconst DesignReferenceSidebar = defineAsyncComponent(\n () => import(\"./DesignReferenceSidebar.vue\"),\n);\nconst TemplateScoringPanel = defineAsyncComponent(\n () => import(\"./TemplateScoringPanel.vue\"),\n);\nconst MediaLibraryModal = defineAsyncComponent(async () => {\n // try/catch downgrades Webpack's \"Module not found\" from error to warning\n // when the optional peer isn't installed. Cloud consumers always install it.\n try {\n const m = await import(\"@templatical/media-library\");\n return m.MediaLibraryModal;\n } catch {\n throw new Error(\n \"[Templatical] Cloud media library requires the optional peer dependency '@templatical/media-library'. Please install it.\",\n );\n }\n});\n\n/**\n * Every piece of Cloud chrome that renders outside the header.\n *\n * `Editor.vue` mounts this behind one lazy `defineAsyncComponent` gated on a\n * cloud runtime being present, so an OSS consumer downloads none of it — the\n * `SavedBlocksPanels` / `TestEmailPanel` pattern.\n *\n * The overlays live here rather than in the shared editor deliberately: `init()`\n * cannot fail after it mounts, and OSS should not grow the ability.\n *\n * The comments sidebar is deliberately not here: it is shared, as `CommentsPanel`\n * in `Editor.vue` over a `CommentsProvider`. Its filter target lives on the shared\n * feature rather than being relayed through this component, so it survives the\n * lazy panel's mount without a runtime hop.\n */\nconst props = defineProps<{\n editor: UseEditorReturn;\n core: UseEditorCoreReturn;\n runtime: CloudRuntime;\n cloud: CloudAttachment;\n ready: CloudReady;\n /**\n * The editor's `init({ locale })`, forwarded to `MediaLibraryModal` so\n * `@templatical/media-library` loads its own strings in the same language.\n * A locale rather than translations: that package owns its copy, and the\n * editor's own chunk carries none of it.\n */\n locale?: string;\n}>();\n\nfunction applyContent(content: TemplateContent): void {\n props.core.history.record();\n props.editor.setContent(content);\n props.core.conditionPreview.reset();\n}\n</script>\n\n<template>\n <!-- A template create / load in flight. Cloud reads its content from the\n server, so there is a real gap where the canvas shows the wrong thing. -->\n <Transition\n enter-active-class=\"tpl:transition-opacity tpl:duration-200\"\n enter-from-class=\"tpl:opacity-100\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition-opacity tpl:duration-300\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <CloudLoadingOverlay :visible=\"editor.state.isLoading\" />\n </Transition>\n\n <!-- Session failure — an auth refresh that could not renew the token. The\n *bootstrap* has no overlay any more: it runs before the mount, so a\n failure there rejects `initCloud()` instead of mounting a dead editor. -->\n <Transition\n enter-active-class=\"tpl:transition-opacity tpl:duration-200\"\n enter-from-class=\"tpl:opacity-0\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition-opacity tpl:duration-300\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <CloudErrorOverlay\n :error=\"ready.sessionError.value\"\n :visible=\"!!ready.sessionError.value\"\n @retry=\"ready.retry\"\n />\n </Transition>\n\n <CloudSaveGateModal\n :open=\"ready.saveGate.modalOpen.value\"\n :issues=\"ready.saveGate.blockingIssues.value\"\n @cancel=\"ready.saveGate.cancel\"\n @confirm=\"ready.saveGate.confirmAndSave\"\n />\n\n <Transition\n enter-active-class=\"tpl:transition-all tpl:duration-200 tpl:ease-out\"\n enter-from-class=\"tpl:translate-y-[-8px] tpl:opacity-0\"\n enter-to-class=\"tpl:translate-y-0 tpl:opacity-100\"\n leave-active-class=\"tpl:transition-all tpl:duration-300 tpl:ease-in\"\n leave-from-class=\"tpl:translate-y-0 tpl:opacity-100\"\n leave-to-class=\"tpl:translate-y-[-8px] tpl:opacity-0\"\n >\n <CollabUndoToast\n :visible=\"ready.collabWarning.collabUndoWarningVisible.value\"\n />\n </Transition>\n\n <AiChatSidebar\n :visible=\"cloud.panelState.aiChatOpen.value\"\n :on-apply=\"applyContent\"\n @close=\"cloud.panelState.aiChatOpen.value = false\"\n />\n\n <TemplateScoringPanel\n :visible=\"cloud.panelState.scoringPanelOpen.value\"\n @close=\"cloud.panelState.scoringPanelOpen.value = false\"\n />\n\n <DesignReferenceSidebar\n :visible=\"cloud.panelState.designReferenceOpen.value\"\n :has-existing-blocks=\"editor.content.value.blocks.length > 0\"\n @close=\"cloud.panelState.designReferenceOpen.value = false\"\n @apply=\"applyContent\"\n />\n\n <!-- `auth-manager` / `project-id` / `plan-config` are props, not injections.\n An injection would have to match the modal's key identity; a mismatch\n arrives as `undefined` with no error and the browser opens inert. -->\n <MediaLibraryModal\n :visible=\"cloud.panelState.mediaLibraryOpen.value\"\n :accept=\"cloud.panelState.mediaLibraryAccept.value\"\n :popover-target=\"core.popoverRoot.value\"\n :locale=\"locale\"\n :ui-theme=\"core.resolvedTheme.value\"\n :auth-manager=\"cloud.mediaBrowser.authManager\"\n :project-id=\"cloud.mediaBrowser.projectId\"\n :plan-config=\"cloud.mediaBrowser.planConfig\"\n @select=\"cloud.mediaLib.handleMediaSelect\"\n @close=\"cloud.mediaLib.handleMediaLibraryClose\"\n />\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"CloudPanels-Cnh07_2G.js","names":[],"sources":["../../../src/cloud/components/CloudErrorOverlay.vue","../../../src/cloud/components/CloudErrorOverlay.vue","../../../src/cloud/components/CloudLoadingOverlay.vue","../../../src/cloud/components/CloudLoadingOverlay.vue","../../../src/cloud/components/CloudSaveGateModal.vue","../../../src/cloud/components/CloudSaveGateModal.vue","../../../src/cloud/components/CollabUndoToast.vue","../../../src/cloud/components/CollabUndoToast.vue","../../../src/cloud/components/CloudPanels.vue","../../../src/cloud/components/CloudPanels.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { CircleAlert } from \"@lucide/vue\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\n\ndefineProps<{\n error: Error | null;\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"retry\"): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n\nfunction getErrorMessage(error: Error): string {\n if (\n \"isUnauthorized\" in error &&\n (error as { isUnauthorized: boolean }).isUnauthorized\n ) {\n return cloudT.error.authFailed;\n }\n if (\"isNotFound\" in error && (error as { isNotFound: boolean }).isNotFound) {\n return cloudT.error.templateNotFound;\n }\n return cloudT.error.defaultMessage;\n}\n\nfunction isNotFoundError(error: Error): boolean {\n return (\n \"isNotFound\" in error && !!(error as { isNotFound: boolean }).isNotFound\n );\n}\n</script>\n\n<template>\n <div\n v-if=\"visible && error\"\n role=\"alert\"\n class=\"tpl-error tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-6 tpl:px-8 tpl:bg-[var(--tpl-bg)]\"\n >\n <div\n class=\"tpl:flex tpl:size-16 tpl:items-center tpl:justify-center tpl:rounded-full tpl:bg-[var(--tpl-danger-light)]\"\n >\n <CircleAlert\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-danger)]\"\n />\n </div>\n <div\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:text-center\"\n >\n <h2 class=\"tpl:text-lg tpl:font-semibold tpl:text-[var(--tpl-text)]\">\n {{ cloudT.error.title }}\n </h2>\n <p class=\"tpl:max-w-md tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ getErrorMessage(error) }}\n </p>\n </div>\n <button\n v-if=\"!isNotFoundError(error)\"\n class=\"tpl-btn tpl-btn-primary tpl:inline-flex tpl:items-center tpl:gap-2 tpl:rounded-md tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:shadow-[var(--tpl-shadow-sm)] tpl:transition-all tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-on-primary)]\"\n @click=\"emit('retry')\"\n >\n {{ cloudT.error.retry }}\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { CircleAlert } from \"@lucide/vue\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\n\ndefineProps<{\n error: Error | null;\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"retry\"): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n\nfunction getErrorMessage(error: Error): string {\n if (\n \"isUnauthorized\" in error &&\n (error as { isUnauthorized: boolean }).isUnauthorized\n ) {\n return cloudT.error.authFailed;\n }\n if (\"isNotFound\" in error && (error as { isNotFound: boolean }).isNotFound) {\n return cloudT.error.templateNotFound;\n }\n return cloudT.error.defaultMessage;\n}\n\nfunction isNotFoundError(error: Error): boolean {\n return (\n \"isNotFound\" in error && !!(error as { isNotFound: boolean }).isNotFound\n );\n}\n</script>\n\n<template>\n <div\n v-if=\"visible && error\"\n role=\"alert\"\n class=\"tpl-error tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:gap-6 tpl:px-8 tpl:bg-[var(--tpl-bg)]\"\n >\n <div\n class=\"tpl:flex tpl:size-16 tpl:items-center tpl:justify-center tpl:rounded-full tpl:bg-[var(--tpl-danger-light)]\"\n >\n <CircleAlert\n :size=\"32\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-danger)]\"\n />\n </div>\n <div\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:gap-2 tpl:text-center\"\n >\n <h2 class=\"tpl:text-lg tpl:font-semibold tpl:text-[var(--tpl-text)]\">\n {{ cloudT.error.title }}\n </h2>\n <p class=\"tpl:max-w-md tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ getErrorMessage(error) }}\n </p>\n </div>\n <button\n v-if=\"!isNotFoundError(error)\"\n class=\"tpl-btn tpl-btn-primary tpl:inline-flex tpl:items-center tpl:gap-2 tpl:rounded-md tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:font-medium tpl:shadow-[var(--tpl-shadow-sm)] tpl:transition-all tpl:hover:opacity-90 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-on-primary)]\"\n @click=\"emit('retry')\"\n >\n {{ cloudT.error.retry }}\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\ndefineProps<{\n visible: boolean;\n}>();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n class=\"tpl-loading tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:bg-[var(--tpl-bg)]\"\n >\n <!-- Skeleton header -->\n <div\n class=\"tpl:flex tpl:h-14 tpl:shrink-0 tpl:items-center tpl:justify-between tpl:px-4 tpl:border-b tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl-shimmer tpl:h-5 tpl:w-28 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div class=\"tpl:flex tpl:gap-3\">\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n </div>\n <!-- Skeleton body -->\n <div class=\"tpl:flex tpl:flex-1 tpl:overflow-hidden\">\n <!-- Left sidebar rail -->\n <div\n class=\"tpl:flex tpl:w-12 tpl:shrink-0 tpl:flex-col tpl:items-center tpl:gap-4 tpl:py-5 tpl:border-r tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-for=\"n in 5\"\n :key=\"n\"\n class=\"tpl-shimmer tpl:size-7 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <!-- Canvas area -->\n <div\n class=\"tpl:flex tpl:flex-1 tpl:items-start tpl:justify-center tpl:overflow-auto tpl:p-8 tpl:bg-[var(--tpl-canvas-bg)]\"\n >\n <div\n class=\"tpl:w-full tpl:max-w-[600px] tpl:rounded-[var(--tpl-radius)] tpl:p-6 tpl:bg-[var(--tpl-bg)] tpl:shadow-[var(--tpl-shadow-sm)]\"\n >\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-3/4 tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-5/6 tpl:rounded\"></div>\n </div>\n <div class=\"tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:h-44 tpl:w-full tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-2/3 tpl:rounded\"></div>\n </div>\n <div class=\"tpl:flex tpl:justify-center tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:h-10 tpl:w-36 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/2 tpl:rounded\"\n ></div>\n <div\n class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/3 tpl:rounded\"\n ></div>\n </div>\n </div>\n </div>\n <!-- Right panel -->\n <div\n class=\"tpl:flex tpl:w-[320px] tpl:shrink-0 tpl:flex-col tpl:gap-4 tpl:p-4 tpl:border-l tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div>\n <div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\ndefineProps<{\n visible: boolean;\n}>();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n class=\"tpl-loading tpl:absolute tpl:inset-0 tpl:z-overlay tpl:flex tpl:flex-col tpl:bg-[var(--tpl-bg)]\"\n >\n <!-- Skeleton header -->\n <div\n class=\"tpl:flex tpl:h-14 tpl:shrink-0 tpl:items-center tpl:justify-between tpl:px-4 tpl:border-b tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl-shimmer tpl:h-5 tpl:w-28 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div class=\"tpl:flex tpl:gap-3\">\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:w-20 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n </div>\n <!-- Skeleton body -->\n <div class=\"tpl:flex tpl:flex-1 tpl:overflow-hidden\">\n <!-- Left sidebar rail -->\n <div\n class=\"tpl:flex tpl:w-12 tpl:shrink-0 tpl:flex-col tpl:items-center tpl:gap-4 tpl:py-5 tpl:border-r tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-for=\"n in 5\"\n :key=\"n\"\n class=\"tpl-shimmer tpl:size-7 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <!-- Canvas area -->\n <div\n class=\"tpl:flex tpl:flex-1 tpl:items-start tpl:justify-center tpl:overflow-auto tpl:p-8 tpl:bg-[var(--tpl-canvas-bg)]\"\n >\n <div\n class=\"tpl:w-full tpl:max-w-[600px] tpl:rounded-[var(--tpl-radius)] tpl:p-6 tpl:bg-[var(--tpl-bg)] tpl:shadow-[var(--tpl-shadow-sm)]\"\n >\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-3/4 tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-5/6 tpl:rounded\"></div>\n </div>\n <div class=\"tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:h-44 tpl:w-full tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-full tpl:rounded\"></div>\n <div class=\"tpl-shimmer tpl:h-3 tpl:w-2/3 tpl:rounded\"></div>\n </div>\n <div class=\"tpl:flex tpl:justify-center tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:h-10 tpl:w-36 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n </div>\n <div class=\"tpl:space-y-2 tpl:py-4\">\n <div\n class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/2 tpl:rounded\"\n ></div>\n <div\n class=\"tpl-shimmer tpl:mx-auto tpl:h-2.5 tpl:w-1/3 tpl:rounded\"\n ></div>\n </div>\n </div>\n </div>\n <!-- Right panel -->\n <div\n class=\"tpl:flex tpl:w-[320px] tpl:shrink-0 tpl:flex-col tpl:gap-4 tpl:p-4 tpl:border-l tpl:border-[var(--tpl-border)]\"\n >\n <div\n class=\"tpl-shimmer tpl:h-8 tpl:rounded-[var(--tpl-radius-sm)]\"\n ></div>\n <div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div>\n <div class=\"tpl-shimmer tpl:h-32 tpl:rounded-[var(--tpl-radius)]\"></div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { AlertTriangle } from \"@lucide/vue\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\nimport type { LintIssue } from \"../../composables/useTemplateLint\";\n\ndefineProps<{\n open: boolean;\n issues: LintIssue[];\n}>();\n\nconst emit = defineEmits<{\n (e: \"cancel\"): void;\n (e: \"confirm\"): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n</script>\n\n<template>\n <Transition\n enter-active-class=\"tpl:transition-opacity\"\n leave-active-class=\"tpl:transition-opacity\"\n enter-from-class=\"tpl:opacity-0\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <div\n v-if=\"open\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"cloudT.saveGate.title\"\n class=\"tpl:fixed tpl:inset-0 tpl:z-50 tpl:flex tpl:items-center tpl:justify-center tpl:bg-black/40 tpl:p-6\"\n @click.self=\"emit('cancel')\"\n >\n <div\n class=\"tpl:flex tpl:max-h-[80%] tpl:w-full tpl:max-w-md tpl:flex-col tpl:gap-4 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-5 tpl:shadow-[var(--tpl-shadow-md)]\"\n >\n <header class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <AlertTriangle\n :size=\"18\"\n :stroke-width=\"2\"\n class=\"tpl:text-[var(--tpl-warning)]\"\n />\n <h2\n class=\"tpl:m-0 tpl:text-base tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.saveGate.title }}\n </h2>\n </header>\n\n <p class=\"tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ cloudT.saveGate.body }}\n </p>\n\n <ul\n class=\"tpl:m-0 tpl:flex tpl:max-h-64 tpl:list-none tpl:flex-col tpl:gap-1.5 tpl:overflow-y-auto tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-2\"\n >\n <li\n v-for=\"issue in issues\"\n :key=\"`${issue.ruleId}-${issue.blockId ?? 'template'}`\"\n class=\"tpl:flex tpl:flex-col tpl:gap-0.5 tpl:rounded tpl:px-2 tpl:py-1.5\"\n >\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\">\n {{ issue.message }}\n </span>\n <span\n class=\"tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ issue.ruleId }}\n </span>\n </li>\n </ul>\n\n <footer class=\"tpl:flex tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n @click=\"emit('cancel')\"\n >\n {{ cloudT.saveGate.cancel }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:bg-[var(--tpl-danger)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-bg)]\"\n @click=\"emit('confirm')\"\n >\n {{ cloudT.saveGate.confirm }}\n </button>\n </footer>\n </div>\n </div>\n </Transition>\n</template>\n","<script setup lang=\"ts\">\nimport { AlertTriangle } from \"@lucide/vue\";\nimport { useCloudI18nStrict } from \"../../composables/useCloudI18n\";\nimport type { LintIssue } from \"../../composables/useTemplateLint\";\n\ndefineProps<{\n open: boolean;\n issues: LintIssue[];\n}>();\n\nconst emit = defineEmits<{\n (e: \"cancel\"): void;\n (e: \"confirm\"): void;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n</script>\n\n<template>\n <Transition\n enter-active-class=\"tpl:transition-opacity\"\n leave-active-class=\"tpl:transition-opacity\"\n enter-from-class=\"tpl:opacity-0\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <div\n v-if=\"open\"\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-label=\"cloudT.saveGate.title\"\n class=\"tpl:fixed tpl:inset-0 tpl:z-50 tpl:flex tpl:items-center tpl:justify-center tpl:bg-black/40 tpl:p-6\"\n @click.self=\"emit('cancel')\"\n >\n <div\n class=\"tpl:flex tpl:max-h-[80%] tpl:w-full tpl:max-w-md tpl:flex-col tpl:gap-4 tpl:rounded-lg tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-5 tpl:shadow-[var(--tpl-shadow-md)]\"\n >\n <header class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <AlertTriangle\n :size=\"18\"\n :stroke-width=\"2\"\n class=\"tpl:text-[var(--tpl-warning)]\"\n />\n <h2\n class=\"tpl:m-0 tpl:text-base tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.saveGate.title }}\n </h2>\n </header>\n\n <p class=\"tpl:m-0 tpl:text-sm tpl:text-[var(--tpl-text-muted)]\">\n {{ cloudT.saveGate.body }}\n </p>\n\n <ul\n class=\"tpl:m-0 tpl:flex tpl:max-h-64 tpl:list-none tpl:flex-col tpl:gap-1.5 tpl:overflow-y-auto tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-2\"\n >\n <li\n v-for=\"issue in issues\"\n :key=\"`${issue.ruleId}-${issue.blockId ?? 'template'}`\"\n class=\"tpl:flex tpl:flex-col tpl:gap-0.5 tpl:rounded tpl:px-2 tpl:py-1.5\"\n >\n <span class=\"tpl:text-xs tpl:text-[var(--tpl-text)]\">\n {{ issue.message }}\n </span>\n <span\n class=\"tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ issue.ruleId }}\n </span>\n </li>\n </ul>\n\n <footer class=\"tpl:flex tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-text)]\"\n @click=\"emit('cancel')\"\n >\n {{ cloudT.saveGate.cancel }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:rounded-md tpl:bg-[var(--tpl-danger)] tpl:px-3 tpl:py-1.5 tpl:text-xs tpl:font-medium tpl:text-[var(--tpl-bg)]\"\n @click=\"emit('confirm')\"\n >\n {{ cloudT.saveGate.confirm }}\n </button>\n </footer>\n </div>\n </div>\n </Transition>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\n\ndefineProps<{\n visible: boolean;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n role=\"status\"\n aria-live=\"polite\"\n class=\"tpl:absolute tpl:top-16 tpl:left-1/2 tpl:z-toast tpl:-translate-x-1/2 tpl:rounded-[var(--tpl-radius)] tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:shadow-[var(--tpl-shadow-lg)]\"\n style=\"\n background-color: var(--tpl-warning-light);\n color: var(--tpl-text);\n border: 1px solid var(--tpl-warning);\n \"\n >\n {{ t.history.collabWarning }}\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../../composables/useI18n\";\n\ndefineProps<{\n visible: boolean;\n}>();\n\nconst { t } = useI18n();\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n role=\"status\"\n aria-live=\"polite\"\n class=\"tpl:absolute tpl:top-16 tpl:left-1/2 tpl:z-toast tpl:-translate-x-1/2 tpl:rounded-[var(--tpl-radius)] tpl:px-4 tpl:py-2.5 tpl:text-sm tpl:shadow-[var(--tpl-shadow-lg)]\"\n style=\"\n background-color: var(--tpl-warning-light);\n color: var(--tpl-text);\n border: 1px solid var(--tpl-warning);\n \"\n >\n {{ t.history.collabWarning }}\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { defineAsyncComponent } from \"vue\";\nimport type { TemplateContent } from \"@templatical/types\";\nimport type { UseEditorReturn } from \"@templatical/core\";\n\nimport type { UseEditorCoreReturn } from \"../../composables/useEditorCore\";\nimport type { CloudAttachment, CloudReady, CloudRuntime } from \"../runtime\";\n\nimport CloudErrorOverlay from \"./CloudErrorOverlay.vue\";\nimport CloudLoadingOverlay from \"./CloudLoadingOverlay.vue\";\nimport CloudSaveGateModal from \"./CloudSaveGateModal.vue\";\nimport CollabUndoToast from \"./CollabUndoToast.vue\";\n\nconst AiChatSidebar = defineAsyncComponent(() => import(\"./AiChatSidebar.vue\"));\nconst DesignReferenceSidebar = defineAsyncComponent(\n () => import(\"./DesignReferenceSidebar.vue\"),\n);\nconst TemplateScoringPanel = defineAsyncComponent(\n () => import(\"./TemplateScoringPanel.vue\"),\n);\nconst MediaLibraryModal = defineAsyncComponent(async () => {\n // try/catch downgrades Webpack's \"Module not found\" from error to warning\n // when the optional peer isn't installed. Cloud consumers always install it.\n try {\n const m = await import(\"@templatical/media-library\");\n return m.MediaLibraryModal;\n } catch {\n throw new Error(\n \"[Templatical] Cloud media library requires the optional peer dependency '@templatical/media-library'. Please install it.\",\n );\n }\n});\n\n/**\n * Every piece of Cloud chrome that renders outside the header.\n *\n * `Editor.vue` mounts this behind one lazy `defineAsyncComponent` gated on a\n * cloud runtime being present, so an OSS consumer downloads none of it — the\n * `SavedBlocksPanels` / `TestEmailPanel` pattern.\n *\n * The overlays live here rather than in the shared editor deliberately: `init()`\n * cannot fail after it mounts, and OSS should not grow the ability.\n *\n * The comments sidebar is deliberately not here: it is shared, as `CommentsPanel`\n * in `Editor.vue` over a `CommentsProvider`. Its filter target lives on the shared\n * feature rather than being relayed through this component, so it survives the\n * lazy panel's mount without a runtime hop.\n */\nconst props = defineProps<{\n editor: UseEditorReturn;\n core: UseEditorCoreReturn;\n runtime: CloudRuntime;\n cloud: CloudAttachment;\n ready: CloudReady;\n /**\n * The editor's `init({ locale })`, forwarded to `MediaLibraryModal` so\n * `@templatical/media-library` loads its own strings in the same language.\n * A locale rather than translations: that package owns its copy, and the\n * editor's own chunk carries none of it.\n */\n locale?: string;\n}>();\n\nfunction applyContent(content: TemplateContent): void {\n props.core.history.record();\n props.editor.setContent(content);\n props.core.conditionPreview.reset();\n}\n</script>\n\n<template>\n <!-- A template create / load in flight. Cloud reads its content from the\n server, so there is a real gap where the canvas shows the wrong thing. -->\n <Transition\n enter-active-class=\"tpl:transition-opacity tpl:duration-200\"\n enter-from-class=\"tpl:opacity-100\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition-opacity tpl:duration-300\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <CloudLoadingOverlay :visible=\"editor.state.isLoading\" />\n </Transition>\n\n <!-- Session failure — an auth refresh that could not renew the token. The\n *bootstrap* has no overlay any more: it runs before the mount, so a\n failure there rejects `initCloud()` instead of mounting a dead editor. -->\n <Transition\n enter-active-class=\"tpl:transition-opacity tpl:duration-200\"\n enter-from-class=\"tpl:opacity-0\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition-opacity tpl:duration-300\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <CloudErrorOverlay\n :error=\"ready.sessionError.value\"\n :visible=\"!!ready.sessionError.value\"\n @retry=\"ready.retry\"\n />\n </Transition>\n\n <CloudSaveGateModal\n :open=\"ready.saveGate.modalOpen.value\"\n :issues=\"ready.saveGate.blockingIssues.value\"\n @cancel=\"ready.saveGate.cancel\"\n @confirm=\"ready.saveGate.confirmAndSave\"\n />\n\n <Transition\n enter-active-class=\"tpl:transition-all tpl:duration-200 tpl:ease-out\"\n enter-from-class=\"tpl:translate-y-[-8px] tpl:opacity-0\"\n enter-to-class=\"tpl:translate-y-0 tpl:opacity-100\"\n leave-active-class=\"tpl:transition-all tpl:duration-300 tpl:ease-in\"\n leave-from-class=\"tpl:translate-y-0 tpl:opacity-100\"\n leave-to-class=\"tpl:translate-y-[-8px] tpl:opacity-0\"\n >\n <CollabUndoToast\n :visible=\"ready.collabWarning.collabUndoWarningVisible.value\"\n />\n </Transition>\n\n <AiChatSidebar\n :visible=\"cloud.panelState.aiChatOpen.value\"\n :on-apply=\"applyContent\"\n @close=\"cloud.panelState.aiChatOpen.value = false\"\n />\n\n <TemplateScoringPanel\n :visible=\"cloud.panelState.scoringPanelOpen.value\"\n @close=\"cloud.panelState.scoringPanelOpen.value = false\"\n />\n\n <DesignReferenceSidebar\n :visible=\"cloud.panelState.designReferenceOpen.value\"\n :has-existing-blocks=\"editor.content.value.blocks.length > 0\"\n @close=\"cloud.panelState.designReferenceOpen.value = false\"\n @apply=\"applyContent\"\n />\n\n <!-- `auth-manager` / `project-id` / `plan-config` are props, not injections.\n An injection would have to match the modal's key identity; a mismatch\n arrives as `undefined` with no error and the browser opens inert. -->\n <MediaLibraryModal\n :visible=\"cloud.panelState.mediaLibraryOpen.value\"\n :accept=\"cloud.panelState.mediaLibraryAccept.value\"\n :popover-target=\"core.popoverRoot.value\"\n :locale=\"locale\"\n :ui-theme=\"core.resolvedTheme.value\"\n :auth-manager=\"cloud.mediaBrowser.authManager\"\n :project-id=\"cloud.mediaBrowser.projectId\"\n :plan-config=\"cloud.mediaBrowser.planConfig\"\n @select=\"cloud.mediaLib.handleMediaSelect\"\n @close=\"cloud.mediaLib.handleMediaLibraryClose\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { defineAsyncComponent } from \"vue\";\nimport type { TemplateContent } from \"@templatical/types\";\nimport type { UseEditorReturn } from \"@templatical/core\";\n\nimport type { UseEditorCoreReturn } from \"../../composables/useEditorCore\";\nimport type { CloudAttachment, CloudReady, CloudRuntime } from \"../runtime\";\n\nimport CloudErrorOverlay from \"./CloudErrorOverlay.vue\";\nimport CloudLoadingOverlay from \"./CloudLoadingOverlay.vue\";\nimport CloudSaveGateModal from \"./CloudSaveGateModal.vue\";\nimport CollabUndoToast from \"./CollabUndoToast.vue\";\n\nconst AiChatSidebar = defineAsyncComponent(() => import(\"./AiChatSidebar.vue\"));\nconst DesignReferenceSidebar = defineAsyncComponent(\n () => import(\"./DesignReferenceSidebar.vue\"),\n);\nconst TemplateScoringPanel = defineAsyncComponent(\n () => import(\"./TemplateScoringPanel.vue\"),\n);\nconst MediaLibraryModal = defineAsyncComponent(async () => {\n // try/catch downgrades Webpack's \"Module not found\" from error to warning\n // when the optional peer isn't installed. Cloud consumers always install it.\n try {\n const m = await import(\"@templatical/media-library\");\n return m.MediaLibraryModal;\n } catch {\n throw new Error(\n \"[Templatical] Cloud media library requires the optional peer dependency '@templatical/media-library'. Please install it.\",\n );\n }\n});\n\n/**\n * Every piece of Cloud chrome that renders outside the header.\n *\n * `Editor.vue` mounts this behind one lazy `defineAsyncComponent` gated on a\n * cloud runtime being present, so an OSS consumer downloads none of it — the\n * `SavedBlocksPanels` / `TestEmailPanel` pattern.\n *\n * The overlays live here rather than in the shared editor deliberately: `init()`\n * cannot fail after it mounts, and OSS should not grow the ability.\n *\n * The comments sidebar is deliberately not here: it is shared, as `CommentsPanel`\n * in `Editor.vue` over a `CommentsProvider`. Its filter target lives on the shared\n * feature rather than being relayed through this component, so it survives the\n * lazy panel's mount without a runtime hop.\n */\nconst props = defineProps<{\n editor: UseEditorReturn;\n core: UseEditorCoreReturn;\n runtime: CloudRuntime;\n cloud: CloudAttachment;\n ready: CloudReady;\n /**\n * The editor's `init({ locale })`, forwarded to `MediaLibraryModal` so\n * `@templatical/media-library` loads its own strings in the same language.\n * A locale rather than translations: that package owns its copy, and the\n * editor's own chunk carries none of it.\n */\n locale?: string;\n}>();\n\nfunction applyContent(content: TemplateContent): void {\n props.core.history.record();\n props.editor.setContent(content);\n props.core.conditionPreview.reset();\n}\n</script>\n\n<template>\n <!-- A template create / load in flight. Cloud reads its content from the\n server, so there is a real gap where the canvas shows the wrong thing. -->\n <Transition\n enter-active-class=\"tpl:transition-opacity tpl:duration-200\"\n enter-from-class=\"tpl:opacity-100\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition-opacity tpl:duration-300\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <CloudLoadingOverlay :visible=\"editor.state.isLoading\" />\n </Transition>\n\n <!-- Session failure — an auth refresh that could not renew the token. The\n *bootstrap* has no overlay any more: it runs before the mount, so a\n failure there rejects `initCloud()` instead of mounting a dead editor. -->\n <Transition\n enter-active-class=\"tpl:transition-opacity tpl:duration-200\"\n enter-from-class=\"tpl:opacity-0\"\n enter-to-class=\"tpl:opacity-100\"\n leave-active-class=\"tpl:transition-opacity tpl:duration-300\"\n leave-from-class=\"tpl:opacity-100\"\n leave-to-class=\"tpl:opacity-0\"\n >\n <CloudErrorOverlay\n :error=\"ready.sessionError.value\"\n :visible=\"!!ready.sessionError.value\"\n @retry=\"ready.retry\"\n />\n </Transition>\n\n <CloudSaveGateModal\n :open=\"ready.saveGate.modalOpen.value\"\n :issues=\"ready.saveGate.blockingIssues.value\"\n @cancel=\"ready.saveGate.cancel\"\n @confirm=\"ready.saveGate.confirmAndSave\"\n />\n\n <Transition\n enter-active-class=\"tpl:transition-all tpl:duration-200 tpl:ease-out\"\n enter-from-class=\"tpl:translate-y-[-8px] tpl:opacity-0\"\n enter-to-class=\"tpl:translate-y-0 tpl:opacity-100\"\n leave-active-class=\"tpl:transition-all tpl:duration-300 tpl:ease-in\"\n leave-from-class=\"tpl:translate-y-0 tpl:opacity-100\"\n leave-to-class=\"tpl:translate-y-[-8px] tpl:opacity-0\"\n >\n <CollabUndoToast\n :visible=\"ready.collabWarning.collabUndoWarningVisible.value\"\n />\n </Transition>\n\n <AiChatSidebar\n :visible=\"cloud.panelState.aiChatOpen.value\"\n :on-apply=\"applyContent\"\n @close=\"cloud.panelState.aiChatOpen.value = false\"\n />\n\n <TemplateScoringPanel\n :visible=\"cloud.panelState.scoringPanelOpen.value\"\n @close=\"cloud.panelState.scoringPanelOpen.value = false\"\n />\n\n <DesignReferenceSidebar\n :visible=\"cloud.panelState.designReferenceOpen.value\"\n :has-existing-blocks=\"editor.content.value.blocks.length > 0\"\n @close=\"cloud.panelState.designReferenceOpen.value = false\"\n @apply=\"applyContent\"\n />\n\n <!-- `auth-manager` / `project-id` / `plan-config` are props, not injections.\n An injection would have to match the modal's key identity; a mismatch\n arrives as `undefined` with no error and the browser opens inert. -->\n <MediaLibraryModal\n :visible=\"cloud.panelState.mediaLibraryOpen.value\"\n :accept=\"cloud.panelState.mediaLibraryAccept.value\"\n :popover-target=\"core.popoverRoot.value\"\n :locale=\"locale\"\n :ui-theme=\"core.resolvedTheme.value\"\n :auth-manager=\"cloud.mediaBrowser.authManager\"\n :project-id=\"cloud.mediaBrowser.projectId\"\n :plan-config=\"cloud.mediaBrowser.planConfig\"\n @select=\"cloud.mediaLib.handleMediaSelect\"\n @close=\"cloud.mediaLib.handleMediaLibraryClose\"\n />\n</template>\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { F as e, N as t, S as n, at as r, f as i, g as a, h as o, lt as s, m as c, p as l, u, ut as d } from "./draggable-D2vo-aQI.js";
2
- import "./useEditorCore-o2emEjgF.js";
2
+ import "./useEditorCore-BQeEwEQ9.js";
3
3
  import { a as f, i as p } from "./icons-BTdLXRXn.js";
4
4
  import { n as m } from "./useCloudI18n-D0AoLV0a.js";
5
5
  import { t as h } from "./readableTextColor-f8Kykfnh.js";
@@ -49,4 +49,4 @@ var g = { class: "tpl-collaborator-bar tpl:flex tpl:items-center tpl:gap-2" }, _
49
49
  //#endregion
50
50
  export { S as default };
51
51
 
52
- //# sourceMappingURL=CollaboratorBar-CvvoIrKS.js.map
52
+ //# sourceMappingURL=CollaboratorBar-CLGB0prG.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CollaboratorBar-CvvoIrKS.js","names":[],"sources":["../../../src/cloud/components/CollaboratorBar.vue","../../../src/cloud/components/CollaboratorBar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Collaborator } from \"@templatical/types\";\nimport { useCloudI18nStrict } from \"../../composables\";\nimport { Wifi, WifiOff } from \"@lucide/vue\";\nimport { computed } from \"vue\";\nimport { readableTextColor } from \"../../utils/readableTextColor\";\n\nconst props = defineProps<{\n collaborators: Collaborator[];\n isConnected: boolean;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n\nconst maxVisible = 3;\n\nconst visibleCollaborators = computed(() =>\n props.collaborators.slice(0, maxVisible),\n);\n\nconst overflowCollaborators = computed(() =>\n props.collaborators.slice(maxVisible),\n);\n\nconst overflowCount = computed(() => overflowCollaborators.value.length);\n\nconst overflowNames = computed(() =>\n overflowCollaborators.value.map((c) => c.name).join(\"\\n\"),\n);\n\nfunction getInitials(name: string): string {\n const parts = name.trim().split(/\\s+/);\n if (parts.length >= 2) {\n return (\n parts[0].charAt(0) + parts[parts.length - 1].charAt(0)\n ).toUpperCase();\n }\n return name.charAt(0).toUpperCase();\n}\n</script>\n\n<template>\n <div class=\"tpl-collaborator-bar tpl:flex tpl:items-center tpl:gap-2\">\n <!-- Connection indicator -->\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:text-[11px]\"\n :style=\"{\n color: isConnected ? 'var(--tpl-success)' : 'var(--tpl-text-muted)',\n }\"\n :title=\"\n isConnected\n ? cloudT.collaboration.connected\n : cloudT.collaboration.disconnected\n \"\n >\n <Wifi v-if=\"isConnected\" :size=\"12\" :stroke-width=\"2\" />\n <WifiOff v-else :size=\"12\" :stroke-width=\"2\" />\n </div>\n\n <!-- Avatar stack -->\n <div\n v-if=\"collaborators.length > 0\"\n class=\"tpl:flex tpl:items-center tpl:-space-x-1.5\"\n >\n <div\n v-for=\"collaborator in visibleCollaborators\"\n :key=\"collaborator.id\"\n class=\"tpl-collaborator-avatar tpl:relative tpl:flex tpl:size-6 tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-2 tpl:text-[10px] tpl:font-bold tpl:transition-transform tpl:hover:z-10 tpl:hover:scale-110 tpl:border-[var(--tpl-bg)]\"\n :style=\"{\n backgroundColor: collaborator.color,\n color: readableTextColor(collaborator.color),\n }\"\n :title=\"collaborator.name\"\n >\n {{ getInitials(collaborator.name) }}\n </div>\n <div\n v-if=\"overflowCount > 0\"\n class=\"tpl:relative tpl:flex tpl:size-6 tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-2 tpl:text-[9px] tpl:font-bold tpl:border-[var(--tpl-bg)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n :title=\"overflowNames\"\n >\n +{{ overflowCount }}\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport type { Collaborator } from \"@templatical/types\";\nimport { useCloudI18nStrict } from \"../../composables\";\nimport { Wifi, WifiOff } from \"@lucide/vue\";\nimport { computed } from \"vue\";\nimport { readableTextColor } from \"../../utils/readableTextColor\";\n\nconst props = defineProps<{\n collaborators: Collaborator[];\n isConnected: boolean;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n\nconst maxVisible = 3;\n\nconst visibleCollaborators = computed(() =>\n props.collaborators.slice(0, maxVisible),\n);\n\nconst overflowCollaborators = computed(() =>\n props.collaborators.slice(maxVisible),\n);\n\nconst overflowCount = computed(() => overflowCollaborators.value.length);\n\nconst overflowNames = computed(() =>\n overflowCollaborators.value.map((c) => c.name).join(\"\\n\"),\n);\n\nfunction getInitials(name: string): string {\n const parts = name.trim().split(/\\s+/);\n if (parts.length >= 2) {\n return (\n parts[0].charAt(0) + parts[parts.length - 1].charAt(0)\n ).toUpperCase();\n }\n return name.charAt(0).toUpperCase();\n}\n</script>\n\n<template>\n <div class=\"tpl-collaborator-bar tpl:flex tpl:items-center tpl:gap-2\">\n <!-- Connection indicator -->\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:text-[11px]\"\n :style=\"{\n color: isConnected ? 'var(--tpl-success)' : 'var(--tpl-text-muted)',\n }\"\n :title=\"\n isConnected\n ? cloudT.collaboration.connected\n : cloudT.collaboration.disconnected\n \"\n >\n <Wifi v-if=\"isConnected\" :size=\"12\" :stroke-width=\"2\" />\n <WifiOff v-else :size=\"12\" :stroke-width=\"2\" />\n </div>\n\n <!-- Avatar stack -->\n <div\n v-if=\"collaborators.length > 0\"\n class=\"tpl:flex tpl:items-center tpl:-space-x-1.5\"\n >\n <div\n v-for=\"collaborator in visibleCollaborators\"\n :key=\"collaborator.id\"\n class=\"tpl-collaborator-avatar tpl:relative tpl:flex tpl:size-6 tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-2 tpl:text-[10px] tpl:font-bold tpl:transition-transform tpl:hover:z-10 tpl:hover:scale-110 tpl:border-[var(--tpl-bg)]\"\n :style=\"{\n backgroundColor: collaborator.color,\n color: readableTextColor(collaborator.color),\n }\"\n :title=\"collaborator.name\"\n >\n {{ getInitials(collaborator.name) }}\n </div>\n <div\n v-if=\"overflowCount > 0\"\n class=\"tpl:relative tpl:flex tpl:size-6 tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-2 tpl:text-[9px] tpl:font-bold tpl:border-[var(--tpl-bg)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n :title=\"overflowNames\"\n >\n +{{ overflowCount }}\n </div>\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;iCAcM,IAAa;;;;;;;EAPnB,IAAM,IAAQ,GAKR,EAAE,GAAG,MAAW,EAAmB,GAInC,IAAuB,QAC3B,EAAM,cAAc,MAAM,GAAG,CAAU,CACzC,GAEM,IAAwB,QAC5B,EAAM,cAAc,MAAM,CAAU,CACtC,GAEM,IAAgB,QAAe,EAAsB,MAAM,MAAM,GAEjE,IAAgB,QACpB,EAAsB,MAAM,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,CAC1D;EAEA,SAAS,EAAY,GAAsB;GACzC,IAAM,IAAQ,EAAK,KAAK,CAAC,CAAC,MAAM,KAAK;GAMrC,OALI,EAAM,UAAU,KAEhB,EAAM,EAAE,CAAC,OAAO,CAAC,IAAI,EAAM,EAAM,SAAS,EAAE,CAAC,OAAO,CAAC,EAAA,CACrD,YAAY,IAET,EAAK,OAAO,CAAC,CAAC,CAAC,YAAY;EACpC;oBAIE,EAAA,GAAA,EA0CM,OA1CN,GA0CM,CAxCJ,EAaM,OAAA;GAZJ,OAAM;GACL,OAAK,EAAA,EAAmB,OAAA,EAAA,cAAW,uBAAA,wBAAA,CAAA;GAGnC,OAAgB,EAAA,cAAwB,EAAA,CAAA,CAAM,CAAC,cAAc,YAAsB,EAAA,CAAA,CAAM,CAAC,cAAc;EAM7F,GAAA,CAAA,EAAA,eAAZ,EAAA,GAAA,EAAwD,EAAA,CAAA,GAAA;;GAA9B,MAAM;GAAK,gBAAc;EACnD,CAAA,MAAA,EAAA,GAAA,EAA+C,EAAA,CAAA,GAAA;;GAA9B,MAAM;GAAK,gBAAc;EAKpC,CAAA,EAAA,GAAA,IAAA,CAAA,GAAA,EAAA,cAAc,SAAM,KAD5B,EAAA,GAAA,EAuBM,OAvBN,GAuBM,EAnBJ,EAAA,EAAA,GAAA,EAWM,GAAA,MAAA,EAVmB,EAAA,QAAhB,OADT,EAAA,GAAA,EAWM,OAAA;GATH,KAAK,EAAa;GACnB,OAAM;GACL,OAAK,EAAA;IAA+B,iBAAA,EAAa;IAAwB,OAAA,EAAA,CAAA,CAAiB,CAAC,EAAa,KAAK;;GAI7G,OAAO,EAAa;EAElB,GAAA,EAAA,EAAY,EAAa,IAAI,CAAA,GAAA,IAAA,CAAA,EAG1B,GAAA,GAAA,IAAA,EAAA,QAAa,KADrB,EAAA,GAAA,EAMM,OAAA;;GAJJ,OAAM;GACL,OAAO,EAAA;EACT,GAAA,OACE,EAAG,EAAA,KAAa,GAAA,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"CollaboratorBar-CLGB0prG.js","names":[],"sources":["../../../src/cloud/components/CollaboratorBar.vue","../../../src/cloud/components/CollaboratorBar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Collaborator } from \"@templatical/types\";\nimport { useCloudI18nStrict } from \"../../composables\";\nimport { Wifi, WifiOff } from \"@lucide/vue\";\nimport { computed } from \"vue\";\nimport { readableTextColor } from \"../../utils/readableTextColor\";\n\nconst props = defineProps<{\n collaborators: Collaborator[];\n isConnected: boolean;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n\nconst maxVisible = 3;\n\nconst visibleCollaborators = computed(() =>\n props.collaborators.slice(0, maxVisible),\n);\n\nconst overflowCollaborators = computed(() =>\n props.collaborators.slice(maxVisible),\n);\n\nconst overflowCount = computed(() => overflowCollaborators.value.length);\n\nconst overflowNames = computed(() =>\n overflowCollaborators.value.map((c) => c.name).join(\"\\n\"),\n);\n\nfunction getInitials(name: string): string {\n const parts = name.trim().split(/\\s+/);\n if (parts.length >= 2) {\n return (\n parts[0].charAt(0) + parts[parts.length - 1].charAt(0)\n ).toUpperCase();\n }\n return name.charAt(0).toUpperCase();\n}\n</script>\n\n<template>\n <div class=\"tpl-collaborator-bar tpl:flex tpl:items-center tpl:gap-2\">\n <!-- Connection indicator -->\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:text-[11px]\"\n :style=\"{\n color: isConnected ? 'var(--tpl-success)' : 'var(--tpl-text-muted)',\n }\"\n :title=\"\n isConnected\n ? cloudT.collaboration.connected\n : cloudT.collaboration.disconnected\n \"\n >\n <Wifi v-if=\"isConnected\" :size=\"12\" :stroke-width=\"2\" />\n <WifiOff v-else :size=\"12\" :stroke-width=\"2\" />\n </div>\n\n <!-- Avatar stack -->\n <div\n v-if=\"collaborators.length > 0\"\n class=\"tpl:flex tpl:items-center tpl:-space-x-1.5\"\n >\n <div\n v-for=\"collaborator in visibleCollaborators\"\n :key=\"collaborator.id\"\n class=\"tpl-collaborator-avatar tpl:relative tpl:flex tpl:size-6 tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-2 tpl:text-[10px] tpl:font-bold tpl:transition-transform tpl:hover:z-10 tpl:hover:scale-110 tpl:border-[var(--tpl-bg)]\"\n :style=\"{\n backgroundColor: collaborator.color,\n color: readableTextColor(collaborator.color),\n }\"\n :title=\"collaborator.name\"\n >\n {{ getInitials(collaborator.name) }}\n </div>\n <div\n v-if=\"overflowCount > 0\"\n class=\"tpl:relative tpl:flex tpl:size-6 tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-2 tpl:text-[9px] tpl:font-bold tpl:border-[var(--tpl-bg)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n :title=\"overflowNames\"\n >\n +{{ overflowCount }}\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport type { Collaborator } from \"@templatical/types\";\nimport { useCloudI18nStrict } from \"../../composables\";\nimport { Wifi, WifiOff } from \"@lucide/vue\";\nimport { computed } from \"vue\";\nimport { readableTextColor } from \"../../utils/readableTextColor\";\n\nconst props = defineProps<{\n collaborators: Collaborator[];\n isConnected: boolean;\n}>();\n\nconst { t: cloudT } = useCloudI18nStrict();\n\nconst maxVisible = 3;\n\nconst visibleCollaborators = computed(() =>\n props.collaborators.slice(0, maxVisible),\n);\n\nconst overflowCollaborators = computed(() =>\n props.collaborators.slice(maxVisible),\n);\n\nconst overflowCount = computed(() => overflowCollaborators.value.length);\n\nconst overflowNames = computed(() =>\n overflowCollaborators.value.map((c) => c.name).join(\"\\n\"),\n);\n\nfunction getInitials(name: string): string {\n const parts = name.trim().split(/\\s+/);\n if (parts.length >= 2) {\n return (\n parts[0].charAt(0) + parts[parts.length - 1].charAt(0)\n ).toUpperCase();\n }\n return name.charAt(0).toUpperCase();\n}\n</script>\n\n<template>\n <div class=\"tpl-collaborator-bar tpl:flex tpl:items-center tpl:gap-2\">\n <!-- Connection indicator -->\n <div\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:text-[11px]\"\n :style=\"{\n color: isConnected ? 'var(--tpl-success)' : 'var(--tpl-text-muted)',\n }\"\n :title=\"\n isConnected\n ? cloudT.collaboration.connected\n : cloudT.collaboration.disconnected\n \"\n >\n <Wifi v-if=\"isConnected\" :size=\"12\" :stroke-width=\"2\" />\n <WifiOff v-else :size=\"12\" :stroke-width=\"2\" />\n </div>\n\n <!-- Avatar stack -->\n <div\n v-if=\"collaborators.length > 0\"\n class=\"tpl:flex tpl:items-center tpl:-space-x-1.5\"\n >\n <div\n v-for=\"collaborator in visibleCollaborators\"\n :key=\"collaborator.id\"\n class=\"tpl-collaborator-avatar tpl:relative tpl:flex tpl:size-6 tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-2 tpl:text-[10px] tpl:font-bold tpl:transition-transform tpl:hover:z-10 tpl:hover:scale-110 tpl:border-[var(--tpl-bg)]\"\n :style=\"{\n backgroundColor: collaborator.color,\n color: readableTextColor(collaborator.color),\n }\"\n :title=\"collaborator.name\"\n >\n {{ getInitials(collaborator.name) }}\n </div>\n <div\n v-if=\"overflowCount > 0\"\n class=\"tpl:relative tpl:flex tpl:size-6 tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-2 tpl:text-[9px] tpl:font-bold tpl:border-[var(--tpl-bg)] tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n :title=\"overflowNames\"\n >\n +{{ overflowCount }}\n </div>\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;iCAcM,IAAa;;;;;;;EAPnB,IAAM,IAAQ,GAKR,EAAE,GAAG,MAAW,EAAmB,GAInC,IAAuB,QAC3B,EAAM,cAAc,MAAM,GAAG,CAAU,CACzC,GAEM,IAAwB,QAC5B,EAAM,cAAc,MAAM,CAAU,CACtC,GAEM,IAAgB,QAAe,EAAsB,MAAM,MAAM,GAEjE,IAAgB,QACpB,EAAsB,MAAM,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,CAC1D;EAEA,SAAS,EAAY,GAAsB;GACzC,IAAM,IAAQ,EAAK,KAAK,CAAC,CAAC,MAAM,KAAK;GAMrC,OALI,EAAM,UAAU,KAEhB,EAAM,EAAE,CAAC,OAAO,CAAC,IAAI,EAAM,EAAM,SAAS,EAAE,CAAC,OAAO,CAAC,EAAA,CACrD,YAAY,IAET,EAAK,OAAO,CAAC,CAAC,CAAC,YAAY;EACpC;oBAIE,EAAA,GAAA,EA0CM,OA1CN,GA0CM,CAxCJ,EAaM,OAAA;GAZJ,OAAM;GACL,OAAK,EAAA,EAAmB,OAAA,EAAA,cAAW,uBAAA,wBAAA,CAAA;GAGnC,OAAgB,EAAA,cAAwB,EAAA,CAAA,CAAM,CAAC,cAAc,YAAsB,EAAA,CAAA,CAAM,CAAC,cAAc;EAM7F,GAAA,CAAA,EAAA,eAAZ,EAAA,GAAA,EAAwD,EAAA,CAAA,GAAA;;GAA9B,MAAM;GAAK,gBAAc;EACnD,CAAA,MAAA,EAAA,GAAA,EAA+C,EAAA,CAAA,GAAA;;GAA9B,MAAM;GAAK,gBAAc;EAKpC,CAAA,EAAA,GAAA,IAAA,CAAA,GAAA,EAAA,cAAc,SAAM,KAD5B,EAAA,GAAA,EAuBM,OAvBN,GAuBM,EAnBJ,EAAA,EAAA,GAAA,EAWM,GAAA,MAAA,EAVmB,EAAA,QAAhB,OADT,EAAA,GAAA,EAWM,OAAA;GATH,KAAK,EAAa;GACnB,OAAM;GACL,OAAK,EAAA;IAA+B,iBAAA,EAAa;IAAwB,OAAA,EAAA,CAAA,CAAiB,CAAC,EAAa,KAAK;;GAI7G,OAAO,EAAa;EAElB,GAAA,EAAA,EAAY,EAAa,IAAI,CAAA,GAAA,IAAA,CAAA,EAG1B,GAAA,GAAA,IAAA,EAAA,QAAa,KADrB,EAAA,GAAA,EAMM,OAAA;;GAJJ,OAAM;GACL,OAAO,EAAA;EACT,GAAA,OACE,EAAG,EAAA,KAAa,GAAA,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,CAAA"}
@@ -2,7 +2,7 @@ import { F as e, H as t, N as n, S as r, at as i, b as a, ct as o, f as s, g as
2
2
  import { t as p } from "./useI18n-DIhawILp.js";
3
3
  import { p as m } from "./styleConstants-C_YAPdQS.js";
4
4
  import { t as h } from "./ColorPicker-Ww4xHHMI.js";
5
- import { i as g, n as _, r as v, t as y } from "./NumberWithSuffix-DOG0zHvY.js";
5
+ import { i as g, n as _, r as v, t as y } from "./FieldRow-CbcpsxZ4.js";
6
6
  //#region src/components/toolbar/CountdownToolbar.vue?vue&type=script&setup=true&lang.ts
7
7
  var b = ["value"], x = ["value"], S = ["value"], C = { class: "tpl:grid tpl:grid-cols-2 tpl:gap-2" }, w = ["value"], T = { value: "" }, E = ["value"], D = { class: "tpl:grid tpl:grid-cols-2 tpl:gap-3" }, O = { class: "tpl:grid tpl:grid-cols-2 tpl:gap-3" }, k = { class: "tpl:grid tpl:grid-cols-2 tpl:gap-2" }, A = [
8
8
  "value",
@@ -60,7 +60,7 @@ var b = ["value"], x = ["value"], S = ["value"], C = { class: "tpl:grid tpl:grid
60
60
  P("update", { [e]: t });
61
61
  }
62
62
  return (s, p) => (n(), c(d, null, [
63
- a(_, { label: i(F).countdown.targetDate }, {
63
+ a(y, { label: i(F).countdown.targetDate }, {
64
64
  default: t(() => [u("input", {
65
65
  type: "datetime-local",
66
66
  class: o(i(m)),
@@ -69,7 +69,7 @@ var b = ["value"], x = ["value"], S = ["value"], C = { class: "tpl:grid tpl:grid
69
69
  }, null, 42, b)]),
70
70
  _: 1
71
71
  }, 8, ["label"]),
72
- a(_, { label: i(F).countdown.timezone }, {
72
+ a(y, { label: i(F).countdown.timezone }, {
73
73
  default: t(() => [u("select", {
74
74
  class: o(i(m)),
75
75
  value: r.block.timezone,
@@ -80,7 +80,7 @@ var b = ["value"], x = ["value"], S = ["value"], C = { class: "tpl:grid tpl:grid
80
80
  }, f(e), 9, S)), 64))], 42, x)]),
81
81
  _: 1
82
82
  }, 8, ["label"]),
83
- a(_, { label: i(F).countdown.display }, {
83
+ a(y, { label: i(F).countdown.display }, {
84
84
  default: t(() => [u("div", C, [(n(!0), c(d, null, e(z.value, (e) => (n(), l(g, {
85
85
  key: e.unit,
86
86
  class: "tpl:text-[12px] tpl:text-[var(--tpl-text)]",
@@ -94,7 +94,7 @@ var b = ["value"], x = ["value"], S = ["value"], C = { class: "tpl:grid tpl:grid
94
94
  ]))), 128))])]),
95
95
  _: 1
96
96
  }, 8, ["label"]),
97
- a(_, { label: i(F).countdown.separator }, {
97
+ a(y, { label: i(F).countdown.separator }, {
98
98
  default: t(() => [a(v, {
99
99
  options: L,
100
100
  "model-value": r.block.separator,
@@ -102,7 +102,7 @@ var b = ["value"], x = ["value"], S = ["value"], C = { class: "tpl:grid tpl:grid
102
102
  }, null, 8, ["model-value"])]),
103
103
  _: 1
104
104
  }, 8, ["label"]),
105
- a(_, { label: i(F).countdown.fontFamily }, {
105
+ a(y, { label: i(F).countdown.fontFamily }, {
106
106
  default: t(() => [u("select", {
107
107
  class: o(i(m)),
108
108
  value: r.block.fontFamily || "",
@@ -113,8 +113,8 @@ var b = ["value"], x = ["value"], S = ["value"], C = { class: "tpl:grid tpl:grid
113
113
  }, f(e.label), 9, E))), 128))], 42, w)]),
114
114
  _: 1
115
115
  }, 8, ["label"]),
116
- u("div", D, [a(_, { label: i(F).countdown.digitFontSize }, {
117
- default: t(() => [a(y, {
116
+ u("div", D, [a(y, { label: i(F).countdown.digitFontSize }, {
117
+ default: t(() => [a(_, {
118
118
  "model-value": r.block.digitFontSize,
119
119
  min: 12,
120
120
  max: 72,
@@ -122,8 +122,8 @@ var b = ["value"], x = ["value"], S = ["value"], C = { class: "tpl:grid tpl:grid
122
122
  "onUpdate:modelValue": p[4] ||= (e) => B("digitFontSize", e)
123
123
  }, null, 8, ["model-value"])]),
124
124
  _: 1
125
- }, 8, ["label"]), a(_, { label: i(F).countdown.labelFontSize }, {
126
- default: t(() => [a(y, {
125
+ }, 8, ["label"]), a(y, { label: i(F).countdown.labelFontSize }, {
126
+ default: t(() => [a(_, {
127
127
  "model-value": r.block.labelFontSize,
128
128
  min: 8,
129
129
  max: 24,
@@ -132,27 +132,27 @@ var b = ["value"], x = ["value"], S = ["value"], C = { class: "tpl:grid tpl:grid
132
132
  }, null, 8, ["model-value"])]),
133
133
  _: 1
134
134
  }, 8, ["label"])]),
135
- u("div", O, [a(_, { label: i(F).countdown.digitColor }, {
135
+ u("div", O, [a(y, { label: i(F).countdown.digitColor }, {
136
136
  default: t(() => [a(h, {
137
137
  "model-value": r.block.digitColor,
138
138
  "onUpdate:modelValue": p[6] ||= (e) => B("digitColor", e)
139
139
  }, null, 8, ["model-value"])]),
140
140
  _: 1
141
- }, 8, ["label"]), a(_, { label: i(F).countdown.labelColor }, {
141
+ }, 8, ["label"]), a(y, { label: i(F).countdown.labelColor }, {
142
142
  default: t(() => [a(h, {
143
143
  "model-value": r.block.labelColor,
144
144
  "onUpdate:modelValue": p[7] ||= (e) => B("labelColor", e)
145
145
  }, null, 8, ["model-value"])]),
146
146
  _: 1
147
147
  }, 8, ["label"])]),
148
- a(_, { label: i(F).countdown.background }, {
148
+ a(y, { label: i(F).countdown.background }, {
149
149
  default: t(() => [a(h, {
150
150
  "model-value": r.block.backgroundColor,
151
151
  "onUpdate:modelValue": p[8] ||= (e) => B("backgroundColor", e)
152
152
  }, null, 8, ["model-value"])]),
153
153
  _: 1
154
154
  }, 8, ["label"]),
155
- a(_, { label: i(F).countdown.labels }, {
155
+ a(y, { label: i(F).countdown.labels }, {
156
156
  default: t(() => [u("div", k, [(n(!0), c(d, null, e(z.value, (e) => (n(), c("input", {
157
157
  key: e.unit,
158
158
  type: "text",
@@ -163,7 +163,7 @@ var b = ["value"], x = ["value"], S = ["value"], C = { class: "tpl:grid tpl:grid
163
163
  }, null, 42, A))), 128))])]),
164
164
  _: 1
165
165
  }, 8, ["label"]),
166
- a(_, { label: i(F).countdown.expiry }, {
166
+ a(y, { label: i(F).countdown.expiry }, {
167
167
  default: t(() => [u("input", {
168
168
  type: "text",
169
169
  class: o(i(m)),
@@ -173,7 +173,7 @@ var b = ["value"], x = ["value"], S = ["value"], C = { class: "tpl:grid tpl:grid
173
173
  }, null, 42, j)]),
174
174
  _: 1
175
175
  }, 8, ["label"]),
176
- a(_, { label: i(F).countdown.expiredImageUrl }, {
176
+ a(y, { label: i(F).countdown.expiredImageUrl }, {
177
177
  default: t(() => [u("input", {
178
178
  type: "url",
179
179
  class: o(i(m)),
@@ -195,4 +195,4 @@ var b = ["value"], x = ["value"], S = ["value"], C = { class: "tpl:grid tpl:grid
195
195
  //#endregion
196
196
  export { N as default };
197
197
 
198
- //# sourceMappingURL=CountdownToolbar-BD90NpY1.js.map
198
+ //# sourceMappingURL=CountdownToolbar-CO6xLgYc.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CountdownToolbar-BD90NpY1.js","names":[],"sources":["../../../src/components/toolbar/CountdownToolbar.vue","../../../src/components/toolbar/CountdownToolbar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport FieldRow from \"./FieldRow.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass } from \"../../constants/styleConstants\";\nimport type { CountdownBlock } from \"@templatical/types\";\nimport { computed } from \"vue\";\n\ndefineProps<{\n block: CountdownBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<CountdownBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst TIMEZONES = [\n \"UTC\",\n \"America/New_York\",\n \"America/Chicago\",\n \"America/Denver\",\n \"America/Los_Angeles\",\n \"Europe/London\",\n \"Europe/Berlin\",\n \"Europe/Paris\",\n \"Europe/Moscow\",\n \"Asia/Dubai\",\n \"Asia/Kolkata\",\n \"Asia/Shanghai\",\n \"Asia/Tokyo\",\n \"Australia/Sydney\",\n \"Pacific/Auckland\",\n];\n\nconst SEPARATORS = [\n { value: \":\", label: \":\" },\n { value: \"-\", label: \"-\" },\n { value: \" \", label: \"␣\" },\n];\n\ntype UnitKey = \"Days\" | \"Hours\" | \"Minutes\" | \"Seconds\";\nconst UNITS: UnitKey[] = [\"Days\", \"Hours\", \"Minutes\", \"Seconds\"];\n\nconst unitItems = computed(() =>\n UNITS.map((unit) => ({\n unit,\n showKey: `show${unit}` as const,\n labelKey: `label${unit}` as const,\n translationKey: unit.toLowerCase() as\n \"days\" | \"hours\" | \"minutes\" | \"seconds\",\n })),\n);\n\nfunction updateField(field: keyof CountdownBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<CountdownBlock>);\n}\n</script>\n\n<template>\n <FieldRow :label=\"t.countdown.targetDate\">\n <input\n type=\"datetime-local\"\n :class=\"inputClass\"\n :value=\"block.targetDate\"\n @input=\"\n updateField('targetDate', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.timezone\">\n <select\n :class=\"inputClass\"\n :value=\"block.timezone\"\n @change=\"\n updateField('timezone', ($event.target as HTMLSelectElement).value)\n \"\n >\n <option v-for=\"tz in TIMEZONES\" :key=\"tz\" :value=\"tz\">{{ tz }}</option>\n </select>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.display\">\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <ToggleSwitch\n v-for=\"item in unitItems\"\n :key=\"item.unit\"\n class=\"tpl:text-[12px] tpl:text-[var(--tpl-text)]\"\n :model-value=\"block[item.showKey]\"\n :label=\"t.countdown[item.translationKey]\"\n @update:model-value=\"updateField(item.showKey, $event)\"\n />\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.separator\">\n <SlidingPillSelect\n :options=\"SEPARATORS\"\n :model-value=\"block.separator\"\n @update:model-value=\"updateField('separator', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.fontFamily\">\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.countdown.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </FieldRow>\n\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <FieldRow :label=\"t.countdown.digitFontSize\">\n <NumberWithSuffix\n :model-value=\"block.digitFontSize\"\n :min=\"12\"\n :max=\"72\"\n suffix=\"px\"\n @update:model-value=\"updateField('digitFontSize', $event)\"\n />\n </FieldRow>\n <FieldRow :label=\"t.countdown.labelFontSize\">\n <NumberWithSuffix\n :model-value=\"block.labelFontSize\"\n :min=\"8\"\n :max=\"24\"\n suffix=\"px\"\n @update:model-value=\"updateField('labelFontSize', $event)\"\n />\n </FieldRow>\n </div>\n\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <FieldRow :label=\"t.countdown.digitColor\">\n <ColorPicker\n :model-value=\"block.digitColor\"\n @update:model-value=\"updateField('digitColor', $event)\"\n />\n </FieldRow>\n <FieldRow :label=\"t.countdown.labelColor\">\n <ColorPicker\n :model-value=\"block.labelColor\"\n @update:model-value=\"updateField('labelColor', $event)\"\n />\n </FieldRow>\n </div>\n\n <FieldRow :label=\"t.countdown.background\">\n <ColorPicker\n :model-value=\"block.backgroundColor\"\n @update:model-value=\"updateField('backgroundColor', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.labels\">\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <input\n v-for=\"item in unitItems\"\n :key=\"item.unit\"\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block[item.labelKey]\"\n :placeholder=\"t.countdown[item.translationKey]\"\n @input=\"\n updateField(item.labelKey, ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.expiry\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.expiredMessage\"\n :placeholder=\"t.countdown.expiredMessagePlaceholder\"\n @input=\"\n updateField('expiredMessage', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.expiredImageUrl\">\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.expiredImageUrl\"\n placeholder=\"https://...\"\n @input=\"\n updateField(\n 'expiredImageUrl',\n ($event.target as HTMLInputElement).value,\n )\n \"\n />\n </FieldRow>\n\n <ToggleSwitch\n class=\"tpl:mb-3.5 tpl:text-[12px] tpl:text-[var(--tpl-text)]\"\n :model-value=\"block.hideOnExpiry\"\n :label=\"t.countdown.hideOnExpiry\"\n @update:model-value=\"updateField('hideOnExpiry', $event)\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport FieldRow from \"./FieldRow.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass } from \"../../constants/styleConstants\";\nimport type { CountdownBlock } from \"@templatical/types\";\nimport { computed } from \"vue\";\n\ndefineProps<{\n block: CountdownBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<CountdownBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst TIMEZONES = [\n \"UTC\",\n \"America/New_York\",\n \"America/Chicago\",\n \"America/Denver\",\n \"America/Los_Angeles\",\n \"Europe/London\",\n \"Europe/Berlin\",\n \"Europe/Paris\",\n \"Europe/Moscow\",\n \"Asia/Dubai\",\n \"Asia/Kolkata\",\n \"Asia/Shanghai\",\n \"Asia/Tokyo\",\n \"Australia/Sydney\",\n \"Pacific/Auckland\",\n];\n\nconst SEPARATORS = [\n { value: \":\", label: \":\" },\n { value: \"-\", label: \"-\" },\n { value: \" \", label: \"␣\" },\n];\n\ntype UnitKey = \"Days\" | \"Hours\" | \"Minutes\" | \"Seconds\";\nconst UNITS: UnitKey[] = [\"Days\", \"Hours\", \"Minutes\", \"Seconds\"];\n\nconst unitItems = computed(() =>\n UNITS.map((unit) => ({\n unit,\n showKey: `show${unit}` as const,\n labelKey: `label${unit}` as const,\n translationKey: unit.toLowerCase() as\n \"days\" | \"hours\" | \"minutes\" | \"seconds\",\n })),\n);\n\nfunction updateField(field: keyof CountdownBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<CountdownBlock>);\n}\n</script>\n\n<template>\n <FieldRow :label=\"t.countdown.targetDate\">\n <input\n type=\"datetime-local\"\n :class=\"inputClass\"\n :value=\"block.targetDate\"\n @input=\"\n updateField('targetDate', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.timezone\">\n <select\n :class=\"inputClass\"\n :value=\"block.timezone\"\n @change=\"\n updateField('timezone', ($event.target as HTMLSelectElement).value)\n \"\n >\n <option v-for=\"tz in TIMEZONES\" :key=\"tz\" :value=\"tz\">{{ tz }}</option>\n </select>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.display\">\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <ToggleSwitch\n v-for=\"item in unitItems\"\n :key=\"item.unit\"\n class=\"tpl:text-[12px] tpl:text-[var(--tpl-text)]\"\n :model-value=\"block[item.showKey]\"\n :label=\"t.countdown[item.translationKey]\"\n @update:model-value=\"updateField(item.showKey, $event)\"\n />\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.separator\">\n <SlidingPillSelect\n :options=\"SEPARATORS\"\n :model-value=\"block.separator\"\n @update:model-value=\"updateField('separator', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.fontFamily\">\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.countdown.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </FieldRow>\n\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <FieldRow :label=\"t.countdown.digitFontSize\">\n <NumberWithSuffix\n :model-value=\"block.digitFontSize\"\n :min=\"12\"\n :max=\"72\"\n suffix=\"px\"\n @update:model-value=\"updateField('digitFontSize', $event)\"\n />\n </FieldRow>\n <FieldRow :label=\"t.countdown.labelFontSize\">\n <NumberWithSuffix\n :model-value=\"block.labelFontSize\"\n :min=\"8\"\n :max=\"24\"\n suffix=\"px\"\n @update:model-value=\"updateField('labelFontSize', $event)\"\n />\n </FieldRow>\n </div>\n\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <FieldRow :label=\"t.countdown.digitColor\">\n <ColorPicker\n :model-value=\"block.digitColor\"\n @update:model-value=\"updateField('digitColor', $event)\"\n />\n </FieldRow>\n <FieldRow :label=\"t.countdown.labelColor\">\n <ColorPicker\n :model-value=\"block.labelColor\"\n @update:model-value=\"updateField('labelColor', $event)\"\n />\n </FieldRow>\n </div>\n\n <FieldRow :label=\"t.countdown.background\">\n <ColorPicker\n :model-value=\"block.backgroundColor\"\n @update:model-value=\"updateField('backgroundColor', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.labels\">\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <input\n v-for=\"item in unitItems\"\n :key=\"item.unit\"\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block[item.labelKey]\"\n :placeholder=\"t.countdown[item.translationKey]\"\n @input=\"\n updateField(item.labelKey, ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.expiry\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.expiredMessage\"\n :placeholder=\"t.countdown.expiredMessagePlaceholder\"\n @input=\"\n updateField('expiredMessage', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.expiredImageUrl\">\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.expiredImageUrl\"\n placeholder=\"https://...\"\n @input=\"\n updateField(\n 'expiredImageUrl',\n ($event.target as HTMLInputElement).value,\n )\n \"\n />\n </FieldRow>\n\n <ToggleSwitch\n class=\"tpl:mb-3.5 tpl:text-[12px] tpl:text-[var(--tpl-text)]\"\n :model-value=\"block.hideOnExpiry\"\n :label=\"t.countdown.hideOnExpiry\"\n @update:model-value=\"updateField('hideOnExpiry', $event)\"\n />\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"CountdownToolbar-CO6xLgYc.js","names":[],"sources":["../../../src/components/toolbar/CountdownToolbar.vue","../../../src/components/toolbar/CountdownToolbar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport FieldRow from \"./FieldRow.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass } from \"../../constants/styleConstants\";\nimport type { CountdownBlock } from \"@templatical/types\";\nimport { computed } from \"vue\";\n\ndefineProps<{\n block: CountdownBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<CountdownBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst TIMEZONES = [\n \"UTC\",\n \"America/New_York\",\n \"America/Chicago\",\n \"America/Denver\",\n \"America/Los_Angeles\",\n \"Europe/London\",\n \"Europe/Berlin\",\n \"Europe/Paris\",\n \"Europe/Moscow\",\n \"Asia/Dubai\",\n \"Asia/Kolkata\",\n \"Asia/Shanghai\",\n \"Asia/Tokyo\",\n \"Australia/Sydney\",\n \"Pacific/Auckland\",\n];\n\nconst SEPARATORS = [\n { value: \":\", label: \":\" },\n { value: \"-\", label: \"-\" },\n { value: \" \", label: \"␣\" },\n];\n\ntype UnitKey = \"Days\" | \"Hours\" | \"Minutes\" | \"Seconds\";\nconst UNITS: UnitKey[] = [\"Days\", \"Hours\", \"Minutes\", \"Seconds\"];\n\nconst unitItems = computed(() =>\n UNITS.map((unit) => ({\n unit,\n showKey: `show${unit}` as const,\n labelKey: `label${unit}` as const,\n translationKey: unit.toLowerCase() as\n \"days\" | \"hours\" | \"minutes\" | \"seconds\",\n })),\n);\n\nfunction updateField(field: keyof CountdownBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<CountdownBlock>);\n}\n</script>\n\n<template>\n <FieldRow :label=\"t.countdown.targetDate\">\n <input\n type=\"datetime-local\"\n :class=\"inputClass\"\n :value=\"block.targetDate\"\n @input=\"\n updateField('targetDate', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.timezone\">\n <select\n :class=\"inputClass\"\n :value=\"block.timezone\"\n @change=\"\n updateField('timezone', ($event.target as HTMLSelectElement).value)\n \"\n >\n <option v-for=\"tz in TIMEZONES\" :key=\"tz\" :value=\"tz\">{{ tz }}</option>\n </select>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.display\">\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <ToggleSwitch\n v-for=\"item in unitItems\"\n :key=\"item.unit\"\n class=\"tpl:text-[12px] tpl:text-[var(--tpl-text)]\"\n :model-value=\"block[item.showKey]\"\n :label=\"t.countdown[item.translationKey]\"\n @update:model-value=\"updateField(item.showKey, $event)\"\n />\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.separator\">\n <SlidingPillSelect\n :options=\"SEPARATORS\"\n :model-value=\"block.separator\"\n @update:model-value=\"updateField('separator', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.fontFamily\">\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.countdown.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </FieldRow>\n\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <FieldRow :label=\"t.countdown.digitFontSize\">\n <NumberWithSuffix\n :model-value=\"block.digitFontSize\"\n :min=\"12\"\n :max=\"72\"\n suffix=\"px\"\n @update:model-value=\"updateField('digitFontSize', $event)\"\n />\n </FieldRow>\n <FieldRow :label=\"t.countdown.labelFontSize\">\n <NumberWithSuffix\n :model-value=\"block.labelFontSize\"\n :min=\"8\"\n :max=\"24\"\n suffix=\"px\"\n @update:model-value=\"updateField('labelFontSize', $event)\"\n />\n </FieldRow>\n </div>\n\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <FieldRow :label=\"t.countdown.digitColor\">\n <ColorPicker\n :model-value=\"block.digitColor\"\n @update:model-value=\"updateField('digitColor', $event)\"\n />\n </FieldRow>\n <FieldRow :label=\"t.countdown.labelColor\">\n <ColorPicker\n :model-value=\"block.labelColor\"\n @update:model-value=\"updateField('labelColor', $event)\"\n />\n </FieldRow>\n </div>\n\n <FieldRow :label=\"t.countdown.background\">\n <ColorPicker\n :model-value=\"block.backgroundColor\"\n @update:model-value=\"updateField('backgroundColor', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.labels\">\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <input\n v-for=\"item in unitItems\"\n :key=\"item.unit\"\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block[item.labelKey]\"\n :placeholder=\"t.countdown[item.translationKey]\"\n @input=\"\n updateField(item.labelKey, ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.expiry\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.expiredMessage\"\n :placeholder=\"t.countdown.expiredMessagePlaceholder\"\n @input=\"\n updateField('expiredMessage', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.expiredImageUrl\">\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.expiredImageUrl\"\n placeholder=\"https://...\"\n @input=\"\n updateField(\n 'expiredImageUrl',\n ($event.target as HTMLInputElement).value,\n )\n \"\n />\n </FieldRow>\n\n <ToggleSwitch\n class=\"tpl:mb-3.5 tpl:text-[12px] tpl:text-[var(--tpl-text)]\"\n :model-value=\"block.hideOnExpiry\"\n :label=\"t.countdown.hideOnExpiry\"\n @update:model-value=\"updateField('hideOnExpiry', $event)\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport ColorPicker from \"../ColorPicker.vue\";\nimport SlidingPillSelect from \"../SlidingPillSelect.vue\";\nimport FieldRow from \"./FieldRow.vue\";\nimport ToggleSwitch from \"../ToggleSwitch.vue\";\nimport NumberWithSuffix from \"./NumberWithSuffix.vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { inputClass } from \"../../constants/styleConstants\";\nimport type { CountdownBlock } from \"@templatical/types\";\nimport { computed } from \"vue\";\n\ndefineProps<{\n block: CountdownBlock;\n fontFamilies: Array<{ value: string; label: string }>;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update\", updates: Partial<CountdownBlock>): void;\n}>();\n\nconst { t } = useI18n();\n\nconst TIMEZONES = [\n \"UTC\",\n \"America/New_York\",\n \"America/Chicago\",\n \"America/Denver\",\n \"America/Los_Angeles\",\n \"Europe/London\",\n \"Europe/Berlin\",\n \"Europe/Paris\",\n \"Europe/Moscow\",\n \"Asia/Dubai\",\n \"Asia/Kolkata\",\n \"Asia/Shanghai\",\n \"Asia/Tokyo\",\n \"Australia/Sydney\",\n \"Pacific/Auckland\",\n];\n\nconst SEPARATORS = [\n { value: \":\", label: \":\" },\n { value: \"-\", label: \"-\" },\n { value: \" \", label: \"␣\" },\n];\n\ntype UnitKey = \"Days\" | \"Hours\" | \"Minutes\" | \"Seconds\";\nconst UNITS: UnitKey[] = [\"Days\", \"Hours\", \"Minutes\", \"Seconds\"];\n\nconst unitItems = computed(() =>\n UNITS.map((unit) => ({\n unit,\n showKey: `show${unit}` as const,\n labelKey: `label${unit}` as const,\n translationKey: unit.toLowerCase() as\n \"days\" | \"hours\" | \"minutes\" | \"seconds\",\n })),\n);\n\nfunction updateField(field: keyof CountdownBlock, value: unknown): void {\n emit(\"update\", { [field]: value } as Partial<CountdownBlock>);\n}\n</script>\n\n<template>\n <FieldRow :label=\"t.countdown.targetDate\">\n <input\n type=\"datetime-local\"\n :class=\"inputClass\"\n :value=\"block.targetDate\"\n @input=\"\n updateField('targetDate', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.timezone\">\n <select\n :class=\"inputClass\"\n :value=\"block.timezone\"\n @change=\"\n updateField('timezone', ($event.target as HTMLSelectElement).value)\n \"\n >\n <option v-for=\"tz in TIMEZONES\" :key=\"tz\" :value=\"tz\">{{ tz }}</option>\n </select>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.display\">\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <ToggleSwitch\n v-for=\"item in unitItems\"\n :key=\"item.unit\"\n class=\"tpl:text-[12px] tpl:text-[var(--tpl-text)]\"\n :model-value=\"block[item.showKey]\"\n :label=\"t.countdown[item.translationKey]\"\n @update:model-value=\"updateField(item.showKey, $event)\"\n />\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.separator\">\n <SlidingPillSelect\n :options=\"SEPARATORS\"\n :model-value=\"block.separator\"\n @update:model-value=\"updateField('separator', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.fontFamily\">\n <select\n :class=\"inputClass\"\n :value=\"block.fontFamily || ''\"\n @change=\"\n updateField(\n 'fontFamily',\n ($event.target as HTMLSelectElement).value || undefined,\n )\n \"\n >\n <option value=\"\">{{ t.countdown.inheritFont }}</option>\n <option\n v-for=\"font in fontFamilies\"\n :key=\"font.value\"\n :value=\"font.value\"\n >\n {{ font.label }}\n </option>\n </select>\n </FieldRow>\n\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <FieldRow :label=\"t.countdown.digitFontSize\">\n <NumberWithSuffix\n :model-value=\"block.digitFontSize\"\n :min=\"12\"\n :max=\"72\"\n suffix=\"px\"\n @update:model-value=\"updateField('digitFontSize', $event)\"\n />\n </FieldRow>\n <FieldRow :label=\"t.countdown.labelFontSize\">\n <NumberWithSuffix\n :model-value=\"block.labelFontSize\"\n :min=\"8\"\n :max=\"24\"\n suffix=\"px\"\n @update:model-value=\"updateField('labelFontSize', $event)\"\n />\n </FieldRow>\n </div>\n\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-3\">\n <FieldRow :label=\"t.countdown.digitColor\">\n <ColorPicker\n :model-value=\"block.digitColor\"\n @update:model-value=\"updateField('digitColor', $event)\"\n />\n </FieldRow>\n <FieldRow :label=\"t.countdown.labelColor\">\n <ColorPicker\n :model-value=\"block.labelColor\"\n @update:model-value=\"updateField('labelColor', $event)\"\n />\n </FieldRow>\n </div>\n\n <FieldRow :label=\"t.countdown.background\">\n <ColorPicker\n :model-value=\"block.backgroundColor\"\n @update:model-value=\"updateField('backgroundColor', $event)\"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.labels\">\n <div class=\"tpl:grid tpl:grid-cols-2 tpl:gap-2\">\n <input\n v-for=\"item in unitItems\"\n :key=\"item.unit\"\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block[item.labelKey]\"\n :placeholder=\"t.countdown[item.translationKey]\"\n @input=\"\n updateField(item.labelKey, ($event.target as HTMLInputElement).value)\n \"\n />\n </div>\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.expiry\">\n <input\n type=\"text\"\n :class=\"inputClass\"\n :value=\"block.expiredMessage\"\n :placeholder=\"t.countdown.expiredMessagePlaceholder\"\n @input=\"\n updateField('expiredMessage', ($event.target as HTMLInputElement).value)\n \"\n />\n </FieldRow>\n\n <FieldRow :label=\"t.countdown.expiredImageUrl\">\n <input\n type=\"url\"\n :class=\"inputClass\"\n :value=\"block.expiredImageUrl\"\n placeholder=\"https://...\"\n @input=\"\n updateField(\n 'expiredImageUrl',\n ($event.target as HTMLInputElement).value,\n )\n \"\n />\n </FieldRow>\n\n <ToggleSwitch\n class=\"tpl:mb-3.5 tpl:text-[12px] tpl:text-[var(--tpl-text)]\"\n :model-value=\"block.hideOnExpiry\"\n :label=\"t.countdown.hideOnExpiry\"\n @update:model-value=\"updateField('hideOnExpiry', $event)\"\n />\n</template>\n"],"mappings":"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"}
@@ -5,7 +5,7 @@ import { Mt as ce, St as le, n as h, pt as g, s as ue, xt as _ } from "./icons-B
5
5
  import { t as de } from "./LoadingTrack-Cz1sT0wP.js";
6
6
  import { t as v } from "./_plugin-vue_export-helper-B3ysoDQm.js";
7
7
  import { n as fe } from "./useCloudI18n-D0AoLV0a.js";
8
- import { g as pe } from "./cloud-C29LPXYn.js";
8
+ import { g as pe } from "./cloud-CITqEHEc.js";
9
9
  //#region src/cloud/components/DesignReferenceSidebar.vue?vue&type=script&setup=true&lang.ts
10
10
  var me = {
11
11
  key: 0,
@@ -220,4 +220,4 @@ var me = {
220
220
  //#endregion
221
221
  export { L as default };
222
222
 
223
- //# sourceMappingURL=DesignReferenceSidebar-DY-laxO5.js.map
223
+ //# sourceMappingURL=DesignReferenceSidebar-DRGqm33h.js.map