domma-cms 0.25.15 → 0.30.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 (67) hide show
  1. package/admin/js/app.js +2 -2
  2. package/admin/js/lib/sidebar-renderer.js +4 -4
  3. package/admin/js/templates/api-reference.html +68 -1233
  4. package/admin/js/templates/docs/api-actions.html +146 -0
  5. package/admin/js/templates/docs/api-authentication.html +202 -0
  6. package/admin/js/templates/docs/api-collections.html +347 -0
  7. package/admin/js/templates/docs/api-layouts.html +123 -0
  8. package/admin/js/templates/docs/api-media.html +158 -0
  9. package/admin/js/templates/docs/api-navigation.html +99 -0
  10. package/admin/js/templates/docs/api-pages.html +214 -0
  11. package/admin/js/templates/docs/api-plugins.html +136 -0
  12. package/admin/js/templates/docs/api-settings.html +148 -0
  13. package/admin/js/templates/docs/api-users.html +189 -0
  14. package/admin/js/templates/docs/api-views.html +142 -0
  15. package/admin/js/templates/docs/components-howto.html +175 -0
  16. package/admin/js/templates/docs/components-reference.html +259 -0
  17. package/admin/js/templates/docs/components-rules.html +167 -0
  18. package/admin/js/templates/docs/components-walkthrough.html +167 -0
  19. package/admin/js/templates/docs/tutorial-crud.html +344 -0
  20. package/admin/js/templates/docs/tutorial-forms.html +93 -0
  21. package/admin/js/templates/docs/tutorial-plugin.html +234 -0
  22. package/admin/js/templates/docs/usage-actions.html +122 -0
  23. package/admin/js/templates/docs/usage-cta-shortcode.html +124 -0
  24. package/admin/js/templates/docs/usage-dconfig.html +148 -0
  25. package/admin/js/templates/docs/usage-media.html +25 -0
  26. package/admin/js/templates/docs/usage-navigation.html +31 -0
  27. package/admin/js/templates/docs/usage-pages.html +68 -0
  28. package/admin/js/templates/docs/usage-plugins.html +36 -0
  29. package/admin/js/templates/docs/usage-shortcodes.html +810 -0
  30. package/admin/js/templates/docs/usage-site-settings.html +52 -0
  31. package/admin/js/templates/docs/usage-users-roles.html +84 -0
  32. package/admin/js/templates/docs/usage-views.html +105 -0
  33. package/admin/js/templates/documentation.html +60 -1522
  34. package/admin/js/templates/effects.html +752 -752
  35. package/admin/js/templates/forms.html +17 -17
  36. package/admin/js/templates/my-profile.html +17 -17
  37. package/admin/js/templates/project-settings.html +7 -0
  38. package/admin/js/templates/role-editor.html +70 -70
  39. package/admin/js/templates/roles.html +10 -10
  40. package/admin/js/templates/tutorials.html +46 -659
  41. package/admin/js/views/api-reference.js +1 -1
  42. package/admin/js/views/doc-pages.js +1 -0
  43. package/admin/js/views/documentation.js +1 -1
  44. package/admin/js/views/index.js +1 -1
  45. package/admin/js/views/project-settings.js +1 -1
  46. package/admin/js/views/projects.js +3 -7
  47. package/admin/js/views/tutorials.js +1 -1
  48. package/bin/lib/config-merge.js +44 -44
  49. package/config/connections.json.bak +9 -0
  50. package/config/menus/admin-sidebar.json +76 -6
  51. package/package.json +1 -1
  52. package/public/js/site.js +1 -1
  53. package/scripts/migrate-docs.js +280 -0
  54. package/server/middleware/auth.js +253 -253
  55. package/server/routes/api/auth.js +309 -309
  56. package/server/routes/api/collections.js +10 -1
  57. package/server/routes/api/navigation.js +42 -42
  58. package/server/routes/api/projects.js +9 -1
  59. package/server/routes/api/settings.js +141 -141
  60. package/server/routes/public.js +9 -0
  61. package/server/server.js +6 -2
  62. package/server/services/email.js +167 -167
  63. package/server/services/presetCollections.js +1 -0
  64. package/server/services/projects.js +72 -3
  65. package/server/services/sidebar-migration.js +145 -3
  66. package/server/services/userProfiles.js +199 -199
  67. package/server/services/users.js +302 -302
