domma-cms 1.6.6 → 1.6.8

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.
@@ -1,4 +1,4 @@
1
- import{apiRequest as at}from"../api.js";import{addBannerAction as st}from"../lib/plugin-chrome.js";import{helpPopovers as O}from"../lib/help-popover.js";import{onceClosed as j,openMenuAt as lt,settingsStore as nt,sidebarCount as it,viewLifetime as dt}from"../lib/tool-kit.js";import{arrangeTodos as rt,countTodos as ct,daysUntil as ut,DEFAULT_SETTINGS as H,dueDay as pt,dueLabel as bt,isoDay as k,isOverdue as mt,normaliseSettings as K,PRIORITIES as P,STATUSES as B}from"../lib/todo-arrange.js";const u={"not-started":"Not started","in-progress":"In progress",completed:"Completed",highest:"Highest",high:"High",medium:"Medium",low:"Low",lowest:"Lowest",open:"Open",all:"All",overdue:"Overdue"},U=nt("todo.settings",K),v=(b,d="GET",n)=>at(b,{method:d,...n!==void 0?{body:JSON.stringify(n)}:{}}),vt=`
1
+ import{protectToolEditor as dt,mountToolTrash as rt,toolDraftKey as ct}from"../lib/tool-recovery.js";import{apiRequest as ut}from"../api.js";import{addBannerAction as pt}from"../lib/plugin-chrome.js";import{helpPopovers as j}from"../lib/help-popover.js";import{onceClosed as G,openMenuAt as bt,settingsStore as mt,sidebarCount as vt,viewLifetime as ft}from"../lib/tool-kit.js";import{arrangeTodos as yt,countTodos as ht,daysUntil as kt,DEFAULT_SETTINGS as W,dueDay as gt,dueLabel as Mt,isoDay as A,isOverdue as wt,normaliseSettings as _,PRIORITIES as C,STATUSES as J}from"../lib/todo-arrange.js";const p={"not-started":"Not started","in-progress":"In progress",completed:"Completed",highest:"Highest",high:"High",medium:"Medium",low:"Low",lowest:"Lowest",open:"Open",all:"All",overdue:"Overdue"},V=mt("todo.settings",_),g=(m,d="GET",l)=>ut(m,{method:d,...l!==void 0?{body:JSON.stringify(l)}:{}}),Tt=`
2
2
  <div class="todo-row{{doneClass}}" data-id="{{id}}">
3
3
  <input type="checkbox" class="todo-check" aria-label="Done" data-bind-checked="isDone">
4
4
  <span class="todo-text" title="Click to edit">{{text}}</span>
@@ -6,7 +6,7 @@ import{apiRequest as at}from"../api.js";import{addBannerAction as st}from"../lib
6
6
  <button type="button" class="todo-pill" data-kind="status" data-value="{{status}}" title="Change status">{{statusLabel}}</button>
7
7
  <button type="button" class="todo-pill" data-kind="priority" data-value="{{priority}}" title="Change priority">{{priorityLabel}}</button>
8
8
  <button type="button" class="todo-kebab" aria-label="Task actions" title="Actions">\u22EE</button>
9
- </div>`;function z(b){const d=document.createElement("template");return d.innerHTML=b.trim(),d.content.firstElementChild}const p=dt(/^#\/todo\b/);export const todoView={templateUrl:"/admin/js/templates/todo.html",async onMount(b){p.reset();const d=b.get(0),n=M.observable(U.read()),l=M.observable([]),m=M.observable(!0),g=M.observable(""),w=M.observable(n.peek().defaultFilter),f=M.observable(""),T=M.observable(n.peek().defaultPriority),x=M.observable(""),A=M.observable(!1),N=M.observable(!1),S=M.computed(()=>ct(l.value)),G=M.computed(()=>["open","all","not-started","in-progress","completed","overdue"].map(e=>({value:e,label:`${u[e]} (${S.value[e]})`}))),y=M.observable([]);p.add(M.effect(()=>{const e=n.value.relativeDue;y.value=rt(l.value,{filter:w.value,query:g.value,sort:n.value.sort}).map(t=>{const o=ut(t.dueAt),s=mt(t);return{id:t.id,text:t.text,status:t.status,priority:t.priority,statusLabel:u[t.status]??t.status,priorityLabel:u[t.priority]??t.priority,doneClass:t.status==="completed"?" is-done":"",isDone:t.status==="completed",due:bt(t.dueAt,{relative:e,format:i=>D(i).format("D MMM")}),dueClass:s?" is-overdue":o===0&&t.status!=="completed"?" is-today":"",dueTitle:o===null?"":`Due ${D(pt(t.dueAt)).format("dddd D MMMM YYYY")}`}})}));const W={rows:y,loading:m,query:g,filter:w,filterOptions:G,counts:S,draft:f,draftPriority:T,draftDue:x,addFocus:A,searchFocus:N,cannotAdd:M.computed(()=>!f.value.trim()),isEmpty:M.computed(()=>!m.value&&y.value.length===0),emptyText:M.computed(()=>l.value.length?w.value==="open"&&!g.value.trim()?"Nothing open - everything is done.":"No tasks match. Change the filter or clear the search.":"No tasks yet. Add one above."),countText:M.computed(()=>{if(m.value)return"";const e=y.value.length,t=l.value.length;return e===t?`${t} task${t===1?"":"s"}`:`${e} of ${t}`})};p.add(M.effect(()=>U.write(n.value))),p.add(M.effect(()=>{l.value,m.peek()||it("#/todo")}));const _=(e,t)=>{n.value=K({...n.peek(),[e]:t})},C=e=>l.peek().find(t=>t.id===e),J=e=>{l.value=l.peek().map(t=>t.id===e.id?e:t)};async function V(){m.value=!0;try{const e=await v("/todo/items");l.value=Array.isArray(e)?e:e.data??[]}catch{E.toast("Failed to load tasks",{type:"error"})}finally{m.value=!1}}async function c(e,t,o){try{return J(await v(`/todo/items/${e}`,"PUT",t)),o&&E.toast(o,{type:"success"}),!0}catch{return E.toast("Failed to update task",{type:"error"}),!1}}async function Q(){const e=f.peek().trim();if(e)try{const t=await v("/todo/items","POST",{text:e,priority:T.peek(),status:"not-started",dueAt:x.peek()||null});l.value=[...l.peek(),t],f.value="",x.value="",T.value=n.peek().defaultPriority,A.value=!0}catch{E.toast("Failed to add task",{type:"error"})}}const X=()=>(Q(),!1);async function q(e){if(n.peek().confirmDelete&&!await E.confirm("Delete this task?"))return!1;try{return await v(`/todo/items/${e}`,"DELETE"),l.value=l.peek().filter(t=>t.id!==e),E.toast("Task deleted",{type:"success"}),!0}catch{return E.toast("Failed to delete task",{type:"error"}),!1}}async function Z(){const e=S.peek().completed;if(!(!e||!await E.confirm(`Delete ${e} completed task${e===1?"":"s"}?`)))try{await v("/todo/completed","DELETE"),l.value=l.peek().filter(t=>t.status!=="completed"),E.toast("Completed tasks cleared",{type:"success"})}catch{E.toast("Failed to clear completed tasks",{type:"error"})}}const F=e=>{const t=C(e);if(!t)return;const o=t.status==="completed"?"not-started":"completed";c(e,{status:o},o==="completed"?"Task completed":"Task reopened")};function Y(e){const t=C(e);if(!t)return;const o=E.slideover({title:"Edit task",size:"md",position:"right"}),s=z(`
9
+ </div>`;function Q(m){const d=document.createElement("template");return d.innerHTML=m.trim(),d.content.firstElementChild}const r=ft(/^#\/todo\b/);export const todoView={templateUrl:"/admin/js/templates/todo.html",async onMount(m){r.reset();const d=m.get(0),l=M.observable(V.read()),n=M.observable([]),v=M.observable(!0),w=M.observable(""),L=M.observable(""),$=M.observable(l.peek().defaultFilter),f=M.observable(""),y=M.observable(l.peek().defaultPriority),h=M.observable(""),O=M.observable(!1),R=M.observable(!1),k=ct("todo","quick-add");try{const t=k&&S.get(k);t?.text&&(f.value=t.text,y.value=C.includes(t.priority)?t.priority:l.peek().defaultPriority,h.value=t.due||"")}catch{}r.add(M.effect(()=>{const t={text:f.value,priority:y.value,due:h.value};try{k&&(t.text?S.set(k,t):S.remove(k))}catch{}}));const P=M.computed(()=>ht(n.value)),X=M.computed(()=>["open","all","not-started","in-progress","completed","overdue"].map(t=>({value:t,label:`${p[t]} (${P.value[t]})`}))),T=M.observable([]);r.add(M.effect(()=>{const t=l.value.relativeDue;T.value=yt(n.value,{filter:$.value,query:L.value,sort:l.value.sort}).map(e=>{const o=kt(e.dueAt),s=wt(e);return{id:e.id,text:e.text,status:e.status,priority:e.priority,statusLabel:p[e.status]??e.status,priorityLabel:p[e.priority]??e.priority,doneClass:e.status==="completed"?" is-done":"",isDone:e.status==="completed",due:Mt(e.dueAt,{relative:t,format:i=>D(i).format("D MMM")}),dueClass:s?" is-overdue":o===0&&e.status!=="completed"?" is-today":"",dueTitle:o===null?"":`Due ${D(gt(e.dueAt)).format("dddd D MMMM YYYY")}`}})}));const Z={rows:T,loading:v,loadError:w,query:L,filter:$,filterOptions:X,counts:P,draft:f,draftPriority:y,draftDue:h,addFocus:O,searchFocus:R,cannotAdd:M.computed(()=>!f.value.trim()),isEmpty:M.computed(()=>!v.value&&!w.value&&T.value.length===0),emptyText:M.computed(()=>n.value.length?$.value==="open"&&!L.value.trim()?"Nothing open - everything is done.":"No tasks match. Change the filter or clear the search.":"No tasks yet. Add one above."),countText:M.computed(()=>{if(v.value)return"";const t=T.value.length,e=n.value.length;return t===e?`${e} task${e===1?"":"s"}`:`${t} of ${e}`})};r.add(M.effect(()=>V.write(l.value))),r.add(M.effect(()=>{n.value,v.peek()||vt("#/todo")}));const tt=(t,e)=>{l.value=_({...l.peek(),[t]:e})},q=t=>n.peek().find(e=>e.id===t),et=t=>{n.value=n.peek().map(e=>e.id===t.id?t:e)};async function N(){v.value=!0;try{const t=await g("/todo/items");n.value=Array.isArray(t)?t:t.data??[],w.value=""}catch{w.value="Could not load this list. Your items have not been removed.",E.toast("Failed to load tasks",{type:"error"})}finally{v.value=!1}}async function u(t,e,o){try{return et(await g(`/todo/items/${t}`,"PUT",e)),o&&E.toast(o,{type:"success"}),!0}catch{return E.toast("Failed to update task",{type:"error"}),!1}}async function ot(){const t=f.peek().trim();if(t)try{const e=await g("/todo/items","POST",{text:t,priority:y.peek(),status:"not-started",dueAt:h.peek()||null});n.value=[...n.peek(),e],f.value="",h.value="",y.value=l.peek().defaultPriority,O.value=!0}catch{E.toast("Failed to add task",{type:"error"})}}const at=()=>(ot(),!1);async function H(t){if(l.peek().confirmDelete&&!await E.confirm("Move this task to Trash?"))return!1;try{return await g(`/todo/items/${t}`,"DELETE"),n.value=n.peek().filter(e=>e.id!==t),E.toast("Task moved to Trash",{type:"success"}),!0}catch{return E.toast("Failed to delete task",{type:"error"}),!1}}async function st(){const t=P.peek().completed;if(!(!t||!await E.confirm(`Delete ${t} completed task${t===1?"":"s"}?`)))try{await g("/todo/completed","DELETE"),n.value=n.peek().filter(e=>e.status!=="completed"),E.toast("Completed tasks moved to Trash",{type:"success"})}catch{E.toast("Failed to clear completed tasks",{type:"error"})}}const B=t=>{const e=q(t);if(!e)return;const o=e.status==="completed"?"not-started":"completed";u(t,{status:o},o==="completed"?"Task completed":"Task reopened")};function U(t){const e=q(t);if(!e)return;const o=E.slideover({title:"Edit task",size:"md",position:"right"}),s=Q(`
10
10
  <div class="todo-panel">
11
11
  <div>
12
12
  <label class="form-label" for="todo-edit-text">Task</label>
@@ -16,13 +16,13 @@ import{apiRequest as at}from"../api.js";import{addBannerAction as st}from"../lib
16
16
  <div>
17
17
  <label class="form-label" for="todo-edit-status">Status</label>
18
18
  <select id="todo-edit-status" class="form-input" data-model="status.value">
19
- ${B.map(a=>`<option value="${a}">${u[a]}</option>`).join("")}
19
+ ${J.map(a=>`<option value="${a}">${p[a]}</option>`).join("")}
20
20
  </select>
21
21
  </div>
22
22
  <div>
23
23
  <label class="form-label" for="todo-edit-priority">Priority</label>
24
24
  <select id="todo-edit-priority" class="form-input" data-model="priority.value">
25
- ${P.map(a=>`<option value="${a}">${u[a]}</option>`).join("")}
25
+ ${C.map(a=>`<option value="${a}">${p[a]}</option>`).join("")}
26
26
  </select>
27
27
  </div>
28
28
  </div>
@@ -48,14 +48,14 @@ import{apiRequest as at}from"../api.js";import{addBannerAction as st}from"../lib
48
48
  <button type="button" class="btn btn-sm btn-primary" data-on-click="save"
49
49
  data-bind-disabled="cannotSave.value">Save</button>
50
50
  </div>
51
- </div>`),i={text:M.observable(t.text||""),status:M.observable(t.status),priority:M.observable(t.priority),due:M.observable(t.dueAt?String(t.dueAt).slice(0,10):""),created:t.createdAt?D(t.createdAt).format("D MMM YYYY"):"earlier"};i.cannotSave=M.computed(()=>!i.text.value.trim());const h=async()=>{await c(t.id,{text:i.text.peek().trim(),status:i.status.peek(),priority:i.priority.peek(),dueAt:i.due.peek()||null},"Task saved")&&o.close()};o.element.appendChild(s);const $=O(s),r=M.applyBindings(i,s,{methods:{save:h,cancel:()=>o.close(),del:async()=>{await q(t.id)&&o.close()},dueIn:a=>{i.due.value=k(Number(a.currentTarget.dataset.days))},clearDue:()=>{i.due.value=""}}});s.addEventListener("keydown",a=>{(a.ctrlKey||a.metaKey)&&a.key==="Enter"&&(a.preventDefault(),h())}),j(o,()=>{r.dispose(),$()}),o.open(),setTimeout(()=>s.querySelector("#todo-edit-text")?.focus(),60)}function tt(){const e=E.slideover({title:"Todo settings",size:"sm",position:"right"}),t=r=>r.map(a=>`<option value="${a}">${u[a]}</option>`).join(""),o=z(`
51
+ </div>`),i={text:M.observable(e.text||""),status:M.observable(e.status),priority:M.observable(e.priority),due:M.observable(e.dueAt?String(e.dueAt).slice(0,10):""),created:e.createdAt?D(e.createdAt).format("D MMM YYYY"):"earlier"};i.cannotSave=M.computed(()=>!i.text.value.trim());const x=async()=>{await u(e.id,{text:i.text.peek().trim(),status:i.status.peek(),priority:i.priority.peek(),dueAt:i.due.peek()||null},"Task saved")&&(b.saved(),o.close())};o.element.appendChild(s);const Y=j(s),c=M.applyBindings(i,s,{methods:{save:x,cancel:()=>o.close(),del:async()=>{await H(e.id)&&(b.discard(),o.close())},dueIn:a=>{i.due.value=A(Number(a.currentTarget.dataset.days))},clearDue:()=>{i.due.value=""}}});s.addEventListener("keydown",a=>{(a.ctrlKey||a.metaKey)&&a.key==="Enter"&&(a.preventDefault(),x())}),G(o,()=>{c.dispose(),Y()});const b=dt({so:o,panel:s,tool:"todo",id:t,lifetime:r,read:()=>Object.fromEntries(["text","status","priority","due"].map(a=>[a,i[a].peek()])),write:a=>{for(const K of["text","status","priority","due"])K in a&&(i[K].value=a[K])}});o.open(),setTimeout(()=>s.querySelector("#todo-edit-text")?.focus(),60)}function lt(){const t=E.slideover({title:"Todo settings",size:"sm",position:"right"}),e=c=>c.map(b=>`<option value="${b}">${p[b]}</option>`).join(""),o=Q(`
52
52
  <div class="todo-panel">
53
53
  <div>
54
54
  <label class="form-label" for="todo-set-filter">Show on opening
55
55
  <span data-help="Which tasks the list shows when you open Todo. The filter in the toolbar changes it for this visit only."></span>
56
56
  </label>
57
57
  <select id="todo-set-filter" class="form-input" data-model="defaultFilter.value">
58
- ${t(["open","all","not-started","in-progress","completed","overdue"])}
58
+ ${e(["open","all","not-started","in-progress","completed","overdue"])}
59
59
  </select>
60
60
  </div>
61
61
  <div>
@@ -72,7 +72,7 @@ import{apiRequest as at}from"../api.js";import{addBannerAction as st}from"../lib
72
72
  <div>
73
73
  <label class="form-label" for="todo-set-priority">Priority for new tasks</label>
74
74
  <select id="todo-set-priority" class="form-input" data-model="defaultPriority.value">
75
- ${t(P)}
75
+ ${e(C)}
76
76
  </select>
77
77
  </div>
78
78
  <div class="todo-setting-row">
@@ -83,7 +83,7 @@ import{apiRequest as at}from"../api.js";import{addBannerAction as st}from"../lib
83
83
  </div>
84
84
  <div class="todo-setting-row">
85
85
  <label class="form-label" for="todo-set-confirm" style="margin:0">Confirm before deleting
86
- <span data-help="Off deletes straight away - there is no undo."></span>
86
+ <span data-help="Off moves items straight to Trash. You can restore them."></span>
87
87
  </label>
88
88
  <input id="todo-set-confirm" type="checkbox" class="form-switch" data-model="confirmDelete.value">
89
89
  </div>
@@ -93,4 +93,4 @@ import{apiRequest as at}from"../api.js";import{addBannerAction as st}from"../lib
93
93
  <span class="spacer"></span>
94
94
  <button type="button" class="btn btn-sm btn-primary" data-on-click="done">Done</button>
95
95
  </div>
