domma-cms 0.55.1 → 0.66.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/CLAUDE.md +130 -2
  2. package/README.md +4 -4
  3. package/admin/css/admin.css +1 -1
  4. package/admin/dist/domma/domma-tools.css +3 -3
  5. package/admin/dist/domma/domma-tools.min.js +3 -3
  6. package/admin/js/api.js +1 -1
  7. package/admin/js/app.js +3 -3
  8. package/admin/js/lib/page-picker.js +1 -0
  9. package/admin/js/lib/plugin-accent.js +1 -0
  10. package/admin/js/lib/plugin-chrome.js +1 -0
  11. package/admin/js/lib/shortcode-context-menu.js +2 -2
  12. package/admin/js/lib/sidebar-grouping.js +1 -1
  13. package/admin/js/lib/sidebar-grouping.test.js +1 -1
  14. package/admin/js/lib/sidebar-renderer.js +4 -4
  15. package/admin/js/lib/slideover-resizable.js +1 -0
  16. package/admin/js/templates/context-menu-editor.html +212 -0
  17. package/admin/js/templates/context-menus.html +16 -0
  18. package/admin/js/templates/plugin-code.html +1 -1
  19. package/{plugins/site-search/admin/templates/site-search.html → admin/js/templates/search.html} +50 -4
  20. package/admin/js/templates/settings.html +16 -97
  21. package/admin/js/templates/theme.html +173 -0
  22. package/admin/js/views/context-menu-editor.js +55 -0
  23. package/admin/js/views/context-menus.js +5 -0
  24. package/admin/js/views/form-editor.js +7 -7
  25. package/admin/js/views/index.js +1 -1
  26. package/admin/js/views/plugin-marketplace.js +1 -1
  27. package/admin/js/views/plugins.js +25 -23
  28. package/admin/js/views/search.js +1 -0
  29. package/admin/js/views/settings.js +3 -3
  30. package/admin/js/views/theme.js +1 -0
  31. package/bin/cli.js +2 -0
  32. package/bin/update.js +13 -2
  33. package/config/menus/admin-sidebar.json +129 -23
  34. package/config/plugins.json +5 -5
  35. package/config/search.json +13 -0
  36. package/config/theme.json +18 -0
  37. package/package.json +12 -4
  38. package/plugins/_lib/admin/mail/compose-window.js +914 -0
  39. package/plugins/_lib/admin/mail/contacts.js +301 -0
  40. package/plugins/_lib/admin/mail/diagnostics-section.js +133 -0
  41. package/plugins/_lib/admin/mail/folder-tree.js +254 -0
  42. package/plugins/_lib/admin/mail/identity.js +480 -0
  43. package/plugins/_lib/admin/mail/image-senders-section.js +131 -0
  44. package/plugins/_lib/admin/mail/keyboard.js +136 -0
  45. package/plugins/_lib/admin/mail/mail.css +1 -0
  46. package/plugins/_lib/admin/mail/mail.html +71 -0
  47. package/plugins/_lib/admin/mail/panes.js +253 -0
  48. package/plugins/_lib/admin/mail/reader-view.js +4453 -0
  49. package/plugins/_lib/admin/mail/resizable.js +26 -0
  50. package/plugins/_lib/admin/mail/rules.js +343 -0
  51. package/plugins/_lib/admin/mail/scheduling.js +203 -0
  52. package/plugins/_lib/admin/mail/section-kit.js +277 -0
  53. package/plugins/_lib/admin/mail/templates.js +238 -0
  54. package/plugins/_lib/admin/mail/threads.js +200 -0
  55. package/plugins/_lib/admin/mail/vacation.js +269 -0
  56. package/plugins/_lib/admin/ui/help.css +1 -0
  57. package/plugins/_lib/admin/ui/help.js +174 -0
  58. package/plugins/_lib/admin/ui/resizable.js +151 -0
  59. package/plugins/_lib/dataStore.js +117 -0
  60. package/plugins/_lib/mail/accounts.js +919 -0
  61. package/plugins/_lib/mail/bodyTokens.js +101 -0
  62. package/plugins/_lib/mail/compose.js +256 -0
  63. package/plugins/_lib/mail/defaults.js +57 -0
  64. package/plugins/_lib/mail/diagnostics.js +154 -0
  65. package/plugins/_lib/mail/envelope.js +161 -0
  66. package/plugins/_lib/mail/folders.js +192 -0
  67. package/plugins/_lib/mail/handoff.js +274 -0
  68. package/plugins/_lib/mail/imapPool.js +293 -0
  69. package/plugins/_lib/mail/mbox.js +74 -0
  70. package/plugins/_lib/mail/pollSchedule.js +79 -0
  71. package/plugins/_lib/mail/poller.js +135 -0
  72. package/plugins/_lib/mail/priority.js +138 -0
  73. package/plugins/_lib/mail/readRoutes.js +680 -0
  74. package/plugins/_lib/mail/render.js +291 -0
  75. package/plugins/_lib/mail/ruleRunner.js +152 -0
  76. package/plugins/_lib/mail/scheduler.js +254 -0
  77. package/plugins/_lib/mail/secretbox.js +229 -0
  78. package/plugins/_lib/mail/send.js +396 -0
  79. package/plugins/_lib/mail/store.js +1002 -0
  80. package/plugins/_lib/mail/sync.js +292 -0
  81. package/plugins/_lib/mail/syncPlan.js +126 -0
  82. package/plugins/_lib/mail/syncSelection.js +82 -0
  83. package/plugins/_lib/mail/unsubscribe.js +183 -0
  84. package/plugins/_lib/mail/vacationRunner.js +114 -0
  85. package/plugins/_lib/mail/write.js +473 -0
  86. package/plugins/_lib/schemaSync.js +83 -0
  87. package/plugins/_template/admin/css/index.css +0 -0
  88. package/plugins/_template/admin/templates/index.html +4 -4
  89. package/plugins/_template/admin/views/index.js +7 -0
  90. package/plugins/analytics/admin/css/index.css +1 -0
  91. package/plugins/analytics/admin/templates/analytics.html +22 -13
  92. package/plugins/analytics/plugin.json +3 -0
  93. package/plugins/blog/admin/css/index.css +1 -0
  94. package/plugins/blog/admin/templates/blog.html +30 -18
  95. package/plugins/blog/admin/templates/categories.html +2 -2
  96. package/plugins/blog/admin/templates/comments.html +2 -2
  97. package/plugins/blog/admin/templates/post-editor.html +34 -34
  98. package/plugins/blog/admin/templates/settings.html +6 -3
  99. package/plugins/blog/admin/views/blog.js +8 -5
  100. package/plugins/blog/admin/views/categories.js +5 -10
  101. package/plugins/blog/admin/views/comments.js +5 -5
  102. package/plugins/blog/admin/views/post-editor.js +39 -20
  103. package/plugins/blog/admin/views/settings.js +52 -50
  104. package/plugins/blog/collections/categories/schema.json +7 -6
  105. package/plugins/blog/collections/comments/schema.json +11 -10
  106. package/plugins/blog/collections/posts/schema.json +14 -13
  107. package/plugins/blog/plugin.js +36 -13
  108. package/plugins/blog/plugin.json +13 -5
  109. package/plugins/blog/plugin.public.js +312 -0
  110. package/plugins/contacts/admin/css/index.css +1 -0
  111. package/plugins/contacts/admin/templates/contacts.html +128 -0
  112. package/plugins/contacts/admin/views/contacts.js +237 -4
  113. package/plugins/contacts/collections/user-contacts/schema.json +108 -0
  114. package/plugins/contacts/plugin.js +214 -27
  115. package/plugins/contacts/plugin.json +4 -1
  116. package/plugins/invoice/admin/css/index.css +1 -0
  117. package/plugins/invoice/admin/templates/editor.html +140 -49
  118. package/plugins/invoice/admin/templates/index.html +153 -23
  119. package/plugins/invoice/admin/templates/issuers.html +2 -5
  120. package/plugins/invoice/admin/templates/receivers.html +2 -5
  121. package/plugins/invoice/admin/views/contacts-source.js +266 -0
  122. package/plugins/invoice/admin/views/editor.js +366 -199
  123. package/plugins/invoice/admin/views/export.js +199 -0
  124. package/plugins/invoice/admin/views/help-content.js +61 -0
  125. package/plugins/invoice/admin/views/index.js +582 -94
  126. package/plugins/invoice/admin/views/issuers.js +24 -17
  127. package/plugins/invoice/admin/views/media.js +172 -0
  128. package/plugins/invoice/admin/views/party-view.js +305 -67
  129. package/plugins/invoice/admin/views/payments.js +127 -0
  130. package/plugins/invoice/admin/views/print.js +130 -0
  131. package/plugins/invoice/admin/views/receivers.js +49 -16
  132. package/plugins/invoice/admin/views/send.js +212 -0
  133. package/plugins/invoice/admin/views/settings.js +594 -0
  134. package/plugins/invoice/admin/views/view-lifecycle.js +33 -0
  135. package/plugins/invoice/collections/invoice-issuers/schema.json +77 -11
  136. package/plugins/invoice/collections/invoice-receivers/schema.json +10 -9
  137. package/plugins/invoice/collections/invoices/schema.json +19 -13
  138. package/plugins/invoice/config.js +27 -6
  139. package/plugins/invoice/pdf.js +164 -0
  140. package/plugins/invoice/plugin.js +1217 -44
  141. package/plugins/invoice/plugin.json +10 -9
  142. package/plugins/invoice/templates/_base.css +1 -0
  143. package/plugins/invoice/templates/classic-nologo.html +100 -0
  144. package/plugins/invoice/templates/classic.html +91 -0
  145. package/plugins/invoice/templates/invoice-print.html +24 -0
  146. package/plugins/invoice/templates/minimal.html +99 -0
  147. package/plugins/invoice/templates/modern-nologo.html +114 -0
  148. package/plugins/invoice/templates/modern.html +113 -0
  149. package/plugins/invoice/templates/templates.json +11 -0
  150. package/plugins/mail-reader/admin/views/mail.js +19 -0
  151. package/plugins/mail-reader/config.js +7 -0
  152. package/plugins/mail-reader/plugin.js +48 -0
  153. package/plugins/mail-reader/plugin.json +33 -0
  154. package/plugins/notes/admin/views/notes.js +1 -1
  155. package/plugins/notes/plugin.json +2 -2
  156. package/plugins/surveys/lib/audience.js +37 -0
  157. package/plugins/surveys/lib/campaigns.js +43 -0
  158. package/plugins/surveys/lib/ledger.js +110 -0
  159. package/plugins/surveys/lib/sending.js +106 -0
  160. package/plugins/surveys/lib/stats.js +62 -0
  161. package/plugins/surveys/lib/submit.js +95 -0
  162. package/plugins/surveys/lib/tokens.js +28 -0
  163. package/plugins/surveys/plugin.public.js +149 -0
  164. package/plugins/theme-switcher/admin/templates/theme-switcher.html +1 -1
  165. package/public/css/forms.css +1 -1
  166. package/public/css/search.css +1 -0
  167. package/public/css/site.css +1 -1
  168. package/public/js/collection-context.js +2 -2
  169. package/public/js/context-menus.js +1 -0
  170. package/public/js/form-logic-engine.js +1 -1
  171. package/public/js/forms.js +2 -2
  172. package/public/js/search.js +1 -0
  173. package/public/js/site.js +1 -1
  174. package/scripts/build.js +37 -3
  175. package/scripts/copy-domma.js +48 -0
  176. package/scripts/seed.js +1996 -0
  177. package/server/routes/api/collections.js +34 -0
  178. package/server/routes/api/context-menus.js +104 -0
  179. package/server/routes/api/forms.js +42 -3
  180. package/server/routes/api/notifications.js +69 -19
  181. package/server/routes/api/plugins.js +50 -6
  182. package/server/routes/api/search.js +43 -0
  183. package/server/routes/api/theme.js +69 -0
  184. package/server/routes/public.js +42 -7
  185. package/server/server.js +74 -0
  186. package/server/services/adapters/FileAdapter.js +6 -1
  187. package/server/services/content.js +26 -0
  188. package/server/services/contextMenus.js +477 -0
  189. package/server/services/markdown.js +70 -9
  190. package/server/services/permissionRegistry.js +24 -0
  191. package/server/services/pluginFiles.js +52 -11
  192. package/server/services/plugins.js +229 -6
  193. package/server/services/renderer.js +144 -22
  194. package/server/services/roles.js +1 -1
  195. package/server/services/search-migration.js +82 -0
  196. package/server/services/search.js +413 -0
  197. package/server/services/sidebar-migration.js +1 -0
  198. package/server/services/themeSettings.js +541 -0
  199. package/server/services/users.js +8 -0
  200. package/server/templates/page.html +4 -2
  201. package/plugins/contacts/data/contacts.json +0 -20
  202. package/plugins/notes/data/notes.json +0 -1
  203. package/plugins/site-search/admin/views/site-search.js +0 -116
  204. package/plugins/site-search/config.js +0 -15
  205. package/plugins/site-search/plugin.js +0 -188
  206. package/plugins/site-search/plugin.json +0 -40
  207. package/plugins/site-search/public/inject-body.html +0 -17
  208. package/plugins/site-search/public/inject-head.html +0 -1
  209. package/plugins/site-search/public/search.css +0 -1
  210. package/plugins/site-search/public/search.js +0 -1
  211. package/plugins/todo/data/todos.json +0 -1