@@ -1 +1 @@
1
- export const apiReferenceView={templateUrl:"/admin/js/templates/api-reference.html",async onMount(e){E.tabs(e.find("#api-tabs").get(0)),Domma.icons.scan(),Domma.syntax.scan()}};
1
+ export const apiReferenceView={templateUrl:"/admin/js/templates/api-reference.html",async onMount(){Domma.icons.scan(),Domma.syntax.scan()}};
@@ -0,0 +1 @@
1
+ import{mountScaffolder as i}from"../lib/crud-tutorial.js";function t(o,n){return{templateUrl:`/admin/js/templates/docs/${o}.html`,async onMount(e){typeof n=="function"&&n(e),Domma.icons.scan(),Domma.syntax.scan()}}}export const docViews={tutorialCrud:t("tutorial-crud",o=>{const n=o.find("#tutorial-scaffolder-mount").get(0);n&&i(n,null,null)}),tutorialPlugin:t("tutorial-plugin"),tutorialForms:t("tutorial-forms"),componentsReference:t("components-reference"),componentsHowto:t("components-howto"),componentsWalkthrough:t("components-walkthrough"),componentsRules:t("components-rules")};const a=["pages","media","navigation","dconfig","shortcodes","site-settings","plugins","users-roles","views","actions","cta-shortcode"],c=["authentication","pages","settings","layouts","navigation","media","users","plugins","collections","views","actions"],s=o=>o.split("-").map(n=>n.charAt(0).toUpperCase()+n.slice(1)).join("");for(const o of a)docViews["usage"+s(o)]=t(`usage-${o}`);for(const o of c)docViews["apiRef"+s(o)]=t(`api-${o}`);
@@ -1 +1 @@
1
- export const documentationView={templateUrl:"/admin/js/templates/documentation.html",async onMount(t){E.tabs(t.find("#docs-tabs").get(0)),Domma.icons.scan(),Domma.syntax.scan()}};
1
+ export const documentationView={templateUrl:"/admin/js/templates/documentation.html",async onMount(){Domma.icons.scan(),Domma.syntax.scan()}};
@@ -1 +1 @@
1
- import{dashboardView as o}from"./dashboard.js";import{pagesView as i}from"./pages.js";import{pageEditorView as r}from"./page-editor.js";import{settingsView as t}from"./settings.js";import{navigationView as e}from"./navigation.js";import{notificationsView as m}from"./notifications.js";import{layoutsView as p}from"./layouts.js";import{mediaView as s}from"./media.js";import{loginView as n}from"./login.js";import{usersView as f}from"./users.js";import{userEditorView as w}from"./user-editor.js";import{pluginsView as c}from"./plugins.js";import{documentationView as V}from"./documentation.js";import{tutorialsView as a}from"./tutorials.js";import{apiReferenceView as d}from"./api-reference.js";import{collectionsView as l}from"./collections.js";import{collectionEditorView as E}from"./collection-editor.js";import{collectionEntriesView as u}from"./collection-entries.js";import{formsView as g}from"./forms.js";import{formEditorView as v}from"./form-editor.js";import{formSubmissionsView as b}from"./form-submissions.js";import{viewsListView as j}from"./views-list.js";import{viewEditorView as k}from"./view-editor.js";import{viewPreviewView as L}from"./view-preview.js";import{actionsListView as y}from"./actions-list.js";import{actionEditorView as h}from"./action-editor.js";import{proDocsView as D}from"./pro-docs.js";import{blocksView as P}from"./blocks.js";import{blockEditorView as R}from"./block-editor.js";import"./block-editor-enhance.js";import{componentsView as S}from"./components.js";import{componentEditorView as T}from"./component-editor.js";import{myProfileView as x}from"./my-profile.js";import{rolesView as M}from"./roles.js";import{roleEditorView as U}from"./role-editor.js";import{effectsView as q}from"./effects.js";import{menusView as z}from"./menus.js";import{menuEditorView as A}from"./menu-editor.js";import{menuLocationsView as B}from"./menu-locations.js";import{projectsView as C}from"./projects.js";import{projectEditorView as F}from"./project-editor.js";import{projectDetailView as G}from"./project-detail.js";import{projectSettingsView as H}from"./project-settings.js";import{apiTokensView as I}from"./api-tokens.js";import{apiEndpointsView as J}from"./api-endpoints.js";import{apiEndpointEditorView as K}from"./api-endpoint-editor.js";const N={templateUrl:"",async onMount(){location.hash="#/menus"}};export const views={projects:C,projectEditor:F,projectDetail:G,projectSettings:H,apiTokens:I,apiEndpoints:J,apiEndpointEditor:K,dashboard:o,pages:i,pageEditor:r,settings:t,navigation:e,layouts:p,media:s,login:n,users:f,userEditor:w,plugins:c,documentation:V,tutorials:a,apiReference:d,collections:l,collectionEditor:E,collectionEntries:u,forms:g,formEditor:v,formSubmissions:b,viewsList:j,viewEditor:k,viewPreview:L,actionsList:y,actionEditor:h,proDocs:D,blocks:P,blockEditor:R,components:S,componentEditor:T,myProfile:x,roles:M,roleEditor:U,effects:q,notifications:m,menus:z,menuEditor:A,menuLocations:B,menusRedirect:N};
1
+ import{dashboardView as o}from"./dashboard.js";import{pagesView as i}from"./pages.js";import{pageEditorView as r}from"./page-editor.js";import{settingsView as t}from"./settings.js";import{navigationView as e}from"./navigation.js";import{notificationsView as m}from"./notifications.js";import{layoutsView as p}from"./layouts.js";import{mediaView as s}from"./media.js";import{loginView as n}from"./login.js";import{usersView as f}from"./users.js";import{userEditorView as w}from"./user-editor.js";import{pluginsView as c}from"./plugins.js";import{documentationView as V}from"./documentation.js";import{tutorialsView as a}from"./tutorials.js";import{docViews as d}from"./doc-pages.js";import{apiReferenceView as l}from"./api-reference.js";import{collectionsView as E}from"./collections.js";import{collectionEditorView as u}from"./collection-editor.js";import{collectionEntriesView as g}from"./collection-entries.js";import{formsView as v}from"./forms.js";import{formEditorView as b}from"./form-editor.js";import{formSubmissionsView as j}from"./form-submissions.js";import{viewsListView as k}from"./views-list.js";import{viewEditorView as L}from"./view-editor.js";import{viewPreviewView as y}from"./view-preview.js";import{actionsListView as h}from"./actions-list.js";import{actionEditorView as D}from"./action-editor.js";import{proDocsView as P}from"./pro-docs.js";import{blocksView as R}from"./blocks.js";import{blockEditorView as S}from"./block-editor.js";import"./block-editor-enhance.js";import{componentsView as T}from"./components.js";import{componentEditorView as x}from"./component-editor.js";import{myProfileView as M}from"./my-profile.js";import{rolesView as U}from"./roles.js";import{roleEditorView as q}from"./role-editor.js";import{effectsView as z}from"./effects.js";import{menusView as A}from"./menus.js";import{menuEditorView as B}from"./menu-editor.js";import{menuLocationsView as C}from"./menu-locations.js";import{projectsView as F}from"./projects.js";import{projectEditorView as G}from"./project-editor.js";import{projectDetailView as H}from"./project-detail.js";import{projectSettingsView as I}from"./project-settings.js";import{apiTokensView as J}from"./api-tokens.js";import{apiEndpointsView as K}from"./api-endpoints.js";import{apiEndpointEditorView as N}from"./api-endpoint-editor.js";const O={templateUrl:"",async onMount(){location.hash="#/menus"}};export const views={projects:F,projectEditor:G,projectDetail:H,projectSettings:I,apiTokens:J,apiEndpoints:K,apiEndpointEditor:N,dashboard:o,pages:i,pageEditor:r,settings:t,navigation:e,layouts:p,media:s,login:n,users:f,userEditor:w,plugins:c,documentation:V,tutorials:a,apiReference:l,collections:E,collectionEditor:u,collectionEntries:g,forms:v,formEditor:b,formSubmissions:j,viewsList:k,viewEditor:L,viewPreview:y,actionsList:h,actionEditor:D,proDocs:P,blocks:R,blockEditor:S,components:T,componentEditor:x,myProfile:M,roles:U,roleEditor:q,effects:z,notifications:m,menus:A,menuEditor:B,menuLocations:C,menusRedirect:O,...d};
@@ -1 +1 @@
1
- import{api as c}from"../api.js";import{makeIconInput as g}from"../lib/shortcode-modal.js";export const projectSettingsView={templateUrl:"/admin/js/templates/project-settings.html",async onMount(e){const i=location.hash.split("?")[0].match(/^#\/projects\/([^/]+)\/settings$/),o=i?decodeURIComponent(i[1]):null;if(!o){location.hash="#/projects";return}let s;try{s=await c.projects.get(o)}catch(t){E.toast(`Failed to load project: ${t.message||t}`,{type:"error"}),location.hash="#/projects";return}const m=encodeURIComponent(o);e.find("#ps-title").text(`Settings \u2014 ${s.name||o}`),e.find("#ps-back").attr("href","#/projects/"+m);const f=e.find("#ps-slug"),l=e.find("#ps-name"),p=e.find("#ps-description"),a=e.find("#ps-rootUrl"),d=e.find("#ps-sortOrder");if(f.val(s.slug||""),l.val(s.name||""),p.val(s.description||""),a.val(s.rootUrl||""),d.val(s.sortOrder??0),s.protected){a.attr("disabled","disabled");const t=a.get(0)?.nextElementSibling;t?.classList.contains("form-hint")&&(t.textContent="Locked \u2014 built-in project"),e.find("#ps-danger-zone").css("display","none")}const u=e.find("#ps-icon-mount").get(0),n=g("e.g. folder, users, box",s.icon||"folder");n.input.id="ps-icon",n.input.classList.add("form-input"),u.appendChild(n.el),Domma.icons.scan(e.get(0)),e.find("#ps-save").on("click",async()=>{const t={name:l.val().trim(),description:p.val().trim(),icon:n.input.value.trim()||"folder",sortOrder:Number.parseInt(d.val(),10)||0};if(!s.protected){const r=a.val().trim();r&&(t.rootUrl=r)}try{await c.projects.update(o,t),E.toast("Saved.",{type:"success"})}catch(r){E.toast(`Save failed: ${r.message||r}`,{type:"error"})}}),e.find("#ps-untag-all").on("click",async()=>{if(await E.confirm(`Untag all artefacts from project "${o}"? They will become site-wide.`))try{const t=await c.projects.untagAll(o);E.toast(`Untagged: ${JSON.stringify(t.untagged)}`,{type:"success"}),setTimeout(()=>location.reload(),600)}catch(t){E.toast(`Untag failed: ${t.message||t}`,{type:"error"})}}),e.find("#ps-delete").on("click",async()=>{if(await E.confirm(`Delete project "${o}"? The server refuses if any artefacts are still tagged.`))try{await c.projects.remove(o),E.toast("Project deleted.",{type:"success"}),location.hash="#/projects"}catch(t){E.toast(`Delete refused: ${t.message||t}`,{type:"error"})}})}};
1
+ import{api as c}from"../api.js";import{makeIconInput as h}from"../lib/shortcode-modal.js";export const projectSettingsView={templateUrl:"/admin/js/templates/project-settings.html",async onMount(e){const i=location.hash.split("?")[0].match(/^#\/projects\/([^/]+)\/settings$/),o=i?decodeURIComponent(i[1]):null;if(!o){location.hash="#/projects";return}let s;try{s=await c.projects.get(o)}catch(t){E.toast(`Failed to load project: ${t.message||t}`,{type:"error"}),location.hash="#/projects";return}const m=encodeURIComponent(o);e.find("#ps-title").text(`Settings \u2014 ${s.name||o}`),e.find("#ps-back").attr("href","#/projects/"+m);const u=e.find("#ps-slug"),l=e.find("#ps-name"),p=e.find("#ps-description"),a=e.find("#ps-rootUrl"),d=e.find("#ps-sortOrder"),f=e.find("#ps-enabled");if(u.val(s.slug||""),l.val(s.name||""),p.val(s.description||""),a.val(s.rootUrl||""),d.val(s.sortOrder??0),f.prop("checked",s.enabled!==!1),s.protected){a.attr("disabled","disabled");const t=a.get(0)?.nextElementSibling;t?.classList.contains("form-hint")&&(t.textContent="Locked \u2014 built-in project"),e.find("#ps-danger-zone").css("display","none"),e.find("#ps-enabled-group").css("display","none")}const g=e.find("#ps-icon-mount").get(0),n=h("e.g. folder, users, box",s.icon||"folder");n.input.id="ps-icon",n.input.classList.add("form-input"),g.appendChild(n.el),Domma.icons.scan(e.get(0)),e.find("#ps-save").on("click",async()=>{const t={name:l.val().trim(),description:p.val().trim(),icon:n.input.value.trim()||"folder",sortOrder:Number.parseInt(d.val(),10)||0};if(!s.protected){const r=a.val().trim();r&&(t.rootUrl=r),t.enabled=f.prop("checked")}try{await c.projects.update(o,t),E.toast("Saved.",{type:"success"})}catch(r){E.toast(`Save failed: ${r.message||r}`,{type:"error"})}}),e.find("#ps-untag-all").on("click",async()=>{if(await E.confirm(`Untag all artefacts from project "${o}"? They will become site-wide.`))try{const t=await c.projects.untagAll(o);E.toast(`Untagged: ${JSON.stringify(t.untagged)}`,{type:"success"}),setTimeout(()=>location.reload(),600)}catch(t){E.toast(`Untag failed: ${t.message||t}`,{type:"error"})}}),e.find("#ps-delete").on("click",async()=>{if(await E.confirm(`Delete project "${o}"? The server refuses if any artefacts are still tagged.`))try{await c.projects.remove(o),E.toast("Project deleted.",{type:"success"}),location.hash="#/projects"}catch(t){E.toast(`Delete refused: ${t.message||t}`,{type:"error"})}})}};
@@ -1,7 +1,3 @@
1
- import{api as s}from"../api.js";function o(a){return String(a??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}export const projectsView={templateUrl:"/admin/js/templates/projects.html",async onMount(a){const n=await s.projects.list().catch(()=>[]),c=await Promise.all(n.map(t=>s.projects.artefacts(t.slug).then(e=>Object.values(e).reduce((l,p)=>l+p.length,0)).catch(()=>0))),r=n.map((t,e)=>({...t,artefactCount:c[e]}));T.create("#projects-table",{data:r,emptyMessage:'No projects yet. Click "New project" to create one.',columns:[{key:"slug",title:"Slug",sortable:!0},{key:"name",title:"Name",sortable:!0,render:(t,e)=>o(t)+(e.protected?' <span class="badge badge-secondary" data-tooltip="Built-in project \u2014 cannot be deleted">System</span>':"")},{key:"icon",title:"Icon",render:t=>t?`<span data-icon="${o(t)}"></span>`:""},{key:"rootUrl",title:"Root URL",render:t=>t?`<code>${o(t)}</code>`:'<span class="text-muted">\u2014</span>'},{key:"artefactCount",title:"Artefacts",render:t=>`<span class="badge badge-info">${t??0}</span>`},{key:"_actions",title:"Actions",render:(t,e)=>`
2
- <span style="display:inline-flex;gap:0.25rem;">
3
- <a class="btn btn-sm btn-ghost" href="#/projects/${encodeURIComponent(e.slug)}" data-tooltip="Overview"><span data-icon="eye"></span></a>
4
- <a class="btn btn-sm btn-ghost" href="#/projects/${encodeURIComponent(e.slug)}/settings" data-tooltip="Settings"><span data-icon="settings"></span></a>
5
- ${e.protected?"":`<button class="btn btn-sm btn-danger btn-delete-project" data-slug="${o(e.slug)}" data-tooltip="Delete (refused while tagged)"><span data-icon="trash"></span></button>`}
6
- </span>
7
- `}]}),Domma.icons.scan(a.get(0)),document.querySelectorAll("#projects-table [data-tooltip]").forEach(t=>{E.tooltip(t,{content:t.getAttribute("data-tooltip"),position:"top"})}),a.off("click",".btn-delete-project").on("click",".btn-delete-project",async function(){const t=$(this).data("slug");if(await E.confirm(`Delete project "${t}"? This cannot be undone.`))try{await s.projects.remove(t),E.toast("Project deleted.",{type:"success"}),location.reload()}catch(e){E.toast(`Delete refused: ${e.message||e}`,{type:"error"})}})}};
1
+ import{api as l}from"../api.js";function s(n){return String(n??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}export const projectsView={templateUrl:"/admin/js/templates/projects.html",async onMount(n){const r=await l.projects.list().catch(()=>[]),p=await Promise.all(r.map(t=>l.projects.artefacts(t.slug).then(e=>Object.values(e).reduce((a,c)=>a+c.length,0)).catch(()=>0))),d=r.map((t,e)=>({...t,artefactCount:p[e]})),o=t=>t.enabled===!1;T.create("#projects-table",{data:d,emptyMessage:'No projects yet. Click "New project" to create one.',columns:[{key:"slug",title:"Slug",sortable:!0,render:(t,e)=>o(e)?`<span class="text-muted">${s(t)}</span>`:s(t)},{key:"name",title:"Name",sortable:!0,render:(t,e)=>{const a=o(e)?`<span class="text-muted">${s(t)}</span>`:s(t),c=e.protected?' <span class="badge badge-secondary" data-tooltip="Built-in project \u2014 cannot be deleted">System</span>':"",i=o(e)?' <span class="badge badge-warning" data-tooltip="Disabled \u2014 public pages and external API are off">Disabled</span>':"";return a+c+i}},{key:"icon",title:"Icon",render:(t,e)=>t?`<span class="${o(e)?"text-muted":""}" data-icon="${s(t)}"></span>`:""},{key:"rootUrl",title:"Root URL",render:t=>t?`<code>${s(t)}</code>`:'<span class="text-muted">\u2014</span>'},{key:"artefactCount",title:"Artefacts",render:t=>`<span class="badge badge-info">${t??0}</span>`},{key:"_actions",title:"Actions",render:(t,e)=>{const a=encodeURIComponent(e.slug),c=o(e)?`<span class="btn btn-sm btn-ghost disabled" aria-disabled="true" style="opacity:.4;pointer-events:none;"><span data-icon="eye"></span></span>
2
+ <span class="btn btn-sm btn-ghost disabled" aria-disabled="true" style="opacity:.4;pointer-events:none;"><span data-icon="settings"></span></span>`:`<a class="btn btn-sm btn-ghost" href="#/projects/${a}" data-tooltip="Overview"><span data-icon="eye"></span></a>
3
+ <a class="btn btn-sm btn-ghost" href="#/projects/${a}/settings" data-tooltip="Settings"><span data-icon="settings"></span></a>`,b=e.slug==="core"?"":o(e)?`<button class="btn btn-sm btn-ghost btn-toggle-project" data-slug="${s(e.slug)}" data-enable="1" data-tooltip="Enable"><span class="text-success" data-icon="play"></span></button>`:`<button class="btn btn-sm btn-ghost btn-toggle-project" data-slug="${s(e.slug)}" data-enable="0" data-tooltip="Disable"><span class="text-warning" data-icon="pause"></span></button>`,g=e.protected?"":`<button class="btn btn-sm btn-danger btn-delete-project" data-slug="${s(e.slug)}" data-tooltip="Delete (refused while tagged)"><span data-icon="trash"></span></button>`;return`<span style="display:inline-flex;gap:0.25rem;">${c}${b}${g}</span>`}}]}),Domma.icons.scan(n.get(0)),document.querySelectorAll("#projects-table [data-tooltip]").forEach(t=>{E.tooltip(t,{content:t.getAttribute("data-tooltip"),position:"top"})}),n.off("click",".btn-toggle-project").on("click",".btn-toggle-project",async function(){const t=$(this).data("slug"),e=String($(this).data("enable"))==="1";if(!(!e&&!await E.confirm(`Disable project "${t}"? Its public pages will 404 and its external API will stop responding. Admin editing is unaffected.`)))try{await l.projects.update(t,{enabled:e}),E.toast(e?"Project enabled.":"Project disabled.",{type:"success"}),location.reload()}catch(a){E.toast(`Failed: ${a.message||a}`,{type:"error"})}}),n.off("click",".btn-delete-project").on("click",".btn-delete-project",async function(){const t=$(this).data("slug");if(await E.confirm(`Delete project "${t}"? This cannot be undone.`))try{await l.projects.remove(t),E.toast("Project deleted.",{type:"success"}),location.reload()}catch(e){E.toast(`Delete refused: ${e.message||e}`,{type:"error"})}})}};
@@ -1 +1 @@
1
- import{mountScaffolder as a}from"../lib/crud-tutorial.js";export const tutorialsView={templateUrl:"/admin/js/templates/tutorials.html",async onMount(t){E.tabs(t.find("#tutorials-tabs").get(0));const o=t.find("#tutorial-scaffolder-mount").get(0);o&&a(o,null,null),Domma.icons.scan(),Domma.syntax.scan()}};
1
+ export const tutorialsView={templateUrl:"/admin/js/templates/tutorials.html",async onMount(){Domma.icons.scan(),Domma.syntax.scan()}};
@@ -1,44 +1,44 @@
1
- /**
2
- * Domma CMS — Config Merge Utility
3
- * Merges new keys from an upstream config into an existing user config,
4
- * without ever overwriting values the user already has.
5
- */
6
-
7
- /**
8
- * Deep-merge new keys from `upstream` into `existing`.
9
- * Existing values are never modified — only missing keys are added.
10
- *
11
- * @param {object} existing - The user's current config object
12
- * @param {object} upstream - The upstream (new version) config object
13
- * @param {string} [_prefix] - Internal: key path prefix for reporting
14
- * @returns {{ merged: object, added: string[] }} Merged object + list of added key paths
15
- */
16
- export function deepMergeNewKeys(existing, upstream, _prefix = '') {
17
- const merged = {...existing};
18
- const added = [];
19
-
20
- for (const [key, upstreamVal] of Object.entries(upstream)) {
21
- const fullKey = _prefix ? `${_prefix}.${key}` : key;
22
-
23
- if (!(key in existing)) {
24
- // Key is entirely missing — add it wholesale
25
- merged[key] = upstreamVal;
26
- added.push(fullKey);
27
- } else if (
28
- upstreamVal !== null &&
29
- typeof upstreamVal === 'object' &&
30
- !Array.isArray(upstreamVal) &&
31
- typeof existing[key] === 'object' &&
32
- existing[key] !== null &&
33
- !Array.isArray(existing[key])
34
- ) {
35
- // Both sides are plain objects — recurse
36
- const child = deepMergeNewKeys(existing[key], upstreamVal, fullKey);
37
- merged[key] = child.merged;
38
- added.push(...child.added);
39
- }
40
- // Otherwise: existing value wins — no action
41
- }
42
-
43
- return {merged, added};
44
- }
1
+ /**
2
+ * Domma CMS — Config Merge Utility
3
+ * Merges new keys from an upstream config into an existing user config,
4
+ * without ever overwriting values the user already has.
5
+ */
6
+
7
+ /**
8
+ * Deep-merge new keys from `upstream` into `existing`.
9
+ * Existing values are never modified — only missing keys are added.
10
+ *
11
+ * @param {object} existing - The user's current config object
12
+ * @param {object} upstream - The upstream (new version) config object
13
+ * @param {string} [_prefix] - Internal: key path prefix for reporting
14
+ * @returns {{ merged: object, added: string[] }} Merged object + list of added key paths
15
+ */
16
+ export function deepMergeNewKeys(existing, upstream, _prefix = '') {
17
+ const merged = {...existing};
18
+ const added = [];
19
+
20
+ for (const [key, upstreamVal] of Object.entries(upstream)) {
21
+ const fullKey = _prefix ? `${_prefix}.${key}` : key;
22
+
23
+ if (!(key in existing)) {
24
+ // Key is entirely missing — add it wholesale
25
+ merged[key] = upstreamVal;
26
+ added.push(fullKey);
27
+ } else if (
28
+ upstreamVal !== null &&
29
+ typeof upstreamVal === 'object' &&
30
+ !Array.isArray(upstreamVal) &&
31
+ typeof existing[key] === 'object' &&
32
+ existing[key] !== null &&
33
+ !Array.isArray(existing[key])
34
+ ) {
35
+ // Both sides are plain objects — recurse
36
+ const child = deepMergeNewKeys(existing[key], upstreamVal, fullKey);
37
+ merged[key] = child.merged;
38
+ added.push(...child.added);
39
+ }
40
+ // Otherwise: existing value wins — no action
41
+ }
42
+
43
+ return {merged, added};
44
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "default": {
3
+ "type": "mongodb",
4
+ "uri": "mongodb://localhost:27017",
5
+ "database": "domma_cms",
6
+ "options": {}
7
+ },
8
+ "_comment": "Copy this file to connections.json and update with your MongoDB connection details. Each key is a named connection. Collections reference connections by name in their schema.json storage config. The 'default' connection is used when no connection name is specified."
9
+ }
@@ -165,18 +165,88 @@
165
165
  "items": [
166
166
  {
167
167
  "text": "Usage",
168
- "url": "#/documentation",
169
- "icon": "book"
168
+ "url": "",
169
+ "icon": "book",
170
+ "items": [
171
+ {"text": "Pages", "url": "#/docs/usage/pages", "icon": "file-text"},
172
+ {"text": "Media", "url": "#/docs/usage/media", "icon": "image"},
173
+ {"text": "Navigation", "url": "#/docs/usage/navigation", "icon": "menu"},
174
+ {"text": "DConfig", "url": "#/docs/usage/dconfig", "icon": "settings"},
175
+ {"text": "Shortcodes", "url": "#/docs/usage/shortcodes", "icon": "code"},
176
+ {"text": "Site Settings", "url": "#/docs/usage/site-settings", "icon": "settings"},
177
+ {"text": "Plugins", "url": "#/docs/usage/plugins", "icon": "package"},
178
+ {"text": "Users & Roles", "url": "#/docs/usage/users-roles", "icon": "users"},
179
+ {"text": "Views", "url": "#/docs/usage/views", "icon": "eye"},
180
+ {"text": "Actions", "url": "#/docs/usage/actions", "icon": "zap"},
181
+ {"text": "CTA Shortcode", "url": "#/docs/usage/cta-shortcode", "icon": "zap"}
182
+ ]
170
183
  },
171
184
  {
172
185
  "text": "Tutorials",
173
- "url": "#/tutorials",
174
- "icon": "document"
186
+ "url": "",
187
+ "icon": "document",
188
+ "items": [
189
+ {
190
+ "text": "Building a CRUD App",
191
+ "url": "#/tutorials/crud",
192
+ "icon": "database"
193
+ },
194
+ {
195
+ "text": "Writing a Plugin",
196
+ "url": "#/tutorials/plugin",
197
+ "icon": "package"
198
+ },
199
+ {
200
+ "text": "Form Follow-Up",
201
+ "url": "#/tutorials/forms",
202
+ "icon": "layout"
203
+ }
204
+ ]
205
+ },
206
+ {
207
+ "text": "Components",
208
+ "url": "",
209
+ "icon": "component",
210
+ "items": [
211
+ {
212
+ "text": "Reference",
213
+ "url": "#/docs/components",
214
+ "icon": "book"
215
+ },
216
+ {
217
+ "text": "How-To",
218
+ "url": "#/docs/components-howto",
219
+ "icon": "zap"
220
+ },
221
+ {
222
+ "text": "Walkthrough",
223
+ "url": "#/docs/components-walkthrough",
224
+ "icon": "document"
225
+ },
226
+ {
227
+ "text": "Rules",
228
+ "url": "#/docs/components-rules",
229
+ "icon": "shield"
230
+ }
231
+ ]
175
232
  },
176
233
  {
177
234
  "text": "API Reference",
178
- "url": "#/api-reference",
179
- "icon": "code"
235
+ "url": "",
236
+ "icon": "code",
237
+ "items": [
238
+ {"text": "Authentication", "url": "#/docs/api/authentication", "icon": "shield"},
239
+ {"text": "Pages", "url": "#/docs/api/pages", "icon": "file-text"},
240
+ {"text": "Settings", "url": "#/docs/api/settings", "icon": "settings"},
241
+ {"text": "Layouts", "url": "#/docs/api/layouts", "icon": "layout"},
242
+ {"text": "Navigation", "url": "#/docs/api/navigation", "icon": "menu"},
243
+ {"text": "Media", "url": "#/docs/api/media", "icon": "image"},
244
+ {"text": "Users", "url": "#/docs/api/users", "icon": "users"},
245
+ {"text": "Plugins", "url": "#/docs/api/plugins", "icon": "package"},
246
+ {"text": "Collections", "url": "#/docs/api/collections", "icon": "database"},
247
+ {"text": "Views API", "url": "#/docs/api/views", "icon": "eye"},
248
+ {"text": "Actions API", "url": "#/docs/api/actions", "icon": "zap"}
249
+ ]
180
250
  }
181
251
  ]
182
252
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-cms",
3
- "version": "0.25.15",
3
+ "version": "0.30.0",
4
4
  "description": "File-based CMS powered by Domma and Fastify. Run npx domma-cms my-site to create a new project.",
5
5
  "type": "module",
6
6
  "main": "server/server.js",
package/public/js/site.js CHANGED
@@ -1 +1 @@
1
- import{makeBadgeEl as _,applyColour as A,applyPill as I}from"/public/js/menu-decor.mjs";$(()=>{const C=window.__CMS_NAV__||{},g=window.__CMS_SITE__||{};if(g.autoTheme?.enabled){let o=function(e){const f=(e||"07:00").split(":");return+f[0]*60+(+f[1]||0)},a=function(){const e=new Date,f=e.getHours()*60+e.getMinutes();return f>=o(t.dayStart)&&f<o(t.nightStart)?t.dayTheme:t.nightTheme};const t=g.autoTheme;Domma.theme.set(a()),setInterval(()=>Domma.theme.set(a()),6e4)}const b=$("#site-navbar");if(b.length&&C.brand){let a=function(i,n=0){return Array.isArray(i)?i.filter(c=>!(c&&c.type==="separator"&&n===0)).map(c=>c&&c.type==="separator"?{divider:!0}:Array.isArray(c?.items)?{...c,items:a(c.items,n+1)}:c):i},e=function(i){return Array.isArray(i)?i.filter(n=>!(n&&n.type==="separator")):[]};const t={...C.brand},o=t.size&&t.size!=="md"?` navbar-brand-${t.size}`:"";if(t.logo||t.icon||t.tagline){let i="";t.logo?i+=`<img src="${t.logo}" class="navbar-brand-logo" alt="${t.text||""}">`:t.icon&&(i+=`<span data-icon="${t.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),t.text&&(i+=`<span class="navbar-brand-text${o}">${t.text}</span>`),t.tagline&&(i+=`<small class="navbar-brand-tagline">${t.tagline}</small>`),t.html=i}else o&&t.text&&(t.html=`<span class="navbar-brand-text${o}">${t.text}</span>`);Domma.elements.navbar("#site-navbar",{brand:t,items:a(C.items||[]),variant:C.variant||"dark",position:C.position||"sticky",collapsible:!0}),Domma.icons.scan("#site-navbar");const f=b.get(0);if(C.appearOnHover){const i=window.matchMedia("(min-width: 993px)");f.querySelectorAll(".navbar-dropdown").forEach(n=>{n.addEventListener("mouseenter",()=>{i.matches&&n.classList.add("open")}),n.addEventListener("mouseleave",()=>{i.matches&&n.classList.remove("open")})})}const d=f.querySelector(".navbar-toggle"),l=f.querySelector(".navbar-collapse");if(d&&l){let i=null;const n=()=>{l.classList.contains("show")&&d.click()},c=()=>{i&&(i.remove(),i=null),document.body.classList.remove("dm-drawer-open")},s=()=>{i||(i=document.createElement("div"),i.className="dm-navbar-backdrop",i.addEventListener("click",n),document.body.appendChild(i),document.body.classList.add("dm-drawer-open"))};d.addEventListener("click",()=>{requestAnimationFrame(()=>{l.classList.contains("show")?s():c()})}),l.addEventListener("click",u=>{u.target.closest("a")&&requestAnimationFrame(c)}),document.addEventListener("keydown",u=>{u.key==="Escape"&&l.classList.contains("show")&&n()})}const y=e(C.items||[]);f.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(i=>{const n=y[parseInt(i.dataset.index,10)];if(!n)return;const c=i.dataset.subindex!=null,s=c&&Array.isArray(n.items)?n.items[parseInt(i.dataset.subindex,10)]:n;if(!s)return;s.colour&&A(i,s.colour),s.pill&&!c&&I(i,s.pill);const u=_(s.badge);u&&i.appendChild(u)})}const v=$("#site-footer");if(v.length){let f=function(r){return String(r??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},d=function(r){return f(r).replace(/'/g,"&#39;")},l=function(r){if(r&&r.type==="separator")return'<li class="footer-sep"><hr></li>';const m=d(r.url||"#"),h=f(r.text||""),k=Array.isArray(r.items)&&r.items.length?`<ul>${r.items.map(l).join("")}</ul>`:"";return`<li><a href="${m}">${h}</a>${k}</li>`},y=function(r,m){return(r||[]).forEach(h=>{h&&h.type==="separator"||(m.push(h),Array.isArray(h.items)&&h.items.length&&y(h.items,m))}),m};const t=g.social||{},o={twitter:{label:"X / Twitter",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.742l7.73-8.835L1.254 2.25H8.08l4.259 5.629L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'},facebook:{label:"Facebook",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'},instagram:{label:"Instagram",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>'},linkedin:{label:"LinkedIn",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'},github:{label:"GitHub",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'},youtube:{label:"YouTube",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z"/></svg>'}};let a='<div class="footer-inner container">';const e=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(e.copyright&&(a+=`<p>${f(e.copyright)}</p>`),e.primary.length&&(a+=`<nav class="footer-nav footer-nav--primary"><ul>${e.primary.map(l).join("")}</ul></nav>`),e.legal.length&&(a+=`<nav class="footer-nav footer-nav--legal"><ul>${e.legal.map(l).join("")}</ul></nav>`),g.footer){const r=Object.keys(o).filter(m=>t[m]);r.length&&(a+='<div class="footer-social">',r.forEach(m=>{const{label:h,svg:k}=o[m];a+=`<a href="${t[m]}" target="_blank" rel="noopener noreferrer" aria-label="${h}" class="footer-social-link">${k}</a>`}),a+="</div>")}a+="</div>",v.html(a);const i=[...y(e.primary,[]),...y(e.legal,[])];v.get(0).querySelectorAll(".footer-nav a").forEach((r,m)=>{const h=i[m];if(!h)return;h.colour&&A(r,h.colour);const k=_(h.badge);k&&r.appendChild(k)});const c=S.get("reduced_motion"),s=c!==null?!!c:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),u=v.get(0).querySelector(".footer-inner");if(u){const r=document.createElement("input");r.type="checkbox",r.className="form-switch-input",r.id="dm-motion-switch",r.checked=s,r.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const m=document.createElement("span");m.className="form-switch-label",m.textContent="Reduce motion";const h=document.createElement("label");h.className="form-switch footer-motion-switch",h.title="Reduce motion",h.appendChild(r),h.appendChild(m),u.appendChild(h)}}$("#site-sidebar").length&&Domma.elements.sidebar("#site-sidebar",{autoGenerate:!0,selector:"h2, h3",collapsible:!1,push:!0,contentSelector:".site-content"}),Domma.icons.scan();const p=$(".page-body");if(p.length){p.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),p.find(".tabs").each(function(){Domma.elements.tabs(this)}),p.find(".carousel").each(function(){const t={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 o=parseInt(this.dataset.animationDuration,10);Number.isFinite(o)&&(t.animationDuration=o)}this.dataset.animationEasing&&(t.animationEasing=this.dataset.animationEasing),Domma.elements.carousel(this,t)}),p.find(".dm-countdown").each(function(){const t={autoStart:!0};this.dataset.to&&(t.targetDate=new Date(this.dataset.to)),this.dataset.duration&&(t.duration=parseInt(this.dataset.duration,10)),this.dataset.format&&(t.format=this.dataset.format),Domma.elements.timer(this,t)}),p.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),p.find(".dm-progression").each(function(){Domma.elements.progression(this,{layout:this.dataset.layout||"vertical",theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0})});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(t=>{const o=t.dataset.revealMode||"stagger",a=t.dataset.revealAnimation||"slide-up",e=parseInt(t.dataset.revealDuration,10)||400,f=parseInt(t.dataset.revealStagger,10)||60,d=parseInt(t.dataset.revealDelay,10)||0,l=t.dataset.revealDirection==="rtl",y=Array.from(t.children),i={"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((n,c)=>{n.style.opacity="0",n.style.transform=i[a]||"",n.style.transition=`opacity ${e}ms ease, transform ${e}ms ease`;const s=l?y.length-1-c:c;n.style.transitionDelay=o==="stagger"?`${d+s*f}ms`:`${d}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const n=new IntersectionObserver(c=>{c.forEach(s=>{s.isIntersecting&&(s.target.offsetWidth,s.target.style.opacity="1",s.target.style.transform="none",n.unobserve(s.target))})},{threshold:.1});y.forEach(c=>n.observe(c))}))}),p.find(".card[data-collapsible]").each(function(){const t=this.querySelector(".card-header");t&&t.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),p.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const t=this.dataset.soTarget,o=document.getElementById(t);if(!o)return;const a=E.slideover({title:o.dataset.soTitle||"",size:o.dataset.soSize||"md",position:o.dataset.soPosition||"right"});o.style.display="",a.setContent(o),a.open()})})}if(typeof $.setup=="function"){const t=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(o=>{try{const a=atob(o.dataset.config),e=JSON.parse(a);Object.assign(t,e)}catch{}}),Object.keys(t).length>0){const o={};for(const[a,e]of Object.entries(t)){const f=e?.events?.click,{confirm:d,toast:l,alert:y,prompt:i,...n}=f||{};d||l||y||i?($(a).on("click",async function(s){if(s.preventDefault(),d&&!await E.confirm(d))return;let u=null;if(!(i&&(u=await E.prompt(i,{inputPlaceholder:n.promptPlaceholder||"",inputValue:n.promptDefault||""}),u===null))){if(n.target){const r=$(n.target);n.toggleClass&&r.toggleClass(n.toggleClass),n.addClass&&r.addClass(n.addClass),n.removeClass&&r.removeClass(n.removeClass),u!==null&&(n.setText&&r.text(u),n.setVal&&r.val(u),n.setAttr&&r.attr(n.setAttr,u))}n.href&&(window.location.href=n.href),l&&E.toast(l,{type:n.toastType||"success"}),y&&E.alert(y)}}),Object.keys(n).length&&(o[a]={...e,events:{...e.events,click:n}})):o[a]=e}$.setup(o)}}p.length&&x(p.get(0))});function x(C){C.querySelectorAll(".dm-cta-trigger").forEach(g=>{g.__ctaWired||(g.__ctaWired=!0,g.addEventListener("click",async()=>{const b=g.dataset.action,v=g.dataset.entry,w=g.dataset.confirm;let p=S.get("auth_token");if(!p){E.toast("Please log in to perform this action.",{type:"warning"});return}if(w&&!await E.confirm(w))return;const t=Array.from(g.childNodes).map(a=>a.cloneNode(!0));g.disabled=!0,g.textContent="Running\u2026";const o=a=>fetch(`/api/actions/${b}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify({entryId:v})});try{let a=await o(p);if(a.status===401){const f=S.get("auth_refresh_token");if(f){const d=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:f})});if(d.ok){const{token:l}=await d.json();S.set("auth_token",l),p=l,a=await o(p)}}}const e=await a.json().catch(()=>({}));if(!a.ok)throw new Error(e.error||e.message||`Error ${a.status}`);E.toast(e.message||"Action completed.",{type:"success"})}catch(a){E.toast(a.message||"Action failed.",{type:"error"})}finally{g.disabled=!1,g.textContent="",t.forEach(a=>g.appendChild(a)),Domma.icons.scan(g)}}))})}(function(){const g=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!g.length)return;const b=S.get("auth_token");function v(p,t){const o=document.createElement("div");o.className="dm-collection-display dm-collection-empty";const a=document.createElement("p");if(t){a.appendChild(document.createTextNode("Please "));const e=document.createElement("a");e.href="/login",e.textContent="sign in",a.appendChild(e),a.appendChild(document.createTextNode(" to view your entries."))}else a.textContent=p;return o.appendChild(a),o}function w(p,t){const o=document.createElement("template");o.innerHTML=t||"",p.replaceWith(o.content)}g.forEach(async p=>{const t=p.dataset.collectionAttrs;if(!t)return;if(!b){p.replaceChildren(v("",!0));return}const o=a=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify({attrs:t})});try{let a=b,e=await o(a);if(e.status===401){const d=S.get("auth_refresh_token");if(d){const l=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:d})});if(l.ok){const{token:y}=await l.json();S.set("auth_token",y),a=y,e=await o(a)}}}if(!e.ok){const{error:d}=await e.json().catch(()=>({}));p.replaceChildren(v(d||"Unable to load entries.",!1));return}const{html:f}=await e.json();w(p,f)}catch{p.replaceChildren(v("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),x(document.body)},100)})(),(function(){const g=document.querySelectorAll("[data-collection-table]");!g.length||typeof T>"u"||!T.create||g.forEach(b=>{let v;try{v=JSON.parse(atob(b.dataset.payload))}catch{return}const{columns:w,rows:p,search:t,sortable:o,exportable:a,pageSize:e,empty:f,ctaConfig:d}=v;if(!w?.length)return;if(d){const i=w.findIndex(n=>n.key==="_cta");i!==-1&&(w[i]={key:"_cta",title:"",render:(n,c)=>{const s=document.createElement("button");if(s.className=`btn btn-${d.style||"primary"} dm-cta-trigger`,s.dataset.action=d.action||"",s.dataset.entry=c._entryId||"",d.confirm&&(s.dataset.confirm=d.confirm),d.icon){const u=document.createElement("span");u.dataset.icon=d.icon,s.appendChild(u),s.appendChild(document.createTextNode(" "))}return s.appendChild(document.createTextNode(d.label||"Run")),s}})}const l="col-table-"+Math.random().toString(36).slice(2,7),y=document.createElement("div");y.id=l,b.replaceChildren(y),T.create("#"+l,{data:p,columns:w,search:t,sortable:o,exportable:a,pageSize:e,emptyMessage:f}),d&&x(y)})})(),(function(){const g=document.querySelectorAll("[data-form-inline]");if(!g.length||typeof F>"u")return;function b(t,o){const a={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const f=e.type==="checkbox"?"boolean":e.type==="date"?"string":e.type,d={...e.formConfig||{}};d.span==="full"&&o&&(d.span=o);const l={type:f,label:e.label,required:e.required,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{hint:e.helper},...d}};e.type==="chooser"&&(e.variant&&(l.variant=e.variant),e.multiple&&(l.multiple=!0),e.density&&(l.density=e.density),e.columns&&(l.columns=e.columns),e.accent&&(l.accent=e.accent),e.accentStyle&&(l.accentStyle=e.accentStyle),e.glow&&(l.glow=!0),e.glowColour&&(l.glowColour=e.glowColour),e.shadow&&(l.shadow=e.shadow),e.shadowColour&&(l.shadowColour=e.shadowColour)),a[e.name]=l}),a}function v(t){const o={};return(t||[]).forEach(a=>{if(!(!a.name||a.type==="page-break"||a.type==="spacer")&&(a.type==="select"||a.type==="multiselect")&&a.required){const e=(a.options||[])[0];e&&(o[a.name]=typeof e=="object"?e.value:e)}}),o}function w(t,o){(o||[]).forEach(a=>{if(a.type!=="date"||!a.name)return;const e=t.querySelector(`[name="${a.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function p(t,o,a){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=a?"color:var(--danger,#f87171);margin-top:.75rem;":"color:var(--success,#4ade80);margin-top:.75rem;"}g.forEach(t=>{let o;try{o=JSON.parse(atob(t.dataset.formInline))}catch{return}const a=o.fields||[],e=o.settings||{},f=e.columns||1,d=e.layout||"stacked",l=a.some(c=>c.type==="page-break"),y=async c=>{try{const s=t.querySelector('[name="website"]'),u=t.querySelector('[name="_t"]'),r=Object.assign({},c);s!==null&&(r._hp=s.value),u!==null&&(r._t=u.value);const m=await H.post(`/api/forms/submit/${o.slug}`,r);if(m&&m.error){p(t,m.error,!0);const h=new Error(m.error);throw h.formSubmitFailed=!0,h}if(m?.redirect){window.location.href=m.redirect;return}for(;t.firstChild;)t.removeChild(t.firstChild);p(t,m?.message||e.successMessage||"Thank you for your submission.",!1)}catch(s){throw s.formSubmitFailed||p(t,s.message||"Submission failed. Please try again.",!0),s}};function i(c){const s=c.querySelector("form");if(!s)return;const u=document.createElement("div");u.className="fb-form-honeypot",u.setAttribute("aria-hidden","true");const r=document.createElement("input");r.name="website",r.type="text",r.tabIndex=-1,r.autocomplete="url",r.placeholder="https://",u.appendChild(r);const m=document.createElement("input");m.name="_t",m.type="hidden",m.value=Date.now(),u.appendChild(m),s.appendChild(u)}function n(){if(!window.FormLogicEngine||!a.some(s=>s.logic))return;const c=new window.FormLogicEngine.FormLogicRuntime(o,t);if(c.init(),t._formLogicRuntime=c,t.parentNode&&typeof MutationObserver<"u"){const s=new MutationObserver(function(u){for(const r of u)for(const m of r.removedNodes)if(m===t||m.nodeType===1&&m.contains&&m.contains(t)){c.destroy(),s.disconnect();return}});s.observe(t.parentNode,{childList:!0,subtree:!1})}}if(l&&F.wizard){const c=[];let s=[],u=o.title||"Step 1",r="";a.forEach(h=>{h.type==="page-break"?(c.push({title:u,description:r,fields:b(s,f)}),s=[],u=h.label||`Step ${c.length+1}`,r=h.description||""):h.type!=="spacer"&&s.push(h)}),c.push({title:u,description:r,fields:b(s,f)});const m=F.wizard(t,{schema:{steps:c},onSubmit:y});Promise.resolve(m).then(function(){w(t,a),e.honeypot!==!1&&i(t),n()})}else if(F.render){const c=F.render(t,b(a,f),v(a),{submitText:e.submitText||"Submit",layout:d,columns:f,onSubmit:y});Promise.resolve(c).then(function(){if(d==="grid"&&e.submitSpan==="full"){const s=t.querySelector(".form-buttons");s&&s.classList.add("col-span-full")}w(t,a),e.honeypot!==!1&&i(t),n()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();
1
+ import{makeBadgeEl as _,applyColour as A,applyPill as D}from"/public/js/menu-decor.mjs";$(()=>{const C=window.__CMS_NAV__||{},g=window.__CMS_SITE__||{};if(g.autoTheme?.enabled){let o=function(e){const f=(e||"07:00").split(":");return+f[0]*60+(+f[1]||0)},a=function(){const e=new Date,f=e.getHours()*60+e.getMinutes();return f>=o(t.dayStart)&&f<o(t.nightStart)?t.dayTheme:t.nightTheme};const t=g.autoTheme;Domma.theme.set(a()),setInterval(()=>Domma.theme.set(a()),6e4)}const b=$("#site-navbar");if(b.length&&C.brand){let a=function(i,n=0){return Array.isArray(i)?i.filter(c=>!(c&&c.type==="separator"&&n===0)).map(c=>c&&c.type==="separator"?{divider:!0}:Array.isArray(c?.items)?{...c,items:a(c.items,n+1)}:c):i},e=function(i){return Array.isArray(i)?i.filter(n=>!(n&&n.type==="separator")):[]};const t={...C.brand},o=t.size&&t.size!=="md"?` navbar-brand-${t.size}`:"";if(t.logo||t.icon||t.tagline){let i="";t.logo?i+=`<img src="${t.logo}" class="navbar-brand-logo" alt="${t.text||""}">`:t.icon&&(i+=`<span data-icon="${t.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),t.text&&(i+=`<span class="navbar-brand-text${o}">${t.text}</span>`),t.tagline&&(i+=`<small class="navbar-brand-tagline">${t.tagline}</small>`),t.html=i}else o&&t.text&&(t.html=`<span class="navbar-brand-text${o}">${t.text}</span>`);Domma.elements.navbar("#site-navbar",{brand:t,items:a(C.items||[]),variant:C.variant||"dark",position:C.position||"sticky",collapsible:!0}),Domma.icons.scan("#site-navbar");const f=b.get(0);if(C.appearOnHover){const i=window.matchMedia("(min-width: 993px)");f.querySelectorAll(".navbar-dropdown").forEach(n=>{n.addEventListener("mouseenter",()=>{i.matches&&n.classList.add("open")}),n.addEventListener("mouseleave",()=>{i.matches&&n.classList.remove("open")})})}const d=f.querySelector(".navbar-toggle"),l=f.querySelector(".navbar-collapse");if(d&&l){let i=null;const n=()=>{l.classList.contains("show")&&d.click()},c=()=>{i&&(i.remove(),i=null),document.body.classList.remove("dm-drawer-open")},s=()=>{i||(i=document.createElement("div"),i.className="dm-navbar-backdrop",i.addEventListener("click",n),document.body.appendChild(i),document.body.classList.add("dm-drawer-open"))};d.addEventListener("click",()=>{requestAnimationFrame(()=>{l.classList.contains("show")?s():c()})}),l.addEventListener("click",u=>{u.target.closest("a")&&requestAnimationFrame(c)}),document.addEventListener("keydown",u=>{u.key==="Escape"&&l.classList.contains("show")&&n()})}const y=e(C.items||[]);f.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(i=>{const n=y[parseInt(i.dataset.index,10)];if(!n)return;const c=i.dataset.subindex!=null,s=c&&Array.isArray(n.items)?n.items[parseInt(i.dataset.subindex,10)]:n;if(!s)return;s.colour&&A(i,s.colour),s.pill&&!c&&D(i,s.pill);const u=_(s.badge);u&&i.appendChild(u)})}const v=$("#site-footer");if(v.length){let f=function(r){return String(r??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},d=function(r){return f(r).replace(/'/g,"&#39;")},l=function(r){if(r&&r.type==="separator")return'<li class="footer-sep"><hr></li>';const m=d(r.url||"#"),h=f(r.text||""),k=Array.isArray(r.items)&&r.items.length?`<ul>${r.items.map(l).join("")}</ul>`:"";return`<li><a href="${m}">${h}</a>${k}</li>`},y=function(r,m){return(r||[]).forEach(h=>{h&&h.type==="separator"||(m.push(h),Array.isArray(h.items)&&h.items.length&&y(h.items,m))}),m};const t=g.social||{},o={twitter:{label:"X / Twitter",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.742l7.73-8.835L1.254 2.25H8.08l4.259 5.629L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'},facebook:{label:"Facebook",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'},instagram:{label:"Instagram",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>'},linkedin:{label:"LinkedIn",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'},github:{label:"GitHub",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'},youtube:{label:"YouTube",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z"/></svg>'}};let a='<div class="footer-inner container">';const e=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(e.copyright&&(a+=`<p>${f(e.copyright)}</p>`),e.primary.length&&(a+=`<nav class="footer-nav footer-nav--primary"><ul>${e.primary.map(l).join("")}</ul></nav>`),e.legal.length&&(a+=`<nav class="footer-nav footer-nav--legal"><ul>${e.legal.map(l).join("")}</ul></nav>`),g.footer){const r=Object.keys(o).filter(m=>t[m]);r.length&&(a+='<div class="footer-social">',r.forEach(m=>{const{label:h,svg:k}=o[m];a+=`<a href="${t[m]}" target="_blank" rel="noopener noreferrer" aria-label="${h}" class="footer-social-link">${k}</a>`}),a+="</div>")}a+="</div>",v.html(a);const i=[...y(e.primary,[]),...y(e.legal,[])];v.get(0).querySelectorAll(".footer-nav a").forEach((r,m)=>{const h=i[m];if(!h)return;h.colour&&A(r,h.colour);const k=_(h.badge);k&&r.appendChild(k)});const c=S.get("reduced_motion"),s=c!==null?!!c:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),u=v.get(0).querySelector(".footer-inner");if(u){const r=document.createElement("input");r.type="checkbox",r.className="form-switch-input",r.id="dm-motion-switch",r.checked=s,r.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const m=document.createElement("span");m.className="form-switch-label",m.textContent="Reduce motion";const h=document.createElement("label");h.className="form-switch footer-motion-switch",h.title="Reduce motion",h.appendChild(r),h.appendChild(m),u.appendChild(h)}}$("#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 p=$(".page-body");if(p.length){p.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),p.find(".tabs").each(function(){Domma.elements.tabs(this)}),p.find(".carousel").each(function(){const t={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 o=parseInt(this.dataset.animationDuration,10);Number.isFinite(o)&&(t.animationDuration=o)}this.dataset.animationEasing&&(t.animationEasing=this.dataset.animationEasing),Domma.elements.carousel(this,t)}),p.find(".dm-countdown").each(function(){const t={autoStart:!0};this.dataset.to&&(t.targetDate=new Date(this.dataset.to)),this.dataset.duration&&(t.duration=parseInt(this.dataset.duration,10)),this.dataset.format&&(t.format=this.dataset.format),Domma.elements.timer(this,t)}),p.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),p.find(".dm-progression").each(function(){Domma.elements.progression(this,{layout:this.dataset.layout||"vertical",theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0})});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(t=>{const o=t.dataset.revealMode||"stagger",a=t.dataset.revealAnimation||"slide-up",e=parseInt(t.dataset.revealDuration,10)||400,f=parseInt(t.dataset.revealStagger,10)||60,d=parseInt(t.dataset.revealDelay,10)||0,l=t.dataset.revealDirection==="rtl",y=Array.from(t.children),i={"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((n,c)=>{n.style.opacity="0",n.style.transform=i[a]||"",n.style.transition=`opacity ${e}ms ease, transform ${e}ms ease`;const s=l?y.length-1-c:c;n.style.transitionDelay=o==="stagger"?`${d+s*f}ms`:`${d}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const n=new IntersectionObserver(c=>{c.forEach(s=>{s.isIntersecting&&(s.target.offsetWidth,s.target.style.opacity="1",s.target.style.transform="none",n.unobserve(s.target))})},{threshold:.1});y.forEach(c=>n.observe(c))}))}),p.find(".card[data-collapsible]").each(function(){const t=this.querySelector(".card-header");t&&t.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),p.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const t=this.dataset.soTarget,o=document.getElementById(t);if(!o)return;const a=E.slideover({title:o.dataset.soTitle||"",size:o.dataset.soSize||"md",position:o.dataset.soPosition||"right"});o.style.display="",a.setContent(o),a.open()})})}if(typeof $.setup=="function"){const t=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(o=>{try{const a=atob(o.dataset.config),e=JSON.parse(a);Object.assign(t,e)}catch{}}),Object.keys(t).length>0){const o={};for(const[a,e]of Object.entries(t)){const f=e?.events?.click,{confirm:d,toast:l,alert:y,prompt:i,...n}=f||{};d||l||y||i?($(a).on("click",async function(s){if(s.preventDefault(),d&&!await E.confirm(d))return;let u=null;if(!(i&&(u=await E.prompt(i,{inputPlaceholder:n.promptPlaceholder||"",inputValue:n.promptDefault||""}),u===null))){if(n.target){const r=$(n.target);n.toggleClass&&r.toggleClass(n.toggleClass),n.addClass&&r.addClass(n.addClass),n.removeClass&&r.removeClass(n.removeClass),u!==null&&(n.setText&&r.text(u),n.setVal&&r.val(u),n.setAttr&&r.attr(n.setAttr,u))}n.href&&(window.location.href=n.href),l&&E.toast(l,{type:n.toastType||"success"}),y&&E.alert(y)}}),Object.keys(n).length&&(o[a]={...e,events:{...e.events,click:n}})):o[a]=e}$.setup(o)}}p.length&&x(p.get(0))});function x(C){C.querySelectorAll(".dm-cta-trigger").forEach(g=>{g.__ctaWired||(g.__ctaWired=!0,g.addEventListener("click",async()=>{const b=g.dataset.action,v=g.dataset.entry,w=g.dataset.confirm;let p=S.get("auth_token");if(!p){E.toast("Please log in to perform this action.",{type:"warning"});return}if(w&&!await E.confirm(w))return;const t=Array.from(g.childNodes).map(a=>a.cloneNode(!0));g.disabled=!0,g.textContent="Running\u2026";const o=a=>fetch(`/api/actions/${b}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify({entryId:v})});try{let a=await o(p);if(a.status===401){const f=S.get("auth_refresh_token");if(f){const d=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:f})});if(d.ok){const{token:l}=await d.json();S.set("auth_token",l),p=l,a=await o(p)}}}const e=await a.json().catch(()=>({}));if(!a.ok)throw new Error(e.error||e.message||`Error ${a.status}`);E.toast(e.message||"Action completed.",{type:"success"})}catch(a){E.toast(a.message||"Action failed.",{type:"error"})}finally{g.disabled=!1,g.textContent="",t.forEach(a=>g.appendChild(a)),Domma.icons.scan(g)}}))})}(function(){const g=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!g.length)return;const b=S.get("auth_token");function v(p,t){const o=document.createElement("div");o.className="dm-collection-display dm-collection-empty";const a=document.createElement("p");if(t){a.appendChild(document.createTextNode("Please "));const e=document.createElement("a");e.href="/login",e.textContent="sign in",a.appendChild(e),a.appendChild(document.createTextNode(" to view your entries."))}else a.textContent=p;return o.appendChild(a),o}function w(p,t){const o=document.createElement("template");o.innerHTML=t||"",p.replaceWith(o.content)}g.forEach(async p=>{const t=p.dataset.collectionAttrs;if(!t)return;if(!b){p.replaceChildren(v("",!0));return}const o=a=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify({attrs:t})});try{let a=b,e=await o(a);if(e.status===401){const d=S.get("auth_refresh_token");if(d){const l=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:d})});if(l.ok){const{token:y}=await l.json();S.set("auth_token",y),a=y,e=await o(a)}}}if(!e.ok){const{error:d}=await e.json().catch(()=>({}));p.replaceChildren(v(d||"Unable to load entries.",!1));return}const{html:f}=await e.json();w(p,f)}catch{p.replaceChildren(v("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),typeof Domma<"u"&&Domma.syntax?.scan&&Domma.syntax.scan(),x(document.body)},100)})(),(function(){const g=document.querySelectorAll("[data-collection-table]");!g.length||typeof T>"u"||!T.create||g.forEach(b=>{let v;try{v=JSON.parse(atob(b.dataset.payload))}catch{return}const{columns:w,rows:p,search:t,sortable:o,exportable:a,pageSize:e,empty:f,ctaConfig:d}=v;if(!w?.length)return;if(d){const i=w.findIndex(n=>n.key==="_cta");i!==-1&&(w[i]={key:"_cta",title:"",render:(n,c)=>{const s=document.createElement("button");if(s.className=`btn btn-${d.style||"primary"} dm-cta-trigger`,s.dataset.action=d.action||"",s.dataset.entry=c._entryId||"",d.confirm&&(s.dataset.confirm=d.confirm),d.icon){const u=document.createElement("span");u.dataset.icon=d.icon,s.appendChild(u),s.appendChild(document.createTextNode(" "))}return s.appendChild(document.createTextNode(d.label||"Run")),s}})}const l="col-table-"+Math.random().toString(36).slice(2,7),y=document.createElement("div");y.id=l,b.replaceChildren(y),T.create("#"+l,{data:p,columns:w,search:t,sortable:o,exportable:a,pageSize:e,emptyMessage:f}),d&&x(y)})})(),(function(){const g=document.querySelectorAll("[data-form-inline]");if(!g.length||typeof F>"u")return;function b(t,o){const a={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const f=e.type==="checkbox"?"boolean":e.type==="date"?"string":e.type,d={...e.formConfig||{}};d.span==="full"&&o&&(d.span=o);const l={type:f,label:e.label,required:e.required,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{hint:e.helper},...d}};e.type==="chooser"&&(e.variant&&(l.variant=e.variant),e.multiple&&(l.multiple=!0),e.density&&(l.density=e.density),e.columns&&(l.columns=e.columns),e.accent&&(l.accent=e.accent),e.accentStyle&&(l.accentStyle=e.accentStyle),e.glow&&(l.glow=!0),e.glowColour&&(l.glowColour=e.glowColour),e.shadow&&(l.shadow=e.shadow),e.shadowColour&&(l.shadowColour=e.shadowColour)),a[e.name]=l}),a}function v(t){const o={};return(t||[]).forEach(a=>{if(!(!a.name||a.type==="page-break"||a.type==="spacer")&&(a.type==="select"||a.type==="multiselect")&&a.required){const e=(a.options||[])[0];e&&(o[a.name]=typeof e=="object"?e.value:e)}}),o}function w(t,o){(o||[]).forEach(a=>{if(a.type!=="date"||!a.name)return;const e=t.querySelector(`[name="${a.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function p(t,o,a){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=a?"color:var(--danger,#f87171);margin-top:.75rem;":"color:var(--success,#4ade80);margin-top:.75rem;"}g.forEach(t=>{let o;try{o=JSON.parse(atob(t.dataset.formInline))}catch{return}const a=o.fields||[],e=o.settings||{},f=e.columns||1,d=e.layout||"stacked",l=a.some(c=>c.type==="page-break"),y=async c=>{try{const s=t.querySelector('[name="website"]'),u=t.querySelector('[name="_t"]'),r=Object.assign({},c);s!==null&&(r._hp=s.value),u!==null&&(r._t=u.value);const m=await H.post(`/api/forms/submit/${o.slug}`,r);if(m&&m.error){p(t,m.error,!0);const h=new Error(m.error);throw h.formSubmitFailed=!0,h}if(m?.redirect){window.location.href=m.redirect;return}for(;t.firstChild;)t.removeChild(t.firstChild);p(t,m?.message||e.successMessage||"Thank you for your submission.",!1)}catch(s){throw s.formSubmitFailed||p(t,s.message||"Submission failed. Please try again.",!0),s}};function i(c){const s=c.querySelector("form");if(!s)return;const u=document.createElement("div");u.className="fb-form-honeypot",u.setAttribute("aria-hidden","true");const r=document.createElement("input");r.name="website",r.type="text",r.tabIndex=-1,r.autocomplete="url",r.placeholder="https://",u.appendChild(r);const m=document.createElement("input");m.name="_t",m.type="hidden",m.value=Date.now(),u.appendChild(m),s.appendChild(u)}function n(){if(!window.FormLogicEngine||!a.some(s=>s.logic))return;const c=new window.FormLogicEngine.FormLogicRuntime(o,t);if(c.init(),t._formLogicRuntime=c,t.parentNode&&typeof MutationObserver<"u"){const s=new MutationObserver(function(u){for(const r of u)for(const m of r.removedNodes)if(m===t||m.nodeType===1&&m.contains&&m.contains(t)){c.destroy(),s.disconnect();return}});s.observe(t.parentNode,{childList:!0,subtree:!1})}}if(l&&F.wizard){const c=[];let s=[],u=o.title||"Step 1",r="";a.forEach(h=>{h.type==="page-break"?(c.push({title:u,description:r,fields:b(s,f)}),s=[],u=h.label||`Step ${c.length+1}`,r=h.description||""):h.type!=="spacer"&&s.push(h)}),c.push({title:u,description:r,fields:b(s,f)});const m=F.wizard(t,{schema:{steps:c},onSubmit:y});Promise.resolve(m).then(function(){w(t,a),e.honeypot!==!1&&i(t),n()})}else if(F.render){const c=F.render(t,b(a,f),v(a),{submitText:e.submitText||"Submit",layout:d,columns:f,onSubmit:y});Promise.resolve(c).then(function(){if(d==="grid"&&e.submitSpan==="full"){const s=t.querySelector(".form-buttons");s&&s.classList.add("col-span-full")}w(t,a),e.honeypot!==!1&&i(t),n()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();