domma-cms 0.55.1 → 0.69.3

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 (305) hide show
  1. package/CLAUDE.md +194 -3
  2. package/README.md +18 -14
  3. package/admin/css/admin.css +1 -1
  4. package/admin/css/contacts.css +1 -0
  5. package/admin/dist/domma/domma-tools.css +3 -3
  6. package/admin/dist/domma/domma-tools.min.js +3 -3
  7. package/admin/index.html +1 -0
  8. package/admin/js/api.js +1 -1
  9. package/admin/js/app.js +4 -4
  10. package/admin/js/lib/analytics-shape.js +1 -0
  11. package/admin/js/lib/contacts-arrange.js +1 -0
  12. package/admin/js/lib/help-popover.js +1 -0
  13. package/admin/js/lib/notes-arrange.js +1 -0
  14. package/admin/js/lib/page-picker.js +1 -0
  15. package/admin/js/lib/plugin-accent.js +1 -0
  16. package/admin/js/lib/plugin-chrome.js +1 -0
  17. package/admin/js/lib/shortcode-context-menu.js +2 -2
  18. package/admin/js/lib/sidebar-grouping.js +1 -1
  19. package/admin/js/lib/sidebar-grouping.test.js +1 -1
  20. package/admin/js/lib/sidebar-renderer.js +4 -4
  21. package/admin/js/lib/slideover-resizable.js +1 -0
  22. package/admin/js/lib/todo-arrange.js +1 -0
  23. package/admin/js/lib/tool-kit.js +1 -0
  24. package/admin/js/templates/analytics.html +138 -0
  25. package/admin/js/templates/contacts.html +156 -0
  26. package/admin/js/templates/context-menu-editor.html +212 -0
  27. package/admin/js/templates/context-menus.html +16 -0
  28. package/admin/js/templates/notes.html +137 -0
  29. package/admin/js/templates/plugin-code.html +1 -1
  30. package/admin/js/templates/plugin-marketplace.html +17 -4
  31. package/admin/js/templates/plugins.html +13 -7
  32. package/{plugins/site-search/admin/templates/site-search.html → admin/js/templates/search.html} +50 -4
  33. package/admin/js/templates/settings.html +16 -97
  34. package/admin/js/templates/theme.html +211 -0
  35. package/admin/js/templates/todo.html +113 -0
  36. package/admin/js/views/analytics.js +62 -0
  37. package/admin/js/views/contacts.js +174 -0
  38. package/admin/js/views/context-menu-editor.js +55 -0
  39. package/admin/js/views/context-menus.js +5 -0
  40. package/admin/js/views/form-editor.js +7 -7
  41. package/admin/js/views/index.js +1 -1
  42. package/admin/js/views/notes.js +104 -0
  43. package/admin/js/views/plugin-marketplace.js +1 -1
  44. package/admin/js/views/plugins.js +28 -24
  45. package/admin/js/views/search.js +1 -0
  46. package/admin/js/views/settings.js +3 -3
  47. package/admin/js/views/theme.js +1 -0
  48. package/admin/js/views/todo.js +96 -0
  49. package/bin/cli.js +6 -10
  50. package/bin/lib/plugin-version.js +28 -0
  51. package/bin/update.js +27 -2
  52. package/config/menus/admin-sidebar.json +129 -23
  53. package/config/plugins.json +5 -5
  54. package/config/search.json +13 -0
  55. package/config/server.json +3 -1
  56. package/package.json +14 -10
  57. package/plugins/_lib/admin/mail/compose-window.js +914 -0
  58. package/plugins/_lib/admin/mail/contacts.js +301 -0
  59. package/plugins/_lib/admin/mail/diagnostics-section.js +133 -0
  60. package/plugins/_lib/admin/mail/folder-tree.js +254 -0
  61. package/plugins/_lib/admin/mail/identity.js +480 -0
  62. package/plugins/_lib/admin/mail/image-senders-section.js +131 -0
  63. package/plugins/_lib/admin/mail/keyboard.js +136 -0
  64. package/plugins/_lib/admin/mail/mail.css +1 -0
  65. package/plugins/_lib/admin/mail/mail.html +71 -0
  66. package/plugins/_lib/admin/mail/panes.js +253 -0
  67. package/plugins/_lib/admin/mail/reader-view.js +4473 -0
  68. package/plugins/_lib/admin/mail/resizable.js +26 -0
  69. package/plugins/_lib/admin/mail/rules.js +343 -0
  70. package/plugins/_lib/admin/mail/scheduling.js +203 -0
  71. package/plugins/_lib/admin/mail/section-kit.js +277 -0
  72. package/plugins/_lib/admin/mail/templates.js +238 -0
  73. package/plugins/_lib/admin/mail/threads.js +200 -0
  74. package/plugins/_lib/admin/mail/vacation.js +269 -0
  75. package/plugins/_lib/admin/ui/help.css +1 -0
  76. package/plugins/_lib/admin/ui/help.js +174 -0
  77. package/plugins/_lib/admin/ui/resizable.js +151 -0
  78. package/plugins/_lib/dataStore.js +117 -0
  79. package/plugins/_lib/mail/accounts.js +919 -0
  80. package/plugins/_lib/mail/bodyTokens.js +101 -0
  81. package/plugins/_lib/mail/compose.js +256 -0
  82. package/plugins/_lib/mail/defaults.js +57 -0
  83. package/plugins/_lib/mail/diagnostics.js +154 -0
  84. package/plugins/_lib/mail/envelope.js +161 -0
  85. package/plugins/_lib/mail/folders.js +192 -0
  86. package/plugins/_lib/mail/handoff.js +274 -0
  87. package/plugins/_lib/mail/imapPool.js +293 -0
  88. package/plugins/_lib/mail/mbox.js +74 -0
  89. package/plugins/_lib/mail/pollSchedule.js +79 -0
  90. package/plugins/_lib/mail/poller.js +135 -0
  91. package/plugins/_lib/mail/priority.js +138 -0
  92. package/plugins/_lib/mail/readRoutes.js +680 -0
  93. package/plugins/_lib/mail/render.js +291 -0
  94. package/plugins/_lib/mail/ruleRunner.js +152 -0
  95. package/plugins/_lib/mail/scheduler.js +254 -0
  96. package/plugins/_lib/mail/secretbox.js +229 -0
  97. package/plugins/_lib/mail/send.js +396 -0
  98. package/plugins/_lib/mail/store.js +1022 -0
  99. package/plugins/_lib/mail/sync.js +292 -0
  100. package/plugins/_lib/mail/syncPlan.js +126 -0
  101. package/plugins/_lib/mail/syncSelection.js +82 -0
  102. package/plugins/_lib/mail/unsubscribe.js +183 -0
  103. package/plugins/_lib/mail/vacationRunner.js +114 -0
  104. package/plugins/_lib/mail/write.js +473 -0
  105. package/plugins/_lib/schemaSync.js +83 -0
  106. package/plugins/_template/admin/css/index.css +0 -0
  107. package/plugins/_template/admin/templates/index.html +4 -4
  108. package/plugins/_template/admin/views/index.js +7 -0
  109. package/plugins/blog/CLAUDE.md +237 -0
  110. package/plugins/blog/admin/css/index.css +178 -0
  111. package/plugins/blog/admin/templates/blog.html +35 -41
  112. package/plugins/blog/admin/templates/categories.html +19 -6
  113. package/plugins/blog/admin/templates/comments.html +26 -9
  114. package/plugins/blog/admin/templates/post-editor.html +63 -40
  115. package/plugins/blog/admin/templates/settings.html +3 -9
  116. package/plugins/blog/admin/views/blog.js +288 -159
  117. package/plugins/blog/admin/views/categories.js +212 -206
  118. package/plugins/blog/admin/views/comments.js +207 -158
  119. package/plugins/blog/admin/views/kit.js +372 -0
  120. package/plugins/blog/admin/views/post-editor.js +306 -39
  121. package/plugins/blog/admin/views/settings-cog.js +56 -0
  122. package/plugins/blog/admin/views/settings.js +16 -84
  123. package/plugins/blog/blocks/blog-card-compact.css +17 -0
  124. package/plugins/blog/blocks/blog-card-compact.html +8 -0
  125. package/plugins/blog/blocks/blog-card-row.css +22 -0
  126. package/plugins/blog/blocks/blog-card-row.html +11 -0
  127. package/plugins/blog/blocks/blog-card.css +35 -0
  128. package/plugins/blog/blocks/blog-card.html +16 -0
  129. package/plugins/blog/blocks/blog-post-classic.css +15 -0
  130. package/plugins/blog/blocks/blog-post-classic.html +28 -0
  131. package/plugins/blog/blocks/blog-post-feature.css +20 -0
  132. package/plugins/blog/blocks/blog-post-feature.html +30 -0
  133. package/plugins/blog/blocks/blog-post-minimal.css +10 -0
  134. package/plugins/blog/blocks/blog-post-minimal.html +23 -0
  135. package/plugins/blog/blocks/blog-post-sidebar.css +22 -0
  136. package/plugins/blog/blocks/blog-post-sidebar.html +36 -0
  137. package/plugins/blog/collections/categories/schema.json +7 -6
  138. package/plugins/blog/collections/comments/schema.json +11 -10
  139. package/plugins/blog/collections/posts/schema.json +16 -13
  140. package/plugins/blog/config.js +6 -0
  141. package/plugins/blog/lib/layouts.js +304 -0
  142. package/plugins/blog/lib/page.js +158 -0
  143. package/plugins/blog/lib/render.js +119 -0
  144. package/plugins/blog/lib/samples.js +234 -0
  145. package/plugins/blog/plugin.js +259 -26
  146. package/plugins/blog/plugin.json +21 -73
  147. package/plugins/blog/plugin.public.js +217 -0
  148. package/plugins/blog/public/blog.css +177 -0
  149. package/plugins/blog/public/blog.js +586 -0
  150. package/plugins/blog/public/samples/aurora.svg +7 -0
  151. package/plugins/blog/public/samples/dusk.svg +7 -0
  152. package/plugins/blog/public/samples/ember.svg +7 -0
  153. package/plugins/blog/public/samples/harbour.svg +7 -0
  154. package/plugins/blog/public/samples/meadow.svg +7 -0
  155. package/plugins/blog/public/samples/rosewood.svg +7 -0
  156. package/plugins/blog/templates/index.html +9 -8
  157. package/plugins/blog/templates/post.html +4 -17
  158. package/plugins/blog/tests/layouts.test.js +97 -0
  159. package/plugins/blog/tests/public.test.js +71 -0
  160. package/plugins/free-tier.lock.json +74 -0
  161. package/plugins/mail-reader/CLAUDE.md +78 -0
  162. package/plugins/mail-reader/admin/views/mail.js +19 -0
  163. package/plugins/mail-reader/config.js +7 -0
  164. package/plugins/mail-reader/plugin.js +48 -0
  165. package/plugins/mail-reader/plugin.json +34 -0
  166. package/public/css/forms.css +1 -1
  167. package/public/css/search.css +1 -0
  168. package/public/css/site.css +1 -1
  169. package/public/css/theme-switcher.css +1 -0
  170. package/public/js/analytics.js +1 -0
  171. package/public/js/collection-context.js +2 -2
  172. package/public/js/context-menus.js +1 -0
  173. package/public/js/form-logic-engine.js +1 -1
  174. package/public/js/forms.js +2 -2
  175. package/public/js/search.js +1 -0
  176. package/public/js/site.js +1 -1
  177. package/public/js/theme-switcher.js +1 -0
  178. package/scripts/build.js +77 -4
  179. package/scripts/copy-domma.js +48 -0
  180. package/scripts/run-tests.mjs +92 -0
  181. package/scripts/seed.js +1996 -0
  182. package/{plugins/analytics/plugin.js → server/routes/api/analytics.js} +89 -41
  183. package/server/routes/api/collections.js +34 -0
  184. package/server/routes/api/contacts.js +506 -0
  185. package/server/routes/api/context-menus.js +104 -0
  186. package/server/routes/api/dashboard.js +8 -2
  187. package/server/routes/api/forms.js +42 -3
  188. package/{plugins/notes/plugin.js → server/routes/api/notes.js} +61 -17
  189. package/server/routes/api/notifications.js +69 -19
  190. package/server/routes/api/plugin-marketplace.js +78 -23
  191. package/server/routes/api/plugins.js +198 -9
  192. package/server/routes/api/search.js +43 -0
  193. package/server/routes/api/theme.js +69 -0
  194. package/server/routes/api/todo.js +178 -0
  195. package/server/routes/public.js +42 -7
  196. package/server/server.js +135 -1
  197. package/server/services/adapters/FileAdapter.js +6 -1
  198. package/server/services/collections.js +13 -3
  199. package/server/services/content.js +26 -0
  200. package/server/services/contextMenus.js +477 -0
  201. package/server/services/managerClient.js +72 -136
  202. package/server/services/markdown.js +124 -10
  203. package/server/services/permissionRegistry.js +74 -0
  204. package/server/services/pluginEntitlement.js +171 -0
  205. package/server/services/pluginEnvelope.js +242 -0
  206. package/server/services/pluginFiles.js +52 -11
  207. package/server/services/pluginInstaller.js +246 -26
  208. package/server/services/pluginScaffold.js +5 -0
  209. package/server/services/plugins.js +340 -7
  210. package/server/services/presetCollections.js +344 -0
  211. package/server/services/promoted-plugins-migration.js +254 -0
  212. package/server/services/publicCors.js +46 -0
  213. package/server/services/renderer.js +224 -22
  214. package/server/services/roles.js +1 -1
  215. package/server/services/search-migration.js +82 -0
  216. package/server/services/search.js +413 -0
  217. package/server/services/sidebar-migration.js +1 -0
  218. package/server/services/sidebarBadges.js +37 -0
  219. package/server/services/siteGitignore.js +307 -0
  220. package/server/services/themeSettings.js +580 -0
  221. package/server/services/users.js +8 -0
  222. package/server/templates/page.html +4 -2
  223. package/plugins/analytics/admin/templates/analytics.html +0 -61
  224. package/plugins/analytics/admin/views/analytics.js +0 -176
  225. package/plugins/analytics/config.js +0 -14
  226. package/plugins/analytics/plugin.json +0 -57
  227. package/plugins/analytics/public/inject-body.html +0 -60
  228. package/plugins/analytics/public/inject-head.html +0 -1
  229. package/plugins/blog/templates/author.html +0 -9
  230. package/plugins/blog/templates/category.html +0 -9
  231. package/plugins/blog/templates/tag.html +0 -9
  232. package/plugins/contacts/admin/templates/contacts.html +0 -126
  233. package/plugins/contacts/admin/views/contacts.js +0 -710
  234. package/plugins/contacts/collections/user-contact-groups/schema.json +0 -35
  235. package/plugins/contacts/collections/user-contacts/schema.json +0 -71
  236. package/plugins/contacts/config.js +0 -6
  237. package/plugins/contacts/data/contacts.json +0 -20
  238. package/plugins/contacts/plugin.js +0 -291
  239. package/plugins/contacts/plugin.json +0 -33
  240. package/plugins/demo-viewer/config.js +0 -4
  241. package/plugins/demo-viewer/plugin.js +0 -36
  242. package/plugins/demo-viewer/plugin.json +0 -9
  243. package/plugins/invoice/admin/templates/editor.html +0 -129
  244. package/plugins/invoice/admin/templates/index.html +0 -43
  245. package/plugins/invoice/admin/templates/issuers.html +0 -5
  246. package/plugins/invoice/admin/templates/receivers.html +0 -5
  247. package/plugins/invoice/admin/views/editor.js +0 -267
  248. package/plugins/invoice/admin/views/index.js +0 -155
  249. package/plugins/invoice/admin/views/issuers.js +0 -23
  250. package/plugins/invoice/admin/views/party-view.js +0 -148
  251. package/plugins/invoice/admin/views/receivers.js +0 -22
  252. package/plugins/invoice/collections/invoice-issuers/schema.json +0 -16
  253. package/plugins/invoice/collections/invoice-receivers/schema.json +0 -15
  254. package/plugins/invoice/collections/invoices/schema.json +0 -27
  255. package/plugins/invoice/config.js +0 -16
  256. package/plugins/invoice/plugin.js +0 -283
  257. package/plugins/invoice/plugin.json +0 -85
  258. package/plugins/invoice/templates/invoice-print.html +0 -213
  259. package/plugins/notes/admin/templates/notes.html +0 -83
  260. package/plugins/notes/admin/views/notes.js +0 -281
  261. package/plugins/notes/collections/user-notes/schema.json +0 -54
  262. package/plugins/notes/config.js +0 -6
  263. package/plugins/notes/data/notes.json +0 -1
  264. package/plugins/notes/plugin.json +0 -33
  265. package/plugins/site-search/admin/views/site-search.js +0 -116
  266. package/plugins/site-search/config.js +0 -15
  267. package/plugins/site-search/plugin.js +0 -188
  268. package/plugins/site-search/plugin.json +0 -40
  269. package/plugins/site-search/public/inject-body.html +0 -17
  270. package/plugins/site-search/public/inject-head.html +0 -1
  271. package/plugins/site-search/public/search.css +0 -1
  272. package/plugins/site-search/public/search.js +0 -1
  273. package/plugins/surveys/admin/templates/audience.html +0 -47
  274. package/plugins/surveys/admin/templates/results.html +0 -58
  275. package/plugins/surveys/admin/templates/survey-editor.html +0 -141
  276. package/plugins/surveys/admin/templates/surveys.html +0 -25
  277. package/plugins/surveys/admin/views/audience.js +0 -301
  278. package/plugins/surveys/admin/views/results.js +0 -172
  279. package/plugins/surveys/admin/views/survey-editor.js +0 -211
  280. package/plugins/surveys/admin/views/surveys.js +0 -161
  281. package/plugins/surveys/collections/survey-contacts/schema.json +0 -13
  282. package/plugins/surveys/collections/survey-groups/schema.json +0 -11
  283. package/plugins/surveys/collections/survey-invites/schema.json +0 -16
  284. package/plugins/surveys/collections/surveys/schema.json +0 -23
  285. package/plugins/surveys/config.js +0 -8
  286. package/plugins/surveys/plugin.js +0 -174
  287. package/plugins/surveys/plugin.json +0 -65
  288. package/plugins/surveys/public/dist-shared.mjs +0 -1
  289. package/plugins/surveys/public/survey.css +0 -1
  290. package/plugins/surveys/public/survey.mjs +0 -1
  291. package/plugins/surveys/templates/survey-page.html +0 -63
  292. package/plugins/theme-switcher/admin/templates/theme-switcher.html +0 -86
  293. package/plugins/theme-switcher/admin/views/theme-switcher.js +0 -65
  294. package/plugins/theme-switcher/config.js +0 -10
  295. package/plugins/theme-switcher/plugin.js +0 -26
  296. package/plugins/theme-switcher/plugin.json +0 -74
  297. package/plugins/theme-switcher/public/inject-body.html +0 -153
  298. package/plugins/theme-switcher/public/inject-head.html +0 -260
  299. package/plugins/todo/admin/templates/todo.html +0 -158
  300. package/plugins/todo/admin/views/todo.js +0 -343
  301. package/plugins/todo/collections/todos/schema.json +0 -60
  302. package/plugins/todo/config.js +0 -7
  303. package/plugins/todo/data/todos.json +0 -1
  304. package/plugins/todo/plugin.js +0 -102
  305. package/plugins/todo/plugin.json +0 -33
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 m="/api";function d(){return S.get("auth_token")}function p(){return S.get("auth_refresh_token")}function y(e){S.set("auth_token",e)}function u(){S.remove("auth_token"),S.remove("auth_refresh_token"),S.remove("auth_user")}async function f(){const e=p();if(!e)throw new Error("No refresh token");const o=await fetch(`${m}/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(!o.ok)throw u(),R.navigate("/login"),new Error("Token refresh failed");const{token:n}=await o.json();return y(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(`${m}${e}`,{...o,headers:r(n)});if(s.status===401&&p())try{n=await f(),s=await fetch(`${m}${e}`,{...o,headers:r(n)})}catch{return}if(!s.ok){const i=await s.json().catch(()=>({error:"Request failed"})),a=new Error(i.error||i.message||`HTTP ${s.status}`);throw Array.isArray(i.validation)&&(a.validation=i.validation),a.status=s.status,a}return s.status===204?null:s.json()}async function g(e,o){const n=d(),r=n?{Authorization:`Bearer ${n}`}:{},s=await fetch(`${m}${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=>g("/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"}),async exportBundle(e,{includeData:o=!1}={}){const n=d(),r=o?"?includeData=1":"",s=await fetch(`${m}/plugins/${encodeURIComponent(e)}/export${r}`,{headers:n?{Authorization:`Bearer ${n}`}:{}});if(!s.ok){const T=await s.json().catch(()=>({}));throw new Error(T.error||`Export failed (${s.status})`)}const i=s.headers.get("Content-Disposition")||"",a=/filename="([^"]+)"/.exec(i)?.[1],c=await s.blob(),h=URL.createObjectURL(c),l=document.createElement("a");l.href=h,l.download=a||`${e}.dcmsplugin`,document.body.appendChild(l),l.click(),l.remove(),URL.revokeObjectURL(h)},async installFile(e,{confirmUnsigned:o=!1,confirmUpgrade:n=!1,confirmDowngrade:r=!1}={}){const s=new FormData;o&&s.append("confirmUnsigned","true"),n&&s.append("confirmUpgrade","true"),r&&s.append("confirmDowngrade","true"),s.append("file",e);const i=d(),a=await fetch(`${m}/plugins/install-upload`,{method:"POST",headers:i?{Authorization:`Bearer ${i}`}:{},body:s}),c=await a.json().catch(()=>({}));if(a.status===409){const h=new Error(c.error||"Plugin already installed");throw h.code="CONFLICT",h.name=c.name,h.installedVersion=c.installedVersion,h.incomingVersion=c.incomingVersion,h}if(!a.ok)throw new Error(c.error||`Install failed (${a.status})`);return c}},marketplace:{catalogue:()=>t("/plugins/marketplace",{method:"GET"}),install:(e,o,{update:n=!1}={})=>t("/plugins/marketplace/install",{method:"POST",body:JSON.stringify({slug:e,version:o,update:n})}),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(`${m}/blocks/${encodeURIComponent(e)}/export`,{headers:o?{Authorization:`Bearer ${o}`}:{}});if(!n.ok){const a=await n.json().catch(()=>({}));throw new Error(a.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(`${m}/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(`${m}/components/${encodeURIComponent(e)}/export`,{headers:o?{Authorization:`Bearer ${o}`}:{}});if(!n.ok){const a=await n.json().catch(()=>({}));throw new Error(a.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(`${m}/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&&y(e),o&&S.set("auth_refresh_token",o),n&&S.set("auth_user",n)}export function logout(){const e=p();e&&fetch(`${m}/auth/logout`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})}).catch(()=>{}),u(),R.navigate("/login")}export{t as apiRequest,f as refreshAccessToken,d as getToken,u 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(`
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>
1
+ import{views as B}from"./views/index.js";import{withPluginChrome as A}from"./lib/plugin-chrome.js";import{api as c,getUser as m,isAuthenticated as p,logout as W}from"./api.js";import{installHttpInterceptor as V}from"./http-interceptor.js";import{installResizableSlideovers as O}from"./lib/slideover-resizable.js";$(()=>{V(),O(),(async()=>{try{const t=p()?await c.theme.get():null;Domma.theme.init({theme:t?.adminTheme||"charcoal-dark",persist:!0})}catch{Domma.theme.init({theme:"charcoal-dark",persist:!0})}})();const k=["jb-company","jb-agent","jb-candidate"],N=["/job-board","/my-profile"];function w(t){return t&&k.includes(t.role)}function g(t){return N.some(e=>t===e||t.startsWith(e+"/"))}R.use(async(t,e,o)=>{if(t.path==="/login"||t.path==="/reset-password")return o();if(!p()){R.navigate("/login");return}if(w(m())&&!g(t.path)){R.navigate("/job-board");return}o()});async function C(){try{return(await c.get("/auth/permissions")).permissions||[]}catch{return[]}}async function D(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")&&r&&(clearInterval(r),r=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:"/notes",view:"notes",title:"Notes - Domma CMS"},{path:"/todo",view:"todo",title:"Todo - Domma CMS"},{path:"/analytics",view:"analytics",title:"Analytics - Domma CMS"},{path:"/contacts",view:"contacts",title:"Contacts - 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(w(m())&&!g(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 C();D(o);try{const i=await c.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 l=d(a.data?.title||""),u=d((a.data?.body||"").slice(0,120));E.toast(`${l} - ${u}`,{type:a.data?.severity==="critical"?"error":"warning",duration:0})}}catch{}}y()}}),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 L(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||!L(e))return;const i=new URLSearchParams(location.hash.split("?")[1]||"").get("project");if(!i||!/\/new$/.test(e))return;let s=0;const a=60,l=1500,u=()=>{s+=a;for(const x of U){const h=document.querySelector(x);if(!h)continue;if(Array.from(h.options||[]).find(_=>_.value===i)){h.value=i,h.dispatchEvent(new Event("change",{bubbles:!0}));return}}s<l&&setTimeout(u,a)};setTimeout(u,a)});const b="cms_card_states",v=S.get(b)||{};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&&v[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&&(v[o]=e.hasClass("card-collapsed")?"collapsed":"open",S.set(b,v))}),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 n={...B},T={notes:{displayName:"Notes",icon:"edit-3"},todo:{displayName:"Todo",icon:"check-square"},analytics:{displayName:"Analytics",icon:"chart-bar"},contacts:{displayName:"Contacts",icon:"users"}};for(const[t,e]of Object.entries(T))n[t]&&(n[t]=A(n[t],{plugin:t,licence:"Built in",...e}));const f=[...I];async function P(){if(p())try{const t=await c.plugins.adminConfig();t.routes?.length&&f.push(...t.routes);for(const[e,o]of Object.entries(t.views||{}))try{const i=await import(`/plugins/${o.entry}`);n[e]=A(i[o.exportName],(t.meta||{})[e])}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 y(){const t=m();if(!t||$("#topbar-user-name").length)return;const o={"super-admin":"Super Admin",admin:"Admin",user:"User"}[t.role]||t.role;$("#topbar-user").html(`
2
+ <span id="topbar-user-name" class="topbar-user-name">${d(t.name)}</span>
3
+ <span class="topbar-role-badge topbar-role-badge--${d(t.role)}">${o}</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"}),j(),r||(r=setInterval(j,6e4))}function d(t){return String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}let r=null;async function j(){if(p())try{const{count:t}=await c.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(!p()&&!window.location.hash.startsWith("#/reset-password"))window.location.hash="#/login";else{const i=(window.location.hash||"#/").slice(1)||"/";if(w(m())&&!g(i)&&(window.location.hash="#/job-board"),await P(),m()){const a=await C();await D(a),y()}}R.init({container:"#view-container",routes:f,views:n,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 l=i.slice(0,-2);return s.startsWith(l+"/")?{}:null}return t(i,s)};const e=(window.location.hash||"#/").slice(1)||"/";f.filter(i=>i.path.endsWith("/*")).some(i=>e.startsWith(i.path.slice(0,-2)+"/"))&&R._handleRouteChange()})()});
@@ -0,0 +1 @@
1
+ export const RANGES=["today","7d","30d","90d","all"],RANGE_LABEL={today:"Today","7d":"Last 7 days","30d":"Last 30 days","90d":"Last 90 days",all:"All time"},LIMITS=[10,25,50,0],CHARTS=["line","bar"],DEFAULT_SETTINGS=Object.freeze({range:"7d",limit:25,chart:"line",live:!0,confirmReset:!0});export function normaliseSettings(o){const e=o&&typeof o=="object"?o:{},t=DEFAULT_SETTINGS,i=Number(e.limit);return{range:RANGES.includes(e.range)?e.range:t.range,limit:LIMITS.includes(i)?i:t.limit,chart:CHARTS.includes(e.chart)?e.chart:t.chart,live:typeof e.live=="boolean"?e.live:t.live,confirmReset:typeof e.confirmReset=="boolean"?e.confirmReset:t.confirmReset}}export function topRows(o,{filter:e="",limit:t=25,total:i=0}={}){const c=String(e).trim().toLowerCase(),n=(o||[]).filter(s=>!c||String(s.url).toLowerCase().includes(c)),r=t>0?n.slice(0,t):n,u=r.reduce((s,a)=>Math.max(s,a.hits),0)||1,l=i||n.reduce((s,a)=>s+a.hits,0)||1;return{matched:n.length,rows:r.map((s,a)=>({key:s.url,rank:a+1,url:s.url,hits:s.hits,hitsText:s.hits.toLocaleString("en-GB"),share:`${(s.hits/l*100).toFixed(s.hits/l<.1?1:0)}%`,bar:`${Math.max(2,Math.round(s.hits/u*100))}%`}))}}export function journeyRows(o){const e=(t,i)=>{const c=(t||[]).reduce((n,r)=>Math.max(n,r.count),0)||1;return(t||[]).map((n,r)=>({key:`${r}:${i(n)}`,url:n.url??n.from,label:i(n),count:n.count,bar:`${Math.max(2,Math.round(n.count/c*100))}%`}))};return{entry:e(o?.entry,t=>t.url),exit:e(o?.exit,t=>t.url),paths:e(o?.paths,t=>`${t.from} \u2192 ${t.to}`)}}export function kpis(o,e){const t=i=>Number.isFinite(i)?i.toLocaleString("en-GB"):"-";return{hits:t(o?.total),pages:t(o?.uniquePages),lifetime:t(o?.lifetimeTotal),sessions:t(e?.totalSessions),bounce:e&&e.totalSessions?`${Math.round(e.bounceRate*100)}%`:"-"}}export const seriesRange=o=>o==="all"?"30d":o;
@@ -0,0 +1 @@
1
+ export const SORTS=["name","company","added","updated"],DENSITIES=["comfortable","compact"],COLUMNS=["company","email","phone","groups"],DEFAULT_SETTINGS=Object.freeze({sort:"name",favouritesFirst:!0,density:"comfortable",confirmDelete:!0,showCompany:!0,showEmail:!0,showPhone:!0,showGroups:!0});const f=(r,e,t)=>e.includes(r)?r:t,a=(r,e)=>typeof r=="boolean"?r:e;export function normaliseSettings(r){const e=r&&typeof r=="object"?r:{},t=DEFAULT_SETTINGS;return{sort:f(e.sort,SORTS,t.sort),favouritesFirst:a(e.favouritesFirst,t.favouritesFirst),density:f(e.density,DENSITIES,t.density),confirmDelete:a(e.confirmDelete,t.confirmDelete),showCompany:a(e.showCompany,t.showCompany),showEmail:a(e.showEmail,t.showEmail),showPhone:a(e.showPhone,t.showPhone),showGroups:a(e.showGroups,t.showGroups)}}export function primaryEmail(r){if(r?.email)return String(r.email);const e=(Array.isArray(r?.emails)?r.emails:[]).find(t=>t?.address);return e?String(e.address):""}export function primaryPhone(r){if(r?.phone)return String(r.phone);const e=(Array.isArray(r?.phones)?r.phones:[]).find(t=>t?.number);return e?String(e.number):""}export function initials(r){const e=String(r??"").trim().split(/\s+/).filter(Boolean);return e.length?e.length===1?e[0].slice(0,2).toUpperCase():(e[0][0]+e[e.length-1][0]).toUpperCase():"?"}function h(r){const e=[...Array.isArray(r.emails)?r.emails.map(t=>t?.address):[],...Array.isArray(r.phones)?r.phones.map(t=>t?.number):[]];return[r.name,r.email,r.phone,r.company,r.jobTitle,r.notes,...r.groups||[],...e].filter(Boolean).join(" ").toLowerCase()}const l=(r,e)=>String(r.name??"").localeCompare(String(e.name??""),void 0,{sensitivity:"base"});export function arrangeContacts(r,{query:e="",filter:t={kind:"all"},sort:s="name",favouritesFirst:i=!0}={}){const p=String(e??"").trim().toLowerCase(),m=p?p.split(/\s+/):[],d=(r||[]).filter(o=>{if(t?.kind==="fav"&&o.favourite!==!0||t?.kind==="group"&&!(Array.isArray(o.groups)&&o.groups.includes(t.value)))return!1;if(!m.length)return!0;const n=h(o);return m.every(u=>n.includes(u))}),y={name:l,company:(o,n)=>{const u=String(o.company??""),c=String(n.company??"");return!u!=!c?u?-1:1:u.localeCompare(c,void 0,{sensitivity:"base"})||l(o,n)},added:(o,n)=>String(n.createdAt??"").localeCompare(String(o.createdAt??""))||l(o,n),updated:(o,n)=>String(n.updatedAt??"").localeCompare(String(o.updatedAt??""))||l(o,n)}[f(s,SORTS,"name")];return[...d].sort((o,n)=>i&&o.favourite===!0!=(n.favourite===!0)?o.favourite===!0?-1:1:y(o,n))}export function countContacts(r,e=[]){const t={all:0,fav:0,groups:Object.fromEntries(e.map(s=>[s,0]))};for(const s of r||[]){t.all++,s.favourite===!0&&t.fav++;for(const i of Array.isArray(s.groups)?s.groups:[])t.groups[i]=(t.groups[i]??0)+1}return t}export function withGroup(r,e,t){const s=Array.isArray(r)?r.filter(i=>i!==e):[];return t?[...s,e]:s}export function duplicateBody(r){const{id:e,userId:t,createdAt:s,updatedAt:i,...p}=r||{};return{...JSON.parse(JSON.stringify(p)),name:`${r?.name||"Contact"} (copy)`,favourite:!1}}
@@ -0,0 +1 @@
1
+ let n=null,m=!1;function s(){n&&(n.btn.setAttribute("aria-expanded","false"),n.el.remove(),n=null)}function g(){m||(m=!0,document.addEventListener("mousedown",t=>{n&&(n.el.contains(t.target)||n.btn.contains(t.target)||s())},!0),document.addEventListener("keydown",t=>{if(t.key==="Escape"&&n){const c=n.btn;s(),c.focus()}}),window.addEventListener("hashchange",s),window.addEventListener("resize",s),document.addEventListener("scroll",t=>{n&&!n.el.contains(t.target)&&s()},!0))}function y(t,c,l){const o=c.getBoundingClientRect(),d=t.offsetWidth,r=t.offsetHeight,a=8,p=l==="bottom"&&o.bottom+r+a<innerHeight||o.top<r+a,e=p?o.bottom+a:o.top-r-a,u=Math.min(Math.max(8,o.left+o.width/2-d/2),innerWidth-d-8);t.style.top=`${Math.round(e)}px`,t.style.left=`${Math.round(u)}px`,t.dataset.side=p?"bottom":"top"}let A=0;export function helpPopovers(t,{position:c="top"}={}){const l=t&&typeof t.get=="function"?t.get(0):t;if(!l)return()=>{};g();const o=[];for(const d of l.querySelectorAll("[data-help]:not([data-help-ready])")){const r=d.getAttribute("data-help")||"",a=d.getAttribute("data-help-title")||"";d.setAttribute("data-help-ready","");const p=`dm-help-${++A}`,e=document.createElement("button");e.type="button",e.className="dm-help-btn",e.setAttribute("aria-label",a?`Help: ${a}`:"Help"),e.setAttribute("aria-expanded","false"),e.setAttribute("aria-controls",p);const u=document.createElement("span");u.setAttribute("data-icon","help-circle"),u.setAttribute("data-icon-size","14"),e.appendChild(u),e.addEventListener("click",f=>{if(f.preventDefault(),f.stopPropagation(),n?.btn===e)return s();s();const i=document.createElement("div");if(i.id=p,i.className="dm-help-pop",i.setAttribute("role","dialog"),a){const b=document.createElement("strong");b.textContent=a,i.appendChild(b)}const h=document.createElement("p");h.textContent=r,i.appendChild(h),document.body.appendChild(i),y(i,e,c),e.setAttribute("aria-expanded","true"),n={btn:e,el:i}}),d.appendChild(e),o.push(e)}return typeof I<"u"&&I.scan&&I.scan(l),()=>{n&&o.includes(n.btn)&&s()}}
@@ -0,0 +1 @@
1
+ export const DEFAULT_SETTINGS=Object.freeze({layout:"cards",sort:"updated",previewLength:160,showCategories:!0,confirmDelete:!0,defaultCategories:[]});const p=new Set(["cards","list"]),f=new Set(["updated","created","title"]);export const PREVIEW_MIN=0,PREVIEW_MAX=600;export function normaliseSettings(r){const e=r&&typeof r=="object"?r:{},t=DEFAULT_SETTINGS,o=Number(e.previewLength);return{layout:p.has(e.layout)?e.layout:t.layout,sort:f.has(e.sort)?e.sort:t.sort,previewLength:Number.isFinite(o)?Math.min(PREVIEW_MAX,Math.max(PREVIEW_MIN,Math.round(o))):t.previewLength,showCategories:typeof e.showCategories=="boolean"?e.showCategories:t.showCategories,confirmDelete:typeof e.confirmDelete=="boolean"?e.confirmDelete:t.confirmDelete,defaultCategories:parseCategories(e.defaultCategories)}}export function parseCategories(r){const e=Array.isArray(r)?r:String(r??"").split(","),t=new Set,o=[];for(const i of e){const a=String(i??"").trim();!a||t.has(a.toLowerCase())||(t.add(a.toLowerCase()),o.push(a))}return o}export function collectCategories(r){return parseCategories((r||[]).flatMap(t=>Array.isArray(t.categories)?t.categories:[])).sort((t,o)=>t.localeCompare(o,void 0,{sensitivity:"base"}))}export function preview(r,e){const t=String(r??"").replace(/\s+/g," ").trim();if(!e||e<=0)return"";if(t.length<=e)return t;const o=t.slice(0,e),i=o.lastIndexOf(" ");return(i>e*.6?o.slice(0,i):o).trimEnd()+"\u2026"}const c=r=>{const e=Date.parse(r);return Number.isNaN(e)?0:e};export function arrangeNotes(r,{query:e="",category:t="",sort:o="updated"}={}){const i=String(e).trim().toLowerCase(),a=String(t).trim().toLowerCase(),l=(r||[]).filter(s=>{const n=Array.isArray(s.categories)?s.categories:[];return a&&!n.some(u=>String(u).toLowerCase()===a)?!1:i?String(s.title??"").toLowerCase().includes(i)||String(s.content??"").toLowerCase().includes(i)||n.some(u=>String(u).toLowerCase().includes(i)):!0}),d=o==="title"?(s,n)=>String(s.title||"").localeCompare(String(n.title||""),void 0,{sensitivity:"base"}):o==="created"?(s,n)=>c(n.createdAt)-c(s.createdAt):(s,n)=>c(n.updatedAt)-c(s.updatedAt);return[...l].sort(d)}
@@ -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
+ function r(e){let t=0;for(let n=0;n<e.length;n++)t=(t*31+e.charCodeAt(n))%360;return t}function a(e){return e==="_template"?0:62}export function pluginAccent(e){const t=String(e||"");return`hsl(${r(t)} ${a(t)}% 52%)`}export function pluginWatermark(e){const t=document.createElement("span");return t.className="plugin-watermark",t.setAttribute("data-icon",e||"package"),t.setAttribute("data-icon-size","148"),t.setAttribute("aria-hidden","true"),t}
@@ -0,0 +1 @@
1
+ const m=/^(built in|included\b)/i;export function formatReleased(e){if(!e||typeof e!="string"||!/^\d{4}-\d{2}-\d{2}/.test(e))return"";const t=new Date(e);if(Number.isNaN(t.getTime()))return"";const n=a=>String(a).padStart(2,"0");return`${n(t.getDate())}.${n(t.getMonth()+1)}.${t.getFullYear()}`}export function expiryFact(e,t=new Date){if(!e||typeof e!="object")return null;const n=formatReleased(e.date);if(!n)return null;const a=e.kind==="rental",i=e.state==="support-ended"||e.state==="expired"||!a&&Date.parse(e.date)<t.getTime(),s=Date.parse(e.date)-t.getTime()<30*864e5;let o;return a?o=e.state==="grace"?`Expired ${n} (grace)`:`Expires ${n}`:o=i?`Support ended ${n}`:`Support to ${n}`,{text:o,title:a?"Licence expiry":"Support and updates expiry - the licence itself does not expire",tone:i||e.state==="grace"?a?"danger":"warning":s?"warning":"ok"}}export function buildBanner(e){const t=e||{},n=document.createElement("header");n.className="dm-plugin-banner";const a=document.createElement("div");a.className="dm-plugin-banner-identity";const i=document.createElement("span");i.className="dm-plugin-banner-icon",i.setAttribute("data-icon",t.icon||"package"),i.setAttribute("data-icon-size","18"),a.appendChild(i);const s=document.createElement("h1");s.className="dm-plugin-banner-name",s.textContent=t.displayName||t.plugin||"Plugin",a.appendChild(s),n.appendChild(a);const o=document.createElement("div");o.className="dm-plugin-banner-facts",((c,d)=>{if(!c)return;const p=document.createElement("span");p.className="dm-plugin-fact"+(d?" "+d:""),p.textContent=c,o.appendChild(p)})(t.version?`v${t.version}`:"","dm-plugin-fact--version");const r=formatReleased(t.date);if(r){const c=document.createElement("span");c.className="dm-plugin-fact dm-plugin-fact--date",c.title="Release date",c.textContent=r,o.appendChild(c)}if(t.licence){const c=document.createElement("span");c.className="dm-plugin-fact dm-plugin-fact--licence"+(m.test(t.licence)?" is-included":" is-licensed"),c.title="Licence",c.textContent=t.licence,o.appendChild(c)}const l=expiryFact(t.expiry);if(l){const c=document.createElement("span");c.className=`dm-plugin-fact dm-plugin-fact--expiry is-${l.tone}`,c.title=l.title,c.textContent=l.text,o.appendChild(c)}return n.appendChild(o),n}export function withPluginChrome(e,t){if(!e||typeof e!="object")return e;const n=Object.create(Object.getPrototypeOf(e));Object.assign(n,e);const a=typeof e.onMount=="function"?e.onMount:null;return n.onMount=async function(...i){const s=i[0],o=s&&typeof s.get=="function"?s.get(0):s;return o&&o.nodeType===1&&!o.querySelector(":scope > .dm-plugin-shell")&&applyChrome(o,t),a?a.apply(e,i):void 0},n}export function applyChrome(e,t){const n=document.createElement("section");n.className="dm-plugin-shell",t&&t.plugin&&n.setAttribute("data-plugin",t.plugin),n.appendChild(buildBanner(t));const a=document.createElement("div");for(a.className="dm-plugin-body";e.firstChild;)a.appendChild(e.firstChild);n.appendChild(a),e.appendChild(n),h(a,t),g(a),typeof I<"u"&&I.scan?I.scan(n):typeof Domma<"u"&&Domma.icons&&Domma.icons.scan()}function f(e){return[...e.children].filter(t=>!/^(STYLE|SCRIPT|TEMPLATE|LINK)$/.test(t.tagName))}function g(e){let t=e;for(let n=0;n<2;n++){const a=f(t);if(a.length!==1)return;if(t=a[0],t.classList.contains("card")){t.classList.add("dm-plugin-unwrap");return}}}function h(e,t){const n=String(t&&t.displayName||"").trim().toLowerCase(),a=e.querySelector(":scope > .view-header, :scope > * > .view-header");if(a){const i=a.querySelector("h1, h2"),s=a.querySelector("button, a, input, select");i&&s?i.remove():i&&a.remove();return}if(n){for(const i of e.querySelectorAll("h1, h2"))if(i.textContent.trim().toLowerCase()===n){i.remove();return}}}export function addBannerAction(e,{icon:t,title:n,onClick:a,id:i="",label:s=""}){const o=e&&typeof e.get=="function"?e.get(0):e;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 r=u.querySelector(":scope > .dm-plugin-banner-facts");r||(r=document.createElement("div"),r.className="dm-plugin-banner-facts",u.appendChild(r));let l=r.querySelector(":scope > .dm-plugin-banner-actions");l||(l=document.createElement("div"),l.className="dm-plugin-banner-actions",r.insertBefore(l,r.firstChild)),i&&l.querySelector(`[data-banner-action="${CSS.escape(i)}"]`)?.remove();const c=document.createElement("button");c.type="button",c.className="btn btn-sm btn-ghost dm-plugin-banner-action",c.title=n||"",c.setAttribute("aria-label",n||s||"Action"),i&&(c.dataset.bannerAction=i);const d=document.createElement("span");return d.setAttribute("data-icon",t||"settings"),d.setAttribute("data-icon-size","16"),c.appendChild(d),s&&c.appendChild(document.createTextNode(` ${s}`)),c.addEventListener("click",a),l.appendChild(c),typeof I<"u"&&I.scan?I.scan(c):typeof Domma<"u"&&Domma.icons&&Domma.icons.scan(c),c}
@@ -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:"Marketplace",url:MANAGE_PLUGINS_URL,icon:"store",permission:"plugins"};export function stripItemByUrl(n,o){const t=[];for(const s of n||[]){if(!s||s.url===o)continue;const e={...s};Array.isArray(s.items)&&s.items.length&&(e.items=stripItemByUrl(s.items,o)),t.push(e)}return t}export function groupPluginItems(n){const o=[],t=[];for(const e of n||[])if(!(!e||!e.item)){if(e.parent){o.push(e);continue}t.push(e.item)}return{toolsFolder:t.length?{text:TOOLS_FOLDER_TEXT,icon:"tool",items:t}:null,managePluginsItem:{...MANAGE_PLUGINS_ITEM},parented:o}}export function insertFoldersBeforeSystem(n,o){const t=(o||[]).filter(Boolean);if(!t.length)return n;const s=["system","documentation"],e=n.findIndex(r=>s.includes(String(r&&r.text||"").toLowerCase()));return e===-1?[...n,...t]:[...n.slice(0,e),...t,...n.slice(e)]}export function pruneEmptySynthesisedFolders(n){const o=new Set([TOOLS_FOLDER_TEXT]);return(n||[]).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 o}from"node:test";import s from"node:assert/strict";import{groupPluginItems as a,stripItemByUrl as p,insertFoldersBeforeSystem as u,pruneEmptySynthesisedFolders as i,TOOLS_FOLDER_TEXT as m,MANAGE_PLUGINS_URL as d}from"./sidebar-grouping.js";const n=(e,t=null)=>({parent:t,item:e});o("groupPluginItems routes every enabled item to Tools, whatever its core flag",()=>{const{toolsFolder:e}=a([n({text:"Analytics",url:"#/plugins/analytics",core:!0}),n({text:"Todo",url:"#/plugins/todo",core:!1}),n({text:"Notes",url:"#/plugins/notes"})]);s.equal(e.text,m),s.deepEqual(e.items.map(t=>t.text),["Analytics","Todo","Notes"])}),o("Marketplace is a standalone entry, not a folder",()=>{const{managePluginsItem:e}=a([n({text:"Todo",url:"#/plugins/todo"})]);s.equal(e.text,"Marketplace"),s.equal(e.url,d),s.equal(e.permission,"plugins"),s.ok(!("items"in e),"it must not carry a sub-tree")}),o("groupPluginItems omits the Tools folder only when nothing is enabled",()=>{const e=a([n({text:"Todo",url:"#/plugins/todo"})]);s.deepEqual(e.toolsFolder.items.map(l=>l.text),["Todo"]);const t=a([]);s.equal(t.toolsFolder,null),s.equal(t.managePluginsItem.text,"Marketplace")}),o("groupPluginItems keeps explicitly-parented items separate",()=>{const{parented:e,toolsFolder:t}=a([n({text:"Nested",url:"#/x"},"Content"),n({text:"Todo",url:"#/plugins/todo",core:!1})]);s.equal(e.length,1),s.equal(e[0].parent,"Content"),s.deepEqual(t.items.map(l=>l.text),["Todo"])}),o("each call returns its own Marketplace object",()=>{const e=a([]).managePluginsItem,t=a([]).managePluginsItem;s.notEqual(e,t),s.deepEqual(e,t)}),o("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"}]}],l=p(e,"#/plugins").find(r=>r.text==="System");s.deepEqual(l.items.map(r=>r.text),["Users"]),s.equal(e.find(r=>r.text==="System").items.length,2)}),o("insertFoldersBeforeSystem places Tools then Marketplace before System",()=>{const t=u([{text:"Overview"},{text:"Data"},{text:"System"},{text:"Documentation"}],[{text:"Tools",items:[]},{text:"Marketplace",url:"#/plugins"},null]);s.deepEqual(t.map(l=>l.text),["Overview","Data","Tools","Marketplace","System","Documentation"])}),o("insertFoldersBeforeSystem appends when no System/Documentation anchor",()=>{const t=u([{text:"Overview"},{text:"Data"}],[{text:"Marketplace",url:"#/plugins"}]);s.deepEqual(t.map(l=>l.text),["Overview","Data","Marketplace"])}),o("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"])}),o("pruneEmptySynthesisedFolders leaves the standalone Marketplace alone",()=>{const e=[{text:"Tools",items:[{text:"Analytics"}]},{text:"Marketplace",url:"#/plugins"}];s.deepEqual(i(e).map(t=>t.text),["Tools","Marketplace"])});
@@ -1,4 +1,4 @@
1
- import{api as u}from"../api.js";import{colourToCss as I,applyFloat as K,applyHighlight as G,itemHighlightAttrs as O}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 f(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 N(e,a){return a?!e||!e.length?!1:e.includes(a)?!0:["read","create","update","delete"].some(t=>e.includes(`${a}.${t}`)):!0}function T(e,a){const t=[];for(const n of e){if(n.hidden||n.permission&&!N(a,n.permission))continue;const i=Array.isArray(n.items)&&n.items.length?T(n.items,a):[];t.push({...n,items:i})}return t}function te(e,a){if(!a||!a.length)return e;const t=(n,i)=>{for(const m of n){if((m.text||"").toLowerCase()===i.toLowerCase())return m;if(Array.isArray(m.items)){const r=t(m.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 ne(e){if(!N(e,"projects"))return[];let a=[];try{a=await u.projects.list()}catch{return[]}const t={};await Promise.all(a.map(async i=>{try{const m=await u.projects.artefacts(i.slug);t[i.slug]=m}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),l=t[i.slug]||{},p=[{text:"Overview",url:r,icon:i.icon||"folder"}];for(const h of n){const g=l[h.key],c=Array.isArray(g)?g.length:0;c<=0||p.push({text:`${h.text} (${c})`,url:r+h.path,icon:h.icon})}p.push({text:"Settings",url:r+"/settings",icon:"settings"});const b=i.enabled===!1;return{text:i.name||i.slug,icon:i.icon||"folder",items:p,...b&&{colour:"neutral",badge:{text:"off"}}}})}function se(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 ie(e,a){if(!e.url||!a)return"";const t=a[e.url];return t==null||t<=0?"":`<span class="sidebar-badge">${f(String(t))}</span>`}function ae(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 U(e,a){if(e&&e.type==="separator")return'<hr class="sidebar-divider">';if(e&&e.type==="spacer"){const c=["xs","sm","md","lg","xl","flex"].includes(e.size)?e.size:"md";return`<div class="sidebar-spacer sidebar-spacer--${f(c)}" aria-hidden="true"></div>`}const t=e.icon?`<span data-icon="${f(e.icon)}"></span>`:"",n=f(e.text||""),i=e.url?f(e.url):"",m=Array.isArray(e.items)&&e.items.length>0,r=e.colour?I(e.colour):"",l=O(e),p=[l.hover&&` data-menu-item-hover="${f(l.hover)}"`,l.active&&` data-menu-item-active="${f(l.active)}"`].filter(Boolean).join(""),b=[r&&`color:${f(r)}`,l.accent&&`--dm-menu-highlight:${f(l.accent)}`].filter(Boolean).join(";"),h=b?` style="${b}"`:"";let g="";if(e.badge&&e.badge.text!=null&&e.badge.text!==""){const c=e.badge.variant?I(e.badge.variant):"";g=`<span class="dm-menu-badge"${c?` style="background:${f(c)};color:var(--dm-text-inverse,#fff)"`:""}>${f(String(e.badge.text))}</span>`}if(m){const c=ae(e),y=S.get(c)!==!1,j=e.items.map(E=>U(E,a)).join("");return`<details data-state-key="${f(c)}"${y?" open":""}>
2
- <summary${h}>${t} <span class="sidebar-text">${n}</span>${g}</summary>
3
- <div class="sidebar-children">${j}</div>
4
- </details>`}return`<a href="${i}" class="sidebar-link" data-url="${i}"${h}${p}>${t} <span class="sidebar-text">${n}</span>${ie(e,a)}${g}</a>`}async function re(){const e=c=>c.then(y=>Array.isArray(y)?y.length:Array.isArray(y?.entries)?y.entries.length:0).catch(()=>0),[a,t,n,i,m,r,l,p,b,h,g]=await Promise.all([e(u.pages.list()),e(u.media.list()),e(u.collections.list()),e(u.forms.list()),e(u.views.list()),e(u.actions.list()),e(u.blocks.list()),e(u.components.list()),e(u.users.list()),e(u.plugins.list()),u.system?.notifications?.unreadCount?.().then(c=>c?.count??0).catch(()=>0)??Promise.resolve(0)]);return{"#/pages":a,"#/media":t,"#/collections":n,"#/forms":i,"#/views":m,"#/actions":r,"#/blocks":l,"#/components":p,"#/users":b,"#/plugins":h,"#/system/notifications":g}}async function oe(){try{const e=await u.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,m=null,r=null,l=null,p=null;try{const s=await u.menus.get("admin-sidebar");n=Array.isArray(s?.items)?s.items:null,i=s?.variant||null,m=s?.position||null,r=s?.style||null,l=s?.float||null,p=s?.highlight||null}catch{n=null}(!n||!n.length)&&(console.warn("[admin-sidebar] No admin-sidebar menu found; using fallback tree"),n=ee.slice());let b=[];try{b=await H.get("/api/sidebar/registered-items")||[]}catch{}n=Y(n,Z);const{toolsFolder:h,pluginsFolder:g,parented:c}=V(b);n=te(n,c),n=J(n,[h,g]);const y=await ne(a);y.length&&se(n,y),n=T(n,a),n=Q(n);const[j,E]=await Promise.all([re().catch(()=>({})),oe()]),q=i?` dm-admin-sidebar--${f(i)}`:"";function L(s,d="px"){if(s==null)return s;const o=String(s).trim();return/^\d+(\.\d+)?$/.test(o)?`${o}${d}`:o}let P="";if(r){const s=[];r.fontFamily&&s.push(`font-family: ${r.fontFamily}, sans-serif`),r.fontSize&&s.push(`font-size: ${L(r.fontSize)}`),r.fontWeight&&s.push(`font-weight: ${r.fontWeight}`),r.letterSpacing&&s.push(`letter-spacing: ${L(r.letterSpacing,"em")}`);const d=[];if(s.length&&d.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=L(r.iconSize);d.push(`#admin-sidebar .dm-admin-sidebar [data-icon], #admin-sidebar .dm-admin-sidebar [data-icon] svg { width: ${o} !important; height: ${o} !important; }`)}d.length&&(P=`<style data-admin-sidebar-style>${d.join(" ")}</style>`)}const X=`<div class="dm-admin-sidebar-header"><span data-icon="layout"></span> ${f(E)}</div>`,W=n.some(s=>s&&s.type==="spacer"&&s.size==="flex")?" dm-admin-sidebar--has-flex-spacer":"",D=`${P}<nav class="dm-admin-sidebar${q}${W}">${X}${n.map(s=>U(s,j)).join("")}</nav>`,F=document.createRange();F.selectNodeContents(t);const _=F.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:p});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=""),m==="floating"){K(t,{position:"floating",float:l||{}});const d=l&&l.anchor||"TL",o=Number.isFinite(Number(l?.offsetX))?Number(l.offsetX):16,A=t.getBoundingClientRect().width||v||260;if(x){const C=Math.round(A+o*2)+"px";d==="R"||d==="TR"||d==="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 d=this.getAttribute("data-state-key");d&&S.set(d,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,z=0,B=0;w.addEventListener("mousedown",s=>{k=!0,z=s.clientX,B=t.getBoundingClientRect().width,document.body.style.cursor="col-resize",document.body.style.userSelect="none",s.preventDefault()}),document.addEventListener("mousemove",s=>{if(!k)return;const d=Math.max(180,Math.min(480,B+(s.clientX-z)));t.style.width=d+"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 R(){const s=location.hash||"#/";$(t).find(".sidebar-link").removeClass("active"),$(t).find(`.sidebar-link[data-url="${s}"]`).addClass("active")}R(),M.subscribe("router:afterChange",R)}
1
+ import{api as f}from"../api.js";import{colourToCss as K,applyFloat as ne,applyHighlight as se,itemHighlightAttrs as ie}from"/public/js/menu-decor.mjs?v=20260918-item-highlight";import{groupPluginItems as ae,stripItemByUrl as oe,insertFoldersBeforeSystem as re,pruneEmptySynthesisedFolders as le,MANAGE_PLUGINS_URL as ce}from"./sidebar-grouping.js";function g(e){return String(e??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}const de=[{text:"Dashboard",url:"#/",icon:"home"},{text:"Menus",url:"#/menus",icon:"menu"}];function V(e,s){return s?!e||!e.length?!1:e.includes(s)?!0:["read","create","update","delete"].some(n=>e.includes(`${s}.${n}`)):!0}function G(e,s){const n=[];for(const t of e){if(t.hidden||t.permission&&!V(s,t.permission))continue;const i=Array.isArray(t.items)&&t.items.length?G(t.items,s):[];n.push({...t,items:i})}return n}function ue(e,s){if(!s||!s.length)return e;const n=(t,i)=>{for(const o of t){if((o.text||"").toLowerCase()===i.toLowerCase())return o;if(Array.isArray(o.items)){const a=n(o.items,i);if(a)return a}}return null};for(const t of s)if(t.parent){const i=n(e,t.parent);i?(i.items=Array.isArray(i.items)?i.items:[],i.items.push(t.item)):e.push(t.item)}else e.push(t.item);return e}async function me(e){if(!V(e,"projects"))return[];let s=[];try{s=await f.projects.list()}catch{return[]}const n={};await Promise.all(s.map(async i=>{try{const o=await f.projects.artefacts(i.slug);n[i.slug]=o}catch{n[i.slug]={}}}));const t=[{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(i=>{const a="#/projects/"+encodeURIComponent(i.slug),r=n[i.slug]||{},c=[{text:"Overview",url:a,icon:i.icon||"folder"}];for(const d of t){const b=r[d.key],h=Array.isArray(b)?b.length:0;h<=0||c.push({text:`${d.text} (${h})`,url:a+d.path,icon:d.icon})}c.push({text:"Settings",url:a+"/settings",icon:"settings"});const u=i.enabled===!1;return{text:i.name||i.slug,icon:i.icon||"folder",items:c,...u&&{colour:"neutral",badge:{text:"off"}}}})}function fe(e,s){for(const n of e)if((n.text||"").toLowerCase()==="projects"){n.items=Array.isArray(n.items)?n.items.slice():[],n.items.push(...s);return}}function J(e,s){const n=Q.includes(s.tone)?s.tone:"info",t=s.label||String(s.count);s.details?N.set(e,s.details):N.delete(e);const i=C.some(o=>o.url===e)?" data-live":"";return`<span class="sidebar-badge sidebar-badge--${n}" data-count-for="${g(e)}"${i} data-badge-label="${g(t)}" aria-label="${g(t)}">${g(String(s.count))}</span>`}function Y(e){for(const s of e.querySelectorAll("details")){const n=s.querySelector(":scope > summary");if(!n)continue;let t=0,i=!1;const o=[];for(const d of s.querySelectorAll(":scope > .sidebar-children .sidebar-badge[data-live]")){const b=Number(d.textContent)||0;b&&(t+=b,i||(i=d.classList.contains("sidebar-badge--danger")),o.push({text:d.closest("a")?.querySelector(".sidebar-text")?.textContent.trim()??"",meta:d.dataset.badgeLabel,tone:d.classList.contains("sidebar-badge--danger")?"danger":void 0}))}const a=`folder:${s.dataset.stateKey||n.textContent.trim()}`;let r=n.querySelector(":scope > .sidebar-badge--sum");if(!t){r?.remove(),N.delete(a);continue}r||(r=document.createElement("span"),n.querySelector(".sidebar-text")?.after(r));const c=n.querySelector(".sidebar-text")?.textContent.trim()||"This folder",u=o.map(d=>`${d.text}: ${d.meta}`).join(" \xB7 ");r.className=`sidebar-badge sidebar-badge--sum sidebar-badge--${i?"danger":"info"}`,r.dataset.countFor=a,r.dataset.badgeLabel=u,r.setAttribute("aria-label",u),r.textContent=String(t),N.set(a,{title:c,items:o})}}const N=new Map;function ge(e,s){if(!e.url||!s)return"";const n=s[e.url];return!n||!(n.count>0)?"":J(e.url,n)}const Q=["info","attention","danger","live"],pe={"#/pages":["page","pages"],"#/media":["file in the media library","files in the media library"],"#/menus":["menu","menus"],"#/context-menus":["context menu","context menus"],"#/theme":["element override in the theme","element overrides in the theme"],"#/collections":["collection","collections"],"#/forms":["form","forms"],"#/views":["view","views"],"#/actions":["action","actions"],"#/blocks":["block","blocks"],"#/components":["component","components"],"#/users":["user","users"],"#/plugins":["plugin installed","plugins installed"],"#/system/notifications":["unread notification","unread notifications"]};function T(e,{fallbackLabel:s="",tone:n="info"}={}){const t=Number(typeof e=="object"&&e!==null?e.count:e);if(!Number.isFinite(t)||t<0)return null;const i=typeof e?.label=="string"&&e.label||(s?`${t} - ${s}`:String(t)),o={count:Math.round(t),label:i,tone:Q.includes(e?.tone)?e.tone:n};return e?.details&&typeof e.details=="object"&&(o.details=e.details),o}function he(e){const s=(e.text||"").replace(/\s*\(\d+\)\s*$/,"").toLowerCase().replace(/\s+/g,"-"),n=(e.url||"").match(/^#\/projects\/([^/]+)/);return`sidebar.expanded.${n?`project-${decodeURIComponent(n[1])}.`:""}${s}`}function Z(e,s){if(e&&e.type==="separator")return'<hr class="sidebar-divider">';if(e&&e.type==="spacer"){const h=["xs","sm","md","lg","xl","flex"].includes(e.size)?e.size:"md";return`<div class="sidebar-spacer sidebar-spacer--${g(h)}" aria-hidden="true"></div>`}const n=e.icon?`<span data-icon="${g(e.icon)}"></span>`:"",t=g(e.text||""),i=e.url?g(e.url):"",o=Array.isArray(e.items)&&e.items.length>0,a=e.colour?K(e.colour):"",r=ie(e),c=[r.hover&&` data-menu-item-hover="${g(r.hover)}"`,r.active&&` data-menu-item-active="${g(r.active)}"`].filter(Boolean).join(""),u=[a&&`color:${g(a)}`,r.accent&&`--dm-menu-highlight:${g(r.accent)}`].filter(Boolean).join(";"),d=u?` style="${u}"`:"";let b="";if(e.badge&&e.badge.text!=null&&e.badge.text!==""){const h=e.badge.variant?K(e.badge.variant):"";b=`<span class="dm-menu-badge"${h?` style="background:${g(h)};color:var(--dm-text-inverse,#fff)"`:""}>${g(String(e.badge.text))}</span>`}if(o){const h=he(e),v=S.get(h)!==!1,w=e.items.map(k=>Z(k,s)).join("");return`<details data-state-key="${g(h)}"${v?" open":""}>
2
+ <summary${d}>${n} <span class="sidebar-text">${t}</span>${b}</summary>
3
+ <div class="sidebar-children">${w}</div>
4
+ </details>`}return`<a href="${i}" class="sidebar-link" data-url="${i}"${d}${c}>${n} <span class="sidebar-text">${t}</span>${ge(e,s)}${b}</a>`}async function be(e=[]){const s=y=>y.then(x=>Array.isArray(x)?x.length:Array.isArray(x?.entries)?x.entries.length:0).catch(()=>0),[n,t,i,o,a,r,c,u,d,b,h,v,w,k]=await Promise.all([s(f.pages.list()),s(f.media.list()),s(f.menus.list()),s(f.contextMenus.list()),f.theme.get().then(y=>(y?.overrides||[]).length).catch(()=>0),s(f.collections.list()),s(f.forms.list()),s(f.views.list()),s(f.actions.list()),s(f.blocks.list()),s(f.components.list()),s(f.users.list()),s(f.plugins.list()),f.system?.notifications?.unreadCount?.().then(y=>y?.count??0).catch(()=>0)??Promise.resolve(0)]);C=ve(e);const B={};await Promise.all(C.map(async y=>{const x=await ee(y);x&&(B[y.url]=x)}));const E={"#/pages":n,"#/media":t,"#/menus":i,"#/context-menus":o,"#/theme":a,"#/collections":r,"#/forms":c,"#/views":u,"#/actions":d,"#/blocks":b,"#/components":h,"#/users":v,"#/plugins":w,"#/system/notifications":k},j={};for(const[y,x]of Object.entries(E)){const[U,R]=pe[y]??["item","items"],A=T(x);A&&(j[y]={...A,label:`${A.count} ${A.count===1?U:R}`})}return{...j,...B}}let C=[];const ye="dm:sidebar-count",xe=6e4;function ve(e=[]){const s=[],n=t=>{if(t){t.url&&typeof t.countUrl=="string"&&t.countUrl&&s.push({url:t.url,countUrl:t.countUrl,text:t.text||"",countLabel:t.countLabel||""});for(const i of[...t.children||[],...t.items||[]])n(i)}};for(const t of e??[])n(t?.item??t);return s}async function ee(e){try{const s=new Date,n=`${s.getFullYear()}-${String(s.getMonth()+1).padStart(2,"0")}-${String(s.getDate()).padStart(2,"0")}`,t=e.countUrl+(e.countUrl.includes("?")?"&":"?")+`today=${n}`;return T(await H.get(t),{fallbackLabel:e.countLabel||e.text})}catch{return null}}function te(e,s,n){const t=[...e.querySelectorAll("a.sidebar-link")].find(o=>o.dataset.url===s);if(!t)return;const i=t.querySelector(".sidebar-badge[data-count-for]");if(!n||!(n.count>0))i?.remove();else{const o=document.createRange();o.selectNodeContents(t);const a=o.createContextualFragment(J(s,n)).firstElementChild;i?i.replaceWith(a):t.querySelector(".sidebar-text")?.after(a)}O.set(s,n),Y(e)}const O=new Map;async function _(e,s=null){const n=s?C.filter(t=>t.url===s):C;await Promise.all(n.map(async t=>{const i=await ee(t),o=O.get(t.url);JSON.stringify(o??null)!==JSON.stringify(i)&&te(e,t.url,i)}))}function $e(e){e.dataset.liveCounts||(e.dataset.liveCounts="1",window.addEventListener(ye,s=>{const n=s.detail||{};if(n.url)if(n.count!==void 0){const t=C.find(i=>i.url===n.url);te(e,n.url,T(n,{fallbackLabel:t?.countLabel||t?.text||""}))}else _(e,n.url)}),setInterval(()=>{!document.hidden&&document.body.contains(e)&&_(e)},xe),document.addEventListener("visibilitychange",()=>{document.hidden||_(e)}),Se(e))}function Ce(e,s,n){const t=(a,r,c)=>{const u=document.createElement(a);return r&&(u.className=r),c!=null&&(u.textContent=String(c)),u};if(!n){e.textContent=s;return}e.appendChild(t("strong","dm-badge-pop-title",n.title||s)),(n.rows?.length||!n.items?.length)&&e.appendChild(t("p","dm-badge-pop-sub",s));const i=Array.isArray(n.rows)?n.rows.slice(0,6):[];if(i.length){const a=t("dl","dm-badge-pop-rows");for(const r of i)a.appendChild(t("dt",null,r.label)),a.appendChild(t("dd",r.tone==="danger"?"is-danger":null,r.value));e.appendChild(a)}const o=Array.isArray(n.items)?n.items.slice(0,6):[];if(o.length){const a=t("ul","dm-badge-pop-items");for(const r of o){const c=t("li",r.tone==="danger"?"is-danger":null);c.appendChild(t("span","dm-badge-pop-text",r.text)),r.meta&&c.appendChild(t("span","dm-badge-pop-meta",r.meta)),a.appendChild(c)}e.appendChild(a)}else n.empty&&e.appendChild(t("p","dm-badge-pop-empty",n.empty))}function Se(e){let s=null,n=null;const t=()=>{clearTimeout(n),s?.remove(),s=null},i=o=>{t();const a=o?.dataset.badgeLabel;a&&(n=setTimeout(()=>{s=document.createElement("div"),s.className="dm-help-pop dm-badge-pop",s.setAttribute("role","tooltip"),Ce(s,a,N.get(o.dataset.countFor)),document.body.appendChild(s);const r=o.getBoundingClientRect(),c=s.getBoundingClientRect(),u=Math.min(r.right+10,innerWidth-c.width-8);s.style.left=`${Math.max(8,u)}px`,s.style.top=`${Math.max(8,Math.min(r.top+r.height/2-c.height/2,innerHeight-c.height-8))}px`},150))};e.addEventListener("mouseover",o=>{const a=o.target.closest?.(".sidebar-badge[data-badge-label]");a&&i(a)}),e.addEventListener("mouseout",o=>{o.target.closest?.(".sidebar-badge[data-badge-label]")&&t()}),e.addEventListener("focusin",o=>{const a=o.target.closest?.("a, summary")?.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");a?i(a):t()}),e.addEventListener("focusout",t),window.addEventListener("scroll",t,!0),window.addEventListener("hashchange",t)}async function we(){try{const e=await f.settings.get();return e?.adminBrand?.title||e?.title||"Admin"}catch{return"Admin"}}export async function renderAdminSidebar({mount:e,permissions:s}){const n=$(e).get(0);if(!n)return;let t,i=null,o=null,a=null,r=null,c=null;try{const l=await f.menus.get("admin-sidebar");t=Array.isArray(l?.items)?l.items:null,i=l?.variant||null,o=l?.position||null,a=l?.style||null,r=l?.float||null,c=l?.highlight||null}catch{t=null}(!t||!t.length)&&(console.warn("[admin-sidebar] No admin-sidebar menu found; using fallback tree"),t=de.slice());let u=[];try{u=await H.get("/api/sidebar/registered-items")||[]}catch{}t=oe(t,ce);const{toolsFolder:d,managePluginsItem:b,parented:h}=ae(u);t=ue(t,h),t=re(t,[d,b]);const v=await me(s);v.length&&fe(t,v),t=G(t,s),t=le(t);const[w,k]=await Promise.all([be(u).catch(()=>({})),we()]),B=i?` dm-admin-sidebar--${g(i)}`:"";function E(l,p="px"){if(l==null)return l;const m=String(l).trim();return/^\d+(\.\d+)?$/.test(m)?`${m}${p}`:m}let j="";if(a){const l=[];a.fontFamily&&l.push(`font-family: ${a.fontFamily}, sans-serif`),a.fontSize&&l.push(`font-size: ${E(a.fontSize)}`),a.fontWeight&&l.push(`font-weight: ${a.fontWeight}`),a.letterSpacing&&l.push(`letter-spacing: ${E(a.letterSpacing,"em")}`);const p=[];if(l.length&&p.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 { ${l.join("; ")} }`),a.iconSize){const m=E(a.iconSize);p.push(`#admin-sidebar .dm-admin-sidebar [data-icon], #admin-sidebar .dm-admin-sidebar [data-icon] svg { width: ${m} !important; height: ${m} !important; }`)}p.length&&(j=`<style data-admin-sidebar-style>${p.join(" ")}</style>`)}const y=`<div class="dm-admin-sidebar-header"><span data-icon="layout"></span> ${g(k)}</div>`,x=t.some(l=>l&&l.type==="spacer"&&l.size==="flex")?" dm-admin-sidebar--has-flex-spacer":"",U=`${j}<nav class="dm-admin-sidebar${B}${x}">${y}${t.map(l=>Z(l,w)).join("")}</nav>`,R=document.createRange();R.selectNodeContents(n);const A=R.createContextualFragment(U);n.replaceChildren(A),Domma.icons.scan(n);for(const l of C)O.set(l.url,w[l.url]??null);Y(n),$e(n);const P=Number.parseInt(S.get("sidebar.width"),10);Number.isFinite(P)&&P>=180&&P<=480&&(n.style.width=P+"px"),n.removeAttribute("data-menu-hover"),n.removeAttribute("data-menu-active"),n.style.removeProperty("--dm-menu-highlight"),se(n,{highlight:c});const L=document.querySelector(".dashboard-wrapper");if(["position","top","bottom","left","right","zIndex","maxHeight","transform"].forEach(l=>{n.style[l]=""}),n.classList.remove("dm-menu-floating"),L&&(L.style.paddingLeft="",L.style.paddingRight=""),o==="floating"){ne(n,{position:"floating",float:r||{}});const p=r&&r.anchor||"TL",m=Number.isFinite(Number(r?.offsetX))?Number(r.offsetX):16,F=n.getBoundingClientRect().width||P||260;if(L){const z=Math.round(F+m*2)+"px";p==="R"||p==="TR"||p==="BR"?L.style.paddingRight=z:L.style.paddingLeft=z}}const q=document.createElement("div");q.className="dm-admin-sidebar-handle",q.setAttribute("aria-label","Resize sidebar"),n.appendChild(q),n.querySelectorAll("details").forEach(l=>{l.addEventListener("toggle",function(){const p=this.getAttribute("data-state-key");p&&S.set(p,this.open);const m=this.querySelector(":scope > .sidebar-children");if(m)if(this.open){const F=m.scrollHeight;m.style.maxHeight="0",requestAnimationFrame(()=>{m.style.maxHeight=F+"px",m.addEventListener("transitionend",function z(){m.style.maxHeight="none",m.removeEventListener("transitionend",z)})})}else{const F=m.scrollHeight;m.style.maxHeight=F+"px",requestAnimationFrame(()=>{m.style.maxHeight="0"})}})});let I=!1,D=0,W=0;q.addEventListener("mousedown",l=>{I=!0,D=l.clientX,W=n.getBoundingClientRect().width,document.body.style.cursor="col-resize",document.body.style.userSelect="none",l.preventDefault()}),document.addEventListener("mousemove",l=>{if(!I)return;const p=Math.max(180,Math.min(480,W+(l.clientX-D)));n.style.width=p+"px"}),document.addEventListener("mouseup",()=>{I&&(I=!1,document.body.style.cursor="",document.body.style.userSelect="",S.set("sidebar.width",parseInt(n.style.width,10)))});function X(){const l=location.hash||"#/";$(n).find(".sidebar-link").removeClass("active"),$(n).find(`.sidebar-link[data-url="${l}"]`).addClass("active")}X(),M.subscribe("router:afterChange",X)}
@@ -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}
@@ -0,0 +1 @@
1
+ export const STATUSES=["not-started","in-progress","completed"],PRIORITIES=["highest","high","medium","low","lowest"],FILTERS=["open","all","not-started","in-progress","completed","overdue"],SORTS=["created","priority","due","status"],DEFAULT_SETTINGS=Object.freeze({defaultPriority:"medium",defaultFilter:"open",sort:"created",relativeDue:!0,confirmDelete:!0});const c=(r,e,t)=>e.includes(r)?r:t;export function normaliseSettings(r){const e=r&&typeof r=="object"?r:{},t=DEFAULT_SETTINGS;return{defaultPriority:c(e.defaultPriority,PRIORITIES,t.defaultPriority),defaultFilter:c(e.defaultFilter,FILTERS,t.defaultFilter),sort:c(e.sort,SORTS,t.sort),relativeDue:typeof e.relativeDue=="boolean"?e.relativeDue:t.relativeDue,confirmDelete:typeof e.confirmDelete=="boolean"?e.confirmDelete:t.confirmDelete}}export function startOfDay(r=new Date){const e=new Date(r);return e.setHours(0,0,0,0),e.getTime()}export function dueDay(r){if(!r)return null;const e=/^(\d{4})-(\d{2})-(\d{2})/.exec(String(r));return e?new Date(Number(e[1]),Number(e[2])-1,Number(e[3])).getTime():null}export function daysUntil(r,e=new Date){const t=dueDay(r);return t===null?null:Math.round((t-startOfDay(e))/864e5)}export function isOverdue(r,e=new Date){const t=daysUntil(r.dueAt,e);return t!==null&&t<0&&r.status!=="completed"}export function dueLabel(r,{relative:e=!0,now:t=new Date,format:i}={}){const s=daysUntil(r,t);return s===null?"":e?s===0?"Today":s===1?"Tomorrow":s===-1?"Yesterday":s>0?`In ${s} days`:`${-s} days ago`:i?i(dueDay(r)):String(r).slice(0,10)}export function isoDay(r,e=new Date){const t=new Date(startOfDay(e)+r*864e5+432e5),i=s=>String(s).padStart(2,"0");return`${t.getFullYear()}-${i(t.getMonth()+1)}-${i(t.getDate())}`}export function arrangeTodos(r,{filter:e="open",query:t="",sort:i="created",now:s=new Date}={}){const l=String(t).trim().toLowerCase(),p=(r||[]).filter(n=>e==="open"&&n.status==="completed"||STATUSES.includes(e)&&n.status!==e||e==="overdue"&&!isOverdue(n,s)?!1:!l||String(n.text??"").toLowerCase().includes(l)),u=(n,o)=>{const f=n.indexOf(o);return f===-1?n.length:f},a=(n,o)=>String(n.createdAt??"").localeCompare(String(o.createdAt??"")),y={created:a,priority:(n,o)=>u(PRIORITIES,n.priority)-u(PRIORITIES,o.priority)||a(n,o),due:(n,o)=>(dueDay(n.dueAt)??1/0)-(dueDay(o.dueAt)??1/0)||u(PRIORITIES,n.priority)-u(PRIORITIES,o.priority),status:(n,o)=>u(STATUSES,n.status)-u(STATUSES,o.status)||a(n,o)}[i]??a,d=n=>n.status==="completed"?1:0;return[...p].sort((n,o)=>d(n)-d(o)||y(n,o))}export function countTodos(r,e=new Date){const t={all:0,open:0,"not-started":0,"in-progress":0,completed:0,overdue:0};for(const i of r||[])t.all++,i.status!=="completed"&&t.open++,t[i.status]!==void 0&&t[i.status]++,isOverdue(i,e)&&t.overdue++;return t}
@@ -0,0 +1 @@
1
+ export function onceClosed(t,e=()=>{}){const n=t.close.bind(t);let o=!1;t.close=(...c)=>{const r=n(...c);return o||(o=!0,e(),setTimeout(()=>{try{t.destroy?.()}catch{}t.element?.remove()},400)),r}}export function openMenuAt(t){const e=t.getBoundingClientRect();t.dispatchEvent(new MouseEvent("contextmenu",{bubbles:!0,cancelable:!0,clientX:e.left,clientY:e.bottom,view:window}))}export function settingsStore(t,e){return{read(){try{return e(S.get(t))}catch{return e(null)}},write(n){try{S.set(t,n)}catch{}}}}export function viewLifetime(t){let e=[];const n=()=>{const c=e;e=[],c.forEach(r=>{try{r?.()}catch{}})},o=()=>{t.test(location.hash)||n()};return{add(c){e.push(c)},reset(){n(),window.addEventListener("hashchange",o),e.push(()=>window.removeEventListener("hashchange",o))}}}export function sidebarCount(t,e={}){try{window.dispatchEvent(new CustomEvent("dm:sidebar-count",{detail:{url:t,...e}}))}catch{}}