domma-cms 0.3.0 → 0.5.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 (145) hide show
  1. package/README.md +3 -3
  2. package/admin/css/admin.css +1 -1
  3. package/admin/dist/domma/domma-tools.css +2313 -0
  4. package/admin/dist/domma/domma-tools.min.js +10 -0
  5. package/admin/index.html +4 -0
  6. package/admin/js/api.js +1 -1
  7. package/admin/js/app.js +8 -4
  8. package/admin/js/config/sidebar-config.js +1 -1
  9. package/admin/js/lib/markdown-toolbar.js +18 -10
  10. package/admin/js/templates/action-editor.html +171 -0
  11. package/admin/js/templates/actions-list.html +19 -0
  12. package/admin/js/templates/api-reference.html +1411 -0
  13. package/admin/js/templates/block-editor.html +158 -0
  14. package/admin/js/templates/blocks.html +8 -0
  15. package/admin/js/templates/collection-editor.html +47 -0
  16. package/admin/js/templates/collection-entries.html +3 -0
  17. package/admin/js/templates/collections.html +51 -4
  18. package/admin/js/templates/documentation.html +258 -0
  19. package/{plugins/form-builder/admin → admin/js}/templates/form-editor.html +238 -199
  20. package/{plugins/form-builder/admin → admin/js}/templates/form-submissions.html +30 -30
  21. package/{plugins/form-builder/admin/templates/forms-list.html → admin/js/templates/forms.html} +17 -17
  22. package/admin/js/templates/login.html +29 -4
  23. package/admin/js/templates/my-profile.html +17 -0
  24. package/admin/js/templates/page-editor.html +39 -0
  25. package/admin/js/templates/pages.html +6 -1
  26. package/admin/js/templates/pro-docs.html +259 -0
  27. package/admin/js/templates/role-editor.html +59 -0
  28. package/admin/js/templates/roles.html +10 -0
  29. package/admin/js/templates/settings.html +123 -21
  30. package/admin/js/templates/tutorials.html +81 -0
  31. package/admin/js/templates/user-editor.html +7 -0
  32. package/admin/js/templates/users.html +3 -26
  33. package/admin/js/templates/view-editor.html +201 -0
  34. package/admin/js/templates/view-preview.html +51 -0
  35. package/admin/js/templates/views-list.html +19 -0
  36. package/admin/js/views/action-editor.js +1 -0
  37. package/admin/js/views/actions-list.js +1 -0
  38. package/admin/js/views/api-reference.js +1 -0
  39. package/admin/js/views/block-editor.js +8 -0
  40. package/admin/js/views/blocks.js +4 -0
  41. package/admin/js/views/collection-editor.js +3 -3
  42. package/admin/js/views/collection-entries.js +1 -1
  43. package/admin/js/views/collections.js +1 -1
  44. package/admin/js/views/dashboard.js +1 -1
  45. package/admin/js/views/form-editor.js +8 -0
  46. package/admin/js/views/form-submissions.js +1 -0
  47. package/admin/js/views/forms.js +1 -0
  48. package/admin/js/views/index.js +1 -1
  49. package/admin/js/views/login.js +2 -2
  50. package/admin/js/views/media.js +1 -1
  51. package/admin/js/views/my-profile.js +1 -0
  52. package/admin/js/views/page-editor.js +34 -15
  53. package/admin/js/views/pages.js +5 -5
  54. package/admin/js/views/plugins.js +10 -10
  55. package/admin/js/views/pro-docs.js +1 -0
  56. package/admin/js/views/role-editor.js +1 -0
  57. package/admin/js/views/roles.js +4 -0
  58. package/admin/js/views/settings.js +3 -1
  59. package/admin/js/views/user-editor.js +1 -1
  60. package/admin/js/views/users.js +4 -7
  61. package/admin/js/views/view-editor.js +1 -0
  62. package/admin/js/views/view-preview.js +1 -0
  63. package/admin/js/views/views-list.js +1 -0
  64. package/bin/cli.js +1 -1
  65. package/config/auth.json +1 -0
  66. package/config/connections.json.bak +9 -0
  67. package/config/connections.json.example +9 -0
  68. package/config/plugins.json +19 -29
  69. package/config/server.json +6 -6
  70. package/config/site.json +12 -2
  71. package/package.json +24 -10
  72. package/plugins/example-analytics/stats.json +17 -12
  73. package/plugins/theme-roller/admin/templates/theme-roller.html +71 -0
  74. package/plugins/theme-roller/admin/views/theme-roller-view.js +403 -0
  75. package/plugins/theme-roller/config.js +1 -0
  76. package/plugins/theme-roller/plugin.js +233 -0
  77. package/plugins/theme-roller/plugin.json +31 -0
  78. package/plugins/theme-roller/public/active-theme.css +0 -0
  79. package/plugins/theme-roller/public/inject-head-late.html +1 -0
  80. package/public/css/forms.css +1 -0
  81. package/public/css/site.css +1 -1
  82. package/public/js/forms.js +1 -0
  83. package/public/js/site.js +1 -1
  84. package/scripts/build.js +194 -129
  85. package/scripts/pro.js +254 -0
  86. package/scripts/reset.js +33 -8
  87. package/scripts/seed.js +343 -78
  88. package/scripts/setup.js +1 -0
  89. package/server/middleware/auth.js +136 -120
  90. package/server/routes/api/actions.js +200 -0
  91. package/server/routes/api/auth.js +292 -146
  92. package/server/routes/api/blocks.js +84 -0
  93. package/server/routes/api/collections.js +79 -27
  94. package/{plugins/form-builder/plugin.js → server/routes/api/forms.js} +483 -505
  95. package/server/routes/api/layouts.js +49 -39
  96. package/server/routes/api/media.js +118 -92
  97. package/server/routes/api/navigation.js +40 -36
  98. package/server/routes/api/pages.js +132 -118
  99. package/server/routes/api/plugins.js +6 -3
  100. package/server/routes/api/settings.js +104 -88
  101. package/server/routes/api/users.js +27 -19
  102. package/server/routes/api/views.js +148 -0
  103. package/server/routes/public.js +124 -108
  104. package/server/server.js +269 -181
  105. package/server/services/actions.js +387 -0
  106. package/server/services/adapterRegistry.js +98 -0
  107. package/server/services/adapters/FileAdapter.js +192 -0
  108. package/server/services/adapters/MongoAdapter.js +220 -0
  109. package/server/services/blocks.js +162 -0
  110. package/server/services/collections.js +74 -86
  111. package/server/services/connectionManager.js +102 -0
  112. package/server/services/content.js +312 -307
  113. package/server/services/email.js +126 -0
  114. package/server/services/forms.js +173 -0
  115. package/server/services/markdown.js +1378 -747
  116. package/server/services/permissionRegistry.js +173 -0
  117. package/server/services/presetCollections.js +251 -0
  118. package/server/services/renderer.js +75 -1
  119. package/server/services/roles.js +227 -0
  120. package/server/services/rowAccess.js +104 -0
  121. package/server/services/userProfiles.js +199 -0
  122. package/server/services/users.js +281 -212
  123. package/server/services/views.js +280 -0
  124. package/server/templates/page.html +119 -113
  125. package/plugins/form-builder/admin/templates/form-settings.html +0 -29
  126. package/plugins/form-builder/admin/views/form-editor.js +0 -1444
  127. package/plugins/form-builder/admin/views/form-settings.js +0 -38
  128. package/plugins/form-builder/admin/views/form-submissions.js +0 -295
  129. package/plugins/form-builder/admin/views/forms-list.js +0 -164
  130. package/plugins/form-builder/config.js +0 -9
  131. package/plugins/form-builder/data/forms/consent.json +0 -104
  132. package/plugins/form-builder/data/forms/contact-details.json +0 -99
  133. package/plugins/form-builder/data/forms/contacts.json +0 -66
  134. package/plugins/form-builder/data/forms/feedback.json +0 -130
  135. package/plugins/form-builder/data/submissions/consent.json +0 -13
  136. package/plugins/form-builder/data/submissions/contact-details.json +0 -1
  137. package/plugins/form-builder/data/submissions/contacts.json +0 -26
  138. package/plugins/form-builder/data/submissions/feedback.json +0 -1
  139. package/plugins/form-builder/plugin.json +0 -52
  140. package/plugins/form-builder/public/inject-body.html +0 -352
  141. package/plugins/form-builder/public/inject-head.html +0 -58
  142. package/plugins/form-builder/public/package.json +0 -1
  143. package/scripts/copy-domma.js +0 -48
  144. package/server/services/userTypes.js +0 -167
  145. /package/{plugins/form-builder/public → public/js}/form-logic-engine.js +0 -0
