@templatical/editor 0.20.0 → 0.21.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 (186) hide show
  1. package/README.md +1 -0
  2. package/dist/{AiChatSidebar-ChrE6pUq.js → AiChatSidebar-K_AehWc-.js} +9 -9
  3. package/dist/{AiFeatureMenu-DkQ9HhQ6.js → AiFeatureMenu-CPdNpHpZ.js} +6 -6
  4. package/dist/{BlockIssueBadge-B4QjePax.js → BlockIssueBadge-Dmj9rVra.js} +4 -4
  5. package/dist/{CloudEditor-CfX-aMiV.js → CloudEditor-3a7mqMhe.js} +263 -315
  6. package/dist/{CollaboratorBar-DTibhQ1j.js → CollaboratorBar-Ds0YZWzV.js} +3 -3
  7. package/dist/{ColorPicker-4vDYhdGe.js → ColorPicker-s9GHXjZs.js} +4 -4
  8. package/dist/CommentsSidebar-DvNEdugN.js +435 -0
  9. package/dist/{CountdownBlock-Bn5w7C-p.js → CountdownBlock-BUHYIAe9.js} +1 -1
  10. package/dist/{CountdownToolbar-D7yQqnA2.js → CountdownToolbar-DPRcv3Fu.js} +3 -4
  11. package/dist/{DesignReferenceSidebar-DnO6ZdwS.js → DesignReferenceSidebar-DNmK3dxp.js} +8 -8
  12. package/dist/{IssuesPanel-X_vkNGrs.js → IssuesPanel-CEnEGFtd.js} +7 -7
  13. package/dist/{MergeTagSuggestion-DCuaSOPQ.js → MergeTagSuggestion-DMe4rqn0.js} +2 -2
  14. package/dist/NumberWithSuffix-CCPN_oCh.js +121 -0
  15. package/dist/{ParagraphEditor-C5ixOIYb.js → ParagraphEditor-CkwBirNl.js} +47 -47
  16. package/dist/{RichTextEditorContent-DwhDnMsG.js → RichTextEditorContent-FbnWsh46.js} +6 -6
  17. package/dist/SaveBlockDialog-Cc5BSUBx.js +264 -0
  18. package/dist/SavedBlockPreviewCanvas-ubttfmKr.js +109 -0
  19. package/dist/SavedBlocksBrowserModal-LkY_U3lY.js +323 -0
  20. package/dist/SavedBlocksPanels-CET0zvH6.js +36 -0
  21. package/dist/SavedBlocksPickBar-poGS4PoI.js +52 -0
  22. package/dist/{SnapshotHistory-DaVueFv6.js → SnapshotHistory-BvF5IXn1.js} +8 -8
  23. package/dist/{TemplateScoringPanel-B0UHoTLx.js → TemplateScoringPanel-ifGGGWs5.js} +14 -14
  24. package/dist/{TestEmailModal-BkauxF7j.js → TestEmailModal-BRaM_eko.js} +4 -4
  25. package/dist/{TitleEditor-n6WDVDjW.js → TitleEditor-Bhgzh-iQ.js} +11 -11
  26. package/dist/{TplModal-BDDyvT-C.js → TplModal-BA-Zsg0O.js} +3 -3
  27. package/dist/{blockTypeIcons-4D0aFXNg.js → blockTypeIcons-B1-Yzp-a.js} +6 -6
  28. package/dist/bundle-stats.json +8 -8
  29. package/dist/{cdn/chunks/ca-DbWoy4rG.js → ca-BBgad6y5.js} +50 -5
  30. package/dist/cdn/chunks/{AiFeatureMenu-CIBHAGSz.js → AiFeatureMenu--PdArSnv.js} +7 -7
  31. package/dist/cdn/chunks/{AiFeatureMenu-CIBHAGSz.js.map → AiFeatureMenu--PdArSnv.js.map} +1 -1
  32. package/dist/cdn/chunks/{BlockIssueBadge-CxMiLo3p.js → BlockIssueBadge-DMSd0SIl.js} +5 -5
  33. package/dist/cdn/chunks/{BlockIssueBadge-CxMiLo3p.js.map → BlockIssueBadge-DMSd0SIl.js.map} +1 -1
  34. package/dist/cdn/chunks/{CloudEditor-CEa3Xgqh.js → CloudEditor-DnTfI5GU.js} +270 -322
  35. package/dist/cdn/chunks/CloudEditor-DnTfI5GU.js.map +1 -0
  36. package/dist/cdn/chunks/{CollaboratorBar-Bk1gD02G.js → CollaboratorBar-CBqd7ldq.js} +3 -3
  37. package/dist/cdn/chunks/{CollaboratorBar-Bk1gD02G.js.map → CollaboratorBar-CBqd7ldq.js.map} +1 -1
  38. package/dist/cdn/chunks/{ColorPicker-ll8pynws.js → ColorPicker-DCdWP8YB.js} +161 -161
  39. package/dist/cdn/chunks/{ColorPicker-ll8pynws.js.map → ColorPicker-DCdWP8YB.js.map} +1 -1
  40. package/dist/cdn/chunks/{CountdownBlock-eGUE7hhw.js → CountdownBlock-BDnWMAME.js} +4 -4
  41. package/dist/cdn/chunks/{CountdownBlock-eGUE7hhw.js.map → CountdownBlock-BDnWMAME.js.map} +1 -1
  42. package/dist/cdn/chunks/{CountdownToolbar-C1bkSjUR.js → CountdownToolbar-BbLoo22S.js} +4 -5
  43. package/dist/cdn/chunks/{CountdownToolbar-C1bkSjUR.js.map → CountdownToolbar-BbLoo22S.js.map} +1 -1
  44. package/dist/cdn/chunks/{IssuesPanel-Csasylf_.js → IssuesPanel-CfudrI3T.js} +7 -7
  45. package/dist/cdn/chunks/{IssuesPanel-Csasylf_.js.map → IssuesPanel-CfudrI3T.js.map} +1 -1
  46. package/dist/cdn/chunks/{MergeTagSuggestion-BmMmdcRg.js → MergeTagSuggestion-D7TsECll.js} +2 -2
  47. package/dist/cdn/chunks/{MergeTagSuggestion-BmMmdcRg.js.map → MergeTagSuggestion-D7TsECll.js.map} +1 -1
  48. package/dist/cdn/chunks/NumberWithSuffix-BmMx9_n_.js +123 -0
  49. package/dist/cdn/chunks/NumberWithSuffix-BmMx9_n_.js.map +1 -0
  50. package/dist/cdn/chunks/{ParagraphEditor-v0_XEL-v.js → ParagraphEditor-DPCUSXpx.js} +86 -86
  51. package/dist/cdn/chunks/{ParagraphEditor-v0_XEL-v.js.map → ParagraphEditor-DPCUSXpx.js.map} +1 -1
  52. package/dist/cdn/chunks/{RichTextEditorContent-NQExNsaV.js → RichTextEditorContent-B8dOiv_D.js} +5 -5
  53. package/dist/cdn/chunks/{RichTextEditorContent-NQExNsaV.js.map → RichTextEditorContent-B8dOiv_D.js.map} +1 -1
  54. package/dist/cdn/chunks/SaveBlockDialog-BY3SMnNV.js +259 -0
  55. package/dist/cdn/chunks/SaveBlockDialog-BY3SMnNV.js.map +1 -0
  56. package/dist/cdn/chunks/SavedBlockPreviewCanvas-LpaOcusq.js +110 -0
  57. package/dist/cdn/chunks/SavedBlockPreviewCanvas-LpaOcusq.js.map +1 -0
  58. package/dist/cdn/chunks/SavedBlocksBrowserModal-CfIcmyzF.js +318 -0
  59. package/dist/cdn/chunks/SavedBlocksBrowserModal-CfIcmyzF.js.map +1 -0
  60. package/dist/cdn/chunks/SavedBlocksPanels-kIowCfh_.js +38 -0
  61. package/dist/cdn/chunks/SavedBlocksPanels-kIowCfh_.js.map +1 -0
  62. package/dist/cdn/chunks/SavedBlocksPickBar-WSGe1GiZ.js +52 -0
  63. package/dist/cdn/chunks/SavedBlocksPickBar-WSGe1GiZ.js.map +1 -0
  64. package/dist/cdn/chunks/{TitleEditor-CS-_MzYs.js → TitleEditor-iwd-O9sO.js} +11 -11
  65. package/dist/cdn/chunks/{TitleEditor-CS-_MzYs.js.map → TitleEditor-iwd-O9sO.js.map} +1 -1
  66. package/dist/cdn/chunks/{blockTypeIcons-B0Hjh42a.js → blockTypeIcons-BYz0fgLz.js} +7 -7
  67. package/dist/cdn/chunks/{blockTypeIcons-B0Hjh42a.js.map → blockTypeIcons-BYz0fgLz.js.map} +1 -1
  68. package/dist/{ca-DbWoy4rG.js → cdn/chunks/ca-BBgad6y5.js} +52 -3
  69. package/dist/cdn/chunks/ca-BBgad6y5.js.map +1 -0
  70. package/dist/{de-CTYXVU9v.js → cdn/chunks/de-Bw6fB5JU.js} +2 -24
  71. package/dist/cdn/chunks/{de-CTYXVU9v.js.map → de-Bw6fB5JU.js.map} +1 -1
  72. package/dist/cdn/chunks/{de-CgGOnerg.js → de-D7WbpFBR.js} +50 -3
  73. package/dist/cdn/chunks/de-D7WbpFBR.js.map +1 -0
  74. package/dist/{en-DrUV9xth.js → cdn/chunks/en-BrZ0uGK2.js} +51 -2
  75. package/dist/cdn/chunks/en-BrZ0uGK2.js.map +1 -0
  76. package/dist/{en-BtmIaJeb.js → cdn/chunks/en-DAdHJz7h.js} +2 -24
  77. package/dist/cdn/chunks/{en-BtmIaJeb.js.map → en-DAdHJz7h.js.map} +1 -1
  78. package/dist/{es-DwbKjU7q.js → cdn/chunks/es-tLSLysPs.js} +51 -2
  79. package/dist/cdn/chunks/es-tLSLysPs.js.map +1 -0
  80. package/dist/cdn/chunks/{extensions-Di3hdIRT.js → extensions-DeJh3E9b.js} +26 -26
  81. package/dist/cdn/chunks/{extensions-Di3hdIRT.js.map → extensions-DeJh3E9b.js.map} +1 -1
  82. package/dist/cdn/chunks/{features-CLIZ7Q-Y.js → features-pLfGJWIB.js} +1118 -962
  83. package/dist/cdn/chunks/features-pLfGJWIB.js.map +1 -0
  84. package/dist/cdn/chunks/{icons-GX40Qm2W.js → icons-FLemooQz.js} +16 -16
  85. package/dist/cdn/chunks/{icons-GX40Qm2W.js.map → icons-FLemooQz.js.map} +1 -1
  86. package/dist/cdn/chunks/{media-library-yYAkAagU.js → media-library-mCCZieYO.js} +176 -176
  87. package/dist/cdn/chunks/{media-library-yYAkAagU.js.map → media-library-mCCZieYO.js.map} +1 -1
  88. package/dist/cdn/chunks/{pt-BR-Bcc5Tjed.js → pt-BR-BWFokdTU.js} +50 -3
  89. package/dist/cdn/chunks/pt-BR-BWFokdTU.js.map +1 -0
  90. package/dist/{pt-BR-B2ZvFlRL.js → cdn/chunks/pt-BR-w2sGzHNX.js} +2 -24
  91. package/dist/cdn/chunks/{pt-BR-B2ZvFlRL.js.map → pt-BR-w2sGzHNX.js.map} +1 -1
  92. package/dist/cdn/chunks/quality-BRbHtAk1.js +2180 -0
  93. package/dist/cdn/chunks/{quality-DffYv9j5.js.map → quality-BRbHtAk1.js.map} +1 -1
  94. package/dist/cdn/chunks/{renderer-DpBGiXOg.js → renderer-Yg1XhsBN.js} +17 -17
  95. package/dist/cdn/chunks/{renderer-DpBGiXOg.js.map → renderer-Yg1XhsBN.js.map} +1 -1
  96. package/dist/cdn/chunks/{src-9K6uFiuK.js → src-BDJeYiVE.js} +33 -33
  97. package/dist/cdn/chunks/{src-9K6uFiuK.js.map → src-BDJeYiVE.js.map} +1 -1
  98. package/dist/cdn/chunks/{styles-DHSmjlpX.js → styles-Bp5Jsu9J.js} +1380 -1294
  99. package/dist/cdn/chunks/styles-Bp5Jsu9J.js.map +1 -0
  100. package/dist/cdn/chunks/{tiptap-C2indasq.js → tiptap-BMZ-OENR.js} +2012 -1863
  101. package/dist/cdn/chunks/tiptap-BMZ-OENR.js.map +1 -0
  102. package/dist/cdn/editor.css +1 -1
  103. package/dist/cdn/editor.js +123 -114
  104. package/dist/cdn/editor.js.map +1 -1
  105. package/dist/{check-Bl4J_tYV.js → check-BpQDKiPz.js} +1 -1
  106. package/dist/{chevron-down-COEcl2iP.js → chevron-down-j8GrdTyz.js} +1 -1
  107. package/dist/chevron-up-BACyVhyC.js +7 -0
  108. package/dist/{circle-alert-CvHi5etc.js → circle-alert-CgtE46ND.js} +1 -1
  109. package/dist/{clock-CZZw62e8.js → clock-BzKLLW5L.js} +1 -1
  110. package/dist/{cloud-B-RebwxI.js → cloud-Du7mnkz2.js} +19 -47
  111. package/dist/{createLucideIcon-_peTZQtY.js → createLucideIcon-JWHwTI6N.js} +2 -2
  112. package/dist/{cdn/chunks/de-CTYXVU9v.js → de-Bw6fB5JU.js} +0 -26
  113. package/dist/{de-CgGOnerg.js → de-D7WbpFBR.js} +49 -2
  114. package/dist/{dist-CgTKuIUN.js → dist-BEpCh2mG.js} +1158 -1125
  115. package/dist/{dist-Cx7IUM7J.js → dist-BgPYC_8-.js} +14 -6
  116. package/dist/{dist-BKKo5wPC.js → dist-BkM8A17N.js} +2 -2
  117. package/dist/{dist-CcX5sLpz.js → dist-BuNDAAVp.js} +361 -256
  118. package/dist/{dist-COSN3oDJ.js → dist-CBzCU-a-.js} +2 -2
  119. package/dist/{dist-YSCPD13F.js → dist-CDO52Rxy.js} +2 -2
  120. package/dist/{dist-Dk5AdwoQ.js → dist-CrjzhIxB.js} +2 -2
  121. package/dist/{dist-DXAZUjj3.js → dist-CtcgoC_R.js} +2 -2
  122. package/dist/{dist-D6M5FAY7.js → dist-CuXha2_7.js} +2 -2
  123. package/dist/dist-CweLDZrs.js +5 -0
  124. package/dist/dist-DOsdqAEZ.js +5 -0
  125. package/dist/{dist-8TKf-Jpg.js → dist-DYClLqy8.js} +6 -3
  126. package/dist/{cdn/chunks/en-DrUV9xth.js → en-BrZ0uGK2.js} +49 -4
  127. package/dist/{cdn/chunks/en-BtmIaJeb.js → en-DAdHJz7h.js} +0 -26
  128. package/dist/{cdn/chunks/es-DwbKjU7q.js → es-tLSLysPs.js} +49 -4
  129. package/dist/{extensions-BCA9pyer.js → extensions-ClY5Sh7S.js} +5 -5
  130. package/dist/{image-up-D26rNdfX.js → image-up-CQdtq8ge.js} +1 -1
  131. package/dist/index.d.ts +81 -4
  132. package/dist/{info-CE3JSTqk.js → info-BUJBXoE6.js} +1 -1
  133. package/dist/{keys-DFaIbGR8.js → keys-CHPcp-Nz.js} +2 -2
  134. package/dist/{list-checks-BQsLrtBB.js → list-checks-BGHaCSD5.js} +1 -1
  135. package/dist/{loader-circle-s9Rh5lLd.js → loader-circle-C-Z6kgI6.js} +1 -1
  136. package/dist/{message-circle-CI62d690.js → message-circle-CEm5qckV.js} +1 -1
  137. package/dist/pencil-DdwmkKLb.js +10 -0
  138. package/dist/{pt-BR-Bcc5Tjed.js → pt-BR-BWFokdTU.js} +49 -2
  139. package/dist/{cdn/chunks/pt-BR-B2ZvFlRL.js → pt-BR-w2sGzHNX.js} +0 -26
  140. package/dist/{refresh-cw-DlYfl65Y.js → refresh-cw-maKlg0dl.js} +1 -1
  141. package/dist/{scan-line-CHadrYVb.js → scan-line-BFgesTOY.js} +1 -1
  142. package/dist/{send-CbP40otC.js → send-B70aQDrA.js} +1 -1
  143. package/dist/{shield-check-Bnj9aRoJ.js → shield-check-B7x5OKt5.js} +1 -1
  144. package/dist/{sparkles-CncWT57a.js → sparkles-DVlm1Ipp.js} +1 -1
  145. package/dist/style.css +1 -1
  146. package/dist/{styles-DkF_-uX8.js → styles-DIDAJdkj.js} +1237 -1151
  147. package/dist/templatical-editor.js +134 -125
  148. package/dist/{text-align-start-B458vzYw.js → text-align-start-Vay3Kvo2.js} +1 -1
  149. package/dist/{trash-2-DO0yjSU1.js → trash-2-DE8CmxAG.js} +1 -1
  150. package/dist/{triangle-alert-C18_ima6.js → triangle-alert-Djg6IG8N.js} +1 -1
  151. package/dist/{upload-DsCd2zTR.js → upload-BwwE0U6G.js} +1 -1
  152. package/dist/{useCloudI18n-PCDOC2kz.js → useCloudI18n-BmiygEM4.js} +1 -1
  153. package/dist/{useEditorCore-kKUemYY6.js → useEditorCore-Bd9CA4L_.js} +997 -817
  154. package/dist/{useI18n-BNMzZTM5.js → useI18n-D2L9Mkik.js} +1 -1
  155. package/dist/{useMergeTag-DfEY0eK5.js → useMergeTag-BTdDq5DC.js} +1 -1
  156. package/dist/{usePopoverPosition-F9STHRIr.js → usePopoverPosition-BO2LNy5n.js} +1 -1
  157. package/dist/{x-BCFQuE_q.js → x-CkD6A1iB.js} +1 -1
  158. package/package.json +21 -21
  159. package/dist/CommentsSidebar-D-o5Stlg.js +0 -442
  160. package/dist/ModuleBrowserModal-AJt--JPh.js +0 -202
  161. package/dist/ModulePreviewCanvas-BKYOcVCh.js +0 -106
  162. package/dist/NumberWithSuffix-Bf3rNxza.js +0 -89
  163. package/dist/SaveModuleDialog-B4QX9eiv.js +0 -125
  164. package/dist/ToggleSwitch-DUNuUPsZ.js +0 -37
  165. package/dist/cdn/chunks/CloudEditor-CEa3Xgqh.js.map +0 -1
  166. package/dist/cdn/chunks/ModuleBrowserModal-DznWWtBI.js +0 -198
  167. package/dist/cdn/chunks/ModuleBrowserModal-DznWWtBI.js.map +0 -1
  168. package/dist/cdn/chunks/ModulePreviewCanvas-D-h7fr87.js +0 -107
  169. package/dist/cdn/chunks/ModulePreviewCanvas-D-h7fr87.js.map +0 -1
  170. package/dist/cdn/chunks/NumberWithSuffix-Dt-0odUN.js +0 -91
  171. package/dist/cdn/chunks/NumberWithSuffix-Dt-0odUN.js.map +0 -1
  172. package/dist/cdn/chunks/SaveModuleDialog-BpFhTi-k.js +0 -120
  173. package/dist/cdn/chunks/SaveModuleDialog-BpFhTi-k.js.map +0 -1
  174. package/dist/cdn/chunks/ToggleSwitch-OrmX_w18.js +0 -39
  175. package/dist/cdn/chunks/ToggleSwitch-OrmX_w18.js.map +0 -1
  176. package/dist/cdn/chunks/ca-DbWoy4rG.js.map +0 -1
  177. package/dist/cdn/chunks/de-CgGOnerg.js.map +0 -1
  178. package/dist/cdn/chunks/en-DrUV9xth.js.map +0 -1
  179. package/dist/cdn/chunks/es-DwbKjU7q.js.map +0 -1
  180. package/dist/cdn/chunks/features-CLIZ7Q-Y.js.map +0 -1
  181. package/dist/cdn/chunks/pt-BR-Bcc5Tjed.js.map +0 -1
  182. package/dist/cdn/chunks/quality-DffYv9j5.js +0 -2180
  183. package/dist/cdn/chunks/styles-DHSmjlpX.js.map +0 -1
  184. package/dist/cdn/chunks/tiptap-C2indasq.js.map +0 -1
  185. package/dist/dist-BDVm2MrW.js +0 -5
  186. package/dist/dist-DbekZDmJ.js +0 -5