96
- </div>`),s=r=>M.computed({read:()=>n.value[r],write:a=>_(r,a)}),i=Object.fromEntries(Object.keys(H).map(r=>[r,s(r)]));e.element.appendChild(o);const h=O(o),$=M.applyBindings(i,o,{methods:{done:()=>e.close(),reset:()=>{n.value={...H}}}});j(e,()=>{$.dispose(),h()}),e.open()}st(b,{id:"todo-settings",icon:"settings",title:"Todo settings",onClick:tt}),d.querySelector(".todo-rows").innerHTML=vt,p.add(O(d,{position:"bottom"}));const et=M.applyBindings(W,d,{methods:{add:X,clearCompleted:Z}});p.add(()=>et.dispose());const L=d.querySelector(".todo-list"),ot=E.contextMenu(L,{cursor:"glyph",match:".todo-row",inherit:!1,density:"compact",items:e=>{const t=e.dataset.id,o=C(t);return o?[{label:"Edit\u2026",icon:"edit",action:()=>Y(t)},{label:o.status==="completed"?"Reopen":"Complete",icon:"check",action:()=>F(t)},{type:"divider"},{label:"Status",icon:"circle",submenu:B.map(s=>({label:u[s],disabled:o.status===s,action:()=>c(t,{status:s})}))},{label:"Priority",icon:"flag",submenu:P.map(s=>({label:u[s],disabled:o.priority===s,action:()=>c(t,{priority:s})}))},{label:"Due",icon:"calendar",submenu:[{label:"Today",action:()=>c(t,{dueAt:k(0)})},{label:"Tomorrow",action:()=>c(t,{dueAt:k(1)})},{label:"Next week",action:()=>c(t,{dueAt:k(7)})},{type:"divider"},{label:"No due date",disabled:!o.dueAt,action:()=>c(t,{dueAt:null})}]},{type:"divider"},{label:"Delete",icon:"trash",danger:!0,action:()=>q(t)}]:[]}});p.add(()=>ot?.destroy?.()),L.addEventListener("click",e=>{const t=e.target.closest(".todo-row");if(t){if(e.target.closest(".todo-kebab, .todo-pill"))return lt(e.target.closest(".todo-kebab, .todo-pill"));e.target.closest(".todo-text")&&Y(t.dataset.id)}}),L.addEventListener("change",e=>{e.target.matches(".todo-check")&&F(e.target.closest(".todo-row").dataset.id)});const R=e=>{e.ctrlKey||e.metaKey||e.altKey||e.target.closest?.("input, textarea, select, [contenteditable], .dm-slideover")||(e.key==="/"?(e.preventDefault(),N.value=!0):(e.key==="n"||e.key==="N")&&(e.preventDefault(),A.value=!0))};document.addEventListener("keydown",R),p.add(()=>document.removeEventListener("keydown",R)),I.scan(d),await V()}};
96
+ </div>`),s=c=>M.computed({read:()=>l.value[c],write:b=>tt(c,b)}),i=Object.fromEntries(Object.keys(W).map(c=>[c,s(c)]));t.element.appendChild(o);const x=j(o),Y=M.applyBindings(i,o,{methods:{done:()=>t.close(),reset:()=>{l.value={...W}}}});G(t,()=>{Y.dispose(),x()}),t.open()}pt(m,{id:"todo-settings",icon:"settings",title:"Todo settings",onClick:lt}),d.querySelector(".todo-rows").innerHTML=Tt,r.add(j(d,{position:"bottom"}));const nt=M.applyBindings(Z,d,{methods:{retry:N,add:at,clearCompleted:st}});r.add(()=>nt.dispose()),rt({root:d,toolbar:d.querySelector(".todo-bar"),tool:"todo",endpoint:"/todo",itemPath:"/items",label:"Todo",titleOf:t=>t.text,reload:N,lifetime:r});const F=d.querySelector(".todo-list"),it=E.contextMenu(F,{cursor:"glyph",match:".todo-row",inherit:!1,density:"compact",items:t=>{const e=t.dataset.id,o=q(e);return o?[{label:"Edit\u2026",icon:"edit",action:()=>U(e)},{label:o.status==="completed"?"Reopen":"Complete",icon:"check",action:()=>B(e)},{type:"divider"},{label:"Status",icon:"circle",submenu:J.map(s=>({label:p[s],disabled:o.status===s,action:()=>u(e,{status:s})}))},{label:"Priority",icon:"flag",submenu:C.map(s=>({label:p[s],disabled:o.priority===s,action:()=>u(e,{priority:s})}))},{label:"Due",icon:"calendar",submenu:[{label:"Today",action:()=>u(e,{dueAt:A(0)})},{label:"Tomorrow",action:()=>u(e,{dueAt:A(1)})},{label:"Next week",action:()=>u(e,{dueAt:A(7)})},{type:"divider"},{label:"No due date",disabled:!o.dueAt,action:()=>u(e,{dueAt:null})}]},{type:"divider"},{label:"Delete",icon:"trash",danger:!0,action:()=>H(e)}]:[]}});r.add(()=>it?.destroy?.()),F.addEventListener("click",t=>{const e=t.target.closest(".todo-row");if(e){if(t.target.closest(".todo-kebab, .todo-pill"))return bt(t.target.closest(".todo-kebab, .todo-pill"));t.target.closest(".todo-text")&&U(e.dataset.id)}}),F.addEventListener("change",t=>{t.target.matches(".todo-check")&&B(t.target.closest(".todo-row").dataset.id)});const z=t=>{t.ctrlKey||t.metaKey||t.altKey||t.target.closest?.("input, textarea, select, [contenteditable], .dm-slideover")||(t.key==="/"?(t.preventDefault(),R.value=!0):(t.key==="n"||t.key==="N")&&(t.preventDefault(),O.value=!0))};document.addEventListener("keydown",z),r.add(()=>document.removeEventListener("keydown",z)),I.scan(d),await N()}};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-cms",
3
- "version": "1.6.6",
3
+ "version": "1.6.8",
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",
@@ -1,2 +1,2 @@
1
- import{compareValues as mt,sortEntries as H}from"/public/js/collection-sort.mjs";import{serialise as T}from"/public/js/collection-export.mjs";import{parseQuery as ut}from"/public/js/collection-query.mjs";(function(){"use strict";const P=document.createElement("style");P.textContent=["[data-ctx] a, [data-ctx] button, [data-collection-table] a, [data-collection-table] button,","[data-ctx] input, [data-collection-table] input { cursor: pointer; }",'[data-ctx] input[type="text"], [data-collection-table] input[type="text"] { cursor: text; }',".dm-ctx-hidden { display: none !important; }",".dm-ctx-menu { position: fixed; z-index: 10000; min-width: 200px; max-width: 300px; max-height: 60vh; overflow-y: auto;background: var(--dm-surface, #1c1c30); border: 1px solid var(--dm-border, #2a2a40); border-radius: 6px;box-shadow: var(--dm-shadow-lg, 0 8px 24px rgba(0,0,0,.4)); padding: .3rem; font-size: .83rem;color: var(--dm-text, #e5e5e5); }",".dm-ctx-menu button.dm-ctx-row { display: flex; align-items: center; gap: .5rem; width: 100%; text-align: left;background: none; border: none; color: inherit; font: inherit; padding: .35rem .6rem; border-radius: 4px; cursor: pointer; }",".dm-ctx-menu button.dm-ctx-row:hover { background: var(--dm-hover-bg, rgba(128,128,128,.14)); }",".dm-ctx-heading { padding: .3rem .6rem; font-size: .72rem; text-transform: uppercase; letter-spacing: .04em;color: var(--dm-text-muted, #9a9aac); }",".dm-ctx-input { width: 100%; font-size: .8rem; padding: .3rem .5rem; border-radius: 4px;border: 1px solid var(--dm-border, #2a2a40); background: var(--dm-input-bg, rgba(128,128,128,.08)); color: inherit; cursor: text; }",".dm-ctx-chips { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; margin: 0 0 .6rem; }",".dm-ctx-chip { display: inline-flex; align-items: center; gap: .35rem; font-size: .76rem; padding: .2rem .6rem;border-radius: 999px; background: var(--dm-hover-bg, rgba(128,128,128,.14)); border: 1px solid var(--dm-border, #2a2a40); }",".dm-ctx-chip button { background: none; border: none; cursor: pointer; color: inherit; font-size: .9rem; line-height: 1; padding: 0; }",".dm-ctx-empty-note { padding: .75rem; font-size: .85rem; color: var(--dm-text-muted, #9a9aac); }",".dm-ctx-panel-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem;border-bottom: 1px solid var(--dm-border, #2a2a40); margin-bottom: .25rem; }",".dm-ctx-menu button.dm-ctx-back { width: auto; padding: .3rem .5rem; color: var(--dm-text-muted, #9a9aac); }",".dm-ctx-group-heading { font-size: .75rem; text-transform: uppercase; letter-spacing: .04em;color: var(--dm-text-muted, #9a9aac); padding: .5rem 0 .25rem; grid-column: 1 / -1; }"].join(`
2
- `),document.head.appendChild(P);function g(t){if(t._dmTable)return{fields:t._dmTable.columns.filter(e=>e.key!=="_cta").map(e=>({name:e.key,label:e.title||e.key})),entries:t._dmTable.rows.map((e,o)=>({id:String(o),data:e}))};if(t._dmCtxModel)return t._dmCtxModel;try{t._dmCtxModel=JSON.parse(atob(t.dataset.ctx))}catch{t._dmCtxModel=null}return t._dmCtxModel}function $(t,e){if(e.field==="*"){const n=String(e.value).toLowerCase();return Object.values(t.data||{}).some(d=>String(d??"").toLowerCase().includes(n))}const o=String(t.data?.[e.field]??"");switch(e.op){case"eq":return o===String(e.value);case"ne":return o!==String(e.value);case"contains":return o.toLowerCase().includes(String(e.value).toLowerCase());case"gt":case"lt":{const n=mt(o,e.value);return e.op==="gt"?n>0:n<0}default:return!0}}function p(t){const e=t._dmCtxFilters||[],o=g(t);if(!o)return;const n=t._dmCtxSort||null,d=H(o.entries.filter(a=>e.every(r=>$(a,r))),n),l=new Set(d.map(a=>a.id));if(t._dmTable)t._dmTable.render(d.map(a=>a.data));else if(t._dmCtx&&t._dmCtx.instance&&t._dmCtx.instance.setItems)t._dmCtx.instance.setItems(t._dmCtx.items.filter(a=>l.has(String(a._entryId))));else if(t._dmCtxSlides)W(t,l);else{const a=Array.from(t.querySelectorAll("[data-entry-id]"));a.forEach(h=>h.classList.toggle("dm-ctx-hidden",!l.has(h.dataset.entryId)));const r=new Map(a.map(h=>[h.dataset.entryId,h])),u=a.length?a[0].parentNode:null;n&&u&&d.forEach(h=>{const x=r.get(h.id);x&&x.parentNode===u&&u.appendChild(x)}),t.querySelectorAll(".dm-ctx-group-heading").forEach(h=>h.remove());const c=t._dmCtxGroup;if(c&&u){let h=null;d.forEach(x=>{const b=String(x.data?.[c]??"")||"-";if(b===h)return;h=b;const i=document.createElement("div");i.className="dm-ctx-group-heading",i.textContent=b;const s=r.get(x.id);s&&u.insertBefore(i,s)})}}w(t,e),tt(t,d.length,e.length),D()}const A="dmf";function M(){return Array.from(document.querySelectorAll("[data-ctx], [data-collection-table]"))}function U(t){return btoa(unescape(encodeURIComponent(JSON.stringify(t)))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function J(t){const e=t.replace(/-/g,"+").replace(/_/g,"/");return JSON.parse(decodeURIComponent(escape(atob(e))))}function D(){const t={};M().forEach((n,d)=>{const l={};n._dmCtxFilters&&n._dmCtxFilters.length&&(l.f=n._dmCtxFilters),n._dmCtxSort&&(l.s=n._dmCtxSort),n._dmCtxGroup&&(l.g=n._dmCtxGroup),Object.keys(l).length&&(t[d]=l)});const e=(location.hash||"").replace(/^#/,"").split("&").filter(n=>n&&!n.startsWith(A+"="));Object.keys(t).length&&e.push(A+"="+U(t));const o=location.pathname+location.search+(e.length?"#"+e.join("&"):"");try{history.replaceState(null,"",o)}catch{}}function V(){const t=(location.hash||"").match(/(?:^#|&)dmf=([A-Za-z0-9_-]+)/);if(!t)return null;try{return J(t[1])}catch{return null}}function N(t=0){const e=V();if(!e)return;const o=M();let n=!1;for(const[d,l]of Object.entries(e)){const a=o[Number(d)];if(!a||!l)continue;if(a.hasAttribute("data-collection-table")?!a._dmTable:a.classList.contains("dm-progression")?!a._dmCtx:a.classList.contains("carousel")?!a._dmCtxSlides:!1){n=!0;continue}const u=Array.isArray(l)?{f:l}:l||{};a._dmCtxFilters=(u.f||[]).filter(c=>c&&c.field&&c.op!=null&&"value"in c),a._dmCtxSort=u.s||null,a._dmCtxGroup=u.g||null,p(a)}n&&t<20&&setTimeout(()=>N(t+1),100)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>setTimeout(N,0)):setTimeout(N,0);function W(t,e){if(t._dmCarousel&&typeof t._dmCarousel.destroy=="function")try{t._dmCarousel.destroy()}catch{}const o=document.createElement("div");o.className="carousel-track",t._dmCtxSlides.forEach(n=>{e.has(n.dataset.entryId)&&o.appendChild(n.cloneNode(!0))}),t.replaceChildren(o),o.children.length&&typeof Domma<"u"&&Domma.elements?.carousel&&(t._dmCarousel=Domma.elements.carousel(t,t._dmCarouselOpts||{}))}function Y(t,e,o){const n=URL.createObjectURL(new Blob([t],{type:o})),d=document.createElement("a");d.href=n,d.download=e,document.body.appendChild(d),d.click(),d.remove(),setTimeout(()=>URL.revokeObjectURL(n),0)}function _(t,e){const o=document.createElement("textarea");o.value=t,o.style.cssText="position:fixed;opacity:0;",document.body.appendChild(o),o.select();const n=document.execCommand("copy");o.remove(),typeof E<"u"&&E.toast&&E.toast(n?e:"Copy failed",{type:n?"success":"error"})}function X(){return new Date().toISOString().slice(0,10)}let L=null;function Z(){if(!L){const t=typeof S<"u"&&S.get?S.get("auth_token"):null;L=t?fetch("/api/auth/me",{headers:{Authorization:`Bearer ${t}`}}).then(e=>e.ok?e.json():null).catch(()=>null):Promise.resolve(null)}return L}async function K(t){if(!t||!t.allowed)return!1;if(t.access==="public")return!0;const e=await Z();return e?[e.role,...e.additionalRoles||[]].filter(Boolean).some(n=>(t.roles||[]).includes(n)):!1}const Q={eq:"=",ne:"\u2260",contains:"contains",gt:">",lt:"<"};function w(t,e){let o=t._dmCtxBar;if(!e.length){o&&(o.remove(),t._dmCtxBar=null);return}o||(o=document.createElement("div"),o.className="dm-ctx-chips",t.parentNode.insertBefore(o,t),t._dmCtxBar=o),o.replaceChildren(),e.forEach((d,l)=>{const a=document.createElement("span");a.className="dm-ctx-chip";const r=String(d.value),u=r.length>24?r.slice(0,24)+"\u2026":r;a.appendChild(document.createTextNode(d.field==="*"?`search \u201C${u}\u201D`:`${d.label||d.field} ${Q[d.op]||d.op} \u201C${u}\u201D`));const c=document.createElement("button");c.type="button",c.textContent="\xD7",c.title="Remove filter",c.addEventListener("click",()=>{t._dmCtxFilters.splice(l,1),p(t)}),a.appendChild(c),o.appendChild(a)});const n=document.createElement("button");n.type="button",n.className="dm-ctx-chip",n.style.cursor="pointer",n.textContent="Clear all",n.addEventListener("click",()=>{t._dmCtxFilters=[],p(t)}),o.appendChild(n)}function tt(t,e,o){if(t._dmCtxEmptyNote&&(t._dmCtxEmptyNote.remove(),t._dmCtxEmptyNote=null),e||!o)return;const n=document.createElement("div");n.className="dm-ctx-empty-note",n.textContent="No entries match the current filters.",t.appendChild(n),t._dmCtxEmptyNote=n}let C=null;function f(){C&&(C.remove(),C=null,document.removeEventListener("mousedown",O,!0),document.removeEventListener("keydown",j,!0),window.removeEventListener("scroll",G,!0))}const O=t=>{C&&!C.contains(t.target)&&f()},j=t=>{t.key==="Escape"&&f()},G=t=>{C&&!C.contains(t.target)&&f()};function m(t,e){const o=document.createElement("button");return o.type="button",o.className="dm-ctx-row",o.textContent=t,o.addEventListener("click",e),o}function v(t){const e=document.createElement("div");return e.className="dm-ctx-heading",e.textContent=t,e}function I(){const t=document.createElement("div");return t.style.cssText="border-top:1px solid var(--dm-border,#2a2a40);margin:.25rem 0;",t}const y=(t,e=24)=>{const o=String(t);return o.length>e?o.slice(0,e)+"\u2026":o};function R(t){const e=t.getBoundingClientRect();t.style.left=Math.min(t._dmX,window.innerWidth-e.width-8)+"px",t.style.top=Math.min(t._dmY,window.innerHeight-e.height-8)+"px"}function B(t,e,o){if(t._dmPanelToken=(t._dmPanelToken||0)+1,t.replaceChildren(),o){const n=document.createElement("div");n.className="dm-ctx-panel-head";const d=document.createElement("button");d.type="button",d.className="dm-ctx-row dm-ctx-back",d.textContent="\u2039 Back",d.addEventListener("click",()=>t._dmPop()),n.appendChild(d),n.appendChild(v(o)),t.appendChild(n)}e(t),R(t)}function et(t){const e=[];return t._dmPush=(o,n)=>{e.push({build:o,title:n}),B(t,o,n)},t._dmPop=()=>{e.pop();const o=e[e.length-1];o&&B(t,o.build,o.title)},t}function nt(t){return!t._dmTable&&!t._dmCtx?.instance?.setItems&&!t._dmCtxSlides}function ot(t,e){const o=t.entries.map(d=>d.data?.[e]).find(d=>d!==""&&d!=null),n=String(o??"");return n!==""&&!Number.isNaN(parseFloat(n))?["low \u2192 high","high \u2192 low"]:Number.isNaN(Date.parse(n))?["A \u2192 Z","Z \u2192 A"]:["oldest \u2192 newest","newest \u2192 oldest"]}function dt(t,e,o){o.fields.forEach(n=>{const[d,l]=ot(o,n.name);t.appendChild(m(`${n.label||n.name} ${d}`,()=>{e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"asc"},p(e),f()})),t.appendChild(m(`${n.label||n.name} ${l}`,()=>{e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"desc"},p(e),f()}))}),e._dmCtxSort&&t.appendChild(m("Clear sort",()=>{e._dmCtxSort=null,p(e),f()}))}function at(t,e,o){o.fields.forEach(n=>{t.appendChild(m(n.label||n.name,()=>{e._dmCtxGroup=n.name,(!e._dmCtxSort||e._dmCtxSort.field!==n.name)&&(e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"asc"}),p(e),f()}))}),e._dmCtxGroup&&t.appendChild(m("Clear grouping",()=>{e._dmCtxGroup=null,p(e),f()}))}function q(t,e){const o=t._dmCtxFilters||[];return H(e.entries.filter(n=>o.every(d=>$(n,d))),t._dmCtxSort||null).map(n=>n.data)}function it(t,e,o,n){const d=o.slug||e._dmTable?.slug||"collection";n.formats.forEach(l=>{t.appendChild(m(l.toUpperCase(),()=>{const a=T(q(e,o),o.fields,l);Y(a,`${d}-${X()}.${l}`,l==="csv"?"text/csv;charset=utf-8":"application/json"),f()}))})}function lt(t,e,o,n,d){n&&d&&t.appendChild(m("This value",()=>{_(String(n.data?.[d.name]??""),"Value copied"),f()})),n&&t.appendChild(m("This row (JSON)",()=>{_(T([n.data],o.fields,"json"),"Row copied"),f()})),t.appendChild(m("Visible entries as a table",()=>{_(T(q(e,o),o.fields,"markdown"),"Table copied"),f()}))}function rt(t,e,o,n,d){const l=(i,s,k,ct)=>{(e._dmCtxFilters=e._dmCtxFilters||[]).push({field:i,label:s,op:k,value:ct}),p(e),f()};if(n&&o){const i=o.data?.[n.name]??"";t.appendChild(v(n.label||n.name)),t.appendChild(m(`Filter: = \u201C${y(i)}\u201D`,()=>l(n.name,n.label,"eq",i))),t.appendChild(m(`Filter: \u2260 \u201C${y(i)}\u201D`,()=>l(n.name,n.label,"ne",i)));const s=String(i);(s!==""&&!Number.isNaN(parseFloat(s))||!Number.isNaN(Date.parse(s)))&&(t.appendChild(m(`Filter: > \u201C${y(i)}\u201D`,()=>l(n.name,n.label,"gt",i))),t.appendChild(m(`Filter: < \u201C${y(i)}\u201D`,()=>l(n.name,n.label,"lt",i))))}else if(o){t.appendChild(v("Filter by"));for(const i of d.fields){const s=o.data?.[i.name];s==null||String(s)===""||t.appendChild(m(`${i.label||i.name}: \u201C${y(s,20)}\u201D`,()=>l(i.name,i.label,"eq",s)))}}const a=document.createElement("div");a.style.padding=".35rem .6rem";const r=document.createElement("input");r.type="text",r.className="dm-ctx-input",r.placeholder="Search\u2026 or name: value",r.addEventListener("mousedown",i=>i.stopPropagation()),r.addEventListener("keydown",i=>{if(i.key!=="Enter"||!r.value.trim())return;const s=ut(r.value,d.fields);s?l(s.field,s.label,s.op,s.value):l("*","search","contains",r.value.trim())}),a.appendChild(r),t.appendChild(a),t.appendChild(I()),t.appendChild(v("This display")),t.appendChild(m("Sort\u2026",()=>t._dmPush(i=>dt(i,e,d),"Sort"))),nt(e)&&t.appendChild(m("Group by\u2026",()=>t._dmPush(i=>at(i,e,d),"Group by"))),t.appendChild(m("Copy\u2026",()=>t._dmPush(i=>lt(i,e,d,o,n),"Copy")));const u=m("Print",()=>{e.setAttribute("data-dm-print",""),document.body.classList.add("dm-ctx-printing"),f();const i=()=>{e.removeAttribute("data-dm-print"),document.body.classList.remove("dm-ctx-printing"),window.removeEventListener("afterprint",i)};window.addEventListener("afterprint",i),window.print()});t.appendChild(u),t.appendChild(m("Copy link to this view",()=>{D(),_(location.href,"Link copied"),f()}));const c=d.export||e._dmTable?.exportCfg,h=t._dmPanelToken;K(c).then(i=>{!i||C!==t||t._dmPanelToken!==h||(t.insertBefore(m("Export\u2026",()=>t._dmPush(s=>it(s,e,d,c),"Export")),u),R(t))});const x=(e._dmCtxFilters||[]).length;if(x||e._dmCtxSort||e._dmCtxGroup){const i=[];x&&i.push(`${x} filter${x===1?"":"s"}`),e._dmCtxSort&&i.push("sort"),e._dmCtxGroup&&i.push("grouping"),t.appendChild(I()),t.appendChild(m(`Clear all (${i.join(", ")})`,()=>{e._dmCtxFilters=[],e._dmCtxSort=null,e._dmCtxGroup=null,p(e),f()}))}}function z(t,e,o,n,d){f();const l=g(o);if(!l)return;const a=et(document.createElement("div"));a.className="dm-ctx-menu",a._dmX=t,a._dmY=e,C=a,document.body.appendChild(a),a._dmPush(r=>rt(r,o,n,d,l),null),document.addEventListener("mousedown",O,!0),document.addEventListener("keydown",j,!0),window.addEventListener("scroll",G,!0),setTimeout(()=>{const r=a.querySelector(".dm-ctx-input");r&&r.focus()},0)}function F(t,e,o){if(t._dmTable){const r=e.closest("tbody tr");if(!r)return{entry:null,field:null};const u=e.closest("td"),c=r.querySelectorAll("td"),h=Array.from(c).map(i=>i.textContent.trim()),x=o.entries.find(i=>o.fields.every((s,k)=>String(i.data?.[s.name]??"").trim()===(h[k]??"").trim()))||null,b=u&&o.fields[u.cellIndex]||null;return{entry:x,field:b}}const n=e.closest("[data-entry-id]");if(!n)return{entry:null,field:null};const d=o.entries.find(r=>r.id===n.dataset.entryId)||null;if(!d)return{entry:null,field:null};const l=(e.textContent||"").trim().slice(0,80);let a=null;if(l){const r=o.fields.filter(u=>{const c=String(d.data?.[u.name]??"");return c&&(c.includes(l)||l.includes(c))});r.length===1&&(a=r[0])}return{entry:d,field:a}}function st(t){document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t,{once:!0}):t()}st(()=>{if(typeof Domma>"u"||!Domma.elements?.contextMenu){document.addEventListener("contextmenu",t=>{const e=t.target.closest("[data-ctx], [data-collection-table]");if(!e)return;const o=g(e);if(!o||!o.fields?.length)return;t.preventDefault();const{entry:n,field:d}=F(e,t.target,o);z(t.clientX,t.clientY,e,n,d)});return}Domma.elements.contextMenu("[data-ctx], [data-collection-table]",{cursor:"glyph",exclusive:!0,enabled:t=>{const e=g(t);return!!(e&&e.fields?.length)},render:t=>{const e=t.container,o=g(e),{entry:n,field:d}=F(e,t.event.target,o);z(t.x,t.y,e,n,d)}})})})();
1
+ import{decodeBase64Json as H}from"/public/js/utf8-base64.mjs";import{compareValues as ut,sortEntries as U}from"/public/js/collection-sort.mjs";import{serialise as T}from"/public/js/collection-export.mjs";import{parseQuery as ft}from"/public/js/collection-query.mjs";(function(){"use strict";const P=document.createElement("style");P.textContent=["[data-ctx] a, [data-ctx] button, [data-collection-table] a, [data-collection-table] button,","[data-ctx] input, [data-collection-table] input { cursor: pointer; }",'[data-ctx] input[type="text"], [data-collection-table] input[type="text"] { cursor: text; }',".dm-ctx-hidden { display: none !important; }",".dm-ctx-menu { position: fixed; z-index: 10000; min-width: 200px; max-width: 300px; max-height: 60vh; overflow-y: auto;background: var(--dm-surface, #1c1c30); border: 1px solid var(--dm-border, #2a2a40); border-radius: 6px;box-shadow: var(--dm-shadow-lg, 0 8px 24px rgba(0,0,0,.4)); padding: .3rem; font-size: .83rem;color: var(--dm-text, #e5e5e5); }",".dm-ctx-menu button.dm-ctx-row { display: flex; align-items: center; gap: .5rem; width: 100%; text-align: left;background: none; border: none; color: inherit; font: inherit; padding: .35rem .6rem; border-radius: 4px; cursor: pointer; }",".dm-ctx-menu button.dm-ctx-row:hover { background: var(--dm-hover-bg, rgba(128,128,128,.14)); }",".dm-ctx-heading { padding: .3rem .6rem; font-size: .72rem; text-transform: uppercase; letter-spacing: .04em;color: var(--dm-text-muted, #9a9aac); }",".dm-ctx-input { width: 100%; font-size: .8rem; padding: .3rem .5rem; border-radius: 4px;border: 1px solid var(--dm-border, #2a2a40); background: var(--dm-input-bg, rgba(128,128,128,.08)); color: inherit; cursor: text; }",".dm-ctx-chips { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; margin: 0 0 .6rem; }",".dm-ctx-chip { display: inline-flex; align-items: center; gap: .35rem; font-size: .76rem; padding: .2rem .6rem;border-radius: 999px; background: var(--dm-hover-bg, rgba(128,128,128,.14)); border: 1px solid var(--dm-border, #2a2a40); }",".dm-ctx-chip button { background: none; border: none; cursor: pointer; color: inherit; font-size: .9rem; line-height: 1; padding: 0; }",".dm-ctx-empty-note { padding: .75rem; font-size: .85rem; color: var(--dm-text-muted, #9a9aac); }",".dm-ctx-panel-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem;border-bottom: 1px solid var(--dm-border, #2a2a40); margin-bottom: .25rem; }",".dm-ctx-menu button.dm-ctx-back { width: auto; padding: .3rem .5rem; color: var(--dm-text-muted, #9a9aac); }",".dm-ctx-group-heading { font-size: .75rem; text-transform: uppercase; letter-spacing: .04em;color: var(--dm-text-muted, #9a9aac); padding: .5rem 0 .25rem; grid-column: 1 / -1; }"].join(`
2
+ `),document.head.appendChild(P);function g(t){if(t._dmTable)return{fields:t._dmTable.columns.filter(e=>e.key!=="_cta").map(e=>({name:e.key,label:e.title||e.key})),entries:t._dmTable.rows.map((e,o)=>({id:String(o),data:e}))};if(t._dmCtxModel)return t._dmCtxModel;try{t._dmCtxModel=H(t.dataset.ctx)}catch{t._dmCtxModel=null}return t._dmCtxModel}function $(t,e){if(e.field==="*"){const n=String(e.value).toLowerCase();return Object.values(t.data||{}).some(d=>String(d??"").toLowerCase().includes(n))}const o=String(t.data?.[e.field]??"");switch(e.op){case"eq":return o===String(e.value);case"ne":return o!==String(e.value);case"contains":return o.toLowerCase().includes(String(e.value).toLowerCase());case"gt":case"lt":{const n=ut(o,e.value);return e.op==="gt"?n>0:n<0}default:return!0}}function p(t){const e=t._dmCtxFilters||[],o=g(t);if(!o)return;const n=t._dmCtxSort||null,d=U(o.entries.filter(i=>e.every(r=>$(i,r))),n),l=new Set(d.map(i=>i.id));if(t._dmTable)t._dmTable.render(d.map(i=>i.data));else if(t._dmCtx&&t._dmCtx.instance&&t._dmCtx.instance.setItems)t._dmCtx.instance.setItems(t._dmCtx.items.filter(i=>l.has(String(i._entryId))));else if(t._dmCtxSlides)J(t,l);else{const i=Array.from(t.querySelectorAll("[data-entry-id]"));i.forEach(h=>h.classList.toggle("dm-ctx-hidden",!l.has(h.dataset.entryId)));const r=new Map(i.map(h=>[h.dataset.entryId,h])),u=i.length?i[0].parentNode:null;n&&u&&d.forEach(h=>{const x=r.get(h.id);x&&x.parentNode===u&&u.appendChild(x)}),t.querySelectorAll(".dm-ctx-group-heading").forEach(h=>h.remove());const c=t._dmCtxGroup;if(c&&u){let h=null;d.forEach(x=>{const b=String(x.data?.[c]??"")||"-";if(b===h)return;h=b;const a=document.createElement("div");a.className="dm-ctx-group-heading",a.textContent=b;const s=r.get(x.id);s&&u.insertBefore(a,s)})}}tt(t,e),et(t,d.length,e.length),D()}const A="dmf";function M(){return Array.from(document.querySelectorAll("[data-ctx], [data-collection-table]"))}function V(t){return btoa(unescape(encodeURIComponent(JSON.stringify(t)))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function W(t){const e=t.replace(/-/g,"+").replace(/_/g,"/");return H(e)}function D(){const t={};M().forEach((n,d)=>{const l={};n._dmCtxFilters&&n._dmCtxFilters.length&&(l.f=n._dmCtxFilters),n._dmCtxSort&&(l.s=n._dmCtxSort),n._dmCtxGroup&&(l.g=n._dmCtxGroup),Object.keys(l).length&&(t[d]=l)});const e=(location.hash||"").replace(/^#/,"").split("&").filter(n=>n&&!n.startsWith(A+"="));Object.keys(t).length&&e.push(A+"="+V(t));const o=location.pathname+location.search+(e.length?"#"+e.join("&"):"");try{history.replaceState(null,"",o)}catch{}}function Y(){const t=(location.hash||"").match(/(?:^#|&)dmf=([A-Za-z0-9_-]+)/);if(!t)return null;try{return W(t[1])}catch{return null}}function N(t=0){const e=Y();if(!e)return;const o=M();let n=!1;for(const[d,l]of Object.entries(e)){const i=o[Number(d)];if(!i||!l)continue;if(i.hasAttribute("data-collection-table")?!i._dmTable:i.classList.contains("dm-progression")?!i._dmCtx:i.classList.contains("carousel")?!i._dmCtxSlides:!1){n=!0;continue}const u=Array.isArray(l)?{f:l}:l||{};i._dmCtxFilters=(u.f||[]).filter(c=>c&&c.field&&c.op!=null&&"value"in c),i._dmCtxSort=u.s||null,i._dmCtxGroup=u.g||null,p(i)}n&&t<20&&setTimeout(()=>N(t+1),100)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>setTimeout(N,0)):setTimeout(N,0);function J(t,e){if(t._dmCarousel&&typeof t._dmCarousel.destroy=="function")try{t._dmCarousel.destroy()}catch{}const o=document.createElement("div");o.className="carousel-track",t._dmCtxSlides.forEach(n=>{e.has(n.dataset.entryId)&&o.appendChild(n.cloneNode(!0))}),t.replaceChildren(o),o.children.length&&typeof Domma<"u"&&Domma.elements?.carousel&&(t._dmCarousel=Domma.elements.carousel(t,t._dmCarouselOpts||{}))}function X(t,e,o){const n=URL.createObjectURL(new Blob([t],{type:o})),d=document.createElement("a");d.href=n,d.download=e,document.body.appendChild(d),d.click(),d.remove(),setTimeout(()=>URL.revokeObjectURL(n),0)}function _(t,e){const o=document.createElement("textarea");o.value=t,o.style.cssText="position:fixed;opacity:0;",document.body.appendChild(o),o.select();const n=document.execCommand("copy");o.remove(),typeof E<"u"&&E.toast&&E.toast(n?e:"Copy failed",{type:n?"success":"error"})}function Z(){return new Date().toISOString().slice(0,10)}let L=null;function K(){if(!L){const t=typeof S<"u"&&S.get?S.get("auth_token"):null;L=t?fetch("/api/auth/me",{headers:{Authorization:`Bearer ${t}`}}).then(e=>e.ok?e.json():null).catch(()=>null):Promise.resolve(null)}return L}async function Q(t){if(!t||!t.allowed)return!1;if(t.access==="public")return!0;const e=await K();return e?[e.role,...e.additionalRoles||[]].filter(Boolean).some(n=>(t.roles||[]).includes(n)):!1}const w={eq:"=",ne:"\u2260",contains:"contains",gt:">",lt:"<"};function tt(t,e){let o=t._dmCtxBar;if(!e.length){o&&(o.remove(),t._dmCtxBar=null);return}o||(o=document.createElement("div"),o.className="dm-ctx-chips",t.parentNode.insertBefore(o,t),t._dmCtxBar=o),o.replaceChildren(),e.forEach((d,l)=>{const i=document.createElement("span");i.className="dm-ctx-chip";const r=String(d.value),u=r.length>24?r.slice(0,24)+"\u2026":r;i.appendChild(document.createTextNode(d.field==="*"?`search \u201C${u}\u201D`:`${d.label||d.field} ${w[d.op]||d.op} \u201C${u}\u201D`));const c=document.createElement("button");c.type="button",c.textContent="\xD7",c.title="Remove filter",c.addEventListener("click",()=>{t._dmCtxFilters.splice(l,1),p(t)}),i.appendChild(c),o.appendChild(i)});const n=document.createElement("button");n.type="button",n.className="dm-ctx-chip",n.style.cursor="pointer",n.textContent="Clear all",n.addEventListener("click",()=>{t._dmCtxFilters=[],p(t)}),o.appendChild(n)}function et(t,e,o){if(t._dmCtxEmptyNote&&(t._dmCtxEmptyNote.remove(),t._dmCtxEmptyNote=null),e||!o)return;const n=document.createElement("div");n.className="dm-ctx-empty-note",n.textContent="No entries match the current filters.",t.appendChild(n),t._dmCtxEmptyNote=n}let C=null;function f(){C&&(C.remove(),C=null,document.removeEventListener("mousedown",j,!0),document.removeEventListener("keydown",B,!0),window.removeEventListener("scroll",G,!0))}const j=t=>{C&&!C.contains(t.target)&&f()},B=t=>{t.key==="Escape"&&f()},G=t=>{C&&!C.contains(t.target)&&f()};function m(t,e){const o=document.createElement("button");return o.type="button",o.className="dm-ctx-row",o.textContent=t,o.addEventListener("click",e),o}function v(t){const e=document.createElement("div");return e.className="dm-ctx-heading",e.textContent=t,e}function O(){const t=document.createElement("div");return t.style.cssText="border-top:1px solid var(--dm-border,#2a2a40);margin:.25rem 0;",t}const y=(t,e=24)=>{const o=String(t);return o.length>e?o.slice(0,e)+"\u2026":o};function I(t){const e=t.getBoundingClientRect();t.style.left=Math.min(t._dmX,window.innerWidth-e.width-8)+"px",t.style.top=Math.min(t._dmY,window.innerHeight-e.height-8)+"px"}function R(t,e,o){if(t._dmPanelToken=(t._dmPanelToken||0)+1,t.replaceChildren(),o){const n=document.createElement("div");n.className="dm-ctx-panel-head";const d=document.createElement("button");d.type="button",d.className="dm-ctx-row dm-ctx-back",d.textContent="\u2039 Back",d.addEventListener("click",()=>t._dmPop()),n.appendChild(d),n.appendChild(v(o)),t.appendChild(n)}e(t),I(t)}function nt(t){const e=[];return t._dmPush=(o,n)=>{e.push({build:o,title:n}),R(t,o,n)},t._dmPop=()=>{e.pop();const o=e[e.length-1];o&&R(t,o.build,o.title)},t}function ot(t){return!t._dmTable&&!t._dmCtx?.instance?.setItems&&!t._dmCtxSlides}function dt(t,e){const o=t.entries.map(d=>d.data?.[e]).find(d=>d!==""&&d!=null),n=String(o??"");return n!==""&&!Number.isNaN(parseFloat(n))?["low \u2192 high","high \u2192 low"]:Number.isNaN(Date.parse(n))?["A \u2192 Z","Z \u2192 A"]:["oldest \u2192 newest","newest \u2192 oldest"]}function it(t,e,o){o.fields.forEach(n=>{const[d,l]=dt(o,n.name);t.appendChild(m(`${n.label||n.name} ${d}`,()=>{e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"asc"},p(e),f()})),t.appendChild(m(`${n.label||n.name} ${l}`,()=>{e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"desc"},p(e),f()}))}),e._dmCtxSort&&t.appendChild(m("Clear sort",()=>{e._dmCtxSort=null,p(e),f()}))}function at(t,e,o){o.fields.forEach(n=>{t.appendChild(m(n.label||n.name,()=>{e._dmCtxGroup=n.name,(!e._dmCtxSort||e._dmCtxSort.field!==n.name)&&(e._dmCtxSort={field:n.name,label:n.label||n.name,dir:"asc"}),p(e),f()}))}),e._dmCtxGroup&&t.appendChild(m("Clear grouping",()=>{e._dmCtxGroup=null,p(e),f()}))}function q(t,e){const o=t._dmCtxFilters||[];return U(e.entries.filter(n=>o.every(d=>$(n,d))),t._dmCtxSort||null).map(n=>n.data)}function lt(t,e,o,n){const d=o.slug||e._dmTable?.slug||"collection";n.formats.forEach(l=>{t.appendChild(m(l.toUpperCase(),()=>{const i=T(q(e,o),o.fields,l);X(i,`${d}-${Z()}.${l}`,l==="csv"?"text/csv;charset=utf-8":"application/json"),f()}))})}function rt(t,e,o,n,d){n&&d&&t.appendChild(m("This value",()=>{_(String(n.data?.[d.name]??""),"Value copied"),f()})),n&&t.appendChild(m("This row (JSON)",()=>{_(T([n.data],o.fields,"json"),"Row copied"),f()})),t.appendChild(m("Visible entries as a table",()=>{_(T(q(e,o),o.fields,"markdown"),"Table copied"),f()}))}function st(t,e,o,n,d){const l=(a,s,k,mt)=>{(e._dmCtxFilters=e._dmCtxFilters||[]).push({field:a,label:s,op:k,value:mt}),p(e),f()};if(n&&o){const a=o.data?.[n.name]??"";t.appendChild(v(n.label||n.name)),t.appendChild(m(`Filter: = \u201C${y(a)}\u201D`,()=>l(n.name,n.label,"eq",a))),t.appendChild(m(`Filter: \u2260 \u201C${y(a)}\u201D`,()=>l(n.name,n.label,"ne",a)));const s=String(a);(s!==""&&!Number.isNaN(parseFloat(s))||!Number.isNaN(Date.parse(s)))&&(t.appendChild(m(`Filter: > \u201C${y(a)}\u201D`,()=>l(n.name,n.label,"gt",a))),t.appendChild(m(`Filter: < \u201C${y(a)}\u201D`,()=>l(n.name,n.label,"lt",a))))}else if(o){t.appendChild(v("Filter by"));for(const a of d.fields){const s=o.data?.[a.name];s==null||String(s)===""||t.appendChild(m(`${a.label||a.name}: \u201C${y(s,20)}\u201D`,()=>l(a.name,a.label,"eq",s)))}}const i=document.createElement("div");i.style.padding=".35rem .6rem";const r=document.createElement("input");r.type="text",r.className="dm-ctx-input",r.placeholder="Search\u2026 or name: value",r.addEventListener("mousedown",a=>a.stopPropagation()),r.addEventListener("keydown",a=>{if(a.key!=="Enter"||!r.value.trim())return;const s=ft(r.value,d.fields);s?l(s.field,s.label,s.op,s.value):l("*","search","contains",r.value.trim())}),i.appendChild(r),t.appendChild(i),t.appendChild(O()),t.appendChild(v("This display")),t.appendChild(m("Sort\u2026",()=>t._dmPush(a=>it(a,e,d),"Sort"))),ot(e)&&t.appendChild(m("Group by\u2026",()=>t._dmPush(a=>at(a,e,d),"Group by"))),t.appendChild(m("Copy\u2026",()=>t._dmPush(a=>rt(a,e,d,o,n),"Copy")));const u=m("Print",()=>{e.setAttribute("data-dm-print",""),document.body.classList.add("dm-ctx-printing"),f();const a=()=>{e.removeAttribute("data-dm-print"),document.body.classList.remove("dm-ctx-printing"),window.removeEventListener("afterprint",a)};window.addEventListener("afterprint",a),window.print()});t.appendChild(u),t.appendChild(m("Copy link to this view",()=>{D(),_(location.href,"Link copied"),f()}));const c=d.export||e._dmTable?.exportCfg,h=t._dmPanelToken;Q(c).then(a=>{!a||C!==t||t._dmPanelToken!==h||(t.insertBefore(m("Export\u2026",()=>t._dmPush(s=>lt(s,e,d,c),"Export")),u),I(t))});const x=(e._dmCtxFilters||[]).length;if(x||e._dmCtxSort||e._dmCtxGroup){const a=[];x&&a.push(`${x} filter${x===1?"":"s"}`),e._dmCtxSort&&a.push("sort"),e._dmCtxGroup&&a.push("grouping"),t.appendChild(O()),t.appendChild(m(`Clear all (${a.join(", ")})`,()=>{e._dmCtxFilters=[],e._dmCtxSort=null,e._dmCtxGroup=null,p(e),f()}))}}function z(t,e,o,n,d){f();const l=g(o);if(!l)return;const i=nt(document.createElement("div"));i.className="dm-ctx-menu",i._dmX=t,i._dmY=e,C=i,document.body.appendChild(i),i._dmPush(r=>st(r,o,n,d,l),null),document.addEventListener("mousedown",j,!0),document.addEventListener("keydown",B,!0),window.addEventListener("scroll",G,!0),setTimeout(()=>{const r=i.querySelector(".dm-ctx-input");r&&r.focus()},0)}function F(t,e,o){if(t._dmTable){const r=e.closest("tbody tr");if(!r)return{entry:null,field:null};const u=e.closest("td"),c=r.querySelectorAll("td"),h=Array.from(c).map(a=>a.textContent.trim()),x=o.entries.find(a=>o.fields.every((s,k)=>String(a.data?.[s.name]??"").trim()===(h[k]??"").trim()))||null,b=u&&o.fields[u.cellIndex]||null;return{entry:x,field:b}}const n=e.closest("[data-entry-id]");if(!n)return{entry:null,field:null};const d=o.entries.find(r=>r.id===n.dataset.entryId)||null;if(!d)return{entry:null,field:null};const l=(e.textContent||"").trim().slice(0,80);let i=null;if(l){const r=o.fields.filter(u=>{const c=String(d.data?.[u.name]??"");return c&&(c.includes(l)||l.includes(c))});r.length===1&&(i=r[0])}return{entry:d,field:i}}function ct(t){document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t,{once:!0}):t()}ct(()=>{if(typeof Domma>"u"||!Domma.elements?.contextMenu){document.addEventListener("contextmenu",t=>{const e=t.target.closest("[data-ctx], [data-collection-table]");if(!e)return;const o=g(e);if(!o||!o.fields?.length)return;t.preventDefault();const{entry:n,field:d}=F(e,t.target,o);z(t.clientX,t.clientY,e,n,d)});return}Domma.elements.contextMenu("[data-ctx], [data-collection-table]",{cursor:"glyph",exclusive:!0,enabled:t=>{const e=g(t);return!!(e&&e.fields?.length)},render:t=>{const e=t.container,o=g(e),{entry:n,field:d}=F(e,t.event.target,o);z(t.x,t.y,e,n,d)}})})})();
@@ -1 +1 @@
1
- (function(){"use strict";const d=window.__CMS_CTXMENUS__;if(!Array.isArray(d)||!d.length||typeof Domma>"u"||!Domma.elements||!Domma.elements.contextMenu)return;const s=(e,t)=>{typeof E<"u"&&E.toast&&E.toast(e,{type:t||"info"})},m=".site-search-trigger, .site-search-floating-trigger";function f(e){return e&&e.closest?e.closest("[data-ctx], [data-collection-table]"):null}function i(e){if(!e||!e.closest)return null;const t=e.closest('input, textarea, [contenteditable=""], [contenteditable="true"]');return!t||t.disabled||t.readOnly||t.tagName==="INPUT"&&!/^(text|search|url|tel|email|password|number)$/i.test(t.type)?null:t}function u(e){const t=i(e);if(t&&typeof t.selectionStart=="number")return t.value.slice(t.selectionStart,t.selectionEnd);const n=window.getSelection();return n?n.toString():""}function y(){try{const e=performance.getEntriesByType("navigation")[0];return!!e&&e.type==="back_forward"}catch{return!1}}function p(e,t){const n=e.selectionStart,r=e.selectionEnd;e.value=e.value.slice(0,n)+t+e.value.slice(r);const o=n+t.length;e.setSelectionRange(o,o),e.dispatchEvent(new Event("input",{bubbles:!0}))}const l={reload:{run:()=>window.location.reload()},back:{run:()=>window.history.back(),disabled:()=>window.history.length<=1},forward:{run:()=>window.history.forward(),disabled:()=>!y()},print:{run:()=>window.print()},copy:{run:async e=>{const t=u(e);if(t)try{await navigator.clipboard.writeText(t)}catch{s("Your browser would not let the page copy","error")}},disabled:e=>!u(e)},cut:{run:async e=>{const t=i(e);if(!t)return;const n=u(e);if(n)try{await navigator.clipboard.writeText(n),p(t,"")}catch{s("Your browser would not let the page cut","error")}},disabled:e=>!i(e)||!u(e)},paste:{run:async e=>{const t=i(e);if(t)try{const n=await navigator.clipboard.readText();n&&p(t,n)}catch{s("Your browser blocks paste from a menu - use Ctrl+V","warning")}},disabled:e=>!i(e)},"site-search":{run:()=>{if(window.DommaSiteSearch&&window.DommaSiteSearch.open){window.DommaSiteSearch.open();return}const e=document.querySelector(m);if(e){e.click();return}s("Site search is not available on this site","warning")},disabled:()=>!window.DommaSiteSearch&&!document.querySelector(m)},"select-all":{run:e=>{const t=i(e);if(t){t.select();return}const n=e&&e.closest&&e.closest("[data-ctx], .page-body")||document.body,r=document.createRange();r.selectNodeContents(n);const o=window.getSelection();o.removeAllRanges(),o.addRange(r)}}};function w(e){if(!e||!e.type)return null;if(l[e.type])return l[e.type].run;switch(e.type){case"navigate":return e.url?()=>{window.location.href=e.url}:null;case"copy-link":return async()=>{try{await navigator.clipboard.writeText(window.location.href),s("Link copied","success")}catch{s("Could not copy the link","error")}};case"run-action":return e.slug?async t=>{try{const n=await fetch(`/api/actions/${encodeURIComponent(e.slug)}/run`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({entryId:t&&t.dataset?t.dataset.entryId:void 0,url:window.location.pathname})});if(!n.ok)throw new Error(`HTTP ${n.status}`);s("Done","success")}catch(n){s(`Action failed: ${n.message}`,"error")}}:null;case"mark-as-spam":return async t=>{const n=t&&t.closest?t.closest("[data-entry-id]"):null,r=n&&n.dataset?n.dataset.entryId:"",o=t&&t.closest?t.closest("[data-ctx]"):null;let c="";try{o&&o.dataset.ctx&&(c=(JSON.parse(atob(o.dataset.ctx))||{}).slug||"")}catch{}if(!r||!c){s("Right-click an entry in a collection display to mark it","error");return}try{const a=await fetch(`/api/collections/${encodeURIComponent(c)}/entries/${encodeURIComponent(r)}/spam`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"same-origin",body:JSON.stringify({field:e.field||void 0})});if(a.status===401||a.status===403){s("You do not have permission to do that","error");return}if(!a.ok)throw new Error(`HTTP ${a.status}`);s("Marked as spam","success"),n&&n.setAttribute("data-spam","true")}catch(a){s(`Could not mark as spam: ${a.message}`,"error")}};case"open-modal":return e.block?async()=>{try{const t=await fetch(`/api/blocks/${encodeURIComponent(e.block)}/render`);if(!t.ok)throw new Error(`HTTP ${t.status}`);const{html:n}=await t.json(),r=E.modal({title:e.title||"",closeOnBackdrop:!0}),o=document.createElement("div");o.innerHTML=typeof DOMPurify<"u"?DOMPurify.sanitize(n):n,r.element.appendChild(o),r.open()}catch(t){s(`Could not open: ${t.message}`,"error")}}:null;case"search":return t=>{const n=f(t);n&&n.dispatchEvent(new MouseEvent("contextmenu",{bubbles:!0,cancelable:!0,clientX:0,clientY:0}))};case"filter":case"sort":case"export":return t=>{const n=f(t);n&&n.dispatchEvent(new CustomEvent("dm-ctx-request",{bubbles:!0,detail:{...e}}))};default:return null}}const b=new Set(["search","filter","sort","export"]);function g(e){return l[e]&&l[e].disabled?l[e].disabled:b.has(e)?t=>!f(t):null}function h(e){const t=[];for(const n of e||[]){const r=n.type||"item";if(r==="divider"){t.push({type:"divider"});continue}if(r==="header"){t.push({type:"header",header:n.text||""});continue}if(Array.isArray(n.submenu)&&n.submenu.length){const a=h(n.submenu);a.length&&t.push({label:n.text,icon:n.icon,danger:!!n.danger,className:n.class||void 0,submenu:a});continue}const o=w(n.action);if(!o)continue;const c=g(n.action&&n.action.type);t.push({label:n.text,icon:n.icon||void 0,danger:!!n.danger,className:n.class||void 0,shortcut:n.shortcut||void 0,...c?{disabled:c}:{},action:o})}return t.some(n=>n.label)?t:[]}for(const e of d){const t=h(e.items);if(!t.length)continue;const n=e.target||{};if(!n.selector)continue;const r=e.theme||{};try{Domma.elements.contextMenu(n.selector,{cursor:"glyph",match:n.match||null,exclude:n.exclude||null,inherit:e.inherit===void 0?"append":e.inherit,priority:e.priority||0,items:t,accent:r.accent,surface:r.surface,radius:r.radius,shadow:r.shadow,opacity:r.opacity,density:r.density,transition:r.transition,ariaLabel:e.name||"Context menu",className:e.class||""})}catch(o){console.warn(`[context-menus] Skipped "${e.slug}": ${o.message}`)}}})();
1
+ import{decodeBase64Json as S}from"/public/js/utf8-base64.mjs";(function(){"use strict";const d=window.__CMS_CTXMENUS__;if(!Array.isArray(d)||!d.length||typeof Domma>"u"||!Domma.elements||!Domma.elements.contextMenu)return;const s=(e,t)=>{typeof E<"u"&&E.toast&&E.toast(e,{type:t||"info"})},m=".site-search-trigger, .site-search-floating-trigger";function f(e){return e&&e.closest?e.closest("[data-ctx], [data-collection-table]"):null}function i(e){if(!e||!e.closest)return null;const t=e.closest('input, textarea, [contenteditable=""], [contenteditable="true"]');return!t||t.disabled||t.readOnly||t.tagName==="INPUT"&&!/^(text|search|url|tel|email|password|number)$/i.test(t.type)?null:t}function u(e){const t=i(e);if(t&&typeof t.selectionStart=="number")return t.value.slice(t.selectionStart,t.selectionEnd);const n=window.getSelection();return n?n.toString():""}function y(){try{const e=performance.getEntriesByType("navigation")[0];return!!e&&e.type==="back_forward"}catch{return!1}}function p(e,t){const n=e.selectionStart,r=e.selectionEnd;e.value=e.value.slice(0,n)+t+e.value.slice(r);const o=n+t.length;e.setSelectionRange(o,o),e.dispatchEvent(new Event("input",{bubbles:!0}))}const l={reload:{run:()=>window.location.reload()},back:{run:()=>window.history.back(),disabled:()=>window.history.length<=1},forward:{run:()=>window.history.forward(),disabled:()=>!y()},print:{run:()=>window.print()},copy:{run:async e=>{const t=u(e);if(t)try{await navigator.clipboard.writeText(t)}catch{s("Your browser would not let the page copy","error")}},disabled:e=>!u(e)},cut:{run:async e=>{const t=i(e);if(!t)return;const n=u(e);if(n)try{await navigator.clipboard.writeText(n),p(t,"")}catch{s("Your browser would not let the page cut","error")}},disabled:e=>!i(e)||!u(e)},paste:{run:async e=>{const t=i(e);if(t)try{const n=await navigator.clipboard.readText();n&&p(t,n)}catch{s("Your browser blocks paste from a menu - use Ctrl+V","warning")}},disabled:e=>!i(e)},"site-search":{run:()=>{if(window.DommaSiteSearch&&window.DommaSiteSearch.open){window.DommaSiteSearch.open();return}const e=document.querySelector(m);if(e){e.click();return}s("Site search is not available on this site","warning")},disabled:()=>!window.DommaSiteSearch&&!document.querySelector(m)},"select-all":{run:e=>{const t=i(e);if(t){t.select();return}const n=e&&e.closest&&e.closest("[data-ctx], .page-body")||document.body,r=document.createRange();r.selectNodeContents(n);const o=window.getSelection();o.removeAllRanges(),o.addRange(r)}}};function w(e){if(!e||!e.type)return null;if(l[e.type])return l[e.type].run;switch(e.type){case"navigate":return e.url?()=>{window.location.href=e.url}:null;case"copy-link":return async()=>{try{await navigator.clipboard.writeText(window.location.href),s("Link copied","success")}catch{s("Could not copy the link","error")}};case"run-action":return e.slug?async t=>{try{const n=await fetch(`/api/actions/${encodeURIComponent(e.slug)}/run`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({entryId:t&&t.dataset?t.dataset.entryId:void 0,url:window.location.pathname})});if(!n.ok)throw new Error(`HTTP ${n.status}`);s("Done","success")}catch(n){s(`Action failed: ${n.message}`,"error")}}:null;case"mark-as-spam":return async t=>{const n=t&&t.closest?t.closest("[data-entry-id]"):null,r=n&&n.dataset?n.dataset.entryId:"",o=t&&t.closest?t.closest("[data-ctx]"):null;let c="";try{o&&o.dataset.ctx&&(c=(S(o.dataset.ctx)||{}).slug||"")}catch{}if(!r||!c){s("Right-click an entry in a collection display to mark it","error");return}try{const a=await fetch(`/api/collections/${encodeURIComponent(c)}/entries/${encodeURIComponent(r)}/spam`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"same-origin",body:JSON.stringify({field:e.field||void 0})});if(a.status===401||a.status===403){s("You do not have permission to do that","error");return}if(!a.ok)throw new Error(`HTTP ${a.status}`);s("Marked as spam","success"),n&&n.setAttribute("data-spam","true")}catch(a){s(`Could not mark as spam: ${a.message}`,"error")}};case"open-modal":return e.block?async()=>{try{const t=await fetch(`/api/blocks/${encodeURIComponent(e.block)}/render`);if(!t.ok)throw new Error(`HTTP ${t.status}`);const{html:n}=await t.json(),r=E.modal({title:e.title||"",closeOnBackdrop:!0}),o=document.createElement("div");o.innerHTML=typeof DOMPurify<"u"?DOMPurify.sanitize(n):n,r.element.appendChild(o),r.open()}catch(t){s(`Could not open: ${t.message}`,"error")}}:null;case"search":return t=>{const n=f(t);n&&n.dispatchEvent(new MouseEvent("contextmenu",{bubbles:!0,cancelable:!0,clientX:0,clientY:0}))};case"filter":case"sort":case"export":return t=>{const n=f(t);n&&n.dispatchEvent(new CustomEvent("dm-ctx-request",{bubbles:!0,detail:{...e}}))};default:return null}}const b=new Set(["search","filter","sort","export"]);function g(e){return l[e]&&l[e].disabled?l[e].disabled:b.has(e)?t=>!f(t):null}function h(e){const t=[];for(const n of e||[]){const r=n.type||"item";if(r==="divider"){t.push({type:"divider"});continue}if(r==="header"){t.push({type:"header",header:n.text||""});continue}if(Array.isArray(n.submenu)&&n.submenu.length){const a=h(n.submenu);a.length&&t.push({label:n.text,icon:n.icon,danger:!!n.danger,className:n.class||void 0,submenu:a});continue}const o=w(n.action);if(!o)continue;const c=g(n.action&&n.action.type);t.push({label:n.text,icon:n.icon||void 0,danger:!!n.danger,className:n.class||void 0,shortcut:n.shortcut||void 0,...c?{disabled:c}:{},action:o})}return t.some(n=>n.label)?t:[]}for(const e of d){const t=h(e.items);if(!t.length)continue;const n=e.target||{};if(!n.selector)continue;const r=e.theme||{};try{Domma.elements.contextMenu(n.selector,{cursor:"glyph",match:n.match||null,exclude:n.exclude||null,inherit:e.inherit===void 0?"append":e.inherit,priority:e.priority||0,items:t,accent:r.accent,surface:r.surface,radius:r.radius,shadow:r.shadow,opacity:r.opacity,density:r.density,transition:r.transition,ariaLabel:e.name||"Context menu",className:e.class||""})}catch(o){console.warn(`[context-menus] Skipped "${e.slug}": ${o.message}`)}}})();
@@ -1 +1 @@
1
- (function(){"use strict";fetch("/api/effects/settings").then(function(b){return b.json()}).then(function(b){var z=b.respectMotion!==!1,A=b.defaultDuration||600,S=b.defaultAnimation||"fade",E=b.defaultThreshold!=null?b.defaultThreshold:.1,p=z&&window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches,d=document.querySelector(".page-body");if(!d)return;function r(t,e){return t.getAttribute("data-fx-"+e)}function n(t,e,i){var a=r(t,e);return a!==null?parseFloat(a):i}function v(t,e,i){var a=r(t,e);return a===null?i:a==="true"||a==="1"}function l(t,e,i){var a=new IntersectionObserver(function(o){o[0].isIntersecting&&(a.unobserve(t),i())},{threshold:e});a.observe(t)}d.querySelectorAll(".dm-fx-reveal").forEach(function(t){var e=r(t,"animation")||S,i=n(t,"duration",A),a=n(t,"delay",0),o=n(t,"threshold",E),u=v(t,"once",!0);p||(t.style.opacity="0",t.style.transition="none",e==="slide-up"?t.style.transform="translateY(30px)":e==="slide-down"?t.style.transform="translateY(-30px)":e==="zoom"?t.style.transform="scale(0.85)":e==="flip"&&(t.style.transform="rotateY(90deg)"),requestAnimationFrame(function(){requestAnimationFrame(function(){t.style.transition="opacity "+i+"ms ease "+a+"ms, transform "+i+"ms ease "+a+"ms";var s=new IntersectionObserver(function(f){f.forEach(function(y){y.isIntersecting?(t.style.opacity="1",t.style.transform="",u&&s.unobserve(t)):u||(t.style.opacity="0",e==="slide-up"?t.style.transform="translateY(30px)":e==="slide-down"?t.style.transform="translateY(-30px)":e==="zoom"?t.style.transform="scale(0.85)":e==="flip"?t.style.transform="rotateY(90deg)":t.style.transform="")})},{threshold:o});s.observe(t)})}))}),d.querySelectorAll(".dm-fx-counter").forEach(function(t){var e=n(t,"to",0),i=n(t,"from",0),a=n(t,"duration",2e3),o=r(t,"prefix")||"",u=r(t,"suffix")||"",s=n(t,"decimals",0),f=r(t,"separator")||"";function y(h){var c=h.toFixed(s);if(f){var x=c.split(".");x[0]=x[0].replace(/\B(?=(\d{3})+(?!\d))/g,f),c=x.join(".")}return o+c+u}if(p){t.textContent=y(e);return}l(t,.5,function(){var h=null;requestAnimationFrame(function c(x){h||(h=x);var w=Math.min((x-h)/a,1),q=1-Math.pow(1-w,3);t.textContent=y(i+(e-i)*q),w<1&&requestAnimationFrame(c)})})}),p||d.querySelectorAll(".dm-fx-scramble").forEach(function(t){var e=n(t,"speed",50),i=t.textContent,a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&",o=0;l(t,.3,function(){var u=setInterval(function(){for(var s="",f=0;f<i.length;f++)f<o||i[f]===" "?s+=i[f]:s+=a[Math.floor(Math.random()*a.length)];t.textContent=s,o<i.length?o++:clearInterval(u)},e)})}),p||d.querySelectorAll(".dm-fx-shake").forEach(function(t){var e=n(t,"intensity",5),i=n(t,"duration",500),a=r(t,"direction")||"horizontal",o=a==="vertical"?"translateY":"translateX";t.animate([{transform:o+"(0)"},{transform:o+"(-"+e+"px)"},{transform:o+"("+e+"px)"},{transform:o+"(-"+e+"px)"},{transform:o+"("+e+"px)"},{transform:o+"(0)"}],{duration:i,easing:"ease-in-out"})}),d.querySelectorAll(".dm-fx-ripple").forEach(function(t){t.style.position="relative",t.style.overflow="hidden",t.addEventListener("click",function(e){var i=r(t,"colour")||"rgba(255,255,255,0.3)",a=n(t,"duration",600),o=t.getBoundingClientRect(),u=Math.max(o.width,o.height)*2,s=document.createElement("span");s.style.cssText="position:absolute;border-radius:50%;pointer-events:none;width:"+u+"px;height:"+u+"px;left:"+(e.clientX-o.left-u/2)+"px;top:"+(e.clientY-o.top-u/2)+"px;background:"+i+";transform:scale(0);opacity:1;transition:transform "+a+"ms ease,opacity "+a+"ms ease;",t.appendChild(s),requestAnimationFrame(function(){s.style.transform="scale(1)",s.style.opacity="0"}),setTimeout(function(){s.remove()},a)})});var m=window.Domma&&window.Domma.effects;m&&(d.querySelectorAll(".dm-fx-breathe").forEach(function(t){if(!p){var e={};r(t,"amplitude")&&(e.amplitude=n(t,"amplitude",6)),r(t,"duration")&&(e.duration=n(t,"duration",3e3)),r(t,"easing")&&(e.easing=r(t,"easing")),r(t,"stagger")&&(e.stagger=n(t,"stagger",0)),m.breathe(t,e)}}),d.querySelectorAll(".dm-fx-pulse").forEach(function(t){if(!p){var e={};r(t,"scale")&&(e.scale=n(t,"scale",1.05)),r(t,"duration")&&(e.duration=n(t,"duration",2e3)),r(t,"easing")&&(e.easing=r(t,"easing")),m.pulse(t,e)}}),d.querySelectorAll(".dm-fx-scribe").forEach(function(t){var e={};r(t,"speed")&&(e.speed=n(t,"speed",50)),r(t,"delete-speed")&&(e.deleteSpeed=n(t,"delete-speed",30)),r(t,"cursor")&&(e.cursor=v(t,"cursor",!0)),r(t,"cursor-char")&&(e.cursorChar=r(t,"cursor-char")),r(t,"cursor-type")&&(e.cursorType=r(t,"cursor-type")),r(t,"mode")&&(e.mode=r(t,"mode")),r(t,"loop")&&(e.loop=v(t,"loop",!1)),r(t,"loop-delay")&&(e.loopDelay=n(t,"loop-delay",1e3)),r(t,"pause-on-hover")&&(e.pauseOnHover=v(t,"pause-on-hover",!1));var i=r(t,"actions");if(i)try{e.actions=JSON.parse(i)}catch{return}else{var a=t.textContent.trim();if(!a)return;t.textContent="",e.actions=[{render:a}]}m.scribe(t,e)}),d.querySelectorAll(".dm-fx-twinkle").forEach(function(t){var e={};r(t,"count")&&(e.count=n(t,"count",100)),r(t,"shape")&&(e.shape=r(t,"shape")),r(t,"colour")&&(e.colour=r(t,"colour")),r(t,"min-size")&&(e.minSize=n(t,"min-size",1)),r(t,"max-size")&&(e.maxSize=n(t,"max-size",3)),m.twinkle(t,e)}),typeof m.tickerTape=="function"&&d.querySelectorAll(".dm-fx-ticker-tape").forEach(function(t){if(!p){var e={};r(t,"palette")&&(e.palette=r(t,"palette")),r(t,"density")&&(e.density=n(t,"density",50)),r(t,"speed")&&(e.speed=n(t,"speed",1)),r(t,"sway")&&(e.sway=n(t,"sway",60)),r(t,"rotation-speed")&&(e.rotationSpeed=n(t,"rotation-speed",6)),r(t,"min-width")&&(e.minWidth=n(t,"min-width",5)),r(t,"max-width")&&(e.maxWidth=n(t,"max-width",9)),r(t,"min-height")&&(e.minHeight=n(t,"min-height",12)),r(t,"max-height")&&(e.maxHeight=n(t,"max-height",22)),r(t,"fade-start")&&(e.fadeStart=n(t,"fade-start",.55)),r(t,"burst")&&(e.burst=v(t,"burst",!1)),r(t,"burst-count")&&(e.burstCount=n(t,"burst-count",150)),r(t,"z-index")&&(e.zIndex=n(t,"z-index",1));var i=r(t,"mode");i==="page"?m.tickerTape(null,e):m.tickerTape(t,e)}}),typeof m.butterflies=="function"&&d.querySelectorAll(".dm-fx-butterflies").forEach(function(t){if(!p){var e={};r(t,"palette")&&(e.palette=r(t,"palette")),r(t,"density")&&(e.density=n(t,"density",18)),r(t,"speed")&&(e.speed=n(t,"speed",1)),r(t,"wander")&&(e.wander=n(t,"wander",1)),r(t,"rise-speed")&&(e.riseSpeed=n(t,"rise-speed",.4)),r(t,"flap-speed")&&(e.flapSpeed=n(t,"flap-speed",1)),r(t,"min-size")&&(e.minSize=n(t,"min-size",14)),r(t,"max-size")&&(e.maxSize=n(t,"max-size",26)),r(t,"two-tone")&&(e.twoTone=v(t,"two-tone",!0)),r(t,"burst")&&(e.burst=v(t,"burst",!1)),r(t,"burst-count")&&(e.burstCount=n(t,"burst-count",40)),r(t,"z-index")&&(e.zIndex=n(t,"z-index",1));var i=r(t,"mode");i==="page"?m.butterflies(null,e):m.butterflies(t,e)}}),typeof m.strobe=="function"&&d.querySelectorAll(".dm-fx-strobe").forEach(function(t){if(p)return;function e(s){var f=r(t,s);if(!f)return null;var y=f.split(",").map(function(h){return h.trim()}).filter(Boolean);return y.length?y:null}var i={};r(t,"preset")&&(i.preset=r(t,"preset")),r(t,"motion")&&(i.motion=r(t,"motion")),r(t,"speed")&&(i.speed=n(t,"speed",1)),r(t,"sweep-arc")&&(i.sweepArc=n(t,"sweep-arc",100)),r(t,"beam-width")&&(i.beamWidth=n(t,"beam-width",16)),r(t,"intensity")&&(i.intensity=n(t,"intensity",.7)),r(t,"flicker")&&(i.flicker=v(t,"flicker",!0)),r(t,"hz")&&(i.hz=n(t,"hz",10));var a=e("origins");a&&(i.origins=a);var o=e("colours");o&&(i.colours=o),r(t,"duration")&&(i.duration=n(t,"duration",null)),r(t,"z-index")&&(i.zIndex=n(t,"z-index",9999));var u=r(t,"mode");u==="page"?m.strobe(null,i):m.strobe(t,i)}));var g=d.querySelectorAll(".dm-fx-celebrate");g.length&&import("/public/js/celebrations/index.js?v=20261003-characters").then(function(t){var e=t.CelebrationsEffect;g.forEach(function(i){var a=r(i,"character");if(a){if(i.querySelector("canvas"))return;t.mountCharacter(i,{character:a,size:r(i,"size")||"medium",facing:r(i,"facing")||"right",animate:!p&&v(i,"animate",!0)}).then(function(c){var x=new MutationObserver(function(){i.isConnected||(c.destroy(),x.disconnect())});i.isConnected?x.observe(document.body,{childList:!0,subtree:!0}):c.destroy()}).catch(function(c){console.error("[celebrate]",c)});return}if(!p){var o=i.getAttribute("data-fx-theme")||"auto",u=i.getAttribute("data-fx-intensity")||"medium",s=i.getAttribute("data-fx-z-index")?parseInt(i.getAttribute("data-fx-z-index"),10):999;if(!(o==="auto"&&(o=e.getCurrentTheme(),!o))){var f={},y=["data-fx-theme","data-fx-intensity","data-fx-z-index"];Array.from(i.attributes).forEach(function(c){c.name.startsWith("data-fx-")&&!y.includes(c.name)&&(f[c.name.slice(8)]=c.value)});var h=new e({theme:o,intensity:u,enabled:!0,zIndex:s,overrides:f});h.init().then(function(){h.start()})}}})}).catch(function(){})}).catch(function(){})})();
1
+ (function(){"use strict";fetch("/api/effects/settings").then(function(x){return x.json()}).then(function(x){var q=x.respectMotion!==!1,E=x.defaultDuration||600,C=x.defaultAnimation||"fade",k=x.defaultThreshold!=null?x.defaultThreshold:.1,u=q&&window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches,c=document.querySelector(".page-body");if(!c)return;var b=c.querySelector?c.querySelector(".dm-fx-demo-controls"):null;if(b){b.hidden=!1;var g=b.querySelector("[data-fx-demo-status]"),w=b.querySelector("[data-fx-demo-play]");g.textContent=u?"Animations are disabled by your Reduce motion preference. The demos show static content.":"Animations are enabled. Scroll to each demo to see it, or replay the page.",w.textContent=u?"Enable animations on this site":"Replay demos",w.addEventListener("click",function(){if(u)try{localStorage.setItem("domma:reduced_motion","false")}catch{g.textContent="Your browser blocked saving the motion preference. Use its accessibility settings to allow animations.";return}window.location.reload()})}function r(t,e){return t.getAttribute("data-fx-"+e)}function n(t,e,i){var o=r(t,e),a=o!==null?parseFloat(o):NaN;return Number.isFinite(a)?a:i}function v(t,e,i){var o=r(t,e);return o===null?i:o==="true"||o==="1"}function z(t,e,i){var o=new IntersectionObserver(function(a){a[0].isIntersecting&&(o.unobserve(t),i())},{threshold:e});o.observe(t)}c.querySelectorAll(".dm-fx-reveal").forEach(function(t){var e=r(t,"animation")||C,i=n(t,"duration",E),o=n(t,"delay",0),a=n(t,"threshold",k),s=v(t,"once",!0);u||(t.style.opacity="0",t.style.transition="none",e==="slide-up"?t.style.transform="translateY(30px)":e==="slide-down"?t.style.transform="translateY(-30px)":e==="zoom"?t.style.transform="scale(0.85)":e==="flip"&&(t.style.transform="rotateY(90deg)"),requestAnimationFrame(function(){requestAnimationFrame(function(){t.style.transition="opacity "+i+"ms ease "+o+"ms, transform "+i+"ms ease "+o+"ms";var f=new IntersectionObserver(function(d){d.forEach(function(y){y.isIntersecting?(t.style.opacity="1",t.style.transform="",s&&f.unobserve(t)):s||(t.style.opacity="0",e==="slide-up"?t.style.transform="translateY(30px)":e==="slide-down"?t.style.transform="translateY(-30px)":e==="zoom"?t.style.transform="scale(0.85)":e==="flip"?t.style.transform="rotateY(90deg)":t.style.transform="")})},{threshold:a});f.observe(t)})}))}),c.querySelectorAll(".dm-fx-counter").forEach(function(t){var e=n(t,"to",0),i=n(t,"from",0),o=n(t,"duration",2e3),a=r(t,"prefix")||"",s=r(t,"suffix")||"",f=n(t,"decimals",0),d=r(t,"separator")||"";function y(h){var m=h.toFixed(f);if(d){var l=m.split(".");l[0]=l[0].replace(/\B(?=(\d{3})+(?!\d))/g,d),m=l.join(".")}return a+m+s}if(u){t.textContent=y(e);return}z(t,.5,function(){var h=null;requestAnimationFrame(function m(l){h||(h=l);var S=Math.min((l-h)/o,1),I=1-Math.pow(1-S,3);t.textContent=y(i+(e-i)*I),S<1&&requestAnimationFrame(m)})})}),u||c.querySelectorAll(".dm-fx-scramble").forEach(function(t){var e=n(t,"speed",50),i=t.textContent,o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&",a=0;z(t,.3,function(){var s=setInterval(function(){for(var f="",d=0;d<i.length;d++)d<a||i[d]===" "?f+=i[d]:f+=o[Math.floor(Math.random()*o.length)];t.textContent=f,a<i.length?a++:clearInterval(s)},e)})}),u||c.querySelectorAll(".dm-fx-shake").forEach(function(t){var e=n(t,"intensity",5),i=n(t,"duration",500),o=r(t,"direction")||"horizontal",a=o==="vertical"?"translateY":"translateX";t.animate([{transform:a+"(0)"},{transform:a+"(-"+e+"px)"},{transform:a+"("+e+"px)"},{transform:a+"(-"+e+"px)"},{transform:a+"("+e+"px)"},{transform:a+"(0)"}],{duration:i,easing:"ease-in-out"})}),c.querySelectorAll(".dm-fx-ripple").forEach(function(t){t.style.position="relative",t.style.overflow="hidden",t.addEventListener("click",function(e){var i=r(t,"colour")||"rgba(255,255,255,0.3)",o=n(t,"duration",600),a=t.getBoundingClientRect(),s=Math.max(a.width,a.height)*2,f=document.createElement("span");f.style.cssText="position:absolute;border-radius:50%;pointer-events:none;width:"+s+"px;height:"+s+"px;left:"+(e.clientX-a.left-s/2)+"px;top:"+(e.clientY-a.top-s/2)+"px;background:"+i+";transform:scale(0);opacity:1;transition:transform "+o+"ms ease,opacity "+o+"ms ease;",t.appendChild(f),requestAnimationFrame(function(){f.style.transform="scale(1)",f.style.opacity="0"}),setTimeout(function(){f.remove()},o)})});var p=window.Domma&&window.Domma.effects;p&&(c.querySelectorAll(".dm-fx-breathe").forEach(function(t){if(!u){var e={};r(t,"amplitude")&&(e.amplitude=n(t,"amplitude",6)),r(t,"duration")&&(e.duration=n(t,"duration",3e3)),r(t,"easing")&&(e.easing=r(t,"easing")),r(t,"stagger")&&(e.stagger=n(t,"stagger",0)),p.breathe(t,e)}}),c.querySelectorAll(".dm-fx-pulse").forEach(function(t){if(!u){var e={};r(t,"scale")&&(e.scale=n(t,"scale",1.05)),r(t,"duration")&&(e.duration=n(t,"duration",2e3)),r(t,"easing")&&(e.easing=r(t,"easing")),p.pulse(t,e)}}),c.querySelectorAll(".dm-fx-scribe").forEach(function(t){var e={};r(t,"speed")&&(e.speed=n(t,"speed",50)),r(t,"delete-speed")&&(e.deleteSpeed=n(t,"delete-speed",30)),r(t,"cursor")&&(e.cursor=v(t,"cursor",!0)),r(t,"cursor-char")&&(e.cursorChar=r(t,"cursor-char")),r(t,"cursor-type")&&(e.cursorType=r(t,"cursor-type")),r(t,"mode")&&(e.mode=r(t,"mode")),r(t,"loop")&&(e.loop=v(t,"loop",!1)),r(t,"loop-delay")&&(e.loopDelay=n(t,"loop-delay",1e3)),r(t,"pause-on-hover")&&(e.pauseOnHover=v(t,"pause-on-hover",!1));var i=r(t,"actions");if(i)try{e.actions=JSON.parse(i)}catch{return}else{var o=t.textContent.trim();if(!o)return;u||(t.textContent=""),e.actions=[{render:o}]}if(u){var a=(e.actions||[]).find(function(s){return s.render!=null});a&&(t.textContent=String(a.render));return}p.scribe(t,e)}),c.querySelectorAll(".dm-fx-twinkle").forEach(function(t){var e={};r(t,"count")&&(e.count=n(t,"count",100)),r(t,"shape")&&(e.shape=r(t,"shape")),r(t,"colour")&&(e.colour=r(t,"colour")),r(t,"min-size")&&(e.minSize=n(t,"min-size",1)),r(t,"max-size")&&(e.maxSize=n(t,"max-size",3)),p.twinkle(t,e)}),typeof p.tickerTape=="function"&&c.querySelectorAll(".dm-fx-ticker-tape").forEach(function(t){if(!u){var e={};r(t,"palette")&&(e.palette=r(t,"palette")),r(t,"density")&&(e.density=n(t,"density",50)),r(t,"speed")&&(e.speed=n(t,"speed",1)),r(t,"sway")&&(e.sway=n(t,"sway",60)),r(t,"rotation-speed")&&(e.rotationSpeed=n(t,"rotation-speed",6)),r(t,"min-width")&&(e.minWidth=n(t,"min-width",5)),r(t,"max-width")&&(e.maxWidth=n(t,"max-width",9)),r(t,"min-height")&&(e.minHeight=n(t,"min-height",12)),r(t,"max-height")&&(e.maxHeight=n(t,"max-height",22)),r(t,"fade-start")&&(e.fadeStart=n(t,"fade-start",.55)),r(t,"burst")&&(e.burst=v(t,"burst",!1)),r(t,"burst-count")&&(e.burstCount=n(t,"burst-count",150)),r(t,"z-index")&&(e.zIndex=n(t,"z-index",1));var i=r(t,"mode");i==="page"?p.tickerTape(null,e):p.tickerTape(t,e)}}),typeof p.butterflies=="function"&&c.querySelectorAll(".dm-fx-butterflies").forEach(function(t){if(!u){var e={};r(t,"palette")&&(e.palette=r(t,"palette")),r(t,"density")&&(e.density=n(t,"density",18)),r(t,"speed")&&(e.speed=n(t,"speed",1)),r(t,"wander")&&(e.wander=n(t,"wander",1)),r(t,"rise-speed")&&(e.riseSpeed=n(t,"rise-speed",.4)),r(t,"flap-speed")&&(e.flapSpeed=n(t,"flap-speed",1)),r(t,"min-size")&&(e.minSize=n(t,"min-size",14)),r(t,"max-size")&&(e.maxSize=n(t,"max-size",26)),r(t,"two-tone")&&(e.twoTone=v(t,"two-tone",!0)),r(t,"burst")&&(e.burst=v(t,"burst",!1)),r(t,"burst-count")&&(e.burstCount=n(t,"burst-count",40)),r(t,"z-index")&&(e.zIndex=n(t,"z-index",1));var i=r(t,"mode");i==="page"?p.butterflies(null,e):p.butterflies(t,e)}}),typeof p.strobe=="function"&&c.querySelectorAll(".dm-fx-strobe").forEach(function(t){if(u)return;function e(f){var d=r(t,f);if(!d)return null;var y=d.split(",").map(function(h){return h.trim()}).filter(Boolean);return y.length?y:null}var i={};r(t,"preset")&&(i.preset=r(t,"preset")),r(t,"motion")&&(i.motion=r(t,"motion")),r(t,"speed")&&(i.speed=n(t,"speed",1)),r(t,"sweep-arc")&&(i.sweepArc=n(t,"sweep-arc",100)),r(t,"beam-width")&&(i.beamWidth=n(t,"beam-width",16)),r(t,"intensity")&&(i.intensity=n(t,"intensity",.7)),r(t,"flicker")&&(i.flicker=v(t,"flicker",!0)),r(t,"hz")&&(i.hz=n(t,"hz",10));var o=e("origins");o&&(i.origins=o);var a=e("colours");a&&(i.colours=a),r(t,"duration")&&(i.duration=n(t,"duration",null)),r(t,"z-index")&&(i.zIndex=n(t,"z-index",9999));var s=r(t,"mode");s==="page"?p.strobe(null,i):p.strobe(t,i)}));var A=c.querySelectorAll(".dm-fx-celebrate");A.length&&import("/public/js/celebrations/index.js?v=20261003-characters").then(function(t){var e=t.CelebrationsEffect;A.forEach(function(i){var o=r(i,"character");if(o){if(i.querySelector("canvas"))return;t.mountCharacter(i,{character:o,size:r(i,"size")||"medium",facing:r(i,"facing")||"right",animate:!u&&v(i,"animate",!0)}).then(function(m){var l=new MutationObserver(function(){i.isConnected||(m.destroy(),l.disconnect())});i.isConnected?l.observe(document.body,{childList:!0,subtree:!0}):m.destroy()}).catch(function(m){console.error("[celebrate]",m)});return}if(!u){var a=i.getAttribute("data-fx-theme")||"auto",s=i.getAttribute("data-fx-intensity")||"medium",f=i.getAttribute("data-fx-z-index")?parseInt(i.getAttribute("data-fx-z-index"),10):999;if(!(a==="auto"&&(a=e.getCurrentTheme(),!a))){var d={},y=["data-fx-theme","data-fx-intensity","data-fx-z-index"];Array.from(i.attributes).forEach(function(m){m.name.startsWith("data-fx-")&&!y.includes(m.name)&&(d[m.name.slice(8)]=m.value)});var h=new e({theme:a,intensity:s,enabled:!0,zIndex:f,overrides:d});h.init().then(function(){h.start()})}}})}).catch(function(){})}).catch(function(){})})();
package/public/js/site.js CHANGED
@@ -1 +1 @@
1
- import{makeBadgeEl as O,applyColour as B,applyPill as X,applyFloat as G,applyHighlight as z,applyItemHighlight as Y,itemHighlightAttrs as U,isCurrentItem as j,menuAlign as K,applyClasses as I,cssClasses as R}from"/public/js/menu-decor.mjs?v=20261009-hover-darken";import{initFloatingMenus as Q}from"/public/js/menu-float.js?v=20260829-float-panel";import{enhancePickerFields as P,markInvalidFields as V,showValidationPopup as J}from"/public/js/forms.js?v=20261009-form-classes";import{buildWizardSteps as Z}from"/public/js/form-steps.mjs";import{applyFormClasses as W,applySubmitButtons as ee,applyWizardButtons as te}from"/public/js/form-submit.mjs";$(()=>{const x=window.__CMS_NAV__||{},f=window.__CMS_SITE__||{};if(f.autoTheme?.enabled){let t=function(e){const i=(e||"07:00").split(":");return+i[0]*60+(+i[1]||0)},s=function(){const e=new Date,i=e.getHours()*60+e.getMinutes();return i>=t(n.dayStart)&&i<t(n.nightStart)?n.dayTheme:n.nightTheme},a=function(e){const i=n.apply&&n.apply[e]||{theme:e,custom:""};Domma.theme.set(i.theme);for(const p of[...document.body.classList])p.startsWith("dm-custom-")&&document.body.classList.remove(p);i.custom&&document.body.classList.add(i.custom)};const n=f.autoTheme;a(s()),setInterval(()=>a(s()),6e4)}const _=$("#site-navbar");if(_.length&&x.brand){let s=function(c,g=0){return Array.isArray(c)?c.filter(r=>!(r&&r.type==="spacer")).filter(r=>!(r&&r.type==="separator"&&g===0)).map(r=>r&&r.type==="separator"?{divider:!0}:Array.isArray(r?.items)?{...r,items:s(r.items,g+1)}:r):c},a=function(c){return Array.isArray(c)?c.filter(g=>!(g&&(g.type==="separator"||g.type==="spacer"))):[]};const n={...x.brand},t=n.size&&n.size!=="md"?` navbar-brand-${n.size}`:"";if(n.logo||n.icon||n.tagline){let c="";n.logo?c+=`<img src="${n.logo}" class="navbar-brand-logo" alt="${n.text||""}">`:n.icon&&(c+=`<span data-icon="${n.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),n.text&&(c+=`<span class="navbar-brand-text${t}">${n.text}</span>`),n.tagline&&(c+=`<small class="navbar-brand-tagline">${n.tagline}</small>`),n.html=c}else t&&n.text&&(n.html=`<span class="navbar-brand-text${t}">${n.text}</span>`);const e=x.position==="floating",i=x.orientation==="vertical";Domma.elements.navbar("#site-navbar",{brand:n,items:s(x.items||[]),variant:x.variant||"dark",position:e?"static":x.position||"sticky",collapsible:!0,appearOnHover:!!x.appearOnHover}),Domma.icons.scan("#site-navbar");const p=_.get(0);p.classList.add(`dm-nav-align-${K(x)||"centre"}`),i&&_.get(0).classList.add("dm-nav-vertical",x.side==="right"?"dm-nav-vertical-right":"dm-nav-vertical-left"),z(p,x),I(p,x.class),e&&(G(_.get(0),x),Q(_.get(0).parentNode||document));const y=p.querySelector(".navbar-toggle"),u=p.querySelector(".navbar-collapse");if(y&&u){let c=null;const g=()=>{u.classList.contains("show")&&y.click()},r=()=>{c&&(c.remove(),c=null),document.body.classList.remove("dm-drawer-open")},o=()=>{c||(c=document.createElement("div"),c.className="dm-navbar-backdrop",c.addEventListener("click",g),document.body.appendChild(c),document.body.classList.add("dm-drawer-open"))};y.addEventListener("click",()=>{requestAnimationFrame(()=>{u.classList.contains("show")?o():r()})}),u.addEventListener("click",d=>{d.target.closest("a")&&requestAnimationFrame(r)}),document.addEventListener("keydown",d=>{d.key==="Escape"&&u.classList.contains("show")&&g()})}const m=p.querySelector(".navbar-nav");if(m){const c=Array.from(m.children);let g=0;for(const r of x.items||[])if(!(r&&r.type==="separator")){if(r&&r.type==="spacer"){const o=document.createElement("li"),d=["xs","sm","md","lg","xl","flex"].includes(r.size)?r.size:"md";o.className=`dm-menu-spacer dm-menu-spacer--${d}`,o.setAttribute("aria-hidden","true"),m.insertBefore(o,c[g]||null);continue}g++}}const b=a(x.items||[]);p.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(c=>{const g=b[parseInt(c.dataset.index,10)];if(!g)return;const r=c.dataset.subindex!=null,o=r&&Array.isArray(g.items)?g.items[parseInt(c.dataset.subindex,10)]:g;if(!o)return;o.colour&&B(c,o.colour),o.pill&&!r&&X(c,o.pill),Y(c,o),I(c.closest("li"),o.class);const d=O(o.badge);d&&c.appendChild(d),j(o.url,location.pathname)&&(c.classList.add("active"),c.setAttribute("aria-current","page"))})}const C=$("#site-footer");if(C.length){let e=function(o){return String(o??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},i=function(o){return e(o).replace(/'/g,"&#39;")},p=function(o){if(o&&o.type==="separator")return'<li class="footer-sep"><hr></li>';if(o&&o.type==="spacer")return`<li class="dm-menu-spacer dm-menu-spacer--${["xs","sm","md","lg","xl","flex"].includes(o.size)?o.size:"md"}" aria-hidden="true"></li>`;const d=i(o.url||"#"),l=e(o.text||""),w=Array.isArray(o.items)&&o.items.length?`<ul>${o.items.map(p).join("")}</ul>`:"",v=U(o),D=[v.hover&&` data-menu-item-hover="${i(v.hover)}"`,v.active&&` data-menu-item-active="${i(v.active)}"`].filter(Boolean).join(""),M=v.accent?` style="--dm-menu-highlight:${i(v.accent)}"`:"",k=R(o.class).join(" ");return`<li${k?` class="${i(k)}"`:""}><a href="${d}"${D}${M}>${l}</a>${w}</li>`},y=function(o,d){return(o||[]).forEach(l=>{l&&(l.type==="separator"||l.type==="spacer")||(d.push(l),Array.isArray(l.items)&&l.items.length&&y(l.items,d))}),d};const n=f.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 s='<div class="footer-inner container">';const a=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(a.copyright&&(s+=`<p>${e(a.copyright)}</p>`),a.primary.length&&(s+=`<nav class="footer-nav footer-nav--primary"><ul>${a.primary.map(p).join("")}</ul></nav>`),a.legal.length&&(s+=`<nav class="footer-nav footer-nav--legal"><ul>${a.legal.map(p).join("")}</ul></nav>`),f.footer){const o=Object.keys(t).filter(l=>n[l]),d=R(f.footer.socialClass).join(" ");o.length&&(s+='<div class="footer-social">',o.forEach(l=>{const{label:w,svg:v}=t[l];s+=`<a href="${n[l]}" target="_blank" rel="noopener noreferrer" aria-label="${w}" class="footer-social-link${d?` ${i(d)}`:""}">${v}</a>`}),s+="</div>")}s+="</div>",C.html(s),I(C.get(0),f.footer?.class);const u=[...y(a.primary,[]),...y(a.legal,[])];C.get(0).querySelectorAll(".footer-nav a").forEach((o,d)=>{const l=u[d];if(!l)return;l.colour&&B(o,l.colour);const w=O(l.badge);w&&o.appendChild(w),j(l.url,location.pathname)&&(o.classList.add("is-active"),o.setAttribute("aria-current","page"))}),z(C.get(0).querySelector(".footer-nav--primary"),{highlight:a.primaryHighlight}),z(C.get(0).querySelector(".footer-nav--legal"),{highlight:a.legalHighlight}),I(C.get(0).querySelector(".footer-nav--primary"),a.primaryClass),I(C.get(0).querySelector(".footer-nav--legal"),a.legalClass);const b=f.footer?.showMotionToggle!==!1,c=S.get("reduced_motion"),g=c!==null?!!c:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),r=C.get(0).querySelector(".footer-inner");if(b&&r){const o=document.createElement("input");o.type="checkbox",o.className="form-switch-input",o.id="dm-motion-switch",o.checked=g,o.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const d=document.createElement("span");d.className="form-switch-label",d.textContent="Reduce motion";const l=document.createElement("label");l.className="form-switch footer-motion-switch",l.title="Reduce motion",l.appendChild(o),l.appendChild(d),r.appendChild(l)}}$("#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 h=$(".page-body");if(h.length){h.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),h.find(".tabs").each(function(){Domma.elements.tabs(this)}),h.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)}),h.find(".dm-countdown").each(function(){const n={autoStart:!0},t=String(this.dataset.format||"").trim(),s=String(this.dataset.to||"").trim(),a=/^(\d{4})-(\d{2})-(\d{2})$/.exec(s),e=a?new Date(+a[1],+a[2]-1,+a[3]).getTime():s?new Date(s).getTime():NaN;Number.isFinite(e)?n.duration=Math.max(0,e-Date.now()):this.dataset.duration&&(n.duration=(parseInt(this.dataset.duration,10)||0)*1e3);const i=/^dd:/i.test(t);n.format=i?"hh:mm:ss":t||(n.duration>=36e5?"hh:mm:ss":"mm:ss");const p=this,y=m=>String(m).padStart(2,"0"),u=m=>{const b=Math.max(0,Math.floor(m/1e3)),c=p.querySelector(".dm-timer-time");c&&(c.textContent=`${y(Math.floor(b/86400))}:${y(Math.floor(b%86400/3600))}:${y(Math.floor(b%3600/60))}:${y(b%60)}`)};i&&(n.onTick=u),Domma.elements.timer(this,n),i&&u(n.duration||0)}),h.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),h.find(".dm-progression").each(function(){const n=Array.from(this.querySelectorAll(".dm-progression-item")).map(a=>{const e={title:a.querySelector(".dm-progression-item-title")?.textContent?.trim()||"",description:a.querySelector(".dm-progression-item-body")?.innerHTML?.trim()||""};return a.dataset.date&&(e.date=a.dataset.date),a.dataset.status&&(e.status=a.dataset.status),a.dataset.entryId&&(e._entryId=a.dataset.entryId),e}),t=this.dataset.layout==="centred"?"centered":this.dataset.layout||"vertical",s=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:s,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",s=n.dataset.revealAnimation||"slide-up",a=parseInt(n.dataset.revealDuration,10)||400,e=parseInt(n.dataset.revealStagger,10)||60,i=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,b)=>{m.style.opacity="0",m.style.transform=u[s]||"",m.style.transition=`opacity ${a}ms ease, transform ${a}ms ease`;const c=p?y.length-1-b:b;m.style.transitionDelay=t==="stagger"?`${i+c*e}ms`:`${i}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const m=new IntersectionObserver(b=>{b.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(b=>m.observe(b))}))}),h.find(".card[data-collapsible]").each(function(){const n=this.querySelector(".card-header");n&&n.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),h.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const n=this.dataset.soTarget,t=document.getElementById(n);if(!t)return;const s=E.slideover({title:t.dataset.soTitle||"",size:t.dataset.soSize||"md",position:t.dataset.soPosition||"right"});t.style.display="",s.setContent(t),s.open()})})}if(typeof $.setup=="function"){const n=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(t=>{try{const s=atob(t.dataset.config),a=JSON.parse(s);Object.assign(n,a)}catch{}}),Object.keys(n).length>0){const t={};for(const[s,a]of Object.entries(n)){const e=a?.events?.click,{confirm:i,toast:p,alert:y,prompt:u,...m}=e||{};i||p||y||u?($(s).on("click",async function(c){if(c.preventDefault(),i&&!await E.confirm(i))return;let g=null;if(!(u&&(g=await E.prompt(u,{inputPlaceholder:m.promptPlaceholder||"",inputValue:m.promptDefault||""}),g===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),g!==null&&(m.setText&&r.text(g),m.setVal&&r.val(g),m.setAttr&&r.attr(m.setAttr,g))}m.href&&(window.location.href=m.href),p&&E.toast(p,{type:m.toastType||"success"}),y&&E.alert(y)}}),Object.keys(m).length&&(t[s]={...a,events:{...a.events,click:m}})):t[s]=a}$.setup(t)}}h.length&&q(h.get(0))}),window.wireCTAButtons=q;function q(x){x.querySelectorAll(".dm-cta-trigger").forEach(f=>{f.__ctaWired||(f.__ctaWired=!0,f.addEventListener("click",async()=>{const _=f.dataset.action,C=f.dataset.entry,A=f.dataset.confirm;let h=S.get("auth_token");if(!h){E.toast("Please log in to perform this action.",{type:"warning"});return}if(A&&!await E.confirm(A))return;const n=Array.from(f.childNodes).map(s=>s.cloneNode(!0));f.disabled=!0,f.textContent="Running\u2026";const t=s=>fetch(`/api/actions/${_}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify(f.dataset.scope?{entryId:C,scope:f.dataset.scope}:{entryId:C})});try{let s=await t(h);if(s.status===401){const e=S.get("auth_refresh_token");if(e){const i=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(i.ok){const{token:p}=await i.json();S.set("auth_token",p),h=p,s=await t(h)}}}const a=await s.json().catch(()=>({}));if(!s.ok)throw new Error(a.error||a.message||`Error ${s.status}`);E.toast(a.message||"Action completed.",{type:"success"})}catch(s){E.toast(s.message||"Action failed.",{type:"error"})}finally{f.disabled=!1,f.textContent="",n.forEach(s=>f.appendChild(s)),Domma.icons.scan(f)}}))})}(function(){const f=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!f.length)return;const _=S.get("auth_token");function C(h,n){const t=document.createElement("div");t.className="dm-collection-display dm-collection-empty";const s=document.createElement("p");if(n){s.appendChild(document.createTextNode("Please "));const a=document.createElement("a");a.href="/login",a.textContent="sign in",s.appendChild(a),s.appendChild(document.createTextNode(" to view your entries."))}else s.textContent=h;return t.appendChild(s),t}function A(h,n){const t=document.createElement("template");t.innerHTML=n||"",h.replaceWith(t.content)}f.forEach(async h=>{const n=h.dataset.collectionAttrs;if(!n)return;if(!_){h.replaceChildren(C("",!0));return}const t=s=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({attrs:n})});try{let s=_,a=await t(s);if(a.status===401){const i=S.get("auth_refresh_token");if(i){const p=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:i})});if(p.ok){const{token:y}=await p.json();S.set("auth_token",y),s=y,a=await t(s)}}}if(!a.ok){const{error:i}=await a.json().catch(()=>({}));h.replaceChildren(C(i||"Unable to load entries.",!1));return}const{html:e}=await a.json();A(h,e),window.DommaCollectionBrowser?.hydrate&&window.DommaCollectionBrowser.hydrate(document)}catch{h.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(),q(document.body)},100)})(),(function(){const f=document.querySelectorAll("[data-collection-table]");!f.length||typeof T>"u"||!T.create||f.forEach(_=>{let C;try{C=JSON.parse(atob(_.dataset.payload))}catch{return}const{columns:A,rows:h,search:n,sortable:t,exportable:s,pageSize:a,empty:e,ctaConfig:i,slug:p,exportCfg:y}=C;if(!A?.length)return;if(i){const b=A.findIndex(c=>c.key==="_cta");b!==-1&&(A[b]={key:"_cta",title:"",render:(c,g)=>{const r=document.createElement("button");if(r.className=`btn btn-${i.style||"primary"} dm-cta-trigger`,r.dataset.action=i.action||"",i.scope&&(r.dataset.scope=i.scope),r.dataset.entry=g._entryId||"",i.confirm&&(r.dataset.confirm=i.confirm),i.icon){const o=document.createElement("span");o.dataset.icon=i.icon,r.appendChild(o),r.appendChild(document.createTextNode(" "))}return r.appendChild(document.createTextNode(i.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:h,columns:A,search:n,sortable:t,exportable:s,pageSize:a,emptyMessage:e}),i&&q(m),_._dmTable={columns:A,rows:h,slug:p,exportCfg:y,render(b){T.create("#"+u,{data:b,columns:A,search:n,sortable:t,exportable:s,pageSize:a,emptyMessage:e}),i&&q(m)}}})})(),(function(){const f=document.querySelectorAll("[data-form-inline]");if(!f.length||typeof F>"u")return;function _(t){const s=t&&t.logic;return!!(s&&(s.visibility||s.requirement))}function C(t,s){const a={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const i=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":i?"chooser":e.type,y={...e.formConfig||{}};y.span==="full"&&s&&(y.span=s);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),i&&(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)),a[e.name]=u}),a}function A(t){const s={};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&&(s[a.name]=typeof e=="object"?e.value:e)}}),s}function h(t,s){(s||[]).forEach(a=>{if(a.type!=="date"||!a.name)return;const e=t.querySelector(`[name="${a.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function n(t,s,a){let e=t.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",t.appendChild(e)),e.textContent=s,e.style.cssText=a?"color:var(--dm-danger);margin-top:.75rem;":"color:var(--dm-success);margin-top:.75rem;"}f.forEach(t=>{let s;try{s=JSON.parse(atob(t.dataset.formInline))}catch{return}const a=s.fields||[],e=s.settings||{},i=Number(e.columns)===3?3:2,p=e.layout||"grid",y=a.some(r=>r.type==="page-break"),u=async r=>{if(t.dataset.triggerBlocked==="true"){const o=t._formLogicRuntime,d=o&&o.submitBlock?o.submitBlock().reasons:[];n(t,d.join(" ")||"This form cannot be submitted yet.",!0);const l=new Error("Blocked by a form trigger");throw l.formSubmitFailed=!0,l}try{const o=t.querySelector(".fb-form-honeypot"),d=o?.querySelector('input[type="text"]')||null,l=o?.querySelector('[name="_t"]')||null,w=Object.assign({},r);delete w._t,o?.dataset.hpName&&delete w[o.dataset.hpName],d!==null&&(w._hp=d.value),l!==null&&(w._t=l.value);const v=await H.post(`/api/forms/submit/${s.slug}`,w);if(v&&v.error){n(t,v.error,!0);const D=new Error(v.error);throw D.formSubmitFailed=!0,D}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(o){throw o.formSubmitFailed||n(t,o.message||"Submission failed. Please try again.",!0),o}},m=Date.now();function b(r){const o=r.querySelector("form");if(!o||o.querySelector(".fb-form-honeypot"))return;const d=document.createElement("div");d.className="fb-form-honeypot",d.setAttribute("aria-hidden","true");const l=document.createElement("input");l.name=o.querySelector('[name="website"]')?"_website":"website",d.dataset.hpName=l.name,l.type="text",l.tabIndex=-1,l.autocomplete="url",l.placeholder="https://",d.appendChild(l);const w=document.createElement("input");w.name="_t",w.type="hidden",w.value=m,d.appendChild(w),o.appendChild(d)}function c(r){if(!window.FormLogicEngine||!a.some(d=>d.logic||Array.isArray(d.triggers)&&d.triggers.length))return null;const o=new window.FormLogicEngine.FormLogicRuntime(s,t,r?{baseValues:r}:void 0);if(o.init(),t._formLogicRuntime=o,t._formLogicObserved)return o;if(t._formLogicObserved=!0,t.parentNode&&typeof MutationObserver<"u"){const d=new MutationObserver(function(l){for(const w of l)for(const v of w.removedNodes)if(v===t||v.nodeType===1&&v.contains&&v.contains(t)){t._formLogicRuntime&&t._formLogicRuntime.destroy(),d.disconnect();return}});d.observe(t.parentNode,{childList:!0,subtree:!1}),t._formLogicObserver=d}return o}let g=!1;if(t.addEventListener("dm-form-end",r=>{const o=r.detail?.validation;if(o&&!o.valid){V(t,o.missing),J(o);return}g||(g=!0,u(r.detail?.values||{}).catch(()=>{}).finally(()=>{g=!1}))}),y&&F.wizard){const r=Z(a,s.title),o=r.map(k=>({title:k.title,description:k.description,fields:C(k.fields,i)})),d=r.map(k=>k.fields);let l=0,w=null,v=null;const D=k=>{const L=window.FormLogicEngine;if(!L||!L.validateFields)return;const N=L.validateFields(t,d[l]||[]);N.valid||(k.preventDefault(),k.stopImmediatePropagation(),V(t,N.missing),J(N))},M=()=>{const k=d[l]||a;te(t,e),W(t,e,k),h(t,k),P(t,k),e.honeypot!==!1&&b(t),w&&w.destroy(),w=c(v?.getStepData?v.getStepData():void 0),t.querySelectorAll(".wizard-next, .wizard-submit").forEach(L=>L.addEventListener("click",D,!0))};v=F.wizard(t,{schema:{steps:o},layout:p,columns:i,finishText:e.submitText||"Finish",onSubmit:u,onStepChange:k=>{l=k,setTimeout(M,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>=o.length||L!==l&&v.goToStep(L)}),Promise.resolve(v).then(M)}else if(F.render){const r=F.render(t,C(a,i),A(a),{submitText:e.submitText||"Submit",layout:p,columns:i,onSubmit:u});Promise.resolve(r).then(function(){ee(t,e,p),W(t,e,a),h(t,a),P(t,a),e.honeypot!==!1&&b(t),c()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();
1
+ import{decodeBase64Json as N}from"/public/js/utf8-base64.mjs";import{makeBadgeEl as B,applyColour as j,applyPill as G,applyFloat as Y,applyHighlight as O,applyItemHighlight as U,itemHighlightAttrs as K,isCurrentItem as R,menuAlign as Q,applyClasses as I,cssClasses as P}from"/public/js/menu-decor.mjs?v=20261009-hover-darken";import{initFloatingMenus as Z}from"/public/js/menu-float.js?v=20260829-float-panel";import{enhancePickerFields as V,markInvalidFields as W,showValidationPopup as J}from"/public/js/forms.js?v=20261009-form-classes";import{buildWizardSteps as ee}from"/public/js/form-steps.mjs";import{applyFormClasses as X,applySubmitButtons as te,applyWizardButtons as ae}from"/public/js/form-submit.mjs";$(()=>{const x=window.__CMS_NAV__||{},f=window.__CMS_SITE__||{};if(f.autoTheme?.enabled){let t=function(e){const i=(e||"07:00").split(":");return+i[0]*60+(+i[1]||0)},s=function(){const e=new Date,i=e.getHours()*60+e.getMinutes();return i>=t(n.dayStart)&&i<t(n.nightStart)?n.dayTheme:n.nightTheme},o=function(e){const i=n.apply&&n.apply[e]||{theme:e,custom:""};Domma.theme.set(i.theme);for(const p of[...document.body.classList])p.startsWith("dm-custom-")&&document.body.classList.remove(p);i.custom&&document.body.classList.add(i.custom)};const n=f.autoTheme;o(s()),setInterval(()=>o(s()),6e4)}const _=$("#site-navbar");if(_.length&&x.brand){let s=function(c,g=0){return Array.isArray(c)?c.filter(r=>!(r&&r.type==="spacer")).filter(r=>!(r&&r.type==="separator"&&g===0)).map(r=>r&&r.type==="separator"?{divider:!0}:Array.isArray(r?.items)?{...r,items:s(r.items,g+1)}:r):c},o=function(c){return Array.isArray(c)?c.filter(g=>!(g&&(g.type==="separator"||g.type==="spacer"))):[]};const n={...x.brand},t=n.size&&n.size!=="md"?` navbar-brand-${n.size}`:"";if(n.logo||n.icon||n.tagline){let c="";n.logo?c+=`<img src="${n.logo}" class="navbar-brand-logo" alt="${n.text||""}">`:n.icon&&(c+=`<span data-icon="${n.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),n.text&&(c+=`<span class="navbar-brand-text${t}">${n.text}</span>`),n.tagline&&(c+=`<small class="navbar-brand-tagline">${n.tagline}</small>`),n.html=c}else t&&n.text&&(n.html=`<span class="navbar-brand-text${t}">${n.text}</span>`);const e=x.position==="floating",i=x.orientation==="vertical";Domma.elements.navbar("#site-navbar",{brand:n,items:s(x.items||[]),variant:x.variant||"dark",position:e?"static":x.position||"sticky",collapsible:!0,appearOnHover:!!x.appearOnHover}),Domma.icons.scan("#site-navbar");const p=_.get(0);p.classList.add(`dm-nav-align-${Q(x)||"centre"}`),i&&_.get(0).classList.add("dm-nav-vertical",x.side==="right"?"dm-nav-vertical-right":"dm-nav-vertical-left"),O(p,x),I(p,x.class),e&&(Y(_.get(0),x),Z(_.get(0).parentNode||document));const y=p.querySelector(".navbar-toggle"),u=p.querySelector(".navbar-collapse");if(y&&u){let c=null;const g=()=>{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",g),document.body.appendChild(c),document.body.classList.add("dm-drawer-open"))};y.addEventListener("click",()=>{requestAnimationFrame(()=>{u.classList.contains("show")?a():r()})}),u.addEventListener("click",d=>{d.target.closest("a")&&requestAnimationFrame(r)}),document.addEventListener("keydown",d=>{d.key==="Escape"&&u.classList.contains("show")&&g()})}const m=p.querySelector(".navbar-nav");if(m){const c=Array.from(m.children);let g=0;for(const r of x.items||[])if(!(r&&r.type==="separator")){if(r&&r.type==="spacer"){const a=document.createElement("li"),d=["xs","sm","md","lg","xl","flex"].includes(r.size)?r.size:"md";a.className=`dm-menu-spacer dm-menu-spacer--${d}`,a.setAttribute("aria-hidden","true"),m.insertBefore(a,c[g]||null);continue}g++}}const b=o(x.items||[]);p.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(c=>{const g=b[parseInt(c.dataset.index,10)];if(!g)return;const r=c.dataset.subindex!=null,a=r&&Array.isArray(g.items)?g.items[parseInt(c.dataset.subindex,10)]:g;if(!a)return;a.colour&&j(c,a.colour),a.pill&&!r&&G(c,a.pill),U(c,a),I(c.closest("li"),a.class);const d=B(a.badge);d&&c.appendChild(d),R(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;")},i=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 d=i(a.url||"#"),l=e(a.text||""),w=Array.isArray(a.items)&&a.items.length?`<ul>${a.items.map(p).join("")}</ul>`:"",v=K(a),D=[v.hover&&` data-menu-item-hover="${i(v.hover)}"`,v.active&&` data-menu-item-active="${i(v.active)}"`].filter(Boolean).join(""),M=v.accent?` style="--dm-menu-highlight:${i(v.accent)}"`:"",k=P(a.class).join(" ");return`<li${k?` class="${i(k)}"`:""}><a href="${d}"${D}${M}>${l}</a>${w}</li>`},y=function(a,d){return(a||[]).forEach(l=>{l&&(l.type==="separator"||l.type==="spacer")||(d.push(l),Array.isArray(l.items)&&l.items.length&&y(l.items,d))}),d};const n=f.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 s='<div class="footer-inner container">';const o=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(o.copyright&&(s+=`<p>${e(o.copyright)}</p>`),o.primary.length&&(s+=`<nav class="footer-nav footer-nav--primary"><ul>${o.primary.map(p).join("")}</ul></nav>`),o.legal.length&&(s+=`<nav class="footer-nav footer-nav--legal"><ul>${o.legal.map(p).join("")}</ul></nav>`),f.footer){const a=Object.keys(t).filter(l=>n[l]),d=P(f.footer.socialClass).join(" ");a.length&&(s+='<div class="footer-social">',a.forEach(l=>{const{label:w,svg:v}=t[l];s+=`<a href="${n[l]}" target="_blank" rel="noopener noreferrer" aria-label="${w}" class="footer-social-link${d?` ${i(d)}`:""}">${v}</a>`}),s+="</div>")}s+="</div>",C.html(s),I(C.get(0),f.footer?.class);const u=[...y(o.primary,[]),...y(o.legal,[])];C.get(0).querySelectorAll(".footer-nav a").forEach((a,d)=>{const l=u[d];if(!l)return;l.colour&&j(a,l.colour);const w=B(l.badge);w&&a.appendChild(w),R(l.url,location.pathname)&&(a.classList.add("is-active"),a.setAttribute("aria-current","page"))}),O(C.get(0).querySelector(".footer-nav--primary"),{highlight:o.primaryHighlight}),O(C.get(0).querySelector(".footer-nav--legal"),{highlight:o.legalHighlight}),I(C.get(0).querySelector(".footer-nav--primary"),o.primaryClass),I(C.get(0).querySelector(".footer-nav--legal"),o.legalClass);const b=f.footer?.showMotionToggle!==!1,c=S.get("reduced_motion"),g=c!==null?!!c:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),r=C.get(0).querySelector(".footer-inner");if(b&&r){const a=document.createElement("input");a.type="checkbox",a.className="form-switch-input",a.id="dm-motion-switch",a.checked=g,a.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const d=document.createElement("span");d.className="form-switch-label",d.textContent="Reduce motion";const l=document.createElement("label");l.className="form-switch footer-motion-switch",l.title="Reduce motion",l.appendChild(a),l.appendChild(d),r.appendChild(l)}}$("#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 h=$(".page-body");if(h.length){h.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),h.find(".tabs").each(function(){Domma.elements.tabs(this)}),h.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)}),h.find(".dm-countdown").each(function(){const n={autoStart:!0},t=String(this.dataset.format||"").trim(),s=String(this.dataset.to||"").trim(),o=/^(\d{4})-(\d{2})-(\d{2})$/.exec(s),e=o?new Date(+o[1],+o[2]-1,+o[3]).getTime():s?new Date(s).getTime():NaN;Number.isFinite(e)?n.duration=Math.max(0,e-Date.now()):this.dataset.duration&&(n.duration=(parseInt(this.dataset.duration,10)||0)*1e3);const i=/^dd:/i.test(t);n.format=i?"hh:mm:ss":t||(n.duration>=36e5?"hh:mm:ss":"mm:ss");const p=this,y=m=>String(m).padStart(2,"0"),u=m=>{const b=Math.max(0,Math.floor(m/1e3)),c=p.querySelector(".dm-timer-time");c&&(c.textContent=`${y(Math.floor(b/86400))}:${y(Math.floor(b%86400/3600))}:${y(Math.floor(b%3600/60))}:${y(b%60)}`)};i&&(n.onTick=u),Domma.elements.timer(this,n),i&&u(n.duration||0)}),h.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),h.find(".dm-progression").each(function(){const n=Array.from(this.querySelectorAll(".dm-progression-item")).map(o=>{const e={title:o.querySelector(".dm-progression-item-title")?.textContent?.trim()||"",description:o.querySelector(".dm-progression-item-body")?.innerHTML?.trim()||""};return o.dataset.date&&(e.date=o.dataset.date),o.dataset.status&&(e.status=o.dataset.status),o.dataset.entryId&&(e._entryId=o.dataset.entryId),e}),t=this.dataset.layout==="centred"?"centered":this.dataset.layout||"vertical",s=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:s,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",s=n.dataset.revealAnimation||"slide-up",o=parseInt(n.dataset.revealDuration,10)||400,e=parseInt(n.dataset.revealStagger,10)||60,i=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,b)=>{m.style.opacity="0",m.style.transform=u[s]||"",m.style.transition=`opacity ${o}ms ease, transform ${o}ms ease`;const c=p?y.length-1-b:b;m.style.transitionDelay=t==="stagger"?`${i+c*e}ms`:`${i}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const m=new IntersectionObserver(b=>{b.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(b=>m.observe(b))}))}),h.find(".card[data-collapsible]").each(function(){const n=this.querySelector(".card-header");n&&n.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),h.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const n=this.dataset.soTarget,t=document.getElementById(n);if(!t)return;const s=E.slideover({title:t.dataset.soTitle||"",size:t.dataset.soSize||"md",position:t.dataset.soPosition||"right"});t.style.display="",s.setContent(t),s.open()})})}if(typeof $.setup=="function"){const n=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(t=>{try{const s=N(t.dataset.config);Object.assign(n,s)}catch{}}),Object.keys(n).length>0){const t={};for(const[s,o]of Object.entries(n)){const e=o?.events?.click,{confirm:i,toast:p,alert:y,prompt:u,...m}=e||{};i||p||y||u?($(s).on("click",async function(c){if(c.preventDefault(),i&&!await E.confirm(i))return;let g=null;if(!(u&&(g=await E.prompt(u,{inputPlaceholder:m.promptPlaceholder||"",inputValue:m.promptDefault||""}),g===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),g!==null&&(m.setText&&r.text(g),m.setVal&&r.val(g),m.setAttr&&r.attr(m.setAttr,g))}m.href&&(window.location.href=m.href),p&&E.toast(p,{type:m.toastType||"success"}),y&&E.alert(y)}}),Object.keys(m).length&&(t[s]={...o,events:{...o.events,click:m}})):t[s]=o}$.setup(t)}}h.length&&q(h.get(0))}),window.wireCTAButtons=q;function q(x){x.querySelectorAll(".dm-cta-trigger").forEach(f=>{f.__ctaWired||(f.__ctaWired=!0,f.addEventListener("click",async()=>{const _=f.dataset.action,C=f.dataset.entry,A=f.dataset.confirm;let h=S.get("auth_token");if(!h){E.toast("Please log in to perform this action.",{type:"warning"});return}if(A&&!await E.confirm(A))return;const n=Array.from(f.childNodes).map(s=>s.cloneNode(!0));f.disabled=!0,f.textContent="Running\u2026";const t=s=>fetch(`/api/actions/${_}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify(f.dataset.scope?{entryId:C,scope:f.dataset.scope}:{entryId:C})});try{let s=await t(h);if(s.status===401){const e=S.get("auth_refresh_token");if(e){const i=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(i.ok){const{token:p}=await i.json();S.set("auth_token",p),h=p,s=await t(h)}}}const o=await s.json().catch(()=>({}));if(!s.ok)throw new Error(o.error||o.message||`Error ${s.status}`);E.toast(o.message||"Action completed.",{type:"success"})}catch(s){E.toast(s.message||"Action failed.",{type:"error"})}finally{f.disabled=!1,f.textContent="",n.forEach(s=>f.appendChild(s)),Domma.icons.scan(f)}}))})}(function(){const f=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!f.length)return;const _=S.get("auth_token");function C(h,n){const t=document.createElement("div");t.className="dm-collection-display dm-collection-empty";const s=document.createElement("p");if(n){s.appendChild(document.createTextNode("Please "));const o=document.createElement("a");o.href="/login",o.textContent="sign in",s.appendChild(o),s.appendChild(document.createTextNode(" to view your entries."))}else s.textContent=h;return t.appendChild(s),t}function A(h,n){const t=document.createElement("template");t.innerHTML=n||"",h.replaceWith(t.content)}f.forEach(async h=>{const n=h.dataset.collectionAttrs;if(!n)return;if(!_){h.replaceChildren(C("",!0));return}const t=s=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({attrs:n})});try{let s=_,o=await t(s);if(o.status===401){const i=S.get("auth_refresh_token");if(i){const p=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:i})});if(p.ok){const{token:y}=await p.json();S.set("auth_token",y),s=y,o=await t(s)}}}if(!o.ok){const{error:i}=await o.json().catch(()=>({}));h.replaceChildren(C(i||"Unable to load entries.",!1));return}const{html:e}=await o.json();A(h,e),window.DommaCollectionBrowser?.hydrate&&window.DommaCollectionBrowser.hydrate(document)}catch{h.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(),q(document.body)},100)})(),(function(){const f=document.querySelectorAll("[data-collection-table]");!f.length||typeof T>"u"||!T.create||f.forEach(_=>{let C;try{C=N(_.dataset.payload)}catch{return}const{columns:A,rows:h,search:n,sortable:t,exportable:s,pageSize:o,empty:e,ctaConfig:i,slug:p,exportCfg:y}=C;if(!A?.length)return;if(i){const b=A.findIndex(c=>c.key==="_cta");b!==-1&&(A[b]={key:"_cta",title:"",render:(c,g)=>{const r=document.createElement("button");if(r.className=`btn btn-${i.style||"primary"} dm-cta-trigger`,r.dataset.action=i.action||"",i.scope&&(r.dataset.scope=i.scope),r.dataset.entry=g._entryId||"",i.confirm&&(r.dataset.confirm=i.confirm),i.icon){const a=document.createElement("span");a.dataset.icon=i.icon,r.appendChild(a),r.appendChild(document.createTextNode(" "))}return r.appendChild(document.createTextNode(i.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:h,columns:A,search:n,sortable:t,exportable:s,pageSize:o,emptyMessage:e}),i&&q(m),_._dmTable={columns:A,rows:h,slug:p,exportCfg:y,render(b){T.create("#"+u,{data:b,columns:A,search:n,sortable:t,exportable:s,pageSize:o,emptyMessage:e}),i&&q(m)}}})})(),(function(){const f=document.querySelectorAll("[data-form-inline]");if(!f.length||typeof F>"u")return;function _(t){const s=t&&t.logic;return!!(s&&(s.visibility||s.requirement))}function C(t,s){const o={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const i=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":i?"chooser":e.type,y={...e.formConfig||{}};y.span==="full"&&s&&(y.span=s);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),i&&(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)),o[e.name]=u}),o}function A(t){const s={};return(t||[]).forEach(o=>{if(!(!o.name||o.type==="page-break"||o.type==="spacer")&&(o.type==="select"||o.type==="multiselect")&&o.required){const e=(o.options||[])[0];e&&(s[o.name]=typeof e=="object"?e.value:e)}}),s}function h(t,s){(s||[]).forEach(o=>{if(o.type!=="date"||!o.name)return;const e=t.querySelector(`[name="${o.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function n(t,s,o){let e=t.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",t.appendChild(e)),e.textContent=s,e.style.cssText=o?"color:var(--dm-danger);margin-top:.75rem;":"color:var(--dm-success);margin-top:.75rem;"}f.forEach(t=>{let s;try{s=N(t.dataset.formInline)}catch{return}const o=s.fields||[],e=s.settings||{},i=Number(e.columns)===3?3:2,p=e.layout||"grid",y=o.some(r=>r.type==="page-break"),u=async r=>{if(t.dataset.triggerBlocked==="true"){const a=t._formLogicRuntime,d=a&&a.submitBlock?a.submitBlock().reasons:[];n(t,d.join(" ")||"This form cannot be submitted yet.",!0);const l=new Error("Blocked by a form trigger");throw l.formSubmitFailed=!0,l}try{const a=t.querySelector(".fb-form-honeypot"),d=a?.querySelector('input[type="text"]')||null,l=a?.querySelector('[name="_t"]')||null,w=Object.assign({},r);delete w._t,a?.dataset.hpName&&delete w[a.dataset.hpName],d!==null&&(w._hp=d.value),l!==null&&(w._t=l.value);const v=await H.post(`/api/forms/submit/${s.slug}`,w);if(v&&v.error){n(t,v.error,!0);const D=new Error(v.error);throw D.formSubmitFailed=!0,D}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 b(r){const a=r.querySelector("form");if(!a||a.querySelector(".fb-form-honeypot"))return;const d=document.createElement("div");d.className="fb-form-honeypot",d.setAttribute("aria-hidden","true");const l=document.createElement("input");l.name=a.querySelector('[name="website"]')?"_website":"website",d.dataset.hpName=l.name,l.type="text",l.tabIndex=-1,l.autocomplete="url",l.placeholder="https://",d.appendChild(l);const w=document.createElement("input");w.name="_t",w.type="hidden",w.value=m,d.appendChild(w),a.appendChild(d)}function c(r){if(!window.FormLogicEngine||!o.some(d=>d.logic||Array.isArray(d.triggers)&&d.triggers.length))return null;const a=new window.FormLogicEngine.FormLogicRuntime(s,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 d=new MutationObserver(function(l){for(const w of l)for(const v of w.removedNodes)if(v===t||v.nodeType===1&&v.contains&&v.contains(t)){t._formLogicRuntime&&t._formLogicRuntime.destroy(),d.disconnect();return}});d.observe(t.parentNode,{childList:!0,subtree:!1}),t._formLogicObserver=d}return a}let g=!1;if(t.addEventListener("dm-form-end",r=>{const a=r.detail?.validation;if(a&&!a.valid){W(t,a.missing),J(a);return}g||(g=!0,u(r.detail?.values||{}).catch(()=>{}).finally(()=>{g=!1}))}),y&&F.wizard){const r=ee(o,s.title),a=r.map(k=>({title:k.title,description:k.description,fields:C(k.fields,i)})),d=r.map(k=>k.fields);let l=0,w=null,v=null;const D=k=>{const L=window.FormLogicEngine;if(!L||!L.validateFields)return;const z=L.validateFields(t,d[l]||[]);z.valid||(k.preventDefault(),k.stopImmediatePropagation(),W(t,z.missing),J(z))},M=()=>{const k=d[l]||o;ae(t,e),X(t,e,k),h(t,k),V(t,k),e.honeypot!==!1&&b(t),w&&w.destroy(),w=c(v?.getStepData?v.getStepData():void 0),t.querySelectorAll(".wizard-next, .wizard-submit").forEach(L=>L.addEventListener("click",D,!0))};v=F.wizard(t,{schema:{steps:a},layout:p,columns:i,finishText:e.submitText||"Finish",onSubmit:u,onStepChange:k=>{l=k,setTimeout(M,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!==l&&v.goToStep(L)}),Promise.resolve(v).then(M)}else if(F.render){const r=F.render(t,C(o,i),A(o),{submitText:e.submitText||"Submit",layout:p,columns:i,onSubmit:u});Promise.resolve(r).then(function(){te(t,e,p),X(t,e,o),h(t,o),V(t,o),e.honeypot!==!1&&b(t),c()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();
@@ -0,0 +1 @@
1
+ export function decodeBase64Json(e){const t=Uint8Array.from(atob(e),o=>o.charCodeAt(0));return JSON.parse(new TextDecoder("utf-8",{fatal:!0}).decode(t))}
@@ -29,6 +29,7 @@
29
29
  * in step with its schema. Core has no need: seedAll() rewrites every preset's
30
30
  * schema.json on each boot, which is the same job done earlier.
31
31
  */
32
+ import {registerToolTrash, trashToolEntry} from '../../services/tool-trash.js';
32
33
  import {authenticate as defaultAuthenticate, requirePermission as defaultRequirePermission} from '../../middleware/auth.js';
33
34
  import {createEntry, deleteEntry, getEntry, listEntries, updateEntry} from '../../services/collections.js';
34
35
  import {hooks} from '../../services/hooks.js';
@@ -248,6 +249,8 @@ export async function contactsRoutes(fastify, opts = {}) {
248
249
  const requirePermission = opts.requirePermission || defaultRequirePermission;
249
250
  const holdsManageGroups = opts.holdsManageGroups || defaultHoldsManageGroups;
250
251
 
252
+ registerToolTrash(fastify, {slug: CONTACTS_SLUG, resource: 'contacts', itemPath: '/contacts', authenticate, requirePermission, project: toContact, onPurge: entry => hooks.emit('contact:deleted', {id: entry.id, userId: entry.data.userId})});
253
+
251
254
  function userId(request) {
252
255
  return request.user?.id ?? request.user?.sub ?? null;
253
256
  }
@@ -384,18 +387,14 @@ export async function contactsRoutes(fastify, opts = {}) {
384
387
  return reply.code(400).send({ error: 'ids array is required' });
385
388
  }
386
389
 
387
- let deleted = 0;
388
- for (const id of ids) {
389
- const entry = await getEntry(CONTACTS_SLUG, id).catch(() => null);
390
- if (!entry) continue;
391
- if (uid && entry.data.userId !== uid) continue;
392
- await deleteEntry(CONTACTS_SLUG, id).catch(() => {
393
- });
394
- hooks.emit('contact:deleted', {id, userId: entry.data.userId ?? null});
395
- deleted++;
390
+ const deletedIds = [], failedIds = [];
391
+ for (const id of [...new Set(ids)]) {
392
+ const entry = await getEntry(CONTACTS_SLUG, id);
393
+ if (!entry || !uid || entry.data.userId !== uid) { failedIds.push(id); continue; }
394
+ try { await trashToolEntry(CONTACTS_SLUG, entry); deletedIds.push(id); }
395
+ catch { failedIds.push(id); }
396
396
  }
397
-
398
- return reply.send({ deleted });
397
+ return reply.send({deleted: deletedIds.length, deletedIds, failedIds});
399
398
  });
400
399
 
401
400
  /** DELETE /api/contacts/contacts/:id */
@@ -407,9 +406,8 @@ export async function contactsRoutes(fastify, opts = {}) {
407
406
  if (!entry) return reply.code(404).send({error: 'Contact not found'});
408
407
  if (uid && entry.data.userId !== uid) return reply.code(404).send({error: 'Contact not found'});
409
408
 
410
- await deleteEntry(CONTACTS_SLUG, id);
411
- // Anything pointing at it (Calendar's event links) can let go now (0.79).
412
- hooks.emit('contact:deleted', {id, userId: entry.data.userId ?? null});
409
+ await trashToolEntry(CONTACTS_SLUG, entry);
410
+ // Keep related links and Pro fields for restore.
413
411
  return reply.send({ ok: true });
414
412
  });
415
413
 
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Notes API - /api/notes
3
3
  *
4
- * Was the `notes` plugin until 0.67.0. The logic is unchanged; what moved is
5
- * where it lives and how it is gated.
4
+ * Was the `notes` plugin until 0.67.0. Personal entries now use recoverable Trash; normal reads omit trashed rows.
6
5
  *
7
6
  * **Per-user scope.** Entries carry a `userId` and every read is filtered to the
8
7
  * caller's own. The plugin made this configurable (`scope: 'user' | 'site'`) and
@@ -15,9 +14,10 @@
15
14
  * `setupPlugin()` read `schema.slug`, so the live collection has always been
16
15
  * called this, and existing entries are found only if it stays exactly so.
17
16
  */
17
+ import {registerToolTrash, trashToolEntry} from '../../services/tool-trash.js';
18
18
  import {ago, clip, plural} from '../../services/sidebarBadges.js';
19
19
  import {authenticate as defaultAuthenticate, requirePermission as defaultRequirePermission} from '../../middleware/auth.js';
20
- import {createEntry, deleteEntry, getEntry, listEntries, updateEntry} from '../../services/collections.js';
20
+ import {createEntry, getEntry, listEntries, updateEntry} from '../../services/collections.js';
21
21
  import {hooks} from '../../services/hooks.js';
22
22
 
23
23
  const SLUG = 'notes-notes';
@@ -45,6 +45,8 @@ export async function notesRoutes(fastify, opts = {}) {
45
45
  const requirePermission = opts.requirePermission || defaultRequirePermission;
46
46
 
47
47
 
48
+ registerToolTrash(fastify, {slug: SLUG, resource: 'notes', itemPath: '/items', authenticate, requirePermission, project: toNote, onPurge: entry => hooks.emit('note:deleted', {id: entry.id, userId: entry.data.userId})});
49
+
48
50
  function userId(request) {
49
51
  return request.user?.id ?? request.user?.sub ?? null;
50
52
  }
@@ -149,9 +151,8 @@ export async function notesRoutes(fastify, opts = {}) {
149
151
  if (!entry) return reply.code(404).send({error: 'Note not found'});
150
152
  if (uid && entry.data.userId !== uid) return reply.code(404).send({error: 'Note not found'});
151
153
 
152
- await deleteEntry(SLUG, id);
153
- // Anything pointing at it (Calendar's event links) can let go now (0.79).
154
- hooks.emit('note:deleted', {id, userId: entry.data.userId ?? null});
154
+ await trashToolEntry(SLUG, entry);
155
+ // Related links remain available if this note is restored.
155
156
  return reply.send({ success: true });
156
157
  });
157
158