@@ -1 +1 @@
1
- body,button,input,select,textarea{font-family:Roboto,sans-serif}.site-main{min-height:calc(100vh - 60px);padding-top:2rem;padding-bottom:4rem}.site-main.with-sidebar{display:grid;grid-template-columns:260px 1fr;gap:0}.site-sidebar{min-height:100%;border-right:1px solid var(--border-color, rgba(255,255,255,.08))}.site-content{overflow:hidden}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}.container{max-width:860px;margin:0 auto;padding:0 1.5rem}.page-title{font-size:2rem;font-weight:700;margin-bottom:1.5rem;line-height:1.2}.page-body{line-height:1.7;font-size:1rem}.page-body h1,.page-body h2,.page-body h3,.page-body h4{margin-top:2rem;margin-bottom:.75rem;font-weight:600}.page-body h2{font-size:1.5rem}.page-body h3{font-size:1.25rem}.page-body p{margin-bottom:1rem}.page-body ul,.page-body ol{margin-bottom:1rem;padding-left:1.5rem}.page-body a{color:var(--primary, #5b8cff)}.page-body a:hover{text-decoration:underline}.page-body code{font-family:Fira Code,Courier New,monospace;font-size:.9em;background:#ffffff0f;padding:.15em .35em;border-radius:3px}.page-body pre{background:#0000004d;border:1px solid rgba(255,255,255,.08);border-radius:6px;padding:1rem;overflow-x:auto;margin-bottom:1rem}.page-body pre code{background:none;padding:0}.page-body img{max-width:100%;border-radius:6px}.page-body blockquote{border-left:3px solid var(--primary, #5b8cff);margin:1.5rem 0;padding:.75rem 1rem;background:#5b8cff0f;border-radius:0 6px 6px 0}.page-body .card-header h2{margin:0;font-size:1rem;font-weight:600;line-height:1.4}.card[data-collapsible] .card-header{cursor:pointer;user-select:none;display:flex;align-items:center;justify-content:space-between}.card[data-collapsible] .card-header:after{content:"\25be";font-size:1.1em;line-height:1;display:inline-block;transition:transform .25s ease;flex-shrink:0}.card[data-collapsible].is-collapsed .card-header:after{transform:rotate(-90deg)}.card[data-collapsible] .card-body{overflow:hidden;max-height:4000px;opacity:1;transition:max-height .3s ease,opacity .25s ease}.card[data-collapsible].is-collapsed .card-body{max-height:0;opacity:0}.navbar-link span[data-icon],.navbar-link svg,.navbar-dropdown-toggle span[data-icon],.navbar-dropdown-toggle svg,.navbar-dropdown-item span[data-icon],.navbar-dropdown-item svg{width:13px!important;height:13px!important;margin-right:10px!important}.navbar-dropdown-toggle{font-size:var(--dm-font-size-base)}@media(min-width:993px){.navbar-dropdown-toggle{font-size:var(--dm-font-size-sm)}}@media(min-width:1201px){.navbar-dropdown-toggle{font-size:var(--dm-font-size-xs)}}.dm-reduced-motion *,.dm-reduced-motion *:before,.dm-reduced-motion *:after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important;scroll-behavior:auto!important}.page-footer{border-top:1px solid var(--border-color, rgba(255,255,255,.08));padding:1.5rem 0}.footer-inner{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:1rem}.footer-inner p{margin:0;color:var(--text-muted, #888);font-size:.875rem}.footer-links{display:flex;gap:1.25rem}.footer-links a{color:var(--text-muted, #888);font-size:.875rem;text-decoration:none}.footer-links a:hover{color:var(--text, #eee)}.footer-social{display:flex;gap:.5rem;align-items:center}.footer-social-link{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;color:var(--text-muted, #888);transition:color .15s}.footer-social-link:hover{color:var(--text, #eee)}.footer-social-link svg{width:1rem;height:1rem}.footer-motion-switch{font-size:.8rem;color:var(--text-muted, #888);white-space:nowrap}.footer-motion-switch .form-switch-label{color:var(--text-muted, #888)}.footer-motion-switch .form-switch-input{width:2rem;height:1.125rem}.footer-motion-switch .form-switch-input:after{width:.875rem;height:.875rem}.footer-motion-switch .form-switch-input:checked:after{transform:translate(.875rem)}.dm-slideover-header{display:flex;align-items:center;justify-content:space-between;padding:.875rem 1.25rem;border-bottom:1px solid var(--border-color, rgba(255, 255, 255, .08));flex-shrink:0}.dm-slideover-title{margin:0;font-size:1rem;font-weight:600;line-height:1.4}.dm-slideover-body{padding:1.25rem;overflow-y:auto;flex:1}@media(max-width:768px){.site-main.with-sidebar{grid-template-columns:1fr}.site-sidebar{display:none}}.dm-spacer{display:block;width:100%}.hero-breakout{width:calc(100vw - 2rem);margin-left:calc(50% - 50vw + 1rem);margin-right:calc(50% - 50vw + 1rem)}body[data-layout=landing]>.site-main .container{max-width:none;padding:0}body[data-layout=landing] .page-body{padding-left:1.5rem;padding-right:1.5rem}body[data-layout=landing] .page-body>p,body[data-layout=landing] .page-body>h1,body[data-layout=landing] .page-body>h2,body[data-layout=landing] .page-body>h3,body[data-layout=landing] .page-body>ul,body[data-layout=landing] .page-body>ol,body[data-layout=landing] .page-body>blockquote{max-width:860px;margin-left:auto;margin-right:auto}body[data-layout=landing] .page-body .hero-breakout{width:calc(100% + 3rem);margin-left:-1.5rem;margin-right:-1.5rem}body[data-layout=landing] .page-body .grid-breakout{width:calc(100% + 3rem);margin-left:-1.5rem;margin-right:-1.5rem;padding-left:1.5rem;padding-right:1.5rem}.page-body .card{transition:transform .2s ease,box-shadow .2s ease}.page-body .card:hover{transform:translateY(-3px);box-shadow:0 8px 24px #00000059}.grid-breakout{width:calc(100vw - 2rem);margin-left:calc(50% - 50vw + 1rem);margin-right:calc(50% - 50vw + 1rem)}
1
+ body,button,input,select,textarea{font-family:Roboto,sans-serif}.site-main{min-height:calc(100vh - 60px);padding-top:2rem;padding-bottom:4rem}.site-main.with-sidebar{display:grid;grid-template-columns:260px 1fr;gap:0}.site-sidebar{min-height:100%;border-right:1px solid var(--border-color, rgba(255,255,255,.08))}.site-content{overflow:hidden}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}.container{max-width:860px;margin:0 auto;padding:0 1.5rem}.page-title{font-size:2rem;font-weight:700;margin-bottom:1.5rem;line-height:1.2}.page-body{line-height:1.7;font-size:1rem}.page-body h1,.page-body h2,.page-body h3,.page-body h4{margin-top:2rem;margin-bottom:.75rem;font-weight:600}.page-body h2{font-size:1.5rem}.page-body h3{font-size:1.25rem}.page-body p{margin-bottom:1rem}.page-body ul,.page-body ol{margin-bottom:1rem;padding-left:1.5rem}.page-body a{color:var(--primary, #5b8cff)}.page-body a:hover{text-decoration:underline}.page-body code{font-family:Fira Code,Courier New,monospace;font-size:.9em;background:#ffffff0f;padding:.15em .35em;border-radius:3px}.page-body pre{background:#0000004d;border:1px solid rgba(255,255,255,.08);border-radius:6px;padding:1rem;overflow-x:auto;margin-bottom:1rem}.page-body pre code{background:none;padding:0}.page-body img{max-width:100%;border-radius:6px}.page-body blockquote{border-left:3px solid var(--primary, #5b8cff);margin:1.5rem 0;padding:.75rem 1rem;background:#5b8cff0f;border-radius:0 6px 6px 0}h3.accordion-header{margin:0}.accordion-button{all:unset;display:flex;align-items:center;justify-content:space-between;width:100%;cursor:pointer;font:inherit}.page-body .card-header h2{margin:0;font-size:1rem;font-weight:600;line-height:1.4}.card[data-collapsible] .card-header{cursor:pointer;user-select:none;display:flex;align-items:center;justify-content:space-between}.card[data-collapsible] .card-header:after{content:"\25be";font-size:1.1em;line-height:1;display:inline-block;transition:transform .25s ease;flex-shrink:0}.card[data-collapsible].is-collapsed .card-header:after{transform:rotate(-90deg)}.card[data-collapsible] .card-body{overflow:hidden;max-height:4000px;opacity:1;transition:max-height .3s ease,opacity .25s ease}.card[data-collapsible].is-collapsed .card-body{max-height:0;opacity:0}.navbar-link span[data-icon],.navbar-link svg,.navbar-dropdown-toggle span[data-icon],.navbar-dropdown-toggle svg,.navbar-dropdown-item span[data-icon],.navbar-dropdown-item svg{width:13px!important;height:13px!important;margin-right:10px!important}.navbar-dropdown-toggle{font-size:var(--dm-font-size-base)}@media(min-width:993px){.navbar-dropdown-toggle{font-size:var(--dm-font-size-sm)}}@media(min-width:1201px){.navbar-dropdown-toggle{font-size:var(--dm-font-size-xs)}}.dm-reduced-motion *,.dm-reduced-motion *:before,.dm-reduced-motion *:after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important;scroll-behavior:auto!important}.page-footer{border-top:1px solid var(--border-color, rgba(255,255,255,.08));padding:1.5rem 0}.footer-inner{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:1rem}.footer-inner p{margin:0;color:var(--text-muted, #888);font-size:.875rem}.footer-links{display:flex;gap:1.25rem}.footer-links a{color:var(--text-muted, #888);font-size:.875rem;text-decoration:none}.footer-links a:hover{color:var(--text, #eee)}.footer-social{display:flex;gap:.5rem;align-items:center}.footer-social-link{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;color:var(--text-muted, #888);transition:color .15s}.footer-social-link:hover{color:var(--text, #eee)}.footer-social-link svg{width:1rem;height:1rem}.footer-motion-switch{font-size:.8rem;color:var(--text-muted, #888);white-space:nowrap}.footer-motion-switch .form-switch-label{color:var(--text-muted, #888)}.footer-motion-switch .form-switch-input{width:2rem;height:1.125rem}.footer-motion-switch .form-switch-input:after{width:.875rem;height:.875rem}.footer-motion-switch .form-switch-input:checked:after{transform:translate(.875rem)}.dm-slideover-header{display:flex;align-items:center;justify-content:space-between;padding:.875rem 1.25rem;border-bottom:1px solid var(--border-color, rgba(255, 255, 255, .08));flex-shrink:0}.dm-slideover-title{margin:0;font-size:1rem;font-weight:600;line-height:1.4}.dm-slideover-body{padding:1.25rem;overflow-y:auto;flex:1}@media(max-width:768px){.site-main.with-sidebar{grid-template-columns:1fr}.site-sidebar{display:none}}.dm-spacer{display:block;width:100%}.hero-breakout{width:calc(100vw - 2rem);margin-left:calc(50% - 50vw + 1rem);margin-right:calc(50% - 50vw + 1rem)}.site-main:has(.page-body>.hero-breakout:first-child){padding-top:0}body[data-layout=landing]>.site-main{padding-top:0}body[data-layout=landing]>.site-main .container{max-width:none;padding:0}body[data-layout=landing] .page-body{padding-left:1.5rem;padding-right:1.5rem}body[data-layout=landing] .page-body>p,body[data-layout=landing] .page-body>h1,body[data-layout=landing] .page-body>h2,body[data-layout=landing] .page-body>h3,body[data-layout=landing] .page-body>ul,body[data-layout=landing] .page-body>ol,body[data-layout=landing] .page-body>blockquote{max-width:860px;margin-left:auto;margin-right:auto}body[data-layout=landing] .page-body .hero-breakout{width:calc(100% + 3rem);margin-left:-1.5rem;margin-right:-1.5rem}body[data-layout=landing] .page-body .grid-breakout{width:calc(100% + 3rem);margin-left:-1.5rem;margin-right:-1.5rem;padding-left:1.5rem;padding-right:1.5rem}.page-body .card{transition:transform .2s ease,box-shadow .2s ease}.page-body .card:hover{transform:translateY(-3px);box-shadow:0 8px 24px #00000059}.page-body .card-header-icon-inline{display:flex;align-items:center;gap:.6rem}.page-body .card-header-icon-inline [data-icon]{flex-shrink:0;line-height:0}.page-body .card-header-icon-inline [data-icon] svg,.page-body .card-header-icon-inline>svg{display:block;width:1.25rem;height:1.25rem}.page-body .card-header-icon-stacked{display:flex;flex-direction:column;align-items:center;text-align:center;gap:.35rem;padding-top:.25rem}.page-body .card-header-icon-stacked [data-icon],.page-body .card-header-icon-stacked svg{width:2rem;height:2rem}.hero.hero-dark{background:linear-gradient(135deg,#1f2937,#111827);color:#e2e8f0}.hero .hero-content{position:relative;z-index:2}.hero.hero-left .hero-content{text-align:left;align-items:flex-start;max-width:62%}@media(max-width:768px){.hero.hero-left .hero-content{max-width:100%}}.hero .hero-cta{display:flex;gap:.85rem;flex-wrap:wrap;margin-top:1.75rem}.hero .hero-cta a{display:inline-flex;align-items:center;gap:.4rem;padding:.55rem 1.35rem;border-radius:6px;font-size:.95rem;font-weight:500;text-decoration:none;transition:background .2s ease,border-color .2s ease,transform .15s ease,box-shadow .2s ease}.hero .hero-cta a:first-child{background:#ffffffeb;color:#111;border:1px solid transparent}.hero .hero-cta a:first-child:hover{background:#fff;box-shadow:0 4px 16px #00000040;transform:translateY(-2px)}.hero .hero-cta a:last-child{background:transparent;color:#fff;border:1px solid rgba(255,255,255,.4)}.hero .hero-cta a:last-child:hover{border-color:#ffffffbf;background:#ffffff14;transform:translateY(-2px)}.hero .hero-label{display:inline-block;margin-bottom:.9rem;padding:.2rem .8rem;border-radius:999px;font-size:.72rem;font-weight:600;letter-spacing:.07em;text-transform:uppercase;color:#ffffffb3;border:1px solid rgba(255,255,255,.22)}.grid-breakout{width:calc(100vw - 2rem);margin-left:calc(50% - 50vw + 1rem);margin-right:calc(50% - 50vw + 1rem)}.dm-breadcrumbs{position:fixed;z-index:200;display:inline-flex;align-items:center;gap:.2rem;padding:.3rem .8rem;border-radius:999px;backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);background:#00000047;border:1px solid rgba(255,255,255,.11);box-shadow:0 2px 10px #00000038;font-size:.72rem;font-weight:500;letter-spacing:.01em;line-height:1.4;max-width:calc(100vw - 2rem);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dm-breadcrumbs .dm-breadcrumbs-item{color:#ffffffa6}.dm-breadcrumbs .dm-breadcrumbs-link{display:inline-flex;align-items:center;gap:.25rem;color:#ffffff8c;text-decoration:none;transition:color .15s}.dm-breadcrumbs .dm-breadcrumbs-home-icon{flex-shrink:0;vertical-align:middle}.dm-breadcrumbs .dm-breadcrumbs-link:hover{color:#fffffff2}.dm-breadcrumbs .dm-breadcrumbs-current{color:#ffffffeb;font-weight:600}.dm-breadcrumbs .dm-breadcrumbs-separator{color:#ffffff47;font-size:.8em;line-height:1;margin:0 .05rem}[data-mode=light] .dm-breadcrumbs{background:#ffffff8c;border-color:#00000012;box-shadow:0 2px 10px #00000014}[data-mode=light] .dm-breadcrumbs .dm-breadcrumbs-item,[data-mode=light] .dm-breadcrumbs .dm-breadcrumbs-link{color:#0000008c}[data-mode=light] .dm-breadcrumbs .dm-breadcrumbs-link:hover{color:#000000e6}[data-mode=light] .dm-breadcrumbs .dm-breadcrumbs-current{color:#000000d9}[data-mode=light] .dm-breadcrumbs .dm-breadcrumbs-separator{color:#00000040}.dm-collection-display{margin:1.5rem 0}.dm-collection-list{display:flex;flex-direction:column;gap:0}.dm-collection-list-item{padding:1rem 0;border-bottom:1px solid var(--border-color, rgba(255, 255, 255, .08))}.dm-collection-list-item:last-child{border-bottom:none}.dm-collection-list-item strong{display:block;font-size:1rem;margin-bottom:.25rem}.dm-collection-list-item p{margin:0;color:var(--text-muted, #888);font-size:.9rem}.dm-collection-empty p{color:var(--text-muted, #888);font-style:italic}
@@ -0,0 +1 @@
1
+ const targets=document.querySelectorAll("[data-form]");targets.length&&targets.forEach(initFormTarget);function showMessage(i,a,t){const e=i.querySelector(".fb-form-success, .fb-form-error");e&&e.remove();const s=document.createElement("div");s.className=t==="success"?"fb-form-success":"fb-form-error",s.textContent=a,i.appendChild(s)}function buildBlueprintFromFields(i,a){const t={};return i.forEach(function(e){if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const s=e.type==="checkbox"?"boolean":e.type==="date"?"string":e.type,r={...e.formConfig||{}};r.span==="full"&&a&&(r.span=a),t[e.name]={type:s,label:e.label||e.name,required:e.required||!1,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{hint:e.helper},...r}},e.minLength!==void 0&&(t[e.name].minLength=e.minLength),e.maxLength!==void 0&&(t[e.name].maxLength=e.maxLength),e.min!==void 0&&(t[e.name].min=e.min),e.max!==void 0&&(t[e.name].max=e.max)}),t}function buildInitialData(i){const a={};return i.forEach(function(t){if(!(!t.name||t.type==="page-break"||t.type==="spacer")&&(t.type==="select"||t.type==="multiselect")&&t.required){const e=(t.options||[])[0];e&&(a[t.name]=typeof e=="object"?e.value:e)}}),a}function patchDateInputs(i,a){(a||[]).forEach(function(t){if(t.type!=="date"||!t.name)return;const e=i.querySelector('[name="'+t.name+'"]');e&&e.type!=="date"&&(e.type="date")})}function buildWizardSteps(i,a){const t=[];let e=[],s=a||"Step 1",r="";return i.forEach(function(c){c.type==="page-break"?(t.push({title:s,description:r,fieldGroup:e}),e=[],s=c.label||"Step "+(t.length+1),r=c.description||""):c.type!=="spacer"&&e.push(c)}),t.push({title:s,description:r,fieldGroup:e}),t}function injectHoneypot(i){const a=document.createElement("div");a.className="fb-form-honeypot",a.setAttribute("aria-hidden","true");const t=document.createElement("input");t.name="_hp",t.type="text",t.tabIndex=-1,t.autocomplete="off",a.appendChild(t),i.appendChild(a)}function injectSpacers(i,a){const t=i.querySelector("form");if(!t)return;const e=Array.from(t.querySelectorAll(".form-group"));let s=0;a.forEach(function(r){if(r.type==="spacer"){const c=document.createElement("div");c.className="fb-spacer";const o=e[s];if(o)t.insertBefore(c,o);else{const m=t.querySelector('[type="submit"]');m?t.insertBefore(c,m):t.appendChild(c)}}else r.type!=="page-break"&&s++})}function submitForm(i,a,t,e,s){const r=(s||e).querySelector('[name="_hp"]')?.value||"",c=Object.assign({},a,{_hp:r});return fetch("/api/forms/submit/"+i,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(c)}).then(o=>o.json().then(m=>({ok:o.ok,body:m}))).then(o=>{o.ok&&o.body.ok?(e.textContent="",showMessage(e,o.body.message||t.successMessage||"Thank you.","success")):showMessage(e,o.body.error||"Something went wrong.","error")}).catch(()=>{showMessage(e,"Unable to submit. Please check your connection.","error")})}function renderManualForm(i,a,t,e,s){const r=document.createElement("form");r.noValidate=!0,a.forEach(function(o){const m=document.createElement("div");m.className="form-group",m.style.marginBottom="1.25rem";const p=document.createElement("label");if(p.className="form-label",p.textContent=o.label||o.name,o.required){const l=document.createElement("span");l.textContent=" *",l.style.color="#f87171",p.appendChild(l)}let n;o.type==="textarea"?(n=document.createElement("textarea"),n.rows=o.formConfig?.rows||4,n.className="form-input"):o.type==="select"?(n=document.createElement("select"),n.className="form-input",(o.options||[]).forEach(function(l){const u=document.createElement("option");u.value=typeof l=="string"?l:l.value??"",u.textContent=typeof l=="string"?l:l.label||u.value,n.appendChild(u)})):(n=document.createElement("input"),n.type=o.type||"text",n.className="form-input",o.placeholder&&(n.placeholder=o.placeholder)),n.name=o.name,n.required=o.required||!1,m.appendChild(p),m.appendChild(n),r.appendChild(m)}),t.honeypot&&injectHoneypot(r);const c=document.createElement("button");c.type="submit",c.className="btn btn-primary",c.textContent=t.submitText||"Submit",r.appendChild(c),r.addEventListener("submit",function(o){o.preventDefault();const m={};if(a.forEach(function(p){const n=r.querySelector('[name="'+p.name+'"]');n&&(m[p.name]=n.value)}),window.FormLogicEngine&&s){const p=window.FormLogicEngine,n=[],l=[];if(a.forEach(function(u){if(p.evaluateFieldVisibility(u,m)==="hidden"){delete m[u.name];return}const h=p.evaluateFieldRequirement(u,m),f=m[u.name];h&&(!f||!String(f).trim())&&n.push(u.label||u.name);const d=p.validateField(u,f||"",m);d.length&&l.push(d[0].message)}),n.length||l.length){const u=[];n.length&&u.push("Required: "+n.join(", ")),l.length&&u.push(l.join("; ")),showMessage(i,u.join(". "),"error");return}}i.classList.add("fb-form-loading"),c.disabled=!0,submitForm(e,m,t,i,r).finally(function(){i.classList.remove("fb-form-loading"),c.disabled=!1})}),i.appendChild(r),window.FormLogicEngine&&s&&a.some(o=>o.logic)&&new window.FormLogicEngine.FormLogicRuntime(s,i).init()}function initFormTarget(i){const a=i.getAttribute("data-form");a&&fetch("/api/forms/"+a+"/public").then(t=>{if(!t.ok)throw new Error("Form not found: "+a);return t.json()}).then(t=>{const e=t.fields||[],s=t.settings||{},r=document.createElement("div");r.className="fb-form-wrapper",i.appendChild(r);const c=e.some(o=>o.type==="page-break");if(typeof Domma<"u"&&Domma.forms){const o=s.columns||1;if(c&&Domma.forms.wizard){const p=buildWizardSteps(e,t.title).map(function(n){return{title:n.title,description:n.description,fields:buildBlueprintFromFields(n.fieldGroup,o)}});if(Domma.forms.wizard(r,{schema:{steps:p},onSubmit:function(n){return submitForm(a,n,s,r,null)}}),patchDateInputs(r,e),window.FormLogicEngine&&e.some(n=>n.logic)&&new window.FormLogicEngine.FormLogicRuntime(t,r).init(),s.honeypot){const n=r.querySelector("form");n&&injectHoneypot(n)}}else if(Domma.forms.render){const m=buildBlueprintFromFields(e,o),p=buildInitialData(e);if(Domma.forms.render(r,m,p,{submitText:s.submitText||"Submit",layout:s.layout||"stacked",columns:o,onSubmit:function(n){return submitForm(a,n,s,r,null)}}),patchDateInputs(r,e),window.FormLogicEngine&&e.some(n=>n.logic)&&new window.FormLogicEngine.FormLogicRuntime(t,r).init(),e.some(n=>n.type==="spacer")&&injectSpacers(r,e),s.honeypot){const n=r.querySelector("form");n&&injectHoneypot(n)}}}else renderManualForm(r,e.filter(o=>o.type!=="page-break"&&o.type!=="spacer"),s,a,t)}).catch(t=>{const e=document.createElement("p");e.textContent="Form unavailable.",e.style.cssText="color:#f87171;font-style:italic;",i.appendChild(e),console.warn("[forms]",t.message)})}
package/public/js/site.js CHANGED
@@ -1 +1 @@
1
- $(()=>{const r=window.__CMS_NAV__||{},h=window.__CMS_SITE__||{};if($("#site-navbar").length&&r.brand){const t={...r.brand};if(t.icon){let a=`<span data-icon="${t.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`;t.text&&(a+=`<span class="navbar-brand-text">${t.text}</span>`),t.html=a}Domma.elements.navbar("#site-navbar",{brand:t,items:r.items||[],variant:r.variant||"dark",position:r.position||"sticky",collapsible:!0}),Domma.icons.scan("#site-navbar")}const m=$("#site-footer");if(m.length){const t=h.social||{},a={twitter:{label:"X / Twitter",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.742l7.73-8.835L1.254 2.25H8.08l4.259 5.629L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'},facebook:{label:"Facebook",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'},instagram:{label:"Instagram",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>'},linkedin:{label:"LinkedIn",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'},github:{label:"GitHub",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'},youtube:{label:"YouTube",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z"/></svg>'}};let e='<div class="footer-inner container">';if(h.footer){const c=h.footer;e+=`<p>${c.copyright||""}</p>`,c.links?.length&&(e+='<nav class="footer-links">',c.links.forEach(o=>{e+=`<a href="${o.url}">${o.text}</a>`}),e+="</nav>");const l=Object.keys(a).filter(o=>t[o]);l.length&&(e+='<div class="footer-social">',l.forEach(o=>{const{label:s,svg:g}=a[o];e+=`<a href="${t[o]}" target="_blank" rel="noopener noreferrer" aria-label="${s}" class="footer-social-link">${g}</a>`}),e+="</div>")}const i=S.get("reduced_motion"),f=i!==null?!!i:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches);e+=`<label class="form-switch footer-motion-switch" title="Reduce motion"><input type="checkbox" class="form-switch-input" id="dm-motion-switch"${f?" checked":""}><span class="form-switch-label">Reduce motion</span></label>`,e+="</div>",m.html(e,{safe:!1}),document.getElementById("dm-motion-switch").addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()})}$("#site-sidebar").length&&Domma.elements.sidebar("#site-sidebar",{autoGenerate:!0,selector:"h2, h3",collapsible:!1,push:!0,contentSelector:".site-content"}),Domma.icons.scan();const n=$(".page-body");if(n.length&&(n.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),n.find(".tabs").each(function(){Domma.elements.tabs(this)}),n.find(".carousel").each(function(){Domma.elements.carousel(this,{autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"})}),n.find(".dm-countdown").each(function(){const t={autoStart:!0};this.dataset.to&&(t.targetDate=new Date(this.dataset.to)),this.dataset.duration&&(t.duration=parseInt(this.dataset.duration,10)),this.dataset.format&&(t.format=this.dataset.format),Domma.elements.timer(this,t)}),n.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),n.find(".card[data-collapsible]").each(function(){const t=this.querySelector(".card-header");t&&t.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),n.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const t=this.dataset.soTarget,a=document.getElementById(t);if(!a)return;const e=E.slideover({title:a.dataset.soTitle||"",size:a.dataset.soSize||"md",position:a.dataset.soPosition||"right"});a.style.display="",e.setContent(a),e.open()})})),typeof $.setup=="function"){const t=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(a=>{try{const e=atob(a.dataset.config),i=JSON.parse(e);Object.assign(t,i)}catch{}}),Object.keys(t).length){const a={};for(const[e,i]of Object.entries(t)){const f=i?.events?.click,{confirm:c,toast:l,alert:o,...s}=f||{};c||l||o?($(e).on("click",async function(v){if(v.preventDefault(),!(c&&!await E.confirm(c))){if(s.target){const d=$(s.target);s.toggleClass&&d.toggleClass(s.toggleClass),s.addClass&&d.addClass(s.addClass),s.removeClass&&d.removeClass(s.removeClass)}s.href&&(window.location.href=s.href),l&&E.toast(l,{type:s.toastType||"success"}),o&&E.alert(o)}}),Object.keys(s).length&&(a[e]={...i,events:{...i.events,click:s}})):a[e]=i}$.setup(a)}}});
1
+ $(()=>{const u=window.__CMS_NAV__||{},i=window.__CMS_SITE__||{};if($("#site-navbar").length&&u.brand){const a={...u.brand};if(a.icon){let o=`<span data-icon="${a.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`;a.text&&(o+=`<span class="navbar-brand-text">${a.text}</span>`),a.html=o}Domma.elements.navbar("#site-navbar",{brand:a,items:u.items||[],variant:u.variant||"dark",position:u.position||"sticky",collapsible:!0}),Domma.icons.scan("#site-navbar")}const y=$("#site-footer");if(y.length){const a=i.social||{},o={twitter:{label:"X / Twitter",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.742l7.73-8.835L1.254 2.25H8.08l4.259 5.629L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'},facebook:{label:"Facebook",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'},instagram:{label:"Instagram",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>'},linkedin:{label:"LinkedIn",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'},github:{label:"GitHub",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'},youtube:{label:"YouTube",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z"/></svg>'}};let t='<div class="footer-inner container">';if(i.footer){const n=i.footer;t+=`<p>${n.copyright||""}</p>`,n.links?.length&&(t+='<nav class="footer-links">',n.links.forEach(l=>{t+=`<a href="${l.url}">${l.text}</a>`}),t+="</nav>");const f=Object.keys(o).filter(l=>a[l]);f.length&&(t+='<div class="footer-social">',f.forEach(l=>{const{label:c,svg:s}=o[l];t+=`<a href="${a[l]}" target="_blank" rel="noopener noreferrer" aria-label="${c}" class="footer-social-link">${s}</a>`}),t+="</div>")}const e=S.get("reduced_motion"),d=e!==null?!!e:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches);t+=`<label class="form-switch footer-motion-switch" title="Reduce motion"><input type="checkbox" class="form-switch-input" id="dm-motion-switch"${d?" checked":""}><span class="form-switch-label">Reduce motion</span></label>`,t+="</div>",y.html(t,{safe:!1}),document.getElementById("dm-motion-switch").addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()})}$("#site-sidebar").length&&Domma.elements.sidebar("#site-sidebar",{autoGenerate:!0,selector:"h2, h3",collapsible:!1,push:!0,contentSelector:".site-content"}),Domma.icons.scan();const r=$(".page-body");if(r.length&&(r.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),r.find(".tabs").each(function(){Domma.elements.tabs(this)}),r.find(".carousel").each(function(){Domma.elements.carousel(this,{autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"})}),r.find(".dm-countdown").each(function(){const a={autoStart:!0};this.dataset.to&&(a.targetDate=new Date(this.dataset.to)),this.dataset.duration&&(a.duration=parseInt(this.dataset.duration,10)),this.dataset.format&&(a.format=this.dataset.format),Domma.elements.timer(this,a)}),r.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),r.find(".dm-progression").each(function(){Domma.elements.progression(this,{layout:this.dataset.layout||"vertical",theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0})}),r.find(".card[data-collapsible]").each(function(){const a=this.querySelector(".card-header");a&&a.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),r.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const a=this.dataset.soTarget,o=document.getElementById(a);if(!o)return;const t=E.slideover({title:o.dataset.soTitle||"",size:o.dataset.soSize||"md",position:o.dataset.soPosition||"right"});o.style.display="",t.setContent(o),t.open()})})),typeof $.setup=="function"){const a=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(o=>{try{const t=atob(o.dataset.config),e=JSON.parse(t);Object.assign(a,e)}catch{}}),Object.keys(a).length>0){const o={};for(const[t,e]of Object.entries(a)){const d=e?.events?.click,{confirm:n,toast:f,alert:l,prompt:c,...s}=d||{};n||f||l||c?($(t).on("click",async function(h){if(h.preventDefault(),n&&!await E.confirm(n))return;let m=null;if(!(c&&(m=await E.prompt(c,{inputPlaceholder:s.promptPlaceholder||"",inputValue:s.promptDefault||""}),m===null))){if(s.target){const b=$(s.target);s.toggleClass&&b.toggleClass(s.toggleClass),s.addClass&&b.addClass(s.addClass),s.removeClass&&b.removeClass(s.removeClass),m!==null&&(s.setText&&b.text(m),s.setVal&&b.val(m),s.setAttr&&b.attr(s.setAttr,m))}s.href&&(window.location.href=s.href),f&&E.toast(f,{type:s.toastType||"success"}),l&&E.alert(l)}}),Object.keys(s).length&&(o[t]={...e,events:{...e.events,click:s}})):o[t]=e}$.setup(o)}}r.length&&wireCTAButtons(r.get(0))});function wireCTAButtons(u){u.querySelectorAll(".dm-cta-trigger").forEach(i=>{i.addEventListener("click",async()=>{const g=i.dataset.action,y=i.dataset.entry,p=i.dataset.confirm;let r=S.get("auth_token");if(!r){E.toast("Please log in to perform this action.",{type:"warning"});return}if(p&&!await E.confirm(p))return;const a=Array.from(i.childNodes).map(t=>t.cloneNode(!0));i.disabled=!0,i.textContent="Running\u2026";const o=t=>fetch(`/api/actions/${g}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify({entryId:y})});try{let t=await o(r);if(t.status===401){const d=S.get("auth_refresh_token");if(d){const n=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:d})});if(n.ok){const{token:f}=await n.json();S.set("auth_token",f),r=f,t=await o(r)}}}const e=await t.json().catch(()=>({}));if(!t.ok)throw new Error(e.error||e.message||`Error ${t.status}`);E.toast(e.message||"Action completed.",{type:"success"})}catch(t){E.toast(t.message||"Action failed.",{type:"error"})}finally{i.disabled=!1,i.textContent="",a.forEach(t=>i.appendChild(t)),Domma.icons.scan(i)}})})}(function(){const i=document.querySelectorAll("[data-collection-table]");!i.length||typeof T>"u"||!T.create||i.forEach(g=>{let y;try{y=JSON.parse(atob(g.dataset.payload))}catch{return}const{columns:p,rows:r,search:a,sortable:o,exportable:t,pageSize:e,empty:d,ctaConfig:n}=y;if(!p?.length)return;if(n){const c=p.findIndex(s=>s.key==="_cta");c!==-1&&(p[c]={key:"_cta",title:"",render:(s,v)=>{const h=document.createElement("button");if(h.className=`btn btn-${n.style||"primary"} dm-cta-trigger`,h.dataset.action=n.action||"",h.dataset.entry=v._entryId||"",n.confirm&&(h.dataset.confirm=n.confirm),n.icon){const m=document.createElement("span");m.dataset.icon=n.icon,h.appendChild(m),h.appendChild(document.createTextNode(" "))}return h.appendChild(document.createTextNode(n.label||"Run")),h}})}const f="col-table-"+Math.random().toString(36).slice(2,7),l=document.createElement("div");l.id=f,g.replaceChildren(l),T.create("#"+f,{data:r,columns:p,search:a,sortable:o,exportable:t,pageSize:e,emptyMessage:d}),n&&wireCTAButtons(l)})})(),(function(){const i=document.querySelectorAll("[data-form-inline]");if(!i.length||typeof F>"u")return;function g(a,o){const t={};return(a||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const d=e.type==="checkbox"?"boolean":e.type==="date"?"string":e.type,n={...e.formConfig||{}};n.span==="full"&&o&&(n.span=o),t[e.name]={type:d,label:e.label,required:e.required,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{hint:e.helper},...n}}}),t}function y(a){const o={};return(a||[]).forEach(t=>{if(!(!t.name||t.type==="page-break"||t.type==="spacer")&&(t.type==="select"||t.type==="multiselect")&&t.required){const e=(t.options||[])[0];e&&(o[t.name]=typeof e=="object"?e.value:e)}}),o}function p(a,o){(o||[]).forEach(t=>{if(t.type!=="date"||!t.name)return;const e=a.querySelector(`[name="${t.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function r(a,o,t){let e=a.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",a.appendChild(e)),e.textContent=o,e.style.cssText=t?"color:var(--danger,#f87171);margin-top:.75rem;":"color:var(--success,#4ade80);margin-top:.75rem;"}i.forEach(a=>{let o;try{o=JSON.parse(atob(a.dataset.formInline))}catch{return}const t=o.fields||[],e=o.settings||{},d=e.columns||1,n=e.layout||"stacked",f=t.some(c=>c.type==="page-break"),l=async c=>{try{const s=await H.post(`/api/forms/submit/${o.slug}`,c);if(s?.redirect){window.location.href=s.redirect;return}for(;a.firstChild;)a.removeChild(a.firstChild);r(a,s?.message||e.successMessage||"Thank you for your submission.",!1)}catch(s){throw r(a,s.message||"Submission failed. Please try again.",!0),s}};if(f&&F.wizard){const c=[];let s=[],v=o.title||"Step 1",h="";t.forEach(m=>{m.type==="page-break"?(c.push({title:v,description:h,fields:g(s,d)}),s=[],v=m.label||`Step ${c.length+1}`,h=m.description||""):m.type!=="spacer"&&s.push(m)}),c.push({title:v,description:h,fields:g(s,d)}),F.wizard(a,{schema:{steps:c},onSubmit:l}),p(a,t)}else if(F.render){if(F.render(a,g(t,d),y(t),{submitText:e.submitText||"Submit",layout:n,columns:d,onSubmit:l}),n==="grid"&&e.submitSpan==="full"){const c=a.querySelector(".form-buttons");c&&c.classList.add("col-span-full")}p(a,t)}})})();
package/scripts/build.js CHANGED
@@ -1,129 +1,194 @@
1
- /**
2
- * Build script — creates a publishable staging copy in _publish/
3
- * Browser-facing JS/CSS is minified; server-side code is copied as-is.
4
- * Run: node scripts/build.js
5
- */
6
-
7
- import {cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync} from 'fs';
8
- import {dirname, join} from 'path';
9
- import {glob} from 'fs/promises';
10
- import * as esbuild from 'esbuild';
11
-
12
- const ROOT = new URL('..', import.meta.url).pathname.replace(/\/$/, '');
13
- const OUT = join(ROOT, '_publish');
14
-
15
- // Files/dirs copied verbatim (server-side, config, docs)
16
- const COPY_AS_IS = [
17
- 'server',
18
- 'bin',
19
- 'config',
20
- 'scripts',
21
- 'package.json',
22
- 'README.md',
23
- 'LICENSE',
24
- '.npmignore',
25
- ];
26
-
27
- // Glob patterns for browser-facing assets to minify
28
- const MINIFY_PATTERNS = [
29
- 'admin/js/**/*.js',
30
- 'admin/css/**/*.css',
31
- 'public/js/**/*.js',
32
- 'public/css/**/*.css',
33
- 'plugins/*/public/**/*.js',
34
- 'plugins/*/public/**/*.css',
35
- ];
36
-
37
- // Files within plugins that are copied as-is (server-side plugin code)
38
- const PLUGIN_AS_IS_PATTERNS = [
39
- 'plugins/**/plugin.js',
40
- 'plugins/**/config.js',
41
- 'plugins/**/routes/**/*.js',
42
- 'plugins/**/services/**/*.js',
43
- 'plugins/**/views/**/*.js',
44
- 'plugins/**/*.json',
45
- 'plugins/**/*.html',
46
- ];
47
-
48
- // HTML templates in admin (copied as-is)
49
- const ADMIN_HTML_PATTERNS = [
50
- 'admin/**/*.html',
51
- ];
52
-
53
- async function collectFiles(pattern) {
54
- const files = [];
55
- for await (const file of glob(pattern, {cwd: ROOT})) {
56
- files.push(file);
57
- }
58
- return files;
59
- }
60
-
61
- async function minifyFile(relPath, loader) {
62
- const src = readFileSync(join(ROOT, relPath), 'utf8');
63
- const result = await esbuild.transform(src, {
64
- loader,
65
- minify: true,
66
- target: 'es2020',
67
- });
68
- const outPath = join(OUT, relPath);
69
- mkdirSync(dirname(outPath), {recursive: true});
70
- writeFileSync(outPath, result.code);
71
- }
72
-
73
- async function build() {
74
- // Clean and recreate _publish/
75
- if (existsSync(OUT)) rmSync(OUT, {recursive: true, force: true});
76
- mkdirSync(OUT, {recursive: true});
77
-
78
- // Copy server-side dirs/files verbatim
79
- for (const item of COPY_AS_IS) {
80
- const src = join(ROOT, item);
81
- if (existsSync(src)) {
82
- cpSync(src, join(OUT, item), {recursive: true});
83
- }
84
- }
85
-
86
- // Copy plugin server-side code verbatim
87
- const pluginAsIs = await collectFiles('{' + PLUGIN_AS_IS_PATTERNS.join(',') + '}');
88
- for (const rel of pluginAsIs) {
89
- const outPath = join(OUT, rel);
90
- mkdirSync(dirname(outPath), {recursive: true});
91
- cpSync(join(ROOT, rel), outPath);
92
- }
93
-
94
- // Copy admin HTML templates verbatim
95
- const adminHtml = await collectFiles(ADMIN_HTML_PATTERNS[0]);
96
- for (const rel of adminHtml) {
97
- const outPath = join(OUT, rel);
98
- mkdirSync(dirname(outPath), {recursive: true});
99
- cpSync(join(ROOT, rel), outPath);
100
- }
101
-
102
- // Minify browser-facing JS and CSS
103
- let jsCount = 0;
104
- let cssCount = 0;
105
-
106
- // Collect all minify targets, deduplicate against plugin as-is
107
- const pluginAsIsSet = new Set(pluginAsIs);
108
-
109
- for (const pattern of MINIFY_PATTERNS) {
110
- const files = await collectFiles(pattern);
111
- for (const rel of files) {
112
- if (pluginAsIsSet.has(rel)) continue; // skip plugin server-side JS
113
- const loader = rel.endsWith('.css') ? 'css' : 'js';
114
- await minifyFile(rel, loader);
115
- if (loader === 'js') jsCount++;
116
- else cssCount++;
117
- }
118
- }
119
-
120
- const totalCopied = COPY_AS_IS.filter(i => existsSync(join(ROOT, i))).length
121
- + pluginAsIs.length + adminHtml.length;
122
-
123
- console.log(`Built into _publish/ JS: ${jsCount} minified, CSS: ${cssCount} minified, ${totalCopied} entries copied`);
124
- }
125
-
126
- build().catch(err => {
127
- console.error('Build failed:', err.message);
128
- process.exit(1);
129
- });
1
+ /**
2
+ * Build script — creates a publishable staging copy in _publish/
3
+ * Browser-facing JS/CSS is minified; server-side code is copied as-is.
4
+ * Run: node scripts/build.js
5
+ */
6
+
7
+ import {cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync} from 'fs';
8
+ import {readdir} from 'fs/promises';
9
+ import {dirname, join, relative} from 'path';
10
+ import * as esbuild from 'esbuild';
11
+
12
+ const ROOT = new URL('..', import.meta.url).pathname.replace(/\/$/, '');
13
+ const OUT = join(ROOT, '_publish');
14
+
15
+ // Files/dirs copied verbatim (server-side, config, docs)
16
+ const COPY_AS_IS = [
17
+ 'server',
18
+ 'bin',
19
+ 'config',
20
+ 'scripts',
21
+ 'package.json',
22
+ 'README.md',
23
+ 'LICENSE',
24
+ '.npmignore',
25
+ ];
26
+
27
+ // Glob patterns for browser-facing assets to minify
28
+ const MINIFY_PATTERNS = [
29
+ 'admin/js/**/*.js',
30
+ 'admin/css/**/*.css',
31
+ 'public/js/**/*.js',
32
+ 'public/css/**/*.css',
33
+ 'plugins/*/public/**/*.js',
34
+ 'plugins/*/public/**/*.css',
35
+ ];
36
+
37
+ // Files within plugins that are copied as-is (server-side plugin code)
38
+ const PLUGIN_AS_IS_PATTERNS = [
39
+ 'plugins/**/plugin.js',
40
+ 'plugins/**/config.js',
41
+ 'plugins/**/routes/**/*.js',
42
+ 'plugins/**/services/**/*.js',
43
+ 'plugins/**/views/**/*.js',
44
+ 'plugins/**/*.json',
45
+ 'plugins/**/*.html',
46
+ ];
47
+
48
+ // HTML templates in admin (copied as-is)
49
+ const ADMIN_HTML_PATTERNS = [
50
+ 'admin/**/*.html',
51
+ ];
52
+
53
+ /**
54
+ * Recursively list all files under a directory, returning paths relative to ROOT.
55
+ * @param {string} dir - Absolute path to the directory to scan.
56
+ * @returns {Promise<string[]>}
57
+ */
58
+ async function listFilesRecursive(dir) {
59
+ const entries = await readdir(dir, {recursive: true, withFileTypes: true});
60
+ return entries
61
+ .filter(e => e.isFile())
62
+ .map(e => relative(ROOT, join(e.parentPath ?? e.path, e.name)));
63
+ }
64
+
65
+ /**
66
+ * Match files relative to ROOT against a simple glob pattern.
67
+ * Supports `**` (any path segment), `*` (any non-separator chars), and `{a,b}` alternation.
68
+ * @param {string} pattern
69
+ * @returns {Promise<string[]>}
70
+ */
71
+ async function collectFiles(pattern) {
72
+ // Expand top-level {a,b,...} alternation into individual patterns
73
+ const braceMatch = pattern.match(/^\{([^}]+)\}\/(.+)$/);
74
+ if (braceMatch) {
75
+ const alts = braceMatch[1].split(',');
76
+ const rest = braceMatch[2];
77
+ const results = await Promise.all(alts.map(a => collectFiles(a + '/' + rest)));
78
+ return [...new Set(results.flat())];
79
+ }
80
+
81
+ // Determine the top-level directory from the pattern (part before first wildcard)
82
+ const parts = pattern.split('/');
83
+ let baseDir = ROOT;
84
+ for (const part of parts) {
85
+ if (part.includes('*') || part.includes('{')) break;
86
+ baseDir = join(baseDir, part);
87
+ }
88
+
89
+ if (!existsSync(baseDir)) return [];
90
+
91
+ const allFiles = await listFilesRecursive(baseDir);
92
+
93
+ // Convert glob pattern to a RegExp
94
+ const reStr = pattern
95
+ .replace(/[.+^${}()|[\]\\]/g, (c) => (c === '{' || c === '}' ? c : '\\' + c))
96
+ .replace(/\{([^}]+)\}/g, (_, alts) => '(?:' + alts.split(',').join('|') + ')')
97
+ .replace(/\*\*\//g, '(?:[^/]+/){0,}') // **/ → zero-or-more path segments
98
+ .replace(/\*\*/g, '(.+)')
99
+ .replace(/(?<!\()\*/g, '([^/]+)');
100
+ const re = new RegExp('^' + reStr + '$');
101
+
102
+ return allFiles.filter(f => re.test(f.replace(/\\/g, '/')));
103
+ }
104
+
105
+ async function minifyFile(relPath, loader) {
106
+ const src = readFileSync(join(ROOT, relPath), 'utf8');
107
+ const result = await esbuild.transform(src, {
108
+ loader,
109
+ minify: true,
110
+ target: 'es2020',
111
+ });
112
+ const outPath = join(OUT, relPath);
113
+ mkdirSync(dirname(outPath), {recursive: true});
114
+ writeFileSync(outPath, result.code);
115
+ }
116
+
117
+ async function build() {
118
+ // Clean and recreate _publish/
119
+ if (existsSync(OUT)) rmSync(OUT, {recursive: true, force: true});
120
+ mkdirSync(OUT, {recursive: true});
121
+
122
+ // Copy server-side dirs/files verbatim
123
+ for (const item of COPY_AS_IS) {
124
+ const src = join(ROOT, item);
125
+ if (existsSync(src)) {
126
+ cpSync(src, join(OUT, item), {recursive: true});
127
+ }
128
+ }
129
+
130
+ // Strip prepublishOnly from _publish/package.json — the guard is for the
131
+ // source tree only; running npm publish inside _publish/ must succeed.
132
+ const pkgPath = join(OUT, 'package.json');
133
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
134
+ delete pkg.scripts?.prepublishOnly;
135
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
136
+
137
+ // Copy plugin server-side code verbatim
138
+ const pluginAsIs = await collectFiles('{' + PLUGIN_AS_IS_PATTERNS.join(',') + '}');
139
+ for (const rel of pluginAsIs) {
140
+ const outPath = join(OUT, rel);
141
+ mkdirSync(dirname(outPath), {recursive: true});
142
+ cpSync(join(ROOT, rel), outPath);
143
+ }
144
+
145
+ // Copy admin HTML templates verbatim
146
+ const adminHtml = await collectFiles(ADMIN_HTML_PATTERNS[0]);
147
+ for (const rel of adminHtml) {
148
+ const outPath = join(OUT, rel);
149
+ mkdirSync(dirname(outPath), {recursive: true});
150
+ cpSync(join(ROOT, rel), outPath);
151
+ }
152
+
153
+ // Copy Domma tools assets (gitignored but tracked for distribution)
154
+ const dommaTools = [
155
+ 'admin/dist/domma/domma-tools.css',
156
+ 'admin/dist/domma/domma-tools.min.js',
157
+ ];
158
+ for (const rel of dommaTools) {
159
+ const src = join(ROOT, rel);
160
+ if (existsSync(src)) {
161
+ const outPath = join(OUT, rel);
162
+ mkdirSync(dirname(outPath), {recursive: true});
163
+ cpSync(src, outPath);
164
+ }
165
+ }
166
+
167
+ // Minify browser-facing JS and CSS
168
+ let jsCount = 0;
169
+ let cssCount = 0;
170
+
171
+ // Collect all minify targets, deduplicate against plugin as-is
172
+ const pluginAsIsSet = new Set(pluginAsIs);
173
+
174
+ for (const pattern of MINIFY_PATTERNS) {
175
+ const files = await collectFiles(pattern);
176
+ for (const rel of files) {
177
+ if (pluginAsIsSet.has(rel)) continue; // skip plugin server-side JS
178
+ const loader = rel.endsWith('.css') ? 'css' : 'js';
179
+ await minifyFile(rel, loader);
180
+ if (loader === 'js') jsCount++;
181
+ else cssCount++;
182
+ }
183
+ }
184
+
185
+ const totalCopied = COPY_AS_IS.filter(i => existsSync(join(ROOT, i))).length
186
+ + pluginAsIs.length + adminHtml.length;
187
+
188
+ console.log(`Built into _publish/ — JS: ${jsCount} minified, CSS: ${cssCount} minified, ${totalCopied} entries copied`);
189
+ }
190
+
191
+ build().catch(err => {
192
+ console.error('Build failed:', err.message);
193
+ process.exit(1);
194
+ });