domma-cms 0.54.2 → 0.66.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 (221) hide show
  1. package/CLAUDE.md +151 -77
  2. package/README.md +4 -4
  3. package/admin/css/admin.css +1 -1
  4. package/admin/dist/domma/domma-tools.css +3 -3
  5. package/admin/dist/domma/domma-tools.min.js +3 -3
  6. package/admin/js/api.js +1 -1
  7. package/admin/js/app.js +3 -3
  8. package/admin/js/lib/page-picker.js +1 -0
  9. package/admin/js/lib/plugin-accent.js +1 -0
  10. package/admin/js/lib/plugin-chrome.js +1 -0
  11. package/admin/js/lib/shortcode-context-menu.js +2 -2
  12. package/admin/js/lib/sidebar-grouping.js +1 -1
  13. package/admin/js/lib/sidebar-grouping.test.js +1 -1
  14. package/admin/js/lib/sidebar-renderer.js +4 -4
  15. package/admin/js/lib/slideover-resizable.js +1 -0
  16. package/admin/js/templates/context-menu-editor.html +212 -0
  17. package/admin/js/templates/context-menus.html +16 -0
  18. package/admin/js/templates/menu-editor.html +19 -25
  19. package/admin/js/templates/plugin-code.html +1 -1
  20. package/{plugins/site-search/admin/templates/site-search.html → admin/js/templates/search.html} +50 -4
  21. package/admin/js/templates/settings.html +27 -97
  22. package/admin/js/templates/theme.html +173 -0
  23. package/admin/js/views/context-menu-editor.js +55 -0
  24. package/admin/js/views/context-menus.js +5 -0
  25. package/admin/js/views/form-editor.js +7 -7
  26. package/admin/js/views/index.js +1 -1
  27. package/admin/js/views/menu-editor.js +13 -13
  28. package/admin/js/views/plugin-marketplace.js +1 -1
  29. package/admin/js/views/plugins.js +25 -23
  30. package/admin/js/views/search.js +1 -0
  31. package/admin/js/views/settings.js +3 -3
  32. package/admin/js/views/theme.js +1 -0
  33. package/bin/cli.js +11 -2
  34. package/bin/lib/smtp-defaults.js +53 -0
  35. package/bin/update.js +13 -2
  36. package/config/menus/admin-sidebar.json +129 -23
  37. package/config/plugins.json +5 -5
  38. package/config/search.json +13 -0
  39. package/config/theme.json +18 -0
  40. package/package.json +12 -4
  41. package/plugins/_lib/admin/mail/compose-window.js +914 -0
  42. package/plugins/_lib/admin/mail/contacts.js +301 -0
  43. package/plugins/_lib/admin/mail/diagnostics-section.js +133 -0
  44. package/plugins/_lib/admin/mail/folder-tree.js +254 -0
  45. package/plugins/_lib/admin/mail/identity.js +480 -0
  46. package/plugins/_lib/admin/mail/image-senders-section.js +131 -0
  47. package/plugins/_lib/admin/mail/keyboard.js +136 -0
  48. package/plugins/_lib/admin/mail/mail.css +1 -0
  49. package/plugins/_lib/admin/mail/mail.html +71 -0
  50. package/plugins/_lib/admin/mail/panes.js +253 -0
  51. package/plugins/_lib/admin/mail/reader-view.js +4453 -0
  52. package/plugins/_lib/admin/mail/resizable.js +26 -0
  53. package/plugins/_lib/admin/mail/rules.js +343 -0
  54. package/plugins/_lib/admin/mail/scheduling.js +203 -0
  55. package/plugins/_lib/admin/mail/section-kit.js +277 -0
  56. package/plugins/_lib/admin/mail/templates.js +238 -0
  57. package/plugins/_lib/admin/mail/threads.js +200 -0
  58. package/plugins/_lib/admin/mail/vacation.js +269 -0
  59. package/plugins/_lib/admin/ui/help.css +1 -0
  60. package/plugins/_lib/admin/ui/help.js +174 -0
  61. package/plugins/_lib/admin/ui/resizable.js +151 -0
  62. package/plugins/_lib/dataStore.js +117 -0
  63. package/plugins/_lib/mail/accounts.js +919 -0
  64. package/plugins/_lib/mail/bodyTokens.js +101 -0
  65. package/plugins/_lib/mail/compose.js +256 -0
  66. package/plugins/_lib/mail/defaults.js +57 -0
  67. package/plugins/_lib/mail/diagnostics.js +154 -0
  68. package/plugins/_lib/mail/envelope.js +161 -0
  69. package/plugins/_lib/mail/folders.js +192 -0
  70. package/plugins/_lib/mail/handoff.js +274 -0
  71. package/plugins/_lib/mail/imapPool.js +293 -0
  72. package/plugins/_lib/mail/mbox.js +74 -0
  73. package/plugins/_lib/mail/pollSchedule.js +79 -0
  74. package/plugins/_lib/mail/poller.js +135 -0
  75. package/plugins/_lib/mail/priority.js +138 -0
  76. package/plugins/_lib/mail/readRoutes.js +680 -0
  77. package/plugins/_lib/mail/render.js +291 -0
  78. package/plugins/_lib/mail/ruleRunner.js +152 -0
  79. package/plugins/_lib/mail/scheduler.js +254 -0
  80. package/plugins/_lib/mail/secretbox.js +229 -0
  81. package/plugins/_lib/mail/send.js +396 -0
  82. package/plugins/_lib/mail/store.js +1002 -0
  83. package/plugins/_lib/mail/sync.js +292 -0
  84. package/plugins/_lib/mail/syncPlan.js +126 -0
  85. package/plugins/_lib/mail/syncSelection.js +82 -0
  86. package/plugins/_lib/mail/unsubscribe.js +183 -0
  87. package/plugins/_lib/mail/vacationRunner.js +114 -0
  88. package/plugins/_lib/mail/write.js +473 -0
  89. package/plugins/_lib/schemaSync.js +83 -0
  90. package/plugins/_template/admin/css/index.css +0 -0
  91. package/plugins/_template/admin/templates/index.html +4 -4
  92. package/plugins/_template/admin/views/index.js +7 -0
  93. package/plugins/analytics/admin/css/index.css +1 -0
  94. package/plugins/analytics/admin/templates/analytics.html +22 -13
  95. package/plugins/analytics/plugin.json +3 -0
  96. package/plugins/blog/admin/css/index.css +1 -0
  97. package/plugins/blog/admin/templates/blog.html +30 -18
  98. package/plugins/blog/admin/templates/categories.html +2 -2
  99. package/plugins/blog/admin/templates/comments.html +2 -2
  100. package/plugins/blog/admin/templates/post-editor.html +34 -34
  101. package/plugins/blog/admin/templates/settings.html +6 -3
  102. package/plugins/blog/admin/views/blog.js +8 -5
  103. package/plugins/blog/admin/views/categories.js +5 -10
  104. package/plugins/blog/admin/views/comments.js +5 -5
  105. package/plugins/blog/admin/views/post-editor.js +39 -20
  106. package/plugins/blog/admin/views/settings.js +52 -50
  107. package/plugins/blog/collections/categories/schema.json +7 -6
  108. package/plugins/blog/collections/comments/schema.json +11 -10
  109. package/plugins/blog/collections/posts/schema.json +14 -13
  110. package/plugins/blog/plugin.js +36 -13
  111. package/plugins/blog/plugin.json +13 -5
  112. package/plugins/blog/plugin.public.js +312 -0
  113. package/plugins/contacts/admin/css/index.css +1 -0
  114. package/plugins/contacts/admin/templates/contacts.html +128 -0
  115. package/plugins/contacts/admin/views/contacts.js +237 -4
  116. package/plugins/contacts/collections/user-contacts/schema.json +108 -0
  117. package/plugins/contacts/plugin.js +214 -27
  118. package/plugins/contacts/plugin.json +4 -1
  119. package/plugins/invoice/admin/css/index.css +1 -0
  120. package/plugins/invoice/admin/templates/editor.html +140 -49
  121. package/plugins/invoice/admin/templates/index.html +153 -23
  122. package/plugins/invoice/admin/templates/issuers.html +2 -5
  123. package/plugins/invoice/admin/templates/receivers.html +2 -5
  124. package/plugins/invoice/admin/views/contacts-source.js +266 -0
  125. package/plugins/invoice/admin/views/editor.js +366 -199
  126. package/plugins/invoice/admin/views/export.js +199 -0
  127. package/plugins/invoice/admin/views/help-content.js +61 -0
  128. package/plugins/invoice/admin/views/index.js +582 -94
  129. package/plugins/invoice/admin/views/issuers.js +24 -17
  130. package/plugins/invoice/admin/views/media.js +172 -0
  131. package/plugins/invoice/admin/views/party-view.js +305 -67
  132. package/plugins/invoice/admin/views/payments.js +127 -0
  133. package/plugins/invoice/admin/views/print.js +130 -0
  134. package/plugins/invoice/admin/views/receivers.js +49 -16
  135. package/plugins/invoice/admin/views/send.js +212 -0
  136. package/plugins/invoice/admin/views/settings.js +594 -0
  137. package/plugins/invoice/admin/views/view-lifecycle.js +33 -0
  138. package/plugins/invoice/collections/invoice-issuers/schema.json +77 -11
  139. package/plugins/invoice/collections/invoice-receivers/schema.json +10 -9
  140. package/plugins/invoice/collections/invoices/schema.json +19 -13
  141. package/plugins/invoice/config.js +27 -6
  142. package/plugins/invoice/pdf.js +164 -0
  143. package/plugins/invoice/plugin.js +1217 -44
  144. package/plugins/invoice/plugin.json +10 -9
  145. package/plugins/invoice/templates/_base.css +1 -0
  146. package/plugins/invoice/templates/classic-nologo.html +100 -0
  147. package/plugins/invoice/templates/classic.html +91 -0
  148. package/plugins/invoice/templates/invoice-print.html +24 -0
  149. package/plugins/invoice/templates/minimal.html +99 -0
  150. package/plugins/invoice/templates/modern-nologo.html +114 -0
  151. package/plugins/invoice/templates/modern.html +113 -0
  152. package/plugins/invoice/templates/templates.json +11 -0
  153. package/plugins/mail-reader/admin/views/mail.js +19 -0
  154. package/plugins/mail-reader/config.js +7 -0
  155. package/plugins/mail-reader/plugin.js +48 -0
  156. package/plugins/mail-reader/plugin.json +33 -0
  157. package/plugins/notes/admin/views/notes.js +1 -1
  158. package/plugins/notes/plugin.json +2 -2
  159. package/plugins/surveys/lib/audience.js +37 -0
  160. package/plugins/surveys/lib/campaigns.js +43 -0
  161. package/plugins/surveys/lib/ledger.js +110 -0
  162. package/plugins/surveys/lib/sending.js +106 -0
  163. package/plugins/surveys/lib/stats.js +62 -0
  164. package/plugins/surveys/lib/submit.js +95 -0
  165. package/plugins/surveys/lib/tokens.js +28 -0
  166. package/plugins/surveys/plugin.public.js +149 -0
  167. package/plugins/theme-switcher/admin/templates/theme-switcher.html +1 -1
  168. package/public/css/forms.css +1 -1
  169. package/public/css/menu-highlight.css +1 -1
  170. package/public/css/search.css +1 -0
  171. package/public/css/site.css +1 -1
  172. package/public/js/collection-context.js +2 -2
  173. package/public/js/context-menus.js +1 -0
  174. package/public/js/form-logic-engine.js +1 -1
  175. package/public/js/forms.js +2 -2
  176. package/public/js/menu-decor.mjs +1 -1
  177. package/public/js/search.js +1 -0
  178. package/public/js/site.js +1 -1
  179. package/scripts/build.js +37 -3
  180. package/scripts/copy-domma.js +48 -0
  181. package/scripts/seed.js +1996 -0
  182. package/scripts/setup.js +8 -0
  183. package/server/routes/api/collections.js +34 -0
  184. package/server/routes/api/context-menus.js +104 -0
  185. package/server/routes/api/forms.js +42 -3
  186. package/server/routes/api/notifications.js +69 -19
  187. package/server/routes/api/plugins.js +50 -6
  188. package/server/routes/api/search.js +43 -0
  189. package/server/routes/api/theme.js +69 -0
  190. package/server/routes/public.js +42 -7
  191. package/server/server.js +74 -0
  192. package/server/services/adapters/FileAdapter.js +6 -1
  193. package/server/services/content.js +26 -0
  194. package/server/services/contextMenus.js +477 -0
  195. package/server/services/email.js +29 -3
  196. package/server/services/health.js +23 -2
  197. package/server/services/markdown.js +70 -9
  198. package/server/services/menuRender.js +28 -4
  199. package/server/services/menus.js +25 -1
  200. package/server/services/permissionRegistry.js +24 -0
  201. package/server/services/pluginFiles.js +52 -11
  202. package/server/services/plugins.js +229 -6
  203. package/server/services/renderer.js +148 -22
  204. package/server/services/roles.js +1 -1
  205. package/server/services/search-migration.js +82 -0
  206. package/server/services/search.js +413 -0
  207. package/server/services/sidebar-migration.js +1 -0
  208. package/server/services/themeSettings.js +541 -0
  209. package/server/services/users.js +8 -0
  210. package/server/templates/page.html +4 -2
  211. package/plugins/contacts/data/contacts.json +0 -20
  212. package/plugins/notes/data/notes.json +0 -1
  213. package/plugins/site-search/admin/views/site-search.js +0 -116
  214. package/plugins/site-search/config.js +0 -15
  215. package/plugins/site-search/plugin.js +0 -188
  216. package/plugins/site-search/plugin.json +0 -40
  217. package/plugins/site-search/public/inject-body.html +0 -17
  218. package/plugins/site-search/public/inject-head.html +0 -1
  219. package/plugins/site-search/public/search.css +0 -1
  220. package/plugins/site-search/public/search.js +0 -1
  221. package/plugins/todo/data/todos.json +0 -1
