domma-cms 1.6.10 → 1.6.12

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.
package/CLAUDE.md CHANGED
@@ -428,7 +428,7 @@ Keep `E.loader` for work in progress (a save, an upload), where there is no shap
428
428
 
429
429
  ## Key Gotchas
430
430
 
431
- 1. **Structural menu items**: `{type:'separator'}` and `{type:'spacer', size}` carry no text/url - every renderer (menuRender, site.js navbar+footer, sidebar-renderer, menu-editor flatten/nest) needs a branch, and anything zipping items against rendered DOM must skip them or the indices shift. Domma's navbar has no spacer concept, so site.js strips them pre-render and re-inserts `<li>`s afterwards **against a snapshot** of the children, not the live HTMLCollection. `size: 'flex'` needs a flex parent, so the renderers stamp `dm-menu--has-flex-spacer` / `dm-admin-sidebar--has-flex-spacer` rather than making every list flex.
431
+ 1. **Structural menu items**: `{type:'separator'}`, `{type:'spacer', size}` and `{type:'theme-switcher'}` carry no text/url - every renderer (menuRender, site.js navbar+footer, sidebar-renderer, menu-editor flatten/nest) needs a branch, and anything zipping items against rendered DOM must skip them or the indices shift. Domma's navbar has no spacer concept, so site.js strips them pre-render and re-inserts `<li>`s afterwards **against a snapshot** of the children, not the live HTMLCollection. `size: 'flex'` needs a flex parent, so the renderers stamp `dm-menu--has-flex-spacer` / `dm-admin-sidebar--has-flex-spacer` rather than making every list flex.
432
432
  2. **Menu item sub-items**: use `items` key, NOT `children` - Domma navbar and the `[menu]` shortcode both read `items`.
433
433
  3. **Domma collections**: use `.get(0)` NOT `[0]` to get native DOM elements.
434
434
  4. **`.html()` strips interactive elements**: `E.modal().setContent(x)` also strips HTML - use light DOM slot
@@ -1,9 +1,9 @@
1
1
  /*!
2
- * Domma Tools CSS v0.51.7
2
+ * Domma Tools CSS v0.51.8
3
3
  * Dynamic Object Manipulation & Modeling API
4
4
  * (c) 2026 Darryl Waterhouse & DCBW-IT
5
- * Built: 2026-10-09T12:01:14.388Z
6
- * Commit: a5d495a
5
+ * Built: 2026-10-10T22:55:51.593Z
6
+ * Commit: 2c578d6
7
7
  */
8
8
 
9
9
  /*!
@@ -1,9 +1,9 @@
1
1
  /*!
2
- * Domma Tools v0.51.7
2
+ * Domma Tools v0.51.8
3
3
  * Developer tools: Theme Roller & Page Roller
4
4
  * (c) 2026 Darryl Waterhouse & DCBW-IT
5
- * Built: 2026-10-09T12:01:02.448Z
6
- * Commit: a5d495a
5
+ * Built: 2026-10-10T22:55:41.156Z
6
+ * Commit: 2c578d6
7
7
  *
8
8
  * Requires: domma.min.js
9
9
  */