@@ -1,3 +1,3 @@
1
- import{api as C,apiRequest as y}from"../api.js";import{populateThemeSelect as w}from"../lib/themes.js";export const settingsView={templateUrl:"/admin/js/templates/settings.html",async onMount(e){E.tabs(e.find("#settings-tabs").get(0));const p=E.loader(e.get(0),{type:"dots"}),t=await C.settings.get().catch(()=>({}));if(p.destroy(),e.find("#field-site-title").val(t.title||""),e.find("#field-tagline").val(t.tagline||""),e.find("#field-font-family").val(t.fontFamily||"Roboto"),e.find("#field-font-size").val(t.fontSize||16),w(e.find("#field-theme").get(0)),w(e.find("#field-admin-theme").get(0),{includeAdmin:!0}),e.find("#field-theme").val(t.baseTheme||t.theme||"charcoal-dark"),t.baseTheme){e.find("#field-theme").prop("disabled",!0);const l=document.createElement("p");l.className="form-hint",l.style.cssText="margin-top:.4rem;font-size:.8rem;color:var(--dm-info);";const s=document.createElement("a");s.href="#/plugins/theme-roller",s.textContent="Theme Roller",l.appendChild(document.createTextNode(`Custom theme \u201C${t.theme}\u201D is active (based on ${t.baseTheme}). Manage via `)),l.appendChild(s),l.appendChild(document.createTextNode(".")),e.find("#field-theme").get(0).closest(".col-6").appendChild(l)}e.find("#field-admin-theme").val(t.adminTheme||"charcoal-dark");const h=t.autoTheme||{},b=!!h.enabled,u=e.find("#field-auto-theme-enabled"),g=e.find("#auto-theme-fields"),v=e.find("#field-theme"),f=v.get(0).innerHTML;e.find("#field-day-theme").html(f),e.find("#field-night-theme").html(f),t.baseTheme?(u.prop("disabled",!0),e.find("#auto-theme-roller-hint").show()):(u.prop("checked",b),b&&(g.show(),v.prop("disabled",!0)),u.on("change",function(){const l=this.checked;g.toggle(l),v.prop("disabled",l)})),e.find("#field-day-theme").val(h.dayTheme||"charcoal-light"),e.find("#field-night-theme").val(h.nightTheme||"charcoal-dark"),e.find("#field-day-start").val(h.dayStart||"07:00"),e.find("#field-night-start").val(h.nightStart||"19:00"),e.find("#field-spacer-size").val(t.layoutOptions?.spacerSize??40),e.find("#field-seo-title").val(t.seo?.defaultTitle||""),e.find("#field-seo-separator").val(t.seo?.titleSeparator||" | "),e.find("#field-seo-desc").val(t.seo?.defaultDescription||""),e.find("#field-footer-copy").val(t.footer?.copyright||""),e.find("#field-footer-motion-toggle").prop("checked",t.footer?.showMotionToggle!==!1),e.find("#field-social-twitter").val(t.social?.twitter||""),e.find("#field-social-facebook").val(t.social?.facebook||""),e.find("#field-social-instagram").val(t.social?.instagram||""),e.find("#field-social-linkedin").val(t.social?.linkedin||""),e.find("#field-social-github").val(t.social?.github||""),e.find("#field-social-youtube").val(t.social?.youtube||""),e.find("#field-smtp-host").val(t.smtp?.host||""),e.find("#field-smtp-port").val(t.smtp?.port||587),e.find("#field-smtp-user").val(t.smtp?.user||""),e.find("#field-smtp-pass").val(t.smtp?.pass||""),e.find("#field-smtp-secure").prop("checked",t.smtp?.secure||!1),e.find("#field-smtp-from-address").val(t.smtp?.fromAddress||""),e.find("#field-smtp-from-name").val(t.smtp?.fromName||""),S(t.smtp?.host||""),e.find("#field-smtp-host").on("input",function(){S(this.value)});const i=t.backToTop||{};e.find("#field-btt-enabled").prop("checked",i.enabled!==!1),e.find("#field-btt-threshold").val(i.scrollThreshold??300),e.find("#field-btt-position").val(i.position||"bottom-right"),e.find("#field-btt-offset").val(i.offset??32),e.find("#field-btt-bottom-offset").val(i.bottomOffset??i.offset??32),e.find("#field-btt-label").val(i.label||""),e.find("#field-btt-smooth").prop("checked",i.smooth!==!1);const d=t.cookieConsent||{};e.find("#field-cc-enabled").prop("checked",d.enabled!==!1),e.find("#field-cc-message").val(d.message||""),e.find("#field-cc-accept-all").val(d.acceptAllText||"Accept All"),e.find("#field-cc-reject-all").val(d.rejectAllText||"Reject All"),e.find("#field-cc-customize").val(d.customizeText||"Customize"),e.find("#field-cc-save-prefs").val(d.savePreferencesText||"Save Preferences"),e.find("#field-cc-privacy-text").val(d.privacyPolicyText||"Privacy Policy"),e.find("#field-cc-privacy-url").val(d.privacyPolicyUrl||""),e.find("#field-cc-cookie-text").val(d.cookiePolicyText||"Cookie Policy"),e.find("#field-cc-cookie-url").val(d.cookiePolicyUrl||""),e.find("#field-cc-position").val(d.position||"bottom"),e.find("#field-cc-layout").val(d.layout||"bar"),e.find("#field-cc-theme").val(d.theme||"dark"),e.find("#field-cc-show-functional").prop("checked",d.showFunctional!==!1),e.find("#field-cc-show-analytics").prop("checked",d.showAnalytics!==!1),e.find("#field-cc-show-marketing").prop("checked",d.showMarketing!==!1),e.find("#field-cc-version").val(d.consentVersion||"1.0");const m=t.breadcrumbs||{};e.find("#field-breadcrumbs-enabled").prop("checked",m.enabled===!0),e.find("#field-breadcrumbs-home-label").val(m.homeLabel||"Home"),e.find("#field-breadcrumbs-position").val(m.position||"TL"),e.find("#field-bc-offset-x").val(m.offsetX??8),e.find("#field-bc-offset-y").val(m.offsetY??8);function c(l){e.find(".bc-pos-btn").each(function(){const s=this.dataset.pos===l;this.style.background=s?"var(--dm-primary, #5b8cff)":"",this.style.color=s?"var(--dm-primary-text)":""})}c(m.position||"TL"),e.find(".bc-pos-btn").on("click",function(){const l=this.dataset.pos;e.find("#field-breadcrumbs-position").val(l),c(l)});let a=null;try{const{css:l}=await y("/settings/custom-css");e.find("#field-custom-css").val(l||""),E.editor&&(a=E.editor(e.find("#field-custom-css").get(0),{mode:"code",language:"css",lineNumbers:!0,showToolbar:!1,minHeight:420,placeholder:"/* Add your custom CSS here */",characterCount:!0}))}catch{}a&&a._editorEl&&a._editorEl.addEventListener("keydown",l=>{if(!l.ctrlKey&&!l.metaKey)return;if(l.key==="s"){l.preventDefault(),e.find("#save-css-btn").get(0)?.click();return}const s=a._editorEl,n=s.selectionStart!==s.selectionEnd;if((l.key==="c"||l.key==="x")&&!n){l.preventDefault();const o=s.value,k=s.selectionStart,T=o.lastIndexOf(`
2
- `,k-1)+1,x=o.indexOf(`
3
- `,k),z=x===-1?o.slice(T):o.slice(T,x+1);if(navigator.clipboard.writeText(z),l.key==="x"){const P=o.slice(0,T),A=x===-1?"":o.slice(x+1);s.value=P+A,s.selectionStart=s.selectionEnd=T,s.dispatchEvent(new Event("input",{bubbles:!0})),E.toast("Line cut",{type:"info",duration:1500})}else E.toast("Line copied",{type:"info",duration:1500})}}),e.find("#save-settings-btn").on("click",async()=>{const l=e.find("#field-admin-theme").val(),s=!t.baseTheme&&e.find("#field-auto-theme-enabled").prop("checked"),n={enabled:s,dayTheme:e.find("#field-day-theme").val()||"charcoal-light",nightTheme:e.find("#field-night-theme").val()||"charcoal-dark",dayStart:e.find("#field-day-start").val()||"07:00",nightStart:e.find("#field-night-start").val()||"19:00"},o={title:e.find("#field-site-title").val().trim(),tagline:e.find("#field-tagline").val().trim(),fontFamily:e.find("#field-font-family").val()||"Roboto",fontSize:parseInt(e.find("#field-font-size").val(),10)||16,theme:t.baseTheme?t.theme:s?n.dayTheme:e.find("#field-theme").val(),...t.baseTheme?{baseTheme:t.baseTheme}:{},autoTheme:n,adminTheme:l,layoutOptions:{spacerSize:parseInt(e.find("#field-spacer-size").val(),10)||40},seo:{defaultTitle:e.find("#field-seo-title").val().trim(),titleSeparator:e.find("#field-seo-separator").val()||" | ",defaultDescription:e.find("#field-seo-desc").val().trim()},footer:{copyright:e.find("#field-footer-copy").val().trim(),showMotionToggle:e.find("#field-footer-motion-toggle").prop("checked"),links:t.footer?.links||[]},social:{twitter:e.find("#field-social-twitter").val().trim(),facebook:e.find("#field-social-facebook").val().trim(),instagram:e.find("#field-social-instagram").val().trim(),linkedin:e.find("#field-social-linkedin").val().trim(),github:e.find("#field-social-github").val().trim(),youtube:e.find("#field-social-youtube").val().trim()},smtp:{host:e.find("#field-smtp-host").val().trim(),port:parseInt(e.find("#field-smtp-port").val(),10)||587,user:e.find("#field-smtp-user").val().trim(),pass:e.find("#field-smtp-pass").val(),secure:e.find("#field-smtp-secure").prop("checked"),fromAddress:e.find("#field-smtp-from-address").val().trim(),fromName:e.find("#field-smtp-from-name").val().trim()},backToTop:{enabled:e.find("#field-btt-enabled").prop("checked"),scrollThreshold:parseInt(e.find("#field-btt-threshold").val(),10)||300,position:e.find("#field-btt-position").val()||"bottom-right",offset:parseInt(e.find("#field-btt-offset").val(),10)||32,bottomOffset:parseInt(e.find("#field-btt-bottom-offset").val(),10)||32,label:e.find("#field-btt-label").val().trim(),smooth:e.find("#field-btt-smooth").prop("checked")},cookieConsent:{enabled:e.find("#field-cc-enabled").prop("checked"),message:e.find("#field-cc-message").val().trim(),acceptAllText:e.find("#field-cc-accept-all").val().trim(),rejectAllText:e.find("#field-cc-reject-all").val().trim(),customizeText:e.find("#field-cc-customize").val().trim(),savePreferencesText:e.find("#field-cc-save-prefs").val().trim(),privacyPolicyText:e.find("#field-cc-privacy-text").val().trim(),privacyPolicyUrl:e.find("#field-cc-privacy-url").val().trim(),cookiePolicyText:e.find("#field-cc-cookie-text").val().trim(),cookiePolicyUrl:e.find("#field-cc-cookie-url").val().trim(),position:e.find("#field-cc-position").val(),layout:e.find("#field-cc-layout").val(),theme:e.find("#field-cc-theme").val(),showFunctional:e.find("#field-cc-show-functional").prop("checked"),showAnalytics:e.find("#field-cc-show-analytics").prop("checked"),showMarketing:e.find("#field-cc-show-marketing").prop("checked"),consentVersion:e.find("#field-cc-version").val().trim()||"1.0"}},k=e.find("#save-settings-btn");k.prop("disabled",!0);try{await C.settings.save(o),Domma.theme.set(l),E.toast("Settings saved.",{type:"success"})}catch{E.toast("Failed to save settings.",{type:"error"})}finally{k.prop("disabled",!1)}}),e.find("#send-test-email-btn").on("click",async()=>{const l=e.find("#field-test-email-to").val().trim(),s=e.find("#test-email-result").get(0),n=e.find("#send-test-email-btn");n.prop("disabled",!0),s&&(s.textContent="Sending\u2026",s.style.color="");try{const o=await y("/settings/test-email",{method:"POST",body:JSON.stringify({to:l||void 0})});s&&(s.textContent=o.message||"Test email sent.",s.style.color="var(--dm-success,#4ade80)")}catch(o){s&&(s.textContent=o.message||"Failed to send test email.",s.style.color="var(--dm-danger,#f87171)")}finally{n.prop("disabled",!1)}}),e.find("#save-breadcrumbs-btn").on("click",async()=>{const l=e.find("#save-breadcrumbs-btn");l.prop("disabled",!0);try{const s={enabled:e.find("#field-breadcrumbs-enabled").prop("checked"),homeLabel:e.find("#field-breadcrumbs-home-label").val().trim()||"Home",position:e.find("#field-breadcrumbs-position").val()||"TL",offsetX:parseInt(e.find("#field-bc-offset-x").val(),10)||8,offsetY:parseInt(e.find("#field-bc-offset-y").val(),10)||8};await C.settings.save({breadcrumbs:s}),t.breadcrumbs=s,E.toast("Breadcrumbs settings saved.",{type:"success"})}catch{E.toast("Failed to save breadcrumbs settings.",{type:"error"})}finally{l.prop("disabled",!1)}}),e.find("#save-css-btn").on("click",async()=>{const l=a?a.getValue():e.find("#field-custom-css").val(),s=e.find("#save-css-btn");s.prop("disabled",!0);try{await y("/settings/custom-css",{method:"PUT",body:JSON.stringify({css:l})}),E.toast("Custom CSS saved.",{type:"success"})}catch(n){E.toast(n.message||"Failed to save CSS.",{type:"error"})}finally{s.prop("disabled",!1)}});async function r(){try{const l=await y("/cache/status"),s=l.enabled?`enabled (${l.driver})`:"disabled",n=l.maxItems?` \xB7 ${l.size} / ${l.maxItems} entries`:"";e.find("#cache-status").text(s+n)}catch{e.find("#cache-status").text("unknown")}try{const{entries:l}=await y("/cache/keys");F(e.find("#cache-keys-table"),l),e.find("#cache-key-count").text(`(${l.length})`)}catch(l){e.find("#cache-keys-table").get(0).textContent="Failed to load keys: "+l.message}}await r(),e.find("#btn-clear-cache").on("click",async()=>{if(!await E.confirm("Clear the entire response cache?"))return;const l=e.find("#btn-clear-cache");l.prop("disabled",!0);try{await y("/cache/clear",{method:"POST"}),E.toast("Cache cleared.",{type:"success"}),await r()}catch(s){E.toast(s.message||"Failed to clear cache.",{type:"error"})}finally{l.prop("disabled",!1)}}),e.find("#btn-refresh-cache-keys").on("click",r)}};function S(e){const p=document.getElementById("smtp-unconfigured-notice");p&&(p.style.display=String(e||"").trim()?"none":"",p.style.display===""&&Domma.icons.scan(p))}function F(e,p){const t=e.get(0);if(!t)return;for(;t.firstChild;)t.removeChild(t.firstChild);if(!p.length){const f=document.createElement("p");f.className="text-muted",f.style.fontSize=".875rem",f.textContent="Cache is empty.",t.appendChild(f);return}const h=[...p].reverse(),b=document.createElement("table");b.className="table table-compact";const u=document.createElement("thead"),g=document.createElement("tr");for(const f of["Key","Tags","Expires"]){const i=document.createElement("th");i.textContent=f,g.appendChild(i)}u.appendChild(g),b.appendChild(u);const v=document.createElement("tbody");for(const f of h){const i=document.createElement("tr"),d=document.createElement("td");d.style.fontFamily="monospace",d.style.fontSize="12px",d.textContent=f.key,i.appendChild(d);const m=document.createElement("td");m.style.fontSize="12px";for(const a of f.tags){const r=document.createElement("span");r.className="badge",r.style.marginRight="4px",r.style.fontSize="11px",r.textContent=a,m.appendChild(r)}i.appendChild(m);const c=document.createElement("td");if(c.style.fontSize="12px",c.style.color="var(--dm-text-muted, #6b7280)",f.expiresAt===null)c.textContent="no expiry";else{const a=f.expiresAt-Date.now();a<=0?c.textContent="expired":a<6e4?c.textContent=`${Math.round(a/1e3)}s`:a<36e5?c.textContent=`${Math.round(a/6e4)}m`:c.textContent=`${Math.round(a/36e5)}h`}i.appendChild(c),v.appendChild(i)}b.appendChild(v),t.appendChild(b)}
1
+ import{api as v,apiRequest as b}from"../api.js";export const settingsView={templateUrl:"/admin/js/templates/settings.html",async onMount(e){E.tabs(e.find("#settings-tabs").get(0));const p=E.loader(e.get(0),{type:"dots"}),l=await v.settings.get().catch(()=>({}));p.destroy(),e.find("#field-site-title").val(l.title||""),e.find("#field-tagline").val(l.tagline||""),e.find("#field-spacer-size").val(l.layoutOptions?.spacerSize??40),e.find("#field-seo-title").val(l.seo?.defaultTitle||""),e.find("#field-seo-separator").val(l.seo?.titleSeparator||" | "),e.find("#field-seo-desc").val(l.seo?.defaultDescription||""),e.find("#field-footer-copy").val(l.footer?.copyright||""),e.find("#field-footer-motion-toggle").prop("checked",l.footer?.showMotionToggle!==!1),e.find("#field-social-twitter").val(l.social?.twitter||""),e.find("#field-social-facebook").val(l.social?.facebook||""),e.find("#field-social-instagram").val(l.social?.instagram||""),e.find("#field-social-linkedin").val(l.social?.linkedin||""),e.find("#field-social-github").val(l.social?.github||""),e.find("#field-social-youtube").val(l.social?.youtube||""),e.find("#field-smtp-host").val(l.smtp?.host||""),e.find("#field-smtp-port").val(l.smtp?.port||587),e.find("#field-smtp-user").val(l.smtp?.user||""),e.find("#field-smtp-pass").val(l.smtp?.pass||""),e.find("#field-smtp-secure").prop("checked",l.smtp?.secure||!1),e.find("#field-smtp-from-address").val(l.smtp?.fromAddress||""),e.find("#field-smtp-from-name").val(l.smtp?.fromName||""),y(l.smtp?.host||""),e.find("#field-smtp-host").on("input",function(){y(this.value)});const c=l.backToTop||{};e.find("#field-btt-enabled").prop("checked",c.enabled!==!1),e.find("#field-btt-threshold").val(c.scrollThreshold??300),e.find("#field-btt-position").val(c.position||"bottom-right"),e.find("#field-btt-offset").val(c.offset??32),e.find("#field-btt-bottom-offset").val(c.bottomOffset??c.offset??32),e.find("#field-btt-label").val(c.label||""),e.find("#field-btt-smooth").prop("checked",c.smooth!==!1);const i=l.cookieConsent||{};e.find("#field-cc-enabled").prop("checked",i.enabled!==!1),e.find("#field-cc-message").val(i.message||""),e.find("#field-cc-accept-all").val(i.acceptAllText||"Accept All"),e.find("#field-cc-reject-all").val(i.rejectAllText||"Reject All"),e.find("#field-cc-customize").val(i.customizeText||"Customize"),e.find("#field-cc-save-prefs").val(i.savePreferencesText||"Save Preferences"),e.find("#field-cc-privacy-text").val(i.privacyPolicyText||"Privacy Policy"),e.find("#field-cc-privacy-url").val(i.privacyPolicyUrl||""),e.find("#field-cc-cookie-text").val(i.cookiePolicyText||"Cookie Policy"),e.find("#field-cc-cookie-url").val(i.cookiePolicyUrl||""),e.find("#field-cc-position").val(i.position||"bottom"),e.find("#field-cc-layout").val(i.layout||"bar"),e.find("#field-cc-theme").val(i.theme||"dark"),e.find("#field-cc-show-functional").prop("checked",i.showFunctional!==!1),e.find("#field-cc-show-analytics").prop("checked",i.showAnalytics!==!1),e.find("#field-cc-show-marketing").prop("checked",i.showMarketing!==!1),e.find("#field-cc-version").val(i.consentVersion||"1.0");const m=l.breadcrumbs||{};e.find("#field-breadcrumbs-enabled").prop("checked",m.enabled===!0),e.find("#field-breadcrumbs-home-label").val(m.homeLabel||"Home"),e.find("#field-breadcrumbs-position").val(m.position||"TL"),e.find("#field-bc-offset-x").val(m.offsetX??8),e.find("#field-bc-offset-y").val(m.offsetY??8);function u(t){e.find(".bc-pos-btn").each(function(){const s=this.dataset.pos===t;this.style.background=s?"var(--dm-primary, #5b8cff)":"",this.style.color=s?"var(--dm-primary-text)":""})}u(m.position||"TL"),e.find(".bc-pos-btn").on("click",function(){const t=this.dataset.pos;e.find("#field-breadcrumbs-position").val(t),u(t)});let n=null;try{const{css:t}=await b("/settings/custom-css");e.find("#field-custom-css").val(t||""),E.editor&&(n=E.editor(e.find("#field-custom-css").get(0),{mode:"code",language:"css",lineNumbers:!0,showToolbar:!1,minHeight:420,placeholder:"/* Add your custom CSS here */",characterCount:!0}))}catch{}n&&n._editorEl&&n._editorEl.addEventListener("keydown",t=>{if(!t.ctrlKey&&!t.metaKey)return;if(t.key==="s"){t.preventDefault(),e.find("#save-css-btn").get(0)?.click();return}const s=n._editorEl,a=s.selectionStart!==s.selectionEnd;if((t.key==="c"||t.key==="x")&&!a){t.preventDefault();const d=s.value,o=s.selectionStart,r=d.lastIndexOf(`
2
+ `,o-1)+1,h=d.indexOf(`
3
+ `,o),g=h===-1?d.slice(r):d.slice(r,h+1);if(navigator.clipboard.writeText(g),t.key==="x"){const k=d.slice(0,r),x=h===-1?"":d.slice(h+1);s.value=k+x,s.selectionStart=s.selectionEnd=r,s.dispatchEvent(new Event("input",{bubbles:!0})),E.toast("Line cut",{type:"info",duration:1500})}else E.toast("Line copied",{type:"info",duration:1500})}}),e.find("#save-settings-btn").on("click",async()=>{const t={title:e.find("#field-site-title").val().trim(),tagline:e.find("#field-tagline").val().trim(),layoutOptions:{spacerSize:parseInt(e.find("#field-spacer-size").val(),10)||40},seo:{defaultTitle:e.find("#field-seo-title").val().trim(),titleSeparator:e.find("#field-seo-separator").val()||" | ",defaultDescription:e.find("#field-seo-desc").val().trim()},footer:{copyright:e.find("#field-footer-copy").val().trim(),showMotionToggle:e.find("#field-footer-motion-toggle").prop("checked"),links:l.footer?.links||[]},social:{twitter:e.find("#field-social-twitter").val().trim(),facebook:e.find("#field-social-facebook").val().trim(),instagram:e.find("#field-social-instagram").val().trim(),linkedin:e.find("#field-social-linkedin").val().trim(),github:e.find("#field-social-github").val().trim(),youtube:e.find("#field-social-youtube").val().trim()},smtp:{host:e.find("#field-smtp-host").val().trim(),port:parseInt(e.find("#field-smtp-port").val(),10)||587,user:e.find("#field-smtp-user").val().trim(),pass:e.find("#field-smtp-pass").val(),secure:e.find("#field-smtp-secure").prop("checked"),fromAddress:e.find("#field-smtp-from-address").val().trim(),fromName:e.find("#field-smtp-from-name").val().trim()},backToTop:{enabled:e.find("#field-btt-enabled").prop("checked"),scrollThreshold:parseInt(e.find("#field-btt-threshold").val(),10)||300,position:e.find("#field-btt-position").val()||"bottom-right",offset:parseInt(e.find("#field-btt-offset").val(),10)||32,bottomOffset:parseInt(e.find("#field-btt-bottom-offset").val(),10)||32,label:e.find("#field-btt-label").val().trim(),smooth:e.find("#field-btt-smooth").prop("checked")},cookieConsent:{enabled:e.find("#field-cc-enabled").prop("checked"),message:e.find("#field-cc-message").val().trim(),acceptAllText:e.find("#field-cc-accept-all").val().trim(),rejectAllText:e.find("#field-cc-reject-all").val().trim(),customizeText:e.find("#field-cc-customize").val().trim(),savePreferencesText:e.find("#field-cc-save-prefs").val().trim(),privacyPolicyText:e.find("#field-cc-privacy-text").val().trim(),privacyPolicyUrl:e.find("#field-cc-privacy-url").val().trim(),cookiePolicyText:e.find("#field-cc-cookie-text").val().trim(),cookiePolicyUrl:e.find("#field-cc-cookie-url").val().trim(),position:e.find("#field-cc-position").val(),layout:e.find("#field-cc-layout").val(),theme:e.find("#field-cc-theme").val(),showFunctional:e.find("#field-cc-show-functional").prop("checked"),showAnalytics:e.find("#field-cc-show-analytics").prop("checked"),showMarketing:e.find("#field-cc-show-marketing").prop("checked"),consentVersion:e.find("#field-cc-version").val().trim()||"1.0"}},s=e.find("#save-settings-btn");s.prop("disabled",!0);try{await v.settings.save(t),E.toast("Settings saved.",{type:"success"})}catch{E.toast("Failed to save settings.",{type:"error"})}finally{s.prop("disabled",!1)}}),e.find("#send-test-email-btn").on("click",async()=>{const t=e.find("#field-test-email-to").val().trim(),s=e.find("#test-email-result").get(0),a=e.find("#send-test-email-btn");a.prop("disabled",!0),s&&(s.textContent="Sending\u2026",s.style.color="");try{const d=await b("/settings/test-email",{method:"POST",body:JSON.stringify({to:t||void 0})});s&&(s.textContent=d.message||"Test email sent.",s.style.color="var(--dm-success,#4ade80)")}catch(d){s&&(s.textContent=d.message||"Failed to send test email.",s.style.color="var(--dm-danger,#f87171)")}finally{a.prop("disabled",!1)}}),e.find("#save-breadcrumbs-btn").on("click",async()=>{const t=e.find("#save-breadcrumbs-btn");t.prop("disabled",!0);try{const s={enabled:e.find("#field-breadcrumbs-enabled").prop("checked"),homeLabel:e.find("#field-breadcrumbs-home-label").val().trim()||"Home",position:e.find("#field-breadcrumbs-position").val()||"TL",offsetX:parseInt(e.find("#field-bc-offset-x").val(),10)||8,offsetY:parseInt(e.find("#field-bc-offset-y").val(),10)||8};await v.settings.save({breadcrumbs:s}),l.breadcrumbs=s,E.toast("Breadcrumbs settings saved.",{type:"success"})}catch{E.toast("Failed to save breadcrumbs settings.",{type:"error"})}finally{t.prop("disabled",!1)}}),e.find("#save-css-btn").on("click",async()=>{const t=n?n.getValue():e.find("#field-custom-css").val(),s=e.find("#save-css-btn");s.prop("disabled",!0);try{await b("/settings/custom-css",{method:"PUT",body:JSON.stringify({css:t})}),E.toast("Custom CSS saved.",{type:"success"})}catch(a){E.toast(a.message||"Failed to save CSS.",{type:"error"})}finally{s.prop("disabled",!1)}});async function f(){try{const t=await b("/cache/status"),s=t.enabled?`enabled (${t.driver})`:"disabled",a=t.maxItems?` \xB7 ${t.size} / ${t.maxItems} entries`:"";e.find("#cache-status").text(s+a)}catch{e.find("#cache-status").text("unknown")}try{const{entries:t}=await b("/cache/keys");w(e.find("#cache-keys-table"),t),e.find("#cache-key-count").text(`(${t.length})`)}catch(t){e.find("#cache-keys-table").get(0).textContent="Failed to load keys: "+t.message}}await f(),e.find("#btn-clear-cache").on("click",async()=>{if(!await E.confirm("Clear the entire response cache?"))return;const t=e.find("#btn-clear-cache");t.prop("disabled",!0);try{await b("/cache/clear",{method:"POST"}),E.toast("Cache cleared.",{type:"success"}),await f()}catch(s){E.toast(s.message||"Failed to clear cache.",{type:"error"})}finally{t.prop("disabled",!1)}}),e.find("#btn-refresh-cache-keys").on("click",f)}};function y(e){const p=document.getElementById("smtp-unconfigured-notice");p&&(p.style.display=String(e||"").trim()?"none":"",p.style.display===""&&Domma.icons.scan(p))}function w(e,p){const l=e.get(0);if(!l)return;for(;l.firstChild;)l.removeChild(l.firstChild);if(!p.length){const f=document.createElement("p");f.className="text-muted",f.style.fontSize=".875rem",f.textContent="Cache is empty.",l.appendChild(f);return}const c=[...p].reverse(),i=document.createElement("table");i.className="table table-compact";const m=document.createElement("thead"),u=document.createElement("tr");for(const f of["Key","Tags","Expires"]){const t=document.createElement("th");t.textContent=f,u.appendChild(t)}m.appendChild(u),i.appendChild(m);const n=document.createElement("tbody");for(const f of c){const t=document.createElement("tr"),s=document.createElement("td");s.style.fontFamily="monospace",s.style.fontSize="12px",s.textContent=f.key,t.appendChild(s);const a=document.createElement("td");a.style.fontSize="12px";for(const o of f.tags){const r=document.createElement("span");r.className="badge",r.style.marginRight="4px",r.style.fontSize="11px",r.textContent=o,a.appendChild(r)}t.appendChild(a);const d=document.createElement("td");if(d.style.fontSize="12px",d.style.color="var(--dm-text-muted, #6b7280)",f.expiresAt===null)d.textContent="no expiry";else{const o=f.expiresAt-Date.now();o<=0?d.textContent="expired":o<6e4?d.textContent=`${Math.round(o/1e3)}s`:o<36e5?d.textContent=`${Math.round(o/6e4)}m`:d.textContent=`${Math.round(o/36e5)}h`}t.appendChild(d),n.appendChild(t)}i.appendChild(n),l.appendChild(i)}
@@ -0,0 +1 @@
1
+ import{api as T}from"../api.js";import{populateThemeSelect as w}from"../lib/themes.js";const se=[{value:"#site-navbar",label:"Navigation bar"},{value:"#site-footer",label:"Footer"},{value:".site-content",label:"Page content area"},{value:".page-body",label:"Page body (inside the container)"},{value:".site-sidebar",label:"Sidebar"},{value:".card",label:"Every card"},{value:".cms-form-embed",label:"Embedded forms"},{value:".cms-collection",label:"Collection displays"}];export const themeView={templateUrl:"/admin/js/templates/theme.html",async onMount(r){E.tabs(r.find("#theme-tabs").get(0));const F=E.loader(r.get(0),{type:"dots"});let d,R;try{[d,R]=await Promise.all([T.theme.get(),T.theme.catalog().catch(()=>({themes:[],tokens:[]}))])}catch(e){F.destroy(),E.toast(`Could not load theme settings: ${e.message||e}`,{type:"error"});return}F.destroy();const z=R.tokens||[],A=new Map;async function K(e){if(!e)return null;if(A.has(e))return A.get(e);const t=await T.theme.themeTokens(e).catch(()=>null),n=t&&t.tokens?t.tokens:null;return A.set(e,n),n}function $(){const e=r.find("#theme-auto-enabled").prop("checked");return d.baseTheme||(e?S.val():f.val())||d.theme}const V=["--dm-card-bg","--dm-surface"],_=[...V,"--dm-text","--dm-primary","--dm-background"];function H(e){const t=document.createElement("span");return t.style.cssText="display:inline-block;width:.7rem;height:.7rem;border-radius:2px;border:1px solid var(--dm-border);vertical-align:middle;margin-right:.25rem;",/^#[0-9a-f]{3,8}$/i.test(e)?t.style.background=e:t.style.background="transparent",t}async function I(e){const t=e.querySelector(".ov-theme-diff");if(!t)return;for(;t.firstChild;)t.removeChild(t.firstChild);const n=e.querySelector(".ov-theme")?.value;if(!n)return;const o=$();if(n===o){const l=document.createElement("p");l.className="form-hint text-muted",l.style.cssText="margin:.4rem 0 0;font-size:.8rem;",l.textContent="That is already the site theme, so this changes nothing on its own. Set a token below instead.",t.appendChild(l);return}const[a,s]=await Promise.all([K(o),K(n)]);if(!a||!s)return;const c=Object.keys(s).filter(l=>String(a[l]??"").toLowerCase()!==String(s[l]).toLowerCase()),i=V.filter(l=>c.includes(l)),m=document.createElement("div");m.style.cssText="margin-top:.5rem;padding:.5rem .6rem;border-radius:var(--dm-radius-md);background:var(--dm-surface-raised);font-size:.8rem;";const h=document.createElement("p");if(h.style.cssText="margin:0 0 .35rem;",h.textContent=`Changes ${c.length} of ${Object.keys(s).length} tokens here.`,m.appendChild(h),!i.length){const l=document.createElement("p");l.style.cssText="margin:0 0 .35rem;color:var(--dm-warning);",l.textContent="Both themes use the same surface colour, so this region keeps the fill it has now. Set --dm-card-bg (a card) or --dm-surface (most other regions) below to change it.",m.appendChild(l)}const u=document.createElement("div");u.style.cssText="display:flex;flex-wrap:wrap;gap:.15rem .75rem;";const v=[..._.filter(l=>c.includes(l)),...c.filter(l=>!_.includes(l))];for(const l of v.slice(0,6)){const p=document.createElement("span");p.style.cssText="white-space:nowrap;color:var(--dm-text-muted);",p.appendChild(H(a[l]||""));const C=document.createElement("span");C.textContent=`${l} `,p.appendChild(C),p.appendChild(H(s[l])),u.appendChild(p)}if(v.length>6){const l=document.createElement("span");l.style.cssText="color:var(--dm-text-muted);",l.textContent=`+${v.length-6} more`,u.appendChild(l)}m.appendChild(u),t.appendChild(m)}let b=Array.isArray(d.overrides)?d.overrides.map(G):[];const f=r.find("#theme-frontend"),B=r.find("#theme-admin");w(f.get(0)),w(B.get(0),{includeAdmin:!0}),f.val(d.baseTheme||d.theme||"charcoal-dark"),B.val(d.adminTheme||"charcoal-dark"),d.baseTheme&&(f.prop("disabled",!0),r.find("#theme-auto-enabled").prop("disabled",!0),r.find("#theme-auto-roller-hint").show());const S=r.find("#theme-day"),D=r.find("#theme-night");w(S.get(0)),w(D.get(0));const g=d.autoTheme||{};S.val(g.dayTheme||"charcoal-light"),D.val(g.nightTheme||"charcoal-dark"),r.find("#theme-day-start").val(g.dayStart||"07:00"),r.find("#theme-night-start").val(g.nightStart||"19:00");const U=r.find("#theme-auto-enabled"),W=r.find("#theme-auto-fields");d.baseTheme||(U.prop("checked",!!g.enabled),g.enabled&&(W.show(),f.prop("disabled",!0)),U.on("change",function(){W.toggle(this.checked),f.prop("disabled",this.checked)})),r.find("#theme-font-family").val(d.font?.family||"Roboto"),r.find("#theme-font-size").val(String(d.font?.size??16));function G(e){return{id:e.id||`ov-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,label:e.label||"",enabled:e.enabled!==!1,scope:e.scope||"",theme:e.theme||"",tokens:{...e.tokens||{}},css:e.css||""}}const y=document.getElementById("theme-overrides"),ee=document.getElementById("theme-overrides-empty");function L(){for(;y.firstChild;)y.removeChild(y.firstChild);ee.style.display=b.length?"none":"",b.forEach((e,t)=>y.appendChild(te(e,t))),Domma.icons.scan("#theme-overrides")}function te(e,t){const n=document.createElement("div");n.className="card mb-3",n.dataset.i=String(t);const o=document.createElement("div");o.className="card-header";const a=document.createElement("h3");a.className="m-0",a.style.fontSize="1rem",a.textContent=e.label||e.scope||`Override ${t+1}`,o.appendChild(a);const s=document.createElement("div");s.style.cssText="display:flex;gap:.5rem;align-items:center;";const c=document.createElement("label");c.style.cssText="display:flex;align-items:center;gap:.35rem;font-size:.85rem;cursor:pointer;";const i=document.createElement("input");i.type="checkbox",i.className="form-check ov-enabled",i.checked=e.enabled,c.appendChild(i),c.appendChild(document.createTextNode("Enabled")),s.appendChild(c);const m=document.createElement("button");m.type="button",m.className="btn btn-ghost btn-sm ov-remove",m.textContent="Remove",s.appendChild(m),o.appendChild(s),n.appendChild(o);const h=document.createElement("div");h.className="card-body";const u=document.createElement("div");u.className="row mb-3",u.appendChild(P("col-4","Name",X("ov-label",e.label,"Navbar island"))),u.appendChild(P("col-8","Target selector",ne(e.scope),"The region to re-theme. Everything inside it follows.")),h.appendChild(u);const v=document.createElement("div");v.className="row mb-3";const l=document.createElement("select");l.className="form-select ov-theme",w(l,{includeDefault:!0,defaultLabel:"Keep the site theme"}),l.value=e.theme||"",v.appendChild(P("col-6","Adopt a theme here",l,"Copies every token of that theme onto this region.")),h.appendChild(v);const p=document.createElement("div");p.className="ov-theme-diff",h.appendChild(p);const C=document.createElement("div");C.className="ov-tokens mb-2",Object.entries(e.tokens||{}).forEach(([oe,ae])=>{C.appendChild(J(oe,ae))}),h.appendChild(Q("Token overrides",C));const q=document.createElement("button");q.type="button",q.className="btn btn-ghost btn-sm ov-add-token",q.textContent="+ Add token",h.appendChild(q);const x=document.createElement("details");x.style.marginTop="1rem";const j=document.createElement("summary");j.style.cssText="cursor:pointer;font-size:.85rem;",j.textContent="Custom CSS for this override",x.appendChild(j);const k=document.createElement("textarea");k.className="form-input ov-css",k.rows=4,k.placeholder="#site-navbar .brand { letter-spacing: .04em; }",k.value=e.css||"",k.style.marginTop=".5rem",x.appendChild(k);const O=document.createElement("p");return O.className="form-hint text-muted",O.style.cssText="font-size:.8rem;margin-top:.35rem;",O.textContent="Appended verbatim after the generated rule - you own these selectors. Prefer tokens: they survive a Domma upgrade, hand-written rules fight the cascade.",x.appendChild(O),h.appendChild(x),n.appendChild(h),setTimeout(()=>I(n),0),n}function J(e,t){const n=document.createElement("div");n.className="ov-token-row",n.style.cssText="display:flex;gap:.5rem;align-items:center;margin-bottom:.5rem;";const o=document.createElement("select");if(o.className="form-select ov-token-name",o.style.flex="1 1 auto",z.length){for(const i of z){const m=document.createElement("option");m.value=i,m.textContent=i,o.appendChild(m)}o.value=e||z[0]}else{const i=document.createElement("option");i.value=e||"",i.textContent=e||"(no tokens found)",o.appendChild(i)}const a=document.createElement("input");a.type="text",a.className="form-input ov-token-value",a.style.flex="0 0 150px",a.placeholder="#0096c7",a.value=t||"";const s=document.createElement("input");s.type="color",s.className="ov-token-swatch",s.style.cssText="flex:0 0 38px;height:34px;padding:0;border:1px solid var(--dm-border);border-radius:var(--dm-radius-md);background:none;cursor:pointer;",/^#[0-9a-f]{6}$/i.test(t||"")&&(s.value=t),s.addEventListener("input",()=>{a.value=s.value,a.dispatchEvent(new Event("input",{bubbles:!0}))});const c=document.createElement("button");return c.type="button",c.className="btn btn-ghost btn-sm ov-token-remove",c.textContent="\xD7",n.append(o,a,s,c),n}function P(e,t,n,o){const a=document.createElement("div");return a.className=e,a.appendChild(Q(t,n,o)),a}function Q(e,t,n){const o=document.createDocumentFragment(),a=document.createElement("label");if(a.className="form-label",a.textContent=e,o.appendChild(a),o.appendChild(t),n){const s=document.createElement("p");s.className="form-hint text-muted",s.style.cssText="margin-top:.35rem;font-size:.8rem;",s.textContent=n,o.appendChild(s)}return o}function X(e,t,n){const o=document.createElement("input");return o.type="text",o.className=`form-input ${e}`,o.value=t||"",o.placeholder=n||"",o}function ne(e){const t=document.createElement("div"),n=X("ov-scope",e,"#site-navbar");if(n.setAttribute("list","theme-scope-options"),t.appendChild(n),!document.getElementById("theme-scope-options")){const o=document.createElement("datalist");o.id="theme-scope-options";for(const a of se){const s=document.createElement("option");s.value=a.value,s.label=a.label,o.appendChild(s)}t.appendChild(o)}return t}function N(){const e=y.querySelectorAll(".card"),t=[];return e.forEach((n,o)=>{const a={};n.querySelectorAll(".ov-token-row").forEach(s=>{const c=s.querySelector(".ov-token-name")?.value?.trim(),i=s.querySelector(".ov-token-value")?.value?.trim();c&&i&&(a[c]=i)}),t.push({id:b[o]?.id,label:n.querySelector(".ov-label")?.value?.trim()||"",enabled:!!n.querySelector(".ov-enabled")?.checked,scope:n.querySelector(".ov-scope")?.value?.trim()||"",theme:n.querySelector(".ov-theme")?.value||"",tokens:a,css:n.querySelector(".ov-css")?.value||""})}),b=t,t}r.on("click",".ov-remove",function(){const e=Number(this.closest(".card").dataset.i);N(),b.splice(e,1),L()}),r.on("change",".ov-theme",function(){I(this.closest(".card"))}),r.on("change","#theme-frontend, #theme-auto-enabled, #theme-day",()=>{y.querySelectorAll(".card").forEach(I)}),r.on("click",".ov-add-token",function(){this.closest(".card").querySelector(".ov-tokens").appendChild(J("",""))}),r.on("click",".ov-token-remove",function(){this.closest(".ov-token-row").remove()}),r.find("#theme-add-override").on("click",()=>{N(),b.push(G({label:"",scope:"",enabled:!0})),L()}),L();async function M(){const e=document.getElementById("theme-css-preview"),t={...Y(),overrides:N()};try{const n=await T.theme.compile(t);e.textContent=n.css?.trim()||"(no overrides - nothing is injected)",e.style.color=""}catch(n){e.textContent=n.message||String(n),e.style.color="var(--dm-danger)"}}r.find("#theme-refresh-css").on("click",M);function Y(){const e=!d.baseTheme&&r.find("#theme-auto-enabled").prop("checked"),t={enabled:e,dayTheme:S.val()||"charcoal-light",nightTheme:D.val()||"charcoal-dark",dayStart:r.find("#theme-day-start").val()||"07:00",nightStart:r.find("#theme-night-start").val()||"19:00"};return{theme:d.baseTheme?d.theme:e?t.dayTheme:f.val(),...d.baseTheme?{baseTheme:d.baseTheme}:{},adminTheme:B.val(),autoTheme:t,font:{family:r.find("#theme-font-family").val()||"Roboto",size:parseInt(r.find("#theme-font-size").val(),10)||16}}}function Z(e){const t=document.getElementById("theme-errors");for(;t.firstChild;)t.removeChild(t.firstChild);if(!e||!e.length){t.style.display="none";return}t.style.display="";for(const n of e){const o=document.createElement("div");o.textContent=n,t.appendChild(o)}t.scrollIntoView({behavior:"smooth",block:"nearest"})}r.find("#theme-save-btn").on("click",async function(){Z(null);const e={...Y(),overrides:N()};this.disabled=!0;try{d=await T.theme.save(e),Domma.theme.set(e.adminTheme),E.toast("Theme saved.",{type:"success"}),await M()}catch(t){const n=t.validation||[t.message||"Could not save the theme."];Z(n),E.toast("Theme not saved.",{type:"error"})}finally{this.disabled=!1}}),await M()}};
package/bin/cli.js CHANGED
@@ -55,6 +55,7 @@ if (flags.has('--help') || args.includes('-h')) {
55
55
  --no-backup Skip creating a backup before updating
56
56
  --no-install Skip npm install after updating
57
57
  --dry-run Preview what would change without writing anything
58
+ --force Re-install even when the installed version already matches
58
59
 
59
60
  Examples:
60
61
  npx domma-cms my-blog
@@ -62,6 +63,7 @@ if (flags.has('--help') || args.includes('-h')) {
62
63
  npx domma-cms update
63
64
  npx domma-cms update --dry-run
64
65
  npx domma-cms update --yes --no-backup
66
+ npx domma-cms update --yes --force
65
67
  `);
66
68
  process.exit(0);
67
69
  }
package/bin/update.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * Domma CMS - Project Update
4
- * Usage: npx domma-cms update [--yes] [--no-backup] [--no-install] [--dry-run]
4
+ * Usage: npx domma-cms update [--yes] [--no-backup] [--no-install] [--dry-run] [--force]
5
5
  *
6
6
  * Run from inside an existing Domma CMS project directory.
7
7
  * Pulls upstream changes from the installed CLI package into the project,
@@ -115,6 +115,12 @@ export default async function update(_positional, flags) {
115
115
  const isDryRun = flags.has('--dry-run');
116
116
  const skipConfirm = flags.has('--yes');
117
117
  const skipBackup = flags.has('--no-backup');
118
+ // Re-install the SAME version. The version check below exists to make a
119
+ // routine `update` cheap, but it also means a bad build that shipped under
120
+ // a good version number cannot be corrected by re-rolling it - the updater
121
+ // reports "already up to date" and copies nothing. --force is how you
122
+ // repair an install whose files are wrong but whose .domma marker is right.
123
+ const force = flags.has('--force');
118
124
  const skipInstall = flags.has('--no-install');
119
125
 
120
126
  const cwd = process.cwd();
@@ -184,11 +190,16 @@ export default async function update(_positional, flags) {
184
190
  info(`Available: ${incomingVersion}`);
185
191
  console.log('');
186
192
 
187
- if (installedVersion !== 'unknown' && installedVersion === incomingVersion) {
193
+ if (installedVersion !== 'unknown' && installedVersion === incomingVersion && !force) {
188
194
  info('✓ Already up to date.');
195
+ info(' (re-install these files anyway with --force)');
189
196
  console.log('');
190
197
  process.exit(0);
191
198
  }
199
+ if (force && installedVersion === incomingVersion) {
200
+ warn(`Re-installing ${incomingVersion} over itself (--force).`);
201
+ console.log('');
202
+ }
192
203
 
193
204
  // -------------------------------------------------------------------------
194
205
  // 3. Dry-run mode - preview changes and exit
@@ -52,6 +52,18 @@
52
52
  "url": "#/menus",
53
53
  "icon": "menu",
54
54
  "permission": "menus"
55
+ },
56
+ {
57
+ "text": "Context Menus",
58
+ "url": "#/context-menus",
59
+ "icon": "menu",
60
+ "permission": "context-menus"
61
+ },
62
+ {
63
+ "text": "Theme",
64
+ "url": "#/theme",
65
+ "icon": "droplet",
66
+ "permission": "theme"
55
67
  }
56
68
  ]
57
69
  },
@@ -155,6 +167,12 @@
155
167
  "url": "#/api-tokens",
156
168
  "icon": "key",
157
169
  "permission": "api-tokens"
170
+ },
171
+ {
172
+ "text": "Search",
173
+ "url": "#/search",
174
+ "icon": "search",
175
+ "permission": "settings"
158
176
  }
159
177
  ]
160
178
  },
@@ -168,17 +186,61 @@
168
186
  "url": "",
169
187
  "icon": "book",
170
188
  "items": [
171
- {"text": "Pages", "url": "#/docs/usage/pages", "icon": "file-text"},
172
- {"text": "Media", "url": "#/docs/usage/media", "icon": "image"},
173
- {"text": "Navigation", "url": "#/docs/usage/navigation", "icon": "menu"},
174
- {"text": "DConfig", "url": "#/docs/usage/dconfig", "icon": "settings"},
175
- {"text": "Shortcodes", "url": "#/docs/usage/shortcodes", "icon": "code"},
176
- {"text": "Site Settings", "url": "#/docs/usage/site-settings", "icon": "settings"},
177
- {"text": "Plugins", "url": "#/docs/usage/plugins", "icon": "package"},
178
- {"text": "Users & Roles", "url": "#/docs/usage/users-roles", "icon": "users"},
179
- {"text": "Views", "url": "#/docs/usage/views", "icon": "eye"},
180
- {"text": "Actions", "url": "#/docs/usage/actions", "icon": "zap"},
181
- {"text": "CTA Shortcode", "url": "#/docs/usage/cta-shortcode", "icon": "zap"}
189
+ {
190
+ "text": "Pages",
191
+ "url": "#/docs/usage/pages",
192
+ "icon": "file-text"
193
+ },
194
+ {
195
+ "text": "Media",
196
+ "url": "#/docs/usage/media",
197
+ "icon": "image"
198
+ },
199
+ {
200
+ "text": "Navigation",
201
+ "url": "#/docs/usage/navigation",
202
+ "icon": "menu"
203
+ },
204
+ {
205
+ "text": "DConfig",
206
+ "url": "#/docs/usage/dconfig",
207
+ "icon": "settings"
208
+ },
209
+ {
210
+ "text": "Shortcodes",
211
+ "url": "#/docs/usage/shortcodes",
212
+ "icon": "code"
213
+ },
214
+ {
215
+ "text": "Site Settings",
216
+ "url": "#/docs/usage/site-settings",
217
+ "icon": "settings"
218
+ },
219
+ {
220
+ "text": "Plugins",
221
+ "url": "#/docs/usage/plugins",
222
+ "icon": "package"
223
+ },
224
+ {
225
+ "text": "Users & Roles",
226
+ "url": "#/docs/usage/users-roles",
227
+ "icon": "users"
228
+ },
229
+ {
230
+ "text": "Views",
231
+ "url": "#/docs/usage/views",
232
+ "icon": "eye"
233
+ },
234
+ {
235
+ "text": "Actions",
236
+ "url": "#/docs/usage/actions",
237
+ "icon": "zap"
238
+ },
239
+ {
240
+ "text": "CTA Shortcode",
241
+ "url": "#/docs/usage/cta-shortcode",
242
+ "icon": "zap"
243
+ }
182
244
  ]
183
245
  },
184
246
  {
@@ -235,17 +297,61 @@
235
297
  "url": "",
236
298
  "icon": "code",
237
299
  "items": [
238
- {"text": "Authentication", "url": "#/docs/api/authentication", "icon": "shield"},
239
- {"text": "Pages", "url": "#/docs/api/pages", "icon": "file-text"},
240
- {"text": "Settings", "url": "#/docs/api/settings", "icon": "settings"},
241
- {"text": "Layouts", "url": "#/docs/api/layouts", "icon": "layout"},
242
- {"text": "Navigation", "url": "#/docs/api/navigation", "icon": "menu"},
243
- {"text": "Media", "url": "#/docs/api/media", "icon": "image"},
244
- {"text": "Users", "url": "#/docs/api/users", "icon": "users"},
245
- {"text": "Plugins", "url": "#/docs/api/plugins", "icon": "package"},
246
- {"text": "Collections", "url": "#/docs/api/collections", "icon": "database"},
247
- {"text": "Views API", "url": "#/docs/api/views", "icon": "eye"},
248
- {"text": "Actions API", "url": "#/docs/api/actions", "icon": "zap"}
300
+ {
301
+ "text": "Authentication",
302
+ "url": "#/docs/api/authentication",
303
+ "icon": "shield"
304
+ },
305
+ {
306
+ "text": "Pages",
307
+ "url": "#/docs/api/pages",
308
+ "icon": "file-text"
309
+ },
310
+ {
311
+ "text": "Settings",
312
+ "url": "#/docs/api/settings",
313
+ "icon": "settings"
314
+ },
315
+ {
316
+ "text": "Layouts",
317
+ "url": "#/docs/api/layouts",
318
+ "icon": "layout"
319
+ },
320
+ {
321
+ "text": "Navigation",
322
+ "url": "#/docs/api/navigation",
323
+ "icon": "menu"
324
+ },
325
+ {
326
+ "text": "Media",
327
+ "url": "#/docs/api/media",
328
+ "icon": "image"
329
+ },
330
+ {
331
+ "text": "Users",
332
+ "url": "#/docs/api/users",
333
+ "icon": "users"
334
+ },
335
+ {
336
+ "text": "Plugins",
337
+ "url": "#/docs/api/plugins",
338
+ "icon": "package"
339
+ },
340
+ {
341
+ "text": "Collections",
342
+ "url": "#/docs/api/collections",
343
+ "icon": "database"
344
+ },
345
+ {
346
+ "text": "Views API",
347
+ "url": "#/docs/api/views",
348
+ "icon": "eye"
349
+ },
350
+ {
351
+ "text": "Actions API",
352
+ "url": "#/docs/api/actions",
353
+ "icon": "zap"
354
+ }
249
355
  ]
250
356
  }
251
357
  ]
@@ -253,7 +359,7 @@
253
359
  ],
254
360
  "meta": {
255
361
  "createdAt": "2026-05-25T11:11:15.223Z",
256
- "updatedAt": "2026-06-10T12:43:41.918Z",
362
+ "updatedAt": "2026-09-22T03:39:59.212Z",
257
363
  "bundled": false,
258
364
  "presetOwner": null,
259
365
  "project": null
@@ -14,11 +14,6 @@
14
14
  "bundled": true,
15
15
  "settings": {}
16
16
  },
17
- "site-search": {
18
- "enabled": false,
19
- "bundled": true,
20
- "settings": {}
21
- },
22
17
  "blog": {
23
18
  "enabled": false,
24
19
  "bundled": false,
@@ -26,5 +21,10 @@
26
21
  },
27
22
  "analytics": {
28
23
  "enabled": true
24
+ },
25
+ "mail-reader": {
26
+ "enabled": false,
27
+ "bundled": true,
28
+ "settings": {}
29
29
  }
30
30
  }
@@ -0,0 +1,13 @@
1
+ {
2
+ "enabled": true,
3
+ "placeholder": "Search pages...",
4
+ "keyboardShortcut": true,
5
+ "maxResults": 10,
6
+ "minQueryLength": 2,
7
+ "debounceMs": 300,
8
+ "displayMode": "navbar",
9
+ "position": "bottom-right",
10
+ "offsetX": 32,
11
+ "offsetY": 32,
12
+ "iconOnly": true
13
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "version": 1,
3
+ "theme": "silver-dark",
4
+ "adminTheme": "silver-dark",
5
+ "baseTheme": null,
6
+ "autoTheme": {
7
+ "enabled": false,
8
+ "dayTheme": "charcoal-light",
9
+ "nightTheme": "charcoal-dark",
10
+ "dayStart": "07:00",
11
+ "nightStart": "19:00"
12
+ },
13
+ "font": {
14
+ "family": "Nunito",
15
+ "size": 18
16
+ },
17
+ "overrides": []
18
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-cms",
3
- "version": "0.55.1",
3
+ "version": "0.66.0",
4
4
  "description": "File-based CMS powered by Domma and Fastify. Run npx domma-cms my-site to create a new project.",
5
5
  "type": "module",
6
6
  "main": "server/server.js",
@@ -21,10 +21,15 @@
21
21
  "!config/connections.json",
22
22
  "plugins/",
23
23
  "!plugins/analytics/data",
24
+ "!plugins/contacts/data",
25
+ "!plugins/notes/data",
26
+ "!plugins/todo/data",
24
27
  "!plugins/analytics/stats.json",
25
28
  "!plugins/analytics/daily.json",
26
29
  "!plugins/analytics/lifetime.json",
27
30
  "!plugins/analytics/journeys.json",
31
+ "!plugins/mail-reader/data",
32
+ "!plugins/_lib/data",
28
33
  "scripts/",
29
34
  "docs/",
30
35
  "CHANGELOG.md",
@@ -51,7 +56,7 @@
51
56
  "pro": "node scripts/pro.js"
52
57
  },
53
58
  "engines": {
54
- "node": ">=18.17.0"
59
+ "node": ">=24"
55
60
  },
56
61
  "keywords": [
57
62
  "cms",
@@ -81,10 +86,12 @@
81
86
  "@fastify/rate-limit": "^10.3.0",
82
87
  "@fastify/static": "9.1.1",
83
88
  "bcryptjs": "^3.0.3",
84
- "domma-js": "0.42.0",
89
+ "domma-js": "0.44.0",
85
90
  "dotenv": "^17.2.3",
86
91
  "fastify": "5.8.5",
87
92
  "gray-matter": "^4.0.3",
93
+ "imapflow": "^2.0.5",
94
+ "mailparser": "^3.9.28",
88
95
  "marked": "^15.0.0",
89
96
  "nodemailer": "8.0.5",
90
97
  "sanitize-html": "^2.17.0",
@@ -92,7 +99,8 @@
92
99
  "uuid": "13.0.1"
93
100
  },
94
101
  "optionalDependencies": {
95
- "mongodb": "^6.21.0"
102
+ "mongodb": "^6.21.0",
103
+ "puppeteer-core": "^25.11.0"
96
104
  },
97
105
  "devDependencies": {
98
106
  "esbuild": "^0.27.3"