@@ -1 +0,0 @@
1
- {"version":3,"file":"ModuleBrowserModal-DznWWtBI.js","names":[],"sources":["../../../src/cloud/components/ModuleBrowserModal.vue","../../../src/cloud/components/ModuleBrowserModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport TplModal from \"../../components/TplModal.vue\";\nimport { useI18n } from \"../../composables\";\nimport { useCloudI18nStrict } from \"../../composables\";\nimport { blockTypeIcons } from \"../../utils/blockTypeIcons\";\nimport {\n SAVED_MODULES_HEADLESS_KEY,\n EDITOR_KEY,\n requireInject,\n} from \"../../keys\";\nimport type { SavedModule } from \"@templatical/types\";\nimport { Package, Search, Trash2, X } from \"@lucide/vue\";\nimport { computed, defineAsyncComponent, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"insert\", module: SavedModule, insertIndex: number | undefined): void;\n}>();\n\nconst ModulePreviewCanvas = defineAsyncComponent(\n () => import(\"./ModulePreviewCanvas.vue\"),\n);\n\nconst { t } = useI18n();\nconst { t: cloudT } = useCloudI18nStrict();\nconst savedModules = requireInject(\n SAVED_MODULES_HEADLESS_KEY,\n \"ModuleBrowserModal\",\n);\nconst editor = requireInject(EDITOR_KEY, \"ModuleBrowserModal\");\n\nconst searchQuery = ref(\"\");\nconst selectedModuleId = ref<string | null>(null);\nconst confirmDeleteId = ref<string | null>(null);\n// 'end' = append, 'beginning' = index 0, or block id = after that block\nconst insertPosition = ref<string>(\"end\");\n\nconst filteredModules = computed(() => {\n const modules = savedModules.modules.value;\n if (!searchQuery.value) return modules;\n const query = searchQuery.value.toLowerCase();\n return modules.filter((m) => m.name.toLowerCase().includes(query));\n});\n\nconst selectedModule = computed(() => {\n if (!selectedModuleId.value) return null;\n return (\n savedModules.modules.value.find((m) => m.id === selectedModuleId.value) ??\n null\n );\n});\n\ninterface PositionOption {\n value: string;\n label: string;\n}\n\nconst positionOptions = computed<PositionOption[]>(() => {\n const options: PositionOption[] = [\n { value: \"beginning\", label: cloudT.modules.insertAtBeginning },\n ];\n const blocks = editor.content.value.blocks;\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n const typeKey = block.type as keyof typeof t.blocks;\n const label = t.blocks[typeKey] ?? block.type;\n options.push({\n value: block.id,\n label: cloudT.modules.insertAfterBlock.replace(\n \"{block}\",\n `${label} ${i + 1}`,\n ),\n });\n }\n options.push({ value: \"end\", label: cloudT.modules.insertAtEnd });\n return options;\n});\n\nconst resolvedInsertIndex = computed<number | undefined>(() => {\n if (insertPosition.value === \"end\") return undefined;\n if (insertPosition.value === \"beginning\") return 0;\n const blocks = editor.content.value.blocks;\n const idx = blocks.findIndex((b) => b.id === insertPosition.value);\n if (idx !== -1) return idx + 1;\n return undefined;\n});\n\nwatch(\n () => props.visible,\n (visible) => {\n if (visible) {\n searchQuery.value = \"\";\n selectedModuleId.value = null;\n confirmDeleteId.value = null;\n // Default to after selected block, or end\n const selectedId = editor.state.selectedBlockId;\n if (selectedId) {\n const idx = editor.content.value.blocks.findIndex(\n (b) => b.id === selectedId,\n );\n insertPosition.value = idx !== -1 ? selectedId : \"end\";\n } else {\n insertPosition.value = \"end\";\n }\n }\n },\n);\n\nfunction getModuleBlockTypeIcons(\n module: SavedModule,\n): { type: string; icon: unknown }[] {\n const icons: { type: string; icon: unknown }[] = [];\n const seen = new Set<string>();\n for (const block of module.content) {\n if (!seen.has(block.type) && blockTypeIcons[block.type]) {\n seen.add(block.type);\n icons.push({ type: block.type, icon: blockTypeIcons[block.type] });\n }\n if (icons.length >= 5) break;\n }\n return icons;\n}\n\nfunction getRemainingTypeCount(module: SavedModule): number {\n const types = new Set(module.content.map((b) => b.type));\n return Math.max(0, types.size - 5);\n}\n\nasync function handleDelete(moduleId: string): Promise<void> {\n try {\n await savedModules.deleteModule(moduleId);\n if (selectedModuleId.value === moduleId) {\n selectedModuleId.value = null;\n }\n } finally {\n confirmDeleteId.value = null;\n }\n}\n\nfunction handleInsert(): void {\n if (selectedModule.value) {\n emit(\"insert\", selectedModule.value, resolvedInsertIndex.value);\n }\n}\n\nfunction handleClose(): void {\n emit(\"close\");\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Escape\") {\n handleClose();\n }\n if (event.key === \"Enter\" && selectedModule.value) {\n event.preventDefault();\n handleInsert();\n }\n}\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"handleClose\" @keydown=\"handleKeydown\">\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"tpl-module-browser-title\"\n class=\"tpl-scale-in tpl:mx-4 tpl:flex tpl:w-full tpl:max-w-[1000px] tpl:flex-col tpl:rounded-[var(--tpl-radius-lg)]\"\n style=\"\n background-color: var(--tpl-bg-elevated);\n box-shadow: var(--tpl-shadow-xl);\n max-height: 90vh;\n \"\n >\n <!-- Header -->\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:px-5 tpl:py-4 tpl:border-[var(--tpl-border)]\"\n >\n <h3\n id=\"tpl-module-browser-title\"\n class=\"tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.modules.browse }}\n </h3>\n <button\n :aria-label=\"cloudT.modules.close\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-1 tpl:transition-colors tpl:duration-100 tpl:text-[var(--tpl-text-dim)]\"\n @click=\"handleClose\"\n >\n <X :size=\"16\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Body -->\n <div class=\"tpl:flex tpl:min-h-0 tpl:flex-1 tpl:overflow-hidden\">\n <!-- Left panel: module grid -->\n <div\n class=\"tpl:flex tpl:w-[300px] tpl:shrink-0 tpl:flex-col tpl:overflow-hidden\"\n >\n <!-- Search -->\n <div class=\"tpl:px-4 tpl:pt-4 tpl:pb-3\">\n <div class=\"tpl:relative\">\n <Search\n :size=\"14\"\n :stroke-width=\"2\"\n class=\"tpl:pointer-events-none tpl:absolute tpl:left-3 tpl:top-1/2 tpl:-translate-y-1/2 tpl:text-[var(--tpl-text-dim)]\"\n />\n <input\n v-model=\"searchQuery\"\n type=\"text\"\n :placeholder=\"cloudT.modules.search\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:pl-9 tpl:pr-3 tpl:text-sm tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n />\n </div>\n </div>\n\n <!-- Grid -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:px-4 tpl:pb-4\">\n <div\n v-if=\"filteredModules.length > 0\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1\"\n >\n <button\n v-for=\"mod in filteredModules\"\n :key=\"mod.id\"\n type=\"button\"\n :aria-pressed=\"selectedModuleId === mod.id\"\n class=\"tpl:group/card tpl:w-full tpl:cursor-pointer tpl:rounded-[var(--tpl-radius-md)] tpl:border tpl:bg-transparent tpl:px-3 tpl:py-2 tpl:text-left tpl:transition-all tpl:duration-[120ms]\"\n :style=\"{\n borderColor:\n selectedModuleId === mod.id\n ? 'var(--tpl-primary)'\n : 'var(--tpl-border)',\n backgroundColor:\n selectedModuleId === mod.id\n ? 'var(--tpl-primary-light)'\n : 'transparent',\n }\"\n @click=\"selectedModuleId = mod.id\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <span\n class=\"tpl:flex-1 tpl:truncate tpl:text-xs tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ mod.name }}\n </span>\n <span\n class=\"tpl:shrink-0 tpl:rounded-full tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{\n cloudT.modules.blockCount.replace(\n \"{count}\",\n String(mod.content.length),\n )\n }}\n </span>\n </div>\n <div class=\"tpl:mt-1 tpl:flex tpl:items-center tpl:gap-1\">\n <component\n :is=\"icon.icon\"\n v-for=\"icon in getModuleBlockTypeIcons(mod)\"\n :key=\"icon.type\"\n :size=\"14\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <span\n v-if=\"getRemainingTypeCount(mod) > 0\"\n class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n +{{ getRemainingTypeCount(mod) }}\n </span>\n <button\n v-if=\"confirmDeleteId === mod.id\"\n :aria-label=\"cloudT.modules.deleteConfirm\"\n class=\"tpl:ml-auto tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-2 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:transition-colors tpl:duration-100 tpl:border-[var(--tpl-danger)] tpl:text-[var(--tpl-danger)]\"\n style=\"background-color: transparent\"\n @click.stop=\"handleDelete(mod.id)\"\n >\n {{ cloudT.modules.deleteConfirm }}\n </button>\n <button\n v-else\n class=\"tpl-module-delete-btn tpl:ml-auto tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-0.5 tpl:transition-colors tpl:duration-100 tpl:text-[var(--tpl-text-dim)]\"\n :aria-label=\"cloudT.modules.delete\"\n :title=\"cloudT.modules.delete\"\n @click.stop=\"confirmDeleteId = mod.id\"\n >\n <Trash2 :size=\"12\" :stroke-width=\"1.5\" />\n </button>\n </div>\n </button>\n </div>\n\n <!-- Empty state -->\n <div\n v-else\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:py-12\"\n >\n <Package\n :size=\"32\"\n :stroke-width=\"1\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <p class=\"tpl:mt-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\">\n {{\n searchQuery\n ? cloudT.modules.noModules\n : cloudT.modules.noModulesHint\n }}\n </p>\n </div>\n </div>\n </div>\n\n <!-- Right panel: preview -->\n <div\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-hidden tpl:border-l tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-if=\"selectedModule\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-hidden\"\n >\n <!-- Visual preview -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <ModulePreviewCanvas :blocks=\"selectedModule.content\" />\n </div>\n </div>\n\n <!-- Empty preview state -->\n <div\n v-else\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:items-center tpl:justify-center tpl:px-4\"\n >\n <Package\n :size=\"32\"\n :stroke-width=\"1\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <p\n class=\"tpl:mt-2 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ cloudT.modules.selectToPreview }}\n </p>\n </div>\n </div>\n </div>\n\n <!-- Footer -->\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-t tpl:px-5 tpl:py-3 tpl:border-[var(--tpl-border)]\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <label\n class=\"tpl:shrink-0 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ cloudT.modules.insertPosition }}\n </label>\n <select\n v-model=\"insertPosition\"\n class=\"tpl:h-7 tpl:max-w-[220px] tpl:rounded-md tpl:border tpl:px-2 tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n >\n <option\n v-for=\"opt in positionOptions\"\n :key=\"opt.value\"\n :value=\"opt.value\"\n >\n {{ opt.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:flex tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n @click=\"handleClose\"\n >\n {{ cloudT.modules.close }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!selectedModule\"\n @click=\"handleInsert\"\n >\n {{ cloudT.modules.insert }}\n </button>\n </div>\n </div>\n </div>\n </TplModal>\n</template>\n\n<style>\n.tpl-module-delete-btn:hover {\n color: var(--tpl-danger) !important;\n}\n</style>\n","<script setup lang=\"ts\">\nimport TplModal from \"../../components/TplModal.vue\";\nimport { useI18n } from \"../../composables\";\nimport { useCloudI18nStrict } from \"../../composables\";\nimport { blockTypeIcons } from \"../../utils/blockTypeIcons\";\nimport {\n SAVED_MODULES_HEADLESS_KEY,\n EDITOR_KEY,\n requireInject,\n} from \"../../keys\";\nimport type { SavedModule } from \"@templatical/types\";\nimport { Package, Search, Trash2, X } from \"@lucide/vue\";\nimport { computed, defineAsyncComponent, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"insert\", module: SavedModule, insertIndex: number | undefined): void;\n}>();\n\nconst ModulePreviewCanvas = defineAsyncComponent(\n () => import(\"./ModulePreviewCanvas.vue\"),\n);\n\nconst { t } = useI18n();\nconst { t: cloudT } = useCloudI18nStrict();\nconst savedModules = requireInject(\n SAVED_MODULES_HEADLESS_KEY,\n \"ModuleBrowserModal\",\n);\nconst editor = requireInject(EDITOR_KEY, \"ModuleBrowserModal\");\n\nconst searchQuery = ref(\"\");\nconst selectedModuleId = ref<string | null>(null);\nconst confirmDeleteId = ref<string | null>(null);\n// 'end' = append, 'beginning' = index 0, or block id = after that block\nconst insertPosition = ref<string>(\"end\");\n\nconst filteredModules = computed(() => {\n const modules = savedModules.modules.value;\n if (!searchQuery.value) return modules;\n const query = searchQuery.value.toLowerCase();\n return modules.filter((m) => m.name.toLowerCase().includes(query));\n});\n\nconst selectedModule = computed(() => {\n if (!selectedModuleId.value) return null;\n return (\n savedModules.modules.value.find((m) => m.id === selectedModuleId.value) ??\n null\n );\n});\n\ninterface PositionOption {\n value: string;\n label: string;\n}\n\nconst positionOptions = computed<PositionOption[]>(() => {\n const options: PositionOption[] = [\n { value: \"beginning\", label: cloudT.modules.insertAtBeginning },\n ];\n const blocks = editor.content.value.blocks;\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n const typeKey = block.type as keyof typeof t.blocks;\n const label = t.blocks[typeKey] ?? block.type;\n options.push({\n value: block.id,\n label: cloudT.modules.insertAfterBlock.replace(\n \"{block}\",\n `${label} ${i + 1}`,\n ),\n });\n }\n options.push({ value: \"end\", label: cloudT.modules.insertAtEnd });\n return options;\n});\n\nconst resolvedInsertIndex = computed<number | undefined>(() => {\n if (insertPosition.value === \"end\") return undefined;\n if (insertPosition.value === \"beginning\") return 0;\n const blocks = editor.content.value.blocks;\n const idx = blocks.findIndex((b) => b.id === insertPosition.value);\n if (idx !== -1) return idx + 1;\n return undefined;\n});\n\nwatch(\n () => props.visible,\n (visible) => {\n if (visible) {\n searchQuery.value = \"\";\n selectedModuleId.value = null;\n confirmDeleteId.value = null;\n // Default to after selected block, or end\n const selectedId = editor.state.selectedBlockId;\n if (selectedId) {\n const idx = editor.content.value.blocks.findIndex(\n (b) => b.id === selectedId,\n );\n insertPosition.value = idx !== -1 ? selectedId : \"end\";\n } else {\n insertPosition.value = \"end\";\n }\n }\n },\n);\n\nfunction getModuleBlockTypeIcons(\n module: SavedModule,\n): { type: string; icon: unknown }[] {\n const icons: { type: string; icon: unknown }[] = [];\n const seen = new Set<string>();\n for (const block of module.content) {\n if (!seen.has(block.type) && blockTypeIcons[block.type]) {\n seen.add(block.type);\n icons.push({ type: block.type, icon: blockTypeIcons[block.type] });\n }\n if (icons.length >= 5) break;\n }\n return icons;\n}\n\nfunction getRemainingTypeCount(module: SavedModule): number {\n const types = new Set(module.content.map((b) => b.type));\n return Math.max(0, types.size - 5);\n}\n\nasync function handleDelete(moduleId: string): Promise<void> {\n try {\n await savedModules.deleteModule(moduleId);\n if (selectedModuleId.value === moduleId) {\n selectedModuleId.value = null;\n }\n } finally {\n confirmDeleteId.value = null;\n }\n}\n\nfunction handleInsert(): void {\n if (selectedModule.value) {\n emit(\"insert\", selectedModule.value, resolvedInsertIndex.value);\n }\n}\n\nfunction handleClose(): void {\n emit(\"close\");\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Escape\") {\n handleClose();\n }\n if (event.key === \"Enter\" && selectedModule.value) {\n event.preventDefault();\n handleInsert();\n }\n}\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"handleClose\" @keydown=\"handleKeydown\">\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"tpl-module-browser-title\"\n class=\"tpl-scale-in tpl:mx-4 tpl:flex tpl:w-full tpl:max-w-[1000px] tpl:flex-col tpl:rounded-[var(--tpl-radius-lg)]\"\n style=\"\n background-color: var(--tpl-bg-elevated);\n box-shadow: var(--tpl-shadow-xl);\n max-height: 90vh;\n \"\n >\n <!-- Header -->\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-b tpl:px-5 tpl:py-4 tpl:border-[var(--tpl-border)]\"\n >\n <h3\n id=\"tpl-module-browser-title\"\n class=\"tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.modules.browse }}\n </h3>\n <button\n :aria-label=\"cloudT.modules.close\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-1 tpl:transition-colors tpl:duration-100 tpl:text-[var(--tpl-text-dim)]\"\n @click=\"handleClose\"\n >\n <X :size=\"16\" :stroke-width=\"2\" />\n </button>\n </div>\n\n <!-- Body -->\n <div class=\"tpl:flex tpl:min-h-0 tpl:flex-1 tpl:overflow-hidden\">\n <!-- Left panel: module grid -->\n <div\n class=\"tpl:flex tpl:w-[300px] tpl:shrink-0 tpl:flex-col tpl:overflow-hidden\"\n >\n <!-- Search -->\n <div class=\"tpl:px-4 tpl:pt-4 tpl:pb-3\">\n <div class=\"tpl:relative\">\n <Search\n :size=\"14\"\n :stroke-width=\"2\"\n class=\"tpl:pointer-events-none tpl:absolute tpl:left-3 tpl:top-1/2 tpl:-translate-y-1/2 tpl:text-[var(--tpl-text-dim)]\"\n />\n <input\n v-model=\"searchQuery\"\n type=\"text\"\n :placeholder=\"cloudT.modules.search\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:pl-9 tpl:pr-3 tpl:text-sm tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n />\n </div>\n </div>\n\n <!-- Grid -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:px-4 tpl:pb-4\">\n <div\n v-if=\"filteredModules.length > 0\"\n class=\"tpl:flex tpl:flex-col tpl:gap-1\"\n >\n <button\n v-for=\"mod in filteredModules\"\n :key=\"mod.id\"\n type=\"button\"\n :aria-pressed=\"selectedModuleId === mod.id\"\n class=\"tpl:group/card tpl:w-full tpl:cursor-pointer tpl:rounded-[var(--tpl-radius-md)] tpl:border tpl:bg-transparent tpl:px-3 tpl:py-2 tpl:text-left tpl:transition-all tpl:duration-[120ms]\"\n :style=\"{\n borderColor:\n selectedModuleId === mod.id\n ? 'var(--tpl-primary)'\n : 'var(--tpl-border)',\n backgroundColor:\n selectedModuleId === mod.id\n ? 'var(--tpl-primary-light)'\n : 'transparent',\n }\"\n @click=\"selectedModuleId = mod.id\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <span\n class=\"tpl:flex-1 tpl:truncate tpl:text-xs tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ mod.name }}\n </span>\n <span\n class=\"tpl:shrink-0 tpl:rounded-full tpl:px-1.5 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:bg-[var(--tpl-bg-hover)] tpl:text-[var(--tpl-text-muted)]\"\n >\n {{\n cloudT.modules.blockCount.replace(\n \"{count}\",\n String(mod.content.length),\n )\n }}\n </span>\n </div>\n <div class=\"tpl:mt-1 tpl:flex tpl:items-center tpl:gap-1\">\n <component\n :is=\"icon.icon\"\n v-for=\"icon in getModuleBlockTypeIcons(mod)\"\n :key=\"icon.type\"\n :size=\"14\"\n :stroke-width=\"1.5\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <span\n v-if=\"getRemainingTypeCount(mod) > 0\"\n class=\"tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n +{{ getRemainingTypeCount(mod) }}\n </span>\n <button\n v-if=\"confirmDeleteId === mod.id\"\n :aria-label=\"cloudT.modules.deleteConfirm\"\n class=\"tpl:ml-auto tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-2 tpl:py-0.5 tpl:text-[10px] tpl:font-medium tpl:transition-colors tpl:duration-100 tpl:border-[var(--tpl-danger)] tpl:text-[var(--tpl-danger)]\"\n style=\"background-color: transparent\"\n @click.stop=\"handleDelete(mod.id)\"\n >\n {{ cloudT.modules.deleteConfirm }}\n </button>\n <button\n v-else\n class=\"tpl-module-delete-btn tpl:ml-auto tpl:cursor-pointer tpl:rounded-md tpl:border-none tpl:bg-transparent tpl:p-0.5 tpl:transition-colors tpl:duration-100 tpl:text-[var(--tpl-text-dim)]\"\n :aria-label=\"cloudT.modules.delete\"\n :title=\"cloudT.modules.delete\"\n @click.stop=\"confirmDeleteId = mod.id\"\n >\n <Trash2 :size=\"12\" :stroke-width=\"1.5\" />\n </button>\n </div>\n </button>\n </div>\n\n <!-- Empty state -->\n <div\n v-else\n class=\"tpl:flex tpl:flex-col tpl:items-center tpl:justify-center tpl:py-12\"\n >\n <Package\n :size=\"32\"\n :stroke-width=\"1\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <p class=\"tpl:mt-2 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\">\n {{\n searchQuery\n ? cloudT.modules.noModules\n : cloudT.modules.noModulesHint\n }}\n </p>\n </div>\n </div>\n </div>\n\n <!-- Right panel: preview -->\n <div\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-hidden tpl:border-l tpl:border-[var(--tpl-border)]\"\n >\n <div\n v-if=\"selectedModule\"\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:overflow-hidden\"\n >\n <!-- Visual preview -->\n <div class=\"tpl:flex-1 tpl:overflow-y-auto tpl:p-4\">\n <ModulePreviewCanvas :blocks=\"selectedModule.content\" />\n </div>\n </div>\n\n <!-- Empty preview state -->\n <div\n v-else\n class=\"tpl:flex tpl:flex-1 tpl:flex-col tpl:items-center tpl:justify-center tpl:px-4\"\n >\n <Package\n :size=\"32\"\n :stroke-width=\"1\"\n class=\"tpl:text-[var(--tpl-text-dim)]\"\n />\n <p\n class=\"tpl:mt-2 tpl:text-center tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ cloudT.modules.selectToPreview }}\n </p>\n </div>\n </div>\n </div>\n\n <!-- Footer -->\n <div\n class=\"tpl:flex tpl:items-center tpl:justify-between tpl:border-t tpl:px-5 tpl:py-3 tpl:border-[var(--tpl-border)]\"\n >\n <div class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <label\n class=\"tpl:shrink-0 tpl:text-xs tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ cloudT.modules.insertPosition }}\n </label>\n <select\n v-model=\"insertPosition\"\n class=\"tpl:h-7 tpl:max-w-[220px] tpl:rounded-md tpl:border tpl:px-2 tpl:text-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n >\n <option\n v-for=\"opt in positionOptions\"\n :key=\"opt.value\"\n :value=\"opt.value\"\n >\n {{ opt.label }}\n </option>\n </select>\n </div>\n <div class=\"tpl:flex tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n @click=\"handleClose\"\n >\n {{ cloudT.modules.close }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!selectedModule\"\n @click=\"handleInsert\"\n >\n {{ cloudT.modules.insert }}\n </button>\n </div>\n </div>\n </div>\n </TplModal>\n</template>\n\n<style>\n.tpl-module-delete-btn:hover {\n color: var(--tpl-danger) !important;\n}\n</style>\n"],"mappings":"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"}
@@ -1,107 +0,0 @@
1
- import { I as e, L as t, M as n, P as r, T as i, f as a, g as o, it as s, m as c, p as l, st as u, u as d, x as f } from "./draggable-D840DIZB.js";
2
- import { A as p, B as m, I as h, It as g, J as _, Jt as v, M as y, N as b, R as x, U as S, W as C, Y as w, j as T, q as E, z as D } from "./features-CLIZ7Q-Y.js";
3
- //#region src/components/blocks/PreviewSectionBlock.vue?vue&type=script&setup=true&lang.ts
4
- var O = { class: "tpl:w-full" }, k = { class: "tpl:flex tpl:gap-0" }, A = /* @__PURE__ */ f({
5
- name: "PreviewSectionBlock",
6
- __name: "PreviewSectionBlock",
7
- props: {
8
- block: {},
9
- viewport: {}
10
- },
11
- setup(f) {
12
- let A = {
13
- title: h,
14
- paragraph: m,
15
- image: C,
16
- video: p,
17
- button: g,
18
- divider: _,
19
- social: b,
20
- menu: S,
21
- table: T,
22
- spacer: y,
23
- html: E,
24
- custom: w
25
- }, j = f, M = i(v, null), N = a(() => {
26
- switch (j.block.columns) {
27
- case "2": return ["50%", "50%"];
28
- case "3": return [
29
- "33.33%",
30
- "33.33%",
31
- "33.33%"
32
- ];
33
- case "1-2": return ["33.33%", "66.67%"];
34
- case "2-1": return ["66.67%", "33.33%"];
35
- default: return ["100%"];
36
- }
37
- }), P = a(() => {
38
- let e = N.value.length, t = [...j.block.children];
39
- for (; t.length < e;) t.push([]);
40
- return t.slice(0, e);
41
- });
42
- function F(e) {
43
- return P.value[e] || [];
44
- }
45
- function I(e) {
46
- return D(e, M, A);
47
- }
48
- return (i, a) => {
49
- let f = e("PreviewSectionBlock", !0);
50
- return n(), o("div", O, [l("div", k, [(n(!0), o(d, null, r(P.value, (e, i) => (n(), o("div", {
51
- key: i,
52
- style: u({ width: N.value[i] })
53
- }, [(n(!0), o(d, null, r(F(i), (e) => (n(), o("div", {
54
- key: e.id,
55
- style: u(s(x)(e))
56
- }, [e.type === "section" ? (n(), c(f, {
57
- key: 0,
58
- block: e,
59
- viewport: "desktop"
60
- }, null, 8, ["block"])) : (n(), c(t(I(e)), {
61
- key: 1,
62
- block: e,
63
- viewport: "desktop"
64
- }, null, 8, ["block"]))], 4))), 128))], 4))), 128))])]);
65
- };
66
- }
67
- }), j = {
68
- class: "tpl:pointer-events-none tpl:mx-auto tpl:w-[600px] tpl:select-none tpl:rounded-lg",
69
- style: {
70
- "background-color": "var(--tpl-canvas-bg)",
71
- "box-shadow": "var(--tpl-shadow-sm)"
72
- }
73
- }, M = /* @__PURE__ */ f({
74
- __name: "ModulePreviewCanvas",
75
- props: { blocks: {} },
76
- setup(e) {
77
- let a = i(v), l = {
78
- section: A,
79
- title: h,
80
- paragraph: m,
81
- image: C,
82
- video: p,
83
- button: g,
84
- divider: _,
85
- social: b,
86
- menu: S,
87
- table: T,
88
- spacer: y,
89
- html: E,
90
- custom: w
91
- };
92
- function f(e) {
93
- return D(e, a, l);
94
- }
95
- return (i, a) => (n(), o("div", j, [(n(!0), o(d, null, r(e.blocks, (e) => (n(), o("div", {
96
- key: e.id,
97
- style: u(s(x)(e))
98
- }, [(n(), c(t(f(e)), {
99
- block: e,
100
- viewport: "desktop"
101
- }, null, 8, ["block"]))], 4))), 128))]));
102
- }
103
- });
104
- //#endregion
105
- export { M as default };
106
-
107
- //# sourceMappingURL=ModulePreviewCanvas-D-h7fr87.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ModulePreviewCanvas-D-h7fr87.js","names":[],"sources":["../../../src/components/blocks/PreviewSectionBlock.vue","../../../src/components/blocks/PreviewSectionBlock.vue","../../../src/cloud/components/ModulePreviewCanvas.vue","../../../src/cloud/components/ModulePreviewCanvas.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport ButtonBlock from \"./ButtonBlock.vue\";\nimport CustomBlock from \"./CustomBlock.vue\";\nimport DividerBlock from \"./DividerBlock.vue\";\nimport HtmlBlock from \"./HtmlBlock.vue\";\nimport ImageBlock from \"./ImageBlock.vue\";\nimport MenuBlock from \"./MenuBlock.vue\";\nimport SocialIconsBlock from \"./SocialIconsBlock.vue\";\nimport SpacerBlock from \"./SpacerBlock.vue\";\nimport TableBlock from \"./TableBlock.vue\";\nimport TitleBlock from \"./TitleBlock.vue\";\nimport ParagraphBlock from \"./ParagraphBlock.vue\";\nimport VideoBlock from \"./VideoBlock.vue\";\nimport {\n resolveBlockComponent,\n getBlockWrapperStyle,\n} from \"../../utils/blockComponentResolver\";\nimport type {\n Block,\n SectionBlock as SectionBlockType,\n} from \"@templatical/types\";\nimport { computed, inject, type Component } from \"vue\";\nimport { BLOCK_REGISTRY_KEY } from \"../../keys\";\n\nconst previewBlockComponentMap: Record<string, Component> = {\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n video: VideoBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n custom: CustomBlock,\n};\n\nconst props = defineProps<{\n block: SectionBlockType;\n viewport: \"desktop\";\n}>();\n\ndefineOptions({ name: \"PreviewSectionBlock\" });\n\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY, null);\n\nconst columnWidths = computed(() => {\n switch (props.block.columns) {\n case \"2\":\n return [\"50%\", \"50%\"];\n case \"3\":\n return [\"33.33%\", \"33.33%\", \"33.33%\"];\n case \"1-2\":\n return [\"33.33%\", \"66.67%\"];\n case \"2-1\":\n return [\"66.67%\", \"33.33%\"];\n default:\n return [\"100%\"];\n }\n});\n\nconst columns = computed(() => {\n const count = columnWidths.value.length;\n const children = [...props.block.children];\n while (children.length < count) {\n children.push([]);\n }\n return children.slice(0, count);\n});\n\nfunction getColumnBlocks(colIndex: number): Block[] {\n return columns.value[colIndex] || [];\n}\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, previewBlockComponentMap);\n}\n</script>\n\n<template>\n <div class=\"tpl:w-full\">\n <div class=\"tpl:flex tpl:gap-0\">\n <div\n v-for=\"(_, colIndex) in columns\"\n :key=\"colIndex\"\n :style=\"{ width: columnWidths[colIndex] }\"\n >\n <div\n v-for=\"childBlock in getColumnBlocks(colIndex)\"\n :key=\"childBlock.id\"\n :style=\"getBlockWrapperStyle(childBlock)\"\n >\n <!-- Recursive self-reference for nested sections -->\n <PreviewSectionBlock\n v-if=\"childBlock.type === 'section'\"\n :block=\"childBlock\"\n viewport=\"desktop\"\n />\n <component\n :is=\"getBlockComponent(childBlock)\"\n v-else\n :block=\"childBlock\"\n viewport=\"desktop\"\n />\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ButtonBlock from \"./ButtonBlock.vue\";\nimport CustomBlock from \"./CustomBlock.vue\";\nimport DividerBlock from \"./DividerBlock.vue\";\nimport HtmlBlock from \"./HtmlBlock.vue\";\nimport ImageBlock from \"./ImageBlock.vue\";\nimport MenuBlock from \"./MenuBlock.vue\";\nimport SocialIconsBlock from \"./SocialIconsBlock.vue\";\nimport SpacerBlock from \"./SpacerBlock.vue\";\nimport TableBlock from \"./TableBlock.vue\";\nimport TitleBlock from \"./TitleBlock.vue\";\nimport ParagraphBlock from \"./ParagraphBlock.vue\";\nimport VideoBlock from \"./VideoBlock.vue\";\nimport {\n resolveBlockComponent,\n getBlockWrapperStyle,\n} from \"../../utils/blockComponentResolver\";\nimport type {\n Block,\n SectionBlock as SectionBlockType,\n} from \"@templatical/types\";\nimport { computed, inject, type Component } from \"vue\";\nimport { BLOCK_REGISTRY_KEY } from \"../../keys\";\n\nconst previewBlockComponentMap: Record<string, Component> = {\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n video: VideoBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n custom: CustomBlock,\n};\n\nconst props = defineProps<{\n block: SectionBlockType;\n viewport: \"desktop\";\n}>();\n\ndefineOptions({ name: \"PreviewSectionBlock\" });\n\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY, null);\n\nconst columnWidths = computed(() => {\n switch (props.block.columns) {\n case \"2\":\n return [\"50%\", \"50%\"];\n case \"3\":\n return [\"33.33%\", \"33.33%\", \"33.33%\"];\n case \"1-2\":\n return [\"33.33%\", \"66.67%\"];\n case \"2-1\":\n return [\"66.67%\", \"33.33%\"];\n default:\n return [\"100%\"];\n }\n});\n\nconst columns = computed(() => {\n const count = columnWidths.value.length;\n const children = [...props.block.children];\n while (children.length < count) {\n children.push([]);\n }\n return children.slice(0, count);\n});\n\nfunction getColumnBlocks(colIndex: number): Block[] {\n return columns.value[colIndex] || [];\n}\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, previewBlockComponentMap);\n}\n</script>\n\n<template>\n <div class=\"tpl:w-full\">\n <div class=\"tpl:flex tpl:gap-0\">\n <div\n v-for=\"(_, colIndex) in columns\"\n :key=\"colIndex\"\n :style=\"{ width: columnWidths[colIndex] }\"\n >\n <div\n v-for=\"childBlock in getColumnBlocks(colIndex)\"\n :key=\"childBlock.id\"\n :style=\"getBlockWrapperStyle(childBlock)\"\n >\n <!-- Recursive self-reference for nested sections -->\n <PreviewSectionBlock\n v-if=\"childBlock.type === 'section'\"\n :block=\"childBlock\"\n viewport=\"desktop\"\n />\n <component\n :is=\"getBlockComponent(childBlock)\"\n v-else\n :block=\"childBlock\"\n viewport=\"desktop\"\n />\n </div>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ButtonBlock from \"../../components/blocks/ButtonBlock.vue\";\nimport CustomBlock from \"../../components/blocks/CustomBlock.vue\";\nimport DividerBlock from \"../../components/blocks/DividerBlock.vue\";\nimport HtmlBlock from \"../../components/blocks/HtmlBlock.vue\";\nimport ImageBlock from \"../../components/blocks/ImageBlock.vue\";\nimport MenuBlock from \"../../components/blocks/MenuBlock.vue\";\nimport PreviewSectionBlock from \"../../components/blocks/PreviewSectionBlock.vue\";\nimport SocialIconsBlock from \"../../components/blocks/SocialIconsBlock.vue\";\nimport SpacerBlock from \"../../components/blocks/SpacerBlock.vue\";\nimport TableBlock from \"../../components/blocks/TableBlock.vue\";\nimport TitleBlock from \"../../components/blocks/TitleBlock.vue\";\nimport ParagraphBlock from \"../../components/blocks/ParagraphBlock.vue\";\nimport VideoBlock from \"../../components/blocks/VideoBlock.vue\";\nimport { BLOCK_REGISTRY_KEY } from \"../../keys\";\nimport {\n resolveBlockComponent,\n getBlockWrapperStyle,\n} from \"../../utils/blockComponentResolver\";\nimport type { Block } from \"@templatical/types\";\nimport { inject, type Component } from \"vue\";\n\ndefineProps<{\n blocks: Block[];\n}>();\n\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY);\n\nconst modulePreviewComponentMap: Record<string, Component> = {\n section: PreviewSectionBlock,\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n video: VideoBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n custom: CustomBlock,\n};\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, modulePreviewComponentMap);\n}\n</script>\n\n<template>\n <div\n class=\"tpl:pointer-events-none tpl:mx-auto tpl:w-[600px] tpl:select-none tpl:rounded-lg\"\n style=\"\n background-color: var(--tpl-canvas-bg);\n box-shadow: var(--tpl-shadow-sm);\n \"\n >\n <div\n v-for=\"block in blocks\"\n :key=\"block.id\"\n :style=\"getBlockWrapperStyle(block)\"\n >\n <component\n :is=\"getBlockComponent(block)\"\n :block=\"block\"\n viewport=\"desktop\"\n />\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport ButtonBlock from \"../../components/blocks/ButtonBlock.vue\";\nimport CustomBlock from \"../../components/blocks/CustomBlock.vue\";\nimport DividerBlock from \"../../components/blocks/DividerBlock.vue\";\nimport HtmlBlock from \"../../components/blocks/HtmlBlock.vue\";\nimport ImageBlock from \"../../components/blocks/ImageBlock.vue\";\nimport MenuBlock from \"../../components/blocks/MenuBlock.vue\";\nimport PreviewSectionBlock from \"../../components/blocks/PreviewSectionBlock.vue\";\nimport SocialIconsBlock from \"../../components/blocks/SocialIconsBlock.vue\";\nimport SpacerBlock from \"../../components/blocks/SpacerBlock.vue\";\nimport TableBlock from \"../../components/blocks/TableBlock.vue\";\nimport TitleBlock from \"../../components/blocks/TitleBlock.vue\";\nimport ParagraphBlock from \"../../components/blocks/ParagraphBlock.vue\";\nimport VideoBlock from \"../../components/blocks/VideoBlock.vue\";\nimport { BLOCK_REGISTRY_KEY } from \"../../keys\";\nimport {\n resolveBlockComponent,\n getBlockWrapperStyle,\n} from \"../../utils/blockComponentResolver\";\nimport type { Block } from \"@templatical/types\";\nimport { inject, type Component } from \"vue\";\n\ndefineProps<{\n blocks: Block[];\n}>();\n\nconst blockRegistry = inject(BLOCK_REGISTRY_KEY);\n\nconst modulePreviewComponentMap: Record<string, Component> = {\n section: PreviewSectionBlock,\n title: TitleBlock,\n paragraph: ParagraphBlock,\n image: ImageBlock,\n video: VideoBlock,\n button: ButtonBlock,\n divider: DividerBlock,\n social: SocialIconsBlock,\n menu: MenuBlock,\n table: TableBlock,\n spacer: SpacerBlock,\n html: HtmlBlock,\n custom: CustomBlock,\n};\n\nfunction getBlockComponent(block: Block): Component | null {\n return resolveBlockComponent(block, blockRegistry, modulePreviewComponentMap);\n}\n</script>\n\n<template>\n <div\n class=\"tpl:pointer-events-none tpl:mx-auto tpl:w-[600px] tpl:select-none tpl:rounded-lg\"\n style=\"\n background-color: var(--tpl-canvas-bg);\n box-shadow: var(--tpl-shadow-sm);\n \"\n >\n <div\n v-for=\"block in blocks\"\n :key=\"block.id\"\n :style=\"getBlockWrapperStyle(block)\"\n >\n <component\n :is=\"getBlockComponent(block)\"\n :block=\"block\"\n viewport=\"desktop\"\n />\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;EAwBA,IAAM,IAAsD;GAC1D,OAAO;GACP,WAAW;GACX,OAAO;GACP,OAAO;GACP,QAAQ;GACR,SAAS;GACT,QAAQ;GACR,MAAM;GACN,OAAO;GACP,QAAQ;GACR,MAAM;GACN,QAAQ;EACV,GAEM,IAAQ,GAOR,IAAgB,EAAO,GAAoB,IAAI,GAE/C,IAAe,QAAe;GAClC,QAAQ,EAAM,MAAM,SAApB;IACE,KAAK,KACH,OAAO,CAAC,OAAO,KAAK;IACtB,KAAK,KACH,OAAO;KAAC;KAAU;KAAU;IAAQ;IACtC,KAAK,OACH,OAAO,CAAC,UAAU,QAAQ;IAC5B,KAAK,OACH,OAAO,CAAC,UAAU,QAAQ;IAC5B,SACE,OAAO,CAAC,MAAM;GAClB;EACF,CAAC,GAEK,IAAU,QAAe;GAC7B,IAAM,IAAQ,EAAa,MAAM,QAC3B,IAAW,CAAC,GAAG,EAAM,MAAM,QAAQ;GACzC,OAAO,EAAS,SAAS,IACvB,EAAS,KAAK,CAAC,CAAC;GAElB,OAAO,EAAS,MAAM,GAAG,CAAK;EAChC,CAAC;EAED,SAAS,EAAgB,GAA2B;GAClD,OAAO,EAAQ,MAAM,MAAa,CAAC;EACrC;EAEA,SAAS,EAAkB,GAAgC;GACzD,OAAO,EAAsB,GAAO,GAAe,CAAwB;EAC7E;;;eAIE,EA2BM,OA3BN,GA2BM,CA1BJ,EAyBM,OAzBN,GAyBM,EAAA,EAAA,EAAA,GAxBJ,EAuBM,GAAA,MAAA,EAtBoB,EAAA,QAAhB,GAAG,YADb,EAuBM,OAAA;IArBH,KAAK;IACL,OAAK,EAAA,EAAA,OAAW,EAAA,MAAa,GAAQ,CAAA;eAEtC,EAiBM,GAAA,MAAA,EAhBiB,EAAgB,CAAQ,IAAtC,YADT,EAiBM,OAAA;IAfH,KAAK,EAAW;IAChB,OAAK,EAAE,EAAA,CAAA,CAAoB,CAAC,CAAU,CAAA;OAI/B,EAAW,SAAI,aAAA,EAAA,GADvB,EAIE,GAAA;;IAFC,OAAO;IACR,UAAS;mCAEX,EAKE,EAJK,EAAkB,CAAU,CAAA,GAAA;;IAEhC,OAAO;IACR,UAAS;;;;;;;;;;;;;;EE9ErB,IAAM,IAAgB,EAAO,CAAkB,GAEzC,IAAuD;GAC3D,SAAS;GACT,OAAO;GACP,WAAW;GACX,OAAO;GACP,OAAO;GACP,QAAQ;GACR,SAAS;GACT,QAAQ;GACR,MAAM;GACN,OAAO;GACP,QAAQ;GACR,MAAM;GACN,QAAQ;EACV;EAEA,SAAS,EAAkB,GAAgC;GACzD,OAAO,EAAsB,GAAO,GAAe,CAAyB;EAC9E;yBAIE,EAkBM,OAlBN,GAkBM,EAAA,EAAA,EAAA,GAXJ,EAUM,GAAA,MAAA,EATY,EAAA,SAAT,YADT,EAUM,OAAA;GARH,KAAK,EAAM;GACX,OAAK,EAAE,EAAA,CAAA,CAAoB,CAAC,CAAK,CAAA;YAElC,EAIE,EAHK,EAAkB,CAAK,CAAA,GAAA;GACpB;GACR,UAAS"}
@@ -1,91 +0,0 @@
1
- import { F as e, L as t, M as n, P as r, ct as i, f as a, g as o, h as s, it as c, m as l, ot as u, p as d, st as f, u as p, x as m } from "./draggable-D840DIZB.js";
2
- import { h, m as g, p as _ } from "./ColorPicker-ll8pynws.js";
3
- //#region src/components/SlidingPillSelect.vue?vue&type=script&setup=true&lang.ts
4
- var v = [
5
- "aria-checked",
6
- "aria-label",
7
- "title",
8
- "onClick"
9
- ], y = { key: 1 }, b = /* @__PURE__ */ m({
10
- __name: "SlidingPillSelect",
11
- props: {
12
- options: {},
13
- modelValue: {}
14
- },
15
- emits: ["update:modelValue"],
16
- setup(e, { emit: c }) {
17
- let u = e, d = c, m = a(() => u.options.findIndex((e) => e.value === u.modelValue)), h = a(() => `translateX(${Math.max(m.value, 0) * 100}%)`);
18
- return (a, c) => (n(), o("div", {
19
- role: "radiogroup",
20
- class: "tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1",
21
- style: f({
22
- gridTemplateColumns: `repeat(${e.options.length}, 1fr)`,
23
- backgroundColor: "var(--tpl-bg-hover)"
24
- })
25
- }, [m.value >= 0 ? (n(), o("div", {
26
- key: 0,
27
- class: "tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]",
28
- style: f({
29
- left: "4px",
30
- width: `calc((100% - 8px) / ${e.options.length})`,
31
- transform: h.value,
32
- backgroundColor: "var(--tpl-bg)",
33
- boxShadow: "var(--tpl-shadow)",
34
- transition: "transform 120ms cubic-bezier(0.16, 1, 0.3, 1)"
35
- })
36
- }, null, 4)) : s("", !0), (n(!0), o(p, null, r(e.options, (r) => (n(), o("button", {
37
- key: r.value,
38
- role: "radio",
39
- "aria-checked": e.modelValue === r.value,
40
- "aria-label": r.label,
41
- class: "tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:py-[5px] tpl:text-xs tpl:font-medium",
42
- style: f({
43
- color: e.modelValue === r.value ? "var(--tpl-primary)" : "var(--tpl-text-muted)",
44
- transition: "color 120ms cubic-bezier(0.16, 1, 0.3, 1)"
45
- }),
46
- title: r.label,
47
- onClick: (e) => d("update:modelValue", r.value)
48
- }, [r.icon ? (n(), l(t(r.icon), {
49
- key: 0,
50
- size: 14,
51
- "stroke-width": 2
52
- })) : (n(), o("span", y, i(r.label), 1))], 12, v))), 128))], 4));
53
- }
54
- }), x = { class: "tpl:mb-3.5" }, S = /* @__PURE__ */ m({
55
- __name: "FieldRow",
56
- props: { label: {} },
57
- setup(t) {
58
- return (r, a) => (n(), o("div", x, [t.label ? (n(), o("label", {
59
- key: 0,
60
- class: u(c(h))
61
- }, i(t.label), 3)) : s("", !0), e(r.$slots, "default")]));
62
- }
63
- }), C = { class: "tpl:flex tpl:items-stretch" }, w = [
64
- "value",
65
- "min",
66
- "max"
67
- ], T = /* @__PURE__ */ m({
68
- __name: "NumberWithSuffix",
69
- props: {
70
- modelValue: {},
71
- min: {},
72
- max: {},
73
- suffix: {}
74
- },
75
- emits: ["update:modelValue"],
76
- setup(e, { emit: t }) {
77
- let r = t;
78
- return (t, a) => (n(), o("div", C, [d("input", {
79
- type: "number",
80
- class: u(c(_)),
81
- value: e.modelValue,
82
- min: e.min,
83
- max: e.max,
84
- onInput: a[0] ||= (e) => r("update:modelValue", Number(e.target.value))
85
- }, null, 42, w), d("span", { class: u(c(g)) }, i(e.suffix), 3)]));
86
- }
87
- });
88
- //#endregion
89
- export { S as n, b as r, T as t };
90
-
91
- //# sourceMappingURL=NumberWithSuffix-Dt-0odUN.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"NumberWithSuffix-Dt-0odUN.js","names":[],"sources":["../../../src/components/SlidingPillSelect.vue","../../../src/components/SlidingPillSelect.vue","../../../src/components/toolbar/FieldRow.vue","../../../src/components/toolbar/FieldRow.vue","../../../src/components/toolbar/NumberWithSuffix.vue","../../../src/components/toolbar/NumberWithSuffix.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Component } from \"vue\";\nimport { computed } from \"vue\";\n\nexport interface PillOption {\n value: string;\n label: string;\n icon?: Component;\n}\n\nconst props = defineProps<{\n options: PillOption[];\n modelValue: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst selectedIndex = computed(() =>\n props.options.findIndex((o) => o.value === props.modelValue),\n);\n\nconst pillOffset = computed(\n () => `translateX(${Math.max(selectedIndex.value, 0) * 100}%)`,\n);\n</script>\n\n<template>\n <div\n role=\"radiogroup\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${options.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill — hidden when modelValue matches no option,\n otherwise it would silently sit on the first option and create\n a visual mismatch with aria-checked. -->\n <div\n v-if=\"selectedIndex >= 0\"\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${options.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"option in options\"\n :key=\"option.value\"\n role=\"radio\"\n :aria-checked=\"modelValue === option.value\"\n :aria-label=\"option.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:py-[5px] tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n modelValue === option.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"option.label\"\n @click=\"emit('update:modelValue', option.value)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span v-else>{{ option.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport type { Component } from \"vue\";\nimport { computed } from \"vue\";\n\nexport interface PillOption {\n value: string;\n label: string;\n icon?: Component;\n}\n\nconst props = defineProps<{\n options: PillOption[];\n modelValue: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n}>();\n\nconst selectedIndex = computed(() =>\n props.options.findIndex((o) => o.value === props.modelValue),\n);\n\nconst pillOffset = computed(\n () => `translateX(${Math.max(selectedIndex.value, 0) * 100}%)`,\n);\n</script>\n\n<template>\n <div\n role=\"radiogroup\"\n class=\"tpl:relative tpl:grid tpl:rounded-[var(--tpl-radius-sm)] tpl:p-1\"\n :style=\"{\n gridTemplateColumns: `repeat(${options.length}, 1fr)`,\n backgroundColor: 'var(--tpl-bg-hover)',\n }\"\n >\n <!-- Sliding pill — hidden when modelValue matches no option,\n otherwise it would silently sit on the first option and create\n a visual mismatch with aria-checked. -->\n <div\n v-if=\"selectedIndex >= 0\"\n class=\"tpl:absolute tpl:inset-y-1 tpl:rounded-[var(--tpl-radius-sm)]\"\n :style=\"{\n left: '4px',\n width: `calc((100% - 8px) / ${options.length})`,\n transform: pillOffset,\n backgroundColor: 'var(--tpl-bg)',\n boxShadow: 'var(--tpl-shadow)',\n transition: 'transform 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n ></div>\n\n <button\n v-for=\"option in options\"\n :key=\"option.value\"\n role=\"radio\"\n :aria-checked=\"modelValue === option.value\"\n :aria-label=\"option.label\"\n class=\"tpl:relative tpl:z-10 tpl:flex tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border-none tpl:bg-transparent tpl:px-2.5 tpl:py-[5px] tpl:text-xs tpl:font-medium\"\n :style=\"{\n color:\n modelValue === option.value\n ? 'var(--tpl-primary)'\n : 'var(--tpl-text-muted)',\n transition: 'color 120ms cubic-bezier(0.16, 1, 0.3, 1)',\n }\"\n :title=\"option.label\"\n @click=\"emit('update:modelValue', option.value)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n :size=\"14\"\n :stroke-width=\"2\"\n />\n <span v-else>{{ option.label }}</span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { labelClass } from \"../../constants/styleConstants\";\n\ndefineProps<{\n label?: string;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label v-if=\"label\" :class=\"labelClass\">{{ label }}</label>\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { labelClass } from \"../../constants/styleConstants\";\n\ndefineProps<{\n label?: string;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:mb-3.5\">\n <label v-if=\"label\" :class=\"labelClass\">{{ label }}</label>\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n} from \"../../constants/styleConstants\";\n\ndefineProps<{\n modelValue: number;\n min?: number;\n max?: number;\n suffix: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: number): void;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"modelValue\"\n :min=\"min\"\n :max=\"max\"\n @input=\"\n emit(\n 'update:modelValue',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">{{ suffix }}</span>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport {\n inputGroupInputClass,\n inputSuffixClass,\n} from \"../../constants/styleConstants\";\n\ndefineProps<{\n modelValue: number;\n min?: number;\n max?: number;\n suffix: string;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: number): void;\n}>();\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:items-stretch\">\n <input\n type=\"number\"\n :class=\"inputGroupInputClass\"\n :value=\"modelValue\"\n :min=\"min\"\n :max=\"max\"\n @input=\"\n emit(\n 'update:modelValue',\n Number(($event.target as HTMLInputElement).value),\n )\n \"\n />\n <span :class=\"inputSuffixClass\">{{ suffix }}</span>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;EAUA,IAAM,IAAQ,GAKR,IAAO,GAIP,IAAgB,QACpB,EAAM,QAAQ,WAAW,MAAM,EAAE,UAAU,EAAM,UAAU,CAC7D,GAEM,IAAa,QACX,cAAc,KAAK,IAAI,EAAc,OAAO,CAAC,IAAI,IAAI,GAC7D;yBAIE,EAiDM,OAAA;GAhDJ,MAAK;GACL,OAAM;GACL,OAAK,EAAA;mCAAyC,EAAA,QAAQ,OAAM;;;MASrD,EAAA,SAAa,KAAA,EAAA,GADrB,EAWO,OAAA;;GATL,OAAM;GACL,OAAK,EAAA;;kCAA+D,EAAA,QAAQ,OAAM;eAAwB,EAAA;;;;;oCAU7G,EAwBS,GAAA,MAAA,EAvBU,EAAA,UAAV,YADT,EAwBS,UAAA;GAtBN,KAAK,EAAO;GACb,MAAK;GACJ,gBAAc,EAAA,eAAe,EAAO;GACpC,cAAY,EAAO;GACpB,OAAM;GACL,OAAK,EAAA;WAA6B,EAAA,eAAe,EAAO,QAAA,uBAAA;;;GAOxD,OAAO,EAAO;GACd,UAAK,MAAE,EAAI,qBAAsB,EAAO,KAAK;MAItC,EAAO,QAAA,EAAA,GAFf,EAKE,EAJK,EAAO,IAAI,GAAA;;GAEf,MAAM;GACN,gBAAc;cAEjB,EAAsC,QAAA,GAAA,EAAtB,EAAO,KAAK,GAAA,CAAA,EAAA,GAAA,IAAA,CAAA;;;;;;yBEnEhC,EAGM,OAHN,GAGM,CAFS,EAAA,SAAA,EAAA,GAAb,EAA2D,SAAA;;GAAtC,OAAK,EAAE,EAAA,CAAA,CAAU;OAAK,EAAA,KAAK,GAAA,CAAA,KAAA,EAAA,IAAA,EAAA,GAChD,EAAQ,EAAA,QAAA,SAAA,CAAA,CAAA;;;;;;;;;;;;;;;;EEEZ,IAAM,IAAO;yBAMX,EAeM,OAfN,GAeM,CAdJ,EAYE,SAAA;GAXA,MAAK;GACJ,OAAK,EAAE,EAAA,CAAA,CAAoB;GAC3B,OAAO,EAAA;GACP,KAAK,EAAA;GACL,KAAK,EAAA;GACL,SAAK,AAAA,EAAA,QAAA,MAAW,EAAA,qBAA+C,OAAQ,EAAO,OAA4B,KAAK,CAAA;mBAOlH,EAAmD,QAAA,EAA5C,OAAK,EAAE,EAAA,CAAA,CAAgB,EAAA,GAAA,EAAK,EAAA,MAAM,GAAA,CAAA,CAAA,CAAA"}
@@ -1,120 +0,0 @@
1
- import { H as e, M as t, P as n, V as r, Z as i, ct as a, f as o, g as s, h as c, it as l, m as u, o as ee, ot as d, p as f, st as p, u as m, v as h, x as g, y as _, z as v } from "./draggable-D840DIZB.js";
2
- import { O as y, St as b, Ut as te, at as ne, gn as x, rn as S, un as C } from "./features-CLIZ7Q-Y.js";
3
- import { t as w } from "./ToggleSwitch-OrmX_w18.js";
4
- //#region src/cloud/components/SaveModuleDialog.vue?vue&type=script&setup=true&lang.ts
5
- var T = ["aria-busy"], E = {
6
- id: "tpl-save-module-title",
7
- class: "tpl:mb-4 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]"
8
- }, D = { class: "tpl:mb-3" }, O = { class: "tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]" }, k = ["placeholder", "disabled"], A = { class: "tpl:mb-3" }, j = { class: "tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]" }, M = { class: "tpl:max-h-40 tpl:space-y-1 tpl:overflow-y-auto tpl:rounded-md tpl:border tpl:p-2 tpl:border-[var(--tpl-border)]" }, N = {
9
- key: 0,
10
- role: "alert",
11
- class: "tpl:mb-3 tpl:text-xs tpl:text-[var(--tpl-danger)]"
12
- }, P = { class: "tpl:flex tpl:justify-end tpl:gap-2" }, F = ["disabled"], I = ["disabled"], L = {
13
- key: 0,
14
- class: "tpl:flex tpl:items-center tpl:gap-1.5"
15
- }, re = { key: 1 }, R = /* @__PURE__ */ g({
16
- __name: "SaveModuleDialog",
17
- props: {
18
- visible: { type: Boolean },
19
- preSelectedBlockId: {}
20
- },
21
- emits: ["close", "saved"],
22
- setup(g, { emit: R }) {
23
- let z = g, B = R, { t: V } = te(), { t: H } = ne(), U = x(S, "SaveModuleDialog"), W = x(C, "SaveModuleDialog"), G = i(""), K = i(/* @__PURE__ */ new Set()), q = i(!1), J = i(null), Y = o(() => U.content.value.blocks);
24
- function X(e, t) {
25
- return `${V.blocks[e.type] ?? e.type} ${t + 1}`;
26
- }
27
- v(() => z.visible, (e) => {
28
- e && (G.value = "", J.value = null, K.value = new Set(z.preSelectedBlockId ? [z.preSelectedBlockId] : []));
29
- });
30
- function ie(e) {
31
- let t = new Set(K.value);
32
- t.has(e) ? t.delete(e) : t.add(e), K.value = t;
33
- }
34
- let Z = o(() => G.value.trim().length > 0 && K.value.size > 0 && !q.value);
35
- async function Q() {
36
- if (Z.value) {
37
- q.value = !0, J.value = null;
38
- try {
39
- let e = Y.value.filter((e) => K.value.has(e.id));
40
- await W.createModule(G.value.trim(), e), B("saved"), B("close");
41
- } catch (e) {
42
- J.value = e.message;
43
- } finally {
44
- q.value = !1;
45
- }
46
- }
47
- }
48
- function $() {
49
- q.value || B("close");
50
- }
51
- function ae(e) {
52
- e.key === "Enter" && !e.shiftKey && (e.preventDefault(), Q()), e.key === "Escape" && $();
53
- }
54
- return (i, o) => (t(), u(y, {
55
- visible: g.visible,
56
- onClose: $,
57
- onKeydown: ae
58
- }, {
59
- default: r(() => [f("div", {
60
- role: "dialog",
61
- "aria-modal": "true",
62
- "aria-busy": q.value,
63
- "aria-labelledby": "tpl-save-module-title",
64
- class: "tpl-scale-in tpl:mx-4 tpl:w-full tpl:max-w-sm tpl:rounded-[var(--tpl-radius-lg)] tpl:p-5",
65
- style: {
66
- "background-color": "var(--tpl-bg-elevated)",
67
- "box-shadow": "var(--tpl-shadow-xl)"
68
- }
69
- }, [
70
- f("h3", E, a(l(H).modules.saveAsModule), 1),
71
- f("div", D, [f("label", O, a(l(H).modules.moduleName), 1), e(f("input", {
72
- "onUpdate:modelValue": o[0] ||= (e) => G.value = e,
73
- type: "text",
74
- placeholder: l(H).modules.moduleNamePlaceholder,
75
- class: "tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]",
76
- disabled: q.value
77
- }, null, 8, k), [[ee, G.value]])]),
78
- f("div", A, [f("label", j, a(l(H).modules.selectBlocks), 1), f("div", M, [(t(!0), s(m, null, n(Y.value, (e, n) => (t(), u(w, {
79
- key: e.id,
80
- class: "tpl:rounded-sm tpl:px-2 tpl:py-1.5 tpl:text-sm tpl:transition-colors tpl:duration-100",
81
- style: p({
82
- color: "var(--tpl-text)",
83
- backgroundColor: K.value.has(e.id) ? "var(--tpl-primary-light)" : "transparent"
84
- }),
85
- "model-value": K.value.has(e.id),
86
- label: X(e, n),
87
- disabled: q.value,
88
- "onUpdate:modelValue": (t) => ie(e.id)
89
- }, null, 8, [
90
- "style",
91
- "model-value",
92
- "label",
93
- "disabled",
94
- "onUpdate:modelValue"
95
- ]))), 128))])]),
96
- J.value ? (t(), s("p", N, a(J.value), 1)) : c("", !0),
97
- f("div", P, [f("button", {
98
- type: "button",
99
- class: d(["tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]", { "tpl:cursor-not-allowed tpl:opacity-50": q.value }]),
100
- disabled: q.value,
101
- onClick: $
102
- }, a(l(H).modules.cancel), 11, F), f("button", {
103
- type: "button",
104
- class: "tpl:cursor-pointer tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]",
105
- disabled: !Z.value,
106
- onClick: Q
107
- }, [q.value ? (t(), s("span", L, [_(l(b), {
108
- class: "tpl:animate-spin",
109
- size: 12,
110
- "stroke-width": 2
111
- }), h(" " + a(l(H).modules.saving), 1)])) : (t(), s("span", re, a(l(H).modules.save), 1))], 8, I)])
112
- ], 8, T)]),
113
- _: 1
114
- }, 8, ["visible"]));
115
- }
116
- });
117
- //#endregion
118
- export { R as default };
119
-
120
- //# sourceMappingURL=SaveModuleDialog-BpFhTi-k.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SaveModuleDialog-BpFhTi-k.js","names":[],"sources":["../../../src/cloud/components/SaveModuleDialog.vue","../../../src/cloud/components/SaveModuleDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport TplModal from \"../../components/TplModal.vue\";\nimport ToggleSwitch from \"../../components/ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables\";\nimport { useCloudI18nStrict } from \"../../composables\";\nimport {\n EDITOR_KEY,\n SAVED_MODULES_HEADLESS_KEY,\n requireInject,\n} from \"../../keys\";\nimport type { Block } from \"@templatical/types\";\nimport { LoaderCircle } from \"@lucide/vue\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n preSelectedBlockId: string | null;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"saved\"): void;\n}>();\n\nconst { t } = useI18n();\nconst { t: cloudT } = useCloudI18nStrict();\nconst editor = requireInject(EDITOR_KEY, \"SaveModuleDialog\");\nconst savedModules = requireInject(\n SAVED_MODULES_HEADLESS_KEY,\n \"SaveModuleDialog\",\n);\n\nconst moduleName = ref(\"\");\nconst selectedBlockIds = ref<Set<string>>(new Set());\nconst isSaving = ref(false);\nconst error = ref<string | null>(null);\n\nconst topLevelBlocks = computed(() => editor.content.value.blocks);\n\nfunction blockLabel(block: Block, index: number): string {\n const typeKey = block.type as keyof typeof t.blocks;\n const label = t.blocks[typeKey] ?? block.type;\n return `${label} ${index + 1}`;\n}\n\nwatch(\n () => props.visible,\n (visible) => {\n if (visible) {\n moduleName.value = \"\";\n error.value = null;\n selectedBlockIds.value = new Set(\n props.preSelectedBlockId ? [props.preSelectedBlockId] : [],\n );\n }\n },\n);\n\nfunction toggleBlock(blockId: string): void {\n const newSet = new Set(selectedBlockIds.value);\n if (newSet.has(blockId)) {\n newSet.delete(blockId);\n } else {\n newSet.add(blockId);\n }\n selectedBlockIds.value = newSet;\n}\n\nconst canSave = computed(\n () =>\n moduleName.value.trim().length > 0 &&\n selectedBlockIds.value.size > 0 &&\n !isSaving.value,\n);\n\nasync function handleSave(): Promise<void> {\n if (!canSave.value) return;\n\n isSaving.value = true;\n error.value = null;\n\n try {\n const selectedBlocks = topLevelBlocks.value.filter((b) =>\n selectedBlockIds.value.has(b.id),\n );\n await savedModules.createModule(moduleName.value.trim(), selectedBlocks);\n emit(\"saved\");\n emit(\"close\");\n } catch (err) {\n error.value = (err as Error).message;\n } finally {\n isSaving.value = false;\n }\n}\n\nfunction handleClose(): void {\n if (!isSaving.value) {\n emit(\"close\");\n }\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n handleSave();\n }\n if (event.key === \"Escape\") {\n handleClose();\n }\n}\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"handleClose\" @keydown=\"handleKeydown\">\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-busy=\"isSaving\"\n aria-labelledby=\"tpl-save-module-title\"\n class=\"tpl-scale-in tpl:mx-4 tpl:w-full tpl:max-w-sm tpl:rounded-[var(--tpl-radius-lg)] tpl:p-5\"\n style=\"\n background-color: var(--tpl-bg-elevated);\n box-shadow: var(--tpl-shadow-xl);\n \"\n >\n <h3\n id=\"tpl-save-module-title\"\n class=\"tpl:mb-4 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.modules.saveAsModule }}\n </h3>\n\n <!-- Module name -->\n <div class=\"tpl:mb-3\">\n <label\n class=\"tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.modules.moduleName }}\n </label>\n <input\n v-model=\"moduleName\"\n type=\"text\"\n :placeholder=\"cloudT.modules.moduleNamePlaceholder\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n :disabled=\"isSaving\"\n />\n </div>\n\n <!-- Block selection -->\n <div class=\"tpl:mb-3\">\n <label\n class=\"tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.modules.selectBlocks }}\n </label>\n <div\n class=\"tpl:max-h-40 tpl:space-y-1 tpl:overflow-y-auto tpl:rounded-md tpl:border tpl:p-2 tpl:border-[var(--tpl-border)]\"\n >\n <ToggleSwitch\n v-for=\"(block, index) in topLevelBlocks\"\n :key=\"block.id\"\n class=\"tpl:rounded-sm tpl:px-2 tpl:py-1.5 tpl:text-sm tpl:transition-colors tpl:duration-100\"\n :style=\"{\n color: 'var(--tpl-text)',\n backgroundColor: selectedBlockIds.has(block.id)\n ? 'var(--tpl-primary-light)'\n : 'transparent',\n }\"\n :model-value=\"selectedBlockIds.has(block.id)\"\n :label=\"blockLabel(block, index)\"\n :disabled=\"isSaving\"\n @update:model-value=\"toggleBlock(block.id)\"\n />\n </div>\n </div>\n\n <!-- Error message -->\n <p\n v-if=\"error\"\n role=\"alert\"\n class=\"tpl:mb-3 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n {{ error }}\n </p>\n\n <!-- Actions -->\n <div class=\"tpl:flex tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n :disabled=\"isSaving\"\n :class=\"{\n 'tpl:cursor-not-allowed tpl:opacity-50': isSaving,\n }\"\n @click=\"handleClose\"\n >\n {{ cloudT.modules.cancel }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!canSave\"\n @click=\"handleSave\"\n >\n <span v-if=\"isSaving\" class=\"tpl:flex tpl:items-center tpl:gap-1.5\">\n <LoaderCircle\n class=\"tpl:animate-spin\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n {{ cloudT.modules.saving }}\n </span>\n <span v-else>\n {{ cloudT.modules.save }}\n </span>\n </button>\n </div>\n </div>\n </TplModal>\n</template>\n","<script setup lang=\"ts\">\nimport TplModal from \"../../components/TplModal.vue\";\nimport ToggleSwitch from \"../../components/ToggleSwitch.vue\";\nimport { useI18n } from \"../../composables\";\nimport { useCloudI18nStrict } from \"../../composables\";\nimport {\n EDITOR_KEY,\n SAVED_MODULES_HEADLESS_KEY,\n requireInject,\n} from \"../../keys\";\nimport type { Block } from \"@templatical/types\";\nimport { LoaderCircle } from \"@lucide/vue\";\nimport { computed, ref, watch } from \"vue\";\n\nconst props = defineProps<{\n visible: boolean;\n preSelectedBlockId: string | null;\n}>();\n\nconst emit = defineEmits<{\n (e: \"close\"): void;\n (e: \"saved\"): void;\n}>();\n\nconst { t } = useI18n();\nconst { t: cloudT } = useCloudI18nStrict();\nconst editor = requireInject(EDITOR_KEY, \"SaveModuleDialog\");\nconst savedModules = requireInject(\n SAVED_MODULES_HEADLESS_KEY,\n \"SaveModuleDialog\",\n);\n\nconst moduleName = ref(\"\");\nconst selectedBlockIds = ref<Set<string>>(new Set());\nconst isSaving = ref(false);\nconst error = ref<string | null>(null);\n\nconst topLevelBlocks = computed(() => editor.content.value.blocks);\n\nfunction blockLabel(block: Block, index: number): string {\n const typeKey = block.type as keyof typeof t.blocks;\n const label = t.blocks[typeKey] ?? block.type;\n return `${label} ${index + 1}`;\n}\n\nwatch(\n () => props.visible,\n (visible) => {\n if (visible) {\n moduleName.value = \"\";\n error.value = null;\n selectedBlockIds.value = new Set(\n props.preSelectedBlockId ? [props.preSelectedBlockId] : [],\n );\n }\n },\n);\n\nfunction toggleBlock(blockId: string): void {\n const newSet = new Set(selectedBlockIds.value);\n if (newSet.has(blockId)) {\n newSet.delete(blockId);\n } else {\n newSet.add(blockId);\n }\n selectedBlockIds.value = newSet;\n}\n\nconst canSave = computed(\n () =>\n moduleName.value.trim().length > 0 &&\n selectedBlockIds.value.size > 0 &&\n !isSaving.value,\n);\n\nasync function handleSave(): Promise<void> {\n if (!canSave.value) return;\n\n isSaving.value = true;\n error.value = null;\n\n try {\n const selectedBlocks = topLevelBlocks.value.filter((b) =>\n selectedBlockIds.value.has(b.id),\n );\n await savedModules.createModule(moduleName.value.trim(), selectedBlocks);\n emit(\"saved\");\n emit(\"close\");\n } catch (err) {\n error.value = (err as Error).message;\n } finally {\n isSaving.value = false;\n }\n}\n\nfunction handleClose(): void {\n if (!isSaving.value) {\n emit(\"close\");\n }\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n handleSave();\n }\n if (event.key === \"Escape\") {\n handleClose();\n }\n}\n</script>\n\n<template>\n <TplModal :visible=\"visible\" @close=\"handleClose\" @keydown=\"handleKeydown\">\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n :aria-busy=\"isSaving\"\n aria-labelledby=\"tpl-save-module-title\"\n class=\"tpl-scale-in tpl:mx-4 tpl:w-full tpl:max-w-sm tpl:rounded-[var(--tpl-radius-lg)] tpl:p-5\"\n style=\"\n background-color: var(--tpl-bg-elevated);\n box-shadow: var(--tpl-shadow-xl);\n \"\n >\n <h3\n id=\"tpl-save-module-title\"\n class=\"tpl:mb-4 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ cloudT.modules.saveAsModule }}\n </h3>\n\n <!-- Module name -->\n <div class=\"tpl:mb-3\">\n <label\n class=\"tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.modules.moduleName }}\n </label>\n <input\n v-model=\"moduleName\"\n type=\"text\"\n :placeholder=\"cloudT.modules.moduleNamePlaceholder\"\n class=\"tpl:h-9 tpl:w-full tpl:rounded-md tpl:border tpl:px-3 tpl:py-1 tpl:text-sm tpl:shadow-xs tpl:outline-none tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:text-[var(--tpl-text)]\"\n :disabled=\"isSaving\"\n />\n </div>\n\n <!-- Block selection -->\n <div class=\"tpl:mb-3\">\n <label\n class=\"tpl:mb-1.5 tpl:block tpl:text-sm tpl:font-medium tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ cloudT.modules.selectBlocks }}\n </label>\n <div\n class=\"tpl:max-h-40 tpl:space-y-1 tpl:overflow-y-auto tpl:rounded-md tpl:border tpl:p-2 tpl:border-[var(--tpl-border)]\"\n >\n <ToggleSwitch\n v-for=\"(block, index) in topLevelBlocks\"\n :key=\"block.id\"\n class=\"tpl:rounded-sm tpl:px-2 tpl:py-1.5 tpl:text-sm tpl:transition-colors tpl:duration-100\"\n :style=\"{\n color: 'var(--tpl-text)',\n backgroundColor: selectedBlockIds.has(block.id)\n ? 'var(--tpl-primary-light)'\n : 'transparent',\n }\"\n :model-value=\"selectedBlockIds.has(block.id)\"\n :label=\"blockLabel(block, index)\"\n :disabled=\"isSaving\"\n @update:model-value=\"toggleBlock(block.id)\"\n />\n </div>\n </div>\n\n <!-- Error message -->\n <p\n v-if=\"error\"\n role=\"alert\"\n class=\"tpl:mb-3 tpl:text-xs tpl:text-[var(--tpl-danger)]\"\n >\n {{ error }}\n </p>\n\n <!-- Actions -->\n <div class=\"tpl:flex tpl:justify-end tpl:gap-2\">\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:border tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:border-[var(--tpl-border)] tpl:text-[var(--tpl-text)] tpl:bg-[var(--tpl-bg)]\"\n :disabled=\"isSaving\"\n :class=\"{\n 'tpl:cursor-not-allowed tpl:opacity-50': isSaving,\n }\"\n @click=\"handleClose\"\n >\n {{ cloudT.modules.cancel }}\n </button>\n <button\n type=\"button\"\n class=\"tpl:cursor-pointer tpl:rounded-md tpl:px-3 tpl:py-1.5 tpl:text-sm tpl:font-medium tpl:shadow-xs tpl:transition-all tpl:duration-150 tpl:hover:opacity-90 tpl:disabled:cursor-not-allowed tpl:disabled:opacity-50 tpl:bg-[var(--tpl-primary)] tpl:text-[var(--tpl-bg)]\"\n :disabled=\"!canSave\"\n @click=\"handleSave\"\n >\n <span v-if=\"isSaving\" class=\"tpl:flex tpl:items-center tpl:gap-1.5\">\n <LoaderCircle\n class=\"tpl:animate-spin\"\n :size=\"12\"\n :stroke-width=\"2\"\n />\n {{ cloudT.modules.saving }}\n </span>\n <span v-else>\n {{ cloudT.modules.save }}\n </span>\n </button>\n </div>\n </div>\n </TplModal>\n</template>\n"],"mappings":"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"}
@@ -1,39 +0,0 @@
1
- import { F as e, M as t, ct as n, g as r, h as i, ot as a, p as o, v as s, x as c } from "./draggable-D840DIZB.js";
2
- //#region src/components/ToggleSwitch.vue?vue&type=script&setup=true&lang.ts
3
- var l = { key: 0 }, u = {
4
- key: 0,
5
- class: "tpl:text-[var(--tpl-danger)]"
6
- }, d = [
7
- "aria-checked",
8
- "aria-label",
9
- "disabled"
10
- ], f = /* @__PURE__ */ c({
11
- __name: "ToggleSwitch",
12
- props: {
13
- modelValue: { type: Boolean },
14
- label: {},
15
- disabled: { type: Boolean },
16
- required: { type: Boolean }
17
- },
18
- emits: ["update:modelValue"],
19
- setup(c, { emit: f }) {
20
- let p = f;
21
- return (f, m) => (t(), r("label", { class: a(["tpl:flex tpl:items-center tpl:justify-between tpl:gap-2", c.disabled ? "tpl:cursor-not-allowed" : "tpl:cursor-pointer"]) }, [f.$slots.default || c.label || c.required ? (t(), r("span", l, [e(f.$slots, "default", {}, () => [s(n(c.label), 1)]), c.required ? (t(), r("span", u, " *")) : i("", !0)])) : i("", !0), o("button", {
22
- type: "button",
23
- role: "switch",
24
- "aria-checked": c.modelValue,
25
- "aria-label": c.label,
26
- disabled: c.disabled,
27
- class: a([
28
- "tpl:relative tpl:inline-flex tpl:h-5 tpl:w-9 tpl:shrink-0 tpl:rounded-full tpl:border-2 tpl:border-transparent tpl:transition-colors tpl:duration-200",
29
- c.modelValue ? "tpl:bg-[var(--tpl-primary)]" : "tpl:bg-[var(--tpl-border)]",
30
- c.disabled ? "tpl:cursor-not-allowed tpl:opacity-60" : "tpl:cursor-pointer"
31
- ]),
32
- onClick: m[0] ||= (e) => !c.disabled && p("update:modelValue", !c.modelValue)
33
- }, [o("span", { class: a(["tpl:pointer-events-none tpl:inline-block tpl:size-4 tpl:rounded-full tpl:bg-[var(--tpl-bg)] tpl:shadow tpl:transition-transform tpl:duration-200", c.modelValue ? "tpl:translate-x-4" : "tpl:translate-x-0"]) }, null, 2)], 10, d)], 2));
34
- }
35
- });
36
- //#endregion
37
- export { f as t };
38
-
39
- //# sourceMappingURL=ToggleSwitch-OrmX_w18.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ToggleSwitch-OrmX_w18.js","names":["$slots"],"sources":["../../../src/components/ToggleSwitch.vue","../../../src/components/ToggleSwitch.vue"],"sourcesContent":["<script setup lang=\"ts\">\ndefineProps<{\n modelValue: boolean;\n label?: string;\n disabled?: boolean;\n required?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: boolean): void;\n}>();\n</script>\n\n<template>\n <label\n :class=\"[\n 'tpl:flex tpl:items-center tpl:justify-between tpl:gap-2',\n disabled ? 'tpl:cursor-not-allowed' : 'tpl:cursor-pointer',\n ]\"\n >\n <span v-if=\"$slots.default || label || required\">\n <slot>{{ label }}</slot>\n <span v-if=\"required\" class=\"tpl:text-[var(--tpl-danger)]\"> *</span>\n </span>\n <button\n type=\"button\"\n role=\"switch\"\n :aria-checked=\"modelValue\"\n :aria-label=\"label\"\n :disabled=\"disabled\"\n :class=\"[\n 'tpl:relative tpl:inline-flex tpl:h-5 tpl:w-9 tpl:shrink-0 tpl:rounded-full tpl:border-2 tpl:border-transparent tpl:transition-colors tpl:duration-200',\n modelValue\n ? 'tpl:bg-[var(--tpl-primary)]'\n : 'tpl:bg-[var(--tpl-border)]',\n disabled\n ? 'tpl:cursor-not-allowed tpl:opacity-60'\n : 'tpl:cursor-pointer',\n ]\"\n @click=\"!disabled && emit('update:modelValue', !modelValue)\"\n >\n <span\n class=\"tpl:pointer-events-none tpl:inline-block tpl:size-4 tpl:rounded-full tpl:bg-[var(--tpl-bg)] tpl:shadow tpl:transition-transform tpl:duration-200\"\n :class=\"modelValue ? 'tpl:translate-x-4' : 'tpl:translate-x-0'\"\n />\n </button>\n </label>\n</template>\n","<script setup lang=\"ts\">\ndefineProps<{\n modelValue: boolean;\n label?: string;\n disabled?: boolean;\n required?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: boolean): void;\n}>();\n</script>\n\n<template>\n <label\n :class=\"[\n 'tpl:flex tpl:items-center tpl:justify-between tpl:gap-2',\n disabled ? 'tpl:cursor-not-allowed' : 'tpl:cursor-pointer',\n ]\"\n >\n <span v-if=\"$slots.default || label || required\">\n <slot>{{ label }}</slot>\n <span v-if=\"required\" class=\"tpl:text-[var(--tpl-danger)]\"> *</span>\n </span>\n <button\n type=\"button\"\n role=\"switch\"\n :aria-checked=\"modelValue\"\n :aria-label=\"label\"\n :disabled=\"disabled\"\n :class=\"[\n 'tpl:relative tpl:inline-flex tpl:h-5 tpl:w-9 tpl:shrink-0 tpl:rounded-full tpl:border-2 tpl:border-transparent tpl:transition-colors tpl:duration-200',\n modelValue\n ? 'tpl:bg-[var(--tpl-primary)]'\n : 'tpl:bg-[var(--tpl-border)]',\n disabled\n ? 'tpl:cursor-not-allowed tpl:opacity-60'\n : 'tpl:cursor-pointer',\n ]\"\n @click=\"!disabled && emit('update:modelValue', !modelValue)\"\n >\n <span\n class=\"tpl:pointer-events-none tpl:inline-block tpl:size-4 tpl:rounded-full tpl:bg-[var(--tpl-bg)] tpl:shadow tpl:transition-transform tpl:duration-200\"\n :class=\"modelValue ? 'tpl:translate-x-4' : 'tpl:translate-x-0'\"\n />\n </button>\n </label>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;EAQA,IAAM,IAAO;yBAMX,EAgCQ,SAAA,EA/BL,OAAK,EAAA,CAAA,2DAA2E,EAAA,WAAQ,2BAAA,oBAAA,CAAA,EAAA,GAAA,CAK7EA,EAAAA,OAAO,WAAW,EAAA,SAAS,EAAA,YAAA,EAAA,GAAvC,EAGO,QAAA,GAAA,CAFL,EAAwB,EAAA,QAAA,WAAA,CAAA,SAAA,CAAA,EAAA,EAAf,EAAA,KAAK,GAAA,CAAA,CAAA,CAAA,GACF,EAAA,YAAA,EAAA,GAAZ,EAAoE,QAApE,GAA2D,IAAE,KAAA,EAAA,IAAA,EAAA,CAAA,CAAA,KAAA,EAAA,IAAA,EAAA,GAE/D,EAqBS,UAAA;GApBP,MAAK;GACL,MAAK;GACJ,gBAAc,EAAA;GACd,cAAY,EAAA;GACZ,UAAU,EAAA;GACV,OAAK,EAAA;;IAA6K,EAAA,aAAA,gCAAA;IAAuG,EAAA,WAAA,0CAAA;;GASzR,SAAK,AAAA,EAAA,QAAA,MAAA,CAAG,EAAA,YAAY,EAAI,qBAAA,CAAuB,EAAA,UAAU;MAE1D,EAGE,QAAA,EAFA,OAAK,EAAA,CAAC,oJACE,EAAA,aAAU,sBAAA,mBAAA,CAAA,EAAA,GAAA,MAAA,CAAA,CAAA,GAAA,IAAA,CAAA,CAAA,GAAA,CAAA"}