@@ -1 +1 @@
1
- export const CORE_HOTKEYS=Object.freeze({"#/":"d","#/pages":"p","#/media":"m","#/collections":"l","#/forms":"f","#/views":"v","#/users":"u","#/analytics":"a","#/notes":"n","#/todo":"t","#/contacts":"c","#/settings":","}),LEADER="g",SEQUENCE_MS=1500,FREQUENT_KEYS=9;export function normaliseHotkey(e){if(typeof e!="string")return null;const r=e.trim().toLowerCase();return r.length!==1||r===LEADER?null:/^[a-z,]$/.test(r)?r:null}export function hotkeyLabel(e){return`G then ${String(e).toUpperCase()}`}export function paletteKeyLabel(e=!1){return e?"\u2318K or \u2318D":"Ctrl+K or Ctrl+D"}export function sidebarTools(e){const r=[],o=new Set,n=(l,s)=>{for(const t of l||[]){if(!t||typeof t!="object"||t.hidden||t.frequent||t.type==="separator"||t.type==="spacer")continue;const i=String(t.text||"").replace(/\s*\(\d+\)\s*$/,"").trim(),c=typeof t.url=="string"?t.url:"";if(c.startsWith("#/")&&i&&!o.has(c)){o.add(c);const a=normaliseHotkey(t.hotkey),f=CORE_HOTKEYS[c]||null;r.push({url:c,text:i,icon:t.icon||null,path:s,wants:f||a,core:!!f})}Array.isArray(t.items)&&t.items.length&&n(t.items,i?[...s,i]:s)}};return n(e,[]),r}export function assignHotkeys(e){const r=sidebarTools(e),o=new Map,n=new Map,l=[],s=t=>{if(!t.wants)return;const i=o.get(t.wants);if(i){l.push({url:t.url,text:t.text,key:t.wants,takenBy:i.text});return}o.set(t.wants,t),n.set(t.url,t.wants)};return r.filter(t=>t.core).forEach(s),r.filter(t=>!t.core).forEach(s),{tools:r.map(t=>({...t,key:n.get(t.url)||null})),byKey:o,byUrl:n,conflicts:l}}export function frequentKeys(e){return((e||[]).find(o=>o&&o.frequent)?.items||[]).filter(o=>o&&typeof o.url=="string"&&o.url.startsWith("#/")).slice(0,FREQUENT_KEYS).map((o,n)=>({n:n+1,url:o.url,text:String(o.text||"")}))}export const CHIP_DELAY_MS=300;export function chipEntries(e,{max:r=14}={}){const o=(e?.frequent||[]).slice(0,r).map(s=>({n:s.n,text:s.text})),n=[...(e?.byKey||new Map).entries()].map(([s,t])=>({key:s,text:t.text})).sort((s,t)=>(s.key===","?1:0)-(t.key===","?1:0)||s.key.localeCompare(t.key)),l=Math.max(r-o.length,0);return{frequent:o,letters:n.slice(0,l),more:Math.max(n.length-l,0)}}export function createSequence(e){let r=null;const o=n=>r!==null&&n-r<=SEQUENCE_MS;return{pending:o,reset(){r=null},press(n,l){const s=typeof n=="string"&&n.length===1?n.toLowerCase():null;if(o(l)){if(r=null,!s)return{type:"miss"};const{byKey:t,frequent:i}=e();if(/^[1-9]$/.test(s)){const a=(i||[]).find(f=>f.n===Number(s));return a?{type:"go",url:a.url}:{type:"miss"}}const c=t?.get(s);return c?{type:"go",url:c.url}:{type:"miss"}}return r=null,s===LEADER?(r=l,{type:"start"}):null}}}export function matchTools(e,r,o=50){const n=String(r||"").trim().toLowerCase(),l=(t,i)=>(i.t.key?1:0)-(t.t.key?1:0)||t.t.path.length-i.t.path.length||t.at-i.at;if(!n)return e.map((t,i)=>({t,at:i})).sort(l).slice(0,o).map(t=>t.t);const s=[];return e.forEach((t,i)=>{const c=t.text.toLowerCase(),a=c.split(/[\s\-_/&]+/).filter(Boolean),f=a.map(p=>p[0]).join(""),y=t.path.join(" ").toLowerCase();let u=0;n.length===1&&t.key===n?u=120:c===n?u=110:c.startsWith(n)?u=100:a.some(p=>p.startsWith(n))?u=80:n.length>1&&f.startsWith(n)?u=70:c.includes(n)?u=50:y.includes(n)&&(u=20),u&&s.push({t,score:u,at:i})}),s.sort((t,i)=>i.score-t.score||l(t,i)),s.slice(0,o).map(t=>t.t)}export function isTypingTarget(e){if(!e||typeof e!="object")return!1;const r=String(e.tagName||"").toUpperCase();if(r==="TEXTAREA"||r==="SELECT")return!0;if(r==="INPUT"){const o=String(e.type||"text").toLowerCase();return!["button","submit","reset","checkbox","radio","range","color","file","image"].includes(o)}return e.isContentEditable?!0:!!(e.closest&&e.closest('[contenteditable=""], [contenteditable="true"], .cm-editor, .CodeMirror, .monaco-editor, .ProseMirror'))}export function isPaletteKey(e){if(!e||e.altKey||e.shiftKey||!(e.ctrlKey||e.metaKey)||e.ctrlKey&&e.metaKey)return!1;const r=String(e.key||"").toLowerCase();return r==="d"?!isTypingTarget(e.target):r==="k"?!g(e.target):!1}function g(e){return!e||typeof e!="object"?!1:e.isContentEditable?!0:!!(e.closest&&e.closest('[contenteditable=""], [contenteditable="true"], .cm-editor, .CodeMirror, .monaco-editor, .ProseMirror'))}
1
+ export const CORE_HOTKEYS=Object.freeze({"#/":"d","#/pages":"p","#/media":"m","#/collections":"l","#/forms":"f","#/views":"v","#/users":"u","#/analytics":"a","#/notes":"n","#/todo":"t","#/contacts":"c","#/settings":","}),LEADER="g",SEQUENCE_MS=1500,FREQUENT_KEYS=9;export function normaliseHotkey(e){if(typeof e!="string")return null;const r=e.trim().toLowerCase();return r.length!==1||r===LEADER?null:/^[a-z,]$/.test(r)?r:null}export function hotkeyLabel(e){return`G then ${String(e).toUpperCase()}`}export function paletteKeyLabel(e=!1){return e?"\u2318K or \u2318D":"Ctrl+K or Ctrl+D"}export function sidebarTools(e){const r=[],o=new Set,n=(c,s)=>{for(const t of c||[]){if(!t||typeof t!="object"||t.hidden||t.frequent||t.type==="separator"||t.type==="spacer"||t.type==="theme-switcher")continue;const i=String(t.text||"").replace(/\s*\(\d+\)\s*$/,"").trim(),l=typeof t.url=="string"?t.url:"";if(l.startsWith("#/")&&i&&!o.has(l)){o.add(l);const a=normaliseHotkey(t.hotkey),f=CORE_HOTKEYS[l]||null;r.push({url:l,text:i,icon:t.icon||null,path:s,wants:f||a,core:!!f})}Array.isArray(t.items)&&t.items.length&&n(t.items,i?[...s,i]:s)}};return n(e,[]),r}export function assignHotkeys(e){const r=sidebarTools(e),o=new Map,n=new Map,c=[],s=t=>{if(!t.wants)return;const i=o.get(t.wants);if(i){c.push({url:t.url,text:t.text,key:t.wants,takenBy:i.text});return}o.set(t.wants,t),n.set(t.url,t.wants)};return r.filter(t=>t.core).forEach(s),r.filter(t=>!t.core).forEach(s),{tools:r.map(t=>({...t,key:n.get(t.url)||null})),byKey:o,byUrl:n,conflicts:c}}export function frequentKeys(e){return((e||[]).find(o=>o&&o.frequent)?.items||[]).filter(o=>o&&typeof o.url=="string"&&o.url.startsWith("#/")).slice(0,FREQUENT_KEYS).map((o,n)=>({n:n+1,url:o.url,text:String(o.text||"")}))}export const CHIP_DELAY_MS=300;export function chipEntries(e,{max:r=14}={}){const o=(e?.frequent||[]).slice(0,r).map(s=>({n:s.n,text:s.text})),n=[...(e?.byKey||new Map).entries()].map(([s,t])=>({key:s,text:t.text})).sort((s,t)=>(s.key===","?1:0)-(t.key===","?1:0)||s.key.localeCompare(t.key)),c=Math.max(r-o.length,0);return{frequent:o,letters:n.slice(0,c),more:Math.max(n.length-c,0)}}export function createSequence(e){let r=null;const o=n=>r!==null&&n-r<=SEQUENCE_MS;return{pending:o,reset(){r=null},press(n,c){const s=typeof n=="string"&&n.length===1?n.toLowerCase():null;if(o(c)){if(r=null,!s)return{type:"miss"};const{byKey:t,frequent:i}=e();if(/^[1-9]$/.test(s)){const a=(i||[]).find(f=>f.n===Number(s));return a?{type:"go",url:a.url}:{type:"miss"}}const l=t?.get(s);return l?{type:"go",url:l.url}:{type:"miss"}}return r=null,s===LEADER?(r=c,{type:"start"}):null}}}export function matchTools(e,r,o=50){const n=String(r||"").trim().toLowerCase(),c=(t,i)=>(i.t.key?1:0)-(t.t.key?1:0)||t.t.path.length-i.t.path.length||t.at-i.at;if(!n)return e.map((t,i)=>({t,at:i})).sort(c).slice(0,o).map(t=>t.t);const s=[];return e.forEach((t,i)=>{const l=t.text.toLowerCase(),a=l.split(/[\s\-_/&]+/).filter(Boolean),f=a.map(p=>p[0]).join(""),y=t.path.join(" ").toLowerCase();let u=0;n.length===1&&t.key===n?u=120:l===n?u=110:l.startsWith(n)?u=100:a.some(p=>p.startsWith(n))?u=80:n.length>1&&f.startsWith(n)?u=70:l.includes(n)?u=50:y.includes(n)&&(u=20),u&&s.push({t,score:u,at:i})}),s.sort((t,i)=>i.score-t.score||c(t,i)),s.slice(0,o).map(t=>t.t)}export function isTypingTarget(e){if(!e||typeof e!="object")return!1;const r=String(e.tagName||"").toUpperCase();if(r==="TEXTAREA"||r==="SELECT")return!0;if(r==="INPUT"){const o=String(e.type||"text").toLowerCase();return!["button","submit","reset","checkbox","radio","range","color","file","image"].includes(o)}return e.isContentEditable?!0:!!(e.closest&&e.closest('[contenteditable=""], [contenteditable="true"], .cm-editor, .CodeMirror, .monaco-editor, .ProseMirror'))}export function isPaletteKey(e){if(!e||e.altKey||e.shiftKey||!(e.ctrlKey||e.metaKey)||e.ctrlKey&&e.metaKey)return!1;const r=String(e.key||"").toLowerCase();return r==="d"?!isTypingTarget(e.target):r==="k"?!m(e.target):!1}function m(e){return!e||typeof e!="object"?!1:e.isContentEditable?!0:!!(e.closest&&e.closest('[contenteditable=""], [contenteditable="true"], .cm-editor, .CodeMirror, .monaco-editor, .ProseMirror'))}
@@ -1,2 +1,2 @@
1
- const d=["separator","spacer"];export const isStructural=t=>d.includes(t?.type);export function siblingsOf(t,n){const e=t.find(i=>i.id===n);return e?t.filter(i=>i.parentId===e.parentId):[]}export function moveRow(t,n,e){const i=siblingsOf(t,n),r=i.findIndex(a=>a.id===n),s=r+(e<0?-1:1);if(r<0||s<0||s>=i.length)return!1;i[r].touched=!0;const o=t.indexOf(i[r]),l=t.indexOf(i[s]);return[t[o],t[l]]=[t[l],t[o]],!0}export function indentRow(t,n){const e=siblingsOf(t,n),i=e.findIndex(r=>r.id===n);return i<=0||isStructural(e[i-1])?!1:(e[i].parentId=e[i-1].id,e[i].touched=!0,!0)}export function outdentRow(t,n){const e=t.find(r=>r.id===n);if(!e||e.parentId==null)return!1;const i=t.find(r=>r.id===e.parentId);return e.parentId=i?i.parentId:null,e.touched=!0,!0}export function newRow(t,n,e=null){return t==="separator"?{id:n,parentId:e,type:"separator"}:t==="spacer"?{id:n,parentId:e,type:"spacer",size:"md"}:{id:n,parentId:e,text:"",url:"/",icon:"",visibility:"",permission:"",hidden:!1,bundled:!1}}export function insertBelow(t,n,e,i){const r=t.findIndex(o=>o.id===n);if(r===-1)return null;const s=newRow(e,i,t[r].parentId);return t.splice(r+1,0,s),s}export function addChild(t,n,e){const i=t.find(s=>s.id===n);if(!i||isStructural(i))return null;const r=newRow("item",e,i.id);return t.push(r),i.touched=!0,r}export function duplicateRow(t,n,e){const i=t.find(a=>a.id===n);if(!i||i.tool)return null;const r=new Map,s=[],o=(a,c)=>{if(a.tool)return;const u={...g(a),id:e(),parentId:c};delete u.tool,delete u.locked,u.touched=!0,r.set(a.id,u.id),s.push(u);for(const f of t.filter(p=>p.parentId===a.id))o(f,u.id)};if(o(i,i.parentId),!s.length)return null;s[0].text&&(s[0].text=`${s[0].text} (copy)`);const l=t.indexOf(i);return t.splice(l+1,0,s[0]),t.push(...s.slice(1)),s[0]}function g(t){return JSON.parse(JSON.stringify(t))}export function bindingFrom({slot:t="",projects:n=[],pagesText:e=""}={}){return t?{slot:t,projects:[...n],pages:String(e).split(`
2
- `).map(i=>i.trim()).filter(Boolean)}:null}export function badPagePatterns(t=[]){return t.filter(n=>!/^\/[^\s?#*]*(\/\*)?$|^\/\*$/.test(n))}const m=["fontFamily","fontSize","fontWeight","letterSpacing","iconSize"];export function menuFromForm(t={},n={},e=null,i=[],r={}){const s=Object.fromEntries(m.filter(a=>t[a]).map(a=>[a,t[a]])),o={};if(t.position==="floating"){t.floatAnchor&&(o.anchor=t.floatAnchor);for(const[a,c]of[["offsetX",t.floatOffsetX],["offsetY",t.floatOffsetY],["width",t.floatWidth],["radius",t.floatRadius],["opacity",t.floatOpacity]])c!==""&&c!=null&&Number.isFinite(Number(c))&&(o[a]=Number(c));t.floatShadow&&(o.shadow=t.floatShadow),t.floatAccent&&(o.accent=t.floatAccent),t.floatTitle&&(o.title=String(t.floatTitle).trim()),t.floatCollapsible&&(o.collapsible=!0),t.floatCollapsible&&t.floatCollapsed&&(o.collapsed=!0),t.floatMovable&&(o.movable=!0),t.floatResizable&&(o.resizable=!0)}const l={};return t.highlightHover&&(l.hover=t.highlightHover),t.highlightActive&&(l.active=t.highlightActive),t.highlightAccent&&(l.accent=t.highlightAccent),{slug:String(n.slug||"").trim(),name:String(n.name||"").trim(),description:String(n.description||"").trim(),...t.variant&&{variant:t.variant},...t.position&&{position:t.position},...t.orientation&&{orientation:t.orientation},...t.orientation==="vertical"&&t.side&&{side:t.side},...t.orientation!=="vertical"&&t.align&&{align:t.align},...Object.keys(o).length&&{float:o},...Object.keys(l).length&&{highlight:l},...t.appearOnHover&&{appearOnHover:!0},...t.class&&String(t.class).trim()&&{class:String(t.class).trim()},...Object.keys(s).length&&{style:s},binding:e,items:i,meta:{...r||{},project:n.project||null}}}export function menuProblems(t){const n=[];/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(t.slug||"")||n.push("The slug must be lowercase letters, numbers and hyphens, no hyphen at either end (Meta tab)."),t.name||n.push("The menu needs a name (Meta tab).");const e=countUnlabelled(t.items);if(e&&n.push(`${e} item${e===1?" has":"s have"} no label - every item needs one (Items tab).`),t.binding){const i=badPagePatterns(t.binding.pages);i.length&&n.push(`Not valid page patterns: ${i.join(", ")} (Binding tab).`)}return n}export function countUnlabelled(t=[]){let n=0;for(const e of t||[])!isStructural(e)&&!(e.ref===!0&&e.url)&&!String(e.text||"").trim()&&n++,n+=countUnlabelled(e.items);return n}export const snapshot=t=>JSON.stringify(t);export function depthOfItems(t){if(!Array.isArray(t)||!t.length)return 0;let n=0;for(const e of t){const i=Array.isArray(e.items)&&e.items.length?depthOfItems(e.items):0;i>n&&(n=i)}return n+1}export function locationRows(t=[],n={},e={},i=[],r={}){const s=Object.fromEntries(i.map(o=>[o.slug,o.name||o.slug]));return t.map(o=>{const l=n[o.slot]||"",a=l&&r[l]||0,c=!!l&&o.maxDepth!=null&&a>o.maxDepth,u=o.slot==="overlay";return{slot:o.slot,label:o.label||o.slot,source:o.source||"",description:o.description||"",menu:l,menuName:l?s[l]||`${l} (missing)`:"",missing:!!l&&!s[l],tooDeep:c,depthText:c?`Nested ${a} deep - this slot shows ${o.maxDepth}`:"",empty:!l,emptyText:l?"":u?"Optional":"Nothing mapped",changed:(n[o.slot]||"")!==(e[o.slot]||"")}})}export function locationsPayload(t={},n=[]){const e=new Set(n.map(i=>i.slot));return Object.fromEntries(Object.entries(t).filter(([i,r])=>r&&(!n.length||e.has(i))))}export function locationsSnapshot(t={}){return JSON.stringify(Object.keys(t).filter(n=>t[n]).sort().map(n=>[n,t[n]]))}
1
+ const d=["separator","spacer","theme-switcher"];export const isStructural=t=>d.includes(t?.type);export function siblingsOf(t,e){const n=t.find(i=>i.id===e);return n?t.filter(i=>i.parentId===n.parentId):[]}export function moveRow(t,e,n){const i=siblingsOf(t,e),r=i.findIndex(a=>a.id===e),s=r+(n<0?-1:1);if(r<0||s<0||s>=i.length)return!1;i[r].touched=!0;const o=t.indexOf(i[r]),l=t.indexOf(i[s]);return[t[o],t[l]]=[t[l],t[o]],!0}export function indentRow(t,e){const n=siblingsOf(t,e),i=n.findIndex(r=>r.id===e);return i<=0||isStructural(n[i-1])?!1:(n[i].parentId=n[i-1].id,n[i].touched=!0,!0)}export function outdentRow(t,e){const n=t.find(r=>r.id===e);if(!n||n.parentId==null)return!1;const i=t.find(r=>r.id===n.parentId);return n.parentId=i?i.parentId:null,n.touched=!0,!0}export function newRow(t,e,n=null){return t==="separator"?{id:e,parentId:n,type:"separator"}:t==="theme-switcher"?{id:e,parentId:n,type:"theme-switcher"}:t==="spacer"?{id:e,parentId:n,type:"spacer",size:"md"}:{id:e,parentId:n,text:"",url:"/",icon:"",visibility:"",permission:"",hidden:!1,bundled:!1}}export function insertBelow(t,e,n,i){const r=t.findIndex(o=>o.id===e);if(r===-1)return null;const s=newRow(n,i,t[r].parentId);return t.splice(r+1,0,s),s}export function addChild(t,e,n){const i=t.find(s=>s.id===e);if(!i||isStructural(i))return null;const r=newRow("item",n,i.id);return t.push(r),i.touched=!0,r}export function duplicateRow(t,e,n){const i=t.find(a=>a.id===e);if(!i||i.tool)return null;const r=new Map,s=[],o=(a,c)=>{if(a.tool)return;const u={...g(a),id:n(),parentId:c};delete u.tool,delete u.locked,u.touched=!0,r.set(a.id,u.id),s.push(u);for(const f of t.filter(p=>p.parentId===a.id))o(f,u.id)};if(o(i,i.parentId),!s.length)return null;s[0].text&&(s[0].text=`${s[0].text} (copy)`);const l=t.indexOf(i);return t.splice(l+1,0,s[0]),t.push(...s.slice(1)),s[0]}function g(t){return JSON.parse(JSON.stringify(t))}export function bindingFrom({slot:t="",projects:e=[],pagesText:n=""}={}){return t?{slot:t,projects:[...e],pages:String(n).split(`
2
+ `).map(i=>i.trim()).filter(Boolean)}:null}export function badPagePatterns(t=[]){return t.filter(e=>!/^\/[^\s?#*]*(\/\*)?$|^\/\*$/.test(e))}const m=["fontFamily","fontSize","fontWeight","letterSpacing","iconSize"];export function menuFromForm(t={},e={},n=null,i=[],r={}){const s=Object.fromEntries(m.filter(a=>t[a]).map(a=>[a,t[a]])),o={};if(t.position==="floating"){t.floatAnchor&&(o.anchor=t.floatAnchor);for(const[a,c]of[["offsetX",t.floatOffsetX],["offsetY",t.floatOffsetY],["width",t.floatWidth],["radius",t.floatRadius],["opacity",t.floatOpacity]])c!==""&&c!=null&&Number.isFinite(Number(c))&&(o[a]=Number(c));t.floatShadow&&(o.shadow=t.floatShadow),t.floatAccent&&(o.accent=t.floatAccent),t.floatTitle&&(o.title=String(t.floatTitle).trim()),t.floatCollapsible&&(o.collapsible=!0),t.floatCollapsible&&t.floatCollapsed&&(o.collapsed=!0),t.floatMovable&&(o.movable=!0),t.floatResizable&&(o.resizable=!0)}const l={};return t.highlightHover&&(l.hover=t.highlightHover),t.highlightActive&&(l.active=t.highlightActive),t.highlightAccent&&(l.accent=t.highlightAccent),{slug:String(e.slug||"").trim(),name:String(e.name||"").trim(),description:String(e.description||"").trim(),...t.variant&&{variant:t.variant},...t.position&&{position:t.position},...t.orientation&&{orientation:t.orientation},...t.orientation==="vertical"&&t.side&&{side:t.side},...t.orientation!=="vertical"&&t.align&&{align:t.align},...Object.keys(o).length&&{float:o},...Object.keys(l).length&&{highlight:l},...t.appearOnHover&&{appearOnHover:!0},...t.class&&String(t.class).trim()&&{class:String(t.class).trim()},...Object.keys(s).length&&{style:s},binding:n,items:i,meta:{...r||{},project:e.project||null}}}export function menuProblems(t){const e=[];/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(t.slug||"")||e.push("The slug must be lowercase letters, numbers and hyphens, no hyphen at either end (Meta tab)."),t.name||e.push("The menu needs a name (Meta tab).");const n=countUnlabelled(t.items);if(n&&e.push(`${n} item${n===1?" has":"s have"} no label - every item needs one (Items tab).`),t.binding){const i=badPagePatterns(t.binding.pages);i.length&&e.push(`Not valid page patterns: ${i.join(", ")} (Binding tab).`)}return e}export function countUnlabelled(t=[]){let e=0;for(const n of t||[])!isStructural(n)&&!(n.ref===!0&&n.url)&&!String(n.text||"").trim()&&e++,e+=countUnlabelled(n.items);return e}export const snapshot=t=>JSON.stringify(t);export function depthOfItems(t){if(!Array.isArray(t)||!t.length)return 0;let e=0;for(const n of t){const i=Array.isArray(n.items)&&n.items.length?depthOfItems(n.items):0;i>e&&(e=i)}return e+1}export function locationRows(t=[],e={},n={},i=[],r={}){const s=Object.fromEntries(i.map(o=>[o.slug,o.name||o.slug]));return t.map(o=>{const l=e[o.slot]||"",a=l&&r[l]||0,c=!!l&&o.maxDepth!=null&&a>o.maxDepth,u=o.slot==="overlay";return{slot:o.slot,label:o.label||o.slot,source:o.source||"",description:o.description||"",menu:l,menuName:l?s[l]||`${l} (missing)`:"",missing:!!l&&!s[l],tooDeep:c,depthText:c?`Nested ${a} deep - this slot shows ${o.maxDepth}`:"",empty:!l,emptyText:l?"":u?"Optional":"Nothing mapped",changed:(e[o.slot]||"")!==(n[o.slot]||"")}})}export function locationsPayload(t={},e=[]){const n=new Set(e.map(i=>i.slot));return Object.fromEntries(Object.entries(t).filter(([i,r])=>r&&(!e.length||n.has(i))))}export function locationsSnapshot(t={}){return JSON.stringify(Object.keys(t).filter(e=>t[e]).sort().map(e=>[e,t[e]]))}
@@ -1 +1 @@
1
- let d=1;export const uid=()=>"i_"+d++;export function flatten(g,l=null,e=[]){for(const i of g||[]){const o=uid();if(i&&i.type==="separator"){e.push({id:o,parentId:l,type:"separator"});continue}if(i&&i.type==="spacer"){e.push({id:o,parentId:l,type:"spacer",size:i.size||"md"});continue}e.push({id:o,parentId:l,text:i.text||"",url:i.url||"",icon:i.icon||"",visibility:i.visibility||"",permission:i.permission||"",hidden:!!i.hidden,bundled:!!i.bundled,badge:i.badge?{...i.badge}:null,pill:i.pill?{...i.pill}:null,colour:i.colour||"",highlight:i.highlight?{...i.highlight}:null,class:i.class||"",key:i.key||"",ref:!!i.ref,tool:i.$tool||null,touched:!1,synthesised:!!i.synthesised}),Array.isArray(i.items)&&i.items.length&&flatten(i.items,o,e)}return e}export function nest(g){const l=new Map;for(const t of g)l.has(t.parentId)||l.set(t.parentId,[]),l.get(t.parentId).push(t);function e(t){const n=[];let s=!1;const a=l.get(t)||[];for(let h=a.length-1;h>=0;h--){const c=a[h].tool?o(a[h],s):i(a[h]);c&&(n.unshift(c),s=!0)}return n}function i(t){if(t.type==="separator")return{type:"separator"};if(t.type==="spacer")return{type:"spacer",...t.size&&t.size!=="md"&&{size:t.size}};const n=e(t.id);return t.synthesised&&!n.length?null:{text:(t.text||"").trim(),url:(t.url||"").trim(),...t.icon&&{icon:t.icon.trim()},...t.visibility&&{visibility:t.visibility},...t.permission&&{permission:t.permission},...t.hidden&&{hidden:!0},...t.bundled&&{bundled:!0},...t.badge&&(t.badge.text||t.badge.countFrom)?{badge:{...t.badge.text&&{text:t.badge.text},...t.badge.countFrom&&{countFrom:t.badge.countFrom},...t.badge.variant&&{variant:t.badge.variant}}}:{},...t.pill&&t.pill.variant?{pill:{style:t.pill.style||"filled",variant:t.pill.variant}}:{},...t.colour&&{colour:t.colour},...t.class&&t.class.trim()&&{class:t.class.trim()},...t.key&&{key:t.key},...t.ref&&{ref:!0},...t.highlight&&(t.highlight.hover||t.highlight.active||t.highlight.accent)?{highlight:{...t.highlight.hover&&{hover:t.highlight.hover},...t.highlight.active&&{active:t.highlight.active},...t.highlight.accent&&{accent:t.highlight.accent}}}:{},...n.length&&{items:n}}}function o(t,n=!1){const s=e(t.id);if(!t.tool.persisted&&!t.touched&&!s.length&&!n)return null;const a=t.tool.defaults,h=(t.text||"").trim(),c=(t.icon||"").trim();return{url:(t.url||"").trim(),ref:!0,...h&&{text:h},...c&&c!==a.icon&&{icon:c},...t.visibility&&{visibility:t.visibility},...t.hidden&&{hidden:!0},...t.badge&&(t.badge.text||t.badge.countFrom)?{badge:{...t.badge.text&&{text:t.badge.text},...t.badge.countFrom&&{countFrom:t.badge.countFrom},...t.badge.variant&&{variant:t.badge.variant}}}:{},...t.colour&&{colour:t.colour},...t.class&&t.class.trim()&&{class:t.class.trim()},...t.highlight&&(t.highlight.hover||t.highlight.active||t.highlight.accent)?{highlight:{...t.highlight.hover&&{hover:t.highlight.hover},...t.highlight.active&&{active:t.highlight.active},...t.highlight.accent&&{accent:t.highlight.accent}}}:{},...s.length&&{items:s}}}return e(null)}
1
+ let u=1;export const uid=()=>"i_"+u++;export function flatten(g,l=null,e=[]){for(const i of g||[]){const s=uid();if(i&&i.type==="separator"){e.push({id:s,parentId:l,type:"separator"});continue}if(i&&i.type==="spacer"){e.push({id:s,parentId:l,type:"spacer",size:i.size||"md"});continue}if(i&&i.type==="theme-switcher"){e.push({id:s,parentId:l,type:"theme-switcher"});continue}e.push({id:s,parentId:l,text:i.text||"",url:i.url||"",icon:i.icon||"",visibility:i.visibility||"",permission:i.permission||"",hidden:!!i.hidden,bundled:!!i.bundled,badge:i.badge?{...i.badge}:null,pill:i.pill?{...i.pill}:null,colour:i.colour||"",highlight:i.highlight?{...i.highlight}:null,class:i.class||"",key:i.key||"",ref:!!i.ref,tool:i.$tool||null,touched:!1,synthesised:!!i.synthesised}),Array.isArray(i.items)&&i.items.length&&flatten(i.items,s,e)}return e}export function nest(g){const l=new Map;for(const t of g)l.has(t.parentId)||l.set(t.parentId,[]),l.get(t.parentId).push(t);function e(t){const h=[];let o=!1;const a=l.get(t)||[];for(let n=a.length-1;n>=0;n--){const c=a[n].tool?s(a[n],o):i(a[n]);c&&(h.unshift(c),o=!0)}return h}function i(t){if(t.type==="separator")return{type:"separator"};if(t.type==="spacer")return{type:"spacer",...t.size&&t.size!=="md"&&{size:t.size}};if(t.type==="theme-switcher")return{type:"theme-switcher"};const h=e(t.id);return t.synthesised&&!h.length?null:{text:(t.text||"").trim(),url:(t.url||"").trim(),...t.icon&&{icon:t.icon.trim()},...t.visibility&&{visibility:t.visibility},...t.permission&&{permission:t.permission},...t.hidden&&{hidden:!0},...t.bundled&&{bundled:!0},...t.badge&&(t.badge.text||t.badge.countFrom)?{badge:{...t.badge.text&&{text:t.badge.text},...t.badge.countFrom&&{countFrom:t.badge.countFrom},...t.badge.variant&&{variant:t.badge.variant}}}:{},...t.pill&&t.pill.variant?{pill:{style:t.pill.style||"filled",variant:t.pill.variant}}:{},...t.colour&&{colour:t.colour},...t.class&&t.class.trim()&&{class:t.class.trim()},...t.key&&{key:t.key},...t.ref&&{ref:!0},...t.highlight&&(t.highlight.hover||t.highlight.active||t.highlight.accent)?{highlight:{...t.highlight.hover&&{hover:t.highlight.hover},...t.highlight.active&&{active:t.highlight.active},...t.highlight.accent&&{accent:t.highlight.accent}}}:{},...h.length&&{items:h}}}function s(t,h=!1){const o=e(t.id);if(!t.tool.persisted&&!t.touched&&!o.length&&!h)return null;const a=t.tool.defaults,n=(t.text||"").trim(),c=(t.icon||"").trim();return{url:(t.url||"").trim(),ref:!0,...n&&{text:n},...c&&c!==a.icon&&{icon:c},...t.visibility&&{visibility:t.visibility},...t.hidden&&{hidden:!0},...t.badge&&(t.badge.text||t.badge.countFrom)?{badge:{...t.badge.text&&{text:t.badge.text},...t.badge.countFrom&&{countFrom:t.badge.countFrom},...t.badge.variant&&{variant:t.badge.variant}}}:{},...t.colour&&{colour:t.colour},...t.class&&t.class.trim()&&{class:t.class.trim()},...t.highlight&&(t.highlight.hover||t.highlight.active||t.highlight.accent)?{highlight:{...t.highlight.hover&&{hover:t.highlight.hover},...t.highlight.active&&{active:t.highlight.active},...t.highlight.accent&&{accent:t.highlight.accent}}}:{},...o.length&&{items:o}}}return e(null)}
@@ -1 +1 @@
1
- export function isWithin(e,s,p){let t=e.find(i=>i.id===s);for(;t;){if(t.id===p)return!0;t=t.parentId?e.find(i=>i.id===t.parentId):null}return!1}export function applyDrop(e,s,p,t){if(s===p)return!1;const i=e.find(n=>n.id===s),r=e.find(n=>n.id===p);if(!i||!r||isWithin(e,p,s))return!1;const o=r.type==="separator"||r.type==="spacer",c=t==="into"&&o?"below":t;if(e.splice(e.indexOf(i),1),c==="into"){i.parentId=r.id;let n=e.indexOf(r)+1;for(;n<e.length&&isWithin(e,e[n].id,r.id);)n++;e.splice(n,0,i)}else{i.parentId=r.parentId;const n=e.indexOf(r);if(c==="above")e.splice(n,0,i);else{let d=n+1;for(;d<e.length&&isWithin(e,e[d].id,r.id);)d++;e.splice(d,0,i)}}return!0}
1
+ export function isWithin(e,s,p){let t=e.find(i=>i.id===s);for(;t;){if(t.id===p)return!0;t=t.parentId?e.find(i=>i.id===t.parentId):null}return!1}export function applyDrop(e,s,p,t){if(s===p)return!1;const i=e.find(n=>n.id===s),r=e.find(n=>n.id===p);if(!i||!r||isWithin(e,p,s))return!1;const o=r.type==="separator"||r.type==="spacer"||r.type==="theme-switcher",c=t==="into"&&o?"below":t;if(e.splice(e.indexOf(i),1),c==="into"){i.parentId=r.id;let n=e.indexOf(r)+1;for(;n<e.length&&isWithin(e,e[n].id,r.id);)n++;e.splice(n,0,i)}else{i.parentId=r.parentId;const n=e.indexOf(r);if(c==="above")e.splice(n,0,i);else{let d=n+1;for(;d<e.length&&isWithin(e,e[d].id,r.id);)d++;e.splice(d,0,i)}}return!0}
@@ -44,4 +44,4 @@ Optional body content.
44
44
  [/table]`,attrs:[{name:"striped",type:"checkbox",label:"Striped"},{name:"bordered",type:"checkbox",label:"Bordered"},{name:"compact",type:"checkbox",label:"Compact"},{name:"caption",type:"text",label:"Caption"},{name:"class",type:"text",label:"CSS class"},{name:"id",type:"text",label:"ID"}]},{name:"badge",label:"Badge",icon:"badge",category:"Content",selfClosing:!1,container:!1,wrappable:!1,template:'[badge variant="success"]New[/badge]',inline:!0,attrs:[{name:"variant",type:"select",label:"Variant",options:[["primary","Primary"],["secondary","Secondary"],["success","Success"],["danger","Danger"],["warning","Warning"],["info","Info"],["light","Light"],["dark","Dark"]]},{name:"pill",type:"checkbox",label:"Pill"},{name:"outline",type:"checkbox",label:"Outline"},{name:"size",type:"select",label:"Size",options:[["","Default"],["small","Small"],["large","Large"]]}]},{name:"text",label:"Text",icon:"text-style",preview:"text",category:"Content",selfClosing:!1,container:!0,wrappable:!0,template:"[text]Styled text[/text]",inline:!0,attrs:[{name:"size",type:"select",label:"Size",options:[["xs","XS"],["sm","Small"],["base","Base"],["lg","Large"],["xl","XL"],["2xl","2XL"],["3xl","3XL"],["4xl","4XL"]]},{name:"weight",type:"select",label:"Weight",options:[["thin","Thin"],["light","Light"],["normal","Normal"],["medium","Medium"],["semibold","Semibold"],["bold","Bold"],["extrabold","Extra bold"],["black","Black"]]},{name:"point-size",type:"number",label:"Point size"},{name:"bold",type:"checkbox",label:"Bold"},{name:"italic",type:"checkbox",label:"Italic"},{name:"color",type:"colour",label:"Colour"},{name:"font",type:"select",label:"Font",options:[["Georgia","Georgia"],["Arial","Arial"],["Verdana","Verdana"],["Courier New","Courier New"],["Times New Roman","Times New Roman"],["Trebuchet MS","Trebuchet MS"]]},{name:"transform",type:"select",label:"Transform",options:[["upper","Uppercase"],["lower","Lowercase"],["capitalize","Capitalise"],["none","None"]]},{name:"decoration",type:"select",label:"Decoration",options:[["underline","Underline"],["line-through","Line through"],["none","None"]]},{name:"spacing",type:"select",label:"Letter spacing",options:[["tight","Tight"],["normal","Normal"],["wide","Wide"],["wider","Wider"]]},{name:"align",type:"select",label:"Align",options:[["","None"],["left","Left"],["center","Centre"],["right","Right"]]},{name:"style",type:"text",label:"Inline CSS"},{name:"class",type:"text",label:"CSS class"},{name:"id",type:"text",label:"ID"}]},{name:"button",label:"Button",icon:"btn-insert",category:"Content",selfClosing:!0,container:!1,wrappable:!1,template:'[button href="/contact" variant="primary" label="Get in touch" /]',attrs:[{name:"href",type:"url",label:"Link"},{name:"label",type:"text",label:"Label"},{name:"variant",type:"select",label:"Variant",options:[["primary","Primary"],["secondary","Secondary"],["success","Success"],["danger","Danger"],["warning","Warning"],["info","Info"],["outline","Outline"],["ghost","Ghost"],["link","Link"],["outline-success","Outline success"],["outline-danger","Outline danger"],["outline-warning","Outline warning"],["outline-info","Outline info"]]},{name:"size",type:"select",label:"Size",options:[["","Default"],["sm","Small"],["lg","Large"]]},{name:"icon",type:"icon",label:"Icon (before)"},{name:"icon-after",type:"icon",label:"Icon (after)"},{name:"target",type:"text",label:"Target"},{name:"class",type:"text",label:"CSS class"},{name:"id",type:"text",label:"ID"}]},{name:"link",label:"Link",icon:"link-shortcode",category:"Content",selfClosing:!0,container:!1,wrappable:!1,template:'[link href="/about" label="About us" /]',formats:[{value:"markdown",label:"Markdown link",emit:e=>Object.keys(e).some(t=>t!=="href"&&t!=="label"&&e[t])||!e.href?null:`[${e.label||e.href}](${e.href})`},{value:"shortcode",label:"Link shortcode"}],attrs:[{name:"href",type:"url",label:"Link"},{name:"label",type:"text",label:"Label"},{name:"target",type:"text",label:"Target"},{name:"class",type:"text",label:"CSS class"},{name:"icon",type:"icon",label:"Icon (before)"},{name:"icon-after",type:"icon",label:"Icon (after)"},{name:"id",type:"text",label:"ID"}]},{name:"listgroup",label:"List group",icon:"list",category:"Content",selfClosing:!1,container:!0,wrappable:!1,template:`[listgroup]
45
45
  [item]First item[/item]
46
46
  [item]Second item[/item]
47
- [/listgroup]`,children:{name:"item",min:1,max:20},attrs:[{name:"variant",type:"select",label:"Variant",options:[["","Default"],["flush","Flush"],["numbered","Numbered"],["horizontal","Horizontal"]]},{name:"size",type:"select",label:"Size",options:[["","Default"],["sm","Small"],["lg","Large"]]},{name:"itemvariant",type:"select",label:"Item variant",options:[["","Default"],["primary","Primary"],["secondary","Secondary"],["success","Success"],["danger","Danger"],["warning","Warning"],["info","Info"]]},{name:"action",type:"checkbox",label:"Actionable items"}]},{name:"collection",label:"Collection",icon:"database",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[collection slug="enquiries" display="table" /]',attrs:[{name:"slug",type:"lookup",source:"collections",label:"Collection"},{name:"display",type:"select",label:"Display",options:[["table","Table"],["cards","Cards"],["list","List"],["accordion","Accordion"],["timeline","Timeline"],["block","Block"]]},{name:"fields",type:"text",label:"Fields (comma-separated)"},{name:"limit",type:"number",label:"Limit"},{name:"sort",type:"lookup",source:"collection-fields",label:"Sort field"},{name:"order",type:"select",label:"Order",options:[["desc","Descending"],["asc","Ascending"]]},{name:"title-field",type:"lookup",source:"collection-fields",label:"Title field"},{name:"columns",type:"number",label:"Card columns"},{name:"search",type:"checkbox",label:"Search (table)",default:!0},{name:"sortable",type:"checkbox",label:"Sortable (table)",default:!0},{name:"exportable",type:"checkbox",label:"CSV export"},{name:"page-size",type:"number",label:"Page size"},{name:"empty",type:"text",label:"Empty message"},{name:"scope",type:"select",label:"Scope",options:[["","All"],["mine","Current user"]]},{name:"cta",type:"lookup",source:"actions",label:"CTA action"},{name:"cta-label",type:"text",label:"CTA label"},{name:"cta-icon",type:"icon",label:"CTA icon"},{name:"cta-style",type:"select",label:"CTA style",options:[["primary","Primary"],["secondary","Secondary"],["success","Success"],["danger","Danger"],["warning","Warning"],["info","Info"]]},{name:"cta-confirm",type:"text",label:"CTA confirm text"},{name:"class",type:"text",label:"CSS class"},{name:"id",type:"text",label:"ID"}]},{name:"view",label:"View",icon:"eye",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[view slug="products" display="table" limit="50" /]',attrs:[{name:"slug",type:"lookup",source:"views",label:"View"},{name:"display",type:"select",label:"Display",options:[["table","Table"],["cards","Cards"],["list","List"],["accordion","Accordion"],["timeline","Timeline"],["block","Block"]]},{name:"limit",type:"number",label:"Limit"},{name:"fields",type:"text",label:"Fields (comma-separated)"},{name:"title-field",type:"text",label:"Title field"},{name:"columns",type:"number",label:"Card columns"},{name:"empty",type:"text",label:"Empty message"}]},{name:"block",label:"Block",icon:"layout",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[block template="post-card" title="My Post" /]',attrs:[{name:"template",type:"lookup",source:"blocks",label:"Template"}]},{name:"form",label:"Form",icon:"file-text",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[form name="contact" /]',attrs:[{name:"name",type:"lookup",source:"forms",label:"Form"},{name:"class",type:"text",label:"CSS class"},{name:"id",type:"text",label:"ID"}]},{name:"cta",label:"CTA",icon:"mouse-pointer",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[cta action="send-welcome" entry="abc123" style="primary" label="Send" /]',attrs:[{name:"action",type:"lookup",source:"actions",label:"Action"},{name:"entry",type:"text",label:"Entry ID"},{name:"style",type:"select",label:"Style",options:[["primary","Primary"],["secondary","Secondary"],["success","Success"],["danger","Danger"],["warning","Warning"],["info","Info"]]},{name:"icon",type:"icon",label:"Icon"},{name:"size",type:"select",label:"Size",options:[["","Default"],["sm","Small"],["lg","Large"]]},{name:"confirm",type:"text",label:"Confirm text"},{name:"label",type:"text",label:"Label"}]},{name:"menu",label:"Menu",icon:"menu",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[menu slug="legal" /]',attrs:[{name:"slug",type:"lookup",source:"menus",label:"Menu"},{name:"location",type:"lookup",source:"menu-locations",label:"Location slot"},{name:"variant",type:"text",label:"Variant"},{name:"class",type:"text",label:"CSS class"},{name:"depth",type:"number",label:"Max depth"},{name:"orientation",type:"select",label:"Orientation",options:[["","Menu default"],["horizontal","Horizontal"],["vertical","Vertical"]]},{name:"float",type:"select",label:"Floating",options:[["","Menu default"],["no","Render inline here"]]}]},{name:"icon",label:"Icon",icon:"icon-pick",category:"Media",selfClosing:!0,container:!1,wrappable:!1,template:'[icon name="arrow-right" /]',attrs:[{name:"name",type:"icon",label:"Icon name"},{name:"size",type:"number",label:"Size (px)"},{name:"color",type:"colour",label:"Colour"},{name:"class",type:"text",label:"CSS class"}]},{name:"embed",label:"Embed",icon:"video",category:"Media",selfClosing:!0,container:!1,wrappable:!1,template:'[embed url="https://youtu.be/dQw4w9WgXcQ" /]',custom:"embed",attrs:[{name:"url",type:"url",label:"URL"},{name:"ratio",type:"select",label:"Aspect ratio",options:[["16:9","16:9"],["4:3","4:3"],["1:1","1:1"],["21:9","21:9"],["9:16","9:16"],["3:4","3:4"]]},{name:"title",type:"text",label:"Title / caption"},{name:"width",type:"text",label:"Width"},{name:"align",type:"select",label:"Alignment",options:[["left","Left"],["center","Centre"],["right","Right"]]},{name:"autoplay",type:"checkbox",label:"Autoplay"},{name:"loop",type:"checkbox",label:"Loop"},{name:"muted",type:"checkbox",label:"Muted"}]}],EDITOR_TOOLS=[{name:"image",label:"Image",icon:"image-add",category:"Media",custom:"image"},{name:"colourpick",label:"Colour picker",icon:"colour-picker",category:"Media",custom:"colourpick"},{name:"component",label:"Component",icon:"component",category:"Components",custom:"component"},{name:"scribe",label:"Scribe",icon:"scribe-insert",category:"Content",custom:"scribe"}];
47
+ [/listgroup]`,children:{name:"item",min:1,max:20},attrs:[{name:"variant",type:"select",label:"Variant",options:[["","Default"],["flush","Flush"],["numbered","Numbered"],["horizontal","Horizontal"]]},{name:"size",type:"select",label:"Size",options:[["","Default"],["sm","Small"],["lg","Large"]]},{name:"itemvariant",type:"select",label:"Item variant",options:[["","Default"],["primary","Primary"],["secondary","Secondary"],["success","Success"],["danger","Danger"],["warning","Warning"],["info","Info"]]},{name:"action",type:"checkbox",label:"Actionable items"}]},{name:"collection",label:"Collection",icon:"database",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[collection slug="enquiries" display="table" /]',attrs:[{name:"slug",type:"lookup",source:"collections",label:"Collection"},{name:"display",type:"select",label:"Display",options:[["table","Table"],["cards","Cards"],["list","List"],["accordion","Accordion"],["timeline","Timeline"],["block","Block"]]},{name:"fields",type:"text",label:"Fields (comma-separated)"},{name:"limit",type:"number",label:"Limit"},{name:"sort",type:"lookup",source:"collection-fields",label:"Sort field"},{name:"order",type:"select",label:"Order",options:[["desc","Descending"],["asc","Ascending"]]},{name:"title-field",type:"lookup",source:"collection-fields",label:"Title field"},{name:"columns",type:"number",label:"Card columns"},{name:"search",type:"checkbox",label:"Search (table)",default:!0},{name:"sortable",type:"checkbox",label:"Sortable (table)",default:!0},{name:"exportable",type:"checkbox",label:"CSV export"},{name:"page-size",type:"number",label:"Page size"},{name:"empty",type:"text",label:"Empty message"},{name:"scope",type:"select",label:"Scope",options:[["","All"],["mine","Current user"]]},{name:"cta",type:"lookup",source:"actions",label:"CTA action"},{name:"cta-label",type:"text",label:"CTA label"},{name:"cta-icon",type:"icon",label:"CTA icon"},{name:"cta-style",type:"select",label:"CTA style",options:[["primary","Primary"],["secondary","Secondary"],["success","Success"],["danger","Danger"],["warning","Warning"],["info","Info"]]},{name:"cta-confirm",type:"text",label:"CTA confirm text"},{name:"class",type:"text",label:"CSS class"},{name:"id",type:"text",label:"ID"}]},{name:"view",label:"View",icon:"eye",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[view slug="products" display="table" limit="50" /]',attrs:[{name:"slug",type:"lookup",source:"views",label:"View"},{name:"display",type:"select",label:"Display",options:[["table","Table"],["cards","Cards"],["list","List"],["accordion","Accordion"],["timeline","Timeline"],["block","Block"]]},{name:"limit",type:"number",label:"Limit"},{name:"fields",type:"text",label:"Fields (comma-separated)"},{name:"title-field",type:"text",label:"Title field"},{name:"columns",type:"number",label:"Card columns"},{name:"empty",type:"text",label:"Empty message"}]},{name:"block",label:"Block",icon:"layout",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[block template="post-card" title="My Post" /]',attrs:[{name:"template",type:"lookup",source:"blocks",label:"Template"}]},{name:"form",label:"Form",icon:"file-text",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[form name="contact" /]',attrs:[{name:"name",type:"lookup",source:"forms",label:"Form"},{name:"class",type:"text",label:"CSS class"},{name:"id",type:"text",label:"ID"}]},{name:"cta",label:"CTA",icon:"mouse-pointer",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[cta action="send-welcome" entry="abc123" style="primary" label="Send" /]',attrs:[{name:"action",type:"lookup",source:"actions",label:"Action"},{name:"entry",type:"text",label:"Entry ID"},{name:"style",type:"select",label:"Style",options:[["primary","Primary"],["secondary","Secondary"],["success","Success"],["danger","Danger"],["warning","Warning"],["info","Info"]]},{name:"icon",type:"icon",label:"Icon"},{name:"size",type:"select",label:"Size",options:[["","Default"],["sm","Small"],["lg","Large"]]},{name:"confirm",type:"text",label:"Confirm text"},{name:"label",type:"text",label:"Label"}]},{name:"menu",label:"Menu",icon:"menu",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[menu slug="legal" /]',attrs:[{name:"slug",type:"lookup",source:"menus",label:"Menu"},{name:"location",type:"lookup",source:"menu-locations",label:"Location slot"},{name:"variant",type:"text",label:"Variant"},{name:"class",type:"text",label:"CSS class"},{name:"depth",type:"number",label:"Max depth"},{name:"orientation",type:"select",label:"Orientation",options:[["","Menu default"],["horizontal","Horizontal"],["vertical","Vertical"]]},{name:"float",type:"select",label:"Floating",options:[["","Menu default"],["no","Render inline here"]]}]},{name:"theme-switcher",label:"Theme switcher",icon:"palette",category:"Components",selfClosing:!0,container:!1,wrappable:!1,template:"[theme-switcher /]",attrs:[{name:"position",type:"select",label:"Position",options:[["","Inline - where the tag is"],["top-left","Top left"],["top-right","Top right"],["left-center","Left, centred"],["right-center","Right, centred"],["bottom-left","Bottom left"],["bottom-right","Bottom right"]]},{name:"offset-x",type:"number",label:"Offset from the side (px, pinned only)"},{name:"offset-y",type:"number",label:"Offset from the top/bottom (px, pinned only)"},{name:"themes",type:"text",label:"Themes (comma list - blank for the site's)"}]},{name:"icon",label:"Icon",icon:"icon-pick",category:"Media",selfClosing:!0,container:!1,wrappable:!1,template:'[icon name="arrow-right" /]',attrs:[{name:"name",type:"icon",label:"Icon name"},{name:"size",type:"number",label:"Size (px)"},{name:"color",type:"colour",label:"Colour"},{name:"class",type:"text",label:"CSS class"}]},{name:"embed",label:"Embed",icon:"video",category:"Media",selfClosing:!0,container:!1,wrappable:!1,template:'[embed url="https://youtu.be/dQw4w9WgXcQ" /]',custom:"embed",attrs:[{name:"url",type:"url",label:"URL"},{name:"ratio",type:"select",label:"Aspect ratio",options:[["16:9","16:9"],["4:3","4:3"],["1:1","1:1"],["21:9","21:9"],["9:16","9:16"],["3:4","3:4"]]},{name:"title",type:"text",label:"Title / caption"},{name:"width",type:"text",label:"Width"},{name:"align",type:"select",label:"Alignment",options:[["left","Left"],["center","Centre"],["right","Right"]]},{name:"autoplay",type:"checkbox",label:"Autoplay"},{name:"loop",type:"checkbox",label:"Loop"},{name:"muted",type:"checkbox",label:"Muted"}]}],EDITOR_TOOLS=[{name:"image",label:"Image",icon:"image-add",category:"Media",custom:"image"},{name:"colourpick",label:"Colour picker",icon:"colour-picker",category:"Media",custom:"colourpick"},{name:"component",label:"Component",icon:"component",category:"Components",custom:"component"},{name:"scribe",label:"Scribe",icon:"scribe-insert",category:"Content",custom:"scribe"}];
@@ -1 +1 @@
1
- import{isPathAllowed as F}from"./admin-scope.js";export const TOOLS_FOLDER_TEXT="Tools",MANAGE_PLUGINS_URL="#/plugins",MANAGE_PLUGINS_ITEM={text:"Marketplace",url:MANAGE_PLUGINS_URL,icon:"store",permission:"plugins"};export function stripItemByUrl(e,t){const n=[];for(const r of e||[]){if(!r||r.url===t)continue;const s={...r};Array.isArray(r.items)&&r.items.length&&(s.items=stripItemByUrl(r.items,t)),n.push(s)}return n}export function managePluginsItem(){return{...MANAGE_PLUGINS_ITEM}}export const TOOLS_FOLDER_KEY="tools";const M=["countUrl","core","permission"],c=e=>String(e??"").toLowerCase();export function slugify(e){return c(e).trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}export function groupKeyFor(e,t){return`${c(e)}.${slugify(t)}`}export const GROUP_ICON="folder";const P=e=>e&&(e.type==="separator"||e.type==="spacer"),A=e=>e&&!P(e)&&!e.url;function O(e){return(e||[]).filter(Boolean).map(t=>Array.isArray(t.items)?{...t,items:O(t.items)}:{...t})}function E(e,t,n=[]){for(const r of e||[]){if(!r)continue;if(t(r))return[...n,r];const s=E(r.items,t,[...n,r]);if(s)return s}return null}function I(e,t,n=[],r=[]){for(const s of e||[])s&&(t(s)&&r.push([...n,s]),I(s.items,t,[...n,s],r));return r}const U=e=>!e.some(t=>t.hidden||t.restricted);function $(e,t){for(const n of e||[]){if(t(n))return n;const r=$(n?.items,t);if(r)return r}return null}export function findFolder(e,t){if(!t)return null;const n=c(t);return $(e,r=>A(r)&&c(r.key)===n)||$(e,r=>A(r)&&!r.key&&c(r.text)===n)}function b(e,t){const n={...t,...e};for(const r of M)t[r]!==void 0&&(n[r]=t[r]);return!(Array.isArray(e.items)&&e.items.length)&&Array.isArray(t.items)&&(n.items=t.items),e.text||(n.text=t.text),!e.icon&&t.icon&&(n.icon=t.icon),delete n.ref,delete n.folder,delete n.group,n}function _(e,{persisted:t,orphan:n=!1,node:r=null}){const s=e?.item||{};return{persisted:t,orphan:n,source:s.source||(s.core?"Built in":""),folder:e?.folder||TOOLS_FOLDER_KEY,defaults:n?{text:null,icon:null}:{text:s.text||"",icon:s.icon||""},...n&&r&&{label:r.text||r.url},...(e?.group||e?.item?.group)&&{group:e.group||e.item.group}}}export function placeTools(e,t,{forEditor:n=!1}={}){const r=O(e),s=(t||[]).filter(o=>o&&o.item&&typeof o.item.text=="string"),d=new Map;for(const o of s)o.item.url&&!d.has(o.item.url)&&d.set(o.item.url,o);const y=new Set,m=o=>{const i=[];for(const u of o){const h=u.url?d.get(u.url):null;if(h&&!y.has(u.url)){y.add(u.url);const g=b(u,h.item);Array.isArray(u.items)&&u.items.length?g.items=m(u.items):n&&(g.items=[]),n&&(g.$tool=_(h,{persisted:!0})),i.push(g);continue}if(u.ref){if(!n)continue;const{ref:g,...f}=u;i.push({...f,items:m(u.items||[]),$tool:_(null,{persisted:!0,orphan:!0,node:u})});continue}Array.isArray(u.items)&&(u.items=m(u.items)),i.push(u)}return i},a=m(r),T=new Set,L=o=>{for(const i of o||[])i.hidden&&i.text&&!i.url&&!(Array.isArray(i.items)&&i.items.length)&&T.add(c(i.text)),L(i.items)};L(a);let x=null;const S=()=>{const o=findFolder(a,TOOLS_FOLDER_KEY);if(o)return o;if(!x){x={text:TOOLS_FOLDER_TEXT,key:TOOLS_FOLDER_KEY,icon:"tool",items:[],synthesised:!0};const i=a.findIndex(u=>["system","documentation"].includes(c(u?.key||u?.text)));a.splice(i===-1?a.length:i,0,x)}return x},l=new Set,k=(o,i)=>{const u=groupKeyFor(o.key||slugify(o.text),i),h=$(a,p=>A(p)&&c(p.key)===u)||(o.items||[]).find(p=>A(p)&&(!p.key||c(p.key)===u)&&c(p.text)===c(i));if(h)return h;const g={text:String(i),key:u,icon:GROUP_ICON,items:[],synthesised:!0},f=o.items.findIndex(p=>l.has(p));return o.items.splice(f===-1?o.items.length:f,0,g),g};for(const o of s){const{item:i}=o;if(i.url&&y.has(i.url))continue;i.url&&y.add(i.url);const u=T.has(c(i.text));if(u&&!n)continue;const h=b({},i);n&&(h.items=[],h.$tool=_(o,{persisted:!1}),u&&(h.hidden=!0));let f=(o.parent?findFolder(a,o.parent)||$(a,w=>!P(w)&&c(w.text)===c(o.parent)):null)||findFolder(a,o.folder||i.folder)||S();f.items=Array.isArray(f.items)?f.items:[];const p=typeof(o.group||i.group)=="string"?(o.group||i.group).trim():"";p&&slugify(p)&&(f=k(f,p),f.items=Array.isArray(f.items)?f.items:[]),f.items.push(h),l.add(h)}return a}export const LOCKED_TOOLS=[{folder:"overview",item:{text:"Dashboard",url:"#/",icon:"home"}},{folder:"content",item:{text:"Menus",url:"#/menus",icon:"menu",permission:"menus"}},{folder:"system",item:{text:"Users",url:"#/users",icon:"users",permission:"users"}},{folder:"system",item:{text:"My Profile",url:"#/my-profile",icon:"user"}}];export function isLockedUrl(e){return LOCKED_TOOLS.some(t=>t.item.url===e)}export function ensureLockedTools(e,{leaveInHiddenFolders:t=!1}={}){const n=O(e);for(const{folder:r,item:s}of LOCKED_TOOLS){const d=I(n,l=>l?.url===s.url),y=d.find(l=>!l.some(k=>k.restricted));if(!y&&d.length)continue;const m=y?.at(-1);if(m){if(m.hidden&&delete m.hidden,!y.slice(0,-1).some(o=>o.hidden)||t)continue;const k=y.at(-2);k.items=k.items.filter(o=>o!==m)}const a=c(r),T=I(n,l=>A(l)&&c(l.key)===a).filter(U),L=I(n,l=>A(l)&&!l.key&&c(l.text)===a).filter(U),x=(T[0]||L[0])?.at(-1)||null,S=m||{...s};x?(x.items=Array.isArray(x.items)?x.items:[],x.items.push(S)):n.push(S)}return n}export function insertFoldersBeforeSystem(e,t){const n=(t||[]).filter(Boolean);if(!n.length)return e;const r=["system","documentation"],s=e.findIndex(d=>r.includes(c(d?.key||d?.text)));return s===-1?[...e,...n]:[...e.slice(0,s),...n,...e.slice(s)]}export function canAny(e,t){return t?!e||!e.length?!1:e.includes(t)?!0:["read","create","update","delete"].some(n=>e.includes(`${t}.${n}`)):!0}export function filterByPermission(e,t,n=null){const r=[];for(const s of e){if(s.hidden||s.permission&&!canAny(t,s.permission))continue;const d=typeof s.url=="string"?s.url:"";if(n&&d.startsWith("#/")&&!F(n,d.slice(1)))continue;const y=Array.isArray(s.items)&&s.items.length>0,m=y?filterByPermission(s.items,t,n):[];y&&!m.length&&!d||r.push({...s,items:m})}return r}export function sidebarStateKey(e,t=null){const n=String(e?.text||"").replace(/\s*\(\d+\)\s*$/,"").toLowerCase().replace(/\s+/g,"-"),r=String(e?.url||"").match(/^#\/projects\/([^/]+)/);let s="";if(r)try{s=`project-${decodeURIComponent(r[1])}.`}catch{s=`project-${r[1]}.`}return t?e?.key?`sidebar.expanded.key.${c(e.key)}`:`${t}/${s}${n}`:`sidebar.expanded.${s}${n}`}export function pruneEmptyToolsFolder(e){return(e||[]).filter(t=>A(t)?!(c(t.key)===TOOLS_FOLDER_KEY||!t.key&&t.text===TOOLS_FOLDER_TEXT)||Array.isArray(t.items)&&t.items.length>0:!0)}
1
+ import{isPathAllowed as F}from"./admin-scope.js";export const TOOLS_FOLDER_TEXT="Tools",MANAGE_PLUGINS_URL="#/plugins",MANAGE_PLUGINS_ITEM={text:"Marketplace",url:MANAGE_PLUGINS_URL,icon:"store",permission:"plugins"};export function stripItemByUrl(e,t){const n=[];for(const r of e||[]){if(!r||r.url===t)continue;const s={...r};Array.isArray(r.items)&&r.items.length&&(s.items=stripItemByUrl(r.items,t)),n.push(s)}return n}export function managePluginsItem(){return{...MANAGE_PLUGINS_ITEM}}export const TOOLS_FOLDER_KEY="tools";const M=["countUrl","core","permission"],c=e=>String(e??"").toLowerCase();export function slugify(e){return c(e).trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}export function groupKeyFor(e,t){return`${c(e)}.${slugify(t)}`}export const GROUP_ICON="folder";const P=e=>e&&(e.type==="separator"||e.type==="spacer"||e.type==="theme-switcher"),A=e=>e&&!P(e)&&!e.url;function w(e){return(e||[]).filter(Boolean).map(t=>Array.isArray(t.items)?{...t,items:w(t.items)}:{...t})}function E(e,t,n=[]){for(const r of e||[]){if(!r)continue;if(t(r))return[...n,r];const s=E(r.items,t,[...n,r]);if(s)return s}return null}function I(e,t,n=[],r=[]){for(const s of e||[])s&&(t(s)&&r.push([...n,s]),I(s.items,t,[...n,s],r));return r}const U=e=>!e.some(t=>t.hidden||t.restricted);function $(e,t){for(const n of e||[]){if(t(n))return n;const r=$(n?.items,t);if(r)return r}return null}export function findFolder(e,t){if(!t)return null;const n=c(t);return $(e,r=>A(r)&&c(r.key)===n)||$(e,r=>A(r)&&!r.key&&c(r.text)===n)}function b(e,t){const n={...t,...e};for(const r of M)t[r]!==void 0&&(n[r]=t[r]);return!(Array.isArray(e.items)&&e.items.length)&&Array.isArray(t.items)&&(n.items=t.items),e.text||(n.text=t.text),!e.icon&&t.icon&&(n.icon=t.icon),delete n.ref,delete n.folder,delete n.group,n}function O(e,{persisted:t,orphan:n=!1,node:r=null}){const s=e?.item||{};return{persisted:t,orphan:n,source:s.source||(s.core?"Built in":""),folder:e?.folder||TOOLS_FOLDER_KEY,defaults:n?{text:null,icon:null}:{text:s.text||"",icon:s.icon||""},...n&&r&&{label:r.text||r.url},...(e?.group||e?.item?.group)&&{group:e.group||e.item.group}}}export function placeTools(e,t,{forEditor:n=!1}={}){const r=w(e),s=(t||[]).filter(o=>o&&o.item&&typeof o.item.text=="string"),m=new Map;for(const o of s)o.item.url&&!m.has(o.item.url)&&m.set(o.item.url,o);const y=new Set,d=o=>{const i=[];for(const u of o){const h=u.url?m.get(u.url):null;if(h&&!y.has(u.url)){y.add(u.url);const g=b(u,h.item);Array.isArray(u.items)&&u.items.length?g.items=d(u.items):n&&(g.items=[]),n&&(g.$tool=O(h,{persisted:!0})),i.push(g);continue}if(u.ref){if(!n)continue;const{ref:g,...f}=u;i.push({...f,items:d(u.items||[]),$tool:O(null,{persisted:!0,orphan:!0,node:u})});continue}Array.isArray(u.items)&&(u.items=d(u.items)),i.push(u)}return i},a=d(r),T=new Set,L=o=>{for(const i of o||[])i.hidden&&i.text&&!i.url&&!(Array.isArray(i.items)&&i.items.length)&&T.add(c(i.text)),L(i.items)};L(a);let x=null;const S=()=>{const o=findFolder(a,TOOLS_FOLDER_KEY);if(o)return o;if(!x){x={text:TOOLS_FOLDER_TEXT,key:TOOLS_FOLDER_KEY,icon:"tool",items:[],synthesised:!0};const i=a.findIndex(u=>["system","documentation"].includes(c(u?.key||u?.text)));a.splice(i===-1?a.length:i,0,x)}return x},l=new Set,k=(o,i)=>{const u=groupKeyFor(o.key||slugify(o.text),i),h=$(a,p=>A(p)&&c(p.key)===u)||(o.items||[]).find(p=>A(p)&&(!p.key||c(p.key)===u)&&c(p.text)===c(i));if(h)return h;const g={text:String(i),key:u,icon:GROUP_ICON,items:[],synthesised:!0},f=o.items.findIndex(p=>l.has(p));return o.items.splice(f===-1?o.items.length:f,0,g),g};for(const o of s){const{item:i}=o;if(i.url&&y.has(i.url))continue;i.url&&y.add(i.url);const u=T.has(c(i.text));if(u&&!n)continue;const h=b({},i);n&&(h.items=[],h.$tool=O(o,{persisted:!1}),u&&(h.hidden=!0));let f=(o.parent?findFolder(a,o.parent)||$(a,_=>!P(_)&&c(_.text)===c(o.parent)):null)||findFolder(a,o.folder||i.folder)||S();f.items=Array.isArray(f.items)?f.items:[];const p=typeof(o.group||i.group)=="string"?(o.group||i.group).trim():"";p&&slugify(p)&&(f=k(f,p),f.items=Array.isArray(f.items)?f.items:[]),f.items.push(h),l.add(h)}return a}export const LOCKED_TOOLS=[{folder:"overview",item:{text:"Dashboard",url:"#/",icon:"home"}},{folder:"content",item:{text:"Menus",url:"#/menus",icon:"menu",permission:"menus"}},{folder:"system",item:{text:"Users",url:"#/users",icon:"users",permission:"users"}},{folder:"system",item:{text:"My Profile",url:"#/my-profile",icon:"user"}}];export function isLockedUrl(e){return LOCKED_TOOLS.some(t=>t.item.url===e)}export function ensureLockedTools(e,{leaveInHiddenFolders:t=!1}={}){const n=w(e);for(const{folder:r,item:s}of LOCKED_TOOLS){const m=I(n,l=>l?.url===s.url),y=m.find(l=>!l.some(k=>k.restricted));if(!y&&m.length)continue;const d=y?.at(-1);if(d){if(d.hidden&&delete d.hidden,!y.slice(0,-1).some(o=>o.hidden)||t)continue;const k=y.at(-2);k.items=k.items.filter(o=>o!==d)}const a=c(r),T=I(n,l=>A(l)&&c(l.key)===a).filter(U),L=I(n,l=>A(l)&&!l.key&&c(l.text)===a).filter(U),x=(T[0]||L[0])?.at(-1)||null,S=d||{...s};x?(x.items=Array.isArray(x.items)?x.items:[],x.items.push(S)):n.push(S)}return n}export function insertFoldersBeforeSystem(e,t){const n=(t||[]).filter(Boolean);if(!n.length)return e;const r=["system","documentation"],s=e.findIndex(m=>r.includes(c(m?.key||m?.text)));return s===-1?[...e,...n]:[...e.slice(0,s),...n,...e.slice(s)]}export function canAny(e,t){return t?!e||!e.length?!1:e.includes(t)?!0:["read","create","update","delete"].some(n=>e.includes(`${t}.${n}`)):!0}export function filterByPermission(e,t,n=null){const r=[];for(const s of e){if(s.hidden||s.permission&&!canAny(t,s.permission))continue;const m=typeof s.url=="string"?s.url:"";if(n&&m.startsWith("#/")&&!F(n,m.slice(1)))continue;const y=Array.isArray(s.items)&&s.items.length>0,d=y?filterByPermission(s.items,t,n):[];y&&!d.length&&!m||r.push({...s,items:d})}return r}export function sidebarStateKey(e,t=null){const n=String(e?.text||"").replace(/\s*\(\d+\)\s*$/,"").toLowerCase().replace(/\s+/g,"-"),r=String(e?.url||"").match(/^#\/projects\/([^/]+)/);let s="";if(r)try{s=`project-${decodeURIComponent(r[1])}.`}catch{s=`project-${r[1]}.`}return t?e?.key?`sidebar.expanded.key.${c(e.key)}`:`${t}/${s}${n}`:`sidebar.expanded.${s}${n}`}export function pruneEmptyToolsFolder(e){return(e||[]).filter(t=>A(t)?!(c(t.key)===TOOLS_FOLDER_KEY||!t.key&&t.text===TOOLS_FOLDER_TEXT)||Array.isArray(t.items)&&t.items.length>0:!0)}
@@ -1,4 +1,4 @@
1
- import{api as g,getUser as L}from"../api.js";import{isPathAllowed as ue}from"./admin-scope.js";import{colourToCss as V,applyFloat as fe,applyHighlight as me,itemHighlightAttrs as pe}from"/public/js/menu-decor.mjs?v=20261009-hover-darken";import{placeTools as he,ensureLockedTools as ge,managePluginsItem as be,stripItemByUrl as ye,insertFoldersBeforeSystem as ve,pruneEmptyToolsFolder as xe,sidebarStateKey as X,canAny as O,filterByPermission as $e,MANAGE_PLUGINS_URL as Z}from"./sidebar-grouping.js";import{arrangeButtonMarkup as we,canArrange as ke,startArrange as Se}from"./sidebar-arrange.js";import{collapseButtonMarkup as qe,wireSidebarSize as Ce,COLLAPSED_WIDTH as Ae}from"./sidebar-size.js";import{FREQUENT_MAX as Y,addFrequent as Fe,frequentFolder as Le,moveFrequent as J,readFrequent as z,readFrequentHidden as Ee,removeFrequent as Te,saveFrequent as Me,syncFrequent as He,writeFrequent as Ne,writeFrequentHidden as Q}from"./sidebar-frequent.js";import{hotkeyFor as ee,hotkeyTitle as je,setHotkeyTree as Be}from"./hotkeys-ui.js";import{hotkeyLabel as Pe}from"./hotkeys.js";function m(e){return String(e??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}const Re=[{text:"Dashboard",url:"#/",icon:"home"},{text:"Menus",url:"#/menus",icon:"menu",permission:"menus"}];async function _e(e){if(!O(e,"projects"))return[];let n=[];try{n=await g.projects.listWithCounts()}catch{return[]}if(!Array.isArray(n))return[];const s=[{key:"pages",text:"Pages",icon:"file-text",path:"/pages"},{key:"collections",text:"Collections",icon:"database",path:"/collections"},{key:"forms",text:"Forms",icon:"layout",path:"/forms"},{key:"actions",text:"Actions",icon:"zap",path:"/actions"},{key:"menus",text:"Menus",icon:"menu",path:"/menus"},{key:"blocks",text:"Blocks",icon:"box",path:"/blocks"},{key:"components",text:"Components",icon:"component",path:"/components"},{key:"views",text:"Views",icon:"eye",path:"/views"},{key:"roles",text:"Roles",icon:"shield",path:"/roles"},{key:"users",text:"Users",icon:"users",path:"/users"},{key:"apis",text:"APIs",icon:"code",path:"/apis"}];return n.map(t=>{const o="#/projects/"+encodeURIComponent(t.slug),a=t.counts||{},d=[{text:"Overview",url:o,icon:t.icon||"folder"}];for(const c of s){const r=Number(a[c.key])||0;r<=0||d.push({text:`${c.text} (${r})`,url:o+c.path,icon:c.icon})}d.push({text:"Settings",url:o+"/settings",icon:"settings"});const u=t.enabled===!1;return{text:t.name||t.slug,key:`project.${t.slug}`,icon:t.icon||"folder",items:d,...u&&{colour:"neutral",badge:{text:"off"}}}})}function Ie(e,n){for(const s of e)if(!s.hidden&&(s.key==="projects"||!s.key&&(s.text||"").toLowerCase()==="projects")){s.items=Array.isArray(s.items)?s.items.slice():[],s.items.push(...n);return}}function te(e,n){const s=se.includes(n.tone)?n.tone:"info",t=n.label||String(n.count);n.details?j.set(e,n.details):j.delete(e);const i=!n.inventory&&A.some(o=>o.url===e&&!o.inventory)?" data-live":"";return`<span class="sidebar-badge sidebar-badge--${s}" data-count-for="${m(e)}"${i} data-badge-label="${m(t)}" aria-label="${m(t)}">${m(String(n.count))}</span>`}function ne(e){for(const n of e.querySelectorAll("details")){const s=n.querySelector(":scope > summary");if(!s)continue;let t=0,i=!1;const o=[];for(const r of n.querySelectorAll(":scope > .sidebar-children .sidebar-badge[data-live]")){const f=Number(r.textContent)||0;f&&(t+=f,i||(i=r.classList.contains("sidebar-badge--danger")),o.push({text:r.closest("a")?.querySelector(".sidebar-text")?.textContent.trim()??"",meta:r.dataset.badgeLabel,tone:r.classList.contains("sidebar-badge--danger")?"danger":void 0}))}const a=`folder:${n.dataset.stateKey||s.textContent.trim()}`;let d=s.querySelector(":scope > .sidebar-badge--sum");if(!t){d?.remove(),j.delete(a);continue}d||(d=document.createElement("span"),s.querySelector(".sidebar-text")?.after(d));const u=s.querySelector(".sidebar-text")?.textContent.trim()||"This folder",c=o.map(r=>`${r.text}: ${r.meta}`).join(" \xB7 ");d.className=`sidebar-badge sidebar-badge--sum sidebar-badge--${i?"danger":"info"}`,d.dataset.countFor=a,d.dataset.badgeLabel=c,d.setAttribute("aria-label",c),d.textContent=String(t),j.set(a,{title:u,items:o})}}const j=new Map;function Oe(e,n){if(!e.url||!n)return"";const s=n[e.url];return!s||!(s.count>0)&&!s.showZero?"":te(e.url,s)}const se=["info","attention","danger","live"],Ue={"#/pages":["page","pages"],"#/media":["file in the media library","files in the media library"],"#/menus":["menu","menus"],"#/context-menus":["context menu","context menus"],"#/theme":["element override in the theme","element overrides in the theme"],"#/collections":["collection","collections"],"#/forms":["form","forms"],"#/views":["view","views"],"#/actions":["action","actions"],"#/blocks":["block","blocks"],"#/components":["component","components"],"#/users":["user","users"],"#/plugins":["plugin installed","plugins installed"],"#/system/notifications":["unread notification","unread notifications"]};function W(e,{fallbackLabel:n="",tone:s="info"}={}){const t=Number(typeof e=="object"&&e!==null?e.count:e);if(!Number.isFinite(t)||t<0)return null;const i=typeof e?.label=="string"&&e.label||(n?`${t} - ${n}`:String(t)),o={count:Math.round(t),label:i,tone:se.includes(e?.tone)?e.tone:s};return e?.details&&typeof e.details=="object"&&(o.details=e.details),e?.inventory===!0&&(o.inventory=!0),e?.showZero===!0&&(o.showZero=!0),o}function oe(e,n,s=null,{inFrequent:t=!1}={}){if(e&&e.type==="separator")return'<hr class="sidebar-divider">';if(e&&e.type==="spacer"){const y=["xs","sm","md","lg","xl","flex"].includes(e.size)?e.size:"md";return`<div class="sidebar-spacer sidebar-spacer--${m(y)}" aria-hidden="true"></div>`}const i=String(e.text||"?").trim().charAt(0).toUpperCase()||"?",o=e.icon?`<span data-icon="${m(e.icon)}"></span>`:`<span class="sidebar-initial" aria-hidden="true">${m(i)}</span>`,a=m(e.text||""),d=e.url?m(e.url):"",u=Array.isArray(e.items)&&e.items.length>0,c=e.colour?V(e.colour):"",r=pe(e),f=[r.hover&&` data-menu-item-hover="${m(r.hover)}"`,r.active&&` data-menu-item-active="${m(r.active)}"`].filter(Boolean).join(""),x=[c&&`color:${m(c)}`,r.accent&&`--dm-menu-highlight:${m(r.accent)}`].filter(Boolean).join(";"),w=x?` style="${x}"`:"",k=e.url?je(e.url,e.text||"",{inFrequent:t}):"",q=k?` title="${m(k)}"`:"";let F="";if(e.badge&&e.badge.text!=null&&e.badge.text!==""){const y=e.badge.variant?V(e.badge.variant):"";F=`<span class="dm-menu-badge"${y?` style="background:${m(y)};color:var(--dm-text-inverse,#fff)"`:""}>${m(String(e.badge.text))}</span>`}if(e.frequent&&e.empty){const y=X(e,s),C=S.get(y)!==!1;return`<details data-state-key="${m(y)}"${C?" open":""} data-frequent data-frequent-empty>
1
+ import{api as g,getUser as L}from"../api.js";import{isPathAllowed as ue}from"./admin-scope.js";import{colourToCss as V,applyFloat as fe,applyHighlight as me,itemHighlightAttrs as pe}from"/public/js/menu-decor.mjs?v=20261009-hover-darken";import{placeTools as he,ensureLockedTools as ge,managePluginsItem as be,stripItemByUrl as ye,insertFoldersBeforeSystem as ve,pruneEmptyToolsFolder as xe,sidebarStateKey as X,canAny as O,filterByPermission as $e,MANAGE_PLUGINS_URL as Z}from"./sidebar-grouping.js";import{arrangeButtonMarkup as we,canArrange as ke,startArrange as Se}from"./sidebar-arrange.js";import{collapseButtonMarkup as qe,wireSidebarSize as Ce,COLLAPSED_WIDTH as Ae}from"./sidebar-size.js";import{FREQUENT_MAX as Y,addFrequent as Fe,frequentFolder as Le,moveFrequent as J,readFrequent as z,readFrequentHidden as Ee,removeFrequent as Te,saveFrequent as Me,syncFrequent as He,writeFrequent as Ne,writeFrequentHidden as Q}from"./sidebar-frequent.js";import{hotkeyFor as ee,hotkeyTitle as je,setHotkeyTree as Be}from"./hotkeys-ui.js";import{hotkeyLabel as Pe}from"./hotkeys.js";function m(e){return String(e??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}const Re=[{text:"Dashboard",url:"#/",icon:"home"},{text:"Menus",url:"#/menus",icon:"menu",permission:"menus"}];async function _e(e){if(!O(e,"projects"))return[];let n=[];try{n=await g.projects.listWithCounts()}catch{return[]}if(!Array.isArray(n))return[];const s=[{key:"pages",text:"Pages",icon:"file-text",path:"/pages"},{key:"collections",text:"Collections",icon:"database",path:"/collections"},{key:"forms",text:"Forms",icon:"layout",path:"/forms"},{key:"actions",text:"Actions",icon:"zap",path:"/actions"},{key:"menus",text:"Menus",icon:"menu",path:"/menus"},{key:"blocks",text:"Blocks",icon:"box",path:"/blocks"},{key:"components",text:"Components",icon:"component",path:"/components"},{key:"views",text:"Views",icon:"eye",path:"/views"},{key:"roles",text:"Roles",icon:"shield",path:"/roles"},{key:"users",text:"Users",icon:"users",path:"/users"},{key:"apis",text:"APIs",icon:"code",path:"/apis"}];return n.map(t=>{const o="#/projects/"+encodeURIComponent(t.slug),a=t.counts||{},d=[{text:"Overview",url:o,icon:t.icon||"folder"}];for(const c of s){const r=Number(a[c.key])||0;r<=0||d.push({text:`${c.text} (${r})`,url:o+c.path,icon:c.icon})}d.push({text:"Settings",url:o+"/settings",icon:"settings"});const u=t.enabled===!1;return{text:t.name||t.slug,key:`project.${t.slug}`,icon:t.icon||"folder",items:d,...u&&{colour:"neutral",badge:{text:"off"}}}})}function Ie(e,n){for(const s of e)if(!s.hidden&&(s.key==="projects"||!s.key&&(s.text||"").toLowerCase()==="projects")){s.items=Array.isArray(s.items)?s.items.slice():[],s.items.push(...n);return}}function te(e,n){const s=se.includes(n.tone)?n.tone:"info",t=n.label||String(n.count);n.details?j.set(e,n.details):j.delete(e);const i=!n.inventory&&A.some(o=>o.url===e&&!o.inventory)?" data-live":"";return`<span class="sidebar-badge sidebar-badge--${s}" data-count-for="${m(e)}"${i} data-badge-label="${m(t)}" aria-label="${m(t)}">${m(String(n.count))}</span>`}function ne(e){for(const n of e.querySelectorAll("details")){const s=n.querySelector(":scope > summary");if(!s)continue;let t=0,i=!1;const o=[];for(const r of n.querySelectorAll(":scope > .sidebar-children .sidebar-badge[data-live]")){const f=Number(r.textContent)||0;f&&(t+=f,i||(i=r.classList.contains("sidebar-badge--danger")),o.push({text:r.closest("a")?.querySelector(".sidebar-text")?.textContent.trim()??"",meta:r.dataset.badgeLabel,tone:r.classList.contains("sidebar-badge--danger")?"danger":void 0}))}const a=`folder:${n.dataset.stateKey||s.textContent.trim()}`;let d=s.querySelector(":scope > .sidebar-badge--sum");if(!t){d?.remove(),j.delete(a);continue}d||(d=document.createElement("span"),s.querySelector(".sidebar-text")?.after(d));const u=s.querySelector(".sidebar-text")?.textContent.trim()||"This folder",c=o.map(r=>`${r.text}: ${r.meta}`).join(" \xB7 ");d.className=`sidebar-badge sidebar-badge--sum sidebar-badge--${i?"danger":"info"}`,d.dataset.countFor=a,d.dataset.badgeLabel=c,d.setAttribute("aria-label",c),d.textContent=String(t),j.set(a,{title:u,items:o})}}const j=new Map;function Oe(e,n){if(!e.url||!n)return"";const s=n[e.url];return!s||!(s.count>0)&&!s.showZero?"":te(e.url,s)}const se=["info","attention","danger","live"],Ue={"#/pages":["page","pages"],"#/media":["file in the media library","files in the media library"],"#/menus":["menu","menus"],"#/context-menus":["context menu","context menus"],"#/theme":["element override in the theme","element overrides in the theme"],"#/collections":["collection","collections"],"#/forms":["form","forms"],"#/views":["view","views"],"#/actions":["action","actions"],"#/blocks":["block","blocks"],"#/components":["component","components"],"#/users":["user","users"],"#/plugins":["plugin installed","plugins installed"],"#/system/notifications":["unread notification","unread notifications"]};function W(e,{fallbackLabel:n="",tone:s="info"}={}){const t=Number(typeof e=="object"&&e!==null?e.count:e);if(!Number.isFinite(t)||t<0)return null;const i=typeof e?.label=="string"&&e.label||(n?`${t} - ${n}`:String(t)),o={count:Math.round(t),label:i,tone:se.includes(e?.tone)?e.tone:s};return e?.details&&typeof e.details=="object"&&(o.details=e.details),e?.inventory===!0&&(o.inventory=!0),e?.showZero===!0&&(o.showZero=!0),o}function oe(e,n,s=null,{inFrequent:t=!1}={}){if(e&&e.type==="separator")return'<hr class="sidebar-divider">';if(e&&e.type==="theme-switcher")return"";if(e&&e.type==="spacer"){const y=["xs","sm","md","lg","xl","flex"].includes(e.size)?e.size:"md";return`<div class="sidebar-spacer sidebar-spacer--${m(y)}" aria-hidden="true"></div>`}const i=String(e.text||"?").trim().charAt(0).toUpperCase()||"?",o=e.icon?`<span data-icon="${m(e.icon)}"></span>`:`<span class="sidebar-initial" aria-hidden="true">${m(i)}</span>`,a=m(e.text||""),d=e.url?m(e.url):"",u=Array.isArray(e.items)&&e.items.length>0,c=e.colour?V(e.colour):"",r=pe(e),f=[r.hover&&` data-menu-item-hover="${m(r.hover)}"`,r.active&&` data-menu-item-active="${m(r.active)}"`].filter(Boolean).join(""),x=[c&&`color:${m(c)}`,r.accent&&`--dm-menu-highlight:${m(r.accent)}`].filter(Boolean).join(";"),w=x?` style="${x}"`:"",k=e.url?je(e.url,e.text||"",{inFrequent:t}):"",q=k?` title="${m(k)}"`:"";let F="";if(e.badge&&e.badge.text!=null&&e.badge.text!==""){const y=e.badge.variant?V(e.badge.variant):"";F=`<span class="dm-menu-badge"${y?` style="background:${m(y)};color:var(--dm-text-inverse,#fff)"`:""}>${m(String(e.badge.text))}</span>`}if(e.frequent&&e.empty){const y=X(e,s),C=S.get(y)!==!1;return`<details data-state-key="${m(y)}"${C?" open":""} data-frequent data-frequent-empty>
2
2
  <summary${w}>${o} <span class="sidebar-text">${a}</span></summary>
3
3
  <div class="sidebar-children"><p class="sidebar-frequent-empty">Right-click any Tool and choose <strong>Add to Frequent</strong> to keep it here.</p></div>
4
4
  </details>`}if(u){const y=X(e,s),C=S.get(y)!==!1,B=e.items.map(T=>oe(T,n,y,{inFrequent:!!e.frequent})).join(""),P=e.frequent?' data-frequent title="Your Frequent Tools - right-click any Tool to add or remove it"':"";return`<details data-state-key="${m(y)}"${C?" open":""}${P}>
@@ -50,7 +50,7 @@
50
50
  round a row means it will go inside that row (the navbar shows nested items as a dropdown,
51
51
  the admin sidebar as a folder inside a folder). Press Esc while dragging to cancel. On a
52
52
  touch screen, drag by the grip too. Right-click an item for add, move, indent, duplicate,
53
- hide and remove. Separators and spacers are items too.</li>
53
+ hide and remove. Separators, spacers and the theme switcher (the site's theme dots, among the links) are items too.</li>
54
54
  <li><strong>Behaviour</strong> - colour scheme, sticky, fixed, static or floating, horizontal
55
55
  or vertical, alignment, and the hover and current-page highlight. A single item can
56
56
  override the highlight in its own editor.</li>
@@ -81,6 +81,10 @@
81
81
  <td><code>[spacer size="16" /]</code></td>
82
82
  <td>Vertical whitespace block (px height)</td>
83
83
  </tr>
84
+ <tr>
85
+ <td><code>[theme-switcher /]</code></td>
86
+ <td>The site's theme dots, where the tag stands - or pinned with <code>position</code> (<code>top-left</code> … <code>bottom-right</code>), <code>offset-x</code> / <code>offset-y</code>; <code>themes</code> narrows the list. Replaces the floating switcher on its page</td>
87
+ </tr>
84
88
  <tr>
85
89
  <td><code>[timeline]...[/timeline]</code></td>
86
90
  <td>Progression timeline; use <code>[event title="..." date="..."
@@ -26,6 +26,7 @@
26
26
  <button type="button" class="btn btn-sm btn-secondary" id="me-add-item" data-on-click="addItem"><span data-icon="plus"></span> Add item</button>
27
27
  <button type="button" class="btn btn-sm btn-ghost" id="me-add-separator" data-on-click="addSeparator"><span data-icon="minus"></span> Add separator</button>
28
28
  <button type="button" class="btn btn-sm btn-ghost" id="me-add-spacer" data-on-click="addSpacer"><span data-icon="move"></span> Add spacer</button>
29
+ <button type="button" class="btn btn-sm btn-ghost" id="me-add-switcher" data-on-click="addSwitcher" title="The site's theme dots, among the links"><span data-icon="palette"></span> Add theme switcher</button>
29
30
  <span class="mex-spacer"></span>
30
31
  <small class="mex-note"><span data-bind-text="itemCount.value"></span> items</small>
31
32
  <span data-help-title="Items" data-help="Click an item to edit its label, link, icon, who sees it, badge and look. Right-click it (or ⋮, or Shift+F10) to add an item or a sub-item below it, move, indent, duplicate, hide or remove it. Drag the grip to move it, or drop it onto another item to nest it. With an item focused: Enter edits, Alt+Up / Down moves, Alt+Right / Left indents, Delete removes. Changes are kept only when you Save."></span>
@@ -1,25 +1,30 @@
1
- import{api as j}from"../api.js";import{colourToCss as kt,HIGHLIGHT_HOVER as st,HIGHLIGHT_ACTIVE as lt,HIGHLIGHT_HOVER_LABELS as it,HIGHLIGHT_ACTIVE_LABELS as rt,cssClassProblems as ct,normaliseCssClasses as St}from"/public/js/menu-decor.mjs?v=20261009-hover-darken";import{openIconPicker as $t}from"../lib/card-builder.js";import{sortableMenuRows as xt}from"../lib/menu-drag.js";import{placeTools as Et,ensureLockedTools as At,isLockedUrl as Ct,groupKeyFor as Lt,slugify as _t,GROUP_ICON as It,TOOLS_FOLDER_KEY as Ht}from"../lib/sidebar-grouping.js";import{flatten as dt,nest as Rt,uid as _e}from"../lib/menu-rows.js";import{addBannerAction as We}from"../lib/plugin-chrome.js";import{helpPopovers as Tt}from"../lib/help-popover.js";import{onceClosed as jt,openMenuAt as qe,sidebarCount as Dt}from"../lib/tool-kit.js";import{addChild as Nt,bindingFrom as Ot,duplicateRow as Pt,indentRow as Ft,insertBelow as Bt,isStructural as ne,menuFromForm as zt,menuProblems as Mt,moveRow as ut,outdentRow as Kt,siblingsOf as Wt,snapshot as Ie}from"../lib/menu-editor-arrange.js";import{fmtDateTime as qt}from"../lib/dates.js";const Ut=760,Gt="admin-sidebar",Vt=/^\/menus\/(new|edit\/[^?#]+)(\?.*)?$/;function b(t){return String(t||"").replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;")}const He=t=>String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;");function Yt(t,c){let l=0,i=c.find(s=>s.id===t);for(;i&&i.parentId;)l++,i=c.find(s=>s.id===i.parentId);return l}function mt(t){const c=new Set,l=new Set,i=s=>{for(const g of t.filter(w=>w.parentId===s))!g.type&&Ct(g.url)&&!l.has(g.url)&&(l.add(g.url),c.add(g.id)),i(g.id)};return i(null),c}const Ue='<button type="button" class="btn btn-sm btn-ghost mex-kebab" aria-label="Actions" title="Actions (right-click, or Shift+F10)">\u22EE</button>';function Xt(t,c,l=!1){const i=l?mt(c):new Set,s=[];function g(w){for(const f of c.filter(P=>P.parentId===w)){const _=`style="margin-left:${Yt(f.id,c)*28}px"`;if(f.type==="separator"){s.push(`
2
- <div class="menu-tree-row menu-tree-row--separator" data-id="${f.id}" tabindex="0" ${_}>
1
+ import{api as j}from"../api.js";import{colourToCss as kt,HIGHLIGHT_HOVER as st,HIGHLIGHT_ACTIVE as it,HIGHLIGHT_HOVER_LABELS as lt,HIGHLIGHT_ACTIVE_LABELS as rt,cssClassProblems as ct,normaliseCssClasses as St}from"/public/js/menu-decor.mjs?v=20261009-hover-darken";import{openIconPicker as $t}from"../lib/card-builder.js";import{sortableMenuRows as xt}from"../lib/menu-drag.js";import{placeTools as Et,ensureLockedTools as At,isLockedUrl as Ct,groupKeyFor as Lt,slugify as _t,GROUP_ICON as It,TOOLS_FOLDER_KEY as Ht}from"../lib/sidebar-grouping.js";import{flatten as dt,nest as Tt,uid as _e}from"../lib/menu-rows.js";import{addBannerAction as qe}from"../lib/plugin-chrome.js";import{helpPopovers as Rt}from"../lib/help-popover.js";import{onceClosed as jt,openMenuAt as Ue,sidebarCount as Dt}from"../lib/tool-kit.js";import{addChild as Nt,bindingFrom as Ot,duplicateRow as Pt,indentRow as Ft,insertBelow as Bt,isStructural as oe,menuFromForm as zt,menuProblems as Mt,moveRow as ut,outdentRow as Kt,siblingsOf as Wt,snapshot as Ie}from"../lib/menu-editor-arrange.js";import{fmtDateTime as qt}from"../lib/dates.js";const Ut=760,Gt="admin-sidebar",Vt=/^\/menus\/(new|edit\/[^?#]+)(\?.*)?$/;function b(t){return String(t||"").replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;")}const He=t=>String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;");function Yt(t,c){let i=0,l=c.find(s=>s.id===t);for(;l&&l.parentId;)i++,l=c.find(s=>s.id===l.parentId);return i}function mt(t){const c=new Set,i=new Set,l=s=>{for(const g of t.filter(w=>w.parentId===s))!g.type&&Ct(g.url)&&!i.has(g.url)&&(i.add(g.url),c.add(g.id)),l(g.id)};return l(null),c}const Te='<button type="button" class="btn btn-sm btn-ghost mex-kebab" aria-label="Actions" title="Actions (right-click, or Shift+F10)">\u22EE</button>';function Xt(t,c,i=!1){const l=i?mt(c):new Set,s=[];function g(w){for(const m of c.filter(F=>F.parentId===w)){const A=`style="margin-left:${Yt(m.id,c)*28}px"`;if(m.type==="separator"){s.push(`
2
+ <div class="menu-tree-row menu-tree-row--separator" data-id="${m.id}" tabindex="0" ${A}>
3
3
  <span class="menu-tree-grip" data-icon="grip-vertical" data-icon-size="18" title="Drag to move"></span>
4
4
  <span class="me-sep-line"><span class="me-sep-label">Separator</span></span>
5
- ${Ue}
6
- </div>`);continue}if(f.type==="spacer"){const te=["xs","sm","md","lg","xl","flex"].map(F=>`<option value="${F}"${(f.size||"md")===F?" selected":""}>${F==="flex"?"flexible":F}</option>`).join("");s.push(`
7
- <div class="menu-tree-row menu-tree-row--spacer" data-id="${f.id}" tabindex="0" ${_}>
5
+ ${Te}
6
+ </div>`);continue}if(m.type==="spacer"){const te=["xs","sm","md","lg","xl","flex"].map(B=>`<option value="${B}"${(m.size||"md")===B?" selected":""}>${B==="flex"?"flexible":B}</option>`).join("");s.push(`
7
+ <div class="menu-tree-row menu-tree-row--spacer" data-id="${m.id}" tabindex="0" ${A}>
8
8
  <span class="menu-tree-grip" data-icon="grip-vertical" data-icon-size="18" title="Drag to move"></span>
9
9
  <span class="me-spacer-line">
10
10
  <span class="me-spacer-label">Spacer</span>
11
11
  <select class="form-input me-spacer-size" title="Spacer size - flexible fills the leftover room" aria-label="Spacer size">${te}</select>
12
12
  </span>
13
- ${Ue}
14
- </div>`);continue}const Y=f.badge&&(f.badge.text||f.badge.countFrom)?`<span class="dm-menu-badge">${b(String(f.badge.text||"#"))}</span>`:"",A=f.tool,h=A&&(A.persisted||f.touched),K=A?A.orphan?' <span class="badge badge-secondary me-tool-badge" title="A placeholder for a Tool that is not enabled - it returns here when it is">Not enabled</span>':` <span class="badge badge-secondary me-tool-badge" title="${h?"A Tool, placed here by this menu":"A Tool in its default place - move or edit it to place it here"}">${b(A.source||"Tool")}${h?"":" \xB7 default"}</span>`:"",oe=i.has(f.id),ee=kt(f.colour),W=ee?` style="color:${b(ee)}"`:"",ae=oe?'<button type="button" class="btn btn-sm btn-ghost" disabled title="Always shown - it can be moved, not hidden" aria-label="Always shown"><span data-icon="lock"></span></button>':`<button type="button" class="btn btn-sm btn-ghost me-hidden ${f.hidden?"active":""}" title="${f.hidden?"Hidden - click to show":"Hide"}" aria-label="${f.hidden?"Show":"Hide"}" aria-pressed="${f.hidden?"true":"false"}"><span data-icon="${f.hidden?"eye-off":"eye"}"></span></button>`;s.push(`
15
- <div class="menu-tree-row menu-tree-row--slim${f.hidden?" menu-tree-row--hidden":""}${A?" menu-tree-row--tool":""}${A&&!h?" menu-tree-row--default":""}" data-id="${f.id}" tabindex="0" ${_}>
13
+ ${Te}
14
+ </div>`);continue}if(m.type==="theme-switcher"){s.push(`
15
+ <div class="menu-tree-row menu-tree-row--switcher" data-id="${m.id}" tabindex="0" ${A}>
16
+ <span class="menu-tree-grip" data-icon="grip-vertical" data-icon-size="18" title="Drag to move"></span>
17
+ <span class="me-sep-line"><span class="me-sep-label"><span data-icon="palette"></span> Theme switcher - the themes chosen under Theme &gt; Theme switcher</span></span>
18
+ ${Te}
19
+ </div>`);continue}const X=m.badge&&(m.badge.text||m.badge.countFrom)?`<span class="dm-menu-badge">${b(String(m.badge.text||"#"))}</span>`:"",C=m.tool,h=C&&(C.persisted||m.touched),W=C?C.orphan?' <span class="badge badge-secondary me-tool-badge" title="A placeholder for a Tool that is not enabled - it returns here when it is">Not enabled</span>':` <span class="badge badge-secondary me-tool-badge" title="${h?"A Tool, placed here by this menu":"A Tool in its default place - move or edit it to place it here"}">${b(C.source||"Tool")}${h?"":" \xB7 default"}</span>`:"",ae=l.has(m.id),ee=kt(m.colour),q=ee?` style="color:${b(ee)}"`:"",se=ae?'<button type="button" class="btn btn-sm btn-ghost" disabled title="Always shown - it can be moved, not hidden" aria-label="Always shown"><span data-icon="lock"></span></button>':`<button type="button" class="btn btn-sm btn-ghost me-hidden ${m.hidden?"active":""}" title="${m.hidden?"Hidden - click to show":"Hide"}" aria-label="${m.hidden?"Show":"Hide"}" aria-pressed="${m.hidden?"true":"false"}"><span data-icon="${m.hidden?"eye-off":"eye"}"></span></button>`;s.push(`
20
+ <div class="menu-tree-row menu-tree-row--slim${m.hidden?" menu-tree-row--hidden":""}${C?" menu-tree-row--tool":""}${C&&!h?" menu-tree-row--default":""}" data-id="${m.id}" tabindex="0" ${A}>
16
21
  <span class="menu-tree-grip" data-icon="grip-vertical" data-icon-size="18" title="Drag to move"></span>
17
- <span class="me-row-label${!f.text&&!A?" mex-untitled":""}"${W}>${b(f.text||A?.label||"(no label - click to add one)")}${Y}${K}</span>
18
- <span class="me-row-url">${b(f.url||"")}</span>
19
- ${ae}
22
+ <span class="me-row-label${!m.text&&!C?" mex-untitled":""}"${q}>${b(m.text||C?.label||"(no label - click to add one)")}${X}${W}</span>
23
+ <span class="me-row-url">${b(m.url||"")}</span>
24
+ ${se}
20
25
  <button type="button" class="btn btn-sm btn-ghost me-edit" title="Edit" aria-label="Edit"><span data-icon="edit"></span></button>
21
- ${Ue}
22
- </div>`),g(f.id)}}g(null),t.innerHTML=s.join("")||'<p class="mex-note">No items yet - Add item, or right-click an item for more.</p>',I.scan(t)}function y(t,c={},...l){const i=document.createElement(t);Object.assign(i,c);for(const s of l)s!=null&&i.append(s);return i}function T(t,c,l){const i=y("label",{className:"form-field"},y("span",{className:"form-label",textContent:t}),c);return l&&i.append(y("small",{className:"form-hint",textContent:l,style:"display:block;font-size:.78em;opacity:.7;margin-top:.25rem;line-height:1.35"})),i}function Jt(t,c){const l=[["","- Everyone -"],["public","Public - anyone, incl. logged-out"],...(c||[]).map(s=>[s.name,`${s.label||s.name} and above`]),...(c||[]).filter(s=>s.level!==0).map(s=>[`=${s.name}`,`${s.label||s.name} only`]),["private","Private - super-admin only"]];t&&!l.some(([s])=>s===t)&&l.push([t,`${t} (custom)`]);const i=Z(l,t||"");return i.name="visibility",i}function Z(t,c){const l=y("select",{className:"form-select"});return t.forEach(([i,s])=>l.add(new Option(s,i))),l.value=c??"",l}function Re(t,c){const l=typeof c=="string"&&c.startsWith("#"),i=Z([["","- none -"],["primary","primary"],["success","success"],["danger","danger"],["warning","warning"],["info","info"],["neutral","neutral"],["__custom__","Custom\u2026"]],l?"__custom__":c||""),s=y("input",{type:"color",className:"form-input me-colour-hex",value:l?c:"#000000"});s.style.display=l?"":"none",i.addEventListener("change",()=>{s.style.display=i.value==="__custom__"?"":"none"});const g=T(t,i);return g.append(s),g._read=()=>i.value==="__custom__"?s.value:i.value,g}function Qt(t,c){const l=y("input",{className:"form-input me-icon",name:"icon",value:c||""}),i=y("span",{className:"me-icon-preview"});c&&i.setAttribute("data-icon",c);const s=y("button",{type:"button",className:"btn btn-sm btn-ghost",textContent:"Browse\u2026"}),g=w=>{w?i.setAttribute("data-icon",w):i.removeAttribute("data-icon"),Domma.icons.scan(i.parentNode||i)};return s.addEventListener("click",()=>$t(w=>{l.value=w,g(w)})),l.addEventListener("input",()=>g(l.value.trim())),T(t,y("div",{className:"me-icon-field"},l,s,i))}function Te(...t){return y("div",{className:"me-field-grid"},...t)}async function Zt(t,c,l,i,s){const g=t.text||t.tool?.label||"",w=u=>y("h4",{className:"me-form-section",textContent:u}),f=T("Label",y("input",{className:"form-input",name:"text",value:t.text||""}),"The text shown for this item."),P=y("input",{className:"form-input",name:"url",value:t.url||""}),_=T("URL",P,t.tool||t.locked?"A Tool's address is its identity, so it cannot be changed here.":"Where it links. Must start with /, #, https://, or mailto: . Leave blank for a parent that only groups sub-items.");(t.tool||t.locked)&&(P.readOnly=!0);const Y=Qt("Icon",t.icon||""),A=Jt(t.visibility||"",i),h=T("Visibility",A,t.locked?"Always in the sidebar for everyone who may use it, so it cannot be restricted here.":'Who can see this item. "Role and above" shows it to that role and anyone more senior; "Role only" shows it to holders of that role alone (and the super-admin). Blank = everyone.');t.locked&&(A.disabled=!0);const K=Z([["","- no permission gate -"],...(c||[]).map(u=>[u.key,u.label||u.key])],t.permission||"");K.name="permission";const oe=T("Permission",K,t.tool||t.locked?"A Tool is guarded by its own permission, which always applies.":"Hide unless the viewer holds this specific permission. Independent of Visibility - both must pass.");(t.tool||t.locked)&&(K.disabled=!0);const ee=T("Badge text",y("input",{className:"form-input",name:"badgeText",value:t.badge?.text||""})),W=Z([["","- none -"],...(l||[]).map(u=>[u.slug,u.title||u.slug])],t.badge?.countFrom||"");W.name="badgeCount";const ae=T("Badge count from",W,"Show a live count of this collection\u2019s entries as the badge."),te=Re("Badge colour",t.badge?.variant||""),F=Z([["link","Link"],["pill","Pill"]],t.pill?"pill":"link");F.name="renderAs";const je=T("Render as",F,"Pill draws the item as a rounded chip. Pill styling applies to top-level navbar items."),q=T("Pill style",Z([["filled","Filled"],["outline","Outline"]],t.pill?.style||"filled"));q.querySelector("select").name="pillStyle";const me=Re("Pill colour",t.pill?.variant||""),se=y("div",{className:"me-pill-wrap"},Te(q,me));se.style.display=t.pill?"":"none",F.addEventListener("change",()=>{se.style.display=F.value==="pill"?"":"none"});const m=Re("Text colour",t.colour||""),D=Z([["","- use the menu default -"],...st.map(u=>[u,it[u]||u])],t.highlight?.hover||"");D.name="hlHover";const le=T("Hover effect",D,"Overrides the menu-wide hover effect for this item only."),pe=Z([["","- use the menu default -"],...lt.map(u=>[u,rt[u]||u])],t.highlight?.active||"");pe.name="hlActive";const Se=T("When selected",pe,"How this item is marked when it points at the page being viewed."),B=Re("Highlight colour",t.highlight?.accent||""),C=y("input",{className:"form-input",name:"class",value:t.class||"",placeholder:"nav-cta push-right",autocomplete:"off",spellcheck:!1}),ie=T("CSS classes",C,"Your own class names, separated by spaces, added to this item's <li> - target the link with .your-class > a. Style them in Custom CSS."),L=y("small",{className:"text-danger",style:"display:none;margin-top:.25rem"});ie.append(L);const U=y("button",{className:"btn btn-primary",textContent:"Apply",title:"Apply to the item (Ctrl+Enter) - Save the menu to keep it"}),N=y("div",{className:"me-item-form"},w("Link"),f,_,Y,w("Access"),Te(h,oe),w("Badge"),Te(ee,te),ae,w("Appearance"),je,se,m,w("Highlighting"),Te(le,Se),B,w("Styling"),ie,U),x=E.slideover({title:g?`Edit ${He(g)}`:"Edit menu item",size:"lg",position:"right"});x.element.appendChild(N);const fe=()=>JSON.stringify([...N.querySelectorAll("input, select, textarea")].map(u=>u.type==="checkbox"?u.checked:u.value)),$e=fe();let re=!1,ce=!1;jt(x);const G=x.close.bind(x);x.close=async u=>{if(!re&&u!==!0&&fe()!==$e){if(ce)return;ce=!0;const X=await E.confirm("Discard the changes to this item?");if(ce=!1,!X)return}return re=!0,G()},N.addEventListener("keydown",u=>{(u.ctrlKey||u.metaKey)&&u.key==="Enter"&&(u.preventDefault(),U.click())}),x.open();const de=x.element.closest(".dm-slideover")||x.element.querySelector(".dm-slideover")||x.element;de.style.setProperty("width",Ut+"px","important"),de.style.setProperty("max-width","95vw","important"),Domma.icons.scan(N);const k=u=>N.querySelector(`[name="${u}"]`)?.value||"";U.addEventListener("click",()=>{const u=ct(k("class"));if(u.length){L.textContent=u.join("; "),L.style.display="block",C.focus();return}t.class=St(k("class")),t.text=k("text"),t.icon=k("icon"),t.locked||(t.visibility=k("visibility")),!t.tool&&!t.locked?(t.url=k("url"),t.permission=k("permission")):t.touched=!0;const X=k("badgeText"),ue=k("badgeCount"),he=te._read();if(t.badge=X||ue?{...X&&{text:X},...ue&&{countFrom:ue},...he&&{variant:he}}:null,k("renderAs")==="pill"){const ye=me._read();t.pill={style:k("pillStyle")||"filled",...ye&&{variant:ye}}}else t.pill=null;t.colour=m._read();const ve=k("hlHover"),ge=k("hlActive"),be=B._read();t.highlight=ve||ge||be?{...ve&&{hover:ve},...ge&&{active:ge},...be&&{accent:be}}:null,re=!0,x.close(!0),s()})}let z=null,pt=!1;function en(){pt||!window.R?.use||(pt=!0,R.use((t,c,l)=>{const i=c?.path||"";return!(Vt.test(i)&&(t?.path||"")!==i)||!z?.dirty()?l():E.confirm(`You have unsaved changes to the menu "${He(z.name())}". Leave anyway? They are kept as a draft for your next visit.`).then(g=>{g?l():history.replaceState(null,"",`#${i}`)})}),M.subscribe("router:afterChange",({to:t}={})=>{if(z&&(t?.path||"")!==z.path){const c=z;z=null,c.dispose()}}))}function ft(t,c){const l=t.querySelector(".dm-plugin-banner-name"),i=l?.querySelector(".dm-plugin-banner-sep");if(!(!l||!i)){for(;i.nextSibling;)i.nextSibling.remove();l.appendChild(document.createTextNode(c))}}export const menuEditorView={templateUrl:"/admin/js/templates/menu-editor.html",async onMount(t){if(en(),z){const e=z;z=null,e.dispose()}const c=[],l=e=>(c.push(e),e);let i=!1;const s=t.get(0).querySelector(".mex")||t.get(0),g=()=>!i&&s.isConnected,w=location.hash.replace(/^#/,""),f=location.hash.match(/^#\/menus\/edit\/([^?#]+)/),P=f?decodeURIComponent(f[1]):null,_=!P,Y=`menu-editor.draft.${P||"new"}`,A=E.loader(t.get(0),{type:"dots"});let h,K,oe,ee,W,ae,te;try{if(_)h={slug:"",name:"",description:"",items:[]};else try{h=await j.menus.get(P)}catch(e){E.toast(`Failed to load menu: ${e.message||e}`,{type:"error"}),location.hash="#/menus";return}[K,oe,ee,W,ae,te]=await Promise.all([j.projects.list().catch(()=>[]),H.get("/api/auth/permissions-registry").catch(()=>({resources:[]})),j.collections.list().catch(()=>[]),j.get("/collections/roles/entries?limit=100").catch(()=>null),j.menuLocations.registry().catch(()=>[]),j.pages.list().catch(()=>[])])}finally{A.destroy()}if(!s.isConnected)return;const je=(Array.isArray(W)?W:W?.entries||[]).map(e=>({name:e.data?.name,label:e.data?.label,level:e.data?.level??99})).filter(e=>e.name).sort((e,n)=>n.level-e.level),q=!_&&P===Gt;let me=[];if(q&&(me=await H.get("/api/sidebar/registered-items").catch(()=>(E.toast("Could not load the Tools - they are not shown, and saving keeps their places as they are.",{type:"warning",duration:8e3}),[])),!s.isConnected))return;const se=e=>dt(q?At(Et(e||[],me,{forEditor:!0}),{leaveInHiddenFolders:!0}):e);let m=se(h.items);const D=M.observable(!1),le=M.observable(!1),pe=M.observable(""),Se=M.observable(0);let B="";const C=s.querySelector("#me-items-list");function ie(){Xt(C,m,q),Se.value=m.filter(e=>!ne(e)).length}const L=()=>{ie(),V()},U=()=>q?mt(m):new Set,N=e=>m.find(n=>n.id===e);function x(e){requestAnimationFrame(()=>C.querySelector(`.menu-tree-row[data-id="${CSS.escape(e)}"]`)?.focus())}function fe(e){const n=v=>m.find(r=>!r.type&&!r.url&&!r.tool&&(r.key||"").toLowerCase()===v)||m.find(r=>!r.type&&!r.url&&!r.tool&&!r.key&&(r.text||"").toLowerCase()===v);let a=n(String(e.tool.folder||"").toLowerCase())||n(Ht);const p=String(e.tool.group||"").trim();if(a&&p){const v=Lt(a.key||_t(a.text),p),r=d=>d!==e&&!d.type&&!d.url&&!d.tool;let $=m.find(d=>r(d)&&(d.key||"").toLowerCase()===v)||m.find(d=>r(d)&&d.parentId===a.id&&(!d.key||d.key.toLowerCase()===v)&&(d.text||"").toLowerCase()===p.toLowerCase());$||([$]=dt([{text:p,key:v,icon:It,synthesised:!0}],a.id),m.push($)),a=$}e.parentId=a?a.id:null,m.splice(m.indexOf(e),1),m.push(e),Object.assign(e,{text:e.tool.defaults.text,icon:e.tool.defaults.icon,visibility:"",hidden:!1,badge:null,pill:null,colour:"",highlight:null,touched:!1}),e.tool={...e.tool,persisted:!1}}const $e=e=>!U().has(e.id)&&!(e.tool&&!e.tool.orphan);async function re(e){const n=N(e);if(!n||!$e(n))return;const a=d=>{let Q=d;for(;Q;){if(Q.id===e)return!0;Q=m.find(O=>O.id===Q.parentId)}return!1},p=m.filter(d=>d.id!==e&&a(d));if(p.length&&!await E.confirm(`Remove "${He(n.text||n.type||"this item")}" and the ${p.length} item${p.length===1?"":"s"} beneath it?`))return;const v=U(),r=p.filter(d=>d.tool),$=p.filter(d=>!d.tool&&v.has(d.id));m=m.filter(d=>!a(d)||r.includes(d)||$.includes(d));for(const d of $)d.parentId=n.parentId??null;for(const d of r)d.tool.orphan?d.parentId=n.parentId??null:fe(d);L()}function ce(e){const n=N(e);!n||ne(n)||U().has(e)||(n.hidden=!n.hidden,n.touched=!0,L())}function G(e){const n=N(e);!n||ne(n)||(n.locked=q&&U().has(n.id),Zt(n,oe.resources||[],ee,je,()=>{L(),x(e)}))}function de(e){const n={id:_e(),parentId:null,...e==="separator"?{type:e}:e==="spacer"?{type:e,size:"md"}:{text:"",url:"/",icon:"",visibility:"",permission:"",hidden:!1,bundled:!1}};m.push(n),L(),ne(n)?x(n.id):G(n.id)}const k=e=>n=>{e(n)&&(L(),x(n))},u=k(e=>ut(m,e,-1)),X=k(e=>ut(m,e,1)),ue=k(e=>Ft(m,e)),he=k(e=>Kt(m,e));function ve(e){const n=N(e);if(!n)return[];const a=ne(n),p=U().has(e),v=Wt(m,e),r=v.findIndex(O=>O.id===e),$=n.tool,d=$&&($.persisted||n.touched),Q=O=>()=>{const ke=Bt(m,e,O,_e());L(),ke&&!ne(ke)?G(ke.id):ke&&x(ke.id)};return[{header:a?n.type==="separator"?"Separator":"Spacer":n.text||$?.label||"(untitled)"},...a?[]:[{label:"Edit\u2026",icon:"edit",action:()=>G(e)}],{type:"divider"},{label:"Add item below",icon:"plus",action:Q("item")},{label:"Add sub-item",icon:"corner-down-right",disabled:a,action:()=>{const O=Nt(m,e,_e());L(),O&&G(O.id)}},{label:"Add separator below",icon:"minus",action:Q("separator")},{label:"Add spacer below",icon:"move",action:Q("spacer")},{type:"divider"},{label:"Move up",icon:"arrow-up",shortcut:"Alt+\u2191",disabled:r<=0,action:()=>u(e)},{label:"Move down",icon:"arrow-down",shortcut:"Alt+\u2193",disabled:r<0||r>=v.length-1,action:()=>X(e)},{label:"Indent",icon:"chevron-right",shortcut:"Alt+\u2192",disabled:r<=0||ne(v[r-1]),action:()=>ue(e)},{label:"Outdent",icon:"chevron-left",shortcut:"Alt+\u2190",disabled:n.parentId==null,action:()=>he(e)},{type:"divider"},...a?[]:[{label:n.hidden?"Show":"Hide",icon:n.hidden?"eye":"eye-off",disabled:p,action:()=>ce(e)}],{label:"Duplicate",icon:"copy",disabled:!!$,action:()=>{const O=Pt(m,e,_e);L(),O&&x(O.id)}},...$&&!$.orphan&&d?[{label:"Back to its default place",icon:"rotate-ccw",action:()=>{fe(n),L()}}]:[],{label:p?"Remove - always in the sidebar":$&&!$.orphan?"Remove - a Tool is hidden, not removed":"Remove\u2026",icon:"trash",danger:!0,disabled:!$e(n),action:()=>re(e)}]}const ge=E.contextMenu(C,{cursor:"glyph",match:".menu-tree-row",inherit:!1,density:"compact",items:e=>ve(e.dataset.id)});l(()=>ge?.destroy?.());const be=xt(C,()=>m,L,{items:".menu-tree-row",handle:".menu-tree-grip"});l(()=>be?.destroy());const ye=e=>{const n=e.target.closest(".menu-tree-row");if(!n)return;const a=n.dataset.id;if(e.target.closest(".mex-kebab")){qe(e.target.closest(".mex-kebab"));return}if(e.target.closest(".me-hidden")){ce(a);return}if(e.target.closest(".me-edit")){G(a);return}e.target.closest("select, .menu-tree-grip, button")||G(a)},Ge=e=>{const n=e.target.closest(".menu-tree-row");if(!n||e.target!==n){n&&e.key==="F10"&&e.shiftKey&&(e.preventDefault(),qe(n));return}const a=n.dataset.id;if(e.key==="F10"&&e.shiftKey)e.preventDefault(),qe(n);else if(e.key==="Enter")e.preventDefault(),G(a);else if(e.key==="Delete")e.preventDefault(),re(a);else if(e.altKey&&e.key==="ArrowUp")e.preventDefault(),u(a);else if(e.altKey&&e.key==="ArrowDown")e.preventDefault(),X(a);else if(e.altKey&&e.key==="ArrowRight")e.preventDefault(),ue(a);else if(e.altKey&&e.key==="ArrowLeft")e.preventDefault(),he(a);else if(e.key==="ArrowDown"||e.key==="ArrowUp"){const p=[...C.querySelectorAll(".menu-tree-row")],v=p.indexOf(n)+(e.key==="ArrowDown"?1:-1);p[v]&&(e.preventDefault(),p[v].focus())}},Ve=e=>{if(!e.target.matches(".me-spacer-size"))return;const n=N(e.target.closest(".menu-tree-row")?.dataset.id);n&&(n.size=e.target.value,V())};C.addEventListener("click",ye),C.addEventListener("keydown",Ge),C.addEventListener("change",Ve),l(()=>{C.removeEventListener("click",ye),C.removeEventListener("keydown",Ge),C.removeEventListener("change",Ve)});const o=e=>t.find(e),xe=o("#me-float-accent"),De=o("#me-float-accent-hex"),Ne=()=>{De.get(0).style.display=xe.val()==="__custom__"?"":"none"},ht=()=>xe.val()==="__custom__"?De.val():xe.val(),Ee=o("#me-highlight-accent"),Oe=o("#me-highlight-accent-hex"),Ye=()=>{Oe.get(0).style.display=Ee.val()==="__custom__"?"":"none"},vt=()=>Ee.val()==="__custom__"?Oe.val():Ee.val(),Xe=()=>{const e=o("#me-position").val()==="floating";o(".dm-float-field").each(function(){this.style.display=e?"":"none"}),e&&Ne();const n=o("#me-orientation").val()==="vertical";o(".dm-vertical-field").each(function(){this.style.display=n?"":"none"});const a=!!(o("#me-float-collapsible").get(0)||{}).checked;o(".dm-float-collapsed-field").each(function(){this.style.display=e&&a?"":"none"})},Je=(e,n,a,p)=>{const v=o(e).get(0);v&&(v.innerHTML="",v.add(new Option(p,"")),n.forEach(r=>v.add(new Option(a[r]||r,r))))};Je("#me-highlight-hover",st,it,"- theme default -"),Je("#me-highlight-active",lt,rt,"- theme default -");const Pe=o("#me-bind-slot"),Fe=o("#me-bind-projects"),Ae=o("#me-bind-pages");Pe.html('<option value="">- not bound -</option>'+(ae||[]).map(e=>`<option value="${b(e.slot)}">${b(e.label||e.slot)} (${b(e.slot)})</option>`).join("")),Fe.html(K.map(e=>`<option value="${b(e.slug)}">${b(e.name||e.slug)}</option>`).join("")),o("#me-bind-page-pick").html('<option value="">- pick a page -</option>'+te.map(e=>{const n=e.urlPath||e.url||"";return n?`<option value="${b(n)}">${b(e.title||n)} - ${b(n)}</option>`:""}).join(""));const Qe=()=>Ot({slot:Pe.val(),projects:Array.from(Fe.get(0).selectedOptions).map(e=>e.value),pagesText:Ae.val()});function Be(){const e=Qe(),n=o("#me-bind-summary");if(!e){n.html('<span class="text-muted">Not bound - this menu renders only where it is mapped or placed by a shortcode.</span>');return}const a=[];e.projects.length&&a.push(`pages in ${e.projects.map(r=>`<code>${b(r)}</code>`).join(", ")}`),e.pages.length&&a.push(`pages matching ${e.pages.map(r=>`<code>${b(r)}</code>`).join(", ")}`);const p=e.pages.filter(r=>!/^\/[^\s?#*]*(\/\*)?$|^\/\*$/.test(r)),v=p.length?`<div class="text-danger" style="margin-top:.4rem">Not valid page patterns: ${p.map(r=>`<code>${b(r)}</code>`).join(", ")}</div>`:"";n.html(a.length?`Takes over the <code>${b(e.slot)}</code> slot on ${a.join(" and ")}.${v}`:`<span class="text-warning">Slot <code>${b(e.slot)}</code> selected but no project or page chosen - the binding will never match.</span>`)}const{getProjectFromHash:gt}=await import("../lib/project-context.js");if(!g())return;const bt=_?gt():null;o("#me-project").html('<option value="">- none -</option>'+K.map(e=>`<option value="${b(e.slug)}">${b(e.name||e.slug)}</option>`).join(""));function ze(e){m=se(e.items),o("#me-variant").val(e.variant||""),o("#me-position").val(e.position||"");const n=o("#me-appearOnHover").get(0);n&&(n.checked=e.appearOnHover===!0),o("#me-float-anchor").val(e.float?.anchor||"TL"),o("#me-float-offsetX").val(e.float?.offsetX??""),o("#me-float-offsetY").val(e.float?.offsetY??""),o("#me-float-width").val(e.float?.width??""),o("#me-float-radius").val(e.float?.radius??""),o("#me-float-shadow").val(e.float?.shadow||""),o("#me-float-opacity").val(e.float?.opacity??""),o("#me-float-title").val(e.float?.title||"");for(const[r,$]of[["#me-float-collapsible",e.float?.collapsible],["#me-float-collapsed",e.float?.collapsed],["#me-float-movable",e.float?.movable],["#me-float-resizable",e.float?.resizable]]){const d=o(r).get(0);d&&(d.checked=$===!0)}const a=e.float?.accent||"";xe.val(a.startsWith("#")?"__custom__":a),a.startsWith("#")&&De.val(a),Ne(),o("#me-orientation").val(e.orientation||""),o("#me-align").val(e.align||""),o("#me-side").val(e.side||"left"),o("#me-class").val(e.class||""),o("#me-highlight-hover").val(e.highlight?.hover||""),o("#me-highlight-active").val(e.highlight?.active||"");const p=e.highlight?.accent||"";Ee.val(p.startsWith("#")?"__custom__":p),p.startsWith("#")&&Oe.val(p),Ye(),o("#me-fontFamily").val(e.style?.fontFamily||""),o("#me-fontSize").val(e.style?.fontSize||""),o("#me-fontWeight").val(e.style?.fontWeight||""),o("#me-letterSpacing").val(e.style?.letterSpacing||""),o("#me-iconSize").val(e.style?.iconSize||""),Pe.val(e.binding?.slot||"");const v=new Set(e.binding?.projects||[]);Array.from(Fe.get(0).options).forEach(r=>{r.selected=v.has(r.value)}),Ae.val((e.binding?.pages||[]).join(`
23
- `)),o("#me-slug").val(e.slug||""),o("#me-name").val(e.name||""),o("#me-description").val(e.description||""),o("#me-project").val(e.meta?.project||bt||""),Xe(),Be(),ie()}const we=e=>!!(o(e).get(0)||{}).checked;function Ce(){const e={variant:o("#me-variant").val(),position:o("#me-position").val(),appearOnHover:we("#me-appearOnHover"),floatAnchor:o("#me-float-anchor").val(),floatOffsetX:o("#me-float-offsetX").val(),floatOffsetY:o("#me-float-offsetY").val(),floatWidth:o("#me-float-width").val(),floatRadius:o("#me-float-radius").val(),floatShadow:o("#me-float-shadow").val(),floatAccent:ht(),floatOpacity:o("#me-float-opacity").val(),floatTitle:o("#me-float-title").val(),floatCollapsible:we("#me-float-collapsible"),floatCollapsed:we("#me-float-collapsed"),floatMovable:we("#me-float-movable"),floatResizable:we("#me-float-resizable"),highlightHover:o("#me-highlight-hover").val(),highlightActive:o("#me-highlight-active").val(),highlightAccent:vt(),class:o("#me-class").val(),orientation:o("#me-orientation").val(),align:o("#me-align").val(),side:o("#me-side").val(),fontFamily:o("#me-fontFamily").val(),fontSize:o("#me-fontSize").val(),fontWeight:o("#me-fontWeight").val(),letterSpacing:o("#me-letterSpacing").val(),iconSize:o("#me-iconSize").val()},n={slug:o("#me-slug").val(),name:o("#me-name").val(),description:o("#me-description").val(),project:o("#me-project").val()};return zt(e,n,Qe(),Rt(m),h.meta||{})}const Ze=e=>[...Mt(e),...ct(e.class).map(n=>`CSS classes: ${n} (Behaviour tab).`)];let et=!1,Me=null;function V(){if(!B)return;const e=Ce();Me=e,D.value=Ie(e)!==B,pe.value=D.peek()||et?Ze(e).join(" "):""}const Le=e=>{e.target.closest("#me-items-list")||(e.target.matches("#me-position, #me-orientation, #me-float-collapsible")&&Xe(),e.target.matches("#me-float-accent")&&Ne(),e.target.matches("#me-highlight-accent")&&Ye(),e.target.closest("#me-binding")&&Be(),V())};s.addEventListener("input",Le),s.addEventListener("change",Le),l(()=>{s.removeEventListener("input",Le),s.removeEventListener("change",Le)}),o("#me-bind-page-add").on("click",()=>{const e=o("#me-bind-page-pick").val();if(!e)return;const n=Ae.val().split(`
26
+ ${Te}
27
+ </div>`),g(m.id)}}g(null),t.innerHTML=s.join("")||'<p class="mex-note">No items yet - Add item, or right-click an item for more.</p>',I.scan(t)}function y(t,c={},...i){const l=document.createElement(t);Object.assign(l,c);for(const s of i)s!=null&&l.append(s);return l}function T(t,c,i){const l=y("label",{className:"form-field"},y("span",{className:"form-label",textContent:t}),c);return i&&l.append(y("small",{className:"form-hint",textContent:i,style:"display:block;font-size:.78em;opacity:.7;margin-top:.25rem;line-height:1.35"})),l}function Jt(t,c){const i=[["","- Everyone -"],["public","Public - anyone, incl. logged-out"],...(c||[]).map(s=>[s.name,`${s.label||s.name} and above`]),...(c||[]).filter(s=>s.level!==0).map(s=>[`=${s.name}`,`${s.label||s.name} only`]),["private","Private - super-admin only"]];t&&!i.some(([s])=>s===t)&&i.push([t,`${t} (custom)`]);const l=Z(i,t||"");return l.name="visibility",l}function Z(t,c){const i=y("select",{className:"form-select"});return t.forEach(([l,s])=>i.add(new Option(s,l))),i.value=c??"",i}function Re(t,c){const i=typeof c=="string"&&c.startsWith("#"),l=Z([["","- none -"],["primary","primary"],["success","success"],["danger","danger"],["warning","warning"],["info","info"],["neutral","neutral"],["__custom__","Custom\u2026"]],i?"__custom__":c||""),s=y("input",{type:"color",className:"form-input me-colour-hex",value:i?c:"#000000"});s.style.display=i?"":"none",l.addEventListener("change",()=>{s.style.display=l.value==="__custom__"?"":"none"});const g=T(t,l);return g.append(s),g._read=()=>l.value==="__custom__"?s.value:l.value,g}function Qt(t,c){const i=y("input",{className:"form-input me-icon",name:"icon",value:c||""}),l=y("span",{className:"me-icon-preview"});c&&l.setAttribute("data-icon",c);const s=y("button",{type:"button",className:"btn btn-sm btn-ghost",textContent:"Browse\u2026"}),g=w=>{w?l.setAttribute("data-icon",w):l.removeAttribute("data-icon"),Domma.icons.scan(l.parentNode||l)};return s.addEventListener("click",()=>$t(w=>{i.value=w,g(w)})),i.addEventListener("input",()=>g(i.value.trim())),T(t,y("div",{className:"me-icon-field"},i,s,l))}function je(...t){return y("div",{className:"me-field-grid"},...t)}async function Zt(t,c,i,l,s){const g=t.text||t.tool?.label||"",w=u=>y("h4",{className:"me-form-section",textContent:u}),m=T("Label",y("input",{className:"form-input",name:"text",value:t.text||""}),"The text shown for this item."),F=y("input",{className:"form-input",name:"url",value:t.url||""}),A=T("URL",F,t.tool||t.locked?"A Tool's address is its identity, so it cannot be changed here.":"Where it links. Must start with /, #, https://, or mailto: . Leave blank for a parent that only groups sub-items.");(t.tool||t.locked)&&(F.readOnly=!0);const X=Qt("Icon",t.icon||""),C=Jt(t.visibility||"",l),h=T("Visibility",C,t.locked?"Always in the sidebar for everyone who may use it, so it cannot be restricted here.":'Who can see this item. "Role and above" shows it to that role and anyone more senior; "Role only" shows it to holders of that role alone (and the super-admin). Blank = everyone.');t.locked&&(C.disabled=!0);const W=Z([["","- no permission gate -"],...(c||[]).map(u=>[u.key,u.label||u.key])],t.permission||"");W.name="permission";const ae=T("Permission",W,t.tool||t.locked?"A Tool is guarded by its own permission, which always applies.":"Hide unless the viewer holds this specific permission. Independent of Visibility - both must pass.");(t.tool||t.locked)&&(W.disabled=!0);const ee=T("Badge text",y("input",{className:"form-input",name:"badgeText",value:t.badge?.text||""})),q=Z([["","- none -"],...(i||[]).map(u=>[u.slug,u.title||u.slug])],t.badge?.countFrom||"");q.name="badgeCount";const se=T("Badge count from",q,"Show a live count of this collection\u2019s entries as the badge."),te=Re("Badge colour",t.badge?.variant||""),B=Z([["link","Link"],["pill","Pill"]],t.pill?"pill":"link");B.name="renderAs";const De=T("Render as",B,"Pill draws the item as a rounded chip. Pill styling applies to top-level navbar items."),D=T("Pill style",Z([["filled","Filled"],["outline","Outline"]],t.pill?.style||"filled"));D.querySelector("select").name="pillStyle";const me=Re("Pill colour",t.pill?.variant||""),ie=y("div",{className:"me-pill-wrap"},je(D,me));ie.style.display=t.pill?"":"none",B.addEventListener("change",()=>{ie.style.display=B.value==="pill"?"":"none"});const p=Re("Text colour",t.colour||""),N=Z([["","- use the menu default -"],...st.map(u=>[u,lt[u]||u])],t.highlight?.hover||"");N.name="hlHover";const le=T("Hover effect",N,"Overrides the menu-wide hover effect for this item only."),pe=Z([["","- use the menu default -"],...it.map(u=>[u,rt[u]||u])],t.highlight?.active||"");pe.name="hlActive";const Se=T("When selected",pe,"How this item is marked when it points at the page being viewed."),z=Re("Highlight colour",t.highlight?.accent||""),L=y("input",{className:"form-input",name:"class",value:t.class||"",placeholder:"nav-cta push-right",autocomplete:"off",spellcheck:!1}),re=T("CSS classes",L,"Your own class names, separated by spaces, added to this item's <li> - target the link with .your-class > a. Style them in Custom CSS."),_=y("small",{className:"text-danger",style:"display:none;margin-top:.25rem"});re.append(_);const U=y("button",{className:"btn btn-primary",textContent:"Apply",title:"Apply to the item (Ctrl+Enter) - Save the menu to keep it"}),O=y("div",{className:"me-item-form"},w("Link"),m,A,X,w("Access"),je(h,ae),w("Badge"),je(ee,te),se,w("Appearance"),De,ie,p,w("Highlighting"),je(le,Se),z,w("Styling"),re,U),x=E.slideover({title:g?`Edit ${He(g)}`:"Edit menu item",size:"lg",position:"right"});x.element.appendChild(O);const fe=()=>JSON.stringify([...O.querySelectorAll("input, select, textarea")].map(u=>u.type==="checkbox"?u.checked:u.value)),$e=fe();let ce=!1,de=!1;jt(x);const G=x.close.bind(x);x.close=async u=>{if(!ce&&u!==!0&&fe()!==$e){if(de)return;de=!0;const J=await E.confirm("Discard the changes to this item?");if(de=!1,!J)return}return ce=!0,G()},O.addEventListener("keydown",u=>{(u.ctrlKey||u.metaKey)&&u.key==="Enter"&&(u.preventDefault(),U.click())}),x.open();const ne=x.element.closest(".dm-slideover")||x.element.querySelector(".dm-slideover")||x.element;ne.style.setProperty("width",Ut+"px","important"),ne.style.setProperty("max-width","95vw","important"),Domma.icons.scan(O);const k=u=>O.querySelector(`[name="${u}"]`)?.value||"";U.addEventListener("click",()=>{const u=ct(k("class"));if(u.length){_.textContent=u.join("; "),_.style.display="block",L.focus();return}t.class=St(k("class")),t.text=k("text"),t.icon=k("icon"),t.locked||(t.visibility=k("visibility")),!t.tool&&!t.locked?(t.url=k("url"),t.permission=k("permission")):t.touched=!0;const J=k("badgeText"),ue=k("badgeCount"),he=te._read();if(t.badge=J||ue?{...J&&{text:J},...ue&&{countFrom:ue},...he&&{variant:he}}:null,k("renderAs")==="pill"){const ye=me._read();t.pill={style:k("pillStyle")||"filled",...ye&&{variant:ye}}}else t.pill=null;t.colour=p._read();const ve=k("hlHover"),ge=k("hlActive"),be=z._read();t.highlight=ve||ge||be?{...ve&&{hover:ve},...ge&&{active:ge},...be&&{accent:be}}:null,ce=!0,x.close(!0),s()})}let K=null,pt=!1;function en(){pt||!window.R?.use||(pt=!0,R.use((t,c,i)=>{const l=c?.path||"";return!(Vt.test(l)&&(t?.path||"")!==l)||!K?.dirty()?i():E.confirm(`You have unsaved changes to the menu "${He(K.name())}". Leave anyway? They are kept as a draft for your next visit.`).then(g=>{g?i():history.replaceState(null,"",`#${l}`)})}),M.subscribe("router:afterChange",({to:t}={})=>{if(K&&(t?.path||"")!==K.path){const c=K;K=null,c.dispose()}}))}function ft(t,c){const i=t.querySelector(".dm-plugin-banner-name"),l=i?.querySelector(".dm-plugin-banner-sep");if(!(!i||!l)){for(;l.nextSibling;)l.nextSibling.remove();i.appendChild(document.createTextNode(c))}}export const menuEditorView={templateUrl:"/admin/js/templates/menu-editor.html",async onMount(t){if(en(),K){const e=K;K=null,e.dispose()}const c=[],i=e=>(c.push(e),e);let l=!1;const s=t.get(0).querySelector(".mex")||t.get(0),g=()=>!l&&s.isConnected,w=location.hash.replace(/^#/,""),m=location.hash.match(/^#\/menus\/edit\/([^?#]+)/),F=m?decodeURIComponent(m[1]):null,A=!F,X=`menu-editor.draft.${F||"new"}`,C=E.loader(t.get(0),{type:"dots"});let h,W,ae,ee,q,se,te;try{if(A)h={slug:"",name:"",description:"",items:[]};else try{h=await j.menus.get(F)}catch(e){E.toast(`Failed to load menu: ${e.message||e}`,{type:"error"}),location.hash="#/menus";return}[W,ae,ee,q,se,te]=await Promise.all([j.projects.list().catch(()=>[]),H.get("/api/auth/permissions-registry").catch(()=>({resources:[]})),j.collections.list().catch(()=>[]),j.get("/collections/roles/entries?limit=100").catch(()=>null),j.menuLocations.registry().catch(()=>[]),j.pages.list().catch(()=>[])])}finally{C.destroy()}if(!s.isConnected)return;const De=(Array.isArray(q)?q:q?.entries||[]).map(e=>({name:e.data?.name,label:e.data?.label,level:e.data?.level??99})).filter(e=>e.name).sort((e,n)=>n.level-e.level),D=!A&&F===Gt;let me=[];if(D&&(me=await H.get("/api/sidebar/registered-items").catch(()=>(E.toast("Could not load the Tools - they are not shown, and saving keeps their places as they are.",{type:"warning",duration:8e3}),[])),!s.isConnected))return;const ie=e=>dt(D?At(Et(e||[],me,{forEditor:!0}),{leaveInHiddenFolders:!0}):e);let p=ie(h.items);const N=M.observable(!1),le=M.observable(!1),pe=M.observable(""),Se=M.observable(0);let z="";const L=s.querySelector("#me-items-list");function re(){Xt(L,p,D),Se.value=p.filter(e=>!oe(e)).length}const _=()=>{re(),V()},U=()=>D?mt(p):new Set,O=e=>p.find(n=>n.id===e);function x(e){requestAnimationFrame(()=>L.querySelector(`.menu-tree-row[data-id="${CSS.escape(e)}"]`)?.focus())}function fe(e){const n=v=>p.find(r=>!r.type&&!r.url&&!r.tool&&(r.key||"").toLowerCase()===v)||p.find(r=>!r.type&&!r.url&&!r.tool&&!r.key&&(r.text||"").toLowerCase()===v);let a=n(String(e.tool.folder||"").toLowerCase())||n(Ht);const f=String(e.tool.group||"").trim();if(a&&f){const v=Lt(a.key||_t(a.text),f),r=d=>d!==e&&!d.type&&!d.url&&!d.tool;let $=p.find(d=>r(d)&&(d.key||"").toLowerCase()===v)||p.find(d=>r(d)&&d.parentId===a.id&&(!d.key||d.key.toLowerCase()===v)&&(d.text||"").toLowerCase()===f.toLowerCase());$||([$]=dt([{text:f,key:v,icon:It,synthesised:!0}],a.id),p.push($)),a=$}e.parentId=a?a.id:null,p.splice(p.indexOf(e),1),p.push(e),Object.assign(e,{text:e.tool.defaults.text,icon:e.tool.defaults.icon,visibility:"",hidden:!1,badge:null,pill:null,colour:"",highlight:null,touched:!1}),e.tool={...e.tool,persisted:!1}}const $e=e=>!U().has(e.id)&&!(e.tool&&!e.tool.orphan);async function ce(e){const n=O(e);if(!n||!$e(n))return;const a=d=>{let Y=d;for(;Y;){if(Y.id===e)return!0;Y=p.find(P=>P.id===Y.parentId)}return!1},f=p.filter(d=>d.id!==e&&a(d));if(f.length&&!await E.confirm(`Remove "${He(n.text||n.type||"this item")}" and the ${f.length} item${f.length===1?"":"s"} beneath it?`))return;const v=U(),r=f.filter(d=>d.tool),$=f.filter(d=>!d.tool&&v.has(d.id));p=p.filter(d=>!a(d)||r.includes(d)||$.includes(d));for(const d of $)d.parentId=n.parentId??null;for(const d of r)d.tool.orphan?d.parentId=n.parentId??null:fe(d);_()}function de(e){const n=O(e);!n||oe(n)||U().has(e)||(n.hidden=!n.hidden,n.touched=!0,_())}function G(e){const n=O(e);!n||oe(n)||(n.locked=D&&U().has(n.id),Zt(n,ae.resources||[],ee,De,()=>{_(),x(e)}))}function ne(e){const n={id:_e(),parentId:null,...e==="separator"||e==="theme-switcher"?{type:e}:e==="spacer"?{type:e,size:"md"}:{text:"",url:"/",icon:"",visibility:"",permission:"",hidden:!1,bundled:!1}};p.push(n),_(),oe(n)?x(n.id):G(n.id)}const k=e=>n=>{e(n)&&(_(),x(n))},u=k(e=>ut(p,e,-1)),J=k(e=>ut(p,e,1)),ue=k(e=>Ft(p,e)),he=k(e=>Kt(p,e));function ve(e){const n=O(e);if(!n)return[];const a=oe(n),f=U().has(e),v=Wt(p,e),r=v.findIndex(P=>P.id===e),$=n.tool,d=$&&($.persisted||n.touched),Y=P=>()=>{const ke=Bt(p,e,P,_e());_(),ke&&!oe(ke)?G(ke.id):ke&&x(ke.id)};return[{header:a?{separator:"Separator",spacer:"Spacer","theme-switcher":"Theme switcher"}[n.type]:n.text||$?.label||"(untitled)"},...a?[]:[{label:"Edit\u2026",icon:"edit",action:()=>G(e)}],{type:"divider"},{label:"Add item below",icon:"plus",action:Y("item")},{label:"Add sub-item",icon:"corner-down-right",disabled:a,action:()=>{const P=Nt(p,e,_e());_(),P&&G(P.id)}},{label:"Add separator below",icon:"minus",action:Y("separator")},{label:"Add spacer below",icon:"move",action:Y("spacer")},...D?[]:[{label:"Add theme switcher below",icon:"palette",action:Y("theme-switcher")}],{type:"divider"},{label:"Move up",icon:"arrow-up",shortcut:"Alt+\u2191",disabled:r<=0,action:()=>u(e)},{label:"Move down",icon:"arrow-down",shortcut:"Alt+\u2193",disabled:r<0||r>=v.length-1,action:()=>J(e)},{label:"Indent",icon:"chevron-right",shortcut:"Alt+\u2192",disabled:r<=0||oe(v[r-1]),action:()=>ue(e)},{label:"Outdent",icon:"chevron-left",shortcut:"Alt+\u2190",disabled:n.parentId==null,action:()=>he(e)},{type:"divider"},...a?[]:[{label:n.hidden?"Show":"Hide",icon:n.hidden?"eye":"eye-off",disabled:f,action:()=>de(e)}],{label:"Duplicate",icon:"copy",disabled:!!$,action:()=>{const P=Pt(p,e,_e);_(),P&&x(P.id)}},...$&&!$.orphan&&d?[{label:"Back to its default place",icon:"rotate-ccw",action:()=>{fe(n),_()}}]:[],{label:f?"Remove - always in the sidebar":$&&!$.orphan?"Remove - a Tool is hidden, not removed":"Remove\u2026",icon:"trash",danger:!0,disabled:!$e(n),action:()=>ce(e)}]}const ge=E.contextMenu(L,{cursor:"glyph",match:".menu-tree-row",inherit:!1,density:"compact",items:e=>ve(e.dataset.id)});i(()=>ge?.destroy?.());const be=xt(L,()=>p,_,{items:".menu-tree-row",handle:".menu-tree-grip"});i(()=>be?.destroy());const ye=e=>{const n=e.target.closest(".menu-tree-row");if(!n)return;const a=n.dataset.id;if(e.target.closest(".mex-kebab")){Ue(e.target.closest(".mex-kebab"));return}if(e.target.closest(".me-hidden")){de(a);return}if(e.target.closest(".me-edit")){G(a);return}e.target.closest("select, .menu-tree-grip, button")||G(a)},Ge=e=>{const n=e.target.closest(".menu-tree-row");if(!n||e.target!==n){n&&e.key==="F10"&&e.shiftKey&&(e.preventDefault(),Ue(n));return}const a=n.dataset.id;if(e.key==="F10"&&e.shiftKey)e.preventDefault(),Ue(n);else if(e.key==="Enter")e.preventDefault(),G(a);else if(e.key==="Delete")e.preventDefault(),ce(a);else if(e.altKey&&e.key==="ArrowUp")e.preventDefault(),u(a);else if(e.altKey&&e.key==="ArrowDown")e.preventDefault(),J(a);else if(e.altKey&&e.key==="ArrowRight")e.preventDefault(),ue(a);else if(e.altKey&&e.key==="ArrowLeft")e.preventDefault(),he(a);else if(e.key==="ArrowDown"||e.key==="ArrowUp"){const f=[...L.querySelectorAll(".menu-tree-row")],v=f.indexOf(n)+(e.key==="ArrowDown"?1:-1);f[v]&&(e.preventDefault(),f[v].focus())}},Ve=e=>{if(!e.target.matches(".me-spacer-size"))return;const n=O(e.target.closest(".menu-tree-row")?.dataset.id);n&&(n.size=e.target.value,V())};L.addEventListener("click",ye),L.addEventListener("keydown",Ge),L.addEventListener("change",Ve),i(()=>{L.removeEventListener("click",ye),L.removeEventListener("keydown",Ge),L.removeEventListener("change",Ve)});const o=e=>t.find(e),xe=o("#me-float-accent"),Ne=o("#me-float-accent-hex"),Oe=()=>{Ne.get(0).style.display=xe.val()==="__custom__"?"":"none"},ht=()=>xe.val()==="__custom__"?Ne.val():xe.val(),Ee=o("#me-highlight-accent"),Pe=o("#me-highlight-accent-hex"),Ye=()=>{Pe.get(0).style.display=Ee.val()==="__custom__"?"":"none"},vt=()=>Ee.val()==="__custom__"?Pe.val():Ee.val(),Xe=()=>{const e=o("#me-position").val()==="floating";o(".dm-float-field").each(function(){this.style.display=e?"":"none"}),e&&Oe();const n=o("#me-orientation").val()==="vertical";o(".dm-vertical-field").each(function(){this.style.display=n?"":"none"});const a=!!(o("#me-float-collapsible").get(0)||{}).checked;o(".dm-float-collapsed-field").each(function(){this.style.display=e&&a?"":"none"})},Je=(e,n,a,f)=>{const v=o(e).get(0);v&&(v.innerHTML="",v.add(new Option(f,"")),n.forEach(r=>v.add(new Option(a[r]||r,r))))};Je("#me-highlight-hover",st,lt,"- theme default -"),Je("#me-highlight-active",it,rt,"- theme default -");const Fe=o("#me-bind-slot"),Be=o("#me-bind-projects"),Ae=o("#me-bind-pages");Fe.html('<option value="">- not bound -</option>'+(se||[]).map(e=>`<option value="${b(e.slot)}">${b(e.label||e.slot)} (${b(e.slot)})</option>`).join("")),Be.html(W.map(e=>`<option value="${b(e.slug)}">${b(e.name||e.slug)}</option>`).join("")),o("#me-bind-page-pick").html('<option value="">- pick a page -</option>'+te.map(e=>{const n=e.urlPath||e.url||"";return n?`<option value="${b(n)}">${b(e.title||n)} - ${b(n)}</option>`:""}).join(""));const Qe=()=>Ot({slot:Fe.val(),projects:Array.from(Be.get(0).selectedOptions).map(e=>e.value),pagesText:Ae.val()});function ze(){const e=Qe(),n=o("#me-bind-summary");if(!e){n.html('<span class="text-muted">Not bound - this menu renders only where it is mapped or placed by a shortcode.</span>');return}const a=[];e.projects.length&&a.push(`pages in ${e.projects.map(r=>`<code>${b(r)}</code>`).join(", ")}`),e.pages.length&&a.push(`pages matching ${e.pages.map(r=>`<code>${b(r)}</code>`).join(", ")}`);const f=e.pages.filter(r=>!/^\/[^\s?#*]*(\/\*)?$|^\/\*$/.test(r)),v=f.length?`<div class="text-danger" style="margin-top:.4rem">Not valid page patterns: ${f.map(r=>`<code>${b(r)}</code>`).join(", ")}</div>`:"";n.html(a.length?`Takes over the <code>${b(e.slot)}</code> slot on ${a.join(" and ")}.${v}`:`<span class="text-warning">Slot <code>${b(e.slot)}</code> selected but no project or page chosen - the binding will never match.</span>`)}const{getProjectFromHash:gt}=await import("../lib/project-context.js");if(!g())return;const bt=A?gt():null;o("#me-project").html('<option value="">- none -</option>'+W.map(e=>`<option value="${b(e.slug)}">${b(e.name||e.slug)}</option>`).join(""));function Me(e){p=ie(e.items),o("#me-variant").val(e.variant||""),o("#me-position").val(e.position||"");const n=o("#me-appearOnHover").get(0);n&&(n.checked=e.appearOnHover===!0),o("#me-float-anchor").val(e.float?.anchor||"TL"),o("#me-float-offsetX").val(e.float?.offsetX??""),o("#me-float-offsetY").val(e.float?.offsetY??""),o("#me-float-width").val(e.float?.width??""),o("#me-float-radius").val(e.float?.radius??""),o("#me-float-shadow").val(e.float?.shadow||""),o("#me-float-opacity").val(e.float?.opacity??""),o("#me-float-title").val(e.float?.title||"");for(const[r,$]of[["#me-float-collapsible",e.float?.collapsible],["#me-float-collapsed",e.float?.collapsed],["#me-float-movable",e.float?.movable],["#me-float-resizable",e.float?.resizable]]){const d=o(r).get(0);d&&(d.checked=$===!0)}const a=e.float?.accent||"";xe.val(a.startsWith("#")?"__custom__":a),a.startsWith("#")&&Ne.val(a),Oe(),o("#me-orientation").val(e.orientation||""),o("#me-align").val(e.align||""),o("#me-side").val(e.side||"left"),o("#me-class").val(e.class||""),o("#me-highlight-hover").val(e.highlight?.hover||""),o("#me-highlight-active").val(e.highlight?.active||"");const f=e.highlight?.accent||"";Ee.val(f.startsWith("#")?"__custom__":f),f.startsWith("#")&&Pe.val(f),Ye(),o("#me-fontFamily").val(e.style?.fontFamily||""),o("#me-fontSize").val(e.style?.fontSize||""),o("#me-fontWeight").val(e.style?.fontWeight||""),o("#me-letterSpacing").val(e.style?.letterSpacing||""),o("#me-iconSize").val(e.style?.iconSize||""),Fe.val(e.binding?.slot||"");const v=new Set(e.binding?.projects||[]);Array.from(Be.get(0).options).forEach(r=>{r.selected=v.has(r.value)}),Ae.val((e.binding?.pages||[]).join(`
28
+ `)),o("#me-slug").val(e.slug||""),o("#me-name").val(e.name||""),o("#me-description").val(e.description||""),o("#me-project").val(e.meta?.project||bt||""),Xe(),ze(),re()}const we=e=>!!(o(e).get(0)||{}).checked;function Ce(){const e={variant:o("#me-variant").val(),position:o("#me-position").val(),appearOnHover:we("#me-appearOnHover"),floatAnchor:o("#me-float-anchor").val(),floatOffsetX:o("#me-float-offsetX").val(),floatOffsetY:o("#me-float-offsetY").val(),floatWidth:o("#me-float-width").val(),floatRadius:o("#me-float-radius").val(),floatShadow:o("#me-float-shadow").val(),floatAccent:ht(),floatOpacity:o("#me-float-opacity").val(),floatTitle:o("#me-float-title").val(),floatCollapsible:we("#me-float-collapsible"),floatCollapsed:we("#me-float-collapsed"),floatMovable:we("#me-float-movable"),floatResizable:we("#me-float-resizable"),highlightHover:o("#me-highlight-hover").val(),highlightActive:o("#me-highlight-active").val(),highlightAccent:vt(),class:o("#me-class").val(),orientation:o("#me-orientation").val(),align:o("#me-align").val(),side:o("#me-side").val(),fontFamily:o("#me-fontFamily").val(),fontSize:o("#me-fontSize").val(),fontWeight:o("#me-fontWeight").val(),letterSpacing:o("#me-letterSpacing").val(),iconSize:o("#me-iconSize").val()},n={slug:o("#me-slug").val(),name:o("#me-name").val(),description:o("#me-description").val(),project:o("#me-project").val()};return zt(e,n,Qe(),Tt(p),h.meta||{})}const Ze=e=>[...Mt(e),...ct(e.class).map(n=>`CSS classes: ${n} (Behaviour tab).`)];let et=!1,Ke=null;function V(){if(!z)return;const e=Ce();Ke=e,N.value=Ie(e)!==z,pe.value=N.peek()||et?Ze(e).join(" "):""}const Le=e=>{e.target.closest("#me-items-list")||(e.target.matches("#me-position, #me-orientation, #me-float-collapsible")&&Xe(),e.target.matches("#me-float-accent")&&Oe(),e.target.matches("#me-highlight-accent")&&Ye(),e.target.closest("#me-binding")&&ze(),V())};s.addEventListener("input",Le),s.addEventListener("change",Le),i(()=>{s.removeEventListener("input",Le),s.removeEventListener("change",Le)}),o("#me-bind-page-add").on("click",()=>{const e=o("#me-bind-page-pick").val();if(!e)return;const n=Ae.val().split(`
24
29
  `).map(a=>a.trim()).filter(Boolean);n.includes(e)||n.push(e),Ae.val(n.join(`
25
- `)),Be(),V()}),ze(h),B=Ie(Ce()),V();const tt=()=>{try{S.remove?S.remove(Y):S.set(Y,null)}catch{}};async function Ke(){if(le.peek())return;const e=Ce(),n=Ze(e);if(n.length){et=!0,V(),E.toast(n.join(" "),{type:"warning",duration:7e3});return}le.value=!0;try{if(_)await j.menus.create(e);else if(e.slug!==h.slug){await j.menus.create(e);const a=await j.menuLocations.get();let p=!1;for(const[v,r]of Object.entries(a))r===h.slug&&(a[v]=e.slug,p=!0);p&&await j.menuLocations.save(a),await j.menus.remove(h.slug)}else await j.menus.update(h.slug,e);if(B=Ie(e),D.value=!1,tt(),Dt("#/menus"),E.toast("Saved.",{type:"success"}),_||e.slug!==h.slug)location.hash="#/menus/edit/"+encodeURIComponent(e.slug);else{h={...h,...e};for(const a of m)a.tool&&a.touched&&(a.tool={...a.tool,persisted:!0});ie(),B=Ie(Ce()),V(),ft(t.get(0),e.name||e.slug)}if(q)try{document.dispatchEvent(new CustomEvent("dm:sidebar-rebuild"))}catch{}}catch(a){E.toast(`Save failed: ${a.message||a}`,{type:"error"})}finally{le.value=!1}}async function yt(){!D.peek()||!await E.confirm("Throw away the changes you have not saved?")||(ze(h),V())}E.tabs(o("#me-tabs").get(0));const wt=M.applyBindings({dirty:D,saving:le,problems:pe,itemCount:Se},s,{methods:{save:()=>Ke(),discard:()=>yt(),addItem:()=>de("item"),addSeparator:()=>de("separator"),addSpacer:()=>de("spacer")}});l(()=>wt.dispose()),l(Tt(s,{position:"bottom"})),ft(t.get(0),_?"New menu":h.name||h.slug),We(t,{id:"menu-editor-back",icon:"arrow-left",title:"All menus",onClick:()=>{location.hash="#/menus"}}),We(t,{id:"menu-editor-locations",icon:"map-pin",title:"Menu locations",onClick:()=>{location.hash="#/menu-locations"}}),We(t,{id:"menu-editor-save",icon:"save",title:"Save (Ctrl+S)",onClick:()=>Ke()});const nt=e=>{if(!(!(e.ctrlKey||e.metaKey)||e.key!=="s")){if(e.preventDefault(),e.stopImmediatePropagation(),document.querySelector(".me-item-form")?.offsetParent){E.toast("Apply the item first (Ctrl+Enter), then Save.",{type:"info"});return}Ke()}};document.addEventListener("keydown",nt,!0),l(()=>document.removeEventListener("keydown",nt,!0));const ot=()=>{if(!(!D.peek()||!Me))try{S.set(Y,{menu:Me,base:B,at:new Date().toISOString()})}catch{}},at=e=>{D.peek()&&(ot(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",at),l(()=>window.removeEventListener("beforeunload",at)),z={path:w,dirty:()=>D.peek(),name:()=>o("#me-name").val()||h.name||h.slug||"new menu",dispose(){i=!0,ot(),c.splice(0).reverse().forEach(e=>{try{e()}catch{}})}},I.scan(s);let J=null;try{J=S.get(Y)}catch{}if(J?.menu){tt();const e=J.base&&J.base!==B,n=J.at?` (${qt(J.at)})`:"",a=_?"the new menu you started":`the menu "${He(h.name||h.slug)}"`,p=e?`You left unsaved changes to ${a}${n}, but the saved menu has changed since. Restore your version anyway?`:`You left unsaved changes to ${a}${n}. Restore them?`;await E.confirm(p)&&g()&&(ze({...J.menu,meta:{...h.meta||{},...J.menu.meta||{}}}),V())}}};
30
+ `)),ze(),V()}),Me(h),z=Ie(Ce()),V();const tt=()=>{try{S.remove?S.remove(X):S.set(X,null)}catch{}};async function We(){if(le.peek())return;const e=Ce(),n=Ze(e);if(n.length){et=!0,V(),E.toast(n.join(" "),{type:"warning",duration:7e3});return}le.value=!0;try{if(A)await j.menus.create(e);else if(e.slug!==h.slug){await j.menus.create(e);const a=await j.menuLocations.get();let f=!1;for(const[v,r]of Object.entries(a))r===h.slug&&(a[v]=e.slug,f=!0);f&&await j.menuLocations.save(a),await j.menus.remove(h.slug)}else await j.menus.update(h.slug,e);if(z=Ie(e),N.value=!1,tt(),Dt("#/menus"),E.toast("Saved.",{type:"success"}),A||e.slug!==h.slug)location.hash="#/menus/edit/"+encodeURIComponent(e.slug);else{h={...h,...e};for(const a of p)a.tool&&a.touched&&(a.tool={...a.tool,persisted:!0});re(),z=Ie(Ce()),V(),ft(t.get(0),e.name||e.slug)}if(D)try{document.dispatchEvent(new CustomEvent("dm:sidebar-rebuild"))}catch{}}catch(a){E.toast(`Save failed: ${a.message||a}`,{type:"error"})}finally{le.value=!1}}async function yt(){!N.peek()||!await E.confirm("Throw away the changes you have not saved?")||(Me(h),V())}E.tabs(o("#me-tabs").get(0));const wt=M.applyBindings({dirty:N,saving:le,problems:pe,itemCount:Se},s,{methods:{save:()=>We(),discard:()=>yt(),addItem:()=>ne("item"),addSeparator:()=>ne("separator"),addSpacer:()=>ne("spacer"),addSwitcher:()=>ne("theme-switcher")}});i(()=>wt.dispose()),i(Rt(s,{position:"bottom"})),D&&o("#me-add-switcher").hide(),ft(t.get(0),A?"New menu":h.name||h.slug),qe(t,{id:"menu-editor-back",icon:"arrow-left",title:"All menus",onClick:()=>{location.hash="#/menus"}}),qe(t,{id:"menu-editor-locations",icon:"map-pin",title:"Menu locations",onClick:()=>{location.hash="#/menu-locations"}}),qe(t,{id:"menu-editor-save",icon:"save",title:"Save (Ctrl+S)",onClick:()=>We()});const nt=e=>{if(!(!(e.ctrlKey||e.metaKey)||e.key!=="s")){if(e.preventDefault(),e.stopImmediatePropagation(),document.querySelector(".me-item-form")?.offsetParent){E.toast("Apply the item first (Ctrl+Enter), then Save.",{type:"info"});return}We()}};document.addEventListener("keydown",nt,!0),i(()=>document.removeEventListener("keydown",nt,!0));const ot=()=>{if(!(!N.peek()||!Ke))try{S.set(X,{menu:Ke,base:z,at:new Date().toISOString()})}catch{}},at=e=>{N.peek()&&(ot(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",at),i(()=>window.removeEventListener("beforeunload",at)),K={path:w,dirty:()=>N.peek(),name:()=>o("#me-name").val()||h.name||h.slug||"new menu",dispose(){l=!0,ot(),c.splice(0).reverse().forEach(e=>{try{e()}catch{}})}},I.scan(s);let Q=null;try{Q=S.get(X)}catch{}if(Q?.menu){tt();const e=Q.base&&Q.base!==z,n=Q.at?` (${qt(Q.at)})`:"",a=A?"the new menu you started":`the menu "${He(h.name||h.slug)}"`,f=e?`You left unsaved changes to ${a}${n}, but the saved menu has changed since. Restore your version anyway?`:`You left unsaved changes to ${a}${n}. Restore them?`;await E.confirm(f)&&g()&&(Me({...Q.menu,meta:{...h.meta||{},...Q.menu.meta||{}}}),V())}}};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-cms",
3
- "version": "1.6.10",
3
+ "version": "1.6.12",
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",
@@ -84,7 +84,7 @@
84
84
  "@fastify/rate-limit": "^10.3.0",
85
85
  "@fastify/static": "9.1.1",
86
86
  "bcryptjs": "^3.0.3",
87
- "domma-js": "0.51.7",
87
+ "domma-js": "0.51.8",
88
88
  "dotenv": "^17.2.3",
89
89
  "fastify": "5.8.5",
90
90
  "gray-matter": "^4.0.3",
@@ -1 +1 @@
1
- .dm-theme-switcher{position:fixed;z-index:9998}.dm-ts-trigger{width:48px;height:48px;border-radius:50%;border:2px solid rgba(255,255,255,.25);cursor:pointer;padding:0;transition:transform .18s ease,box-shadow .18s ease;box-shadow:0 3px 12px #0006,0 0 0 1px #ffffff14}.dm-ts-trigger:hover{transform:scale(1.14);box-shadow:0 6px 20px #00000080,0 0 0 1px #ffffff26}.dm-ts-options{position:absolute;display:grid;grid-template-columns:repeat(2,28px);gap:8px;padding:10px;background:var(--dm-surface, #1e2030);border:1px solid var(--dm-border, rgba(255, 255, 255, .12));border-radius:14px;box-shadow:0 10px 28px #00000073;opacity:0;visibility:hidden;transform:scale(.92) translateY(6px);transform-origin:bottom right;transition:opacity .18s ease,transform .18s ease,visibility .18s;pointer-events:none;bottom:56px;right:0;max-height:60vh;overflow-y:auto}.dm-theme-switcher.dm-ts-left .dm-ts-options{right:auto;left:0;transform-origin:bottom left}.dm-theme-switcher.dm-ts-top .dm-ts-options{bottom:auto;top:56px;transform:scale(.92) translateY(-6px);transform-origin:top right}.dm-theme-switcher.dm-ts-top.dm-ts-left .dm-ts-options{transform-origin:top left}.dm-theme-switcher:hover .dm-ts-options,.dm-theme-switcher:focus-within .dm-ts-options{opacity:1;visibility:visible;transform:scale(1) translateY(0);pointer-events:all}.dm-theme-switcher.dm-ts-top:hover .dm-ts-options,.dm-theme-switcher.dm-ts-top:focus-within .dm-ts-options{transform:scale(1) translateY(0)}.dm-ts-dot{width:28px;height:28px;border-radius:50%;border:2px solid transparent;cursor:pointer;padding:0;position:relative;transition:transform .15s ease,border-color .15s ease,box-shadow .15s ease;box-shadow:0 1px 4px #00000059;outline:none}.dm-ts-dot:hover{transform:scale(1.22)}.dm-ts-dot.active{border-color:#fff;box-shadow:0 0 0 2px #ffffff80,0 2px 8px #0006}.dm-ts-dot:after,.dm-ts-trigger:after{content:attr(data-tooltip);position:absolute;top:50%;transform:translateY(-50%);background:#0a0c14eb;color:#fff;padding:4px 9px;border-radius:5px;font-size:.7rem;font-family:system-ui,sans-serif;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s ease;z-index:1}.dm-theme-switcher:not(.dm-ts-left) .dm-ts-dot:after,.dm-theme-switcher:not(.dm-ts-left) .dm-ts-trigger:after{right:calc(100% + 7px);left:auto}.dm-theme-switcher.dm-ts-left .dm-ts-dot:after,.dm-theme-switcher.dm-ts-left .dm-ts-trigger:after{left:calc(100% + 7px);right:auto}.dm-ts-dot:hover:after,.dm-ts-trigger:hover:after{opacity:1}@media(prefers-reduced-motion:reduce){.dm-ts-trigger,.dm-ts-dot,.dm-ts-options{transition:none}}
1
+ .dm-theme-switcher{position:fixed;z-index:9998}.dm-ts-trigger{width:48px;height:48px;border-radius:50%;border:2px solid rgba(255,255,255,.25);cursor:pointer;padding:0;transition:transform .18s ease,box-shadow .18s ease;box-shadow:0 3px 12px #0006,0 0 0 1px #ffffff14}.dm-ts-trigger:hover{transform:scale(1.14);box-shadow:0 6px 20px #00000080,0 0 0 1px #ffffff26}.dm-ts-options{position:absolute;display:grid;grid-template-columns:repeat(2,28px);gap:8px;padding:10px;background:var(--dm-surface, #1e2030);border:1px solid var(--dm-border, rgba(255, 255, 255, .12));border-radius:14px;box-shadow:0 10px 28px #00000073;opacity:0;visibility:hidden;transform:scale(.92) translateY(6px);transform-origin:bottom right;transition:opacity .18s ease,transform .18s ease,visibility .18s;pointer-events:none;bottom:56px;right:0;max-height:60vh;overflow-y:auto}.dm-theme-switcher.dm-ts-left .dm-ts-options{right:auto;left:0;transform-origin:bottom left}.dm-theme-switcher.dm-ts-top .dm-ts-options{bottom:auto;top:56px;transform:scale(.92) translateY(-6px);transform-origin:top right}.dm-theme-switcher.dm-ts-top.dm-ts-left .dm-ts-options{transform-origin:top left}.dm-theme-switcher:hover .dm-ts-options,.dm-theme-switcher:focus-within .dm-ts-options{opacity:1;visibility:visible;transform:scale(1) translateY(0);pointer-events:all}.dm-theme-switcher.dm-ts-top:hover .dm-ts-options,.dm-theme-switcher.dm-ts-top:focus-within .dm-ts-options{transform:scale(1) translateY(0)}.dm-ts-dot{width:28px;height:28px;border-radius:50%;border:2px solid transparent;cursor:pointer;padding:0;position:relative;transition:transform .15s ease,border-color .15s ease,box-shadow .15s ease;box-shadow:0 1px 4px #00000059;outline:none}.dm-ts-dot:hover{transform:scale(1.22)}.dm-ts-dot.active{border-color:#fff;box-shadow:0 0 0 2px #ffffff80,0 2px 8px #0006}.dm-ts-dot:after,.dm-ts-trigger:after{content:attr(data-tooltip);position:absolute;top:50%;transform:translateY(-50%);background:#0a0c14eb;color:#fff;padding:4px 9px;border-radius:5px;font-size:.7rem;font-family:system-ui,sans-serif;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s ease;z-index:1}.dm-theme-switcher:not(.dm-ts-left) .dm-ts-dot:after,.dm-theme-switcher:not(.dm-ts-left) .dm-ts-trigger:after{right:calc(100% + 7px);left:auto}.dm-theme-switcher.dm-ts-left .dm-ts-dot:after,.dm-theme-switcher.dm-ts-left .dm-ts-trigger:after{left:calc(100% + 7px);right:auto}.dm-ts-dot:hover:after,.dm-ts-trigger:hover:after{opacity:1}.dm-theme-switcher.dm-ts-inline{position:relative;display:inline-block;vertical-align:middle;z-index:20}.dm-ts-inline .dm-ts-trigger{width:28px;height:28px}.dm-theme-switcher.dm-ts-inline .dm-ts-options{top:36px}.dm-menu-theme-switcher{display:flex;align-items:center;padding:.375rem .75rem;list-style:none}@media(prefers-reduced-motion:reduce){.dm-ts-trigger,.dm-ts-dot,.dm-ts-options{transition:none}}
package/public/js/site.js CHANGED
@@ -1 +1 @@
1
- import{decodeBase64Json as N}from"/public/js/utf8-base64.mjs";import{makeBadgeEl as B,applyColour as j,applyPill as Y,applyFloat as G,applyHighlight as O,applyItemHighlight as U,itemHighlightAttrs as K,isCurrentItem as R,menuAlign as Q,applyClasses as D,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 oe}from"/public/js/form-submit.mjs";$(()=>{const x=window.__CMS_NAV__||{},h=window.__CMS_SITE__||{};if(h.autoTheme?.enabled){let t=function(e){const r=(e||"07:00").split(":");return+r[0]*60+(+r[1]||0)},a=function(){const e=new Date,r=e.getHours()*60+e.getMinutes();return r>=t(o.dayStart)&&r<t(o.nightStart)?o.dayTheme:o.nightTheme},n=function(e){const r=o.apply&&o.apply[e]||{theme:e,custom:""};Domma.theme.set(r.theme);for(const l of[...document.body.classList])l.startsWith("dm-custom-")&&document.body.classList.remove(l);r.custom&&document.body.classList.add(r.custom)};const o=h.autoTheme;n(a()),setInterval(()=>n(a()),6e4)}const A=$("#site-navbar");if(A.length&&x.brand){let a=function(c,y=0){return Array.isArray(c)?c.filter(i=>!(i&&i.type==="spacer")).filter(i=>!(i&&i.type==="separator"&&y===0)).map(i=>i&&i.type==="separator"?{divider:!0}:Array.isArray(i?.items)?{...i,items:a(i.items,y+1)}:i):c},n=function(c){return Array.isArray(c)?c.filter(y=>!(y&&(y.type==="separator"||y.type==="spacer"))):[]};const o={...x.brand},t=o.size&&o.size!=="md"?` navbar-brand-${o.size}`:"";if(o.logo||o.icon||o.tagline){let c="";o.logo?c+=`<img src="${o.logo}" class="navbar-brand-logo" alt="${o.text||""}">`:o.icon&&(c+=`<span data-icon="${o.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),o.text&&(c+=`<span class="navbar-brand-text${t}">${o.text}</span>`),o.tagline&&(c+=`<small class="navbar-brand-tagline">${o.tagline}</small>`),o.html=c}else t&&o.text&&(o.html=`<span class="navbar-brand-text${t}">${o.text}</span>`);const e=x.position==="floating",r=x.orientation==="vertical";Domma.elements.navbar("#site-navbar",{brand:o,items:a(x.items||[]),variant:x.variant||"dark",position:e?"static":x.position||"sticky",collapsible:!0,appearOnHover:!!x.appearOnHover}),Domma.icons.scan("#site-navbar");const l=A.get(0);l.classList.add(`dm-nav-align-${Q(x)||"centre"}`),r&&A.get(0).classList.add("dm-nav-vertical",x.side==="right"?"dm-nav-vertical-right":"dm-nav-vertical-left"),O(l,x),D(l,x.class),e&&(G(A.get(0),x),Z(A.get(0).parentNode||document));const f=l.querySelector(".navbar-toggle"),p=l.querySelector(".navbar-collapse");if(f&&p){let c=null;const y=()=>{p.classList.contains("show")&&f.click()},i=()=>{c&&(c.remove(),c=null),document.body.classList.remove("dm-drawer-open")},s=()=>{c||(c=document.createElement("div"),c.className="dm-navbar-backdrop",c.addEventListener("click",y),document.body.appendChild(c),document.body.classList.add("dm-drawer-open"))};f.addEventListener("click",()=>{requestAnimationFrame(()=>{p.classList.contains("show")?s():i()})}),p.addEventListener("click",u=>{u.target.closest("a")&&requestAnimationFrame(i)}),document.addEventListener("keydown",u=>{u.key==="Escape"&&p.classList.contains("show")&&y()})}const m=l.querySelector(".navbar-nav");if(m){const c=Array.from(m.children);let y=0;for(const i of x.items||[])if(!(i&&i.type==="separator")){if(i&&i.type==="spacer"){const s=document.createElement("li"),u=["xs","sm","md","lg","xl","flex"].includes(i.size)?i.size:"md";s.className=`dm-menu-spacer dm-menu-spacer--${u}`,s.setAttribute("aria-hidden","true"),m.insertBefore(s,c[y]||null);continue}y++}}const b=n(x.items||[]);l.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(c=>{const y=b[parseInt(c.dataset.index,10)];if(!y)return;const i=c.dataset.subindex!=null,s=i&&Array.isArray(y.items)?y.items[parseInt(c.dataset.subindex,10)]:y;if(!s)return;s.colour&&j(c,s.colour),s.pill&&!i&&Y(c,s.pill),U(c,s),D(c.closest("li"),s.class);const u=B(s.badge);u&&c.appendChild(u),R(s.url,location.pathname)&&(c.classList.add("active"),c.setAttribute("aria-current","page"))})}const C=$("#site-footer");if(C.length){let e=function(s){return String(s??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},r=function(s){return e(s).replace(/'/g,"&#39;")},l=function(s){if(s&&s.type==="separator")return'<li class="footer-sep"><hr></li>';if(s&&s.type==="spacer")return`<li class="dm-menu-spacer dm-menu-spacer--${["xs","sm","md","lg","xl","flex"].includes(s.size)?s.size:"md"}" aria-hidden="true"></li>`;const u=r(s.url||"#"),d=e(s.text||""),w=Array.isArray(s.items)&&s.items.length?`<ul>${s.items.map(l).join("")}</ul>`:"",v=K(s),q=[v.hover&&` data-menu-item-hover="${r(v.hover)}"`,v.active&&` data-menu-item-active="${r(v.active)}"`].filter(Boolean).join(""),M=v.accent?` style="--dm-menu-highlight:${r(v.accent)}"`:"",k=P(s.class).join(" ");return`<li${k?` class="${r(k)}"`:""}><a href="${u}"${q}${M}>${d}</a>${w}</li>`},f=function(s,u){return(s||[]).forEach(d=>{d&&(d.type==="separator"||d.type==="spacer")||(u.push(d),Array.isArray(d.items)&&d.items.length&&f(d.items,u))}),u};const o=h.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 a='<div class="footer-inner container">';const n=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(n.copyright&&(a+=`<p>${e(n.copyright)}</p>`),n.primary.length&&(a+=`<nav class="footer-nav footer-nav--primary"><ul>${n.primary.map(l).join("")}</ul></nav>`),n.legal.length&&(a+=`<nav class="footer-nav footer-nav--legal"><ul>${n.legal.map(l).join("")}</ul></nav>`),h.footer){const s=Object.keys(t).filter(d=>o[d]),u=P(h.footer.socialClass).join(" ");s.length&&(a+='<div class="footer-social">',s.forEach(d=>{const{label:w,svg:v}=t[d];a+=`<a href="${o[d]}" target="_blank" rel="noopener noreferrer" aria-label="${w}" class="footer-social-link${u?` ${r(u)}`:""}">${v}</a>`}),a+="</div>")}a+="</div>",C.html(a),D(C.get(0),h.footer?.class);const p=[...f(n.primary,[]),...f(n.legal,[])];C.get(0).querySelectorAll(".footer-nav a").forEach((s,u)=>{const d=p[u];if(!d)return;d.colour&&j(s,d.colour);const w=B(d.badge);w&&s.appendChild(w),R(d.url,location.pathname)&&(s.classList.add("is-active"),s.setAttribute("aria-current","page"))}),O(C.get(0).querySelector(".footer-nav--primary"),{highlight:n.primaryHighlight}),O(C.get(0).querySelector(".footer-nav--legal"),{highlight:n.legalHighlight}),D(C.get(0).querySelector(".footer-nav--primary"),n.primaryClass),D(C.get(0).querySelector(".footer-nav--legal"),n.legalClass);const b=h.footer?.showMotionToggle!==!1,c=S.get("reduced_motion"),y=c!==null?!!c:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),i=C.get(0).querySelector(".footer-inner");if(b&&i){const s=document.createElement("input");s.type="checkbox",s.className="form-switch-input",s.id="dm-motion-switch",s.checked=y,s.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const u=document.createElement("span");u.className="form-switch-label",u.textContent="Reduce motion";const d=document.createElement("label");d.className="form-switch footer-motion-switch",d.title="Reduce motion",d.appendChild(s),d.appendChild(u),i.appendChild(d)}}if($("#site-sidebar").length){const o=document.querySelector(".site-content"),t=l=>!l.closest(".hero, .card, .accordion, .tabs, figure, #site-sidebar"),a=l=>o?[...o.querySelectorAll(l)].filter(t):[],n=a("h2").length?a("h2"):a("h3"),e=new Set([...document.querySelectorAll("[id]")].map(l=>l.id)),r=[];for(const l of n){if(!l.id){const f=(l.textContent||"").toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"section";let p=f;for(let m=2;e.has(p);m++)p=`${f}-${m}`;e.add(p),l.id=p}r.push({text:l.textContent.trim(),url:`#${l.id}`,section:l.id})}r.length?Domma.elements.sidebar("#site-sidebar",{items:r,fixed:!1,push:!1,collapsible:!1,variant:"theme",scrollSpy:!0,position:document.querySelector(".site-main.sidebar-right")?"right":"left"}):document.querySelector(".site-main.with-sidebar")?.classList.add("no-sidebar-items")}Domma.icons.scan(),Domma.syntax?.scan&&Domma.syntax.scan();const g=$(".page-body");if(g.length){g.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),g.find(".tabs").each(function(){Domma.elements.tabs(this)}),g.find(".carousel").each(function(){const o={autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"};if(this.dataset.animationDuration){const t=parseInt(this.dataset.animationDuration,10);Number.isFinite(t)&&(o.animationDuration=t)}this.dataset.animationEasing&&(o.animationEasing=this.dataset.animationEasing),this.dataset.ctx&&(this._dmCtxSlides=Array.from(this.querySelectorAll(".carousel-slide")).map(t=>t.cloneNode(!0)),this._dmCarouselOpts=o),this._dmCarousel=Domma.elements.carousel(this,o)}),g.find(".dm-countdown").each(function(){const o={autoStart:!0},t=String(this.dataset.format||"").trim(),a=String(this.dataset.to||"").trim(),n=/^(\d{4})-(\d{2})-(\d{2})$/.exec(a),e=n?new Date(+n[1],+n[2]-1,+n[3]).getTime():a?new Date(a).getTime():NaN;Number.isFinite(e)?o.duration=Math.max(0,e-Date.now()):this.dataset.duration&&(o.duration=(parseInt(this.dataset.duration,10)||0)*1e3);const r=/^dd:/i.test(t);o.format=r?"hh:mm:ss":t||(o.duration>=36e5?"hh:mm:ss":"mm:ss");const l=this,f=m=>String(m).padStart(2,"0"),p=m=>{const b=Math.max(0,Math.floor(m/1e3)),c=l.querySelector(".dm-timer-time");c&&(c.textContent=`${f(Math.floor(b/86400))}:${f(Math.floor(b%86400/3600))}:${f(Math.floor(b%3600/60))}:${f(b%60)}`)};r&&(o.onTick=p),Domma.elements.timer(this,o),r&&p(o.duration||0)}),g.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),g.find(".dm-progression").each(function(){const o=Array.from(this.querySelectorAll(".dm-progression-item")).map(n=>{const e={title:n.querySelector(".dm-progression-item-title")?.textContent?.trim()||"",description:n.querySelector(".dm-progression-item-body")?.innerHTML?.trim()||""};return n.dataset.date&&(e.date=n.dataset.date),n.dataset.status&&(e.status=n.dataset.status),n.dataset.entryId&&(e._entryId=n.dataset.entryId),e}),t=this.dataset.layout==="centred"?"centered":this.dataset.layout||"vertical",a=Domma.elements.progression(this,{layout:t,theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0,allowHtml:!0,...o.length?{items:o}:{}});o.length&&(this._dmCtx={instance:a,items:o})});try{Domma.effects.reveal(".page-body .hero",{animation:"slide-up",duration:480,threshold:.06,stagger:60,once:!1})}catch{}document.querySelectorAll(".page-body .row[data-reveal]").forEach(o=>{const t=o.dataset.revealMode||"stagger",a=o.dataset.revealAnimation||"slide-up",n=parseInt(o.dataset.revealDuration,10)||400,e=parseInt(o.dataset.revealStagger,10)||60,r=parseInt(o.dataset.revealDelay,10)||0,l=o.dataset.revealDirection==="rtl",f=Array.from(o.children),p={"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)"};f.forEach((m,b)=>{m.style.opacity="0",m.style.transform=p[a]||"",m.style.transition=`opacity ${n}ms ease, transform ${n}ms ease`;const c=l?f.length-1-b:b;m.style.transitionDelay=t==="stagger"?`${r+c*e}ms`:`${r}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});f.forEach(b=>m.observe(b))}))}),g.find(".card[data-collapsible]").each(function(){const o=this.querySelector(".card-header");o&&o.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),g.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const o=this.dataset.soTarget,t=document.getElementById(o);if(!t)return;const a=E.slideover({title:t.dataset.soTitle||"",size:t.dataset.soSize||"md",position:t.dataset.soPosition||"right"});t.style.display="",a.setContent(t),a.open()})})}if(typeof $.setup=="function"){const o=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(t=>{try{const a=N(t.dataset.config);Object.assign(o,a)}catch{}}),Object.keys(o).length>0){const t={};for(const[a,n]of Object.entries(o)){const e=n?.events?.click,{confirm:r,toast:l,alert:f,prompt:p,...m}=e||{};r||l||f||p?($(a).on("click",async function(c){if(c.preventDefault(),r&&!await E.confirm(r))return;let y=null;if(!(p&&(y=await E.prompt(p,{inputPlaceholder:m.promptPlaceholder||"",inputValue:m.promptDefault||""}),y===null))){if(m.target){const i=$(m.target);m.toggleClass&&i.toggleClass(m.toggleClass),m.addClass&&i.addClass(m.addClass),m.removeClass&&i.removeClass(m.removeClass),y!==null&&(m.setText&&i.text(y),m.setVal&&i.val(y),m.setAttr&&i.attr(m.setAttr,y))}m.href&&(window.location.href=m.href),l&&E.toast(l,{type:m.toastType||"success"}),f&&E.alert(f)}}),Object.keys(m).length&&(t[a]={...n,events:{...n.events,click:m}})):t[a]=n}$.setup(t)}}g.length&&I(g.get(0))}),window.wireCTAButtons=I;function I(x){x.querySelectorAll(".dm-cta-trigger").forEach(h=>{h.__ctaWired||(h.__ctaWired=!0,h.addEventListener("click",async()=>{const A=h.dataset.action,C=h.dataset.entry,_=h.dataset.confirm;let g=S.get("auth_token");if(!g){E.toast("Please log in to perform this action.",{type:"warning"});return}if(_&&!await E.confirm(_))return;const o=Array.from(h.childNodes).map(a=>a.cloneNode(!0));h.disabled=!0,h.textContent="Running\u2026";const t=a=>fetch(`/api/actions/${A}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify(h.dataset.scope?{entryId:C,scope:h.dataset.scope}:{entryId:C})});try{let a=await t(g);if(a.status===401){const e=S.get("auth_refresh_token");if(e){const r=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(r.ok){const{token:l}=await r.json();S.set("auth_token",l),g=l,a=await t(g)}}}const n=await a.json().catch(()=>({}));if(!a.ok)throw new Error(n.error||n.message||`Error ${a.status}`);E.toast(n.message||"Action completed.",{type:"success"})}catch(a){E.toast(a.message||"Action failed.",{type:"error"})}finally{h.disabled=!1,h.textContent="",o.forEach(a=>h.appendChild(a)),Domma.icons.scan(h)}}))})}(function(){const h=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!h.length)return;const A=S.get("auth_token");function C(g,o){const t=document.createElement("div");t.className="dm-collection-display dm-collection-empty";const a=document.createElement("p");if(o){a.appendChild(document.createTextNode("Please "));const n=document.createElement("a");n.href="/login",n.textContent="sign in",a.appendChild(n),a.appendChild(document.createTextNode(" to view your entries."))}else a.textContent=g;return t.appendChild(a),t}function _(g,o){const t=document.createElement("template");t.innerHTML=o||"",g.replaceWith(t.content)}h.forEach(async g=>{const o=g.dataset.collectionAttrs;if(!o)return;if(!A){g.replaceChildren(C("",!0));return}const t=a=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify({attrs:o})});try{let a=A,n=await t(a);if(n.status===401){const r=S.get("auth_refresh_token");if(r){const l=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:r})});if(l.ok){const{token:f}=await l.json();S.set("auth_token",f),a=f,n=await t(a)}}}if(!n.ok){const{error:r}=await n.json().catch(()=>({}));g.replaceChildren(C(r||"Unable to load entries.",!1));return}const{html:e}=await n.json();_(g,e),window.DommaCollectionBrowser?.hydrate&&window.DommaCollectionBrowser.hydrate(document)}catch{g.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(),I(document.body)},100)})(),(function(){const h=document.querySelectorAll("[data-collection-table]");!h.length||typeof T>"u"||!T.create||h.forEach(A=>{let C;try{C=N(A.dataset.payload)}catch{return}const{columns:_,rows:g,search:o,sortable:t,exportable:a,pageSize:n,empty:e,ctaConfig:r,slug:l,exportCfg:f}=C;if(!_?.length)return;if(r){const b=_.findIndex(c=>c.key==="_cta");b!==-1&&(_[b]={key:"_cta",title:"",render:(c,y)=>{const i=document.createElement("button");if(i.className=`btn btn-${r.style||"primary"} dm-cta-trigger`,i.dataset.action=r.action||"",r.scope&&(i.dataset.scope=r.scope),i.dataset.entry=y._entryId||"",r.confirm&&(i.dataset.confirm=r.confirm),r.icon){const s=document.createElement("span");s.dataset.icon=r.icon,i.appendChild(s),i.appendChild(document.createTextNode(" "))}return i.appendChild(document.createTextNode(r.label||"Run")),i}})}const p="col-table-"+Math.random().toString(36).slice(2,7),m=document.createElement("div");m.id=p,A.replaceChildren(m),T.create("#"+p,{data:g,columns:_,search:o,sortable:t,exportable:a,pageSize:n,emptyMessage:e}),r&&I(m),A._dmTable={columns:_,rows:g,slug:l,exportCfg:f,render(b){T.create("#"+p,{data:b,columns:_,search:o,sortable:t,exportable:a,pageSize:n,emptyMessage:e}),r&&I(m)}}})})(),(function(){const h=document.querySelectorAll("[data-form-inline]");if(!h.length||typeof F>"u")return;function A(t){const a=t&&t.logic;return!!(a&&(a.visibility||a.requirement))}function C(t,a){const n={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const r=e.type==="chooser"||!!(e.variant&&["select","radio","checkbox-group"].includes(e.type)),l=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":r?"chooser":e.type,f={...e.formConfig||{}};f.span==="full"&&a&&(f.span=a);const p={type:l,required:A(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},...f}};e.minLength!==void 0&&(p.minLength=e.minLength),e.maxLength!==void 0&&(p.maxLength=e.maxLength),e.min!==void 0&&(p.min=e.min),e.max!==void 0&&(p.max=e.max),r&&(e.variant&&(p.variant=e.variant),(e.multiple||e.type==="checkbox-group")&&(p.multiple=!0),e.density&&(p.density=e.density),e.columns&&(p.columns=e.columns),e.accent&&(p.accent=e.accent),e.accentStyle&&(p.accentStyle=e.accentStyle),e.glow&&(p.glow=!0),e.glowColour&&(p.glowColour=e.glowColour),e.shadow&&(p.shadow=e.shadow),e.shadowColour&&(p.shadowColour=e.shadowColour)),n[e.name]=p}),n}function _(t){const a={};return(t||[]).forEach(n=>{if(!(!n.name||n.type==="page-break"||n.type==="spacer")&&(n.type==="select"||n.type==="multiselect")&&n.required){const e=(n.options||[])[0];e&&(a[n.name]=typeof e=="object"?e.value:e)}}),a}function g(t,a){(a||[]).forEach(n=>{if(n.type!=="date"||!n.name)return;const e=t.querySelector(`[name="${n.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function o(t,a,n){let e=t.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",t.appendChild(e)),e.textContent=a,e.style.cssText=n?"color:var(--dm-danger);margin-top:.75rem;":"color:var(--dm-success);margin-top:.75rem;"}h.forEach(t=>{let a;try{a=N(t.dataset.formInline)}catch{return}const n=a.fields||[],e=a.settings||{},r=Number(e.columns)===3?3:2,l=e.layout||"grid",f=n.some(i=>i.type==="page-break"),p=async i=>{if(t.dataset.triggerBlocked==="true"){const s=t._formLogicRuntime,u=s&&s.submitBlock?s.submitBlock().reasons:[];o(t,u.join(" ")||"This form cannot be submitted yet.",!0);const d=new Error("Blocked by a form trigger");throw d.formSubmitFailed=!0,d}try{const s=t.querySelector(".fb-form-honeypot"),u=s?.querySelector('input[type="text"]')||null,d=s?.querySelector('[name="_t"]')||null,w=Object.assign({},i);delete w._t,s?.dataset.hpName&&delete w[s.dataset.hpName],u!==null&&(w._hp=u.value),d!==null&&(w._t=d.value);const v=await H.post(`/api/forms/submit/${a.slug}`,w);if(v&&v.error){o(t,v.error,!0);const q=new Error(v.error);throw q.formSubmitFailed=!0,q}if(v?.redirect){window.location.href=v.redirect;return}for(;t.firstChild;)t.removeChild(t.firstChild);o(t,v?.message||e.successMessage||"Thank you for your submission.",!1)}catch(s){throw s.formSubmitFailed||o(t,s.message||"Submission failed. Please try again.",!0),s}},m=Date.now();function b(i){const s=i.querySelector("form");if(!s||s.querySelector(".fb-form-honeypot"))return;const u=document.createElement("div");u.className="fb-form-honeypot",u.setAttribute("aria-hidden","true");const d=document.createElement("input");d.name=s.querySelector('[name="website"]')?"_website":"website",u.dataset.hpName=d.name,d.type="text",d.tabIndex=-1,d.autocomplete="url",d.placeholder="https://",u.appendChild(d);const w=document.createElement("input");w.name="_t",w.type="hidden",w.value=m,u.appendChild(w),s.appendChild(u)}function c(i){if(!window.FormLogicEngine||!n.some(u=>u.logic||Array.isArray(u.triggers)&&u.triggers.length))return null;const s=new window.FormLogicEngine.FormLogicRuntime(a,t,i?{baseValues:i}:void 0);if(s.init(),t._formLogicRuntime=s,t._formLogicObserved)return s;if(t._formLogicObserved=!0,t.parentNode&&typeof MutationObserver<"u"){const u=new MutationObserver(function(d){for(const w of d)for(const v of w.removedNodes)if(v===t||v.nodeType===1&&v.contains&&v.contains(t)){t._formLogicRuntime&&t._formLogicRuntime.destroy(),u.disconnect();return}});u.observe(t.parentNode,{childList:!0,subtree:!1}),t._formLogicObserver=u}return s}let y=!1;if(t.addEventListener("dm-form-end",i=>{const s=i.detail?.validation;if(s&&!s.valid){W(t,s.missing),J(s);return}y||(y=!0,p(i.detail?.values||{}).catch(()=>{}).finally(()=>{y=!1}))}),f&&F.wizard){const i=ee(n,a.title),s=i.map(k=>({title:k.title,description:k.description,fields:C(k.fields,r)})),u=i.map(k=>k.fields);let d=0,w=null,v=null;const q=k=>{const L=window.FormLogicEngine;if(!L||!L.validateFields)return;const z=L.validateFields(t,u[d]||[]);z.valid||(k.preventDefault(),k.stopImmediatePropagation(),W(t,z.missing),J(z))},M=()=>{const k=u[d]||n;oe(t,e),X(t,e,k),g(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",q,!0))};v=F.wizard(t,{schema:{steps:s},layout:l,columns:r,finishText:e.submitText||"Finish",onSubmit:p,onStepChange:k=>{d=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>=s.length||L!==d&&v.goToStep(L)}),Promise.resolve(v).then(M)}else if(F.render){const i=F.render(t,C(n,r),_(n),{submitText:e.submitText||"Submit",layout:l,columns:r,onSubmit:p});Promise.resolve(i).then(function(){te(t,e,l),X(t,e,n),g(t,n),V(t,n),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 O,applyColour as j,applyPill as Y,applyFloat as G,applyHighlight as B,applyItemHighlight as U,itemHighlightAttrs as K,isCurrentItem as R,menuAlign as Q,applyClasses as D,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__||{},g=window.__CMS_SITE__||{};if(g.autoTheme?.enabled){let t=function(e){const r=(e||"07:00").split(":");return+r[0]*60+(+r[1]||0)},n=function(){const e=new Date,r=e.getHours()*60+e.getMinutes();return r>=t(o.dayStart)&&r<t(o.nightStart)?o.dayTheme:o.nightTheme},s=function(e){const r=o.apply&&o.apply[e]||{theme:e,custom:""};Domma.theme.set(r.theme);for(const d of[...document.body.classList])d.startsWith("dm-custom-")&&document.body.classList.remove(d);r.custom&&document.body.classList.add(r.custom)};const o=g.autoTheme;s(n()),setInterval(()=>s(n()),6e4)}const A=$("#site-navbar");if(A.length&&x.brand){let n=function(c,f=0){return Array.isArray(c)?c.filter(i=>!(i&&(i.type==="spacer"||i.type==="theme-switcher"))).filter(i=>!(i&&i.type==="separator"&&f===0)).map(i=>i&&i.type==="separator"?{divider:!0}:Array.isArray(i?.items)?{...i,items:n(i.items,f+1)}:i):c},s=function(c){return Array.isArray(c)?c.filter(f=>!(f&&(f.type==="separator"||f.type==="spacer"||f.type==="theme-switcher"))):[]};const o={...x.brand},t=o.size&&o.size!=="md"?` navbar-brand-${o.size}`:"";if(o.logo||o.icon||o.tagline){let c="";o.logo?c+=`<img src="${o.logo}" class="navbar-brand-logo" alt="${o.text||""}">`:o.icon&&(c+=`<span data-icon="${o.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),o.text&&(c+=`<span class="navbar-brand-text${t}">${o.text}</span>`),o.tagline&&(c+=`<small class="navbar-brand-tagline">${o.tagline}</small>`),o.html=c}else t&&o.text&&(o.html=`<span class="navbar-brand-text${t}">${o.text}</span>`);const e=x.position==="floating",r=x.orientation==="vertical";Domma.elements.navbar("#site-navbar",{brand:o,items:n(x.items||[]),variant:x.variant||"dark",position:e?"static":x.position||"sticky",collapsible:!0,appearOnHover:!!x.appearOnHover}),Domma.icons.scan("#site-navbar");const d=A.get(0);d.classList.add(`dm-nav-align-${Q(x)||"centre"}`),r&&A.get(0).classList.add("dm-nav-vertical",x.side==="right"?"dm-nav-vertical-right":"dm-nav-vertical-left"),B(d,x),D(d,x.class),e&&(G(A.get(0),x),Z(A.get(0).parentNode||document));const h=d.querySelector(".navbar-toggle"),p=d.querySelector(".navbar-collapse");if(h&&p){let c=null;const f=()=>{p.classList.contains("show")&&h.click()},i=()=>{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",f),document.body.appendChild(c),document.body.classList.add("dm-drawer-open"))};h.addEventListener("click",()=>{requestAnimationFrame(()=>{p.classList.contains("show")?a():i()})}),p.addEventListener("click",l=>{l.target.closest("a")&&requestAnimationFrame(i)}),document.addEventListener("keydown",l=>{l.key==="Escape"&&p.classList.contains("show")&&f()})}const m=d.querySelector(".navbar-nav");if(m){const c=Array.from(m.children);let f=0;for(const i of x.items||[])if(!(i&&i.type==="separator")){if(i&&i.type==="spacer"){const a=document.createElement("li"),l=["xs","sm","md","lg","xl","flex"].includes(i.size)?i.size:"md";a.className=`dm-menu-spacer dm-menu-spacer--${l}`,a.setAttribute("aria-hidden","true"),m.insertBefore(a,c[f]||null);continue}if(i&&i.type==="theme-switcher"){const a=document.createElement("li");a.className="dm-menu-theme-switcher";const l=document.createElement("span");l.dataset.themeSwitcher="menu",l.dataset.position="inline",a.appendChild(l),m.insertBefore(a,c[f]||null);continue}f++}window.CMSThemeSwitcher?.scan(m)}const b=s(x.items||[]);d.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(c=>{const f=b[parseInt(c.dataset.index,10)];if(!f)return;const i=c.dataset.subindex!=null,a=i&&Array.isArray(f.items)?f.items[parseInt(c.dataset.subindex,10)]:f;if(!a)return;a.colour&&j(c,a.colour),a.pill&&!i&&Y(c,a.pill),U(c,a),D(c.closest("li"),a.class);const l=O(a.badge);l&&c.appendChild(l),R(a.url,location.pathname)&&(c.classList.add("active"),c.setAttribute("aria-current","page"))})}const w=$("#site-footer");if(w.length){let e=function(a){return String(a??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},r=function(a){return e(a).replace(/'/g,"&#39;")},d=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>`;if(a&&a.type==="theme-switcher")return'<li class="dm-menu-theme-switcher"><span data-theme-switcher="menu" data-position="inline"></span></li>';const l=r(a.url||"#"),u=e(a.text||""),C=Array.isArray(a.items)&&a.items.length?`<ul>${a.items.map(d).join("")}</ul>`:"",v=K(a),q=[v.hover&&` data-menu-item-hover="${r(v.hover)}"`,v.active&&` data-menu-item-active="${r(v.active)}"`].filter(Boolean).join(""),M=v.accent?` style="--dm-menu-highlight:${r(v.accent)}"`:"",k=P(a.class).join(" ");return`<li${k?` class="${r(k)}"`:""}><a href="${l}"${q}${M}>${u}</a>${C}</li>`},h=function(a,l){return(a||[]).forEach(u=>{u&&(u.type==="separator"||u.type==="spacer"||u.type==="theme-switcher")||(l.push(u),Array.isArray(u.items)&&u.items.length&&h(u.items,l))}),l};const o=g.social||{},t={twitter:{label:"X / Twitter",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.742l7.73-8.835L1.254 2.25H8.08l4.259 5.629L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'},facebook:{label:"Facebook",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'},instagram:{label:"Instagram",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>'},linkedin:{label:"LinkedIn",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'},github:{label:"GitHub",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'},youtube:{label:"YouTube",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z"/></svg>'}};let n='<div class="footer-inner container">';const s=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(s.copyright&&(n+=`<p>${e(s.copyright)}</p>`),s.primary.length&&(n+=`<nav class="footer-nav footer-nav--primary"><ul>${s.primary.map(d).join("")}</ul></nav>`),s.legal.length&&(n+=`<nav class="footer-nav footer-nav--legal"><ul>${s.legal.map(d).join("")}</ul></nav>`),g.footer){const a=Object.keys(t).filter(u=>o[u]),l=P(g.footer.socialClass).join(" ");a.length&&(n+='<div class="footer-social">',a.forEach(u=>{const{label:C,svg:v}=t[u];n+=`<a href="${o[u]}" target="_blank" rel="noopener noreferrer" aria-label="${C}" class="footer-social-link${l?` ${r(l)}`:""}">${v}</a>`}),n+="</div>")}n+="</div>",w.html(n),window.CMSThemeSwitcher?.scan(w.get(0)),D(w.get(0),g.footer?.class);const p=[...h(s.primary,[]),...h(s.legal,[])];w.get(0).querySelectorAll(".footer-nav a").forEach((a,l)=>{const u=p[l];if(!u)return;u.colour&&j(a,u.colour);const C=O(u.badge);C&&a.appendChild(C),R(u.url,location.pathname)&&(a.classList.add("is-active"),a.setAttribute("aria-current","page"))}),B(w.get(0).querySelector(".footer-nav--primary"),{highlight:s.primaryHighlight}),B(w.get(0).querySelector(".footer-nav--legal"),{highlight:s.legalHighlight}),D(w.get(0).querySelector(".footer-nav--primary"),s.primaryClass),D(w.get(0).querySelector(".footer-nav--legal"),s.legalClass);const b=g.footer?.showMotionToggle!==!1,c=S.get("reduced_motion"),f=c!==null?!!c:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),i=w.get(0).querySelector(".footer-inner");if(b&&i){const a=document.createElement("input");a.type="checkbox",a.className="form-switch-input",a.id="dm-motion-switch",a.checked=f,a.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const l=document.createElement("span");l.className="form-switch-label",l.textContent="Reduce motion";const u=document.createElement("label");u.className="form-switch footer-motion-switch",u.title="Reduce motion",u.appendChild(a),u.appendChild(l),i.appendChild(u)}}if($("#site-sidebar").length){const o=document.querySelector(".site-content"),t=d=>!d.closest(".hero, .card, .accordion, .tabs, figure, #site-sidebar"),n=d=>o?[...o.querySelectorAll(d)].filter(t):[],s=n("h2").length?n("h2"):n("h3"),e=new Set([...document.querySelectorAll("[id]")].map(d=>d.id)),r=[];for(const d of s){if(!d.id){const h=(d.textContent||"").toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"section";let p=h;for(let m=2;e.has(p);m++)p=`${h}-${m}`;e.add(p),d.id=p}r.push({text:d.textContent.trim(),url:`#${d.id}`,section:d.id})}r.length?Domma.elements.sidebar("#site-sidebar",{items:r,fixed:!1,push:!1,collapsible:!1,variant:"theme",scrollSpy:!0,position:document.querySelector(".site-main.sidebar-right")?"right":"left"}):document.querySelector(".site-main.with-sidebar")?.classList.add("no-sidebar-items")}Domma.icons.scan(),Domma.syntax?.scan&&Domma.syntax.scan();const y=$(".page-body");if(y.length){y.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),y.find(".tabs").each(function(){Domma.elements.tabs(this)}),y.find(".carousel").each(function(){const o={autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"};if(this.dataset.animationDuration){const t=parseInt(this.dataset.animationDuration,10);Number.isFinite(t)&&(o.animationDuration=t)}this.dataset.animationEasing&&(o.animationEasing=this.dataset.animationEasing),this.dataset.ctx&&(this._dmCtxSlides=Array.from(this.querySelectorAll(".carousel-slide")).map(t=>t.cloneNode(!0)),this._dmCarouselOpts=o),this._dmCarousel=Domma.elements.carousel(this,o)}),y.find(".dm-countdown").each(function(){const o={autoStart:!0},t=String(this.dataset.format||"").trim(),n=String(this.dataset.to||"").trim(),s=/^(\d{4})-(\d{2})-(\d{2})$/.exec(n),e=s?new Date(+s[1],+s[2]-1,+s[3]).getTime():n?new Date(n).getTime():NaN;Number.isFinite(e)?o.duration=Math.max(0,e-Date.now()):this.dataset.duration&&(o.duration=(parseInt(this.dataset.duration,10)||0)*1e3);const r=/^dd:/i.test(t);o.format=r?"hh:mm:ss":t||(o.duration>=36e5?"hh:mm:ss":"mm:ss");const d=this,h=m=>String(m).padStart(2,"0"),p=m=>{const b=Math.max(0,Math.floor(m/1e3)),c=d.querySelector(".dm-timer-time");c&&(c.textContent=`${h(Math.floor(b/86400))}:${h(Math.floor(b%86400/3600))}:${h(Math.floor(b%3600/60))}:${h(b%60)}`)};r&&(o.onTick=p),Domma.elements.timer(this,o),r&&p(o.duration||0)}),y.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),y.find(".dm-progression").each(function(){const o=Array.from(this.querySelectorAll(".dm-progression-item")).map(s=>{const e={title:s.querySelector(".dm-progression-item-title")?.textContent?.trim()||"",description:s.querySelector(".dm-progression-item-body")?.innerHTML?.trim()||""};return s.dataset.date&&(e.date=s.dataset.date),s.dataset.status&&(e.status=s.dataset.status),s.dataset.entryId&&(e._entryId=s.dataset.entryId),e}),t=this.dataset.layout==="centred"?"centered":this.dataset.layout||"vertical",n=Domma.elements.progression(this,{layout:t,theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0,allowHtml:!0,...o.length?{items:o}:{}});o.length&&(this._dmCtx={instance:n,items:o})});try{Domma.effects.reveal(".page-body .hero",{animation:"slide-up",duration:480,threshold:.06,stagger:60,once:!1})}catch{}document.querySelectorAll(".page-body .row[data-reveal]").forEach(o=>{const t=o.dataset.revealMode||"stagger",n=o.dataset.revealAnimation||"slide-up",s=parseInt(o.dataset.revealDuration,10)||400,e=parseInt(o.dataset.revealStagger,10)||60,r=parseInt(o.dataset.revealDelay,10)||0,d=o.dataset.revealDirection==="rtl",h=Array.from(o.children),p={"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)"};h.forEach((m,b)=>{m.style.opacity="0",m.style.transform=p[n]||"",m.style.transition=`opacity ${s}ms ease, transform ${s}ms ease`;const c=d?h.length-1-b:b;m.style.transitionDelay=t==="stagger"?`${r+c*e}ms`:`${r}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});h.forEach(b=>m.observe(b))}))}),y.find(".card[data-collapsible]").each(function(){const o=this.querySelector(".card-header");o&&o.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),y.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const o=this.dataset.soTarget,t=document.getElementById(o);if(!t)return;const n=E.slideover({title:t.dataset.soTitle||"",size:t.dataset.soSize||"md",position:t.dataset.soPosition||"right"});t.style.display="",n.setContent(t),n.open()})})}if(typeof $.setup=="function"){const o=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(t=>{try{const n=N(t.dataset.config);Object.assign(o,n)}catch{}}),Object.keys(o).length>0){const t={};for(const[n,s]of Object.entries(o)){const e=s?.events?.click,{confirm:r,toast:d,alert:h,prompt:p,...m}=e||{};r||d||h||p?($(n).on("click",async function(c){if(c.preventDefault(),r&&!await E.confirm(r))return;let f=null;if(!(p&&(f=await E.prompt(p,{inputPlaceholder:m.promptPlaceholder||"",inputValue:m.promptDefault||""}),f===null))){if(m.target){const i=$(m.target);m.toggleClass&&i.toggleClass(m.toggleClass),m.addClass&&i.addClass(m.addClass),m.removeClass&&i.removeClass(m.removeClass),f!==null&&(m.setText&&i.text(f),m.setVal&&i.val(f),m.setAttr&&i.attr(m.setAttr,f))}m.href&&(window.location.href=m.href),d&&E.toast(d,{type:m.toastType||"success"}),h&&E.alert(h)}}),Object.keys(m).length&&(t[n]={...s,events:{...s.events,click:m}})):t[n]=s}$.setup(t)}}y.length&&I(y.get(0))}),window.wireCTAButtons=I;function I(x){x.querySelectorAll(".dm-cta-trigger").forEach(g=>{g.__ctaWired||(g.__ctaWired=!0,g.addEventListener("click",async()=>{const A=g.dataset.action,w=g.dataset.entry,_=g.dataset.confirm;let y=S.get("auth_token");if(!y){E.toast("Please log in to perform this action.",{type:"warning"});return}if(_&&!await E.confirm(_))return;const o=Array.from(g.childNodes).map(n=>n.cloneNode(!0));g.disabled=!0,g.textContent="Running\u2026";const t=n=>fetch(`/api/actions/${A}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},body:JSON.stringify(g.dataset.scope?{entryId:w,scope:g.dataset.scope}:{entryId:w})});try{let n=await t(y);if(n.status===401){const e=S.get("auth_refresh_token");if(e){const r=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(r.ok){const{token:d}=await r.json();S.set("auth_token",d),y=d,n=await t(y)}}}const s=await n.json().catch(()=>({}));if(!n.ok)throw new Error(s.error||s.message||`Error ${n.status}`);E.toast(s.message||"Action completed.",{type:"success"})}catch(n){E.toast(n.message||"Action failed.",{type:"error"})}finally{g.disabled=!1,g.textContent="",o.forEach(n=>g.appendChild(n)),Domma.icons.scan(g)}}))})}(function(){const g=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!g.length)return;const A=S.get("auth_token");function w(y,o){const t=document.createElement("div");t.className="dm-collection-display dm-collection-empty";const n=document.createElement("p");if(o){n.appendChild(document.createTextNode("Please "));const s=document.createElement("a");s.href="/login",s.textContent="sign in",n.appendChild(s),n.appendChild(document.createTextNode(" to view your entries."))}else n.textContent=y;return t.appendChild(n),t}function _(y,o){const t=document.createElement("template");t.innerHTML=o||"",y.replaceWith(t.content)}g.forEach(async y=>{const o=y.dataset.collectionAttrs;if(!o)return;if(!A){y.replaceChildren(w("",!0));return}const t=n=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},body:JSON.stringify({attrs:o})});try{let n=A,s=await t(n);if(s.status===401){const r=S.get("auth_refresh_token");if(r){const d=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:r})});if(d.ok){const{token:h}=await d.json();S.set("auth_token",h),n=h,s=await t(n)}}}if(!s.ok){const{error:r}=await s.json().catch(()=>({}));y.replaceChildren(w(r||"Unable to load entries.",!1));return}const{html:e}=await s.json();_(y,e),window.DommaCollectionBrowser?.hydrate&&window.DommaCollectionBrowser.hydrate(document)}catch{y.replaceChildren(w("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),typeof Domma<"u"&&Domma.syntax?.scan&&Domma.syntax.scan(),I(document.body)},100)})(),(function(){const g=document.querySelectorAll("[data-collection-table]");!g.length||typeof T>"u"||!T.create||g.forEach(A=>{let w;try{w=N(A.dataset.payload)}catch{return}const{columns:_,rows:y,search:o,sortable:t,exportable:n,pageSize:s,empty:e,ctaConfig:r,slug:d,exportCfg:h}=w;if(!_?.length)return;if(r){const b=_.findIndex(c=>c.key==="_cta");b!==-1&&(_[b]={key:"_cta",title:"",render:(c,f)=>{const i=document.createElement("button");if(i.className=`btn btn-${r.style||"primary"} dm-cta-trigger`,i.dataset.action=r.action||"",r.scope&&(i.dataset.scope=r.scope),i.dataset.entry=f._entryId||"",r.confirm&&(i.dataset.confirm=r.confirm),r.icon){const a=document.createElement("span");a.dataset.icon=r.icon,i.appendChild(a),i.appendChild(document.createTextNode(" "))}return i.appendChild(document.createTextNode(r.label||"Run")),i}})}const p="col-table-"+Math.random().toString(36).slice(2,7),m=document.createElement("div");m.id=p,A.replaceChildren(m),T.create("#"+p,{data:y,columns:_,search:o,sortable:t,exportable:n,pageSize:s,emptyMessage:e}),r&&I(m),A._dmTable={columns:_,rows:y,slug:d,exportCfg:h,render(b){T.create("#"+p,{data:b,columns:_,search:o,sortable:t,exportable:n,pageSize:s,emptyMessage:e}),r&&I(m)}}})})(),(function(){const g=document.querySelectorAll("[data-form-inline]");if(!g.length||typeof F>"u")return;function A(t){const n=t&&t.logic;return!!(n&&(n.visibility||n.requirement))}function w(t,n){const s={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const r=e.type==="chooser"||!!(e.variant&&["select","radio","checkbox-group"].includes(e.type)),d=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":r?"chooser":e.type,h={...e.formConfig||{}};h.span==="full"&&n&&(h.span=n);const p={type:d,required:A(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},...h}};e.minLength!==void 0&&(p.minLength=e.minLength),e.maxLength!==void 0&&(p.maxLength=e.maxLength),e.min!==void 0&&(p.min=e.min),e.max!==void 0&&(p.max=e.max),r&&(e.variant&&(p.variant=e.variant),(e.multiple||e.type==="checkbox-group")&&(p.multiple=!0),e.density&&(p.density=e.density),e.columns&&(p.columns=e.columns),e.accent&&(p.accent=e.accent),e.accentStyle&&(p.accentStyle=e.accentStyle),e.glow&&(p.glow=!0),e.glowColour&&(p.glowColour=e.glowColour),e.shadow&&(p.shadow=e.shadow),e.shadowColour&&(p.shadowColour=e.shadowColour)),s[e.name]=p}),s}function _(t){const n={};return(t||[]).forEach(s=>{if(!(!s.name||s.type==="page-break"||s.type==="spacer")&&(s.type==="select"||s.type==="multiselect")&&s.required){const e=(s.options||[])[0];e&&(n[s.name]=typeof e=="object"?e.value:e)}}),n}function y(t,n){(n||[]).forEach(s=>{if(s.type!=="date"||!s.name)return;const e=t.querySelector(`[name="${s.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function o(t,n,s){let e=t.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",t.appendChild(e)),e.textContent=n,e.style.cssText=s?"color:var(--dm-danger);margin-top:.75rem;":"color:var(--dm-success);margin-top:.75rem;"}g.forEach(t=>{let n;try{n=N(t.dataset.formInline)}catch{return}const s=n.fields||[],e=n.settings||{},r=Number(e.columns)===3?3:2,d=e.layout||"grid",h=s.some(i=>i.type==="page-break"),p=async i=>{if(t.dataset.triggerBlocked==="true"){const a=t._formLogicRuntime,l=a&&a.submitBlock?a.submitBlock().reasons:[];o(t,l.join(" ")||"This form cannot be submitted yet.",!0);const u=new Error("Blocked by a form trigger");throw u.formSubmitFailed=!0,u}try{const a=t.querySelector(".fb-form-honeypot"),l=a?.querySelector('input[type="text"]')||null,u=a?.querySelector('[name="_t"]')||null,C=Object.assign({},i);delete C._t,a?.dataset.hpName&&delete C[a.dataset.hpName],l!==null&&(C._hp=l.value),u!==null&&(C._t=u.value);const v=await H.post(`/api/forms/submit/${n.slug}`,C);if(v&&v.error){o(t,v.error,!0);const q=new Error(v.error);throw q.formSubmitFailed=!0,q}if(v?.redirect){window.location.href=v.redirect;return}for(;t.firstChild;)t.removeChild(t.firstChild);o(t,v?.message||e.successMessage||"Thank you for your submission.",!1)}catch(a){throw a.formSubmitFailed||o(t,a.message||"Submission failed. Please try again.",!0),a}},m=Date.now();function b(i){const a=i.querySelector("form");if(!a||a.querySelector(".fb-form-honeypot"))return;const l=document.createElement("div");l.className="fb-form-honeypot",l.setAttribute("aria-hidden","true");const u=document.createElement("input");u.name=a.querySelector('[name="website"]')?"_website":"website",l.dataset.hpName=u.name,u.type="text",u.tabIndex=-1,u.autocomplete="url",u.placeholder="https://",l.appendChild(u);const C=document.createElement("input");C.name="_t",C.type="hidden",C.value=m,l.appendChild(C),a.appendChild(l)}function c(i){if(!window.FormLogicEngine||!s.some(l=>l.logic||Array.isArray(l.triggers)&&l.triggers.length))return null;const a=new window.FormLogicEngine.FormLogicRuntime(n,t,i?{baseValues:i}:void 0);if(a.init(),t._formLogicRuntime=a,t._formLogicObserved)return a;if(t._formLogicObserved=!0,t.parentNode&&typeof MutationObserver<"u"){const l=new MutationObserver(function(u){for(const C of u)for(const v of C.removedNodes)if(v===t||v.nodeType===1&&v.contains&&v.contains(t)){t._formLogicRuntime&&t._formLogicRuntime.destroy(),l.disconnect();return}});l.observe(t.parentNode,{childList:!0,subtree:!1}),t._formLogicObserver=l}return a}let f=!1;if(t.addEventListener("dm-form-end",i=>{const a=i.detail?.validation;if(a&&!a.valid){W(t,a.missing),J(a);return}f||(f=!0,p(i.detail?.values||{}).catch(()=>{}).finally(()=>{f=!1}))}),h&&F.wizard){const i=ee(s,n.title),a=i.map(k=>({title:k.title,description:k.description,fields:w(k.fields,r)})),l=i.map(k=>k.fields);let u=0,C=null,v=null;const q=k=>{const L=window.FormLogicEngine;if(!L||!L.validateFields)return;const z=L.validateFields(t,l[u]||[]);z.valid||(k.preventDefault(),k.stopImmediatePropagation(),W(t,z.missing),J(z))},M=()=>{const k=l[u]||s;ae(t,e),X(t,e,k),y(t,k),V(t,k),e.honeypot!==!1&&b(t),C&&C.destroy(),C=c(v?.getStepData?v.getStepData():void 0),t.querySelectorAll(".wizard-next, .wizard-submit").forEach(L=>L.addEventListener("click",q,!0))};v=F.wizard(t,{schema:{steps:a},layout:d,columns:r,finishText:e.submitText||"Finish",onSubmit:p,onStepChange:k=>{u=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!==u&&v.goToStep(L)}),Promise.resolve(v).then(M)}else if(F.render){const i=F.render(t,w(s,r),_(s),{submitText:e.submitText||"Submit",layout:d,columns:r,onSubmit:p});Promise.resolve(i).then(function(){te(t,e,d),X(t,e,s),y(t,s),V(t,s),e.honeypot!==!1&&b(t),c()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();
@@ -1 +1 @@
1
- (function(){"use strict";var a=window.__CMS_THEME_SWITCHER__;if(!a||!Array.isArray(a.themes)||!a.themes.length)return;var i=a.position||"right-center",m=i==="left-center"||i==="bottom-left"||i==="top-left",u=i==="bottom-left"||i==="bottom-right",l=i==="top-left"||i==="top-right",c=a.offsetX!=null?a.offsetX:32,f=a.offsetY!=null?a.offsetY:32,n=document.createElement("div");n.className="dm-theme-switcher"+(m?" dm-ts-left":"")+(l?" dm-ts-top":""),n.id="dm-theme-switcher",n.setAttribute("role","navigation"),n.setAttribute("aria-label","Theme switcher");var o=document.createElement("button");o.className="dm-ts-trigger",o.setAttribute("data-tooltip","Switch theme"),o.setAttribute("aria-haspopup","true"),o.setAttribute("aria-expanded","false");var p=360/a.themes.length;o.style.background="conic-gradient("+a.themes.map(function(e,t){return(e.from||"#888")+" "+(t*p).toFixed(2)+"deg "+((t+1)*p).toFixed(2)+"deg"}).join(", ")+")";var s=document.createElement("div");s.className="dm-ts-options",s.setAttribute("role","menu");var h=a.themes.map(function(e){var t=document.createElement("button");return t.className="dm-ts-dot",t.dataset.theme=e.id,t.setAttribute("data-tooltip",e.label),t.setAttribute("aria-label",e.label),t.setAttribute("role","menuitem"),t.style.background="linear-gradient(135deg, "+(e.from||"#888")+", "+(e.to||e.from||"#888")+")",s.appendChild(t),t});n.appendChild(o),n.appendChild(s),Object.assign(n.style,{right:m?"auto":c+"px",left:m?c+"px":"auto",top:l?f+"px":u?"auto":"50vh",bottom:u?f+"px":"auto",transform:l||u?"none":"translateY(-50%)"});function b(){document.body.appendChild(n)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",b):b();function g(e,t){typeof Domma<"u"&&Domma.theme?e(Domma.theme):t>0&&setTimeout(function(){g(e,t-1)},100)}g(function(e){function t(){var r=e.get();h.forEach(function(d){d.classList.toggle("active",d.dataset.theme===r)})}h.forEach(function(r){r.addEventListener("click",function(){e.set(r.dataset.theme),document.body.className=document.body.className.split(" ").filter(function(d){return d.indexOf("dm-custom-")!==0}).join(" "),t()})}),e.onChange(t),t(),o.addEventListener("click",function(){var r=o.getAttribute("aria-expanded")==="true";o.setAttribute("aria-expanded",String(!r))})},30)})();
1
+ (function(){"use strict";var o=window.__CMS_THEME_SWITCHER__;if(!o||!Array.isArray(o.themes)||!o.themes.length)return;var E=["top-left","top-right","left-center","right-center","bottom-left","bottom-right"],h=[],u=null;function S(t){if(!t)return o.themes;var e=String(t).split(",").map(function(n){return n.trim()}).filter(Boolean),a=o.themes.filter(function(n){return e.indexOf(n.id)!==-1});return a.length?a:o.themes}function b(t){var e=t.themes||o.themes,a=t.position==="inline",n=E.indexOf(t.position)!==-1?t.position:"right-center",d=n==="left-center"||n==="bottom-left"||n==="top-left",m=n==="bottom-left"||n==="bottom-right",l=n==="top-left"||n==="top-right",y=t.offsetX!=null?t.offsetX:32,x=t.offsetY!=null?t.offsetY:32,f=document.createElement("div");f.className="dm-theme-switcher"+(a?" dm-ts-inline dm-ts-left dm-ts-top":(d?" dm-ts-left":"")+(l?" dm-ts-top":"")),f.setAttribute("role","navigation"),f.setAttribute("aria-label","Theme switcher");var r=document.createElement("button");r.type="button",r.className="dm-ts-trigger",r.setAttribute("data-tooltip","Switch theme"),r.setAttribute("aria-label","Switch theme"),r.setAttribute("aria-haspopup","true"),r.setAttribute("aria-expanded","false");var w=360/e.length;r.style.background="conic-gradient("+e.map(function(s,i){return(s.from||"#888")+" "+(i*w).toFixed(2)+"deg "+((i+1)*w).toFixed(2)+"deg"}).join(", ")+")",r.addEventListener("click",function(){var s=r.getAttribute("aria-expanded")==="true";r.setAttribute("aria-expanded",String(!s))});var p=document.createElement("div");return p.className="dm-ts-options",p.setAttribute("role","menu"),e.forEach(function(s){var i=document.createElement("button");i.type="button",i.className="dm-ts-dot",i.dataset.theme=s.id,i.setAttribute("data-tooltip",s.label),i.setAttribute("aria-label",s.label),i.setAttribute("role","menuitem"),i.style.background="linear-gradient(135deg, "+(s.from||"#888")+", "+(s.to||s.from||"#888")+")",i.addEventListener("click",function(){C(i.dataset.theme)}),p.appendChild(i),h.push(i)}),f.appendChild(r),f.appendChild(p),a||Object.assign(f.style,{right:d?"auto":y+"px",left:d?y+"px":"auto",top:l?x+"px":m?"auto":"50vh",bottom:m?x+"px":"auto",transform:l||m?"none":"translateY(-50%)"}),c(),f}function v(t){var e=(t||document).querySelectorAll("[data-theme-switcher]:not([data-ts-mounted])");Array.prototype.forEach.call(e,function(a){a.setAttribute("data-ts-mounted","");var n=function(m){var l=parseInt(m,10);return isFinite(l)?l:null},d=b({position:a.dataset.position||"inline",offsetX:n(a.dataset.offsetX),offsetY:n(a.dataset.offsetY),themes:S(a.dataset.themes)});d.classList.contains("dm-ts-inline")?a.appendChild(d):document.body.appendChild(d)})}function C(t){u&&(u.set(t),document.body.className=document.body.className.split(" ").filter(function(e){return e.indexOf("dm-custom-")!==0}).join(" "),c())}function c(){if(u){var t=u.get();h.forEach(function(e){e.classList.toggle("active",e.dataset.theme===t)})}}function g(t,e){typeof Domma<"u"&&Domma.theme?t(Domma.theme):e>0&&setTimeout(function(){g(t,e-1)},100)}g(function(t){u=t,t.onChange(c),c()},30),window.CMSThemeSwitcher={scan:v};function A(){o.floating!==!1&&document.body.appendChild(b({position:o.position,offsetX:o.offsetX,offsetY:o.offsetY})),v(document)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",A):A()})();
@@ -3596,6 +3596,53 @@ function processSpacerBlocks(markdown) {
3596
3596
  ));
3597
3597
  }
3598
3598
 
3599
+ /**
3600
+ * [theme-switcher] - the site's theme dots, placed by the page.
3601
+ *
3602
+ * Syntax:
3603
+ * [theme-switcher /] inline, where the tag stands
3604
+ * [theme-switcher position="bottom-left" offset-x="24" offset-y="24" /]
3605
+ * [theme-switcher themes="charcoal-dark,charcoal-light" /]
3606
+ *
3607
+ * Supported attributes:
3608
+ * position - inline (default) | top-left | top-right | left-center |
3609
+ * right-center | bottom-left | bottom-right (pinned to the window)
3610
+ * offset-x, offset-y - distance from the window's edges when pinned (0-400 px, default 32)
3611
+ * themes - a comma list narrowing the themes chosen under Appearance >
3612
+ * Theme switcher (the site's list when absent or none match)
3613
+ *
3614
+ * Leaves a slot that public/js/theme-switcher.js mounts. The renderer loads
3615
+ * that script when the slot is present, with the floating switcher off or on,
3616
+ * and a page with the tag shows it instead of the floating one.
3617
+ *
3618
+ * @param {string} markdown
3619
+ * @returns {string}
3620
+ */
3621
+ export const THEME_SWITCHER_POSITIONS = ['inline', 'top-left', 'top-right', 'left-center', 'right-center', 'bottom-left', 'bottom-right'];
3622
+
3623
+ function processThemeSwitcherBlocks(markdown) {
3624
+ const {scrubbed, restore} = scrubCodeRegions(markdown);
3625
+ return restore(scrubbed.replace(
3626
+ /\[theme-switcher([^\]]*?)\/?\]/gi,
3627
+ (_, attrStr) => {
3628
+ const attrs = parseShortcodeAttrs(attrStr || '');
3629
+ const position = THEME_SWITCHER_POSITIONS.includes(attrs.position) ? attrs.position : 'inline';
3630
+ const offset = (v) => {
3631
+ const n = parseInt(v, 10);
3632
+ return Number.isFinite(n) ? Math.min(400, Math.max(0, n)) : null;
3633
+ };
3634
+ const themes = String(attrs.themes || '').split(',').map(t => t.trim()).filter(t => /^[a-z0-9-]+$/.test(t)).join(',');
3635
+ const data = [`data-theme-switcher="page"`, `data-position="${position}"`];
3636
+ if (position !== 'inline') {
3637
+ if (offset(attrs['offset-x']) !== null) data.push(`data-offset-x="${offset(attrs['offset-x'])}"`);
3638
+ if (offset(attrs['offset-y']) !== null) data.push(`data-offset-y="${offset(attrs['offset-y'])}"`);
3639
+ }
3640
+ if (themes) data.push(`data-themes="${themes}"`);
3641
+ return `<span class="dm-theme-switcher-slot" ${data.join(' ')}></span>`;
3642
+ }
3643
+ ));
3644
+ }
3645
+
3599
3646
  /**
3600
3647
  * Pre-process [center]...[/center] shortcodes before running through marked.
3601
3648
  *
@@ -4423,7 +4470,8 @@ export async function parseMarkdown(raw, opts = {}) {
4423
4470
  const withTimeline = processTimelineBlocks(withCountdown);
4424
4471
  const withListGroup = processListGroupBlocks(withTimeline);
4425
4472
  const withSpacer = processSpacerBlocks(withListGroup);
4426
- const withCenter = processCenterBlocks(withSpacer);
4473
+ const withSwitcher = processThemeSwitcherBlocks(withSpacer);
4474
+ const withCenter = processCenterBlocks(withSwitcher);
4427
4475
  const withIcon = processIconBlocks(withCenter);
4428
4476
  const withDemo = processDemoBlocks(withIcon);
4429
4477
  const withEmbed = processEmbedBlocks(withDemo);
@@ -47,6 +47,10 @@ export function renderMenuItemsAsUl(items, currentPath = '') {
47
47
  if (it && it.type === 'spacer') {
48
48
  return `<li class="dm-menu-spacer dm-menu-spacer--${esc(spacerSize(it))}" aria-hidden="true"></li>`;
49
49
  }
50
+ // The site's theme dots, mounted by theme-switcher.js.
51
+ if (it && it.type === 'theme-switcher') {
52
+ return '<li class="dm-menu-theme-switcher"><span data-theme-switcher="menu" data-position="inline"></span></li>';
53
+ }
50
54
  const href = esc(it.url || '#');
51
55
  const text = esc(it.text || '');
52
56
  const child = Array.isArray(it.items) && it.items.length ? renderMenuItemsAsUl(it.items, currentPath) : '';
@@ -248,6 +248,21 @@ export function spacerSize(item) {
248
248
  return SPACER_SIZES.has(item?.size) ? item.size : SPACER_DEFAULT_SIZE;
249
249
  }
250
250
 
251
+ // ---------------------------------------------------------------------------
252
+ // Theme switcher items - `{type: 'theme-switcher'}`, the site's theme dots
253
+ // placed among a menu's links. Like a spacer it has no text or url; it shows
254
+ // the themes chosen under Appearance > Theme switcher, whether or not the
255
+ // floating switcher itself is on.
256
+ // ---------------------------------------------------------------------------
257
+
258
+ /** Items that hold no link: they are drawn, not navigated. */
259
+ export const isStructuralItem = (it) => !!it && (it.type === 'separator' || it.type === 'spacer' || it.type === 'theme-switcher');
260
+
261
+ /** True when a menu (at any depth) carries a theme switcher item. */
262
+ export function hasThemeSwitcherItem(items) {
263
+ return (items || []).some(it => it && (it.type === 'theme-switcher' || hasThemeSwitcherItem(it.items)));
264
+ }
265
+
251
266
  export const ORIENTATIONS = new Set(['horizontal', 'vertical']);
252
267
  export const SIDES = new Set(['left', 'right']);
253
268
 
@@ -315,7 +330,7 @@ export async function resolveMenuDecorations(items, opts = {}) {
315
330
 
316
331
  async function walk(list) {
317
332
  return Promise.all((list || []).map(async (it) => {
318
- if (!it || it.type === 'separator' || it.type === 'spacer') return it;
333
+ if (!it || isStructuralItem(it)) return it;
319
334
  const next = {...it};
320
335
  if (it.badge && it.badge.countFrom) {
321
336
  try {
@@ -751,6 +766,9 @@ export function validateMenu(menu) {
751
766
  // Renderers translate them into <hr> / structural breaks.
752
767
  if (item && item.type === 'separator') return;
753
768
 
769
+ // A theme switcher has nothing to point at either.
770
+ if (item && item.type === 'theme-switcher') return;
771
+
754
772
  // Spacers are blank room, likewise with nothing to point at.
755
773
  if (item && item.type === 'spacer') {
756
774
  if (item.size != null && !SPACER_SIZES.has(item.size)) {
@@ -8,7 +8,7 @@ import {fileURLToPath} from 'url';
8
8
  import {getConfig} from '../config.js';
9
9
  import {getInjectionSnippets} from './plugins.js';
10
10
  import {applyTransforms} from './hooks.js';
11
- import {resolveLocation, resolveMenuDecorations, resolveOverlaysForPage} from './menus.js';
11
+ import {hasThemeSwitcherItem, resolveLocation, resolveMenuDecorations, resolveOverlaysForPage} from './menus.js';
12
12
  import {buildMenuNav} from './menuRender.js';
13
13
  import {getProjectForPage} from './projects.js';
14
14
  import {resolveContextMenusForPage} from './contextMenus.js';
@@ -77,7 +77,7 @@ function buildSearchAssets() {
77
77
  };
78
78
  }
79
79
 
80
- const THEME_SWITCHER_ASSET_V = '20260925-ts';
80
+ const THEME_SWITCHER_ASSET_V = '20261011-ts-inline';
81
81
  const ANALYTICS_ASSET_V = '20260922-analytics';
82
82
 
83
83
  /**
@@ -112,11 +112,23 @@ function buildAnalyticsTag() {
112
112
  * Both render paths need this, the same standing trap buildThemeView() and
113
113
  * buildSearchAssets() carry.
114
114
  *
115
- * @returns {{headTag: string, bodyTag: string}} Empty strings when it is off
115
+ * A switcher is also placed by a `{type: 'theme-switcher'}` menu item or the
116
+ * `[theme-switcher]` tag, which need the script and the theme list even with
117
+ * the floating disc off - so the page's html and its menus are passed in. A
118
+ * page with the tag shows that one instead of the floating disc.
119
+ *
120
+ * @param {object} themeCfg
121
+ * @param {{html?: string, menus?: Array<Array>}} [page] The page's rendered
122
+ * html (body and overlay menus) and the item lists of its navbar and footers
123
+ * @returns {{headTag: string, bodyTag: string}} Empty strings when nothing needs it
116
124
  */
117
- function buildThemeSwitcherAssets(themeCfg) {
118
- const sw = themeCfg?.switcher;
119
- if (!sw || sw.enabled !== true) return {headTag: '', bodyTag: ''};
125
+ function buildThemeSwitcherAssets(themeCfg, page = {}) {
126
+ const sw = themeCfg?.switcher || {};
127
+ const html = page.html || '';
128
+ const pageTag = html.includes('data-theme-switcher="page"');
129
+ const inMenu = html.includes('data-theme-switcher="menu"') || (page.menus || []).some(hasThemeSwitcherItem);
130
+ const floating = sw.enabled === true && !pageTag;
131
+ if (!floating && !pageTag && !inMenu) return {headTag: '', bodyTag: ''};
120
132
 
121
133
  let ids;
122
134
  try {
@@ -140,6 +152,7 @@ function buildThemeSwitcherAssets(themeCfg) {
140
152
  });
141
153
 
142
154
  const payload = {
155
+ floating,
143
156
  position: sw.position || 'right-center',
144
157
  offsetX: sw.offsetX ?? 32,
145
158
  offsetY: sw.offsetY ?? 32,
@@ -321,7 +334,10 @@ export async function renderPage(page, opts = {}) {
321
334
 
322
335
  const themeView = await buildThemeView(page);
323
336
  const searchAssets = buildSearchAssets();
324
- const switcherAssets = buildThemeSwitcherAssets(themeView.cfg);
337
+ const switcherAssets = buildThemeSwitcherAssets(themeView.cfg, {
338
+ html: `${page.html || ''}${overlayMenus}`,
339
+ menus: [navItemsDecorated, footerPrimaryDecorated, footerLegalDecorated]
340
+ });
325
341
  const {fontLink, fontStyleTag, autoTheme, activeTheme, dommaTheme, customThemeClass} = themeView;
326
342
 
327
343
  const {navbarFontLink, navbarStyleTag} = buildNavbarStyleTag(navigation);
@@ -1043,7 +1059,10 @@ export async function renderBlogPage(templatePath, data = {}, seoMeta = {}) {
1043
1059
  // other is the standing trap in this file.
1044
1060
  const themeView = await buildThemeView({});
1045
1061
  const searchAssets = buildSearchAssets();
1046
- const switcherAssets = buildThemeSwitcherAssets(themeView.cfg);
1062
+ const switcherAssets = buildThemeSwitcherAssets(themeView.cfg, {
1063
+ html: `${renderedFragment || ''}${overlayMenus}`,
1064
+ menus: [navItemsDecorated, footerPrimaryDecorated, footerLegalDecorated]
1065
+ });
1047
1066
  const {fontLink, fontStyleTag, activeTheme, dommaTheme, customThemeClass} = themeView;
1048
1067
  const {navbarFontLink, navbarStyleTag} = buildNavbarStyleTag(navigation);
1049
1068
 
@@ -137,7 +137,7 @@
137
137
  </script>
138
138
 
139
139
  <!-- Site initialisation -->
140
- <script src="/public/js/site.js?v=20261011-sidebar-right" type="module"></script>
140
+ <script src="/public/js/site.js?v=20261011-theme-switcher-item" type="module"></script>
141
141
  <script src="/public/js/collection-context.js?v=20261010-utf8-payloads" type="module"></script>
142
142
  {{ctxMenusModule}}
143
143