domma-cms 0.92.1 → 0.94.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 (177) hide show
  1. package/CLAUDE.md +5 -3
  2. package/admin/css/admin.css +1 -1
  3. package/admin/js/app.js +2 -2
  4. package/admin/js/lib/action-editor-arrange.js +1 -1
  5. package/admin/js/lib/api-tokens-arrange.js +2 -2
  6. package/admin/js/lib/block-editor-arrange.js +1 -1
  7. package/admin/js/lib/blocks-arrange.js +1 -1
  8. package/admin/js/lib/collection-entries-arrange.js +1 -1
  9. package/admin/js/lib/components-arrange.js +1 -1
  10. package/admin/js/lib/dashboard-arrange.js +1 -1
  11. package/admin/js/lib/dates.js +1 -0
  12. package/admin/js/lib/forms-arrange.js +1 -1
  13. package/admin/js/lib/media-arrange.js +1 -1
  14. package/admin/js/lib/notifications-arrange.js +1 -1
  15. package/admin/js/lib/pages-arrange.js +1 -1
  16. package/admin/js/lib/related.js +1 -1
  17. package/admin/js/lib/timeline-builder.js +2 -2
  18. package/admin/js/templates/action-editor.html +6 -5
  19. package/admin/js/templates/actions-list.html +1 -1
  20. package/admin/js/templates/contacts.html +1 -1
  21. package/admin/js/templates/docs/api-actions.html +86 -60
  22. package/admin/js/templates/docs/api-authentication.html +159 -123
  23. package/admin/js/templates/docs/api-builder.html +197 -0
  24. package/admin/js/templates/docs/api-collections.html +199 -259
  25. package/admin/js/templates/docs/api-external.html +225 -0
  26. package/admin/js/templates/docs/api-forms.html +268 -0
  27. package/admin/js/templates/docs/api-layouts.html +70 -45
  28. package/admin/js/templates/docs/api-media.html +57 -80
  29. package/admin/js/templates/docs/api-navigation.html +66 -22
  30. package/admin/js/templates/docs/api-pages.html +109 -129
  31. package/admin/js/templates/docs/api-plugins.html +123 -61
  32. package/admin/js/templates/docs/api-scaffold.html +185 -0
  33. package/admin/js/templates/docs/api-settings.html +72 -64
  34. package/admin/js/templates/docs/api-users.html +74 -107
  35. package/admin/js/templates/docs/api-views.html +68 -54
  36. package/admin/js/templates/docs/components-howto.html +20 -17
  37. package/admin/js/templates/docs/components-reference.html +13 -16
  38. package/admin/js/templates/docs/components-rules.html +7 -6
  39. package/admin/js/templates/docs/components-walkthrough.html +19 -19
  40. package/admin/js/templates/docs/tutorial-crud.html +71 -40
  41. package/admin/js/templates/docs/tutorial-forms.html +51 -35
  42. package/admin/js/templates/docs/tutorial-plugin.html +132 -56
  43. package/admin/js/templates/docs/usage-actions.html +61 -15
  44. package/admin/js/templates/docs/usage-collections.html +108 -0
  45. package/admin/js/templates/docs/usage-cta-shortcode.html +14 -3
  46. package/admin/js/templates/docs/usage-dconfig.html +0 -3
  47. package/admin/js/templates/docs/usage-editions.html +213 -0
  48. package/admin/js/templates/docs/usage-media.html +22 -6
  49. package/admin/js/templates/docs/usage-navigation.html +74 -18
  50. package/admin/js/templates/docs/usage-pages.html +60 -20
  51. package/admin/js/templates/docs/usage-plugins.html +89 -17
  52. package/admin/js/templates/docs/usage-shortcodes.html +123 -70
  53. package/admin/js/templates/docs/usage-site-settings.html +50 -18
  54. package/admin/js/templates/docs/usage-tools.html +73 -0
  55. package/admin/js/templates/docs/usage-users-roles.html +99 -20
  56. package/admin/js/templates/docs/usage-views.html +36 -19
  57. package/admin/js/templates/documentation.html +153 -32
  58. package/admin/js/templates/page-editor.html +0 -5
  59. package/admin/js/templates/plugin-guide.html +15 -0
  60. package/admin/js/templates/plugin-guides.html +21 -0
  61. package/admin/js/templates/pro-docs.html +53 -234
  62. package/admin/js/templates/tutorials.html +5 -4
  63. package/admin/js/views/actions-list.js +3 -3
  64. package/admin/js/views/analytics.js +5 -5
  65. package/admin/js/views/api-endpoint-editor.js +2 -2
  66. package/admin/js/views/block-editor.js +4 -4
  67. package/admin/js/views/blocks.js +4 -4
  68. package/admin/js/views/collection-editor.js +4 -4
  69. package/admin/js/views/collection-entries.js +7 -7
  70. package/admin/js/views/component-editor.js +2 -2
  71. package/admin/js/views/contacts.js +22 -20
  72. package/admin/js/views/context-menu-editor.js +5 -5
  73. package/admin/js/views/doc-pages.js +1 -1
  74. package/admin/js/views/form-editor.js +4 -4
  75. package/admin/js/views/form-submissions.js +2 -2
  76. package/admin/js/views/index.js +1 -1
  77. package/admin/js/views/media.js +3 -3
  78. package/admin/js/views/menu-editor.js +13 -13
  79. package/admin/js/views/menu-locations.js +2 -2
  80. package/admin/js/views/my-profile.js +1 -1
  81. package/admin/js/views/page-editor.js +8 -8
  82. package/admin/js/views/plugin-guides.js +5 -0
  83. package/admin/js/views/project-detail.js +2 -2
  84. package/admin/js/views/project-settings.js +1 -1
  85. package/admin/js/views/role-editor.js +4 -4
  86. package/admin/js/views/search.js +2 -2
  87. package/admin/js/views/seo.js +17 -17
  88. package/admin/js/views/settings.js +3 -3
  89. package/admin/js/views/theme.js +3 -3
  90. package/admin/js/views/user-editor.js +1 -1
  91. package/admin/js/views/users.js +2 -2
  92. package/admin/js/views/view-editor.js +1 -1
  93. package/bin/cli.js +13 -13
  94. package/bin/lib/node-version.js +29 -0
  95. package/package.json +1 -1
  96. package/plugins/_lib/admin/mail/compose-window.js +3 -2
  97. package/plugins/_lib/admin/mail/reader-view.js +7 -6
  98. package/plugins/_lib/admin/mail/scheduling.js +4 -2
  99. package/plugins/_lib/admin/mail/templates.js +4 -4
  100. package/plugins/_lib/admin/ui/dates.js +85 -0
  101. package/plugins/blog/CLAUDE.md +31 -22
  102. package/plugins/blog/admin/views/blog.js +3 -2
  103. package/plugins/blog/admin/views/comments.js +2 -1
  104. package/plugins/blog/admin/views/post-editor.js +4 -4
  105. package/plugins/blog/blocks/blog-card-row.html +1 -1
  106. package/plugins/blog/blocks/blog-card.html +2 -2
  107. package/plugins/blog/blocks/blog-post-classic.html +2 -2
  108. package/plugins/blog/blocks/blog-post-essay.html +2 -2
  109. package/plugins/blog/blocks/blog-post-feature.html +2 -2
  110. package/plugins/blog/blocks/blog-post-minimal.html +2 -2
  111. package/plugins/blog/blocks/blog-post-sidebar.html +2 -2
  112. package/plugins/blog/blocks/blog-post-split.html +2 -2
  113. package/plugins/blog/docs/guide.md +205 -0
  114. package/plugins/blog/lib/layouts.js +3 -3
  115. package/plugins/blog/lib/page.js +2 -1
  116. package/plugins/blog/plugin.js +3 -3
  117. package/plugins/blog/plugin.json +4 -4
  118. package/plugins/blog/tests/layouts.test.js +6 -0
  119. package/plugins/feedback/CLAUDE.md +22 -3
  120. package/plugins/feedback/admin/lib/kit.js +6 -7
  121. package/plugins/feedback/admin/views/feedback.js +79 -10
  122. package/plugins/feedback/admin/views/send.js +28 -6
  123. package/plugins/feedback/docs/guide.md +95 -0
  124. package/plugins/feedback/lib/receiver.js +9 -2
  125. package/plugins/feedback/lib/sender.js +3 -2
  126. package/plugins/feedback/plugin.js +54 -6
  127. package/plugins/feedback/plugin.json +4 -4
  128. package/plugins/feedback/tests/api.test.js +74 -2
  129. package/plugins/free-tier.lock.json +49 -44
  130. package/plugins/mail-reader/CLAUDE.md +33 -18
  131. package/plugins/mail-reader/docs/guide.md +147 -0
  132. package/plugins/mail-reader/plugin.json +1 -1
  133. package/plugins/security/CLAUDE.md +4 -1
  134. package/plugins/security/admin/views/security.js +5 -5
  135. package/plugins/security/docs/guide.md +170 -0
  136. package/plugins/security/plugin.js +2 -1
  137. package/plugins/security/plugin.json +2 -1
  138. package/plugins/shopping-cart/CLAUDE.md +7 -1
  139. package/plugins/shopping-cart/admin/lib/kit.js +5 -2
  140. package/plugins/shopping-cart/admin/views/orders.js +4 -4
  141. package/plugins/shopping-cart/admin/views/overview.js +2 -2
  142. package/plugins/shopping-cart/docs/guide.md +191 -0
  143. package/plugins/shopping-cart/lib/render.js +2 -1
  144. package/plugins/shopping-cart/plugin.json +3 -3
  145. package/public/js/collection-browser.js +2 -2
  146. package/public/js/site.js +1 -1
  147. package/scripts/gen-instance-secret.js +3 -1
  148. package/scripts/setup.js +3 -1
  149. package/server/middleware/auth.js +2 -1
  150. package/server/routes/api/actions.js +47 -27
  151. package/server/routes/api/blocks.js +2 -1
  152. package/server/routes/api/collections.js +16 -52
  153. package/server/routes/api/contacts.js +66 -3
  154. package/server/routes/api/documentation.js +42 -0
  155. package/server/routes/api/notifications.js +3 -2
  156. package/server/routes/api/users.js +10 -6
  157. package/server/server.js +16 -1
  158. package/server/services/actions.js +110 -34
  159. package/server/services/adapterRegistry.js +169 -16
  160. package/server/services/adapters/FileAdapter.js +25 -0
  161. package/server/services/adapters/MongoAdapter.js +23 -0
  162. package/server/services/collections.js +104 -1
  163. package/server/services/connectionManager.js +12 -0
  164. package/server/services/dates.js +81 -0
  165. package/server/services/docs.js +13 -2
  166. package/server/services/markdown.js +75 -26
  167. package/server/services/notification-sources.js +6 -5
  168. package/server/services/passwordReset.js +2 -1
  169. package/server/services/permissionRegistry.js +3 -2
  170. package/server/services/pluginGuides.js +255 -0
  171. package/server/services/pluginInstaller.js +54 -13
  172. package/server/services/plugins.js +29 -1
  173. package/server/services/presetCollections.js +31 -5
  174. package/server/services/renderer.js +2 -2
  175. package/server/services/sidebarBadges.js +3 -1
  176. package/server/services/tools.js +4 -2
  177. package/server/templates/page.html +2 -2