package/admin/js/api.js CHANGED
@@ -1 +1 @@
1
- const a="/api";function d(){return S.get("auth_token")}function c(){return S.get("auth_refresh_token")}function l(e){S.set("auth_token",e)}function h(){S.remove("auth_token"),S.remove("auth_refresh_token"),S.remove("auth_user")}async function p(){const e=c();if(!e)throw new Error("No refresh token");const o=await fetch(`${a}/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(!o.ok)throw h(),R.navigate("/login"),new Error("Token refresh failed");const{token:s}=await o.json();return l(s),s}async function t(e,o={}){let s=d();const r=i=>({...o.body!==void 0?{"Content-Type":"application/json"}:{},...o.headers,...i?{Authorization:`Bearer ${i}`}:{}});let n=await fetch(`${a}${e}`,{...o,headers:r(s)});if(n.status===401&&c())try{s=await p(),n=await fetch(`${a}${e}`,{...o,headers:r(s)})}catch{return}if(!n.ok){const i=await n.json().catch(()=>({error:"Request failed"}));throw new Error(i.error||i.message||`HTTP ${n.status}`)}return n.status===204?null:n.json()}async function u(e,o){const s=d(),r=s?{Authorization:`Bearer ${s}`}:{},n=await fetch(`${a}${e}`,{method:"POST",headers:r,body:o});if(!n.ok){const i=await n.json().catch(()=>({error:"Upload failed"}));throw new Error(i.error||i.message||`HTTP ${n.status}`)}return n.json()}export const api={auth:{setupStatus:()=>t("/auth/setup-status",{method:"GET"}),setup:e=>t("/auth/setup",{method:"POST",body:JSON.stringify(e)}),login:e=>t("/auth/login",{method:"POST",body:JSON.stringify(e)}),me:()=>t("/auth/me",{method:"GET"}),updateMe:e=>t("/auth/me",{method:"PUT",body:JSON.stringify(e)}),refresh:e=>t("/auth/refresh",{method:"POST",body:JSON.stringify({refreshToken:e})}),forgotPassword:e=>t("/auth/forgot-password",{method:"POST",body:JSON.stringify({email:e})}),resetPassword:(e,o)=>t("/auth/reset-password",{method:"POST",body:JSON.stringify({token:e,password:o})})},pages:{list:()=>t("/pages",{method:"GET"}),get:e=>t(`/pages${e}`,{method:"GET"}),create:e=>t("/pages",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/pages${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/pages${e}`,{method:"DELETE"}),preview:e=>t("/pages/preview",{method:"POST",body:JSON.stringify({markdown:e})}),previewFull:(e,o,s)=>t("/pages/preview/full",{method:"POST",body:JSON.stringify({urlPath:e,frontmatter:o,body:s})}),previewLinks:{list:e=>t(`/pages/preview-links${e?`?urlPath=${encodeURIComponent(e)}`:""}`,{method:"GET"}).then(o=>o.links||[]),create:(e,o,s)=>t("/pages/preview-links",{method:"POST",body:JSON.stringify({urlPath:e,expiresIn:o,label:s})}),revoke:e=>t(`/pages/preview-links/${e}`,{method:"DELETE"})},tags:()=>t("/pages/tags",{method:"GET"}).then(e=>e.tags||[])},settings:{get:()=>t("/settings",{method:"GET"}),save:e=>t("/settings",{method:"PUT",body:JSON.stringify(e)}),getCustomCss:()=>t("/settings/custom-css",{method:"GET"}),saveCustomCss:e=>t("/settings/custom-css",{method:"PUT",body:JSON.stringify({css:e})})},navigation:{get:()=>t("/navigation",{method:"GET"}),save:e=>t("/navigation",{method:"PUT",body:JSON.stringify(e)})},menus:{list:()=>t("/menus",{method:"GET"}),get:e=>t(`/menus/${encodeURIComponent(e)}`,{method:"GET"}),create:e=>t("/menus",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/menus/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/menus/${encodeURIComponent(e)}`,{method:"DELETE"}),duplicate:e=>t(`/menus/${encodeURIComponent(e)}/duplicate`,{method:"POST"})},menuLocations:{get:()=>t("/menu-locations",{method:"GET"}),save:e=>t("/menu-locations",{method:"PUT",body:JSON.stringify(e)}),registry:()=>t("/menu-locations/registry",{method:"GET"})},projects:{list:()=>t("/projects",{method:"GET"}),get:e=>t(`/projects/${encodeURIComponent(e)}`,{method:"GET"}),create:e=>t("/projects",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/projects/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/projects/${encodeURIComponent(e)}`,{method:"DELETE"}),artefacts:e=>t(`/projects/${encodeURIComponent(e)}/artefacts`,{method:"GET"}),untagAll:e=>t(`/projects/${encodeURIComponent(e)}/untag-all`,{method:"POST"})},apiTokens:{list:()=>t("/api-tokens",{method:"GET"}),create:e=>t("/api-tokens",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/api-tokens/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),revoke:e=>t(`/api-tokens/${encodeURIComponent(e)}`,{method:"DELETE"})},apiEndpoints:{list:()=>t("/api-endpoints",{method:"GET"}),get:e=>t(`/api-endpoints/${encodeURIComponent(e)}`,{method:"GET"}),create:e=>t("/api-endpoints",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/api-endpoints/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/api-endpoints/${encodeURIComponent(e)}`,{method:"DELETE"})},layouts:{get:()=>t("/layouts",{method:"GET"}),save:e=>t("/layouts",{method:"PUT",body:JSON.stringify(e)}),create:e=>t("/layouts",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/layouts/${e}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/layouts/${e}`,{method:"DELETE"}),getOptions:()=>t("/layouts/options",{method:"GET"}),saveOptions:e=>t("/layouts/options",{method:"PUT",body:JSON.stringify(e)})},media:{list:()=>t("/media",{method:"GET"}),upload:e=>u("/media",e),delete:e=>t(`/media/${encodeURIComponent(e)}`,{method:"DELETE"}),rename:(e,o)=>t(`/media/${encodeURIComponent(e)}`,{method:"PATCH",body:JSON.stringify({newName:o})}),info:e=>t(`/media/${encodeURIComponent(e)}/info`,{method:"GET"}),transform:(e,o)=>t(`/media/${encodeURIComponent(e)}/transform`,{method:"POST",body:JSON.stringify(o)})},users:{list:()=>t("/users",{method:"GET"}),get:e=>t(`/users/${e}`,{method:"GET"}),create:e=>t("/users",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/users/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/users/${e}`,{method:"DELETE"})},plugins:{list:()=>t("/plugins",{method:"GET"}),scaffold:e=>t("/plugins/scaffold",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/plugins/${e}`,{method:"PUT",body:JSON.stringify(o)}),files:e=>t(`/plugins/${e}/files`,{method:"GET"}),readFile:(e,o)=>t(`/plugins/${e}/file?path=${encodeURIComponent(o)}`,{method:"GET"}),writeFile:(e,o,s)=>t(`/plugins/${e}/file`,{method:"PUT",body:JSON.stringify({path:o,content:s})}),deleteFile:(e,o)=>t(`/plugins/${e}/file?path=${encodeURIComponent(o)}`,{method:"DELETE"}),restart:()=>t("/plugins/restart",{method:"POST"}),adminConfig:()=>t("/plugins/admin-config",{method:"GET"})},marketplace:{catalogue:()=>t("/plugins/marketplace",{method:"GET"}),install:(e,o)=>t("/plugins/marketplace/install",{method:"POST",body:JSON.stringify({slug:e,version:o})}),uninstall:e=>t(`/plugins/marketplace/${encodeURIComponent(e)}`,{method:"DELETE"})},collections:{list:()=>t("/collections",{method:"GET"}),proStatus:()=>t("/collections/pro-status",{method:"GET"}),get:e=>t(`/collections/${e}`,{method:"GET"}),create:e=>t("/collections",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/collections/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/collections/${e}`,{method:"DELETE"}),listEntries:(e,o={})=>{const s=new URLSearchParams(o).toString();return t(`/collections/${e}/entries${s?"?"+s:""}`,{method:"GET"})},getEntry:(e,o)=>t(`/collections/${e}/entries/${o}`,{method:"GET"}),createEntry:(e,o)=>t(`/collections/${e}/entries`,{method:"POST",body:JSON.stringify({data:o})}),updateEntry:(e,o,s)=>t(`/collections/${e}/entries/${o}`,{method:"PUT",body:JSON.stringify({data:s})}),deleteEntry:(e,o)=>t(`/collections/${e}/entries/${o}`,{method:"DELETE"}),clearEntries:e=>t(`/collections/${e}/entries`,{method:"DELETE"}),import:(e,o)=>t(`/collections/${e}/import`,{method:"POST",body:JSON.stringify({entries:o})}),publicList:(e,o={})=>{const s=new URLSearchParams(o).toString();return t(`/collections/${e}/public${s?"?"+s:""}`,{method:"GET"})},getConnections:()=>t("/collections/connections",{method:"GET"}),saveConnections:e=>t("/collections/connections",{method:"PUT",body:JSON.stringify(e)}),migrateStorage:(e,o)=>t(`/collections/${e}/migrate-storage`,{method:"POST",body:JSON.stringify({storage:o})})},forms:{list:()=>t("/forms",{method:"GET"}),create:e=>t("/forms",{method:"POST",body:JSON.stringify(e)}),get:e=>t(`/forms/${e}`,{method:"GET"}),update:(e,o)=>t(`/forms/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/forms/${e}`,{method:"DELETE"}),listSubmissions:e=>t(`/forms/${e}/submissions`,{method:"GET"}),clearSubmissions:e=>t(`/forms/${e}/submissions`,{method:"DELETE"}),deleteSubmission:(e,o)=>t(`/forms/${e}/submissions/${o}`,{method:"DELETE"}),testEmail:e=>t("/forms/test-email",{method:"POST",body:JSON.stringify({to:e})})},views:{list:()=>t("/views",{method:"GET"}),get:e=>t(`/views/${e}`,{method:"GET"}),create:e=>t("/views",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/views/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/views/${e}`,{method:"DELETE"}),execute:(e,o={})=>{const s=new URLSearchParams(o).toString();return t(`/views/${e}/execute${s?"?"+s:""}`,{method:"GET"})},forCollection:e=>t(`/views/collection/${e}`,{method:"GET"})},actions:{list:()=>t("/actions",{method:"GET"}),get:e=>t(`/actions/${e}`,{method:"GET"}),create:e=>t("/actions",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/actions/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/actions/${e}`,{method:"DELETE"}),execute:(e,o)=>t(`/actions/${e}/execute`,{method:"POST",body:JSON.stringify({entryId:o})}),forCollection:e=>t(`/actions/collection/${e}`,{method:"GET"}),checkAccess:(e,o)=>t(`/actions/${e}/check-access`,{method:"POST",body:JSON.stringify({entryIds:o})})},versions:{list:e=>t(`/versions/list${e}`),get:(e,o)=>t(`/versions/get/${encodeURIComponent(o)}${e}`),create:(e,o)=>t(`/versions/create${e}`,{method:"POST",body:JSON.stringify({label:o})}),restore:(e,o)=>t(`/versions/restore/${encodeURIComponent(o)}${e}`,{method:"POST"}),delete:(e,o)=>t(`/versions/delete/${encodeURIComponent(o)}${e}`,{method:"DELETE"}),bulkDelete:(e,o)=>t(`/versions/bulk-delete${e}`,{method:"POST",body:JSON.stringify({filenames:o})}),prune:(e,o)=>t(`/versions/prune${e}`,{method:"POST",body:JSON.stringify({keep:o})})},blocks:{list:()=>t("/blocks",{method:"GET"}),get:e=>t(`/blocks/${encodeURIComponent(e)}`,{method:"GET"}),put:(e,o)=>t(`/blocks/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/blocks/${encodeURIComponent(e)}`,{method:"DELETE"}),async exportBundle(e){const o=d(),s=await fetch(`${a}/blocks/${encodeURIComponent(e)}/export`,{headers:o?{Authorization:`Bearer ${o}`}:{}});if(!s.ok){const m=await s.json().catch(()=>({}));throw new Error(m.error||`Export failed (${s.status})`)}const r=await s.blob(),n=URL.createObjectURL(r),i=document.createElement("a");i.href=n,i.download=`${e}.dmblock.json`,document.body.appendChild(i),i.click(),i.remove(),URL.revokeObjectURL(n)},async importBundle(e,{overwrite:o=!1}={}){const s=await fetch(`${a}/blocks/import`,{method:"POST",headers:{"Content-Type":"application/json",...d()?{Authorization:`Bearer ${d()}`}:{}},body:JSON.stringify({...e,overwrite:o})});if(s.status===409){const r=await s.json().catch(()=>({})),n=new Error(r.error||"Block already exists");throw n.code="CONFLICT",n.name=r.name,n}if(!s.ok){const r=await s.json().catch(()=>({}));throw new Error(r.error||`Import failed (${s.status})`)}return s.json()}},components:{list:()=>t("/components",{method:"GET"}),get:e=>t(`/components/${encodeURIComponent(e)}`,{method:"GET"}),compile:(e,o)=>t("/components/compile",{method:"POST",body:JSON.stringify({name:e,source:o})}),put:(e,o)=>t(`/components/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/components/${encodeURIComponent(e)}`,{method:"DELETE"}),async exportBundle(e){const o=d(),s=await fetch(`${a}/components/${encodeURIComponent(e)}/export`,{headers:o?{Authorization:`Bearer ${o}`}:{}});if(!s.ok){const m=await s.json().catch(()=>({}));throw new Error(m.error||`Export failed (${s.status})`)}const r=await s.blob(),n=URL.createObjectURL(r),i=document.createElement("a");i.href=n,i.download=`${e}.dmcomponent.json`,document.body.appendChild(i),i.click(),i.remove(),URL.revokeObjectURL(n)},async importBundle(e,{overwrite:o=!1}={}){const s=await fetch(`${a}/components/import`,{method:"POST",headers:{"Content-Type":"application/json",...d()?{Authorization:`Bearer ${d()}`}:{}},body:JSON.stringify({...e,overwrite:o})});if(s.status===409){const r=await s.json().catch(()=>({})),n=new Error(r.error||"Component already exists");throw n.code="CONFLICT",n.name=r.name,n}if(!s.ok){const r=await s.json().catch(()=>({}));throw new Error(r.error||`Import failed (${s.status})`)}return s.json()}},get:e=>t(e,{method:"GET"}),post:(e,o)=>t(e,{method:"POST",body:JSON.stringify(o)}),put:(e,o)=>t(e,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(e,{method:"DELETE"}),settingsExt:{testEmail:e=>t("/settings/test-email",{method:"POST",body:JSON.stringify({to:e})})},system:{notifications:{list:()=>t("/system/notifications",{method:"GET"}),unreadCount:()=>t("/system/notifications/unread-count",{method:"GET"}),markRead:e=>t(`/system/notifications/${e}/read`,{method:"POST"}),dismiss:e=>t(`/system/notifications/${e}/dismiss`,{method:"POST"}),remove:e=>t(`/system/notifications/${e}`,{method:"DELETE"})}},dashboard:{summary:()=>t("/dashboard/summary",{method:"GET"}),summaryLite:()=>t("/dashboard/summary?lite=1",{method:"GET"})}};export function isAuthenticated(){return!!d()}export function getUser(){return S.get("auth_user")}export function setAuthData({token:e,refreshToken:o,user:s}){e&&l(e),o&&S.set("auth_refresh_token",o),s&&S.set("auth_user",s)}export function logout(){const e=c();e&&fetch(`${a}/auth/logout`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})}).catch(()=>{}),h(),R.navigate("/login")}export{t as apiRequest,p as refreshAccessToken,d as getToken,h as clearAuth};
1
+ const a="/api";function d(){return S.get("auth_token")}function c(){return S.get("auth_refresh_token")}function l(e){S.set("auth_token",e)}function h(){S.remove("auth_token"),S.remove("auth_refresh_token"),S.remove("auth_user")}async function p(){const e=c();if(!e)throw new Error("No refresh token");const o=await fetch(`${a}/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(!o.ok)throw h(),R.navigate("/login"),new Error("Token refresh failed");const{token:n}=await o.json();return l(n),n}async function t(e,o={}){let n=d();const r=i=>({...o.body!==void 0?{"Content-Type":"application/json"}:{},...o.headers,...i?{Authorization:`Bearer ${i}`}:{}});let s=await fetch(`${a}${e}`,{...o,headers:r(n)});if(s.status===401&&c())try{n=await p(),s=await fetch(`${a}${e}`,{...o,headers:r(n)})}catch{return}if(!s.ok){const i=await s.json().catch(()=>({error:"Request failed"})),m=new Error(i.error||i.message||`HTTP ${s.status}`);throw Array.isArray(i.validation)&&(m.validation=i.validation),m.status=s.status,m}return s.status===204?null:s.json()}async function u(e,o){const n=d(),r=n?{Authorization:`Bearer ${n}`}:{},s=await fetch(`${a}${e}`,{method:"POST",headers:r,body:o});if(!s.ok){const i=await s.json().catch(()=>({error:"Upload failed"}));throw new Error(i.error||i.message||`HTTP ${s.status}`)}return s.json()}export const api={auth:{setupStatus:()=>t("/auth/setup-status",{method:"GET"}),setup:e=>t("/auth/setup",{method:"POST",body:JSON.stringify(e)}),login:e=>t("/auth/login",{method:"POST",body:JSON.stringify(e)}),me:()=>t("/auth/me",{method:"GET"}),updateMe:e=>t("/auth/me",{method:"PUT",body:JSON.stringify(e)}),refresh:e=>t("/auth/refresh",{method:"POST",body:JSON.stringify({refreshToken:e})}),forgotPassword:e=>t("/auth/forgot-password",{method:"POST",body:JSON.stringify({email:e})}),resetPassword:(e,o)=>t("/auth/reset-password",{method:"POST",body:JSON.stringify({token:e,password:o})})},pages:{list:()=>t("/pages",{method:"GET"}),get:e=>t(`/pages${e}`,{method:"GET"}),create:e=>t("/pages",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/pages${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/pages${e}`,{method:"DELETE"}),preview:e=>t("/pages/preview",{method:"POST",body:JSON.stringify({markdown:e})}),previewFull:(e,o,n)=>t("/pages/preview/full",{method:"POST",body:JSON.stringify({urlPath:e,frontmatter:o,body:n})}),previewLinks:{list:e=>t(`/pages/preview-links${e?`?urlPath=${encodeURIComponent(e)}`:""}`,{method:"GET"}).then(o=>o.links||[]),create:(e,o,n)=>t("/pages/preview-links",{method:"POST",body:JSON.stringify({urlPath:e,expiresIn:o,label:n})}),revoke:e=>t(`/pages/preview-links/${e}`,{method:"DELETE"})},tags:()=>t("/pages/tags",{method:"GET"}).then(e=>e.tags||[])},settings:{get:()=>t("/settings",{method:"GET"}),save:e=>t("/settings",{method:"PUT",body:JSON.stringify(e)}),getCustomCss:()=>t("/settings/custom-css",{method:"GET"}),saveCustomCss:e=>t("/settings/custom-css",{method:"PUT",body:JSON.stringify({css:e})})},theme:{get:()=>t("/theme",{method:"GET"}),save:e=>t("/theme",{method:"PUT",body:JSON.stringify(e)}),catalog:()=>t("/theme/catalog",{method:"GET"}),themeTokens:e=>t(`/theme/themes/${encodeURIComponent(e)}`,{method:"GET"}),compile:e=>t("/theme/compile",{method:"POST",body:JSON.stringify(e)})},navigation:{get:()=>t("/navigation",{method:"GET"}),save:e=>t("/navigation",{method:"PUT",body:JSON.stringify(e)})},menus:{list:()=>t("/menus",{method:"GET"}),get:e=>t(`/menus/${encodeURIComponent(e)}`,{method:"GET"}),create:e=>t("/menus",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/menus/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/menus/${encodeURIComponent(e)}`,{method:"DELETE"}),duplicate:e=>t(`/menus/${encodeURIComponent(e)}/duplicate`,{method:"POST"})},contextMenus:{list:()=>t("/context-menus",{method:"GET"}),get:e=>t(`/context-menus/${encodeURIComponent(e)}`,{method:"GET"}),create:e=>t("/context-menus",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/context-menus/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/context-menus/${encodeURIComponent(e)}`,{method:"DELETE"}),duplicate:e=>t(`/context-menus/${encodeURIComponent(e)}/duplicate`,{method:"POST"}),actionTypes:()=>t("/context-menus/action-types",{method:"GET"})},menuLocations:{get:()=>t("/menu-locations",{method:"GET"}),save:e=>t("/menu-locations",{method:"PUT",body:JSON.stringify(e)}),registry:()=>t("/menu-locations/registry",{method:"GET"})},projects:{list:()=>t("/projects",{method:"GET"}),get:e=>t(`/projects/${encodeURIComponent(e)}`,{method:"GET"}),create:e=>t("/projects",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/projects/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/projects/${encodeURIComponent(e)}`,{method:"DELETE"}),artefacts:e=>t(`/projects/${encodeURIComponent(e)}/artefacts`,{method:"GET"}),untagAll:e=>t(`/projects/${encodeURIComponent(e)}/untag-all`,{method:"POST"})},apiTokens:{list:()=>t("/api-tokens",{method:"GET"}),create:e=>t("/api-tokens",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/api-tokens/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),revoke:e=>t(`/api-tokens/${encodeURIComponent(e)}`,{method:"DELETE"})},apiEndpoints:{list:()=>t("/api-endpoints",{method:"GET"}),get:e=>t(`/api-endpoints/${encodeURIComponent(e)}`,{method:"GET"}),create:e=>t("/api-endpoints",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/api-endpoints/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/api-endpoints/${encodeURIComponent(e)}`,{method:"DELETE"})},layouts:{get:()=>t("/layouts",{method:"GET"}),save:e=>t("/layouts",{method:"PUT",body:JSON.stringify(e)}),create:e=>t("/layouts",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/layouts/${e}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/layouts/${e}`,{method:"DELETE"}),getOptions:()=>t("/layouts/options",{method:"GET"}),saveOptions:e=>t("/layouts/options",{method:"PUT",body:JSON.stringify(e)})},media:{list:()=>t("/media",{method:"GET"}),upload:e=>u("/media",e),delete:e=>t(`/media/${encodeURIComponent(e)}`,{method:"DELETE"}),rename:(e,o)=>t(`/media/${encodeURIComponent(e)}`,{method:"PATCH",body:JSON.stringify({newName:o})}),info:e=>t(`/media/${encodeURIComponent(e)}/info`,{method:"GET"}),transform:(e,o)=>t(`/media/${encodeURIComponent(e)}/transform`,{method:"POST",body:JSON.stringify(o)})},users:{list:()=>t("/users",{method:"GET"}),get:e=>t(`/users/${e}`,{method:"GET"}),create:e=>t("/users",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/users/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/users/${e}`,{method:"DELETE"})},plugins:{list:()=>t("/plugins",{method:"GET"}),scaffold:e=>t("/plugins/scaffold",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/plugins/${e}`,{method:"PUT",body:JSON.stringify(o)}),files:e=>t(`/plugins/${e}/files`,{method:"GET"}),readFile:(e,o)=>t(`/plugins/${e}/file?path=${encodeURIComponent(o)}`,{method:"GET"}),writeFile:(e,o,n)=>t(`/plugins/${e}/file`,{method:"PUT",body:JSON.stringify({path:o,content:n})}),deleteFile:(e,o)=>t(`/plugins/${e}/file?path=${encodeURIComponent(o)}`,{method:"DELETE"}),restart:()=>t("/plugins/restart",{method:"POST"}),adminConfig:()=>t("/plugins/admin-config",{method:"GET"})},marketplace:{catalogue:()=>t("/plugins/marketplace",{method:"GET"}),install:(e,o)=>t("/plugins/marketplace/install",{method:"POST",body:JSON.stringify({slug:e,version:o})}),uninstall:e=>t(`/plugins/marketplace/${encodeURIComponent(e)}`,{method:"DELETE"})},collections:{list:()=>t("/collections",{method:"GET"}),proStatus:()=>t("/collections/pro-status",{method:"GET"}),get:e=>t(`/collections/${e}`,{method:"GET"}),create:e=>t("/collections",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/collections/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/collections/${e}`,{method:"DELETE"}),listEntries:(e,o={})=>{const n=new URLSearchParams(o).toString();return t(`/collections/${e}/entries${n?"?"+n:""}`,{method:"GET"})},getEntry:(e,o)=>t(`/collections/${e}/entries/${o}`,{method:"GET"}),createEntry:(e,o)=>t(`/collections/${e}/entries`,{method:"POST",body:JSON.stringify({data:o})}),updateEntry:(e,o,n)=>t(`/collections/${e}/entries/${o}`,{method:"PUT",body:JSON.stringify({data:n})}),deleteEntry:(e,o)=>t(`/collections/${e}/entries/${o}`,{method:"DELETE"}),clearEntries:e=>t(`/collections/${e}/entries`,{method:"DELETE"}),import:(e,o)=>t(`/collections/${e}/import`,{method:"POST",body:JSON.stringify({entries:o})}),publicList:(e,o={})=>{const n=new URLSearchParams(o).toString();return t(`/collections/${e}/public${n?"?"+n:""}`,{method:"GET"})},getConnections:()=>t("/collections/connections",{method:"GET"}),saveConnections:e=>t("/collections/connections",{method:"PUT",body:JSON.stringify(e)}),migrateStorage:(e,o)=>t(`/collections/${e}/migrate-storage`,{method:"POST",body:JSON.stringify({storage:o})})},forms:{list:()=>t("/forms",{method:"GET"}),create:e=>t("/forms",{method:"POST",body:JSON.stringify(e)}),get:e=>t(`/forms/${e}`,{method:"GET"}),update:(e,o)=>t(`/forms/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/forms/${e}`,{method:"DELETE"}),listSubmissions:e=>t(`/forms/${e}/submissions`,{method:"GET"}),clearSubmissions:e=>t(`/forms/${e}/submissions`,{method:"DELETE"}),deleteSubmission:(e,o)=>t(`/forms/${e}/submissions/${o}`,{method:"DELETE"}),testEmail:e=>t("/forms/test-email",{method:"POST",body:JSON.stringify({to:e})})},views:{list:()=>t("/views",{method:"GET"}),get:e=>t(`/views/${e}`,{method:"GET"}),create:e=>t("/views",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/views/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/views/${e}`,{method:"DELETE"}),execute:(e,o={})=>{const n=new URLSearchParams(o).toString();return t(`/views/${e}/execute${n?"?"+n:""}`,{method:"GET"})},forCollection:e=>t(`/views/collection/${e}`,{method:"GET"})},actions:{list:()=>t("/actions",{method:"GET"}),get:e=>t(`/actions/${e}`,{method:"GET"}),create:e=>t("/actions",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/actions/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/actions/${e}`,{method:"DELETE"}),execute:(e,o)=>t(`/actions/${e}/execute`,{method:"POST",body:JSON.stringify({entryId:o})}),forCollection:e=>t(`/actions/collection/${e}`,{method:"GET"}),checkAccess:(e,o)=>t(`/actions/${e}/check-access`,{method:"POST",body:JSON.stringify({entryIds:o})})},versions:{list:e=>t(`/versions/list${e}`),get:(e,o)=>t(`/versions/get/${encodeURIComponent(o)}${e}`),create:(e,o)=>t(`/versions/create${e}`,{method:"POST",body:JSON.stringify({label:o})}),restore:(e,o)=>t(`/versions/restore/${encodeURIComponent(o)}${e}`,{method:"POST"}),delete:(e,o)=>t(`/versions/delete/${encodeURIComponent(o)}${e}`,{method:"DELETE"}),bulkDelete:(e,o)=>t(`/versions/bulk-delete${e}`,{method:"POST",body:JSON.stringify({filenames:o})}),prune:(e,o)=>t(`/versions/prune${e}`,{method:"POST",body:JSON.stringify({keep:o})})},blocks:{list:()=>t("/blocks",{method:"GET"}),get:e=>t(`/blocks/${encodeURIComponent(e)}`,{method:"GET"}),put:(e,o)=>t(`/blocks/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/blocks/${encodeURIComponent(e)}`,{method:"DELETE"}),async exportBundle(e){const o=d(),n=await fetch(`${a}/blocks/${encodeURIComponent(e)}/export`,{headers:o?{Authorization:`Bearer ${o}`}:{}});if(!n.ok){const m=await n.json().catch(()=>({}));throw new Error(m.error||`Export failed (${n.status})`)}const r=await n.blob(),s=URL.createObjectURL(r),i=document.createElement("a");i.href=s,i.download=`${e}.dmblock.json`,document.body.appendChild(i),i.click(),i.remove(),URL.revokeObjectURL(s)},async importBundle(e,{overwrite:o=!1}={}){const n=await fetch(`${a}/blocks/import`,{method:"POST",headers:{"Content-Type":"application/json",...d()?{Authorization:`Bearer ${d()}`}:{}},body:JSON.stringify({...e,overwrite:o})});if(n.status===409){const r=await n.json().catch(()=>({})),s=new Error(r.error||"Block already exists");throw s.code="CONFLICT",s.name=r.name,s}if(!n.ok){const r=await n.json().catch(()=>({}));throw new Error(r.error||`Import failed (${n.status})`)}return n.json()}},components:{list:()=>t("/components",{method:"GET"}),get:e=>t(`/components/${encodeURIComponent(e)}`,{method:"GET"}),compile:(e,o)=>t("/components/compile",{method:"POST",body:JSON.stringify({name:e,source:o})}),put:(e,o)=>t(`/components/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/components/${encodeURIComponent(e)}`,{method:"DELETE"}),async exportBundle(e){const o=d(),n=await fetch(`${a}/components/${encodeURIComponent(e)}/export`,{headers:o?{Authorization:`Bearer ${o}`}:{}});if(!n.ok){const m=await n.json().catch(()=>({}));throw new Error(m.error||`Export failed (${n.status})`)}const r=await n.blob(),s=URL.createObjectURL(r),i=document.createElement("a");i.href=s,i.download=`${e}.dmcomponent.json`,document.body.appendChild(i),i.click(),i.remove(),URL.revokeObjectURL(s)},async importBundle(e,{overwrite:o=!1}={}){const n=await fetch(`${a}/components/import`,{method:"POST",headers:{"Content-Type":"application/json",...d()?{Authorization:`Bearer ${d()}`}:{}},body:JSON.stringify({...e,overwrite:o})});if(n.status===409){const r=await n.json().catch(()=>({})),s=new Error(r.error||"Component already exists");throw s.code="CONFLICT",s.name=r.name,s}if(!n.ok){const r=await n.json().catch(()=>({}));throw new Error(r.error||`Import failed (${n.status})`)}return n.json()}},get:e=>t(e,{method:"GET"}),post:(e,o)=>t(e,{method:"POST",body:JSON.stringify(o)}),put:(e,o)=>t(e,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(e,{method:"DELETE"}),settingsExt:{testEmail:e=>t("/settings/test-email",{method:"POST",body:JSON.stringify({to:e})})},system:{notifications:{list:()=>t("/system/notifications",{method:"GET"}),unreadCount:()=>t("/system/notifications/unread-count",{method:"GET"}),markRead:e=>t(`/system/notifications/${e}/read`,{method:"POST"}),dismiss:e=>t(`/system/notifications/${e}/dismiss`,{method:"POST"}),remove:e=>t(`/system/notifications/${e}`,{method:"DELETE"})}},dashboard:{summary:()=>t("/dashboard/summary",{method:"GET"}),summaryLite:()=>t("/dashboard/summary?lite=1",{method:"GET"})}};export function isAuthenticated(){return!!d()}export function getUser(){return S.get("auth_user")}export function setAuthData({token:e,refreshToken:o,user:n}){e&&l(e),o&&S.set("auth_refresh_token",o),n&&S.set("auth_user",n)}export function logout(){const e=c();e&&fetch(`${a}/auth/logout`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})}).catch(()=>{}),h(),R.navigate("/login")}export{t as apiRequest,p as refreshAccessToken,d as getToken,h as clearAuth};
package/admin/js/app.js CHANGED
@@ -1,6 +1,6 @@
1
- import{views as W}from"./views/index.js";import{api as l,getUser as c,isAuthenticated as m,logout as _}from"./api.js";import{installHttpInterceptor as B}from"./http-interceptor.js";$(()=>{B(),(async()=>{try{const t=m()?await l.settings.get():null;Domma.theme.init({theme:t?.adminTheme||"charcoal-dark",persist:!0})}catch{Domma.theme.init({theme:"charcoal-dark",persist:!0})}})();const A=["jb-company","jb-agent","jb-candidate"],k=["/job-board","/my-profile"];function h(t){return t&&A.includes(t.role)}function w(t){return k.some(e=>t===e||t.startsWith(e+"/"))}R.use(async(t,e,o)=>{if(t.path==="/login"||t.path==="/reset-password")return o();if(!m()){R.navigate("/login");return}if(h(c())&&!w(t.path)){R.navigate("/job-board");return}o()});async function f(){try{return(await l.get("/auth/permissions")).permissions||[]}catch{return[]}}async function C(t){const{renderAdminSidebar:e}=await import("./lib/sidebar-renderer.js");await e({mount:"#admin-sidebar",permissions:t})}M.subscribe("router:afterChange",({to:t})=>{(t.path==="/login"||t.path==="/reset-password")&&n&&(clearInterval(n),n=null)});const I=[{path:"/",view:"dashboard",title:"Dashboard - Domma CMS"},{path:"/pages",view:"pages",title:"Pages - Domma CMS"},{path:"/pages/new",view:"pageEditor",title:"New Page - Domma CMS"},{path:"/pages/edit/*",view:"pageEditor",title:"Edit Page - Domma CMS"},{path:"/media",view:"media",title:"Media - Domma CMS"},{path:"/menus",view:"menus",title:"Menus - Domma CMS"},{path:"/menus/new",view:"menuEditor",title:"New Menu - Domma CMS"},{path:"/menus/edit/:slug",view:"menuEditor",title:"Edit Menu - Domma CMS"},{path:"/menu-locations",view:"menuLocations",title:"Menu Locations - Domma CMS"},{path:"/navigation",view:"menusRedirect",title:"Menus - Domma CMS"},{path:"/layouts",view:"layouts",title:"Layouts - Domma CMS"},{path:"/settings",view:"settings",title:"Settings - Domma CMS"},{path:"/users",view:"users",title:"Users - Domma CMS"},{path:"/users/new",view:"userEditor",title:"New User - Domma CMS"},{path:"/users/edit/:id",view:"userEditor",title:"Edit User - Domma CMS"},{path:"/plugins",view:"plugins",title:"Plugins - Domma CMS"},{path:"/plugins/code/:name",view:"pluginCode",title:"Plugin Code - Domma CMS"},{path:"/documentation",view:"documentation",title:"Usage - Domma CMS"},{path:"/tutorials",view:"tutorials",title:"Tutorials - Domma CMS"},{path:"/tutorials/crud",view:"tutorialCrud",title:"Building a CRUD App - Domma CMS"},{path:"/tutorials/plugin",view:"tutorialPlugin",title:"Writing a Plugin - Domma CMS"},{path:"/tutorials/forms",view:"tutorialForms",title:"Form Follow-Up - Domma CMS"},{path:"/docs/components",view:"componentsReference",title:"Components Reference - Domma CMS"},{path:"/docs/components-howto",view:"componentsHowto",title:"Components How-To - Domma CMS"},{path:"/docs/components-walkthrough",view:"componentsWalkthrough",title:"Components Walkthrough - Domma CMS"},{path:"/docs/components-rules",view:"componentsRules",title:"Components Rules - Domma CMS"},{path:"/docs/usage/pages",view:"usagePages",title:"Pages - Usage - Domma CMS"},{path:"/docs/usage/media",view:"usageMedia",title:"Media - Usage - Domma CMS"},{path:"/docs/usage/navigation",view:"usageNavigation",title:"Navigation - Usage - Domma CMS"},{path:"/docs/usage/dconfig",view:"usageDconfig",title:"DConfig - Usage - Domma CMS"},{path:"/docs/usage/shortcodes",view:"usageShortcodes",title:"Shortcodes - Usage - Domma CMS"},{path:"/docs/usage/site-settings",view:"usageSiteSettings",title:"Site Settings - Usage - Domma CMS"},{path:"/docs/usage/plugins",view:"usagePlugins",title:"Plugins - Usage - Domma CMS"},{path:"/docs/usage/users-roles",view:"usageUsersRoles",title:"Users & Roles - Usage - Domma CMS"},{path:"/docs/usage/views",view:"usageViews",title:"Views - Usage - Domma CMS"},{path:"/docs/usage/actions",view:"usageActions",title:"Actions - Usage - Domma CMS"},{path:"/docs/usage/cta-shortcode",view:"usageCtaShortcode",title:"CTA Shortcode - Usage - Domma CMS"},{path:"/docs/api/authentication",view:"apiRefAuthentication",title:"Authentication - API - Domma CMS"},{path:"/docs/api/pages",view:"apiRefPages",title:"Pages - API - Domma CMS"},{path:"/docs/api/settings",view:"apiRefSettings",title:"Settings - API - Domma CMS"},{path:"/docs/api/layouts",view:"apiRefLayouts",title:"Layouts - API - Domma CMS"},{path:"/docs/api/navigation",view:"apiRefNavigation",title:"Navigation - API - Domma CMS"},{path:"/docs/api/media",view:"apiRefMedia",title:"Media - API - Domma CMS"},{path:"/docs/api/users",view:"apiRefUsers",title:"Users - API - Domma CMS"},{path:"/docs/api/plugins",view:"apiRefPlugins",title:"Plugins - API - Domma CMS"},{path:"/docs/api/collections",view:"apiRefCollections",title:"Collections - API - Domma CMS"},{path:"/docs/api/views",view:"apiRefViews",title:"Views API - Domma CMS"},{path:"/docs/api/actions",view:"apiRefActions",title:"Actions API - Domma CMS"},{path:"/api-reference",view:"apiReference",title:"API Reference - Domma CMS"},{path:"/collections",view:"collections",title:"Collections - Domma CMS"},{path:"/collections/new",view:"collectionEditor",title:"New Collection - Domma CMS"},{path:"/collections/edit/:slug",view:"collectionEditor",title:"Edit Collection - Domma CMS"},{path:"/collections/:slug/entries",view:"collectionEntries",title:"Entries - Domma CMS"},{path:"/forms",view:"forms",title:"Forms - Domma CMS"},{path:"/forms/new",view:"formEditor",title:"New Form - Domma CMS"},{path:"/forms/edit/:slug",view:"formEditor",title:"Edit Form - Domma CMS"},{path:"/forms/:slug/submissions",view:"formSubmissions",title:"Submissions - Domma CMS"},{path:"/views",view:"viewsList",title:"Views - Domma CMS"},{path:"/views/new",view:"viewEditor",title:"New View - Domma CMS"},{path:"/views/edit/:slug",view:"viewEditor",title:"Edit View - Domma CMS"},{path:"/views/:slug/preview",view:"viewPreview",title:"View Preview - Domma CMS"},{path:"/actions",view:"actionsList",title:"Actions - Domma CMS"},{path:"/actions/new",view:"actionEditor",title:"New Action - Domma CMS"},{path:"/actions/edit/:slug",view:"actionEditor",title:"Edit Action - Domma CMS"},{path:"/pro/docs",view:"proDocs",title:"Pro Documentation - Domma CMS"},{path:"/blocks",view:"blocks",title:"Blocks - Domma CMS"},{path:"/blocks/new",view:"blockEditor",title:"New Block - Domma CMS"},{path:"/blocks/edit/:name",view:"blockEditor",title:"Edit Block - Domma CMS"},{path:"/components",view:"components",title:"Components - Domma CMS"},{path:"/components/new",view:"componentEditor",title:"New Component - Domma CMS"},{path:"/components/edit/:name",view:"componentEditor",title:"Edit Component - Domma CMS"},{path:"/my-profile",view:"myProfile",title:"My Profile - Domma CMS"},{path:"/roles",view:"roles",title:"Roles & Permissions - Domma CMS"},{path:"/roles/edit/:id",view:"roleEditor",title:"Edit Role - Domma CMS"},{path:"/effects",view:"effects",title:"Effects - Domma CMS"},{path:"/system/notifications",view:"notifications",title:"Notifications - Domma CMS"},{path:"/api-tokens",view:"apiTokens",title:"API Tokens - Domma CMS"},{path:"/api-endpoints",view:"apiEndpoints",title:"API Builder - Domma CMS"},{path:"/api-endpoints/new",view:"apiEndpointEditor",title:"New API Endpoint - Domma CMS"},{path:"/api-endpoints/edit/:id",view:"apiEndpointEditor",title:"Edit API Endpoint - Domma CMS"},{path:"/projects",view:"projects",title:"Projects - Domma CMS"},{path:"/projects/new",view:"projectEditor",title:"New Project - Domma CMS"},{path:"/projects/edit/:slug",view:"projectEditor",title:"Edit Project - Domma CMS"},{path:"/projects/:slug/settings",view:"projectSettings",title:"Project settings - Domma CMS"},{path:"/projects/:slug/pages",view:"pages",title:"Project pages - Domma CMS"},{path:"/projects/:slug/collections",view:"collections",title:"Project collections - Domma CMS"},{path:"/projects/:slug/forms",view:"forms",title:"Project forms - Domma CMS"},{path:"/projects/:slug/actions",view:"actionsList",title:"Project actions - Domma CMS"},{path:"/projects/:slug/menus",view:"menus",title:"Project menus - Domma CMS"},{path:"/projects/:slug/blocks",view:"blocks",title:"Project blocks - Domma CMS"},{path:"/projects/:slug/components",view:"components",title:"Project components - Domma CMS"},{path:"/projects/:slug/views",view:"viewsList",title:"Project views - Domma CMS"},{path:"/projects/:slug/roles",view:"roles",title:"Project roles - Domma CMS"},{path:"/projects/:slug/users",view:"users",title:"Project users - Domma CMS"},{path:"/projects/:slug/apis",view:"apiEndpoints",title:"Project APIs - Domma CMS"},{path:"/projects/:slug",view:"projectDetail",title:"Project - Domma CMS"},{path:"/login",view:"login",title:"Sign in - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}},{path:"/reset-password",view:"login",title:"Reset Password - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}}];M.subscribe("router:afterChange",async({to:t,from:e})=>{if(!(t.path==="/login"||t.path==="/reset-password")){if(h(c())&&!w(t.path)){R.navigate("/job-board");return}if($("#admin-sidebar").show(),$("#admin-topbar").show(),e?.path==="/login"||e?.path==="/reset-password"){$("#topbar-user-name").remove(),await P();const o=await f();C(o);try{const i=await l.system.notifications.list().catch(()=>[]),s=(Array.isArray(i)?i:[]).filter(a=>a.unread&&["warning","critical"].includes(a.data?.severity)).slice(0,3);for(const a of s){const r=p(a.data?.title||""),d=p((a.data?.body||"").slice(0,120));E.toast(`${r} - ${d}`,{type:a.data?.severity==="critical"?"error":"warning",duration:0})}}catch{}}j()}}),M.subscribe("router:afterChange",()=>{setTimeout(()=>{$(".btn-primary, .btn-danger").length&&Domma.effects.reveal(".btn-primary, .btn-danger",{animation:"fade",stagger:40,duration:300})},50)});const U=["#field-project","#menu-project","#collection-project","#endpoint-project","#action-project","#view-project","#block-project","#component-project","#role-project",'[name="project"]','[name="ownedByProject"]'];function N(t){return/^\/(pages|menus|collections|forms|actions|views|blocks|components|users|roles|api-endpoints)\/(new|edit\/)/.test(t)}M.subscribe("router:afterChange",({to:t})=>{const e=t?String(t.path).split("?")[0]:"";if(!t||!N(e))return;const i=new URLSearchParams(location.hash.split("?")[1]||"").get("project");if(!i||!/\/new$/.test(e))return;let s=0;const a=60,r=1500,d=()=>{s+=a;for(const L of U){const u=document.querySelector(L);if(!u)continue;if(Array.from(u.options||[]).find(T=>T.value===i)){u.value=i,u.dispatchEvent(new Event("change",{bubbles:!0}));return}}s<r&&setTimeout(d,a)};setTimeout(d,a)});const D="cms_card_states",g=S.get(D)||{};M.subscribe("router:afterChange",()=>{setTimeout(()=>{$("#view-container .card-collapsible").each(function(){const t=$(this).find(".card-header h2, .card-header h3").first().text().trim();t&&g[t]==="collapsed"&&$(this).addClass("card-collapsed")})},200)}),$("#view-container").on("click",".card-collapsible .card-header",function(t){if($(t.target).closest("button, a").length)return;const e=$(this).closest(".card");e.toggleClass("card-collapsed");const o=$(this).find("h2, h3").first().text().trim();o&&(g[o]=e.hasClass("card-collapsed")?"collapsed":"open",S.set(D,g))}),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash==="#/login"||window.location.hash.startsWith("#/reset-password"))return;const e=document.querySelector("#view-container .view-header button.btn-primary");e&&(t.preventDefault(),e.click())});const b={...W},v=[...I];async function P(){if(m())try{const t=await l.plugins.adminConfig();t.routes?.length&&v.push(...t.routes);for(const[e,o]of Object.entries(t.views||{}))try{const i=await import(`/plugins/${o.entry}`);b[e]=i[o.exportName]}catch{}for(const{id:e,href:o}of t.css||[])if(!document.getElementById(e)){const i=document.createElement("link");i.id=e,i.rel="stylesheet",i.href=o,document.head.appendChild(i)}}catch{}}function j(){const t=c();if(!t||$("#topbar-user-name").length)return;const o={"super-admin":"Super Admin",admin:"Admin",user:"User"}[t.role]||t.role;$("#topbar-user").html(`
1
+ import{views as T}from"./views/index.js";import{api as l,getUser as c,isAuthenticated as m,logout as W}from"./api.js";import{installHttpInterceptor as _}from"./http-interceptor.js";import{installResizableSlideovers as B}from"./lib/slideover-resizable.js";$(()=>{_(),B(),(async()=>{try{const t=m()?await l.theme.get():null;Domma.theme.init({theme:t?.adminTheme||"charcoal-dark",persist:!0})}catch{Domma.theme.init({theme:"charcoal-dark",persist:!0})}})();const A=["jb-company","jb-agent","jb-candidate"],k=["/job-board","/my-profile"];function h(t){return t&&A.includes(t.role)}function w(t){return k.some(e=>t===e||t.startsWith(e+"/"))}R.use(async(t,e,a)=>{if(t.path==="/login"||t.path==="/reset-password")return a();if(!m()){R.navigate("/login");return}if(h(c())&&!w(t.path)){R.navigate("/job-board");return}a()});async function f(){try{return(await l.get("/auth/permissions")).permissions||[]}catch{return[]}}async function C(t){const{renderAdminSidebar:e}=await import("./lib/sidebar-renderer.js");await e({mount:"#admin-sidebar",permissions:t})}M.subscribe("router:afterChange",({to:t})=>{(t.path==="/login"||t.path==="/reset-password")&&n&&(clearInterval(n),n=null)});const I=[{path:"/",view:"dashboard",title:"Dashboard - Domma CMS"},{path:"/pages",view:"pages",title:"Pages - Domma CMS"},{path:"/pages/new",view:"pageEditor",title:"New Page - Domma CMS"},{path:"/pages/edit/*",view:"pageEditor",title:"Edit Page - Domma CMS"},{path:"/media",view:"media",title:"Media - Domma CMS"},{path:"/menus",view:"menus",title:"Menus - Domma CMS"},{path:"/context-menus",view:"contextMenus",title:"Context Menus - Domma CMS"},{path:"/context-menus/new",view:"contextMenuEditor",title:"New Context Menu - Domma CMS"},{path:"/context-menus/edit/:slug",view:"contextMenuEditor",title:"Edit Context Menu - Domma CMS"},{path:"/menus/new",view:"menuEditor",title:"New Menu - Domma CMS"},{path:"/menus/edit/:slug",view:"menuEditor",title:"Edit Menu - Domma CMS"},{path:"/menu-locations",view:"menuLocations",title:"Menu Locations - Domma CMS"},{path:"/navigation",view:"menusRedirect",title:"Menus - Domma CMS"},{path:"/layouts",view:"layouts",title:"Layouts - Domma CMS"},{path:"/settings",view:"settings",title:"Settings - Domma CMS"},{path:"/theme",view:"theme",title:"Theme - Domma CMS"},{path:"/users",view:"users",title:"Users - Domma CMS"},{path:"/users/new",view:"userEditor",title:"New User - Domma CMS"},{path:"/users/edit/:id",view:"userEditor",title:"Edit User - Domma CMS"},{path:"/plugins",view:"plugins",title:"Plugins - Domma CMS"},{path:"/plugins/code/:name",view:"pluginCode",title:"Plugin Code - Domma CMS"},{path:"/documentation",view:"documentation",title:"Usage - Domma CMS"},{path:"/tutorials",view:"tutorials",title:"Tutorials - Domma CMS"},{path:"/tutorials/crud",view:"tutorialCrud",title:"Building a CRUD App - Domma CMS"},{path:"/tutorials/plugin",view:"tutorialPlugin",title:"Writing a Plugin - Domma CMS"},{path:"/tutorials/forms",view:"tutorialForms",title:"Form Follow-Up - Domma CMS"},{path:"/docs/components",view:"componentsReference",title:"Components Reference - Domma CMS"},{path:"/docs/components-howto",view:"componentsHowto",title:"Components How-To - Domma CMS"},{path:"/docs/components-walkthrough",view:"componentsWalkthrough",title:"Components Walkthrough - Domma CMS"},{path:"/docs/components-rules",view:"componentsRules",title:"Components Rules - Domma CMS"},{path:"/docs/usage/pages",view:"usagePages",title:"Pages - Usage - Domma CMS"},{path:"/docs/usage/media",view:"usageMedia",title:"Media - Usage - Domma CMS"},{path:"/docs/usage/navigation",view:"usageNavigation",title:"Navigation - Usage - Domma CMS"},{path:"/docs/usage/dconfig",view:"usageDconfig",title:"DConfig - Usage - Domma CMS"},{path:"/docs/usage/shortcodes",view:"usageShortcodes",title:"Shortcodes - Usage - Domma CMS"},{path:"/docs/usage/site-settings",view:"usageSiteSettings",title:"Site Settings - Usage - Domma CMS"},{path:"/docs/usage/plugins",view:"usagePlugins",title:"Plugins - Usage - Domma CMS"},{path:"/docs/usage/users-roles",view:"usageUsersRoles",title:"Users & Roles - Usage - Domma CMS"},{path:"/docs/usage/views",view:"usageViews",title:"Views - Usage - Domma CMS"},{path:"/docs/usage/actions",view:"usageActions",title:"Actions - Usage - Domma CMS"},{path:"/docs/usage/cta-shortcode",view:"usageCtaShortcode",title:"CTA Shortcode - Usage - Domma CMS"},{path:"/docs/api/authentication",view:"apiRefAuthentication",title:"Authentication - API - Domma CMS"},{path:"/docs/api/pages",view:"apiRefPages",title:"Pages - API - Domma CMS"},{path:"/docs/api/settings",view:"apiRefSettings",title:"Settings - API - Domma CMS"},{path:"/docs/api/layouts",view:"apiRefLayouts",title:"Layouts - API - Domma CMS"},{path:"/docs/api/navigation",view:"apiRefNavigation",title:"Navigation - API - Domma CMS"},{path:"/docs/api/media",view:"apiRefMedia",title:"Media - API - Domma CMS"},{path:"/docs/api/users",view:"apiRefUsers",title:"Users - API - Domma CMS"},{path:"/docs/api/plugins",view:"apiRefPlugins",title:"Plugins - API - Domma CMS"},{path:"/docs/api/collections",view:"apiRefCollections",title:"Collections - API - Domma CMS"},{path:"/docs/api/views",view:"apiRefViews",title:"Views API - Domma CMS"},{path:"/docs/api/actions",view:"apiRefActions",title:"Actions API - Domma CMS"},{path:"/api-reference",view:"apiReference",title:"API Reference - Domma CMS"},{path:"/collections",view:"collections",title:"Collections - Domma CMS"},{path:"/collections/new",view:"collectionEditor",title:"New Collection - Domma CMS"},{path:"/collections/edit/:slug",view:"collectionEditor",title:"Edit Collection - Domma CMS"},{path:"/collections/:slug/entries",view:"collectionEntries",title:"Entries - Domma CMS"},{path:"/forms",view:"forms",title:"Forms - Domma CMS"},{path:"/forms/new",view:"formEditor",title:"New Form - Domma CMS"},{path:"/forms/edit/:slug",view:"formEditor",title:"Edit Form - Domma CMS"},{path:"/forms/:slug/submissions",view:"formSubmissions",title:"Submissions - Domma CMS"},{path:"/views",view:"viewsList",title:"Views - Domma CMS"},{path:"/views/new",view:"viewEditor",title:"New View - Domma CMS"},{path:"/views/edit/:slug",view:"viewEditor",title:"Edit View - Domma CMS"},{path:"/views/:slug/preview",view:"viewPreview",title:"View Preview - Domma CMS"},{path:"/actions",view:"actionsList",title:"Actions - Domma CMS"},{path:"/actions/new",view:"actionEditor",title:"New Action - Domma CMS"},{path:"/actions/edit/:slug",view:"actionEditor",title:"Edit Action - Domma CMS"},{path:"/pro/docs",view:"proDocs",title:"Pro Documentation - Domma CMS"},{path:"/blocks",view:"blocks",title:"Blocks - Domma CMS"},{path:"/blocks/new",view:"blockEditor",title:"New Block - Domma CMS"},{path:"/blocks/edit/:name",view:"blockEditor",title:"Edit Block - Domma CMS"},{path:"/components",view:"components",title:"Components - Domma CMS"},{path:"/components/new",view:"componentEditor",title:"New Component - Domma CMS"},{path:"/components/edit/:name",view:"componentEditor",title:"Edit Component - Domma CMS"},{path:"/my-profile",view:"myProfile",title:"My Profile - Domma CMS"},{path:"/roles",view:"roles",title:"Roles & Permissions - Domma CMS"},{path:"/roles/edit/:id",view:"roleEditor",title:"Edit Role - Domma CMS"},{path:"/effects",view:"effects",title:"Effects - Domma CMS"},{path:"/search",view:"search",title:"Search - Domma CMS"},{path:"/system/notifications",view:"notifications",title:"Notifications - Domma CMS"},{path:"/api-tokens",view:"apiTokens",title:"API Tokens - Domma CMS"},{path:"/api-endpoints",view:"apiEndpoints",title:"API Builder - Domma CMS"},{path:"/api-endpoints/new",view:"apiEndpointEditor",title:"New API Endpoint - Domma CMS"},{path:"/api-endpoints/edit/:id",view:"apiEndpointEditor",title:"Edit API Endpoint - Domma CMS"},{path:"/projects",view:"projects",title:"Projects - Domma CMS"},{path:"/projects/new",view:"projectEditor",title:"New Project - Domma CMS"},{path:"/projects/edit/:slug",view:"projectEditor",title:"Edit Project - Domma CMS"},{path:"/projects/:slug/settings",view:"projectSettings",title:"Project settings - Domma CMS"},{path:"/projects/:slug/pages",view:"pages",title:"Project pages - Domma CMS"},{path:"/projects/:slug/collections",view:"collections",title:"Project collections - Domma CMS"},{path:"/projects/:slug/forms",view:"forms",title:"Project forms - Domma CMS"},{path:"/projects/:slug/actions",view:"actionsList",title:"Project actions - Domma CMS"},{path:"/projects/:slug/menus",view:"menus",title:"Project menus - Domma CMS"},{path:"/projects/:slug/blocks",view:"blocks",title:"Project blocks - Domma CMS"},{path:"/projects/:slug/components",view:"components",title:"Project components - Domma CMS"},{path:"/projects/:slug/views",view:"viewsList",title:"Project views - Domma CMS"},{path:"/projects/:slug/roles",view:"roles",title:"Project roles - Domma CMS"},{path:"/projects/:slug/users",view:"users",title:"Project users - Domma CMS"},{path:"/projects/:slug/apis",view:"apiEndpoints",title:"Project APIs - Domma CMS"},{path:"/projects/:slug",view:"projectDetail",title:"Project - Domma CMS"},{path:"/login",view:"login",title:"Sign in - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}},{path:"/reset-password",view:"login",title:"Reset Password - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}}];M.subscribe("router:afterChange",async({to:t,from:e})=>{if(!(t.path==="/login"||t.path==="/reset-password")){if(h(c())&&!w(t.path)){R.navigate("/job-board");return}if($("#admin-sidebar").show(),$("#admin-topbar").show(),e?.path==="/login"||e?.path==="/reset-password"){$("#topbar-user-name").remove(),await P();const a=await f();C(a);try{const i=await l.system.notifications.list().catch(()=>[]),o=(Array.isArray(i)?i:[]).filter(s=>s.unread&&["warning","critical"].includes(s.data?.severity)).slice(0,3);for(const s of o){const r=p(s.data?.title||""),d=p((s.data?.body||"").slice(0,120));E.toast(`${r} - ${d}`,{type:s.data?.severity==="critical"?"error":"warning",duration:0})}}catch{}}j()}}),M.subscribe("router:afterChange",()=>{setTimeout(()=>{$(".btn-primary, .btn-danger").length&&Domma.effects.reveal(".btn-primary, .btn-danger",{animation:"fade",stagger:40,duration:300})},50)});const U=["#field-project","#menu-project","#collection-project","#endpoint-project","#action-project","#view-project","#block-project","#component-project","#role-project",'[name="project"]','[name="ownedByProject"]'];function N(t){return/^\/(pages|menus|context-menus|collections|forms|actions|views|blocks|components|users|roles|api-endpoints)\/(new|edit\/)/.test(t)}M.subscribe("router:afterChange",({to:t})=>{const e=t?String(t.path).split("?")[0]:"";if(!t||!N(e))return;const i=new URLSearchParams(location.hash.split("?")[1]||"").get("project");if(!i||!/\/new$/.test(e))return;let o=0;const s=60,r=1500,d=()=>{o+=s;for(const x of U){const u=document.querySelector(x);if(!u)continue;if(Array.from(u.options||[]).find(L=>L.value===i)){u.value=i,u.dispatchEvent(new Event("change",{bubbles:!0}));return}}o<r&&setTimeout(d,s)};setTimeout(d,s)});const D="cms_card_states",g=S.get(D)||{};M.subscribe("router:afterChange",()=>{setTimeout(()=>{$("#view-container .card-collapsible").each(function(){const t=$(this).find(".card-header h2, .card-header h3").first().text().trim();t&&g[t]==="collapsed"&&$(this).addClass("card-collapsed")})},200)}),$("#view-container").on("click",".card-collapsible .card-header",function(t){if($(t.target).closest("button, a").length)return;const e=$(this).closest(".card");e.toggleClass("card-collapsed");const a=$(this).find("h2, h3").first().text().trim();a&&(g[a]=e.hasClass("card-collapsed")?"collapsed":"open",S.set(D,g))}),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash==="#/login"||window.location.hash.startsWith("#/reset-password"))return;const e=document.querySelector("#view-container .view-header button.btn-primary");e&&(t.preventDefault(),e.click())});const b={...T},v=[...I];async function P(){if(m())try{const t=await l.plugins.adminConfig();t.routes?.length&&v.push(...t.routes);const{withPluginChrome:e}=await import("./lib/plugin-chrome.js");for(const[a,i]of Object.entries(t.views||{}))try{const o=await import(`/plugins/${i.entry}`);b[a]=e(o[i.exportName],(t.meta||{})[a])}catch{}for(const{id:a,href:i}of t.css||[])if(!document.getElementById(a)){const o=document.createElement("link");o.id=a,o.rel="stylesheet",o.href=i,document.head.appendChild(o)}}catch{}}function j(){const t=c();if(!t||$("#topbar-user-name").length)return;const a={"super-admin":"Super Admin",admin:"Admin",user:"User"}[t.role]||t.role;$("#topbar-user").html(`
2
2
  <span id="topbar-user-name" class="topbar-user-name">${p(t.name)}</span>
3
- <span class="topbar-role-badge topbar-role-badge--${p(t.role)}">${o}</span>
3
+ <span class="topbar-role-badge topbar-role-badge--${p(t.role)}">${a}</span>
4
4
  `),$("#topbar-actions").html(`
5
5
  <a href="#/system/notifications" id="topbar-bell" class="topbar-action-link" data-tooltip="Notifications" data-tooltip-placement="bottom" hidden>
6
6
  <span data-icon="bell"></span>
@@ -18,4 +18,4 @@ import{views as W}from"./views/index.js";import{api as l,getUser as c,isAuthenti
18
18
  <span data-icon="log-out"></span>
19
19
  <span>Sign out</span>
20
20
  </a>
21
- `),$("#topbar-logout-btn").on("click",i=>{i.preventDefault(),_()}),Domma.icons.scan("#admin-topbar"),E.tooltip("#topbar-actions [data-tooltip]",{placement:"bottom"}),y(),n||(n=setInterval(y,6e4))}function p(t){return String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}let n=null;async function y(){if(m())try{const{count:t}=await l.system.notifications.unreadCount();$("#topbar-bell").removeAttr("hidden");const e=$("#topbar-bell-badge");t>0?e.text(t>99?"99+":String(t)).removeAttr("hidden"):e.attr("hidden","").text("")}catch{}}(async()=>{if(!m()&&!window.location.hash.startsWith("#/reset-password"))window.location.hash="#/login";else{const i=(window.location.hash||"#/").slice(1)||"/";if(h(c())&&!w(i)&&(window.location.hash="#/job-board"),await P(),c()){const a=await f();await C(a),j()}}R.init({container:"#view-container",routes:v,views:b,default:"/",transitions:{enter:"fadeIn",leave:"fadeOut",duration:150}});const t=R._extractParams.bind(R);R._extractParams=function(i,s){const a=s.indexOf("?");if(a!==-1&&(s=s.slice(0,a)),i.endsWith("/*")){const r=i.slice(0,-2);return s.startsWith(r+"/")?{}:null}return t(i,s)};const e=(window.location.hash||"#/").slice(1)||"/";v.filter(i=>i.path.endsWith("/*")).some(i=>e.startsWith(i.path.slice(0,-2)+"/"))&&R._handleRouteChange()})()});
21
+ `),$("#topbar-logout-btn").on("click",i=>{i.preventDefault(),W()}),Domma.icons.scan("#admin-topbar"),E.tooltip("#topbar-actions [data-tooltip]",{placement:"bottom"}),y(),n||(n=setInterval(y,6e4))}function p(t){return String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}let n=null;async function y(){if(m())try{const{count:t}=await l.system.notifications.unreadCount();$("#topbar-bell").removeAttr("hidden");const e=$("#topbar-bell-badge");t>0?e.text(t>99?"99+":String(t)).removeAttr("hidden"):e.attr("hidden","").text("")}catch{}}(async()=>{if(!m()&&!window.location.hash.startsWith("#/reset-password"))window.location.hash="#/login";else{const i=(window.location.hash||"#/").slice(1)||"/";if(h(c())&&!w(i)&&(window.location.hash="#/job-board"),await P(),c()){const s=await f();await C(s),j()}}R.init({container:"#view-container",routes:v,views:b,default:"/",transitions:{enter:"fadeIn",leave:"fadeOut",duration:150}});const t=R._extractParams.bind(R);R._extractParams=function(i,o){const s=o.indexOf("?");if(s!==-1&&(o=o.slice(0,s)),i.endsWith("/*")){const r=i.slice(0,-2);return o.startsWith(r+"/")?{}:null}return t(i,o)};const e=(window.location.hash||"#/").slice(1)||"/";v.filter(i=>i.path.endsWith("/*")).some(i=>e.startsWith(i.path.slice(0,-2)+"/"))&&R._handleRouteChange()})()});
@@ -0,0 +1 @@
1
+ import{api as b}from"../api.js";function o(s,e){const r=document.createElement(s);return e&&(r.style.cssText=e),r}let m=null;export function invalidatePageCache(){m=null}async function y(){if(m)return m;const s=await b.pages.list().catch(()=>[]);return m=(Array.isArray(s)?s:[]).filter(e=>e&&e.urlPath).sort((e,r)=>String(e.urlPath).localeCompare(String(r.urlPath))),m}export function openPagePicker(s){const e=o("div","position:fixed;inset:0;background:rgba(0,0,0,.7);display:flex;align-items:center;justify-content:center;z-index:10000;padding:24px"),r=o("div","background:var(--dm-surface,#1e293b);border:1px solid var(--dm-border,#475569);border-radius:12px;padding:16px;width:100%;max-width:560px;max-height:70vh;display:flex;flex-direction:column;gap:12px"),a=o("div","display:flex;gap:8px;align-items:center"),t=o("input","flex:1;min-width:160px;padding:8px 12px;border-radius:8px;border:1px solid var(--dm-border,#334155);background:var(--dm-background,#0f172a);color:var(--dm-text,#e2e8f0);font-size:13px;font-family:inherit");t.type="text",t.placeholder="Search pages\u2026";const d=o("button","padding:8px 14px;background:var(--dm-border,#334155);border:none;border-radius:8px;color:var(--dm-text-muted,#94a3b8);font-size:12px;cursor:pointer");d.type="button",d.textContent="Close",d.addEventListener("click",()=>e.remove()),a.appendChild(t),a.appendChild(d);const u=o("div","font-size:11px;color:var(--dm-text-muted,#94a3b8)"),p=o("div","display:flex;flex-direction:column;gap:4px;overflow-y:auto;max-height:420px");let x=[];function f(){for(;p.firstChild;)p.removeChild(p.firstChild);const l=t.value.trim().toLowerCase(),c=x.filter(n=>!l||String(n.urlPath).toLowerCase().includes(l)||String(n.title||"").toLowerCase().includes(l));if(u.textContent=`${c.length} page${c.length===1?"":"s"}`,!c.length){const n=o("div","font-size:12px;color:var(--dm-text-muted,#94a3b8);padding:12px");n.textContent=x.length?"No page matches that.":"No pages yet.",p.appendChild(n);return}for(const n of c){const i=o("button","display:flex;align-items:baseline;gap:10px;width:100%;text-align:left;padding:8px 10px;border-radius:8px;border:1px solid transparent;cursor:pointer;background:var(--dm-background,#0f172a);color:var(--dm-text,#e2e8f0);font-family:inherit");i.type="button";const g=o("span","font-size:13px;font-weight:600");g.textContent=n.title||n.slug||n.urlPath;const h=o("span","font-size:11px;color:var(--dm-text-muted,#94a3b8);font-family:var(--dm-font-mono,monospace)");if(h.textContent=n.urlPath,i.appendChild(g),i.appendChild(h),n.status&&n.status!=="published"){const v=o("span","font-size:10px;text-transform:uppercase;letter-spacing:.04em;color:var(--dm-warning,#f59e0b);margin-left:auto");v.textContent=n.status,i.appendChild(v)}i.addEventListener("mouseenter",()=>{i.style.borderColor="var(--dm-primary)"}),i.addEventListener("mouseleave",()=>{i.style.borderColor="transparent"}),i.addEventListener("click",()=>{s(n.urlPath),e.remove()}),p.appendChild(i)}}t.addEventListener("input",f),r.appendChild(a),r.appendChild(u),r.appendChild(p),e.appendChild(r),e.addEventListener("click",l=>{l.target===e&&e.remove()}),document.addEventListener("keydown",function l(c){c.key==="Escape"&&(e.remove(),document.removeEventListener("keydown",l))}),document.body.appendChild(e),u.textContent="Loading\u2026",y().then(l=>{x=l,f(),t.focus()})}export function makePageInput(s="/about",e=""){const r=o("div","display:flex;gap:.35rem;"),a=document.createElement("input");a.type="text",a.className="form-input",a.placeholder=s,a.value=e||"",a.style.flex="1";const t=document.createElement("button");t.type="button",t.title="Browse pages",t.style.cssText="padding:.35rem .55rem;background:transparent;border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text-muted,#aaa);cursor:pointer;flex-shrink:0;display:inline-flex;align-items:center;";const d=document.createElement("span");return d.setAttribute("data-icon","file-text"),d.setAttribute("data-icon-size","14"),d.style.cssText="display:inline-flex;align-items:center;vertical-align:middle;",t.appendChild(d),window.I?.scan?.(t),t.addEventListener("click",()=>openPagePicker(u=>{a.value=u,a.dispatchEvent(new Event("input",{bubbles:!0}))})),r.appendChild(a),r.appendChild(t),{el:r,input:a}}
@@ -0,0 +1 @@
1
+ const a={invoice:150,contacts:210,blog:265,analytics:285,"mail-reader":190,"email-pro":190,messages:220,notes:45,todo:25,surveys:330,calendar:10,"site-search":200,"theme-switcher":310,"demo-viewer":175,docs:240,garage:20,"form-builder":255,"site-manager":205,"site-tweaks":95,"shopping-cart":140,"job-board":35,"customer-registration":170,_template:0};function n(t){let e=0;for(let r=0;r<t.length;r++)e=(e*31+t.charCodeAt(r))%360;return e}function i(t){return t==="_template"?0:62}export function pluginAccent(t){const e=String(t||"");return`hsl(${a[e]??n(e)} ${i(e)}% 52%)`}export function pluginWatermark(t){const e=document.createElement("span");return e.className="plugin-watermark",e.setAttribute("data-icon",t||"package"),e.setAttribute("data-icon-size","148"),e.setAttribute("aria-hidden","true"),e}
@@ -0,0 +1 @@
1
+ const p=/^(built in|included\b)/i;export function formatReleased(n){if(!n||typeof n!="string"||!/^\d{4}-\d{2}-\d{2}/.test(n))return"";const e=new Date(n);if(Number.isNaN(e.getTime()))return"";const t=c=>String(c).padStart(2,"0");return`${t(e.getDate())}.${t(e.getMonth()+1)}.${e.getFullYear()}`}export function buildBanner(n){const e=n||{},t=document.createElement("header");t.className="dm-plugin-banner";const c=document.createElement("div");c.className="dm-plugin-banner-identity";const a=document.createElement("span");a.className="dm-plugin-banner-icon",a.setAttribute("data-icon",e.icon||"package"),a.setAttribute("data-icon-size","18"),c.appendChild(a);const r=document.createElement("h1");r.className="dm-plugin-banner-name",r.textContent=e.displayName||e.plugin||"Plugin",c.appendChild(r),t.appendChild(c);const o=document.createElement("div");o.className="dm-plugin-banner-facts",((i,l)=>{if(!i)return;const d=document.createElement("span");d.className="dm-plugin-fact"+(l?" "+l:""),d.textContent=i,o.appendChild(d)})(e.version?`v${e.version}`:"","dm-plugin-fact--version");const s=formatReleased(e.date);if(s){const i=document.createElement("span");i.className="dm-plugin-fact dm-plugin-fact--date",i.title="Release date",i.textContent=s,o.appendChild(i)}if(e.licence){const i=document.createElement("span");i.className="dm-plugin-fact dm-plugin-fact--licence"+(p.test(e.licence)?" is-included":" is-licensed"),i.title="Licence",i.textContent=e.licence,o.appendChild(i)}return t.appendChild(o),t}export function withPluginChrome(n,e){if(!n||typeof n!="object")return n;const t=Object.create(Object.getPrototypeOf(n));Object.assign(t,n);const c=typeof n.onMount=="function"?n.onMount:null;return t.onMount=async function(...a){const r=a[0],o=r&&typeof r.get=="function"?r.get(0):r;return o&&o.nodeType===1&&!o.querySelector(":scope > .dm-plugin-shell")&&applyChrome(o,e),c?c.apply(n,a):void 0},t}export function applyChrome(n,e){const t=document.createElement("section");t.className="dm-plugin-shell",e&&e.plugin&&t.setAttribute("data-plugin",e.plugin),t.appendChild(buildBanner(e));const c=document.createElement("div");for(c.className="dm-plugin-body";n.firstChild;)c.appendChild(n.firstChild);t.appendChild(c),n.appendChild(t),g(c,e),f(c),typeof I<"u"&&I.scan?I.scan(t):typeof Domma<"u"&&Domma.icons&&Domma.icons.scan()}function m(n){return[...n.children].filter(e=>!/^(STYLE|SCRIPT|TEMPLATE|LINK)$/.test(e.tagName))}function f(n){let e=n;for(let t=0;t<2;t++){const c=m(e);if(c.length!==1)return;if(e=c[0],e.classList.contains("card")){e.classList.add("dm-plugin-unwrap");return}}}function g(n,e){const t=String(e&&e.displayName||"").trim().toLowerCase(),c=n.querySelector(":scope > .view-header, :scope > * > .view-header");if(c){const a=c.querySelector("h1, h2"),r=c.querySelector("button, a, input, select");a&&r?a.remove():a&&c.remove();return}if(t){for(const a of n.querySelectorAll("h1, h2"))if(a.textContent.trim().toLowerCase()===t){a.remove();return}}}export function addBannerAction(n,{icon:e,title:t,onClick:c,id:a="",label:r=""}){const o=n&&typeof n.get=="function"?n.get(0):n;if(!o||o.nodeType!==1)return null;const u=o.querySelector(":scope > .dm-plugin-shell > .dm-plugin-banner")||o.closest(".dm-plugin-shell")?.querySelector(":scope > .dm-plugin-banner");if(!u)return null;let s=u.querySelector(":scope > .dm-plugin-banner-facts");s||(s=document.createElement("div"),s.className="dm-plugin-banner-facts",u.appendChild(s));let i=s.querySelector(":scope > .dm-plugin-banner-actions");i||(i=document.createElement("div"),i.className="dm-plugin-banner-actions",s.insertBefore(i,s.firstChild)),a&&i.querySelector(`[data-banner-action="${CSS.escape(a)}"]`)?.remove();const l=document.createElement("button");l.type="button",l.className="btn btn-sm btn-ghost dm-plugin-banner-action",l.title=t||"",l.setAttribute("aria-label",t||r||"Action"),a&&(l.dataset.bannerAction=a);const d=document.createElement("span");return d.setAttribute("data-icon",e||"settings"),d.setAttribute("data-icon-size","16"),l.appendChild(d),r&&l.appendChild(document.createTextNode(` ${r}`)),l.addEventListener("click",c),i.appendChild(l),typeof I<"u"&&I.scan?I.scan(l):typeof Domma<"u"&&Domma.icons&&Domma.icons.scan(l),l}
@@ -1,3 +1,3 @@
1
- import{SHORTCODES as $}from"./shortcode-registry.js";import{EFFECTS as C}from"./effect-defs.js";import{boolState as B,boolValue as K,findEnclosingShortcode as U,setAttr as M,setAttrs as V,effectAttrKeys as G}from"./shortcode-tags.js";import{insertAtCursor as X}from"./markdown-toolbar.js";import{clearLookupCache as q,lookupOptions as J}from"./shortcode-lookups.js";import{insertMenuGroups as Q,insertShortcodeByName as Z}from"./shortcode-insert.js";import{openShortcodeForm as N}from"./shortcode-form.js";function ee(e){return J(e,c?.span?.attrs||{})}const te=$.map(e=>({name:e.name,selfClosing:!!e.selfClosing})),ne=new Map($.map(e=>[e.name,e])),oe=new Map(C.map(e=>[e.name,e])),se=C.map(e=>({name:e.name,selfClosing:!!e.selfClosing})),ie=[...te,...se],R=new Set(["card","hero","banner","col","button"]);let c=null;const z="position:fixed;z-index:10000;min-width:210px;max-height:70vh;overflow-y:auto;background:var(--dm-surface,#1c1c30);border:1px solid var(--dm-border,#2a2a40);border-radius:6px;box-shadow:var(--dm-shadow-lg);padding:.3rem;font-size:.83rem;color:var(--dm-text,#e5e5e5);",p="display:flex;align-items:center;gap:.5rem;width:100%;text-align:left;padding:.35rem .55rem;border:none;background:transparent;color:inherit;border-radius:4px;cursor:pointer;font-size:.83rem;",S="padding:.3rem .55rem;color:var(--dm-text-muted,#8a8a9a);font-size:.68rem;text-transform:uppercase;letter-spacing:.06em;",P="flex:1;min-width:0;padding:.2rem .4rem;background:var(--dm-input-bg,#12121e);border:1px solid var(--dm-border,#333);border-radius:3px;color:var(--dm-text,#eee);font-size:.8rem;",ce="height:1px;background:var(--dm-border,#2a2a40);margin:.3rem .2rem;";function m(e){return e.addEventListener("mouseenter",()=>{e.style.background="var(--dm-primary,#4338ca)",e.style.color="var(--dm-primary-text,#fff)"}),e.addEventListener("mouseleave",()=>{e.style.background="transparent",e.style.color="inherit"}),e}export function attachContextMenu(e){e.addEventListener("contextmenu",n=>{n.preventDefault(),h();const t=U(e,ie);ae(e,t,n.clientX,n.clientY)})}function h(){c&&(v(),c.root.remove(),document.removeEventListener("mousedown",H,!0),document.removeEventListener("keydown",Y,!0),window.removeEventListener("scroll",W,!0),c=null)}const H=e=>{c&&!c.root.contains(e.target)&&!(c.submenu&&c.submenu.contains(e.target))&&h()},W=e=>{c&&!c.root.contains(e.target)&&!(c.submenu&&c.submenu.contains(e.target))&&h()},Y=e=>{e.key==="Escape"&&h()};function ae(e,n,t,o){q();const s=document.createElement("div");s.className="dm-sc-menu",s.style.cssText=z,c={root:s,textarea:e,span:n,submenu:null};const i=n&&(ne.get(n.name)||oe.get(n.name)),l=n&&R.has(n.name)?C.filter(r=>r.name in n.attrs):[];i&&re(s,i);for(const r of l)xe(s,r);(i||l.length)&&s.appendChild(le()),s.appendChild(y("Add effect\u2026",()=>be())),s.appendChild(y("Insert\u2026",()=>ye()));const a=e.selectionStart!==e.selectionEnd;(n||a)&&s.appendChild(y(a?"Wrap selection in\u2026":"Wrap in\u2026",()=>Ee())),document.body.appendChild(s),O(s,t,o),document.addEventListener("mousedown",H,!0),document.addEventListener("keydown",Y,!0),window.addEventListener("scroll",W,!0),window.I&&I.scan(s)}function le(){const e=document.createElement("div");return e.style.cssText=ce,e}function k(e){const n=document.createElement("div");return n.style.cssText=S,n.textContent=e,n}function re(e,n){e.appendChild(k(`${n.label} options`));const t=m(document.createElement("button"));t.type="button",t.style.cssText=p,t.innerHTML='<span data-icon="edit" data-icon-size="13" style="display:inline-flex;align-items:center;"></span>',t.appendChild(document.createTextNode(`Edit ${n.label}\u2026`)),t.addEventListener("click",()=>{const{textarea:o,span:s}=c;h(),N(o,n,{span:s})}),e.appendChild(t);for(const o of n.attrs||[])e.appendChild(j(o))}function j(e,n=e.name){const t=c.span.attrs[n];switch(e.type){case"checkbox":return de(e,n,B(t,e.default));case"select":return me(e,n,t);case"lookup":return he(e,n,t);case"colour":return pe(e,n,t);case"icon":return fe(e,n,t);default:return ue(e,n,t)}}function b(e,n){c.span=M(c.textarea,c.span,e,n)}function de(e,n,t){const o=m(document.createElement("label"));o.style.cssText=p;const s=document.createElement("input");return s.type="checkbox",s.checked=t,s.addEventListener("change",()=>b(n,K(e,s.checked))),o.appendChild(s),o.appendChild(document.createTextNode(e.label)),o}function ue(e,n,t){const o=document.createElement("div");o.style.cssText=p+"cursor:default;";const s=document.createElement("span");s.textContent=e.label,s.style.cssText="white-space:nowrap;color:var(--dm-text-muted,#9a9aac);";const i=document.createElement("input");i.type=e.type==="number"?"number":"text",i.value=t===!0||t==null?"":t,e.default!=null&&e.default!==""&&(i.placeholder=String(e.default)),i.style.cssText=P;let l=null;return i.addEventListener("input",()=>{clearTimeout(l),l=setTimeout(()=>b(n,i.value.trim()),180)}),i.addEventListener("mousedown",a=>a.stopPropagation()),o.appendChild(s),o.appendChild(i),o}function pe(e,n,t){const o=document.createElement("div");o.style.cssText=p+"cursor:default;";const s=document.createElement("span");s.textContent=e.label,s.style.flex="1";const i=document.createElement("input");return i.type="color",typeof t=="string"&&/^#/.test(t)?i.value=t:typeof e.default=="string"&&/^#/.test(e.default)&&(i.value=e.default),i.addEventListener("input",()=>b(n,i.value)),o.appendChild(s),o.appendChild(i),o}function me(e,n,t){const o=t==null||t===!0?e.default??"":t;return y(`${e.label}${o!==""?`: ${o}`:""}`,()=>{const s=g();for(const[i,l]of e.options||[]){const a=m(document.createElement("button"));a.type="button",a.style.cssText=p,a.textContent=l,i===o&&(a.style.fontWeight="700"),a.addEventListener("click",()=>{b(n,i),v()}),s.appendChild(a)}return s})}function fe(e,n,t){const o=t==null||t===!0?"":String(t),s=o?`${e.label}: ${o}`:e.label;return y(s,()=>{const i=g();i.style.width="232px";const l=document.createElement("input");l.type="text",l.placeholder="Search icons\u2026",l.style.cssText=P+"margin:6px;width:calc(100% - 12px);",l.addEventListener("mousedown",T=>T.stopPropagation()),i.appendChild(l);const a=m(document.createElement("button"));a.type="button",a.style.cssText=p,a.textContent="- none -",o||(a.style.fontWeight="700"),a.addEventListener("click",()=>{b(n,null),v()}),i.appendChild(a);const r=document.createElement("div");r.style.cssText="display:flex;flex-wrap:wrap;gap:2px;padding:4px 6px;max-height:220px;overflow-y:auto;",i.appendChild(r);const u=96,w=()=>{const T=typeof I<"u"&&typeof I.list=="function"?I.list():[],A=l.value.trim().toLowerCase(),_=A?T.filter(d=>d.toLowerCase().includes(A)):T;r.replaceChildren();for(const d of _.slice(0,u)){const x=m(document.createElement("button"));x.type="button",x.title=d,x.style.cssText="width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;background:none;border:1px solid transparent;border-radius:4px;cursor:pointer;color:inherit;",d===o&&(x.style.borderColor="var(--dm-primary,#5b8cff)");const L=document.createElement("span");L.setAttribute("data-icon",d),L.setAttribute("data-icon-size","15"),L.style.cssText="display:inline-flex;align-items:center;",x.appendChild(L),x.addEventListener("click",()=>{b(n,d),v()}),r.appendChild(x)}if(_.length>u){const d=document.createElement("div");d.style.cssText=S,d.textContent=`${_.length-u} more - refine search`,r.appendChild(d)}if(!_.length){const d=document.createElement("div");d.style.cssText=S,d.textContent="No icons match",r.appendChild(d)}window.I&&I.scan(r)};let f=null;return l.addEventListener("input",()=>{clearTimeout(f),f=setTimeout(w,120)}),w(),setTimeout(()=>l.focus(),0),i})}function he(e,n,t){const o=t==null||t===!0?e.default??"":t;return y(`${e.label}${o!==""?`: ${o}`:""}`,()=>{const s=g();s.style.minHeight="48px";const i=typeof E<"u"&&E.loader?E.loader(s,{type:"dots"}):null;return ee(e.source).then(l=>{if(i&&i.destroy(),!c||c.submenu!==s)return;if(s.style.minHeight="",!l.length){const u=document.createElement("div");u.style.cssText=S,u.textContent="Nothing found",s.appendChild(u)}const a=l.some(([u])=>u===o),r=[["","- none -"],...l];o!==""&&!a&&r.push([o,`${o} (missing)`]);for(const[u,w]of r){const f=m(document.createElement("button"));f.type="button",f.style.cssText=p,f.textContent=w,u===o&&(f.style.fontWeight="700"),f.addEventListener("click",()=>{b(n,u===""?null:u),v()}),s.appendChild(f)}}),s})}function xe(e,n){e.appendChild(k(`Effect: ${n.label}`));for(const o of n.attrs||[])e.appendChild(j(o,`${n.name}-${o.name}`));const t=m(document.createElement("button"));t.type="button",t.style.cssText=p,t.innerHTML='<span data-icon="trash" data-icon-size="13" style="display:inline-flex;align-items:center;"></span> Remove effect',t.addEventListener("click",()=>{const o={};for(const s of G(c.span.attrs,n.name))o[s]=null;V(c.textarea,c.span,o),h()}),e.appendChild(t)}function be(){const e=g(),n=c.span,t=n&&R.has(n.name)?n.name:null;for(const o of[...new Set(C.map(s=>s.category))]){const s=C.filter(i=>i.category===o);if(s.length){e.appendChild(k(o));for(const i of s){const l=t&&(i.hosts||[]).includes(t)&&!(i.name in n.attrs),a=m(document.createElement("button"));if(a.type="button",a.style.cssText=p+"justify-content:space-between;",a.appendChild(document.createTextNode(i.label)),l){const r=document.createElement("span");r.textContent=`inject \u2192 [${t}]`,r.style.cssText="opacity:.6;font-size:.7rem;",a.appendChild(r)}a.addEventListener("click",()=>{if(l)M(c.textarea,c.span,i.name,!0);else{const r=i.selfClosing?`[${i.name} /]`:`[${i.name}]
1
+ import{SHORTCODES as $}from"./shortcode-registry.js";import{EFFECTS as C}from"./effect-defs.js";import{boolState as V,boolValue as G,findEnclosingShortcode as M,setAttr as N,setAttrs as X,effectAttrKeys as q}from"./shortcode-tags.js";import{insertAtCursor as J}from"./markdown-toolbar.js";import{clearLookupCache as Q,lookupOptions as Z}from"./shortcode-lookups.js";import{insertMenuGroups as ee,insertShortcodeByName as te}from"./shortcode-insert.js";import{openShortcodeForm as R}from"./shortcode-form.js";function ne(e){return Z(e,c?.span?.attrs||{})}const oe=$.map(e=>({name:e.name,selfClosing:!!e.selfClosing})),se=new Map($.map(e=>[e.name,e])),ie=new Map(C.map(e=>[e.name,e])),ce=C.map(e=>({name:e.name,selfClosing:!!e.selfClosing})),z=[...oe,...ce],D=new Set(["card","hero","banner","col","button"]);let c=null;const P="position:fixed;z-index:10000;min-width:210px;max-height:70vh;overflow-y:auto;background:var(--dm-surface,#1c1c30);border:1px solid var(--dm-border,#2a2a40);border-radius:6px;box-shadow:var(--dm-shadow-lg);padding:.3rem;font-size:.83rem;color:var(--dm-text,#e5e5e5);",p="display:flex;align-items:center;gap:.5rem;width:100%;text-align:left;padding:.35rem .55rem;border:none;background:transparent;color:inherit;border-radius:4px;cursor:pointer;font-size:.83rem;",S="padding:.3rem .55rem;color:var(--dm-text-muted,#8a8a9a);font-size:.68rem;text-transform:uppercase;letter-spacing:.06em;",H="flex:1;min-width:0;padding:.2rem .4rem;background:var(--dm-input-bg,#12121e);border:1px solid var(--dm-border,#333);border-radius:3px;color:var(--dm-text,#eee);font-size:.8rem;",ae="height:1px;background:var(--dm-border,#2a2a40);margin:.3rem .2rem;";function m(e){return e.addEventListener("mouseenter",()=>{e.style.background="var(--dm-primary,#4338ca)",e.style.color="var(--dm-primary-text,#fff)"}),e.addEventListener("mouseleave",()=>{e.style.background="transparent",e.style.color="inherit"}),e}export function attachContextMenu(e){return typeof Domma>"u"||!Domma.elements?.contextMenu?(e.addEventListener("contextmenu",n=>{n.preventDefault(),f();const t=M(e,z);F(e,t,n.clientX,n.clientY)}),null):Domma.elements.contextMenu(e,{inherit:!1,render:n=>{f();const t=M(e,z);F(e,t,n.x,n.y)}})}function f(){c&&(v(),c.root.remove(),document.removeEventListener("mousedown",W,!0),document.removeEventListener("keydown",j,!0),window.removeEventListener("scroll",Y,!0),c=null)}const W=e=>{c&&!c.root.contains(e.target)&&!(c.submenu&&c.submenu.contains(e.target))&&f()},Y=e=>{c&&!c.root.contains(e.target)&&!(c.submenu&&c.submenu.contains(e.target))&&f()},j=e=>{e.key==="Escape"&&f()};function F(e,n,t,o){Q();const s=document.createElement("div");s.className="dm-sc-menu",s.style.cssText=P,c={root:s,textarea:e,span:n,submenu:null};const i=n&&(se.get(n.name)||ie.get(n.name)),l=n&&D.has(n.name)?C.filter(r=>r.name in n.attrs):[];i&&re(s,i);for(const r of l)xe(s,r);(i||l.length)&&s.appendChild(le()),s.appendChild(y("Add effect\u2026",()=>be())),s.appendChild(y("Insert\u2026",()=>ye()));const a=e.selectionStart!==e.selectionEnd;(n||a)&&s.appendChild(y(a?"Wrap selection in\u2026":"Wrap in\u2026",()=>Ee())),document.body.appendChild(s),U(s,t,o),document.addEventListener("mousedown",W,!0),document.addEventListener("keydown",j,!0),window.addEventListener("scroll",Y,!0),window.I&&I.scan(s)}function le(){const e=document.createElement("div");return e.style.cssText=ae,e}function k(e){const n=document.createElement("div");return n.style.cssText=S,n.textContent=e,n}function re(e,n){e.appendChild(k(`${n.label} options`));const t=m(document.createElement("button"));t.type="button",t.style.cssText=p,t.innerHTML='<span data-icon="edit" data-icon-size="13" style="display:inline-flex;align-items:center;"></span>',t.appendChild(document.createTextNode(`Edit ${n.label}\u2026`)),t.addEventListener("click",()=>{const{textarea:o,span:s}=c;f(),R(o,n,{span:s})}),e.appendChild(t);for(const o of n.attrs||[])e.appendChild(O(o))}function O(e,n=e.name){const t=c.span.attrs[n];switch(e.type){case"checkbox":return de(e,n,V(t,e.default));case"select":return me(e,n,t);case"lookup":return he(e,n,t);case"colour":return pe(e,n,t);case"icon":return fe(e,n,t);default:return ue(e,n,t)}}function b(e,n){c.span=N(c.textarea,c.span,e,n)}function de(e,n,t){const o=m(document.createElement("label"));o.style.cssText=p;const s=document.createElement("input");return s.type="checkbox",s.checked=t,s.addEventListener("change",()=>b(n,G(e,s.checked))),o.appendChild(s),o.appendChild(document.createTextNode(e.label)),o}function ue(e,n,t){const o=document.createElement("div");o.style.cssText=p+"cursor:default;";const s=document.createElement("span");s.textContent=e.label,s.style.cssText="white-space:nowrap;color:var(--dm-text-muted,#9a9aac);";const i=document.createElement("input");i.type=e.type==="number"?"number":"text",i.value=t===!0||t==null?"":t,e.default!=null&&e.default!==""&&(i.placeholder=String(e.default)),i.style.cssText=H;let l=null;return i.addEventListener("input",()=>{clearTimeout(l),l=setTimeout(()=>b(n,i.value.trim()),180)}),i.addEventListener("mousedown",a=>a.stopPropagation()),o.appendChild(s),o.appendChild(i),o}function pe(e,n,t){const o=document.createElement("div");o.style.cssText=p+"cursor:default;";const s=document.createElement("span");s.textContent=e.label,s.style.flex="1";const i=document.createElement("input");return i.type="color",typeof t=="string"&&/^#/.test(t)?i.value=t:typeof e.default=="string"&&/^#/.test(e.default)&&(i.value=e.default),i.addEventListener("input",()=>b(n,i.value)),o.appendChild(s),o.appendChild(i),o}function me(e,n,t){const o=t==null||t===!0?e.default??"":t;return y(`${e.label}${o!==""?`: ${o}`:""}`,()=>{const s=g();for(const[i,l]of e.options||[]){const a=m(document.createElement("button"));a.type="button",a.style.cssText=p,a.textContent=l,i===o&&(a.style.fontWeight="700"),a.addEventListener("click",()=>{b(n,i),v()}),s.appendChild(a)}return s})}function fe(e,n,t){const o=t==null||t===!0?"":String(t),s=o?`${e.label}: ${o}`:e.label;return y(s,()=>{const i=g();i.style.width="232px";const l=document.createElement("input");l.type="text",l.placeholder="Search icons\u2026",l.style.cssText=H+"margin:6px;width:calc(100% - 12px);",l.addEventListener("mousedown",T=>T.stopPropagation()),i.appendChild(l);const a=m(document.createElement("button"));a.type="button",a.style.cssText=p,a.textContent="- none -",o||(a.style.fontWeight="700"),a.addEventListener("click",()=>{b(n,null),v()}),i.appendChild(a);const r=document.createElement("div");r.style.cssText="display:flex;flex-wrap:wrap;gap:2px;padding:4px 6px;max-height:220px;overflow-y:auto;",i.appendChild(r);const u=96,w=()=>{const T=typeof I<"u"&&typeof I.list=="function"?I.list():[],A=l.value.trim().toLowerCase(),_=A?T.filter(d=>d.toLowerCase().includes(A)):T;r.replaceChildren();for(const d of _.slice(0,u)){const x=m(document.createElement("button"));x.type="button",x.title=d,x.style.cssText="width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;background:none;border:1px solid transparent;border-radius:4px;cursor:pointer;color:inherit;",d===o&&(x.style.borderColor="var(--dm-primary,#5b8cff)");const L=document.createElement("span");L.setAttribute("data-icon",d),L.setAttribute("data-icon-size","15"),L.style.cssText="display:inline-flex;align-items:center;",x.appendChild(L),x.addEventListener("click",()=>{b(n,d),v()}),r.appendChild(x)}if(_.length>u){const d=document.createElement("div");d.style.cssText=S,d.textContent=`${_.length-u} more - refine search`,r.appendChild(d)}if(!_.length){const d=document.createElement("div");d.style.cssText=S,d.textContent="No icons match",r.appendChild(d)}window.I&&I.scan(r)};let h=null;return l.addEventListener("input",()=>{clearTimeout(h),h=setTimeout(w,120)}),w(),setTimeout(()=>l.focus(),0),i})}function he(e,n,t){const o=t==null||t===!0?e.default??"":t;return y(`${e.label}${o!==""?`: ${o}`:""}`,()=>{const s=g();s.style.minHeight="48px";const i=typeof E<"u"&&E.loader?E.loader(s,{type:"dots"}):null;return ne(e.source).then(l=>{if(i&&i.destroy(),!c||c.submenu!==s)return;if(s.style.minHeight="",!l.length){const u=document.createElement("div");u.style.cssText=S,u.textContent="Nothing found",s.appendChild(u)}const a=l.some(([u])=>u===o),r=[["","- none -"],...l];o!==""&&!a&&r.push([o,`${o} (missing)`]);for(const[u,w]of r){const h=m(document.createElement("button"));h.type="button",h.style.cssText=p,h.textContent=w,u===o&&(h.style.fontWeight="700"),h.addEventListener("click",()=>{b(n,u===""?null:u),v()}),s.appendChild(h)}}),s})}function xe(e,n){e.appendChild(k(`Effect: ${n.label}`));for(const o of n.attrs||[])e.appendChild(O(o,`${n.name}-${o.name}`));const t=m(document.createElement("button"));t.type="button",t.style.cssText=p,t.innerHTML='<span data-icon="trash" data-icon-size="13" style="display:inline-flex;align-items:center;"></span> Remove effect',t.addEventListener("click",()=>{const o={};for(const s of q(c.span.attrs,n.name))o[s]=null;X(c.textarea,c.span,o),f()}),e.appendChild(t)}function be(){const e=g(),n=c.span,t=n&&D.has(n.name)?n.name:null;for(const o of[...new Set(C.map(s=>s.category))]){const s=C.filter(i=>i.category===o);if(s.length){e.appendChild(k(o));for(const i of s){const l=t&&(i.hosts||[]).includes(t)&&!(i.name in n.attrs),a=m(document.createElement("button"));if(a.type="button",a.style.cssText=p+"justify-content:space-between;",a.appendChild(document.createTextNode(i.label)),l){const r=document.createElement("span");r.textContent=`inject \u2192 [${t}]`,r.style.cssText="opacity:.6;font-size:.7rem;",a.appendChild(r)}a.addEventListener("click",()=>{if(l)N(c.textarea,c.span,i.name,!0);else{const r=i.selfClosing?`[${i.name} /]`:`[${i.name}]
2
2
  Content
3
- [/${i.name}]`;X(c.textarea,r)}h()}),e.appendChild(a)}}}return e}function y(e,n){const t=m(document.createElement("button"));t.type="button",t.style.cssText=p+"justify-content:space-between;",t.appendChild(document.createTextNode(e));const o=document.createElement("span");return o.setAttribute("data-icon","chevron-right"),o.setAttribute("data-icon-size","12"),o.style.cssText="opacity:.6;display:inline-flex;align-items:center;flex-shrink:0;",t.appendChild(o),t.addEventListener("mouseenter",()=>D(t,n)),t.addEventListener("click",s=>{s.stopPropagation(),D(t,n)}),t}function g(){const e=document.createElement("div");return e.className="dm-sc-submenu",e.style.cssText=z,e}function D(e,n){v();const t=n();c.submenu=t,document.body.appendChild(t);const o=e.getBoundingClientRect();O(t,o.right+2,o.top,o.left-2),window.I&&I.scan(t)}function v(){c&&c.submenu&&(c.submenu.remove(),c.submenu=null)}function F(e){const n=m(document.createElement("button"));if(n.type="button",n.style.cssText=p,e.icon){const t=document.createElement("span");t.setAttribute("data-icon",e.icon),t.setAttribute("data-icon-size","14"),t.style.cssText="opacity:.75;display:inline-flex;align-items:center;flex-shrink:0;",n.appendChild(t)}return n.appendChild(document.createTextNode(e.label)),n}function ye(){const e=g();for(const{category:n,items:t}of Q()){e.appendChild(k(n));for(const o of t){const s=F(o);s.addEventListener("click",()=>{const i=c.textarea;h(),Z(o.name,i)}),e.appendChild(s)}}return e}function Ee(){const e=g();for(const n of $.filter(t=>t.wrappable||t.container)){const t=F(n);t.addEventListener("click",()=>{const o=c.textarea;h(),N(o,n)}),e.appendChild(t)}return e}function O(e,n,t,o){e.style.left="0px",e.style.top="0px";const s=e.getBoundingClientRect();let i=n;n+s.width>window.innerWidth-8&&(i=o!=null?o-s.width:n-s.width);let l=t;t+s.height>window.innerHeight-8&&(l=Math.max(8,window.innerHeight-s.height-8)),e.style.left=`${Math.max(8,i)}px`,e.style.top=`${Math.max(8,l)}px`}
3
+ [/${i.name}]`;J(c.textarea,r)}f()}),e.appendChild(a)}}}return e}function y(e,n){const t=m(document.createElement("button"));t.type="button",t.style.cssText=p+"justify-content:space-between;",t.appendChild(document.createTextNode(e));const o=document.createElement("span");return o.setAttribute("data-icon","chevron-right"),o.setAttribute("data-icon-size","12"),o.style.cssText="opacity:.6;display:inline-flex;align-items:center;flex-shrink:0;",t.appendChild(o),t.addEventListener("mouseenter",()=>B(t,n)),t.addEventListener("click",s=>{s.stopPropagation(),B(t,n)}),t}function g(){const e=document.createElement("div");return e.className="dm-sc-submenu",e.style.cssText=P,e}function B(e,n){v();const t=n();c.submenu=t,document.body.appendChild(t);const o=e.getBoundingClientRect();U(t,o.right+2,o.top,o.left-2),window.I&&I.scan(t)}function v(){c&&c.submenu&&(c.submenu.remove(),c.submenu=null)}function K(e){const n=m(document.createElement("button"));if(n.type="button",n.style.cssText=p,e.icon){const t=document.createElement("span");t.setAttribute("data-icon",e.icon),t.setAttribute("data-icon-size","14"),t.style.cssText="opacity:.75;display:inline-flex;align-items:center;flex-shrink:0;",n.appendChild(t)}return n.appendChild(document.createTextNode(e.label)),n}function ye(){const e=g();for(const{category:n,items:t}of ee()){e.appendChild(k(n));for(const o of t){const s=K(o);s.addEventListener("click",()=>{const i=c.textarea;f(),te(o.name,i)}),e.appendChild(s)}}return e}function Ee(){const e=g();for(const n of $.filter(t=>t.wrappable||t.container)){const t=K(n);t.addEventListener("click",()=>{const o=c.textarea;f(),R(o,n)}),e.appendChild(t)}return e}function U(e,n,t,o){e.style.left="0px",e.style.top="0px";const s=e.getBoundingClientRect();let i=n;n+s.width>window.innerWidth-8&&(i=o!=null?o-s.width:n-s.width);let l=t;t+s.height>window.innerHeight-8&&(l=Math.max(8,window.innerHeight-s.height-8)),e.style.left=`${Math.max(8,i)}px`,e.style.top=`${Math.max(8,l)}px`}
@@ -1 +1 @@
1
- export const TOOLS_FOLDER_TEXT="Tools",PLUGINS_FOLDER_TEXT="Plugins",MANAGE_PLUGINS_URL="#/plugins",MANAGE_PLUGINS_ITEM={text:"Manage Plugins",url:MANAGE_PLUGINS_URL,icon:"sliders",permission:"plugins"};export function stripItemByUrl(e,s){const t=[];for(const n of e||[]){if(!n||n.url===s)continue;const o={...n};Array.isArray(n.items)&&n.items.length&&(o.items=stripItemByUrl(n.items,s)),t.push(o)}return t}export function groupPluginItems(e){const s=[],t=[],n=[];for(const i of e||[])if(!(!i||!i.item)){if(i.parent){s.push(i);continue}i.item.core===!0?t.push(i.item):n.push(i.item)}const o=t.length?{text:TOOLS_FOLDER_TEXT,icon:"tool",items:t}:null,r={text:PLUGINS_FOLDER_TEXT,icon:"package",items:[{...MANAGE_PLUGINS_ITEM},...n]};return{toolsFolder:o,pluginsFolder:r,parented:s}}export function insertFoldersBeforeSystem(e,s){const t=(s||[]).filter(Boolean);if(!t.length)return e;const n=["system","documentation"],o=e.findIndex(r=>n.includes(String(r&&r.text||"").toLowerCase()));return o===-1?[...e,...t]:[...e.slice(0,o),...t,...e.slice(o)]}export function pruneEmptySynthesisedFolders(e){const s=new Set([TOOLS_FOLDER_TEXT,PLUGINS_FOLDER_TEXT]);return(e||[]).filter(t=>!t||!s.has(t.text)?!0:Array.isArray(t.items)&&t.items.length>0)}
1
+ export const TOOLS_FOLDER_TEXT="Tools",MANAGE_PLUGINS_URL="#/plugins",MANAGE_PLUGINS_ITEM={text:"Manage Plugins",url:MANAGE_PLUGINS_URL,icon:"sliders",permission:"plugins"};export function stripItemByUrl(e,o){const t=[];for(const s of e||[]){if(!s||s.url===o)continue;const n={...s};Array.isArray(s.items)&&s.items.length&&(n.items=stripItemByUrl(s.items,o)),t.push(n)}return t}export function groupPluginItems(e){const o=[],t=[];for(const n of e||[])if(!(!n||!n.item)){if(n.parent){o.push(n);continue}t.push(n.item)}return{toolsFolder:t.length?{text:TOOLS_FOLDER_TEXT,icon:"tool",items:t}:null,managePluginsItem:{...MANAGE_PLUGINS_ITEM},parented:o}}export function insertFoldersBeforeSystem(e,o){const t=(o||[]).filter(Boolean);if(!t.length)return e;const s=["system","documentation"],n=e.findIndex(r=>s.includes(String(r&&r.text||"").toLowerCase()));return n===-1?[...e,...t]:[...e.slice(0,n),...t,...e.slice(n)]}export function pruneEmptySynthesisedFolders(e){const o=new Set([TOOLS_FOLDER_TEXT]);return(e||[]).filter(t=>!t||!o.has(t.text)?!0:Array.isArray(t.items)&&t.items.length>0)}
@@ -1 +1 @@
1
- import{test as l}from"node:test";import t from"node:assert/strict";import{groupPluginItems as u,stripItemByUrl as a,insertFoldersBeforeSystem as i,pruneEmptySynthesisedFolders as p,TOOLS_FOLDER_TEXT as m,PLUGINS_FOLDER_TEXT as g}from"./sidebar-grouping.js";const n=(s,e=null)=>({parent:e,item:s});l("groupPluginItems routes core items to Tools, optional to Plugins",()=>{const{toolsFolder:s,pluginsFolder:e}=u([n({text:"Analytics",url:"#/plugins/analytics",core:!0}),n({text:"Todo",url:"#/plugins/todo",core:!1}),n({text:"Notes",url:"#/plugins/notes"})]);t.equal(s.text,m),t.deepEqual(s.items.map(o=>o.text),["Analytics"]),t.equal(e.text,g),t.deepEqual(e.items.map(o=>o.text),["Manage Plugins","Todo","Notes"]),t.equal(e.items[0].url,"#/plugins"),t.equal(e.items[0].permission,"plugins")}),l("groupPluginItems omits Tools folder when no core plugins",()=>{const{toolsFolder:s,pluginsFolder:e}=u([n({text:"Todo",url:"#/plugins/todo",core:!1})]);t.equal(s,null),t.deepEqual(e.items.map(o=>o.text),["Manage Plugins","Todo"])}),l("groupPluginItems still returns Plugins folder (with Manage Plugins) when no plugins at all",()=>{const{toolsFolder:s,pluginsFolder:e}=u([]);t.equal(s,null),t.deepEqual(e.items.map(o=>o.text),["Manage Plugins"])}),l("groupPluginItems keeps explicitly-parented items separate",()=>{const{parented:s,pluginsFolder:e}=u([n({text:"Nested",url:"#/x"},"Content"),n({text:"Todo",url:"#/plugins/todo",core:!1})]);t.equal(s.length,1),t.equal(s[0].parent,"Content"),t.deepEqual(e.items.map(o=>o.text),["Manage Plugins","Todo"])}),l("stripItemByUrl removes the management link at any depth",()=>{const s=[{text:"Overview",items:[{text:"Dashboard",url:"#/"}]},{text:"System",items:[{text:"Users",url:"#/users"},{text:"Plugins",url:"#/plugins"}]}],o=a(s,"#/plugins").find(r=>r.text==="System");t.deepEqual(o.items.map(r=>r.text),["Users"]),t.equal(s.find(r=>r.text==="System").items.length,2)}),l("insertFoldersBeforeSystem places folders just before System",()=>{const e=i([{text:"Overview"},{text:"Data"},{text:"System"},{text:"Documentation"}],[{text:"Tools"},{text:"Plugins"},null]);t.deepEqual(e.map(o=>o.text),["Overview","Data","Tools","Plugins","System","Documentation"])}),l("insertFoldersBeforeSystem appends when no System/Documentation anchor",()=>{const e=i([{text:"Overview"},{text:"Data"}],[{text:"Plugins"}]);t.deepEqual(e.map(o=>o.text),["Overview","Data","Plugins"])}),l("pruneEmptySynthesisedFolders drops an empty Plugins folder but keeps built-ins",()=>{const e=p([{text:"Overview",items:[]},{text:"Tools",items:[{text:"Analytics"}]},{text:"Plugins",items:[]}]);t.deepEqual(e.map(o=>o.text),["Overview","Tools"])});
1
+ import{test as n}from"node:test";import s from"node:assert/strict";import{groupPluginItems as l,stripItemByUrl as m,insertFoldersBeforeSystem as u,pruneEmptySynthesisedFolders as i,TOOLS_FOLDER_TEXT as g,MANAGE_PLUGINS_URL as p}from"./sidebar-grouping.js";const a=(e,t=null)=>({parent:t,item:e});n("groupPluginItems routes every enabled item to Tools, whatever its core flag",()=>{const{toolsFolder:e}=l([a({text:"Analytics",url:"#/plugins/analytics",core:!0}),a({text:"Todo",url:"#/plugins/todo",core:!1}),a({text:"Notes",url:"#/plugins/notes"})]);s.equal(e.text,g),s.deepEqual(e.items.map(t=>t.text),["Analytics","Todo","Notes"])}),n("Manage Plugins is a standalone entry, not a folder",()=>{const{managePluginsItem:e}=l([a({text:"Todo",url:"#/plugins/todo"})]);s.equal(e.text,"Manage Plugins"),s.equal(e.url,p),s.equal(e.permission,"plugins"),s.ok(!("items"in e),"it must not carry a sub-tree")}),n("groupPluginItems omits the Tools folder only when nothing is enabled",()=>{const e=l([a({text:"Todo",url:"#/plugins/todo"})]);s.deepEqual(e.toolsFolder.items.map(o=>o.text),["Todo"]);const t=l([]);s.equal(t.toolsFolder,null),s.equal(t.managePluginsItem.text,"Manage Plugins")}),n("groupPluginItems keeps explicitly-parented items separate",()=>{const{parented:e,toolsFolder:t}=l([a({text:"Nested",url:"#/x"},"Content"),a({text:"Todo",url:"#/plugins/todo",core:!1})]);s.equal(e.length,1),s.equal(e[0].parent,"Content"),s.deepEqual(t.items.map(o=>o.text),["Todo"])}),n("each call returns its own Manage Plugins object",()=>{const e=l([]).managePluginsItem,t=l([]).managePluginsItem;s.notEqual(e,t),s.deepEqual(e,t)}),n("stripItemByUrl removes the management link at any depth",()=>{const e=[{text:"Overview",items:[{text:"Dashboard",url:"#/"}]},{text:"System",items:[{text:"Users",url:"#/users"},{text:"Plugins",url:"#/plugins"}]}],o=m(e,"#/plugins").find(r=>r.text==="System");s.deepEqual(o.items.map(r=>r.text),["Users"]),s.equal(e.find(r=>r.text==="System").items.length,2)}),n("insertFoldersBeforeSystem places Tools then Manage Plugins before System",()=>{const t=u([{text:"Overview"},{text:"Data"},{text:"System"},{text:"Documentation"}],[{text:"Tools",items:[]},{text:"Manage Plugins",url:"#/plugins"},null]);s.deepEqual(t.map(o=>o.text),["Overview","Data","Tools","Manage Plugins","System","Documentation"])}),n("insertFoldersBeforeSystem appends when no System/Documentation anchor",()=>{const t=u([{text:"Overview"},{text:"Data"}],[{text:"Manage Plugins",url:"#/plugins"}]);s.deepEqual(t.map(o=>o.text),["Overview","Data","Manage Plugins"])}),n("pruneEmptySynthesisedFolders drops an empty Tools folder but keeps built-ins",()=>{const e=[{text:"Overview",items:[]},{text:"Tools",items:[]}];s.deepEqual(i(e).map(t=>t.text),["Overview"])}),n("pruneEmptySynthesisedFolders leaves the standalone Manage Plugins alone",()=>{const e=[{text:"Tools",items:[{text:"Analytics"}]},{text:"Manage Plugins",url:"#/plugins"}];s.deepEqual(i(e).map(t=>t.text),["Tools","Manage Plugins"])});
@@ -1,4 +1,4 @@
1
- import{api as c}from"../api.js";import{colourToCss as R,applyFloat as K,applyHighlight as G}from"/public/js/menu-decor.mjs?v=20260905-menu-highlight";import{groupPluginItems as O,stripItemByUrl as V,insertFoldersBeforeSystem as Y,pruneEmptySynthesisedFolders as J,MANAGE_PLUGINS_URL as Q}from"./sidebar-grouping.js";function h(e){return String(e??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}const Z=[{text:"Dashboard",url:"#/",icon:"home"},{text:"Menus",url:"#/menus",icon:"menu"}];function B(e,a){return a?!e||!e.length?!1:e.includes(a)?!0:["read","create","update","delete"].some(t=>e.includes(`${a}.${t}`)):!0}function I(e,a){const t=[];for(const n of e){if(n.hidden||n.permission&&!B(a,n.permission))continue;const i=Array.isArray(n.items)&&n.items.length?I(n.items,a):[];t.push({...n,items:i})}return t}function ee(e,a){if(!a||!a.length)return e;const t=(n,i)=>{for(const d of n){if((d.text||"").toLowerCase()===i.toLowerCase())return d;if(Array.isArray(d.items)){const r=t(d.items,i);if(r)return r}}return null};for(const n of a)if(n.parent){const i=t(e,n.parent);i?(i.items=Array.isArray(i.items)?i.items:[],i.items.push(n.item)):e.push(n.item)}else e.push(n.item);return e}async function te(e){if(!B(e,"projects"))return[];let a=[];try{a=await c.projects.list()}catch{return[]}const t={};await Promise.all(a.map(async i=>{try{const d=await c.projects.artefacts(i.slug);t[i.slug]=d}catch{t[i.slug]={}}}));const n=[{key:"pages",text:"Pages",icon:"file-text",path:"/pages"},{key:"collections",text:"Collections",icon:"database",path:"/collections"},{key:"forms",text:"Forms",icon:"layout",path:"/forms"},{key:"actions",text:"Actions",icon:"zap",path:"/actions"},{key:"menus",text:"Menus",icon:"menu",path:"/menus"},{key:"blocks",text:"Blocks",icon:"box",path:"/blocks"},{key:"components",text:"Components",icon:"component",path:"/components"},{key:"views",text:"Views",icon:"eye",path:"/views"},{key:"roles",text:"Roles",icon:"shield",path:"/roles"},{key:"users",text:"Users",icon:"users",path:"/users"},{key:"apis",text:"APIs",icon:"code",path:"/apis"}];return a.map(i=>{const r="#/projects/"+encodeURIComponent(i.slug),m=t[i.slug]||{},f=[{text:"Overview",url:r,icon:i.icon||"folder"}];for(const p of n){const y=m[p.key],g=Array.isArray(y)?y.length:0;g<=0||f.push({text:`${p.text} (${g})`,url:r+p.path,icon:p.icon})}f.push({text:"Settings",url:r+"/settings",icon:"settings"});const u=i.enabled===!1;return{text:i.name||i.slug,icon:i.icon||"folder",items:f,...u&&{colour:"neutral",badge:{text:"off"}}}})}function ne(e,a){for(const t of e)if((t.text||"").toLowerCase()==="projects"){t.items=Array.isArray(t.items)?t.items.slice():[],t.items.push(...a);return}}function se(e,a){if(!e.url||!a)return"";const t=a[e.url];return t==null||t<=0?"":`<span class="sidebar-badge">${h(String(t))}</span>`}function ie(e){const a=(e.text||"").replace(/\s*\(\d+\)\s*$/,"").toLowerCase().replace(/\s+/g,"-"),t=(e.url||"").match(/^#\/projects\/([^/]+)/);return`sidebar.expanded.${t?`project-${decodeURIComponent(t[1])}.`:""}${a}`}function N(e,a){if(e&&e.type==="separator")return'<hr class="sidebar-divider">';if(e&&e.type==="spacer"){const u=["xs","sm","md","lg","xl","flex"].includes(e.size)?e.size:"md";return`<div class="sidebar-spacer sidebar-spacer--${h(u)}" aria-hidden="true"></div>`}const t=e.icon?`<span data-icon="${h(e.icon)}"></span>`:"",n=h(e.text||""),i=e.url?h(e.url):"",d=Array.isArray(e.items)&&e.items.length>0,r=e.colour?R(e.colour):"",m=r?` style="color:${h(r)}"`:"";let f="";if(e.badge&&e.badge.text!=null&&e.badge.text!==""){const u=e.badge.variant?R(e.badge.variant):"";f=`<span class="dm-menu-badge"${u?` style="background:${h(u)};color:var(--dm-text-inverse,#fff)"`:""}>${h(String(e.badge.text))}</span>`}if(d){const u=ie(e),p=S.get(u)!==!1,y=e.items.map(g=>N(g,a)).join("");return`<details data-state-key="${h(u)}"${p?" open":""}>
2
- <summary${m}>${t} <span class="sidebar-text">${n}</span>${f}</summary>
3
- <div class="sidebar-children">${y}</div>
4
- </details>`}return`<a href="${i}" class="sidebar-link" data-url="${i}"${m}>${t} <span class="sidebar-text">${n}</span>${se(e,a)}${f}</a>`}async function ae(){const e=g=>g.then(b=>Array.isArray(b)?b.length:Array.isArray(b?.entries)?b.entries.length:0).catch(()=>0),[a,t,n,i,d,r,m,f,u,p,y]=await Promise.all([e(c.pages.list()),e(c.media.list()),e(c.collections.list()),e(c.forms.list()),e(c.views.list()),e(c.actions.list()),e(c.blocks.list()),e(c.components.list()),e(c.users.list()),e(c.plugins.list()),c.system?.notifications?.unreadCount?.().then(g=>g?.count??0).catch(()=>0)??Promise.resolve(0)]);return{"#/pages":a,"#/media":t,"#/collections":n,"#/forms":i,"#/views":d,"#/actions":r,"#/blocks":m,"#/components":f,"#/users":u,"#/plugins":p,"#/system/notifications":y}}async function re(){try{const e=await c.settings.get();return e?.adminBrand?.title||e?.title||"Admin"}catch{return"Admin"}}export async function renderAdminSidebar({mount:e,permissions:a}){const t=$(e).get(0);if(!t)return;let n,i=null,d=null,r=null,m=null,f=null;try{const s=await c.menus.get("admin-sidebar");n=Array.isArray(s?.items)?s.items:null,i=s?.variant||null,d=s?.position||null,r=s?.style||null,m=s?.float||null,f=s?.highlight||null}catch{n=null}(!n||!n.length)&&(console.warn("[admin-sidebar] No admin-sidebar menu found; using fallback tree"),n=Z.slice());let u=[];try{u=await H.get("/api/sidebar/registered-items")||[]}catch{}n=V(n,Q);const{toolsFolder:p,pluginsFolder:y,parented:g}=O(u);n=ee(n,g),n=Y(n,[p,y]);const b=await te(a);b.length&&ne(n,b),n=I(n,a),n=J(n);const[T,U]=await Promise.all([ae().catch(()=>({})),re()]),q=i?` dm-admin-sidebar--${h(i)}`:"";function j(s,l="px"){if(s==null)return s;const o=String(s).trim();return/^\d+(\.\d+)?$/.test(o)?`${o}${l}`:o}let E="";if(r){const s=[];r.fontFamily&&s.push(`font-family: ${r.fontFamily}, sans-serif`),r.fontSize&&s.push(`font-size: ${j(r.fontSize)}`),r.fontWeight&&s.push(`font-weight: ${r.fontWeight}`),r.letterSpacing&&s.push(`letter-spacing: ${j(r.letterSpacing,"em")}`);const l=[];if(s.length&&l.push(`#admin-sidebar .dm-admin-sidebar, #admin-sidebar .dm-admin-sidebar .sidebar-link, #admin-sidebar .dm-admin-sidebar summary, #admin-sidebar .dm-admin-sidebar .sidebar-text { ${s.join("; ")} }`),r.iconSize){const o=j(r.iconSize);l.push(`#admin-sidebar .dm-admin-sidebar [data-icon], #admin-sidebar .dm-admin-sidebar [data-icon] svg { width: ${o} !important; height: ${o} !important; }`)}l.length&&(E=`<style data-admin-sidebar-style>${l.join(" ")}</style>`)}const X=`<div class="dm-admin-sidebar-header"><span data-icon="layout"></span> ${h(U)}</div>`,W=n.some(s=>s&&s.type==="spacer"&&s.size==="flex")?" dm-admin-sidebar--has-flex-spacer":"",D=`${E}<nav class="dm-admin-sidebar${q}${W}">${X}${n.map(s=>N(s,T)).join("")}</nav>`,L=document.createRange();L.selectNodeContents(t);const _=L.createContextualFragment(D);t.replaceChildren(_),Domma.icons.scan(t);const v=Number.parseInt(S.get("sidebar.width"),10);Number.isFinite(v)&&v>=180&&v<=480&&(t.style.width=v+"px"),t.removeAttribute("data-menu-hover"),t.removeAttribute("data-menu-active"),t.style.removeProperty("--dm-menu-highlight"),G(t,{highlight:f});const x=document.querySelector(".dashboard-wrapper");if(["position","top","bottom","left","right","zIndex","maxHeight","transform"].forEach(s=>{t.style[s]=""}),t.classList.remove("dm-menu-floating"),x&&(x.style.paddingLeft="",x.style.paddingRight=""),d==="floating"){K(t,{position:"floating",float:m||{}});const l=m&&m.anchor||"TL",o=Number.isFinite(Number(m?.offsetX))?Number(m.offsetX):16,A=t.getBoundingClientRect().width||v||260;if(x){const C=Math.round(A+o*2)+"px";l==="R"||l==="TR"||l==="BR"?x.style.paddingRight=C:x.style.paddingLeft=C}}const w=document.createElement("div");w.className="dm-admin-sidebar-handle",w.setAttribute("aria-label","Resize sidebar"),t.appendChild(w),t.querySelectorAll("details").forEach(s=>{s.addEventListener("toggle",function(){const l=this.getAttribute("data-state-key");l&&S.set(l,this.open);const o=this.querySelector(":scope > .sidebar-children");if(o)if(this.open){const A=o.scrollHeight;o.style.maxHeight="0",requestAnimationFrame(()=>{o.style.maxHeight=A+"px",o.addEventListener("transitionend",function C(){o.style.maxHeight="none",o.removeEventListener("transitionend",C)})})}else{const A=o.scrollHeight;o.style.maxHeight=A+"px",requestAnimationFrame(()=>{o.style.maxHeight="0"})}})});let k=!1,P=0,F=0;w.addEventListener("mousedown",s=>{k=!0,P=s.clientX,F=t.getBoundingClientRect().width,document.body.style.cursor="col-resize",document.body.style.userSelect="none",s.preventDefault()}),document.addEventListener("mousemove",s=>{if(!k)return;const l=Math.max(180,Math.min(480,F+(s.clientX-P)));t.style.width=l+"px"}),document.addEventListener("mouseup",()=>{k&&(k=!1,document.body.style.cursor="",document.body.style.userSelect="",S.set("sidebar.width",parseInt(t.style.width,10)))});function z(){const s=location.hash||"#/";$(t).find(".sidebar-link").removeClass("active"),$(t).find(`.sidebar-link[data-url="${s}"]`).addClass("active")}z(),M.subscribe("router:afterChange",z)}
1
+ import{api as c}from"../api.js";import{colourToCss as U,applyFloat as K,applyHighlight as O,itemHighlightAttrs as G}from"/public/js/menu-decor.mjs?v=20260918-item-highlight";import{groupPluginItems as V,stripItemByUrl as Y,insertFoldersBeforeSystem as J,pruneEmptySynthesisedFolders as Q,MANAGE_PLUGINS_URL as Z}from"./sidebar-grouping.js";function h(e){return String(e??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}const ee=[{text:"Dashboard",url:"#/",icon:"home"},{text:"Menus",url:"#/menus",icon:"menu"}];function T(e,s){return s?!e||!e.length?!1:e.includes(s)?!0:["read","create","update","delete"].some(t=>e.includes(`${s}.${t}`)):!0}function q(e,s){const t=[];for(const n of e){if(n.hidden||n.permission&&!T(s,n.permission))continue;const a=Array.isArray(n.items)&&n.items.length?q(n.items,s):[];t.push({...n,items:a})}return t}function te(e,s){if(!s||!s.length)return e;const t=(n,a)=>{for(const m of n){if((m.text||"").toLowerCase()===a.toLowerCase())return m;if(Array.isArray(m.items)){const r=t(m.items,a);if(r)return r}}return null};for(const n of s)if(n.parent){const a=t(e,n.parent);a?(a.items=Array.isArray(a.items)?a.items:[],a.items.push(n.item)):e.push(n.item)}else e.push(n.item);return e}async function ne(e){if(!T(e,"projects"))return[];let s=[];try{s=await c.projects.list()}catch{return[]}const t={};await Promise.all(s.map(async a=>{try{const m=await c.projects.artefacts(a.slug);t[a.slug]=m}catch{t[a.slug]={}}}));const n=[{key:"pages",text:"Pages",icon:"file-text",path:"/pages"},{key:"collections",text:"Collections",icon:"database",path:"/collections"},{key:"forms",text:"Forms",icon:"layout",path:"/forms"},{key:"actions",text:"Actions",icon:"zap",path:"/actions"},{key:"menus",text:"Menus",icon:"menu",path:"/menus"},{key:"blocks",text:"Blocks",icon:"box",path:"/blocks"},{key:"components",text:"Components",icon:"component",path:"/components"},{key:"views",text:"Views",icon:"eye",path:"/views"},{key:"roles",text:"Roles",icon:"shield",path:"/roles"},{key:"users",text:"Users",icon:"users",path:"/users"},{key:"apis",text:"APIs",icon:"code",path:"/apis"}];return s.map(a=>{const r="#/projects/"+encodeURIComponent(a.slug),d=t[a.slug]||{},g=[{text:"Overview",url:r,icon:a.icon||"folder"}];for(const p of n){const b=d[p.key],f=Array.isArray(b)?b.length:0;f<=0||g.push({text:`${p.text} (${f})`,url:r+p.path,icon:p.icon})}g.push({text:"Settings",url:r+"/settings",icon:"settings"});const y=a.enabled===!1;return{text:a.name||a.slug,icon:a.icon||"folder",items:g,...y&&{colour:"neutral",badge:{text:"off"}}}})}function se(e,s){for(const t of e)if((t.text||"").toLowerCase()==="projects"){t.items=Array.isArray(t.items)?t.items.slice():[],t.items.push(...s);return}}function ie(e,s){if(!e.url||!s)return"";const t=s[e.url];return t==null||t<=0?"":`<span class="sidebar-badge">${h(String(t))}</span>`}function ae(e){const s=(e.text||"").replace(/\s*\(\d+\)\s*$/,"").toLowerCase().replace(/\s+/g,"-"),t=(e.url||"").match(/^#\/projects\/([^/]+)/);return`sidebar.expanded.${t?`project-${decodeURIComponent(t[1])}.`:""}${s}`}function X(e,s){if(e&&e.type==="separator")return'<hr class="sidebar-divider">';if(e&&e.type==="spacer"){const f=["xs","sm","md","lg","xl","flex"].includes(e.size)?e.size:"md";return`<div class="sidebar-spacer sidebar-spacer--${h(f)}" aria-hidden="true"></div>`}const t=e.icon?`<span data-icon="${h(e.icon)}"></span>`:"",n=h(e.text||""),a=e.url?h(e.url):"",m=Array.isArray(e.items)&&e.items.length>0,r=e.colour?U(e.colour):"",d=G(e),g=[d.hover&&` data-menu-item-hover="${h(d.hover)}"`,d.active&&` data-menu-item-active="${h(d.active)}"`].filter(Boolean).join(""),y=[r&&`color:${h(r)}`,d.accent&&`--dm-menu-highlight:${h(d.accent)}`].filter(Boolean).join(";"),p=y?` style="${y}"`:"";let b="";if(e.badge&&e.badge.text!=null&&e.badge.text!==""){const f=e.badge.variant?U(e.badge.variant):"";b=`<span class="dm-menu-badge"${f?` style="background:${h(f)};color:var(--dm-text-inverse,#fff)"`:""}>${h(String(e.badge.text))}</span>`}if(m){const f=ae(e),v=S.get(f)!==!1,w=e.items.map(k=>X(k,s)).join("");return`<details data-state-key="${h(f)}"${v?" open":""}>
2
+ <summary${p}>${t} <span class="sidebar-text">${n}</span>${b}</summary>
3
+ <div class="sidebar-children">${w}</div>
4
+ </details>`}return`<a href="${a}" class="sidebar-link" data-url="${a}"${p}${g}>${t} <span class="sidebar-text">${n}</span>${ie(e,s)}${b}</a>`}async function re(e=[]){const s=o=>o.then(x=>Array.isArray(x)?x.length:Array.isArray(x?.entries)?x.entries.length:0).catch(()=>0),[t,n,a,m,r,d,g,y,p,b,f,v,w,k]=await Promise.all([s(c.pages.list()),s(c.media.list()),s(c.menus.list()),s(c.contextMenus.list()),c.theme.get().then(o=>(o?.overrides||[]).length).catch(()=>0),s(c.collections.list()),s(c.forms.list()),s(c.views.list()),s(c.actions.list()),s(c.blocks.list()),s(c.components.list()),s(c.users.list()),s(c.plugins.list()),c.system?.notifications?.unreadCount?.().then(o=>o?.count??0).catch(()=>0)??Promise.resolve(0)]),E={};return await Promise.all((e??[]).map(o=>o?.item??o).filter(o=>o?.url&&typeof o.countUrl=="string"&&o.countUrl).map(async o=>{try{const x=await H.get(o.countUrl),C=Number(x?.count);Number.isFinite(C)&&C>0&&(E[o.url]=C)}catch{}})),{...E,"#/pages":t,"#/media":n,"#/menus":a,"#/context-menus":m,"#/theme":r,"#/collections":d,"#/forms":g,"#/views":y,"#/actions":p,"#/blocks":b,"#/components":f,"#/users":v,"#/plugins":w,"#/system/notifications":k}}async function oe(){try{const e=await c.settings.get();return e?.adminBrand?.title||e?.title||"Admin"}catch{return"Admin"}}export async function renderAdminSidebar({mount:e,permissions:s}){const t=$(e).get(0);if(!t)return;let n,a=null,m=null,r=null,d=null,g=null;try{const i=await c.menus.get("admin-sidebar");n=Array.isArray(i?.items)?i.items:null,a=i?.variant||null,m=i?.position||null,r=i?.style||null,d=i?.float||null,g=i?.highlight||null}catch{n=null}(!n||!n.length)&&(console.warn("[admin-sidebar] No admin-sidebar menu found; using fallback tree"),n=ee.slice());let y=[];try{y=await H.get("/api/sidebar/registered-items")||[]}catch{}n=Y(n,Z);const{toolsFolder:p,managePluginsItem:b,parented:f}=V(y);n=te(n,f),n=J(n,[p,b]);const v=await ne(s);v.length&&se(n,v),n=q(n,s),n=Q(n);const[w,k]=await Promise.all([re(y).catch(()=>({})),oe()]),E=a?` dm-admin-sidebar--${h(a)}`:"";function o(i,u="px"){if(i==null)return i;const l=String(i).trim();return/^\d+(\.\d+)?$/.test(l)?`${l}${u}`:l}let x="";if(r){const i=[];r.fontFamily&&i.push(`font-family: ${r.fontFamily}, sans-serif`),r.fontSize&&i.push(`font-size: ${o(r.fontSize)}`),r.fontWeight&&i.push(`font-weight: ${r.fontWeight}`),r.letterSpacing&&i.push(`letter-spacing: ${o(r.letterSpacing,"em")}`);const u=[];if(i.length&&u.push(`#admin-sidebar .dm-admin-sidebar, #admin-sidebar .dm-admin-sidebar .sidebar-link, #admin-sidebar .dm-admin-sidebar summary, #admin-sidebar .dm-admin-sidebar .sidebar-text { ${i.join("; ")} }`),r.iconSize){const l=o(r.iconSize);u.push(`#admin-sidebar .dm-admin-sidebar [data-icon], #admin-sidebar .dm-admin-sidebar [data-icon] svg { width: ${l} !important; height: ${l} !important; }`)}u.length&&(x=`<style data-admin-sidebar-style>${u.join(" ")}</style>`)}const C=`<div class="dm-admin-sidebar-header"><span data-icon="layout"></span> ${h(k)}</div>`,W=n.some(i=>i&&i.type==="spacer"&&i.size==="flex")?" dm-admin-sidebar--has-flex-spacer":"",D=`${x}<nav class="dm-admin-sidebar${E}${W}">${C}${n.map(i=>X(i,w)).join("")}</nav>`,B=document.createRange();B.selectNodeContents(t);const _=B.createContextualFragment(D);t.replaceChildren(_),Domma.icons.scan(t);const j=Number.parseInt(S.get("sidebar.width"),10);Number.isFinite(j)&&j>=180&&j<=480&&(t.style.width=j+"px"),t.removeAttribute("data-menu-hover"),t.removeAttribute("data-menu-active"),t.style.removeProperty("--dm-menu-highlight"),O(t,{highlight:g});const A=document.querySelector(".dashboard-wrapper");if(["position","top","bottom","left","right","zIndex","maxHeight","transform"].forEach(i=>{t.style[i]=""}),t.classList.remove("dm-menu-floating"),A&&(A.style.paddingLeft="",A.style.paddingRight=""),m==="floating"){K(t,{position:"floating",float:d||{}});const u=d&&d.anchor||"TL",l=Number.isFinite(Number(d?.offsetX))?Number(d.offsetX):16,P=t.getBoundingClientRect().width||j||260;if(A){const z=Math.round(P+l*2)+"px";u==="R"||u==="TR"||u==="BR"?A.style.paddingRight=z:A.style.paddingLeft=z}}const L=document.createElement("div");L.className="dm-admin-sidebar-handle",L.setAttribute("aria-label","Resize sidebar"),t.appendChild(L),t.querySelectorAll("details").forEach(i=>{i.addEventListener("toggle",function(){const u=this.getAttribute("data-state-key");u&&S.set(u,this.open);const l=this.querySelector(":scope > .sidebar-children");if(l)if(this.open){const P=l.scrollHeight;l.style.maxHeight="0",requestAnimationFrame(()=>{l.style.maxHeight=P+"px",l.addEventListener("transitionend",function z(){l.style.maxHeight="none",l.removeEventListener("transitionend",z)})})}else{const P=l.scrollHeight;l.style.maxHeight=P+"px",requestAnimationFrame(()=>{l.style.maxHeight="0"})}})});let F=!1,R=0,I=0;L.addEventListener("mousedown",i=>{F=!0,R=i.clientX,I=t.getBoundingClientRect().width,document.body.style.cursor="col-resize",document.body.style.userSelect="none",i.preventDefault()}),document.addEventListener("mousemove",i=>{if(!F)return;const u=Math.max(180,Math.min(480,I+(i.clientX-R)));t.style.width=u+"px"}),document.addEventListener("mouseup",()=>{F&&(F=!1,document.body.style.cursor="",document.body.style.userSelect="",S.set("sidebar.width",parseInt(t.style.width,10)))});function N(){const i=location.hash||"#/";$(t).find(".sidebar-link").removeClass("active"),$(t).find(`.sidebar-link[data-url="${i}"]`).addClass("active")}N(),M.subscribe("router:afterChange",N)}
@@ -0,0 +1 @@
1
+ const I=360,g="dm-slideover-resizer",L="dm.slideover.width.",f=new Set;function _(e){return String(e||"").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"default"}function a(e){return Math.min(Math.max(360,Math.round(e)),Math.max(360,window.innerWidth-80))}function z(e){try{const n=Number(S.get(e));return Number.isFinite(n)&&n>=360?n:null}catch{return null}}function W(){for(const e of f){if(!e.isConnected){f.delete(e);continue}e.style.setProperty("width",`${a(e.__dmResize.width)}px`,"important")}}window.addEventListener("resize",W);export function applyResizable(e,{storageKey:n,defaultWidth:c}={}){if(!e||e.nodeType!==1)return;const r=e.classList.contains("dm-slideover-right"),l=e.classList.contains("dm-slideover-left");if(!r&&!l)return;const s=e.__dmResize,t=s??{width:640,storageKey:"",defaultWidth:640};n&&(t.storageKey=n),c&&(t.defaultWidth=c);const v=t.storageKey?z(t.storageKey):null;t.width=a(v??t.defaultWidth);const o=()=>{e.style.setProperty("width",`${t.width}px`,"important"),e.style.setProperty("min-width","360px","important")};if(t.apply=o,e.__dmResize=t,o(),s)return;const i=document.createElement("span");i.className=g,i.setAttribute("role","separator"),i.setAttribute("aria-orientation","vertical"),i.title="Drag to resize \xB7 double-click to reset",e.appendChild(i),f.add(e),i.addEventListener("pointerdown",u=>{u.preventDefault();const y=u.clientX,h=t.width;i.setPointerCapture?.(u.pointerId),e.classList.add("is-resizing");const m=w=>{const p=w.clientX-y;t.width=a(r?h-p:h+p),o()},d=()=>{i.removeEventListener("pointermove",m),i.removeEventListener("pointerup",d),i.removeEventListener("pointercancel",d),e.classList.remove("is-resizing");try{t.storageKey&&S.set(t.storageKey,t.width)}catch{}};i.addEventListener("pointermove",m),i.addEventListener("pointerup",d),i.addEventListener("pointercancel",d)}),i.addEventListener("dblclick",()=>{t.width=a(t.defaultWidth),o();try{t.storageKey&&S.set(t.storageKey,t.width)}catch{}})}export function installResizableSlideovers(){if(typeof E>"u"||typeof E.slideover!="function"||E.slideover.__dmResizable)return;const e=E.slideover,n=function(r={},...l){const s=e.call(this,r,...l);try{applyResizable(s?.element,{storageKey:`${L}${_(r.title)}`,defaultWidth:r.defaultWidth})}catch{}return s};n.__dmResizable=!0,E.slideover=n}