@@ -1,4 +1,4 @@
1
- const __browsers=[];(function(){const t=document.querySelectorAll(".dm-collection-browser");t.length&&(t.forEach((e,i)=>{try{__browsers.push(new CollectionBrowser(e,i))}catch(n){console.error("[collection-browser] hydration failed:",n)}}),installGlobalKeyboardNav())})();function installGlobalKeyboardNav(){__browsers.length&&document.addEventListener("keydown",l=>{const t=l.target&&(l.target.tagName==="INPUT"||l.target.tagName==="TEXTAREA"||l.target.isContentEditable);if(l.key==="/"&&!t){const i=__browsers[0]?.el.querySelector(".dm-cb-search-input");i&&(l.preventDefault(),i.focus(),i.select());return}if((l.key==="ArrowLeft"||l.key==="ArrowRight")&&!t){const e=__browsers.find(s=>s.el.contains(l.target))||__browsers[0];if(!e)return;const i=Math.max(1,Math.ceil(e.currentTotal/e.config.pageSize)),n=l.key==="ArrowRight"?Math.min(i,e.state.page+1):Math.max(1,e.state.page-1);n!==e.state.page&&(l.preventDefault(),e.setState({page:n}))}})}function decodePayload(l){if(!l)return null;try{const t=atob(l),e=decodeURIComponent(escape(t));return JSON.parse(e)}catch{try{return JSON.parse(atob(l))}catch{return null}}}function clear(l){l&&typeof l.replaceChildren=="function"&&l.replaceChildren()}function distinctValues(l,t){const e=new Set;for(const i of l||[]){const n=i?.data?.[t];n!=null&&(Array.isArray(n)?n.forEach(s=>s!=null&&e.add(String(s))):e.add(String(n)))}return[...e].sort()}class CollectionBrowser{constructor(t,e){this.el=t,this.config=decodePayload(t.dataset.cbConfig)||{},this.schema=decodePayload(t.dataset.cbSchema)||{fields:[]};const i=decodePayload(t.dataset.cbData)||{entries:[],total:0,page:1,pageSize:12},n=(this.config.id||`${this.config.slug}-${e}`).replace(/[^a-z0-9]/gi,"_");let s=n,r=1;for(;__browsers.some(a=>a.idScope===s);)r+=1,s=`${n}_${r}`;this.idScope=s,this.urlPrefix=`cb_${s}_`,this._fragReqId=0,this.entries=i.entries||[],this.total=i.total||0,this.state={search:"",filter:{},sort:this.config.initial?.sort||"createdAt",order:this.config.initial?.order||"desc",page:1},this.readUrlState(),this.buildSkeleton(),this.refresh()}buildSkeleton(){clear(this.el);const t=elt("div","dm-cb-header");this.config.filterable?.length&&t.appendChild(this.buildDrawerToggle()),this.config.searchable&&t.appendChild(this.buildSearchInput()),this.config.sortable&&t.appendChild(this.buildSortControl()),this.config.savedSearches!==!1&&t.appendChild(this.buildSavedSearches()),this.config.exportable&&t.appendChild(this.buildExportButton());const e=elt("div","dm-cb-layout");this.filterRail=elt("aside","dm-cb-filter-rail"),this.bodyEl=elt("div","dm-cb-body"),e.appendChild(this.filterRail),e.appendChild(this.bodyEl),this.config.filterable?.length?this.renderFilterRail():this.filterRail.style.display="none",this.pagerEl=elt("div","dm-cb-pagination"),t.childElementCount&&this.el.appendChild(t),this.el.appendChild(e),this.el.appendChild(this.pagerEl),this.el.classList.add("dm-cb-hydrated")}buildSavedSearches(){const t=elt("div","dm-cb-saved"),e=elt("button","dm-cb-saved-toggle btn btn-outline");e.type="button",e.textContent="Saved \u25BE",t.appendChild(e);const i=elt("div","dm-cb-saved-menu");i.hidden=!0,t.appendChild(i);const n=()=>{clear(i);const s=this.loadSavedSearches();if(!s.length){const o=elt("p","dm-cb-saved-empty");o.textContent="No saved views yet.",i.appendChild(o)}for(const o of s){const c=elt("div","dm-cb-saved-item"),d=elt("button","dm-cb-saved-apply");d.type="button",d.textContent=o.name,d.addEventListener("click",()=>{this.state.search=o.search||"",this.state.filter={...o.filter||{}},this.state.sort=o.sort||this.config.initial?.sort||"createdAt",this.state.order=o.order||this.config.initial?.order||"desc",this.state.page=1,this.renderFilterRail();const p=this.el.querySelector(".dm-cb-search-input");p&&(p.value=this.state.search),i.hidden=!0,this.refresh()});const h=elt("button","dm-cb-saved-del");h.type="button",h.textContent="\xD7",h.title="Delete saved view",h.addEventListener("click",p=>{p.stopPropagation(),this.deleteSavedSearch(o.name),n()}),c.appendChild(d),c.appendChild(h),i.appendChild(c)}const r=elt("div","dm-cb-saved-actions"),a=elt("button","dm-cb-saved-create btn btn-primary");a.type="button",a.textContent="Save current view",a.addEventListener("click",async()=>{const o=await this.promptName();o&&(this.addSavedSearch({name:o,search:this.state.search,filter:this.state.filter,sort:this.state.sort,order:this.state.order}),n())}),r.appendChild(a),i.appendChild(r)};return e.addEventListener("click",()=>{i.hidden=!i.hidden,i.hidden||n()}),document.addEventListener("click",s=>{t.contains(s.target)||(i.hidden=!0)}),t}async promptName(){const t=typeof window<"u"&&typeof window.prompt=="function"?window.prompt("Name this saved view:"):null;return t&&t.trim()||null}loadSavedSearches(){try{const t=`cb_saved_${this.idScope}`,e=typeof S<"u"&&S.get?S.get(t):null;return Array.isArray(e)?e:[]}catch{return[]}}addSavedSearch(t){const e=this.loadSavedSearches().filter(i=>i.name!==t.name);e.push(t);try{typeof S<"u"&&S.set&&S.set(`cb_saved_${this.idScope}`,e)}catch{}}deleteSavedSearch(t){const e=this.loadSavedSearches().filter(i=>i.name!==t);try{typeof S<"u"&&S.set&&S.set(`cb_saved_${this.idScope}`,e)}catch{}}buildExportButton(){const t=elt("button","dm-cb-export btn btn-outline");return t.type="button",t.textContent="Export CSV",t.addEventListener("click",()=>this.exportCsv()),t}exportCsv(){if(this.config.mode==="client"){let n=this.entries;if(this.state.search){const o=this.state.search.toLowerCase();n=n.filter(c=>Object.values(c.data||{}).some(d=>String(d).toLowerCase().includes(o)))}const s={...this.state.filter,...this.config.baked||{}};n=applyFilterInMemory(n,s);const r=this.visibleFields(n),a=toCsv(n,r);downloadCsv(`${this.config.slug}-export.csv`,a);return}const t=new URLSearchParams;t.set("format","csv"),this.state.search&&t.set("search",this.state.search);const e={...this.state.filter,...this.config.baked||{}};for(const[n,s]of Object.entries(e))t.set(`filter[${n}]`,s);const i=`/api/collections/${encodeURIComponent(this.config.slug)}/export?${t.toString()}`;window.open(i,"_blank")}buildDrawerToggle(){const t=elt("button","dm-cb-drawer-toggle btn btn-outline");t.type="button",t.setAttribute("aria-label","Toggle filters");const e=elt("span","dm-cb-drawer-label");e.textContent="Filters";const i=elt("span","dm-cb-drawer-count");return t.appendChild(e),t.appendChild(document.createTextNode(" ")),t.appendChild(i),t.addEventListener("click",()=>{this.el.classList.toggle("dm-cb-drawer-open")}),t}buildSearchInput(){const t=elt("div","dm-cb-search"),e=elt("input","dm-cb-search-input");return e.type="search",e.placeholder="Search\u2026",e.value=this.state.search,e.addEventListener("input",debounce(()=>{this.setState({search:e.value,page:1})},200)),t.appendChild(e),t}buildSortControl(){const t=elt("div","dm-cb-sort"),e=elt("label","dm-cb-sort-label");e.textContent="Sort:",t.appendChild(e);const i=elt("select","dm-cb-sort-select"),n=this.sortableFieldList();for(const r of n){const a=elt("option");a.value=r.name,a.textContent=r.label,r.name===this.state.sort&&(a.selected=!0),i.appendChild(a)}i.addEventListener("change",()=>this.setState({sort:i.value,page:1})),t.appendChild(i);const s=elt("button","dm-cb-sort-dir");return s.type="button",s.textContent=this.state.order==="asc"?"\u2191":"\u2193",s.title=this.state.order==="asc"?"Ascending":"Descending",s.addEventListener("click",()=>{this.setState({order:this.state.order==="asc"?"desc":"asc",page:1}),s.textContent=this.state.order==="asc"?"\u2191":"\u2193"}),t.appendChild(s),t}sortableFieldList(){const t=this.config.sortableFields?.length?new Set(this.config.sortableFields):null,e=[{name:"createdAt",label:"Most recent"}];for(const i of this.schema.fields||[])t&&!t.has(i.name)||e.push({name:i.name,label:i.label||i.name});return e}renderFilterRail(){clear(this.filterRail);const t=elt("h4","dm-cb-filter-heading");t.textContent="Filters",this.filterRail.appendChild(t);for(const i of this.config.filterable){const n=(this.schema.fields||[]).find(r=>r.name===i);if(!n)continue;const s=this.buildFilterControl(n);s&&this.filterRail.appendChild(s)}const e=elt("button","dm-cb-filter-reset");e.type="button",e.textContent="Clear filters",e.addEventListener("click",()=>this.setState({filter:{},search:"",page:1})),this.filterRail.appendChild(e)}buildFilterControl(t){const e=elt("div","dm-cb-filter"),i=elt("label","dm-cb-filter-label");switch(i.textContent=t.label||t.name,e.appendChild(i),t.type){case"reference":e.appendChild(this.buildReferenceFilter(t));break;case"select":case"chooser":e.appendChild(this.buildSelectControl(t,!1));break;case"multiselect":case"tags":case"tag":e.appendChild(this.buildSelectControl(t,!0));break;case"number":e.appendChild(this.buildRangeControl(t,"number"));break;case"date":case"datetime":e.appendChild(this.buildRangeControl(t,"date"));break;case"boolean":case"checkbox":e.appendChild(this.buildBooleanControl(t));break;default:e.appendChild(this.buildTextControl(t));break}return e}buildReferenceFilter(t){const e=new Map;for(const r of this.entries){const a=r._refs?.[t.name];if(!a)continue;const o=Array.isArray(a)?a:[a];for(const c of o)!c||c.missing||e.has(c.id)||e.set(c.id,c.display)}const i=`${t.name}_eq`,n=elt("select","dm-cb-select"),s=elt("option");s.value="",s.textContent=this.config.mode==="server"?"Any (loaded)":"Any",n.appendChild(s);for(const[r,a]of e){const o=elt("option");o.value=r,o.textContent=a,String(this.state.filter[i]??"")===String(r)&&(o.selected=!0),n.appendChild(o)}return n.addEventListener("change",()=>this.updateFilter(i,n.value)),n}buildTextControl(t){const e=`${t.name}_contains`,i=elt("input","dm-cb-text");return i.type="text",i.placeholder="Contains\u2026",i.value=this.state.filter[e]||"",i.addEventListener("input",debounce(()=>{this.updateFilter(e,i.value.trim())},200)),i}buildSelectControl(t,e){const i=Array.isArray(t.options)&&t.options.length?t.options.map(o=>typeof o=="object"?o:{value:o,label:o}):distinctValues(this.entries,t.name).map(o=>({value:o,label:o})),n=this.config.mode==="client"?this.facetCounts(t.name,e?"_in":"_eq",i.map(o=>o.value)):null;if(e){const o=`${t.name}_in`,c=elt("div","dm-cb-chips"),d=new Set((this.state.filter[o]||"").split(",").filter(Boolean));for(const h of i){const p=elt("button","dm-cb-chip");p.type="button",d.has(String(h.value))&&p.classList.add("dm-cb-chip-active");const u=elt("span","dm-cb-chip-label");if(u.textContent=h.label??h.value,p.appendChild(u),n){const g=elt("span","dm-cb-chip-count");g.textContent=n[String(h.value)]??0,p.appendChild(g)}p.addEventListener("click",()=>{d.has(String(h.value))?d.delete(String(h.value)):d.add(String(h.value)),this.updateFilter(o,d.size?[...d].join(","):"")}),c.appendChild(p)}return c}const s=`${t.name}_eq`,r=elt("select","dm-cb-select"),a=elt("option");a.value="",a.textContent="Any",r.appendChild(a);for(const o of i){const c=elt("option");c.value=o.value;const d=n?` (${n[String(o.value)]??0})`:"";c.textContent=(o.label??o.value)+d,String(this.state.filter[s]??"")===String(o.value)&&(c.selected=!0),r.appendChild(c)}return r.addEventListener("change",()=>this.updateFilter(s,r.value)),r}facetCounts(t,e,i){const n={};for(const[o,c]of Object.entries(this.state.filter||{})){const{field:d}=parseFilterKey(o);d!==t&&(n[o]=c)}let s=this.entries;if(this.state.search){const o=this.state.search.toLowerCase();s=s.filter(c=>Object.values(c.data||{}).some(d=>String(d).toLowerCase().includes(o)))}const r={...n,...this.config.baked||{}};s=applyFilterInMemory(s,r);const a={};for(const o of i){const c={...r,[`${t}${e}`]:String(o)};a[String(o)]=applyFilterInMemory(s,c).length}return a}buildRangeControl(t,e){const i=`${t.name}_gte`,n=`${t.name}_lte`,s=elt("div","dm-cb-range"),r=elt("div","dm-cb-range-inputs"),a=elt("input","dm-cb-range-min"),o=elt("input","dm-cb-range-max");a.type=e,o.type=e,a.placeholder="Min",o.placeholder="Max",a.value=this.state.filter[i]||"",o.value=this.state.filter[n]||"",r.appendChild(a),r.appendChild(document.createTextNode(" - ")),r.appendChild(o),s.appendChild(r);let c=null;if(e==="number"&&this.config.mode==="client"&&this.entries.length){const h=this.entries.map(p=>Number(p.data?.[t.name])).filter(p=>Number.isFinite(p));if(h.length){const p=Math.min(...h),u=Math.max(...h);u>p&&(c=this.buildDualSlider(p,u,a,o,i,n),s.appendChild(c))}}const d=debounce(()=>{this.updateFilter(i,a.value,!0),this.updateFilter(n,o.value),c&&syncSliderToInputs(c,a.value,o.value)},250);return a.addEventListener("input",d),o.addEventListener("input",d),s}buildDualSlider(t,e,i,n,s,r){const a=elt("div","dm-cb-slider");a.dataset.lo=String(t),a.dataset.hi=String(e);const o=elt("div","dm-cb-slider-track"),c=elt("div","dm-cb-slider-fill");o.appendChild(c),a.appendChild(o);const d=elt("input","dm-cb-slider-handle dm-cb-slider-handle-min"),h=elt("input","dm-cb-slider-handle dm-cb-slider-handle-max");for(const[f,m]of[[d,t],[h,e]])f.type="range",f.min=String(t),f.max=String(e),f.step="any",f.value=String(m);d.value=String(i.value||t),h.value=String(n.value||e),a.appendChild(d),a.appendChild(h);const p=()=>{const f=Math.min(Number(d.value),Number(h.value)),m=Math.max(Number(d.value),Number(h.value)),b=e-t||1;c.style.left=`${(f-t)/b*100}%`,c.style.right=`${100-(m-t)/b*100}%`};p();const u=()=>{Number(d.value)>Number(h.value)&&(d.value=h.value),p(),i.value=d.value,n.value=h.value};d.addEventListener("input",u),h.addEventListener("input",u);const g=()=>{this.updateFilter(s,Number(d.value)>t?d.value:"",!0),this.updateFilter(r,Number(h.value)<e?h.value:"")};return d.addEventListener("change",g),h.addEventListener("change",g),a}buildBooleanControl(t){const e=`${t.name}_eq`,i=elt("select","dm-cb-select");for(const[n,s]of[["","Any"],["true","Yes"],["false","No"]]){const r=elt("option");r.value=n,r.textContent=s,String(this.state.filter[e]??"")===n&&(r.selected=!0),i.appendChild(r)}return i.addEventListener("change",()=>this.updateFilter(e,i.value)),i}updateFilter(t,e,i=!1){const n={...this.state.filter};e===""||e==null?delete n[t]:n[t]=e,this.state.filter=n,this.state.page=1,i||this.refresh()}setState(t){Object.assign(this.state,t),this.refresh()}async refresh(){this.config.syncUrl&&this.writeUrlState(),this.config.mode==="server"&&await this.fetchPage(),this.renderBody(),this.renderPagination(),this.updateFacetCounts(),this.updateDrawerBadge()}updateFacetCounts(){if(this.config.mode==="client"&&this.filterRail)for(const t of this.config.filterable||[]){const e=(this.schema.fields||[]).find(a=>a.name===t);if(!e)continue;const i=e.type==="multiselect"||e.type==="tags"||e.type==="tag"?"_in":"_eq",n=Array.isArray(e.options)&&e.options.length?e.options.map(a=>typeof a=="object"?a:{value:a,label:a}):distinctValues(this.entries,t).map(a=>({value:a,label:a})),s=this.facetCounts(t,i,n.map(a=>a.value)),r=this.filterRail.querySelectorAll(".dm-cb-filter");for(const a of r){const o=a.querySelector(".dm-cb-filter-label");if(!o||o.textContent!==(e.label||e.name))continue;a.querySelectorAll(".dm-cb-chip").forEach((h,p)=>{const u=h.querySelector(".dm-cb-chip-count");u&&n[p]&&(u.textContent=s[String(n[p].value)]??0)});const d=a.querySelector("select.dm-cb-select");d&&Array.from(d.options).forEach(h=>{if(!h.value)return;const p=n.find(u=>String(u.value)===h.value)?.label??h.value;h.textContent=`${p} (${s[h.value]??0})`})}}}updateDrawerBadge(){const t=this.el.querySelector(".dm-cb-drawer-toggle");if(!t)return;const e=(this.state.search?1:0)+Object.keys(this.state.filter||{}).length,i=t.querySelector(".dm-cb-drawer-count");i&&(i.textContent=e>0?`(${e})`:"")}async fetchPage(){const t={...this.state.filter,...this.config.baked||{}},e=new URLSearchParams;e.set("page",this.state.page),e.set("limit",this.config.pageSize),e.set("sort",this.state.sort),e.set("order",this.state.order),this.state.search&&e.set("search",this.state.search);for(const[n,s]of Object.entries(t))e.set(`filter[${n}]`,s);this._fetchAbort&&this._fetchAbort.abort(),this._fetchAbort=new AbortController;const i=this.config.sourceKind==="view"?`/api/views/${encodeURIComponent(this.config.slug)}/public?${e.toString()}`:`/api/collections/${encodeURIComponent(this.config.slug)}/public?${e.toString()}`;try{const n=await fetch(i,{headers:this.authHeaders(),signal:this._fetchAbort.signal});if(!n.ok){this.entries=[],this.total=0;return}const s=await n.json(),r=s.entries||s.results||[];this.entries=r.map(a=>a.data?a:{id:a.id||"",data:a.data||a}),this.total=s.total||r.length}catch(n){if(n.name==="AbortError")return;this.entries=[],this.total=0}}authHeaders(){try{const t=typeof S<"u"&&S.get?S.get("auth_token"):null;return t?{Authorization:`Bearer ${t}`}:{}}catch{return{}}}pipeline(){if(this.config.mode==="server")return{visible:this.entries,total:this.total};let t=this.entries,e=null;if(this.state.search){const c=this.state.search.toLowerCase();e=new Map,t=t.filter(d=>{let h=0;for(const[p,u]of Object.entries(d.data||{})){if(u==null)continue;const g=String(u).toLowerCase();let f=0,m=0;for(;(f=g.indexOf(c,f))!==-1;)m++,f+=c.length;m>0&&(h+=m*(p===this.config.titleField?3:1))}return h>0&&e.set(d.id||d,h),h>0})}if(Object.keys(this.state.filter).length||Object.keys(this.config.baked||{}).length){const c={...this.state.filter,...this.config.baked||{}};t=applyFilterInMemory(t,c)}const i=this.config.initial?.sort||"createdAt",n=this.config.initial?.order||"desc",s=this.state.sort===i&&this.state.order===n;this.state.search&&e&&s?(t=[...t].sort((c,d)=>{const h=e.get(c.id||c)||0;return(e.get(d.id||d)||0)-h}),this._activeSortMode="relevance"):(t=sortEntries(t,this.state.sort,this.state.order),this._activeSortMode=null);const r=t.length,a=(this.state.page-1)*this.config.pageSize;return{visible:t.slice(a,a+this.config.pageSize),total:r}}renderBody(){const{visible:t,total:e}=this.pipeline();if(this.currentTotal=e,clear(this.bodyEl),!t.length){this.bodyEl.appendChild(this.buildEmptyState());return}const i=this.visibleFields(t);this.bodyEl.appendChild(this.renderForDisplay(t,i)),typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(this.bodyEl),this.config.cta&&typeof wireCTAButtons=="function"&&wireCTAButtons(this.bodyEl)}buildEmptyState(){const t=elt("div","dm-collection-display dm-collection-empty"),e=!!this.state.search||Object.keys(this.state.filter||{}).length>0,i=elt("p");if(e){i.textContent="No results for these filters.",t.appendChild(i);const n=elt("button","btn btn-outline dm-cb-empty-clear");n.type="button",n.textContent="Clear filters",n.addEventListener("click",()=>{this.setState({filter:{},search:"",page:1}),this.renderFilterRail();const s=this.el.querySelector(".dm-cb-search-input");s&&(s.value="")}),t.appendChild(n)}else i.textContent=this.config.emptyMsg,t.appendChild(i);return t}visibleFields(t){let e=this.schema.fields||[];return this.config.fields?.length&&(e=this.config.fields.map(i=>e.find(n=>n.name===i)||{name:i,label:i})),!e.length&&t.length&&(e=Object.keys(t[0].data||{}).map(i=>({name:i,label:i}))),e}renderCards(t,e){const i=elt("div",`dm-collection-cards grid grid-cols-${this.config.columns}`);for(const n of t){const s=elt("div","card dm-collection-card"),r=e.find(c=>c.name===this.config.titleField),a=r?fieldDisplay(n,r):this.config.titleField?formatValue(n.data?.[this.config.titleField]):"";if(a){const c=elt("div","card-header");c.textContent=a,s.appendChild(c)}const o=elt("div","card-body");for(const c of e){if(this.config.titleField===c.name||!fieldDisplay(n,c))continue;const h=elt("div","dm-collection-card-field"),p=elt("strong");p.textContent=(c.label||c.name)+": ",h.appendChild(p),h.appendChild(fieldDisplayNode(n,c)),o.appendChild(h)}this.config.cta&&o.appendChild(this.buildCtaButton(n)),this.config.transitions&&o.appendChild(this.buildTransitionsGroup(n)),s.appendChild(o),i.appendChild(s)}return i}renderList(t,e){const i=elt("ul","dm-collection-list");for(const n of t){const s=elt("li","dm-collection-list-item"),r=e.find(c=>c.name===this.config.titleField),a=r?fieldDisplay(n,r):this.config.titleField?formatValue(n.data?.[this.config.titleField]):"";if(a){const c=elt("div","dm-collection-list-title");c.textContent=a,s.appendChild(c)}const o=e.filter(c=>c.name!==this.config.titleField).map(c=>{const d=fieldDisplay(n,c);return d?`${c.label||c.name}: ${d}`:""}).filter(Boolean).join(" \xB7 ");if(o){const c=elt("div","dm-collection-list-meta");c.textContent=o,s.appendChild(c)}this.config.cta&&s.appendChild(this.buildCtaButton(n)),this.config.transitions&&s.appendChild(this.buildTransitionsGroup(n)),i.appendChild(s)}return i}renderTable(t,e){const i=elt("table","dm-collection-table"),n=elt("thead"),s=elt("tr");for(const a of e){const o=elt("th");o.textContent=a.label||a.name,s.appendChild(o)}this.config.cta&&s.appendChild(elt("th")),this.config.transitions&&s.appendChild(elt("th")),n.appendChild(s),i.appendChild(n);const r=elt("tbody");for(const a of t){const o=elt("tr");for(const c of e){const d=elt("td");d.appendChild(fieldDisplayNode(a,c)),o.appendChild(d)}if(this.config.cta){const c=elt("td");c.appendChild(this.buildCtaButton(a)),o.appendChild(c)}if(this.config.transitions){const c=elt("td");c.appendChild(this.buildTransitionsGroup(a)),o.appendChild(c)}r.appendChild(o)}return i.appendChild(r),i}renderAccordion(t){const e=elt("div","accordion dm-collection-accordion");for(const i of t){const n=elt("div","accordion-item"),s=this.config.titleField&&i.data?.[this.config.titleField]||"Untitled",r=this.config.bodyField&&i.data?.[this.config.bodyField]||"",a=elt("button","accordion-header");a.type="button",a.textContent=String(s);const o=elt("div","accordion-panel");o.textContent=String(r),a.addEventListener("click",()=>n.classList.toggle("open")),n.appendChild(a),n.appendChild(o),e.appendChild(n)}return e}buildCtaButton(t){const e=this.config.cta,i=elt("button",`btn btn-${e.style||"primary"} dm-cta-trigger`);if(i.dataset.action=e.action,i.dataset.entry=t.id||"",e.confirm&&(i.dataset.confirm=e.confirm),e.icon){const n=elt("span");n.dataset.icon=e.icon,i.appendChild(n),i.appendChild(document.createTextNode(" "))}return i.appendChild(document.createTextNode(e.label||"Run")),i}buildTransitionsGroup(t){const e=elt("div","dm-cb-transitions");return e.dataset.entry=t.id||"",e.textContent="Loading actions\u2026",Promise.resolve().then(()=>this.loadTransitions(t,e)),e}async loadTransitions(t,e){try{const i=new URLSearchParams({collection:this.config.slug,entryId:t.id||""}),n=await fetch(`/api/actions/transitions?${i.toString()}`,{headers:this.authHeaders()});if(clear(e),!n.ok)return;const{transitions:s}=await n.json();if(!Array.isArray(s)||!s.length)return;for(const r of s){const a=elt("button",`btn btn-${r.trigger?.style||"outline"} dm-cb-transition-btn dm-cta-trigger`);if(a.dataset.action=r.slug,a.dataset.entry=t.id||"",r.trigger?.confirmMessage&&(a.dataset.confirm=r.trigger.confirmMessage),r.trigger?.icon){const o=elt("span");o.dataset.icon=r.trigger.icon,a.appendChild(o),a.appendChild(document.createTextNode(" "))}a.appendChild(document.createTextNode(r.trigger?.label||r.title)),e.appendChild(a)}typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(e),typeof wireCTAButtons=="function"&&wireCTAButtons(e)}catch{clear(e)}}renderPagination(){if(this.config.pagination==="scroll"){this.renderInfiniteSentinel();return}const t=Math.max(1,Math.ceil(this.currentTotal/this.config.pageSize));if(clear(this.pagerEl),t<=1)return;const e=elt("button","dm-cb-page-prev btn btn-outline");e.type="button",e.textContent="\u2039 Prev",e.disabled=this.state.page<=1,e.addEventListener("click",()=>this.setState({page:Math.max(1,this.state.page-1)}));const i=elt("button","dm-cb-page-next btn btn-outline");i.type="button",i.textContent="Next \u203A",i.disabled=this.state.page>=t,i.addEventListener("click",()=>this.setState({page:Math.min(t,this.state.page+1)}));const n=elt("span","dm-cb-page-info");n.textContent=`Page ${this.state.page} of ${t} (${this.currentTotal} entr${this.currentTotal===1?"y":"ies"})`,this.pagerEl.appendChild(e),this.pagerEl.appendChild(n),this.pagerEl.appendChild(i)}renderInfiniteSentinel(){clear(this.pagerEl);const t=Math.max(1,Math.ceil(this.currentTotal/this.config.pageSize));if(this.state.page>=t){const i=elt("p","dm-cb-scroll-end");i.textContent=`All ${this.currentTotal} entries loaded.`,this.pagerEl.appendChild(i);return}const e=elt("div","dm-cb-scroll-sentinel");e.setAttribute("aria-hidden","true"),e.textContent="Loading more\u2026",this.pagerEl.appendChild(e),this._scrollObserver&&this._scrollObserver.disconnect(),this._scrollObserver=new IntersectionObserver(async i=>{i[0].isIntersecting&&(this._scrollObserver.disconnect(),await this.loadMore())},{rootMargin:"200px 0px"}),this._scrollObserver.observe(e)}async loadMore(){const t=this.state.page+1,e=this.currentTotal,i=this.entries,n=this.state.page;this.state.page=t,this.config.syncUrl&&this.writeUrlState();let s;if(this.config.mode==="server")await this.fetchPage(),s=this.entries;else{const{visible:c}=this.pipeline();s=c}if(!s.length){this.state.page=n,this.entries=i,this.currentTotal=e,this.renderPagination();return}const r=this.visibleFields(s),a=this.renderForDisplay(s,r),o=this.bodyEl.firstElementChild;if(o&&o.tagName===a.tagName)for(;a.firstChild;)o.appendChild(a.firstChild);else this.bodyEl.appendChild(a);typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(this.bodyEl),this.config.cta&&typeof wireCTAButtons=="function"&&wireCTAButtons(this.bodyEl),this.renderPagination()}renderForDisplay(t,e){const i=this.config.display;return i==="list"?this.renderList(t,e):i==="table"?this.renderTable(t,e):i==="accordion"?this.renderAccordion(t):i==="timeline"?this.renderTimeline(t):i==="block"?this.renderBlockPlaceholder(t):this.renderCards(t,e)}renderTimeline(t){const e=["vertical","centred","horizontal"].includes(this.config.layout)?this.config.layout:"vertical",i=["minimal","corporate","modern"].includes(this.config.theme)?this.config.theme:"minimal",n=elt("div",`dm-collection-timeline timeline timeline-${e} timeline-${i}`);for(const s of t){const r=elt("div","timeline-event"),a=this.config.dateField&&s.data?.[this.config.dateField]||"",o=this.config.titleField&&s.data?.[this.config.titleField]||"",c=this.config.bodyField&&s.data?.[this.config.bodyField]||"",d=this.config.statusField&&s.data?.[this.config.statusField]||"",h=this.config.iconField&&s.data?.[this.config.iconField]||"";if(h){const p=elt("span","timeline-icon");p.dataset.icon=String(h),r.appendChild(p)}if(a){const p=elt("div","timeline-date");p.textContent=String(a),r.appendChild(p)}if(o){const p=elt("h4","timeline-title");p.textContent=String(o),r.appendChild(p)}if(d){const p=elt("span",`badge badge-${d}`);p.textContent=String(d),r.appendChild(p)}if(c){const p=elt("div","timeline-body");p.textContent=String(c),r.appendChild(p)}n.appendChild(r)}return n}renderBlockPlaceholder(t){const e=elt("div","dm-collection-display dm-cb-block-loading");return e.textContent="Rendering\u2026",this.fetchBlockFragment(t,e),e}async fetchBlockFragment(t,e){const i=++this._fragReqId,n=t.map(a=>a.id).filter(Boolean),s={slug:this.config.slug,display:"block",block:this.config.block,"title-field":this.config.titleField,"body-field":this.config.bodyField,fields:this.config.fields?this.config.fields.join(","):"",empty:this.config.emptyMsg,limit:String(n.length||1)},r={...this.state.filter,...this.config.baked||{}};for(const[a,o]of Object.entries(r))o!==""&&o!=null&&(s[`where_${a}`]=String(o));s.sort=this.state.sort,s.order=this.state.order,s.limit=String(this.config.pageSize);try{const a=btoa(unescape(encodeURIComponent(JSON.stringify(s)))),o=await fetch("/api/collections/render-fragment",{method:"POST",headers:{"Content-Type":"application/json",...this.authHeaders()},body:JSON.stringify({attrs:a,page:this.state.page})});if(i!==this._fragReqId)return;if(!o.ok){e.textContent=this.config.emptyMsg;return}const{html:c}=await o.json(),d=document.createElement("template");d.innerHTML=c||"",e.replaceWith(d.content)}catch{e.textContent="Unable to load block view."}}readUrlState(){if(!this.config.syncUrl)return;const t=new URLSearchParams(window.location.search),e=this.urlPrefix,i=r=>t.get(e+r);i("search")&&(this.state.search=i("search")),i("sort")&&(this.state.sort=i("sort")),i("order")&&(this.state.order=i("order")),i("page")&&(this.state.page=parseInt(i("page"),10)||1);const n={},s=`${e}filter[`;for(const[r,a]of t.entries())if(r.startsWith(s)&&r.endsWith("]")){const o=r.slice(s.length,-1);o&&(n[o]=a)}Object.keys(n).length&&(this.state.filter=n)}writeUrlState(){const t=new URL(window.location.href),e=this.urlPrefix,i=[];for(const s of t.searchParams.keys())s.startsWith(e)&&i.push(s);i.forEach(s=>t.searchParams.delete(s));const n=(s,r)=>{r!==""&&r!=null&&t.searchParams.set(e+s,r)};n("search",this.state.search),this.state.sort!==(this.config.initial?.sort||"createdAt")&&n("sort",this.state.sort),this.state.order!==(this.config.initial?.order||"desc")&&n("order",this.state.order),this.state.page!==1&&n("page",this.state.page);for(const[s,r]of Object.entries(this.state.filter))r!==""&&r!=null&&t.searchParams.set(`${e}filter[${s}]`,r);try{window.history.replaceState({},"",t.toString())}catch{}}}const OPERATORS=["_eq","_ne","_gte","_lte","_gt","_lt","_in","_nin","_contains","_starts","_ends","_exists"];function parseFilterKey(l){for(const t of OPERATORS)if(l.endsWith(t))return{field:l.slice(0,-t.length),op:t};return{field:l,op:"_eq"}}function resolveField(l,t){if(t==="createdBy")return l.meta?.createdBy;if(t==="id")return l.id;const e=t.split(".");let i=l.data;for(const n of e){if(i==null)return;i=i[n]}return i}function maybeNumber(l){if(typeof l=="number"||typeof l!="string")return l;const t=Number(l);return Number.isFinite(t)&&String(t)===l.trim()?t:l}function applyOp(l,t,e){switch(l){case"_eq":{const i=maybeNumber(t),n=maybeNumber(e);return Array.isArray(t)?t.map(maybeNumber).includes(n):i===n||String(i)===String(n)}case"_ne":{const i=maybeNumber(t),n=maybeNumber(e);return Array.isArray(t)?!t.map(maybeNumber).includes(n):!(i===n||String(i)===String(n))}case"_gt":return maybeNumber(t)>maybeNumber(e);case"_gte":return maybeNumber(t)>=maybeNumber(e);case"_lt":return maybeNumber(t)<maybeNumber(e);case"_lte":return maybeNumber(t)<=maybeNumber(e);case"_in":{const i=String(e).split(",").map(n=>maybeNumber(n.trim()));return Array.isArray(t)?t.map(maybeNumber).some(n=>i.includes(n)):i.includes(maybeNumber(t))}case"_nin":{const i=String(e).split(",").map(n=>maybeNumber(n.trim()));return Array.isArray(t)?!t.map(maybeNumber).some(n=>i.includes(n)):!i.includes(maybeNumber(t))}case"_contains":return String(t??"").toLowerCase().includes(String(e).toLowerCase());case"_starts":return String(t??"").toLowerCase().startsWith(String(e).toLowerCase());case"_ends":return String(t??"").toLowerCase().endsWith(String(e).toLowerCase());case"_exists":{const i=t!=null&&t!=="";return e==="true"||e==="1"||e===!0?i:!i}default:return!1}}function applyFilterInMemory(l,t){return!t||!Object.keys(t).length?l:l.filter(e=>{for(const[i,n]of Object.entries(t)){if(n==null||n==="")continue;const{field:s,op:r}=parseFilterKey(i),a=resolveField(e,s);if(!applyOp(r,a,n))return!1}return!0})}function sortEntries(l,t,e){const i=e==="asc"?1:-1;return[...l].sort((n,s)=>{const r=t==="createdAt"?n.meta?.createdAt||"":n.data?.[t]??"",a=t==="createdAt"?s.meta?.createdAt||"":s.data?.[t]??"";return r<a?-i:r>a?i:0})}function elt(l,t){const e=document.createElement(l);return t&&(e.className=t),e}function isFileRef(l){return l&&typeof l=="object"&&typeof l.url=="string"&&typeof l.name=="string"}function formatValue(l){return l==null?"":isFileRef(l)?l.name:Array.isArray(l)?l.join(", "):typeof l=="boolean"?l?"Yes":"No":String(l)}function fieldDisplay(l,t){if(t?.type==="reference"){const e=l?._refs?.[t.name];if(e!=null)return Array.isArray(e)?e.map(i=>i.missing?`${i.id} (missing)`:i.display).join(", "):e.missing?`${e.id} (missing)`:e.display}return formatValue(l?.data?.[t?.name])}function fieldDisplayNode(l,t){if(t?.type==="reference"){const i=l?._refs?.[t.name];if(i!=null){const n=document.createDocumentFragment();return(Array.isArray(i)?i:[i]).forEach((r,a)=>{if(a>0&&n.appendChild(document.createTextNode(", ")),r.missing)n.appendChild(document.createTextNode(`${r.id} (missing)`));else if(r.link){const o=document.createElement("a");o.href=r.link,o.textContent=r.display,n.appendChild(o)}else n.appendChild(document.createTextNode(r.display))}),n}}const e=l?.data?.[t?.name];if(isFileRef(e)){const i=document.createElement("a");if(i.href=e.url,String(e.mime||"").startsWith("image/")){i.target="_blank",i.rel="noopener";const n=document.createElement("img");n.src=e.url,n.alt=e.name,n.loading="lazy",n.className="dm-file-thumb",i.appendChild(n)}else i.download="",i.textContent=e.name;return i}return document.createTextNode(formatValue(e))}function debounce(l,t){let e;return(...i)=>{clearTimeout(e),e=setTimeout(()=>l(...i),t)}}function toCsv(l,t){const e=s=>{if(s==null)return"";let r=Array.isArray(s)?s.join(", "):String(s);return/[",\n\r]/.test(r)&&(r='"'+r.replace(/"/g,'""')+'"'),r},i=t.map(s=>e(s.label||s.name)).join(","),n=l.map(s=>t.map(r=>e(s.data?.[r.name])).join(",")).join(`
1
+ const __browsers=[];let __keyboardNavInstalled=!1;function bootstrapCollectionBrowsers(l=document){const t=[...l.querySelectorAll(".dm-collection-browser")].filter(e=>!e.__dmCollectionBrowser);t.length&&(t.forEach(e=>{e.__dmCollectionBrowser=!0;try{__browsers.push(new CollectionBrowser(e,__browsers.length))}catch(i){console.error("[collection-browser] hydration failed:",i)}}),!__keyboardNavInstalled&&__browsers.length&&(__keyboardNavInstalled=!0,installGlobalKeyboardNav()))}function installGlobalKeyboardNav(){__browsers.length&&document.addEventListener("keydown",l=>{const t=l.target&&(l.target.tagName==="INPUT"||l.target.tagName==="TEXTAREA"||l.target.isContentEditable);if(l.key==="/"&&!t){const i=__browsers[0]?.el.querySelector(".dm-cb-search-input");i&&(l.preventDefault(),i.focus(),i.select());return}if((l.key==="ArrowLeft"||l.key==="ArrowRight")&&!t){const e=__browsers.find(s=>s.el.contains(l.target))||__browsers[0];if(!e)return;const i=Math.max(1,Math.ceil(e.currentTotal/e.config.pageSize)),n=l.key==="ArrowRight"?Math.min(i,e.state.page+1):Math.max(1,e.state.page-1);n!==e.state.page&&(l.preventDefault(),e.setState({page:n}))}})}function decodePayload(l){if(!l)return null;try{const t=atob(l),e=decodeURIComponent(escape(t));return JSON.parse(e)}catch{try{return JSON.parse(atob(l))}catch{return null}}}function clear(l){l&&typeof l.replaceChildren=="function"&&l.replaceChildren()}function distinctValues(l,t){const e=new Set;for(const i of l||[]){const n=i?.data?.[t];n!=null&&(Array.isArray(n)?n.forEach(s=>s!=null&&e.add(String(s))):e.add(String(n)))}return[...e].sort()}class CollectionBrowser{constructor(t,e){this.el=t,this.config=decodePayload(t.dataset.cbConfig)||{},this.schema=decodePayload(t.dataset.cbSchema)||{fields:[]};const i=decodePayload(t.dataset.cbData)||{entries:[],total:0,page:1,pageSize:12},n=(this.config.id||`${this.config.slug}-${e}`).replace(/[^a-z0-9]/gi,"_");let s=n,r=1;for(;__browsers.some(a=>a.idScope===s);)r+=1,s=`${n}_${r}`;this.idScope=s,this.urlPrefix=`cb_${s}_`,this._fragReqId=0,this.entries=i.entries||[],this.total=i.total||0,this.state={search:"",filter:{},sort:this.config.initial?.sort||"createdAt",order:this.config.initial?.order||"desc",page:1},this.readUrlState(),this.buildSkeleton(),this.refresh()}buildSkeleton(){clear(this.el);const t=elt("div","dm-cb-header");this.config.filterable?.length&&t.appendChild(this.buildDrawerToggle()),this.config.searchable&&t.appendChild(this.buildSearchInput()),this.config.sortable&&t.appendChild(this.buildSortControl()),this.config.savedSearches!==!1&&t.appendChild(this.buildSavedSearches()),this.config.exportable&&t.appendChild(this.buildExportButton());const e=elt("div","dm-cb-layout");this.filterRail=elt("aside","dm-cb-filter-rail"),this.bodyEl=elt("div","dm-cb-body"),e.appendChild(this.filterRail),e.appendChild(this.bodyEl),this.config.filterable?.length?this.renderFilterRail():this.filterRail.style.display="none",this.pagerEl=elt("div","dm-cb-pagination"),t.childElementCount&&this.el.appendChild(t),this.el.appendChild(e),this.el.appendChild(this.pagerEl),this.el.classList.add("dm-cb-hydrated")}buildSavedSearches(){const t=elt("div","dm-cb-saved"),e=elt("button","dm-cb-saved-toggle btn btn-outline");e.type="button",e.textContent="Saved \u25BE",t.appendChild(e);const i=elt("div","dm-cb-saved-menu");i.hidden=!0,t.appendChild(i);const n=()=>{clear(i);const s=this.loadSavedSearches();if(!s.length){const o=elt("p","dm-cb-saved-empty");o.textContent="No saved views yet.",i.appendChild(o)}for(const o of s){const c=elt("div","dm-cb-saved-item"),d=elt("button","dm-cb-saved-apply");d.type="button",d.textContent=o.name,d.addEventListener("click",()=>{this.state.search=o.search||"",this.state.filter={...o.filter||{}},this.state.sort=o.sort||this.config.initial?.sort||"createdAt",this.state.order=o.order||this.config.initial?.order||"desc",this.state.page=1,this.renderFilterRail();const p=this.el.querySelector(".dm-cb-search-input");p&&(p.value=this.state.search),i.hidden=!0,this.refresh()});const h=elt("button","dm-cb-saved-del");h.type="button",h.textContent="\xD7",h.title="Delete saved view",h.addEventListener("click",p=>{p.stopPropagation(),this.deleteSavedSearch(o.name),n()}),c.appendChild(d),c.appendChild(h),i.appendChild(c)}const r=elt("div","dm-cb-saved-actions"),a=elt("button","dm-cb-saved-create btn btn-primary");a.type="button",a.textContent="Save current view",a.addEventListener("click",async()=>{const o=await this.promptName();o&&(this.addSavedSearch({name:o,search:this.state.search,filter:this.state.filter,sort:this.state.sort,order:this.state.order}),n())}),r.appendChild(a),i.appendChild(r)};return e.addEventListener("click",()=>{i.hidden=!i.hidden,i.hidden||n()}),document.addEventListener("click",s=>{t.contains(s.target)||(i.hidden=!0)}),t}async promptName(){const t=typeof window<"u"&&typeof window.prompt=="function"?window.prompt("Name this saved view:"):null;return t&&t.trim()||null}loadSavedSearches(){try{const t=`cb_saved_${this.idScope}`,e=typeof S<"u"&&S.get?S.get(t):null;return Array.isArray(e)?e:[]}catch{return[]}}addSavedSearch(t){const e=this.loadSavedSearches().filter(i=>i.name!==t.name);e.push(t);try{typeof S<"u"&&S.set&&S.set(`cb_saved_${this.idScope}`,e)}catch{}}deleteSavedSearch(t){const e=this.loadSavedSearches().filter(i=>i.name!==t);try{typeof S<"u"&&S.set&&S.set(`cb_saved_${this.idScope}`,e)}catch{}}buildExportButton(){const t=elt("button","dm-cb-export btn btn-outline");return t.type="button",t.textContent="Export CSV",t.addEventListener("click",()=>this.exportCsv()),t}exportCsv(){if(this.config.mode==="client"){let n=this.entries;if(this.state.search){const o=this.state.search.toLowerCase();n=n.filter(c=>Object.values(c.data||{}).some(d=>String(d).toLowerCase().includes(o)))}const s={...this.state.filter,...this.config.baked||{}};n=applyFilterInMemory(n,s);const r=this.visibleFields(n),a=toCsv(n,r);downloadCsv(`${this.config.slug}-export.csv`,a);return}const t=new URLSearchParams;t.set("format","csv"),this.state.search&&t.set("search",this.state.search);const e={...this.state.filter,...this.config.baked||{}};for(const[n,s]of Object.entries(e))t.set(`filter[${n}]`,s);const i=`/api/collections/${encodeURIComponent(this.config.slug)}/export?${t.toString()}`;window.open(i,"_blank")}buildDrawerToggle(){const t=elt("button","dm-cb-drawer-toggle btn btn-outline");t.type="button",t.setAttribute("aria-label","Toggle filters");const e=elt("span","dm-cb-drawer-label");e.textContent="Filters";const i=elt("span","dm-cb-drawer-count");return t.appendChild(e),t.appendChild(document.createTextNode(" ")),t.appendChild(i),t.addEventListener("click",()=>{this.el.classList.toggle("dm-cb-drawer-open")}),t}buildSearchInput(){const t=elt("div","dm-cb-search"),e=elt("input","dm-cb-search-input");return e.type="search",e.placeholder="Search\u2026",e.value=this.state.search,e.addEventListener("input",debounce(()=>{this.setState({search:e.value,page:1})},200)),t.appendChild(e),t}buildSortControl(){const t=elt("div","dm-cb-sort"),e=elt("label","dm-cb-sort-label");e.textContent="Sort:",t.appendChild(e);const i=elt("select","dm-cb-sort-select"),n=this.sortableFieldList();for(const r of n){const a=elt("option");a.value=r.name,a.textContent=r.label,r.name===this.state.sort&&(a.selected=!0),i.appendChild(a)}i.addEventListener("change",()=>this.setState({sort:i.value,page:1})),t.appendChild(i);const s=elt("button","dm-cb-sort-dir");return s.type="button",s.textContent=this.state.order==="asc"?"\u2191":"\u2193",s.title=this.state.order==="asc"?"Ascending":"Descending",s.addEventListener("click",()=>{this.setState({order:this.state.order==="asc"?"desc":"asc",page:1}),s.textContent=this.state.order==="asc"?"\u2191":"\u2193"}),t.appendChild(s),t}sortableFieldList(){const t=this.config.sortableFields?.length?new Set(this.config.sortableFields):null,e=[{name:"createdAt",label:"Most recent"}];for(const i of this.schema.fields||[])t&&!t.has(i.name)||e.push({name:i.name,label:i.label||i.name});return e}renderFilterRail(){clear(this.filterRail);const t=elt("h4","dm-cb-filter-heading");t.textContent="Filters",this.filterRail.appendChild(t);for(const i of this.config.filterable){const n=(this.schema.fields||[]).find(r=>r.name===i);if(!n)continue;const s=this.buildFilterControl(n);s&&this.filterRail.appendChild(s)}const e=elt("button","dm-cb-filter-reset");e.type="button",e.textContent="Clear filters",e.addEventListener("click",()=>this.setState({filter:{},search:"",page:1})),this.filterRail.appendChild(e)}buildFilterControl(t){const e=elt("div","dm-cb-filter"),i=elt("label","dm-cb-filter-label");switch(i.textContent=t.label||t.name,e.appendChild(i),t.type){case"reference":e.appendChild(this.buildReferenceFilter(t));break;case"select":case"chooser":e.appendChild(this.buildSelectControl(t,!1));break;case"multiselect":case"tags":case"tag":e.appendChild(this.buildSelectControl(t,!0));break;case"number":e.appendChild(this.buildRangeControl(t,"number"));break;case"date":case"datetime":e.appendChild(this.buildRangeControl(t,"date"));break;case"boolean":case"checkbox":e.appendChild(this.buildBooleanControl(t));break;default:e.appendChild(this.buildTextControl(t));break}return e}buildReferenceFilter(t){const e=new Map;for(const r of this.entries){const a=r._refs?.[t.name];if(!a)continue;const o=Array.isArray(a)?a:[a];for(const c of o)!c||c.missing||e.has(c.id)||e.set(c.id,c.display)}const i=`${t.name}_eq`,n=elt("select","dm-cb-select"),s=elt("option");s.value="",s.textContent=this.config.mode==="server"?"Any (loaded)":"Any",n.appendChild(s);for(const[r,a]of e){const o=elt("option");o.value=r,o.textContent=a,String(this.state.filter[i]??"")===String(r)&&(o.selected=!0),n.appendChild(o)}return n.addEventListener("change",()=>this.updateFilter(i,n.value)),n}buildTextControl(t){const e=`${t.name}_contains`,i=elt("input","dm-cb-text");return i.type="text",i.placeholder="Contains\u2026",i.value=this.state.filter[e]||"",i.addEventListener("input",debounce(()=>{this.updateFilter(e,i.value.trim())},200)),i}buildSelectControl(t,e){const i=Array.isArray(t.options)&&t.options.length?t.options.map(o=>typeof o=="object"?o:{value:o,label:o}):distinctValues(this.entries,t.name).map(o=>({value:o,label:o})),n=this.config.mode==="client"?this.facetCounts(t.name,e?"_in":"_eq",i.map(o=>o.value)):null;if(e){const o=`${t.name}_in`,c=elt("div","dm-cb-chips"),d=new Set((this.state.filter[o]||"").split(",").filter(Boolean));for(const h of i){const p=elt("button","dm-cb-chip");p.type="button",d.has(String(h.value))&&p.classList.add("dm-cb-chip-active");const u=elt("span","dm-cb-chip-label");if(u.textContent=h.label??h.value,p.appendChild(u),n){const g=elt("span","dm-cb-chip-count");g.textContent=n[String(h.value)]??0,p.appendChild(g)}p.addEventListener("click",()=>{d.has(String(h.value))?d.delete(String(h.value)):d.add(String(h.value)),this.updateFilter(o,d.size?[...d].join(","):"")}),c.appendChild(p)}return c}const s=`${t.name}_eq`,r=elt("select","dm-cb-select"),a=elt("option");a.value="",a.textContent="Any",r.appendChild(a);for(const o of i){const c=elt("option");c.value=o.value;const d=n?` (${n[String(o.value)]??0})`:"";c.textContent=(o.label??o.value)+d,String(this.state.filter[s]??"")===String(o.value)&&(c.selected=!0),r.appendChild(c)}return r.addEventListener("change",()=>this.updateFilter(s,r.value)),r}facetCounts(t,e,i){const n={};for(const[o,c]of Object.entries(this.state.filter||{})){const{field:d}=parseFilterKey(o);d!==t&&(n[o]=c)}let s=this.entries;if(this.state.search){const o=this.state.search.toLowerCase();s=s.filter(c=>Object.values(c.data||{}).some(d=>String(d).toLowerCase().includes(o)))}const r={...n,...this.config.baked||{}};s=applyFilterInMemory(s,r);const a={};for(const o of i){const c={...r,[`${t}${e}`]:String(o)};a[String(o)]=applyFilterInMemory(s,c).length}return a}buildRangeControl(t,e){const i=`${t.name}_gte`,n=`${t.name}_lte`,s=elt("div","dm-cb-range"),r=elt("div","dm-cb-range-inputs"),a=elt("input","dm-cb-range-min"),o=elt("input","dm-cb-range-max");a.type=e,o.type=e,a.placeholder="Min",o.placeholder="Max",a.value=this.state.filter[i]||"",o.value=this.state.filter[n]||"",r.appendChild(a),r.appendChild(document.createTextNode(" - ")),r.appendChild(o),s.appendChild(r);let c=null;if(e==="number"&&this.config.mode==="client"&&this.entries.length){const h=this.entries.map(p=>Number(p.data?.[t.name])).filter(p=>Number.isFinite(p));if(h.length){const p=Math.min(...h),u=Math.max(...h);u>p&&(c=this.buildDualSlider(p,u,a,o,i,n),s.appendChild(c))}}const d=debounce(()=>{this.updateFilter(i,a.value,!0),this.updateFilter(n,o.value),c&&syncSliderToInputs(c,a.value,o.value)},250);return a.addEventListener("input",d),o.addEventListener("input",d),s}buildDualSlider(t,e,i,n,s,r){const a=elt("div","dm-cb-slider");a.dataset.lo=String(t),a.dataset.hi=String(e);const o=elt("div","dm-cb-slider-track"),c=elt("div","dm-cb-slider-fill");o.appendChild(c),a.appendChild(o);const d=elt("input","dm-cb-slider-handle dm-cb-slider-handle-min"),h=elt("input","dm-cb-slider-handle dm-cb-slider-handle-max");for(const[f,m]of[[d,t],[h,e]])f.type="range",f.min=String(t),f.max=String(e),f.step="any",f.value=String(m);d.value=String(i.value||t),h.value=String(n.value||e),a.appendChild(d),a.appendChild(h);const p=()=>{const f=Math.min(Number(d.value),Number(h.value)),m=Math.max(Number(d.value),Number(h.value)),b=e-t||1;c.style.left=`${(f-t)/b*100}%`,c.style.right=`${100-(m-t)/b*100}%`};p();const u=()=>{Number(d.value)>Number(h.value)&&(d.value=h.value),p(),i.value=d.value,n.value=h.value};d.addEventListener("input",u),h.addEventListener("input",u);const g=()=>{this.updateFilter(s,Number(d.value)>t?d.value:"",!0),this.updateFilter(r,Number(h.value)<e?h.value:"")};return d.addEventListener("change",g),h.addEventListener("change",g),a}buildBooleanControl(t){const e=`${t.name}_eq`,i=elt("select","dm-cb-select");for(const[n,s]of[["","Any"],["true","Yes"],["false","No"]]){const r=elt("option");r.value=n,r.textContent=s,String(this.state.filter[e]??"")===n&&(r.selected=!0),i.appendChild(r)}return i.addEventListener("change",()=>this.updateFilter(e,i.value)),i}updateFilter(t,e,i=!1){const n={...this.state.filter};e===""||e==null?delete n[t]:n[t]=e,this.state.filter=n,this.state.page=1,i||this.refresh()}setState(t){Object.assign(this.state,t),this.refresh()}async refresh(){this.config.syncUrl&&this.writeUrlState(),this.config.mode==="server"&&await this.fetchPage(),this.renderBody(),this.renderPagination(),this.updateFacetCounts(),this.updateDrawerBadge()}updateFacetCounts(){if(this.config.mode==="client"&&this.filterRail)for(const t of this.config.filterable||[]){const e=(this.schema.fields||[]).find(a=>a.name===t);if(!e)continue;const i=e.type==="multiselect"||e.type==="tags"||e.type==="tag"?"_in":"_eq",n=Array.isArray(e.options)&&e.options.length?e.options.map(a=>typeof a=="object"?a:{value:a,label:a}):distinctValues(this.entries,t).map(a=>({value:a,label:a})),s=this.facetCounts(t,i,n.map(a=>a.value)),r=this.filterRail.querySelectorAll(".dm-cb-filter");for(const a of r){const o=a.querySelector(".dm-cb-filter-label");if(!o||o.textContent!==(e.label||e.name))continue;a.querySelectorAll(".dm-cb-chip").forEach((h,p)=>{const u=h.querySelector(".dm-cb-chip-count");u&&n[p]&&(u.textContent=s[String(n[p].value)]??0)});const d=a.querySelector("select.dm-cb-select");d&&Array.from(d.options).forEach(h=>{if(!h.value)return;const p=n.find(u=>String(u.value)===h.value)?.label??h.value;h.textContent=`${p} (${s[h.value]??0})`})}}}updateDrawerBadge(){const t=this.el.querySelector(".dm-cb-drawer-toggle");if(!t)return;const e=(this.state.search?1:0)+Object.keys(this.state.filter||{}).length,i=t.querySelector(".dm-cb-drawer-count");i&&(i.textContent=e>0?`(${e})`:"")}async fetchPage(){const t={...this.state.filter,...this.config.baked||{}},e=new URLSearchParams;e.set("page",this.state.page),e.set("limit",this.config.pageSize),e.set("sort",this.state.sort),e.set("order",this.state.order),this.state.search&&e.set("search",this.state.search);for(const[n,s]of Object.entries(t))e.set(`filter[${n}]`,s);this.config.scope==="mine"&&e.set("scope","mine"),this._fetchAbort&&this._fetchAbort.abort(),this._fetchAbort=new AbortController;const i=this.config.sourceKind==="view"?`/api/views/${encodeURIComponent(this.config.slug)}/public?${e.toString()}`:`/api/collections/${encodeURIComponent(this.config.slug)}/public?${e.toString()}`;try{const n=await fetch(i,{headers:this.authHeaders(),signal:this._fetchAbort.signal});if(!n.ok){this.entries=[],this.total=0;return}const s=await n.json(),r=s.entries||s.results||[];this.entries=r.map(a=>a.data?a:{id:a.id||"",data:a.data||a}),this.total=s.total||r.length}catch(n){if(n.name==="AbortError")return;this.entries=[],this.total=0}}authHeaders(){try{const t=typeof S<"u"&&S.get?S.get("auth_token"):null;return t?{Authorization:`Bearer ${t}`}:{}}catch{return{}}}pipeline(){if(this.config.mode==="server")return{visible:this.entries,total:this.total};let t=this.entries,e=null;if(this.state.search){const c=this.state.search.toLowerCase();e=new Map,t=t.filter(d=>{let h=0;for(const[p,u]of Object.entries(d.data||{})){if(u==null)continue;const g=String(u).toLowerCase();let f=0,m=0;for(;(f=g.indexOf(c,f))!==-1;)m++,f+=c.length;m>0&&(h+=m*(p===this.config.titleField?3:1))}return h>0&&e.set(d.id||d,h),h>0})}if(Object.keys(this.state.filter).length||Object.keys(this.config.baked||{}).length){const c={...this.state.filter,...this.config.baked||{}};t=applyFilterInMemory(t,c)}const i=this.config.initial?.sort||"createdAt",n=this.config.initial?.order||"desc",s=this.state.sort===i&&this.state.order===n;this.state.search&&e&&s?(t=[...t].sort((c,d)=>{const h=e.get(c.id||c)||0;return(e.get(d.id||d)||0)-h}),this._activeSortMode="relevance"):(t=sortEntries(t,this.state.sort,this.state.order),this._activeSortMode=null);const r=t.length,a=(this.state.page-1)*this.config.pageSize;return{visible:t.slice(a,a+this.config.pageSize),total:r}}renderBody(){const{visible:t,total:e}=this.pipeline();if(this.currentTotal=e,clear(this.bodyEl),!t.length){this.bodyEl.appendChild(this.buildEmptyState());return}const i=this.visibleFields(t);this.bodyEl.appendChild(this.renderForDisplay(t,i)),typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(this.bodyEl),this.config.cta&&typeof wireCTAButtons=="function"&&wireCTAButtons(this.bodyEl)}buildEmptyState(){const t=elt("div","dm-collection-display dm-collection-empty"),e=!!this.state.search||Object.keys(this.state.filter||{}).length>0,i=elt("p");if(e){i.textContent="No results for these filters.",t.appendChild(i);const n=elt("button","btn btn-outline dm-cb-empty-clear");n.type="button",n.textContent="Clear filters",n.addEventListener("click",()=>{this.setState({filter:{},search:"",page:1}),this.renderFilterRail();const s=this.el.querySelector(".dm-cb-search-input");s&&(s.value="")}),t.appendChild(n)}else i.textContent=this.config.emptyMsg,t.appendChild(i);return t}visibleFields(t){let e=this.schema.fields||[];return this.config.fields?.length&&(e=this.config.fields.map(i=>e.find(n=>n.name===i)||{name:i,label:i})),!e.length&&t.length&&(e=Object.keys(t[0].data||{}).map(i=>({name:i,label:i}))),e}renderCards(t,e){const i=elt("div",`dm-collection-cards grid grid-cols-${this.config.columns}`);for(const n of t){const s=elt("div","card dm-collection-card"),r=e.find(c=>c.name===this.config.titleField),a=r?fieldDisplay(n,r):this.config.titleField?formatValue(n.data?.[this.config.titleField]):"";if(a){const c=elt("div","card-header");c.textContent=a,s.appendChild(c)}const o=elt("div","card-body");for(const c of e){if(this.config.titleField===c.name||!fieldDisplay(n,c))continue;const h=elt("div","dm-collection-card-field"),p=elt("strong");p.textContent=(c.label||c.name)+": ",h.appendChild(p),h.appendChild(fieldDisplayNode(n,c)),o.appendChild(h)}this.config.cta&&o.appendChild(this.buildCtaButton(n)),this.config.transitions&&o.appendChild(this.buildTransitionsGroup(n)),s.appendChild(o),i.appendChild(s)}return i}renderList(t,e){const i=elt("ul","dm-collection-list");for(const n of t){const s=elt("li","dm-collection-list-item"),r=e.find(c=>c.name===this.config.titleField),a=r?fieldDisplay(n,r):this.config.titleField?formatValue(n.data?.[this.config.titleField]):"";if(a){const c=elt("div","dm-collection-list-title");c.textContent=a,s.appendChild(c)}const o=e.filter(c=>c.name!==this.config.titleField).map(c=>{const d=fieldDisplay(n,c);return d?`${c.label||c.name}: ${d}`:""}).filter(Boolean).join(" \xB7 ");if(o){const c=elt("div","dm-collection-list-meta");c.textContent=o,s.appendChild(c)}this.config.cta&&s.appendChild(this.buildCtaButton(n)),this.config.transitions&&s.appendChild(this.buildTransitionsGroup(n)),i.appendChild(s)}return i}renderTable(t,e){const i=elt("table","dm-collection-table"),n=elt("thead"),s=elt("tr");for(const a of e){const o=elt("th");o.textContent=a.label||a.name,s.appendChild(o)}this.config.cta&&s.appendChild(elt("th")),this.config.transitions&&s.appendChild(elt("th")),n.appendChild(s),i.appendChild(n);const r=elt("tbody");for(const a of t){const o=elt("tr");for(const c of e){const d=elt("td");d.appendChild(fieldDisplayNode(a,c)),o.appendChild(d)}if(this.config.cta){const c=elt("td");c.appendChild(this.buildCtaButton(a)),o.appendChild(c)}if(this.config.transitions){const c=elt("td");c.appendChild(this.buildTransitionsGroup(a)),o.appendChild(c)}r.appendChild(o)}return i.appendChild(r),i}renderAccordion(t){const e=elt("div","accordion dm-collection-accordion");for(const i of t){const n=elt("div","accordion-item"),s=this.config.titleField&&i.data?.[this.config.titleField]||"Untitled",r=this.config.bodyField&&i.data?.[this.config.bodyField]||"",a=elt("button","accordion-header");a.type="button",a.textContent=String(s);const o=elt("div","accordion-panel");o.textContent=String(r),a.addEventListener("click",()=>n.classList.toggle("open")),n.appendChild(a),n.appendChild(o),e.appendChild(n)}return e}buildCtaButton(t){const e=this.config.cta,i=elt("button",`btn btn-${e.style||"primary"} dm-cta-trigger`);if(i.dataset.action=e.action,i.dataset.entry=t.id||"",this.config.scope&&(i.dataset.scope=this.config.scope),e.confirm&&(i.dataset.confirm=e.confirm),e.icon){const n=elt("span");n.dataset.icon=e.icon,i.appendChild(n),i.appendChild(document.createTextNode(" "))}return i.appendChild(document.createTextNode(e.label||"Run")),i}buildTransitionsGroup(t){const e=elt("div","dm-cb-transitions");return e.dataset.entry=t.id||"",e.textContent="Loading actions\u2026",Promise.resolve().then(()=>this.loadTransitions(t,e)),e}async loadTransitions(t,e){try{const i=new URLSearchParams({collection:this.config.slug,entryId:t.id||""});this.config.scope==="mine"&&i.set("scope","mine");const n=await fetch(`/api/actions/transitions?${i.toString()}`,{headers:this.authHeaders()});if(clear(e),!n.ok)return;const{transitions:s}=await n.json();if(!Array.isArray(s)||!s.length)return;for(const r of s){const a=elt("button",`btn btn-${r.trigger?.style||"outline"} dm-cb-transition-btn dm-cta-trigger`);if(a.dataset.action=r.slug,a.dataset.entry=t.id||"",this.config.scope&&(a.dataset.scope=this.config.scope),r.trigger?.confirmMessage&&(a.dataset.confirm=r.trigger.confirmMessage),r.trigger?.icon){const o=elt("span");o.dataset.icon=r.trigger.icon,a.appendChild(o),a.appendChild(document.createTextNode(" "))}a.appendChild(document.createTextNode(r.trigger?.label||r.title)),e.appendChild(a)}typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(e),typeof wireCTAButtons=="function"&&wireCTAButtons(e)}catch{clear(e)}}renderPagination(){if(this.config.pagination==="scroll"){this.renderInfiniteSentinel();return}const t=Math.max(1,Math.ceil(this.currentTotal/this.config.pageSize));if(clear(this.pagerEl),t<=1)return;const e=elt("button","dm-cb-page-prev btn btn-outline");e.type="button",e.textContent="\u2039 Prev",e.disabled=this.state.page<=1,e.addEventListener("click",()=>this.setState({page:Math.max(1,this.state.page-1)}));const i=elt("button","dm-cb-page-next btn btn-outline");i.type="button",i.textContent="Next \u203A",i.disabled=this.state.page>=t,i.addEventListener("click",()=>this.setState({page:Math.min(t,this.state.page+1)}));const n=elt("span","dm-cb-page-info");n.textContent=`Page ${this.state.page} of ${t} (${this.currentTotal} entr${this.currentTotal===1?"y":"ies"})`,this.pagerEl.appendChild(e),this.pagerEl.appendChild(n),this.pagerEl.appendChild(i)}renderInfiniteSentinel(){clear(this.pagerEl);const t=Math.max(1,Math.ceil(this.currentTotal/this.config.pageSize));if(this.state.page>=t){const i=elt("p","dm-cb-scroll-end");i.textContent=`All ${this.currentTotal} entries loaded.`,this.pagerEl.appendChild(i);return}const e=elt("div","dm-cb-scroll-sentinel");e.setAttribute("aria-hidden","true"),e.textContent="Loading more\u2026",this.pagerEl.appendChild(e),this._scrollObserver&&this._scrollObserver.disconnect(),this._scrollObserver=new IntersectionObserver(async i=>{i[0].isIntersecting&&(this._scrollObserver.disconnect(),await this.loadMore())},{rootMargin:"200px 0px"}),this._scrollObserver.observe(e)}async loadMore(){const t=this.state.page+1,e=this.currentTotal,i=this.entries,n=this.state.page;this.state.page=t,this.config.syncUrl&&this.writeUrlState();let s;if(this.config.mode==="server")await this.fetchPage(),s=this.entries;else{const{visible:c}=this.pipeline();s=c}if(!s.length){this.state.page=n,this.entries=i,this.currentTotal=e,this.renderPagination();return}const r=this.visibleFields(s),a=this.renderForDisplay(s,r),o=this.bodyEl.firstElementChild;if(o&&o.tagName===a.tagName)for(;a.firstChild;)o.appendChild(a.firstChild);else this.bodyEl.appendChild(a);typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(this.bodyEl),this.config.cta&&typeof wireCTAButtons=="function"&&wireCTAButtons(this.bodyEl),this.renderPagination()}renderForDisplay(t,e){const i=this.config.display;return i==="list"?this.renderList(t,e):i==="table"?this.renderTable(t,e):i==="accordion"?this.renderAccordion(t):i==="timeline"?this.renderTimeline(t):i==="block"?this.renderBlockPlaceholder(t):this.renderCards(t,e)}renderTimeline(t){const e=["vertical","centred","horizontal"].includes(this.config.layout)?this.config.layout:"vertical",i=["minimal","corporate","modern"].includes(this.config.theme)?this.config.theme:"minimal",n=elt("div",`dm-collection-timeline timeline timeline-${e} timeline-${i}`);for(const s of t){const r=elt("div","timeline-event"),a=this.config.dateField&&s.data?.[this.config.dateField]||"",o=this.config.titleField&&s.data?.[this.config.titleField]||"",c=this.config.bodyField&&s.data?.[this.config.bodyField]||"",d=this.config.statusField&&s.data?.[this.config.statusField]||"",h=this.config.iconField&&s.data?.[this.config.iconField]||"";if(h){const p=elt("span","timeline-icon");p.dataset.icon=String(h),r.appendChild(p)}if(a){const p=elt("div","timeline-date");p.textContent=String(a),r.appendChild(p)}if(o){const p=elt("h4","timeline-title");p.textContent=String(o),r.appendChild(p)}if(d){const p=elt("span",`badge badge-${d}`);p.textContent=String(d),r.appendChild(p)}if(c){const p=elt("div","timeline-body");p.textContent=String(c),r.appendChild(p)}n.appendChild(r)}return n}renderBlockPlaceholder(t){const e=elt("div","dm-collection-display dm-cb-block-loading");return e.textContent="Rendering\u2026",this.fetchBlockFragment(t,e),e}async fetchBlockFragment(t,e){const i=++this._fragReqId,n=t.map(a=>a.id).filter(Boolean),s={slug:this.config.slug,display:"block",block:this.config.block,"title-field":this.config.titleField,"body-field":this.config.bodyField,fields:this.config.fields?this.config.fields.join(","):"",empty:this.config.emptyMsg,limit:String(n.length||1)},r={...this.state.filter,...this.config.baked||{}};for(const[a,o]of Object.entries(r))o!==""&&o!=null&&(s[`where_${a}`]=String(o));s.sort=this.state.sort,s.order=this.state.order,s.limit=String(this.config.pageSize);try{const a=btoa(unescape(encodeURIComponent(JSON.stringify(s)))),o=this.config.scope==="mine"?"/api/collections/render-scope":"/api/collections/render-fragment",c=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",...this.authHeaders()},body:JSON.stringify({attrs:a,page:this.state.page})});if(i!==this._fragReqId)return;if(!c.ok){e.textContent=this.config.emptyMsg;return}const{html:d}=await c.json(),h=document.createElement("template");h.innerHTML=d||"",e.replaceWith(h.content)}catch{e.textContent="Unable to load block view."}}readUrlState(){if(!this.config.syncUrl)return;const t=new URLSearchParams(window.location.search),e=this.urlPrefix,i=r=>t.get(e+r);i("search")&&(this.state.search=i("search")),i("sort")&&(this.state.sort=i("sort")),i("order")&&(this.state.order=i("order")),i("page")&&(this.state.page=parseInt(i("page"),10)||1);const n={},s=`${e}filter[`;for(const[r,a]of t.entries())if(r.startsWith(s)&&r.endsWith("]")){const o=r.slice(s.length,-1);o&&(n[o]=a)}Object.keys(n).length&&(this.state.filter=n)}writeUrlState(){const t=new URL(window.location.href),e=this.urlPrefix,i=[];for(const s of t.searchParams.keys())s.startsWith(e)&&i.push(s);i.forEach(s=>t.searchParams.delete(s));const n=(s,r)=>{r!==""&&r!=null&&t.searchParams.set(e+s,r)};n("search",this.state.search),this.state.sort!==(this.config.initial?.sort||"createdAt")&&n("sort",this.state.sort),this.state.order!==(this.config.initial?.order||"desc")&&n("order",this.state.order),this.state.page!==1&&n("page",this.state.page);for(const[s,r]of Object.entries(this.state.filter))r!==""&&r!=null&&t.searchParams.set(`${e}filter[${s}]`,r);try{window.history.replaceState({},"",t.toString())}catch{}}}const OPERATORS=["_eq","_ne","_gte","_lte","_gt","_lt","_in","_nin","_contains","_starts","_ends","_exists"];function parseFilterKey(l){for(const t of OPERATORS)if(l.endsWith(t))return{field:l.slice(0,-t.length),op:t};return{field:l,op:"_eq"}}function resolveField(l,t){if(t==="createdBy")return l.meta?.createdBy;if(t==="id")return l.id;const e=t.split(".");let i=l.data;for(const n of e){if(i==null)return;i=i[n]}return i}function maybeNumber(l){if(typeof l=="number"||typeof l!="string")return l;const t=Number(l);return Number.isFinite(t)&&String(t)===l.trim()?t:l}function applyOp(l,t,e){switch(l){case"_eq":{const i=maybeNumber(t),n=maybeNumber(e);return Array.isArray(t)?t.map(maybeNumber).includes(n):i===n||String(i)===String(n)}case"_ne":{const i=maybeNumber(t),n=maybeNumber(e);return Array.isArray(t)?!t.map(maybeNumber).includes(n):!(i===n||String(i)===String(n))}case"_gt":return maybeNumber(t)>maybeNumber(e);case"_gte":return maybeNumber(t)>=maybeNumber(e);case"_lt":return maybeNumber(t)<maybeNumber(e);case"_lte":return maybeNumber(t)<=maybeNumber(e);case"_in":{const i=String(e).split(",").map(n=>maybeNumber(n.trim()));return Array.isArray(t)?t.map(maybeNumber).some(n=>i.includes(n)):i.includes(maybeNumber(t))}case"_nin":{const i=String(e).split(",").map(n=>maybeNumber(n.trim()));return Array.isArray(t)?!t.map(maybeNumber).some(n=>i.includes(n)):!i.includes(maybeNumber(t))}case"_contains":return String(t??"").toLowerCase().includes(String(e).toLowerCase());case"_starts":return String(t??"").toLowerCase().startsWith(String(e).toLowerCase());case"_ends":return String(t??"").toLowerCase().endsWith(String(e).toLowerCase());case"_exists":{const i=t!=null&&t!=="";return e==="true"||e==="1"||e===!0?i:!i}default:return!1}}function applyFilterInMemory(l,t){return!t||!Object.keys(t).length?l:l.filter(e=>{for(const[i,n]of Object.entries(t)){if(n==null||n==="")continue;const{field:s,op:r}=parseFilterKey(i),a=resolveField(e,s);if(!applyOp(r,a,n))return!1}return!0})}function sortEntries(l,t,e){const i=e==="asc"?1:-1;return[...l].sort((n,s)=>{const r=t==="createdAt"?n.meta?.createdAt||"":n.data?.[t]??"",a=t==="createdAt"?s.meta?.createdAt||"":s.data?.[t]??"";return r<a?-i:r>a?i:0})}function elt(l,t){const e=document.createElement(l);return t&&(e.className=t),e}function isFileRef(l){return l&&typeof l=="object"&&typeof l.url=="string"&&typeof l.name=="string"}function formatValue(l){return l==null?"":isFileRef(l)?l.name:Array.isArray(l)?l.join(", "):typeof l=="boolean"?l?"Yes":"No":String(l)}function fieldDisplay(l,t){if(t?.type==="reference"){const e=l?._refs?.[t.name];if(e!=null)return Array.isArray(e)?e.map(i=>i.missing?`${i.id} (missing)`:i.display).join(", "):e.missing?`${e.id} (missing)`:e.display}return formatValue(l?.data?.[t?.name])}function fieldDisplayNode(l,t){if(t?.type==="reference"){const i=l?._refs?.[t.name];if(i!=null){const n=document.createDocumentFragment();return(Array.isArray(i)?i:[i]).forEach((r,a)=>{if(a>0&&n.appendChild(document.createTextNode(", ")),r.missing)n.appendChild(document.createTextNode(`${r.id} (missing)`));else if(r.link){const o=document.createElement("a");o.href=r.link,o.textContent=r.display,n.appendChild(o)}else n.appendChild(document.createTextNode(r.display))}),n}}const e=l?.data?.[t?.name];if(isFileRef(e)){const i=document.createElement("a");if(i.href=e.url,String(e.mime||"").startsWith("image/")){i.target="_blank",i.rel="noopener";const n=document.createElement("img");n.src=e.url,n.alt=e.name,n.loading="lazy",n.className="dm-file-thumb",i.appendChild(n)}else i.download="",i.textContent=e.name;return i}return document.createTextNode(formatValue(e))}function debounce(l,t){let e;return(...i)=>{clearTimeout(e),e=setTimeout(()=>l(...i),t)}}function toCsv(l,t){const e=s=>{if(s==null)return"";let r=Array.isArray(s)?s.join(", "):String(s);return/[",\n\r]/.test(r)&&(r='"'+r.replace(/"/g,'""')+'"'),r},i=t.map(s=>e(s.label||s.name)).join(","),n=l.map(s=>t.map(r=>e(s.data?.[r.name])).join(",")).join(`
2
2
  `);return i+`
3
3
  `+n+`
4
- `}function downloadCsv(l,t){const e=new Blob([t],{type:"text/csv;charset=utf-8"}),i=URL.createObjectURL(e),n=document.createElement("a");n.href=i,n.download=l,document.body.appendChild(n),n.click(),n.remove(),setTimeout(()=>URL.revokeObjectURL(i),1e3)}function syncSliderToInputs(l,t,e){if(!l)return;const i=l.querySelector(".dm-cb-slider-handle-min"),n=l.querySelector(".dm-cb-slider-handle-max"),s=l.querySelector(".dm-cb-slider-fill");if(!i||!n||!s)return;const r=Number(l.dataset.lo),a=Number(l.dataset.hi),o=Number(t),c=Number(e);Number.isFinite(o)&&(i.value=String(Math.max(r,Math.min(a,o)))),Number.isFinite(c)&&(n.value=String(Math.max(r,Math.min(a,c))));const d=Math.min(Number(i.value),Number(n.value)),h=Math.max(Number(i.value),Number(n.value)),p=a-r||1;s.style.left=`${(d-r)/p*100}%`,s.style.right=`${100-(h-r)/p*100}%`}
4
+ `}function downloadCsv(l,t){const e=new Blob([t],{type:"text/csv;charset=utf-8"}),i=URL.createObjectURL(e),n=document.createElement("a");n.href=i,n.download=l,document.body.appendChild(n),n.click(),n.remove(),setTimeout(()=>URL.revokeObjectURL(i),1e3)}function syncSliderToInputs(l,t,e){if(!l)return;const i=l.querySelector(".dm-cb-slider-handle-min"),n=l.querySelector(".dm-cb-slider-handle-max"),s=l.querySelector(".dm-cb-slider-fill");if(!i||!n||!s)return;const r=Number(l.dataset.lo),a=Number(l.dataset.hi),o=Number(t),c=Number(e);Number.isFinite(o)&&(i.value=String(Math.max(r,Math.min(a,o)))),Number.isFinite(c)&&(n.value=String(Math.max(r,Math.min(a,c))));const d=Math.min(Number(i.value),Number(n.value)),h=Math.max(Number(i.value),Number(n.value)),p=a-r||1;s.style.left=`${(d-r)/p*100}%`,s.style.right=`${100-(h-r)/p*100}%`}window.DommaCollectionBrowser={hydrate:bootstrapCollectionBrowsers},bootstrapCollectionBrowsers();
package/public/js/site.js CHANGED
@@ -1 +1 @@
1
- import{makeBadgeEl as M,applyColour as O,applyPill as V,applyFloat as J,applyHighlight as N,applyItemHighlight as W,itemHighlightAttrs as G,isCurrentItem as j,menuAlign as X}from"/public/js/menu-decor.mjs?v=20260918-item-highlight";import{initFloatingMenus as Y}from"/public/js/menu-float.js?v=20260829-float-panel";import{enhancePickerFields as B,markInvalidFields as R,showValidationPopup as P}from"/public/js/forms.js?v=20260926-form-end";import{buildWizardSteps as U}from"/public/js/form-steps.mjs";$(()=>{const w=window.__CMS_NAV__||{},g=window.__CMS_SITE__||{};if(g.autoTheme?.enabled){let t=function(e){const c=(e||"07:00").split(":");return+c[0]*60+(+c[1]||0)},o=function(){const e=new Date,c=e.getHours()*60+e.getMinutes();return c>=t(n.dayStart)&&c<t(n.nightStart)?n.dayTheme:n.nightTheme},i=function(e){const c=n.apply&&n.apply[e]||{theme:e,custom:""};Domma.theme.set(c.theme);for(const p of[...document.body.classList])p.startsWith("dm-custom-")&&document.body.classList.remove(p);c.custom&&document.body.classList.add(c.custom)};const n=g.autoTheme;i(o()),setInterval(()=>i(o()),6e4)}const _=$("#site-navbar");if(_.length&&w.brand){let o=function(s,h=0){return Array.isArray(s)?s.filter(r=>!(r&&r.type==="spacer")).filter(r=>!(r&&r.type==="separator"&&h===0)).map(r=>r&&r.type==="separator"?{divider:!0}:Array.isArray(r?.items)?{...r,items:o(r.items,h+1)}:r):s},i=function(s){return Array.isArray(s)?s.filter(h=>!(h&&(h.type==="separator"||h.type==="spacer"))):[]};const n={...w.brand},t=n.size&&n.size!=="md"?` navbar-brand-${n.size}`:"";if(n.logo||n.icon||n.tagline){let s="";n.logo?s+=`<img src="${n.logo}" class="navbar-brand-logo" alt="${n.text||""}">`:n.icon&&(s+=`<span data-icon="${n.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),n.text&&(s+=`<span class="navbar-brand-text${t}">${n.text}</span>`),n.tagline&&(s+=`<small class="navbar-brand-tagline">${n.tagline}</small>`),n.html=s}else t&&n.text&&(n.html=`<span class="navbar-brand-text${t}">${n.text}</span>`);const e=w.position==="floating",c=w.orientation==="vertical";Domma.elements.navbar("#site-navbar",{brand:n,items:o(w.items||[]),variant:w.variant||"dark",position:e?"static":w.position||"sticky",collapsible:!0,appearOnHover:!!w.appearOnHover}),Domma.icons.scan("#site-navbar");const p=_.get(0);p.classList.add(`dm-nav-align-${X(w)||"centre"}`),c&&_.get(0).classList.add("dm-nav-vertical",w.side==="right"?"dm-nav-vertical-right":"dm-nav-vertical-left"),N(p,w),e&&(J(_.get(0),w),Y(_.get(0).parentNode||document));const y=p.querySelector(".navbar-toggle"),u=p.querySelector(".navbar-collapse");if(y&&u){let s=null;const h=()=>{u.classList.contains("show")&&y.click()},r=()=>{s&&(s.remove(),s=null),document.body.classList.remove("dm-drawer-open")},a=()=>{s||(s=document.createElement("div"),s.className="dm-navbar-backdrop",s.addEventListener("click",h),document.body.appendChild(s),document.body.classList.add("dm-drawer-open"))};y.addEventListener("click",()=>{requestAnimationFrame(()=>{u.classList.contains("show")?a():r()})}),u.addEventListener("click",l=>{l.target.closest("a")&&requestAnimationFrame(r)}),document.addEventListener("keydown",l=>{l.key==="Escape"&&u.classList.contains("show")&&h()})}const m=p.querySelector(".navbar-nav");if(m){const s=Array.from(m.children);let h=0;for(const r of w.items||[])if(!(r&&r.type==="separator")){if(r&&r.type==="spacer"){const a=document.createElement("li"),l=["xs","sm","md","lg","xl","flex"].includes(r.size)?r.size:"md";a.className=`dm-menu-spacer dm-menu-spacer--${l}`,a.setAttribute("aria-hidden","true"),m.insertBefore(a,s[h]||null);continue}h++}}const C=i(w.items||[]);p.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(s=>{const h=C[parseInt(s.dataset.index,10)];if(!h)return;const r=s.dataset.subindex!=null,a=r&&Array.isArray(h.items)?h.items[parseInt(s.dataset.subindex,10)]:h;if(!a)return;a.colour&&O(s,a.colour),a.pill&&!r&&V(s,a.pill),W(s,a);const l=M(a.badge);l&&s.appendChild(l),j(a.url,location.pathname)&&(s.classList.add("active"),s.setAttribute("aria-current","page"))})}const x=$("#site-footer");if(x.length){let e=function(a){return String(a??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},c=function(a){return e(a).replace(/'/g,"&#39;")},p=function(a){if(a&&a.type==="separator")return'<li class="footer-sep"><hr></li>';if(a&&a.type==="spacer")return`<li class="dm-menu-spacer dm-menu-spacer--${["xs","sm","md","lg","xl","flex"].includes(a.size)?a.size:"md"}" aria-hidden="true"></li>`;const l=c(a.url||"#"),d=e(a.text||""),b=Array.isArray(a.items)&&a.items.length?`<ul>${a.items.map(p).join("")}</ul>`:"",v=G(a),I=[v.hover&&` data-menu-item-hover="${c(v.hover)}"`,v.active&&` data-menu-item-active="${c(v.active)}"`].filter(Boolean).join(""),D=v.accent?` style="--dm-menu-highlight:${c(v.accent)}"`:"";return`<li><a href="${l}"${I}${D}>${d}</a>${b}</li>`},y=function(a,l){return(a||[]).forEach(d=>{d&&(d.type==="separator"||d.type==="spacer")||(l.push(d),Array.isArray(d.items)&&d.items.length&&y(d.items,l))}),l};const n=g.social||{},t={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 o='<div class="footer-inner container">';const i=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(i.copyright&&(o+=`<p>${e(i.copyright)}</p>`),i.primary.length&&(o+=`<nav class="footer-nav footer-nav--primary"><ul>${i.primary.map(p).join("")}</ul></nav>`),i.legal.length&&(o+=`<nav class="footer-nav footer-nav--legal"><ul>${i.legal.map(p).join("")}</ul></nav>`),g.footer){const a=Object.keys(t).filter(l=>n[l]);a.length&&(o+='<div class="footer-social">',a.forEach(l=>{const{label:d,svg:b}=t[l];o+=`<a href="${n[l]}" target="_blank" rel="noopener noreferrer" aria-label="${d}" class="footer-social-link">${b}</a>`}),o+="</div>")}o+="</div>",x.html(o);const u=[...y(i.primary,[]),...y(i.legal,[])];x.get(0).querySelectorAll(".footer-nav a").forEach((a,l)=>{const d=u[l];if(!d)return;d.colour&&O(a,d.colour);const b=M(d.badge);b&&a.appendChild(b),j(d.url,location.pathname)&&(a.classList.add("is-active"),a.setAttribute("aria-current","page"))}),N(x.get(0).querySelector(".footer-nav--primary"),{highlight:i.primaryHighlight}),N(x.get(0).querySelector(".footer-nav--legal"),{highlight:i.legalHighlight});const C=g.footer?.showMotionToggle!==!1,s=S.get("reduced_motion"),h=s!==null?!!s:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),r=x.get(0).querySelector(".footer-inner");if(C&&r){const a=document.createElement("input");a.type="checkbox",a.className="form-switch-input",a.id="dm-motion-switch",a.checked=h,a.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const l=document.createElement("span");l.className="form-switch-label",l.textContent="Reduce motion";const d=document.createElement("label");d.className="form-switch footer-motion-switch",d.title="Reduce motion",d.appendChild(a),d.appendChild(l),r.appendChild(d)}}$("#site-sidebar").length&&Domma.elements.sidebar("#site-sidebar",{autoGenerate:!0,selector:"h2, h3",collapsible:!1,push:!0,contentSelector:".site-content"}),Domma.icons.scan(),Domma.syntax?.scan&&Domma.syntax.scan();const f=$(".page-body");if(f.length){f.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),f.find(".tabs").each(function(){Domma.elements.tabs(this)}),f.find(".carousel").each(function(){const n={autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"};if(this.dataset.animationDuration){const t=parseInt(this.dataset.animationDuration,10);Number.isFinite(t)&&(n.animationDuration=t)}this.dataset.animationEasing&&(n.animationEasing=this.dataset.animationEasing),this.dataset.ctx&&(this._dmCtxSlides=Array.from(this.querySelectorAll(".carousel-slide")).map(t=>t.cloneNode(!0)),this._dmCarouselOpts=n),this._dmCarousel=Domma.elements.carousel(this,n)}),f.find(".dm-countdown").each(function(){const n={autoStart:!0};this.dataset.to&&(n.targetDate=new Date(this.dataset.to)),this.dataset.duration&&(n.duration=parseInt(this.dataset.duration,10)),this.dataset.format&&(n.format=this.dataset.format),Domma.elements.timer(this,n)}),f.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),f.find(".dm-progression").each(function(){const n=Array.from(this.querySelectorAll(".dm-progression-item")).map(i=>{const e={title:i.querySelector(".dm-progression-item-title")?.textContent?.trim()||"",description:i.querySelector(".dm-progression-item-body")?.innerHTML?.trim()||""};return i.dataset.date&&(e.date=i.dataset.date),i.dataset.status&&(e.status=i.dataset.status),i.dataset.entryId&&(e._entryId=i.dataset.entryId),e}),t=this.dataset.layout==="centred"?"centered":this.dataset.layout||"vertical",o=Domma.elements.progression(this,{layout:t,theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0,allowHtml:!0,...n.length?{items:n}:{}});n.length&&(this._dmCtx={instance:o,items:n})});try{Domma.effects.reveal(".page-body .hero",{animation:"slide-up",duration:480,threshold:.06,stagger:60,once:!1})}catch{}document.querySelectorAll(".page-body .row[data-reveal]").forEach(n=>{const t=n.dataset.revealMode||"stagger",o=n.dataset.revealAnimation||"slide-up",i=parseInt(n.dataset.revealDuration,10)||400,e=parseInt(n.dataset.revealStagger,10)||60,c=parseInt(n.dataset.revealDelay,10)||0,p=n.dataset.revealDirection==="rtl",y=Array.from(n.children),u={"slide-up":"translateY(30px)","slide-down":"translateY(-30px)","slide-left":"translateX(30px)","slide-right":"translateX(-30px)",zoom:"scale(0.85)",flip:"perspective(600px) rotateX(15deg)"};y.forEach((m,C)=>{m.style.opacity="0",m.style.transform=u[o]||"",m.style.transition=`opacity ${i}ms ease, transform ${i}ms ease`;const s=p?y.length-1-C:C;m.style.transitionDelay=t==="stagger"?`${c+s*e}ms`:`${c}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const m=new IntersectionObserver(C=>{C.forEach(s=>{s.isIntersecting&&(s.target.offsetWidth,s.target.style.opacity="1",s.target.style.transform="none",m.unobserve(s.target))})},{threshold:.1});y.forEach(C=>m.observe(C))}))}),f.find(".card[data-collapsible]").each(function(){const n=this.querySelector(".card-header");n&&n.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),f.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const n=this.dataset.soTarget,t=document.getElementById(n);if(!t)return;const o=E.slideover({title:t.dataset.soTitle||"",size:t.dataset.soSize||"md",position:t.dataset.soPosition||"right"});t.style.display="",o.setContent(t),o.open()})})}if(typeof $.setup=="function"){const n=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(t=>{try{const o=atob(t.dataset.config),i=JSON.parse(o);Object.assign(n,i)}catch{}}),Object.keys(n).length>0){const t={};for(const[o,i]of Object.entries(n)){const e=i?.events?.click,{confirm:c,toast:p,alert:y,prompt:u,...m}=e||{};c||p||y||u?($(o).on("click",async function(s){if(s.preventDefault(),c&&!await E.confirm(c))return;let h=null;if(!(u&&(h=await E.prompt(u,{inputPlaceholder:m.promptPlaceholder||"",inputValue:m.promptDefault||""}),h===null))){if(m.target){const r=$(m.target);m.toggleClass&&r.toggleClass(m.toggleClass),m.addClass&&r.addClass(m.addClass),m.removeClass&&r.removeClass(m.removeClass),h!==null&&(m.setText&&r.text(h),m.setVal&&r.val(h),m.setAttr&&r.attr(m.setAttr,h))}m.href&&(window.location.href=m.href),p&&E.toast(p,{type:m.toastType||"success"}),y&&E.alert(y)}}),Object.keys(m).length&&(t[o]={...i,events:{...i.events,click:m}})):t[o]=i}$.setup(t)}}f.length&&q(f.get(0))});function q(w){w.querySelectorAll(".dm-cta-trigger").forEach(g=>{g.__ctaWired||(g.__ctaWired=!0,g.addEventListener("click",async()=>{const _=g.dataset.action,x=g.dataset.entry,A=g.dataset.confirm;let f=S.get("auth_token");if(!f){E.toast("Please log in to perform this action.",{type:"warning"});return}if(A&&!await E.confirm(A))return;const n=Array.from(g.childNodes).map(o=>o.cloneNode(!0));g.disabled=!0,g.textContent="Running\u2026";const t=o=>fetch(`/api/actions/${_}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify({entryId:x})});try{let o=await t(f);if(o.status===401){const e=S.get("auth_refresh_token");if(e){const c=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(c.ok){const{token:p}=await c.json();S.set("auth_token",p),f=p,o=await t(f)}}}const i=await o.json().catch(()=>({}));if(!o.ok)throw new Error(i.error||i.message||`Error ${o.status}`);E.toast(i.message||"Action completed.",{type:"success"})}catch(o){E.toast(o.message||"Action failed.",{type:"error"})}finally{g.disabled=!1,g.textContent="",n.forEach(o=>g.appendChild(o)),Domma.icons.scan(g)}}))})}(function(){const g=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!g.length)return;const _=S.get("auth_token");function x(f,n){const t=document.createElement("div");t.className="dm-collection-display dm-collection-empty";const o=document.createElement("p");if(n){o.appendChild(document.createTextNode("Please "));const i=document.createElement("a");i.href="/login",i.textContent="sign in",o.appendChild(i),o.appendChild(document.createTextNode(" to view your entries."))}else o.textContent=f;return t.appendChild(o),t}function A(f,n){const t=document.createElement("template");t.innerHTML=n||"",f.replaceWith(t.content)}g.forEach(async f=>{const n=f.dataset.collectionAttrs;if(!n)return;if(!_){f.replaceChildren(x("",!0));return}const t=o=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify({attrs:n})});try{let o=_,i=await t(o);if(i.status===401){const c=S.get("auth_refresh_token");if(c){const p=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:c})});if(p.ok){const{token:y}=await p.json();S.set("auth_token",y),o=y,i=await t(o)}}}if(!i.ok){const{error:c}=await i.json().catch(()=>({}));f.replaceChildren(x(c||"Unable to load entries.",!1));return}const{html:e}=await i.json();A(f,e)}catch{f.replaceChildren(x("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),typeof Domma<"u"&&Domma.syntax?.scan&&Domma.syntax.scan(),q(document.body)},100)})(),(function(){const g=document.querySelectorAll("[data-collection-table]");!g.length||typeof T>"u"||!T.create||g.forEach(_=>{let x;try{x=JSON.parse(atob(_.dataset.payload))}catch{return}const{columns:A,rows:f,search:n,sortable:t,exportable:o,pageSize:i,empty:e,ctaConfig:c,slug:p,exportCfg:y}=x;if(!A?.length)return;if(c){const C=A.findIndex(s=>s.key==="_cta");C!==-1&&(A[C]={key:"_cta",title:"",render:(s,h)=>{const r=document.createElement("button");if(r.className=`btn btn-${c.style||"primary"} dm-cta-trigger`,r.dataset.action=c.action||"",r.dataset.entry=h._entryId||"",c.confirm&&(r.dataset.confirm=c.confirm),c.icon){const a=document.createElement("span");a.dataset.icon=c.icon,r.appendChild(a),r.appendChild(document.createTextNode(" "))}return r.appendChild(document.createTextNode(c.label||"Run")),r}})}const u="col-table-"+Math.random().toString(36).slice(2,7),m=document.createElement("div");m.id=u,_.replaceChildren(m),T.create("#"+u,{data:f,columns:A,search:n,sortable:t,exportable:o,pageSize:i,emptyMessage:e}),c&&q(m),_._dmTable={columns:A,rows:f,slug:p,exportCfg:y,render(C){T.create("#"+u,{data:C,columns:A,search:n,sortable:t,exportable:o,pageSize:i,emptyMessage:e}),c&&q(m)}}})})(),(function(){const g=document.querySelectorAll("[data-form-inline]");if(!g.length||typeof F>"u")return;function _(t){const o=t&&t.logic;return!!(o&&(o.visibility||o.requirement))}function x(t,o){const i={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const c=e.type==="chooser"||!!(e.variant&&["select","radio","checkbox-group"].includes(e.type)),p=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":c?"chooser":e.type,y={...e.formConfig||{}};y.span==="full"&&o&&(y.span=o);const u={type:p,required:_(e)?!1:e.required,label:e.label,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{hint:e.helper},...e.tooltip&&{tooltip:e.tooltip},...y}};e.minLength!==void 0&&(u.minLength=e.minLength),e.maxLength!==void 0&&(u.maxLength=e.maxLength),e.min!==void 0&&(u.min=e.min),e.max!==void 0&&(u.max=e.max),c&&(e.variant&&(u.variant=e.variant),(e.multiple||e.type==="checkbox-group")&&(u.multiple=!0),e.density&&(u.density=e.density),e.columns&&(u.columns=e.columns),e.accent&&(u.accent=e.accent),e.accentStyle&&(u.accentStyle=e.accentStyle),e.glow&&(u.glow=!0),e.glowColour&&(u.glowColour=e.glowColour),e.shadow&&(u.shadow=e.shadow),e.shadowColour&&(u.shadowColour=e.shadowColour)),i[e.name]=u}),i}function A(t){const o={};return(t||[]).forEach(i=>{if(!(!i.name||i.type==="page-break"||i.type==="spacer")&&(i.type==="select"||i.type==="multiselect")&&i.required){const e=(i.options||[])[0];e&&(o[i.name]=typeof e=="object"?e.value:e)}}),o}function f(t,o){(o||[]).forEach(i=>{if(i.type!=="date"||!i.name)return;const e=t.querySelector(`[name="${i.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function n(t,o,i){let e=t.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",t.appendChild(e)),e.textContent=o,e.style.cssText=i?"color:var(--dm-danger);margin-top:.75rem;":"color:var(--dm-success);margin-top:.75rem;"}g.forEach(t=>{let o;try{o=JSON.parse(atob(t.dataset.formInline))}catch{return}const i=o.fields||[],e=o.settings||{},c=Number(e.columns)===3?3:2,p=e.layout||"grid",y=i.some(r=>r.type==="page-break"),u=async r=>{if(t.dataset.triggerBlocked==="true"){const a=t._formLogicRuntime,l=a&&a.submitBlock?a.submitBlock().reasons:[];n(t,l.join(" ")||"This form cannot be submitted yet.",!0);const d=new Error("Blocked by a form trigger");throw d.formSubmitFailed=!0,d}try{const a=t.querySelector(".fb-form-honeypot"),l=a?.querySelector('input[type="text"]')||null,d=a?.querySelector('[name="_t"]')||null,b=Object.assign({},r);delete b._t,a?.dataset.hpName&&delete b[a.dataset.hpName],l!==null&&(b._hp=l.value),d!==null&&(b._t=d.value);const v=await H.post(`/api/forms/submit/${o.slug}`,b);if(v&&v.error){n(t,v.error,!0);const I=new Error(v.error);throw I.formSubmitFailed=!0,I}if(v?.redirect){window.location.href=v.redirect;return}for(;t.firstChild;)t.removeChild(t.firstChild);n(t,v?.message||e.successMessage||"Thank you for your submission.",!1)}catch(a){throw a.formSubmitFailed||n(t,a.message||"Submission failed. Please try again.",!0),a}},m=Date.now();function C(r){const a=r.querySelector("form");if(!a||a.querySelector(".fb-form-honeypot"))return;const l=document.createElement("div");l.className="fb-form-honeypot",l.setAttribute("aria-hidden","true");const d=document.createElement("input");d.name=a.querySelector('[name="website"]')?"_website":"website",l.dataset.hpName=d.name,d.type="text",d.tabIndex=-1,d.autocomplete="url",d.placeholder="https://",l.appendChild(d);const b=document.createElement("input");b.name="_t",b.type="hidden",b.value=m,l.appendChild(b),a.appendChild(l)}function s(r){if(!window.FormLogicEngine||!i.some(l=>l.logic||Array.isArray(l.triggers)&&l.triggers.length))return null;const a=new window.FormLogicEngine.FormLogicRuntime(o,t,r?{baseValues:r}:void 0);if(a.init(),t._formLogicRuntime=a,t._formLogicObserved)return a;if(t._formLogicObserved=!0,t.parentNode&&typeof MutationObserver<"u"){const l=new MutationObserver(function(d){for(const b of d)for(const v of b.removedNodes)if(v===t||v.nodeType===1&&v.contains&&v.contains(t)){t._formLogicRuntime&&t._formLogicRuntime.destroy(),l.disconnect();return}});l.observe(t.parentNode,{childList:!0,subtree:!1}),t._formLogicObserver=l}return a}let h=!1;if(t.addEventListener("dm-form-end",r=>{const a=r.detail?.validation;if(a&&!a.valid){R(t,a.missing),P(a);return}h||(h=!0,u(r.detail?.values||{}).catch(()=>{}).finally(()=>{h=!1}))}),y&&F.wizard){const r=U(i,o.title),a=r.map(k=>({title:k.title,description:k.description,fields:x(k.fields,c)})),l=r.map(k=>k.fields);let d=0,b=null,v=null;const I=k=>{const L=window.FormLogicEngine;if(!L||!L.validateFields)return;const z=L.validateFields(t,l[d]||[]);z.valid||(k.preventDefault(),k.stopImmediatePropagation(),R(t,z.missing),P(z))},D=()=>{const k=l[d]||i;f(t,k),B(t,k),e.honeypot!==!1&&C(t),b&&b.destroy(),b=s(v?.getStepData?v.getStepData():void 0),t.querySelectorAll(".wizard-next, .wizard-submit").forEach(L=>L.addEventListener("click",I,!0))};v=F.wizard(t,{schema:{steps:a},layout:p,columns:c,finishText:e.submitText||"Finish",onSubmit:u,onStepChange:k=>{d=k,setTimeout(D,0)}}),t.addEventListener("dm-form-trigger",k=>{if(k.detail?.type!=="goto-step"||!v?.goToStep)return;const L=Number(k.detail.action.step);!Number.isFinite(L)||L<0||L>=a.length||L!==d&&v.goToStep(L)}),Promise.resolve(v).then(D)}else if(F.render){const r=F.render(t,x(i,c),A(i),{submitText:e.submitText||"Submit",layout:p,columns:c,onSubmit:u});Promise.resolve(r).then(function(){if(p==="grid"&&e.submitSpan==="full"){const a=t.querySelector(".form-buttons");a&&a.classList.add("col-span-full")}f(t,i),B(t,i),e.honeypot!==!1&&C(t),s()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();
1
+ import{makeBadgeEl as M,applyColour as O,applyPill as V,applyFloat as J,applyHighlight as N,applyItemHighlight as W,itemHighlightAttrs as G,isCurrentItem as B,menuAlign as X}from"/public/js/menu-decor.mjs?v=20260918-item-highlight";import{initFloatingMenus as Y}from"/public/js/menu-float.js?v=20260829-float-panel";import{enhancePickerFields as j,markInvalidFields as R,showValidationPopup as P}from"/public/js/forms.js?v=20260926-form-end";import{buildWizardSteps as U}from"/public/js/form-steps.mjs";$(()=>{const w=window.__CMS_NAV__||{},g=window.__CMS_SITE__||{};if(g.autoTheme?.enabled){let t=function(e){const s=(e||"07:00").split(":");return+s[0]*60+(+s[1]||0)},n=function(){const e=new Date,s=e.getHours()*60+e.getMinutes();return s>=t(o.dayStart)&&s<t(o.nightStart)?o.dayTheme:o.nightTheme},i=function(e){const s=o.apply&&o.apply[e]||{theme:e,custom:""};Domma.theme.set(s.theme);for(const p of[...document.body.classList])p.startsWith("dm-custom-")&&document.body.classList.remove(p);s.custom&&document.body.classList.add(s.custom)};const o=g.autoTheme;i(n()),setInterval(()=>i(n()),6e4)}const _=$("#site-navbar");if(_.length&&w.brand){let n=function(c,h=0){return Array.isArray(c)?c.filter(r=>!(r&&r.type==="spacer")).filter(r=>!(r&&r.type==="separator"&&h===0)).map(r=>r&&r.type==="separator"?{divider:!0}:Array.isArray(r?.items)?{...r,items:n(r.items,h+1)}:r):c},i=function(c){return Array.isArray(c)?c.filter(h=>!(h&&(h.type==="separator"||h.type==="spacer"))):[]};const o={...w.brand},t=o.size&&o.size!=="md"?` navbar-brand-${o.size}`:"";if(o.logo||o.icon||o.tagline){let c="";o.logo?c+=`<img src="${o.logo}" class="navbar-brand-logo" alt="${o.text||""}">`:o.icon&&(c+=`<span data-icon="${o.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),o.text&&(c+=`<span class="navbar-brand-text${t}">${o.text}</span>`),o.tagline&&(c+=`<small class="navbar-brand-tagline">${o.tagline}</small>`),o.html=c}else t&&o.text&&(o.html=`<span class="navbar-brand-text${t}">${o.text}</span>`);const e=w.position==="floating",s=w.orientation==="vertical";Domma.elements.navbar("#site-navbar",{brand:o,items:n(w.items||[]),variant:w.variant||"dark",position:e?"static":w.position||"sticky",collapsible:!0,appearOnHover:!!w.appearOnHover}),Domma.icons.scan("#site-navbar");const p=_.get(0);p.classList.add(`dm-nav-align-${X(w)||"centre"}`),s&&_.get(0).classList.add("dm-nav-vertical",w.side==="right"?"dm-nav-vertical-right":"dm-nav-vertical-left"),N(p,w),e&&(J(_.get(0),w),Y(_.get(0).parentNode||document));const y=p.querySelector(".navbar-toggle"),u=p.querySelector(".navbar-collapse");if(y&&u){let c=null;const h=()=>{u.classList.contains("show")&&y.click()},r=()=>{c&&(c.remove(),c=null),document.body.classList.remove("dm-drawer-open")},a=()=>{c||(c=document.createElement("div"),c.className="dm-navbar-backdrop",c.addEventListener("click",h),document.body.appendChild(c),document.body.classList.add("dm-drawer-open"))};y.addEventListener("click",()=>{requestAnimationFrame(()=>{u.classList.contains("show")?a():r()})}),u.addEventListener("click",l=>{l.target.closest("a")&&requestAnimationFrame(r)}),document.addEventListener("keydown",l=>{l.key==="Escape"&&u.classList.contains("show")&&h()})}const m=p.querySelector(".navbar-nav");if(m){const c=Array.from(m.children);let h=0;for(const r of w.items||[])if(!(r&&r.type==="separator")){if(r&&r.type==="spacer"){const a=document.createElement("li"),l=["xs","sm","md","lg","xl","flex"].includes(r.size)?r.size:"md";a.className=`dm-menu-spacer dm-menu-spacer--${l}`,a.setAttribute("aria-hidden","true"),m.insertBefore(a,c[h]||null);continue}h++}}const x=i(w.items||[]);p.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(c=>{const h=x[parseInt(c.dataset.index,10)];if(!h)return;const r=c.dataset.subindex!=null,a=r&&Array.isArray(h.items)?h.items[parseInt(c.dataset.subindex,10)]:h;if(!a)return;a.colour&&O(c,a.colour),a.pill&&!r&&V(c,a.pill),W(c,a);const l=M(a.badge);l&&c.appendChild(l),B(a.url,location.pathname)&&(c.classList.add("active"),c.setAttribute("aria-current","page"))})}const C=$("#site-footer");if(C.length){let e=function(a){return String(a??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},s=function(a){return e(a).replace(/'/g,"&#39;")},p=function(a){if(a&&a.type==="separator")return'<li class="footer-sep"><hr></li>';if(a&&a.type==="spacer")return`<li class="dm-menu-spacer dm-menu-spacer--${["xs","sm","md","lg","xl","flex"].includes(a.size)?a.size:"md"}" aria-hidden="true"></li>`;const l=s(a.url||"#"),d=e(a.text||""),b=Array.isArray(a.items)&&a.items.length?`<ul>${a.items.map(p).join("")}</ul>`:"",v=G(a),I=[v.hover&&` data-menu-item-hover="${s(v.hover)}"`,v.active&&` data-menu-item-active="${s(v.active)}"`].filter(Boolean).join(""),q=v.accent?` style="--dm-menu-highlight:${s(v.accent)}"`:"";return`<li><a href="${l}"${I}${q}>${d}</a>${b}</li>`},y=function(a,l){return(a||[]).forEach(d=>{d&&(d.type==="separator"||d.type==="spacer")||(l.push(d),Array.isArray(d.items)&&d.items.length&&y(d.items,l))}),l};const o=g.social||{},t={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 n='<div class="footer-inner container">';const i=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(i.copyright&&(n+=`<p>${e(i.copyright)}</p>`),i.primary.length&&(n+=`<nav class="footer-nav footer-nav--primary"><ul>${i.primary.map(p).join("")}</ul></nav>`),i.legal.length&&(n+=`<nav class="footer-nav footer-nav--legal"><ul>${i.legal.map(p).join("")}</ul></nav>`),g.footer){const a=Object.keys(t).filter(l=>o[l]);a.length&&(n+='<div class="footer-social">',a.forEach(l=>{const{label:d,svg:b}=t[l];n+=`<a href="${o[l]}" target="_blank" rel="noopener noreferrer" aria-label="${d}" class="footer-social-link">${b}</a>`}),n+="</div>")}n+="</div>",C.html(n);const u=[...y(i.primary,[]),...y(i.legal,[])];C.get(0).querySelectorAll(".footer-nav a").forEach((a,l)=>{const d=u[l];if(!d)return;d.colour&&O(a,d.colour);const b=M(d.badge);b&&a.appendChild(b),B(d.url,location.pathname)&&(a.classList.add("is-active"),a.setAttribute("aria-current","page"))}),N(C.get(0).querySelector(".footer-nav--primary"),{highlight:i.primaryHighlight}),N(C.get(0).querySelector(".footer-nav--legal"),{highlight:i.legalHighlight});const x=g.footer?.showMotionToggle!==!1,c=S.get("reduced_motion"),h=c!==null?!!c:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),r=C.get(0).querySelector(".footer-inner");if(x&&r){const a=document.createElement("input");a.type="checkbox",a.className="form-switch-input",a.id="dm-motion-switch",a.checked=h,a.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const l=document.createElement("span");l.className="form-switch-label",l.textContent="Reduce motion";const d=document.createElement("label");d.className="form-switch footer-motion-switch",d.title="Reduce motion",d.appendChild(a),d.appendChild(l),r.appendChild(d)}}$("#site-sidebar").length&&Domma.elements.sidebar("#site-sidebar",{autoGenerate:!0,selector:"h2, h3",collapsible:!1,push:!0,contentSelector:".site-content"}),Domma.icons.scan(),Domma.syntax?.scan&&Domma.syntax.scan();const f=$(".page-body");if(f.length){f.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),f.find(".tabs").each(function(){Domma.elements.tabs(this)}),f.find(".carousel").each(function(){const o={autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"};if(this.dataset.animationDuration){const t=parseInt(this.dataset.animationDuration,10);Number.isFinite(t)&&(o.animationDuration=t)}this.dataset.animationEasing&&(o.animationEasing=this.dataset.animationEasing),this.dataset.ctx&&(this._dmCtxSlides=Array.from(this.querySelectorAll(".carousel-slide")).map(t=>t.cloneNode(!0)),this._dmCarouselOpts=o),this._dmCarousel=Domma.elements.carousel(this,o)}),f.find(".dm-countdown").each(function(){const o={autoStart:!0};this.dataset.to&&(o.targetDate=new Date(this.dataset.to)),this.dataset.duration&&(o.duration=parseInt(this.dataset.duration,10)),this.dataset.format&&(o.format=this.dataset.format),Domma.elements.timer(this,o)}),f.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),f.find(".dm-progression").each(function(){const o=Array.from(this.querySelectorAll(".dm-progression-item")).map(i=>{const e={title:i.querySelector(".dm-progression-item-title")?.textContent?.trim()||"",description:i.querySelector(".dm-progression-item-body")?.innerHTML?.trim()||""};return i.dataset.date&&(e.date=i.dataset.date),i.dataset.status&&(e.status=i.dataset.status),i.dataset.entryId&&(e._entryId=i.dataset.entryId),e}),t=this.dataset.layout==="centred"?"centered":this.dataset.layout||"vertical",n=Domma.elements.progression(this,{layout:t,theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0,allowHtml:!0,...o.length?{items:o}:{}});o.length&&(this._dmCtx={instance:n,items:o})});try{Domma.effects.reveal(".page-body .hero",{animation:"slide-up",duration:480,threshold:.06,stagger:60,once:!1})}catch{}document.querySelectorAll(".page-body .row[data-reveal]").forEach(o=>{const t=o.dataset.revealMode||"stagger",n=o.dataset.revealAnimation||"slide-up",i=parseInt(o.dataset.revealDuration,10)||400,e=parseInt(o.dataset.revealStagger,10)||60,s=parseInt(o.dataset.revealDelay,10)||0,p=o.dataset.revealDirection==="rtl",y=Array.from(o.children),u={"slide-up":"translateY(30px)","slide-down":"translateY(-30px)","slide-left":"translateX(30px)","slide-right":"translateX(-30px)",zoom:"scale(0.85)",flip:"perspective(600px) rotateX(15deg)"};y.forEach((m,x)=>{m.style.opacity="0",m.style.transform=u[n]||"",m.style.transition=`opacity ${i}ms ease, transform ${i}ms ease`;const c=p?y.length-1-x:x;m.style.transitionDelay=t==="stagger"?`${s+c*e}ms`:`${s}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const m=new IntersectionObserver(x=>{x.forEach(c=>{c.isIntersecting&&(c.target.offsetWidth,c.target.style.opacity="1",c.target.style.transform="none",m.unobserve(c.target))})},{threshold:.1});y.forEach(x=>m.observe(x))}))}),f.find(".card[data-collapsible]").each(function(){const o=this.querySelector(".card-header");o&&o.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),f.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const o=this.dataset.soTarget,t=document.getElementById(o);if(!t)return;const n=E.slideover({title:t.dataset.soTitle||"",size:t.dataset.soSize||"md",position:t.dataset.soPosition||"right"});t.style.display="",n.setContent(t),n.open()})})}if(typeof $.setup=="function"){const o=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(t=>{try{const n=atob(t.dataset.config),i=JSON.parse(n);Object.assign(o,i)}catch{}}),Object.keys(o).length>0){const t={};for(const[n,i]of Object.entries(o)){const e=i?.events?.click,{confirm:s,toast:p,alert:y,prompt:u,...m}=e||{};s||p||y||u?($(n).on("click",async function(c){if(c.preventDefault(),s&&!await E.confirm(s))return;let h=null;if(!(u&&(h=await E.prompt(u,{inputPlaceholder:m.promptPlaceholder||"",inputValue:m.promptDefault||""}),h===null))){if(m.target){const r=$(m.target);m.toggleClass&&r.toggleClass(m.toggleClass),m.addClass&&r.addClass(m.addClass),m.removeClass&&r.removeClass(m.removeClass),h!==null&&(m.setText&&r.text(h),m.setVal&&r.val(h),m.setAttr&&r.attr(m.setAttr,h))}m.href&&(window.location.href=m.href),p&&E.toast(p,{type:m.toastType||"success"}),y&&E.alert(y)}}),Object.keys(m).length&&(t[n]={...i,events:{...i.events,click:m}})):t[n]=i}$.setup(t)}}f.length&&D(f.get(0))}),window.wireCTAButtons=D;function D(w){w.querySelectorAll(".dm-cta-trigger").forEach(g=>{g.__ctaWired||(g.__ctaWired=!0,g.addEventListener("click",async()=>{const _=g.dataset.action,C=g.dataset.entry,A=g.dataset.confirm;let f=S.get("auth_token");if(!f){E.toast("Please log in to perform this action.",{type:"warning"});return}if(A&&!await E.confirm(A))return;const o=Array.from(g.childNodes).map(n=>n.cloneNode(!0));g.disabled=!0,g.textContent="Running\u2026";const t=n=>fetch(`/api/actions/${_}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},body:JSON.stringify(g.dataset.scope?{entryId:C,scope:g.dataset.scope}:{entryId:C})});try{let n=await t(f);if(n.status===401){const e=S.get("auth_refresh_token");if(e){const s=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(s.ok){const{token:p}=await s.json();S.set("auth_token",p),f=p,n=await t(f)}}}const i=await n.json().catch(()=>({}));if(!n.ok)throw new Error(i.error||i.message||`Error ${n.status}`);E.toast(i.message||"Action completed.",{type:"success"})}catch(n){E.toast(n.message||"Action failed.",{type:"error"})}finally{g.disabled=!1,g.textContent="",o.forEach(n=>g.appendChild(n)),Domma.icons.scan(g)}}))})}(function(){const g=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!g.length)return;const _=S.get("auth_token");function C(f,o){const t=document.createElement("div");t.className="dm-collection-display dm-collection-empty";const n=document.createElement("p");if(o){n.appendChild(document.createTextNode("Please "));const i=document.createElement("a");i.href="/login",i.textContent="sign in",n.appendChild(i),n.appendChild(document.createTextNode(" to view your entries."))}else n.textContent=f;return t.appendChild(n),t}function A(f,o){const t=document.createElement("template");t.innerHTML=o||"",f.replaceWith(t.content)}g.forEach(async f=>{const o=f.dataset.collectionAttrs;if(!o)return;if(!_){f.replaceChildren(C("",!0));return}const t=n=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},body:JSON.stringify({attrs:o})});try{let n=_,i=await t(n);if(i.status===401){const s=S.get("auth_refresh_token");if(s){const p=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:s})});if(p.ok){const{token:y}=await p.json();S.set("auth_token",y),n=y,i=await t(n)}}}if(!i.ok){const{error:s}=await i.json().catch(()=>({}));f.replaceChildren(C(s||"Unable to load entries.",!1));return}const{html:e}=await i.json();A(f,e),window.DommaCollectionBrowser?.hydrate&&window.DommaCollectionBrowser.hydrate(document)}catch{f.replaceChildren(C("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),typeof Domma<"u"&&Domma.syntax?.scan&&Domma.syntax.scan(),D(document.body)},100)})(),(function(){const g=document.querySelectorAll("[data-collection-table]");!g.length||typeof T>"u"||!T.create||g.forEach(_=>{let C;try{C=JSON.parse(atob(_.dataset.payload))}catch{return}const{columns:A,rows:f,search:o,sortable:t,exportable:n,pageSize:i,empty:e,ctaConfig:s,slug:p,exportCfg:y}=C;if(!A?.length)return;if(s){const x=A.findIndex(c=>c.key==="_cta");x!==-1&&(A[x]={key:"_cta",title:"",render:(c,h)=>{const r=document.createElement("button");if(r.className=`btn btn-${s.style||"primary"} dm-cta-trigger`,r.dataset.action=s.action||"",s.scope&&(r.dataset.scope=s.scope),r.dataset.entry=h._entryId||"",s.confirm&&(r.dataset.confirm=s.confirm),s.icon){const a=document.createElement("span");a.dataset.icon=s.icon,r.appendChild(a),r.appendChild(document.createTextNode(" "))}return r.appendChild(document.createTextNode(s.label||"Run")),r}})}const u="col-table-"+Math.random().toString(36).slice(2,7),m=document.createElement("div");m.id=u,_.replaceChildren(m),T.create("#"+u,{data:f,columns:A,search:o,sortable:t,exportable:n,pageSize:i,emptyMessage:e}),s&&D(m),_._dmTable={columns:A,rows:f,slug:p,exportCfg:y,render(x){T.create("#"+u,{data:x,columns:A,search:o,sortable:t,exportable:n,pageSize:i,emptyMessage:e}),s&&D(m)}}})})(),(function(){const g=document.querySelectorAll("[data-form-inline]");if(!g.length||typeof F>"u")return;function _(t){const n=t&&t.logic;return!!(n&&(n.visibility||n.requirement))}function C(t,n){const i={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const s=e.type==="chooser"||!!(e.variant&&["select","radio","checkbox-group"].includes(e.type)),p=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":s?"chooser":e.type,y={...e.formConfig||{}};y.span==="full"&&n&&(y.span=n);const u={type:p,required:_(e)?!1:e.required,label:e.label,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{hint:e.helper},...e.tooltip&&{tooltip:e.tooltip},...y}};e.minLength!==void 0&&(u.minLength=e.minLength),e.maxLength!==void 0&&(u.maxLength=e.maxLength),e.min!==void 0&&(u.min=e.min),e.max!==void 0&&(u.max=e.max),s&&(e.variant&&(u.variant=e.variant),(e.multiple||e.type==="checkbox-group")&&(u.multiple=!0),e.density&&(u.density=e.density),e.columns&&(u.columns=e.columns),e.accent&&(u.accent=e.accent),e.accentStyle&&(u.accentStyle=e.accentStyle),e.glow&&(u.glow=!0),e.glowColour&&(u.glowColour=e.glowColour),e.shadow&&(u.shadow=e.shadow),e.shadowColour&&(u.shadowColour=e.shadowColour)),i[e.name]=u}),i}function A(t){const n={};return(t||[]).forEach(i=>{if(!(!i.name||i.type==="page-break"||i.type==="spacer")&&(i.type==="select"||i.type==="multiselect")&&i.required){const e=(i.options||[])[0];e&&(n[i.name]=typeof e=="object"?e.value:e)}}),n}function f(t,n){(n||[]).forEach(i=>{if(i.type!=="date"||!i.name)return;const e=t.querySelector(`[name="${i.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function o(t,n,i){let e=t.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",t.appendChild(e)),e.textContent=n,e.style.cssText=i?"color:var(--dm-danger);margin-top:.75rem;":"color:var(--dm-success);margin-top:.75rem;"}g.forEach(t=>{let n;try{n=JSON.parse(atob(t.dataset.formInline))}catch{return}const i=n.fields||[],e=n.settings||{},s=Number(e.columns)===3?3:2,p=e.layout||"grid",y=i.some(r=>r.type==="page-break"),u=async r=>{if(t.dataset.triggerBlocked==="true"){const a=t._formLogicRuntime,l=a&&a.submitBlock?a.submitBlock().reasons:[];o(t,l.join(" ")||"This form cannot be submitted yet.",!0);const d=new Error("Blocked by a form trigger");throw d.formSubmitFailed=!0,d}try{const a=t.querySelector(".fb-form-honeypot"),l=a?.querySelector('input[type="text"]')||null,d=a?.querySelector('[name="_t"]')||null,b=Object.assign({},r);delete b._t,a?.dataset.hpName&&delete b[a.dataset.hpName],l!==null&&(b._hp=l.value),d!==null&&(b._t=d.value);const v=await H.post(`/api/forms/submit/${n.slug}`,b);if(v&&v.error){o(t,v.error,!0);const I=new Error(v.error);throw I.formSubmitFailed=!0,I}if(v?.redirect){window.location.href=v.redirect;return}for(;t.firstChild;)t.removeChild(t.firstChild);o(t,v?.message||e.successMessage||"Thank you for your submission.",!1)}catch(a){throw a.formSubmitFailed||o(t,a.message||"Submission failed. Please try again.",!0),a}},m=Date.now();function x(r){const a=r.querySelector("form");if(!a||a.querySelector(".fb-form-honeypot"))return;const l=document.createElement("div");l.className="fb-form-honeypot",l.setAttribute("aria-hidden","true");const d=document.createElement("input");d.name=a.querySelector('[name="website"]')?"_website":"website",l.dataset.hpName=d.name,d.type="text",d.tabIndex=-1,d.autocomplete="url",d.placeholder="https://",l.appendChild(d);const b=document.createElement("input");b.name="_t",b.type="hidden",b.value=m,l.appendChild(b),a.appendChild(l)}function c(r){if(!window.FormLogicEngine||!i.some(l=>l.logic||Array.isArray(l.triggers)&&l.triggers.length))return null;const a=new window.FormLogicEngine.FormLogicRuntime(n,t,r?{baseValues:r}:void 0);if(a.init(),t._formLogicRuntime=a,t._formLogicObserved)return a;if(t._formLogicObserved=!0,t.parentNode&&typeof MutationObserver<"u"){const l=new MutationObserver(function(d){for(const b of d)for(const v of b.removedNodes)if(v===t||v.nodeType===1&&v.contains&&v.contains(t)){t._formLogicRuntime&&t._formLogicRuntime.destroy(),l.disconnect();return}});l.observe(t.parentNode,{childList:!0,subtree:!1}),t._formLogicObserver=l}return a}let h=!1;if(t.addEventListener("dm-form-end",r=>{const a=r.detail?.validation;if(a&&!a.valid){R(t,a.missing),P(a);return}h||(h=!0,u(r.detail?.values||{}).catch(()=>{}).finally(()=>{h=!1}))}),y&&F.wizard){const r=U(i,n.title),a=r.map(k=>({title:k.title,description:k.description,fields:C(k.fields,s)})),l=r.map(k=>k.fields);let d=0,b=null,v=null;const I=k=>{const L=window.FormLogicEngine;if(!L||!L.validateFields)return;const z=L.validateFields(t,l[d]||[]);z.valid||(k.preventDefault(),k.stopImmediatePropagation(),R(t,z.missing),P(z))},q=()=>{const k=l[d]||i;f(t,k),j(t,k),e.honeypot!==!1&&x(t),b&&b.destroy(),b=c(v?.getStepData?v.getStepData():void 0),t.querySelectorAll(".wizard-next, .wizard-submit").forEach(L=>L.addEventListener("click",I,!0))};v=F.wizard(t,{schema:{steps:a},layout:p,columns:s,finishText:e.submitText||"Finish",onSubmit:u,onStepChange:k=>{d=k,setTimeout(q,0)}}),t.addEventListener("dm-form-trigger",k=>{if(k.detail?.type!=="goto-step"||!v?.goToStep)return;const L=Number(k.detail.action.step);!Number.isFinite(L)||L<0||L>=a.length||L!==d&&v.goToStep(L)}),Promise.resolve(v).then(q)}else if(F.render){const r=F.render(t,C(i,s),A(i),{submitText:e.submitText||"Submit",layout:p,columns:s,onSubmit:u});Promise.resolve(r).then(function(){if(p==="grid"&&e.submitSpan==="full"){const a=t.querySelector(".form-buttons");a&&a.classList.add("col-span-full")}f(t,i),j(t,i),e.honeypot!==!1&&x(t),c()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Generate a secure INSTANCE_SECRET for domma-cms-manager integration.
3
+ * Generate a secure INSTANCE_SECRET - the key Mail Reader seals stored mailbox
4
+ * passwords with (plugins/_lib/mail/secretbox.js; MAIL_SECRET overrides it). It
5
+ * is no longer part of manager integration: managed sites use FLEET_TOKEN.
4
6
  * Usage: node scripts/gen-instance-secret.js
5
7
  * Writes INSTANCE_SECRET to .env (skips if already set).
6
8
  */
package/scripts/setup.js CHANGED
@@ -7,7 +7,9 @@
7
7
  *
8
8
  * Steps:
9
9
  * 1. Generate JWT_SECRET (skips if already set)
10
- * 2. Generate INSTANCE_SECRET (skips if already set)
10
+ * 2. Generate INSTANCE_SECRET (skips if already set) - the key Mail Reader seals
11
+ * stored mailbox passwords with (plugins/_lib/mail/secretbox.js); without it
12
+ * they fall back to a key file beside the sealed data
11
13
  * 3. Create admin account (skips if users already exist)
12
14
  * 4. Set site title and tagline
13
15
  * 5. Pick a theme
@@ -125,7 +125,8 @@ export async function requireAdmin(request, reply) {
125
125
 
126
126
  /**
127
127
  * Determine whether an actor can manage a target user.
128
- * Managers cannot create, edit, or delete users with a lower level number (higher privilege).
128
+ * True only when the actor is strictly more senior (a lower level number) than the
129
+ * target, so nobody can create, edit or delete a peer or anyone above them.
129
130
  *
130
131
  * Accepts either user objects (preferred - uses effective level across all roles)
131
132
  * or bare role-name strings (legacy compat). Strings are looked up via
@@ -12,7 +12,8 @@
12
12
  * GET /actions/collection/:slug - List actions for a collection
13
13
  *
14
14
  * Public endpoint (role-checked per action access config):
15
- * POST /actions/:slug/public - Execute action (role-restricted)
15
+ * POST /actions/:slug/public - Execute action (role-restricted; body scope: 'mine')
16
+ * GET /actions/transitions - Transitions offered on one entry (?scope=mine)
16
17
  */
17
18
  import {
18
19
  createAction,
@@ -21,6 +22,7 @@ import {
21
22
  getAction,
22
23
  listActions,
23
24
  listActionsForCollection,
25
+ canRunAction,
24
26
  listTransitionsForEntry,
25
27
  updateAction
26
28
  } from '../../services/actions.js';
@@ -29,10 +31,22 @@ import {
29
31
  authenticate as defaultAuthenticate,
30
32
  requirePermission as defaultRequirePermission
31
33
  } from '../../middleware/auth.js';
32
- import {getRoleLevel} from '../../services/roles.js';
33
34
  import {getEffectiveLevel} from '../../services/userRoles.js';
34
35
  import {checkEntryAccess} from '../../services/rowAccess.js';
35
36
 
37
+ /**
38
+ * HTTP status for an error thrown by executeAction(): row-level denial 403, an
39
+ * illegal state-machine transition 409 (the request was authorised but the entry
40
+ * has moved on), a missing action or entry 404, anything else 400.
41
+ *
42
+ * @param {Error & {statusCode?: number}} err
43
+ * @returns {number}
44
+ */
45
+ export function executeErrorStatus(err) {
46
+ if (err?.statusCode === 403 || err?.statusCode === 409) return err.statusCode;
47
+ return String(err?.message || '').includes('not found') ? 404 : 400;
48
+ }
49
+
36
50
  /**
37
51
  * The sidebar badge on Actions (its countUrl): how many actions this user can
38
52
  * see, which collections they act on, which are state-machine transitions.
@@ -180,9 +194,7 @@ export async function actionsRoutes(fastify, opts = {}) {
180
194
  );
181
195
  return result;
182
196
  } catch (err) {
183
- const status = err.statusCode === 403 ? 403
184
- : err.message.includes('not found') ? 404 : 400;
185
- return reply.status(status).send({ error: err.message });
197
+ return reply.status(executeErrorStatus(err)).send({ error: err.message });
186
198
  }
187
199
  });
188
200
 
@@ -241,32 +253,33 @@ export async function actionsRoutes(fastify, opts = {}) {
241
253
 
242
254
  if (!action) return reply.status(404).send({ error: 'Action not found' });
243
255
 
244
- // Always require authentication for public action endpoints
256
+ // Always require authentication (an access token) for public action endpoints
245
257
  try {
246
- await request.jwtVerify();
258
+ const decoded = await request.jwtVerify();
259
+ if (decoded.type !== 'access') throw new Error('not an access token');
247
260
  } catch {
248
261
  return reply.status(401).send({ error: 'Unauthorised' });
249
262
  }
250
263
 
251
- const user = request.user;
252
- const allowedRoles = action.access?.roles || ['admin', 'super-admin'];
253
- // Use effective level - a candidate-with-also-admin can run admin-tier actions.
254
- const userLevel = getEffectiveLevel(user);
255
- const minAllowed = Math.min(...allowedRoles.map(r => getRoleLevel(r)));
256
-
257
- if (userLevel > minAllowed) {
264
+ const user = request.user;
265
+ // Any listed role is enough, on its ladder, across every role the user
266
+ // holds; an unknown role name admits nobody but level 0 (canRunAction).
267
+ if (!canRunAction(user, action)) {
258
268
  return reply.status(403).send({ error: 'Insufficient permissions' });
259
269
  }
260
270
 
261
- const { entryId } = request.body || {};
271
+ const { entryId, scope } = request.body || {};
262
272
  if (!entryId) return reply.status(400).send({ error: 'entryId is required' });
273
+ // scope: 'mine' - sent by a [collection scope="mine"] display; a row
274
+ // outside the viewer's scope is refused 403 like a row-level denial.
275
+ if (scope !== undefined && scope !== 'mine') {
276
+ return reply.status(400).send({ error: 'scope must be "mine"' });
277
+ }
263
278
 
264
279
  try {
265
- return await executeAction(action.slug, entryId, { user });
280
+ return await executeAction(action.slug, entryId, { user, scope });
266
281
  } catch (err) {
267
- const status = err.statusCode === 403 ? 403
268
- : err.message.includes('not found') ? 404 : 400;
269
- return reply.status(status).send({ error: err.message });
282
+ return reply.status(executeErrorStatus(err)).send({ error: err.message });
270
283
  }
271
284
  });
272
285
 
@@ -275,26 +288,33 @@ export async function actionsRoutes(fastify, opts = {}) {
275
288
  *
276
289
  * Returns the actions valid as *next-state transitions* for the given
277
290
  * entry, filtered by the requesting user's role. Used by the per-row
278
- * transitions UI in the Collection Browser and the `[transitions]`
279
- * shortcode to render only the buttons that will actually succeed.
291
+ * transitions UI in the Collection Browser (`[collection … transitions]`)
292
+ * to render only the buttons that will actually succeed.
280
293
  *
281
- * Auth: optional. Anonymous calls return actions accessible to anonymous
282
- * users (rare - most transitions require a role). With a JWT we narrow
283
- * to what the signed-in user can do.
294
+ * Auth: optional. Anonymous calls get none (running an action needs a
295
+ * signed-in user). With a JWT: the actions whose roles admit the user
296
+ * (canRunAction) and whose row-level rule passes for this entry.
297
+ * `scope=mine` (a [collection scope="mine"] display) offers nothing on a
298
+ * row the viewer did not create.
284
299
  *
285
300
  * Response: { transitions: [{slug, title, trigger, transition}, ...] }
286
301
  * Returns 503 if MongoDB is unavailable (actions are a Pro feature).
287
302
  */
288
303
  fastify.get('/actions/transitions', async (request, reply) => {
289
- const { collection: collectionSlug, entryId } = request.query;
304
+ const { collection: collectionSlug, entryId, scope } = request.query;
290
305
  if (!collectionSlug || !entryId) {
291
306
  return reply.status(400).send({ error: 'collection and entryId are required' });
292
307
  }
308
+ if (scope !== undefined && scope !== 'mine') {
309
+ return reply.status(400).send({ error: 'scope must be "mine"' });
310
+ }
293
311
 
294
312
  let user = null;
295
313
  try {
296
314
  const decoded = await request.jwtVerify();
297
- if (decoded.type === 'access') user = {id: decoded.id, role: decoded.role};
315
+ if (decoded.type === 'access') {
316
+ user = {id: decoded.id, role: decoded.role, additionalRoles: decoded.additionalRoles || []};
317
+ }
298
318
  } catch { /* anonymous */ }
299
319
 
300
320
  let entry;
@@ -306,7 +326,7 @@ export async function actionsRoutes(fastify, opts = {}) {
306
326
  if (!entry) return reply.status(404).send({ error: 'Entry not found' });
307
327
 
308
328
  try {
309
- const transitions = await listTransitionsForEntry(collectionSlug, entry, user);
329
+ const transitions = await listTransitionsForEntry(collectionSlug, entry, user, {scope});
310
330
  return { transitions };
311
331
  } catch (err) {
312
332
  return reply.status(503).send({ error: err.message, transitions: [] });
@@ -15,6 +15,7 @@ import path from 'path';
15
15
  import {authenticate, requirePermission} from '../../middleware/auth.js';
16
16
  import {deleteBlock, getBlock, listBlocks, saveBlock} from '../../services/blocks.js';
17
17
  import {config} from '../../config.js';
18
+ import {fmtDate} from '../../services/dates.js';
18
19
 
19
20
  /**
20
21
  * A block's own doc comment - the one every block opens with - read for the
@@ -139,7 +140,7 @@ export function blocksBadge(blocks = []) {
139
140
  ],
140
141
  items: recent.map(b => ({
141
142
  text: b.name,
142
- meta: [b.updatedAt ? `changed ${String(b.updatedAt).slice(0, 10)}` : '', b.usage ? `used ${used(b)} time${used(b) === 1 ? '' : 's'}` : ''].filter(Boolean).join(' · ')
143
+ meta: [b.updatedAt ? `changed ${fmtDate(b.updatedAt)}` : '', b.usage ? `used ${used(b)} time${used(b) === 1 ? '' : 's'}` : ''].filter(Boolean).join(' · ')
143
144
  })),
144
145
  empty: 'No blocks yet.'
145
146
  }