domma-cms 1.6.11 → 1.6.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,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,2 +1,2 @@
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
+ const p=["separator","spacer","theme-switcher"];export const isStructural=t=>p.includes(t?.type);export function siblingsOf(t,e){const i=t.find(n=>n.id===e);return i?t.filter(n=>n.parentId===i.parentId):[]}export function moveRow(t,e,i){const n=siblingsOf(t,e),r=n.findIndex(a=>a.id===e),l=r+(i<0?-1:1);if(r<0||l<0||l>=n.length)return!1;n[r].touched=!0;const o=t.indexOf(n[r]),s=t.indexOf(n[l]);return[t[o],t[s]]=[t[s],t[o]],!0}export function indentRow(t,e){const i=siblingsOf(t,e),n=i.findIndex(r=>r.id===e);return n<=0||isStructural(i[n-1])?!1:(i[n].parentId=i[n-1].id,i[n].touched=!0,!0)}export function outdentRow(t,e){const i=t.find(r=>r.id===e);if(!i||i.parentId==null)return!1;const n=t.find(r=>r.id===i.parentId);return i.parentId=n?n.parentId:null,i.touched=!0,!0}export function newRow(t,e,i=null){return t==="separator"?{id:e,parentId:i,type:"separator"}:t==="theme-switcher"?{id:e,parentId:i,type:"theme-switcher"}:t==="spacer"?{id:e,parentId:i,type:"spacer",size:"md"}:{id:e,parentId:i,text:"",url:"/",icon:"",visibility:"",permission:"",hidden:!1,bundled:!1}}export function insertBelow(t,e,i,n){const r=t.findIndex(o=>o.id===e);if(r===-1)return null;const l=newRow(i,n,t[r].parentId);return t.splice(r+1,0,l),l}export function addChild(t,e,i){const n=t.find(l=>l.id===e);if(!n||isStructural(n))return null;const r=newRow("item",i,n.id);return t.push(r),n.touched=!0,r}export function duplicateRow(t,e,i){const n=t.find(a=>a.id===e);if(!n||n.tool)return null;const r=new Map,l=[],o=(a,u)=>{if(a.tool)return;const c={...m(a),id:i(),parentId:u};delete c.tool,delete c.locked,c.touched=!0,r.set(a.id,c.id),l.push(c);for(const d of t.filter(f=>f.parentId===a.id))o(d,c.id)};if(o(n,n.parentId),!l.length)return null;l[0].text&&(l[0].text=`${l[0].text} (copy)`);const s=t.indexOf(n);return t.splice(s+1,0,l[0]),t.push(...l.slice(1)),l[0]}function m(t){return JSON.parse(JSON.stringify(t))}export function bindingFrom({slot:t="",projects:e=[],pagesText:i=""}={}){return t?{slot:t,projects:[...e],pages:String(i).split(`
2
+ `).map(n=>n.trim()).filter(Boolean)}:null}export function badPagePatterns(t=[]){return t.filter(e=>!/^\/[^\s?#*]*(\/\*)?$|^\/\*$/.test(e))}const g=["fontFamily","fontSize","fontWeight","letterSpacing","iconSize"];export function menuFromForm(t={},e={},i=null,n=[],r={}){const l=Object.fromEntries(g.filter(u=>t[u]).map(u=>[u,t[u]])),o={};if(t.position==="floating"){t.floatAnchor&&(o.anchor=t.floatAnchor);for(const[u,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[u]=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 s={};t.position!=="floating"&&(t.orientation==="vertical"?(t.dockWidth!==""&&t.dockWidth!=null&&Number.isFinite(Number(t.dockWidth))&&(s.width=Number(t.dockWidth)),t.dockAutohide?s.autohide=!0:t.dockCollapsible&&(s.collapsible=!0,t.dockCollapsed&&(s.collapsed=!0))):t.dockHideOnScroll&&(s.hideOnScroll=!0));const a={};return t.highlightHover&&(a.hover=t.highlightHover),t.highlightActive&&(a.active=t.highlightActive),t.highlightAccent&&(a.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(a).length&&{highlight:a},...Object.keys(s).length&&{dock:s},...t.appearOnHover&&{appearOnHover:!0},...t.class&&String(t.class).trim()&&{class:String(t.class).trim()},...Object.keys(l).length&&{style:l},binding:i,items:n,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 i=countUnlabelled(t.items);if(i&&e.push(`${i} item${i===1?" has":"s have"} no label - every item needs one (Items tab).`),t.dock?.width!=null&&!(t.dock.width>=160&&t.dock.width<=480)&&e.push("The rail width must be from 160 to 480 px (Behaviour tab)."),t.binding){const n=badPagePatterns(t.binding.pages);n.length&&e.push(`Not valid page patterns: ${n.join(", ")} (Binding tab).`)}return e}export function countUnlabelled(t=[]){let e=0;for(const i of t||[])!isStructural(i)&&!(i.ref===!0&&i.url)&&!String(i.text||"").trim()&&e++,e+=countUnlabelled(i.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 i of t){const n=Array.isArray(i.items)&&i.items.length?depthOfItems(i.items):0;n>e&&(e=n)}return e+1}export function locationRows(t=[],e={},i={},n=[],r={}){const l=Object.fromEntries(n.map(o=>[o.slug,o.name||o.slug]));return t.map(o=>{const s=e[o.slot]||"",a=s&&r[s]||0,u=!!s&&o.maxDepth!=null&&a>o.maxDepth,c=o.slot==="overlay";return{slot:o.slot,label:o.label||o.slot,source:o.source||"",description:o.description||"",menu:s,menuName:s?l[s]||`${s} (missing)`:"",missing:!!s&&!l[s],tooDeep:u,depthText:u?`Nested ${a} deep - this slot shows ${o.maxDepth}`:"",empty:!s,emptyText:s?"":c?"Optional":"Nothing mapped",changed:(e[o.slot]||"")!==(i[o.slot]||"")}})}export function locationsPayload(t={},e=[]){const i=new Set(e.map(n=>n.slot));return Object.fromEntries(Object.entries(t).filter(([n,r])=>r&&(!e.length||i.has(n))))}export function locationsSnapshot(t={}){return JSON.stringify(Object.keys(t).filter(e=>t[e]).sort().map(e=>[e,t[e]]))}
@@ -1,3 +1,3 @@
1
- import{SHORTCODES as $}from"./shortcode-registry.js";import{EFFECTS as w,CELEBRATION_THEMES as J,getEffectAttrs as R}from"./effect-defs.js";import{boolState as Q,boolValue as Z,findEnclosingShortcode as z,setAttr as P,setAttrs as ee,effectAttrKeys as te}from"./shortcode-tags.js";import{insertAtCursor as D}from"./markdown-toolbar.js";import{clearLookupCache as ne,lookupOptions as oe}from"./shortcode-lookups.js";import{insertMenuGroups as se,insertShortcodeByName as ie}from"./shortcode-insert.js";import{openShortcodeForm as A}from"./shortcode-form.js";function ce(e){return oe(e,c?.span?.attrs||{})}const ae=$.map(e=>({name:e.name,selfClosing:!!e.selfClosing})),le=new Map($.map(e=>[e.name,e])),H=new Map(w.map(e=>[e.name,e])),re=w.map(e=>({name:e.name,selfClosing:!!e.selfClosing})),O=[...ae,...re],j=new Set(["card","box","hero","banner","col","button"]);let c=null;const W="position:fixed;z-index:10000;min-width:210px;max-height:70vh;overflow-y:auto;background:var(--dm-surface,#1c1c30);border:1px solid var(--dm-border,#2a2a40);border-radius:6px;box-shadow:var(--dm-shadow-lg);padding:.3rem;font-size:.83rem;color:var(--dm-text,#e5e5e5);",p="display:flex;align-items:center;gap:.5rem;width:100%;text-align:left;padding:.35rem .55rem;border:none;background:transparent;color:inherit;border-radius:4px;cursor:pointer;font-size:.83rem;",k="padding:.3rem .55rem;color:var(--dm-text-muted,#8a8a9a);font-size:.68rem;text-transform:uppercase;letter-spacing:.06em;",Y="flex:1;min-width:0;padding:.2rem .4rem;background:var(--dm-input-bg,#12121e);border:1px solid var(--dm-border,#333);border-radius:3px;color:var(--dm-text,#eee);font-size:.8rem;",de="height:1px;background:var(--dm-border,#2a2a40);margin:.3rem .2rem;";function f(e){return e.addEventListener("mouseenter",()=>{e.style.background="var(--dm-primary,#4338ca)",e.style.color="var(--dm-primary-text,#fff)"}),e.addEventListener("mouseleave",()=>{e.style.background="transparent",e.style.color="inherit"}),e}export function attachContextMenu(e){return typeof Domma>"u"||!Domma.elements?.contextMenu?(e.addEventListener("contextmenu",n=>{n.preventDefault(),m();const t=z(e,O);M(e,t,n.clientX,n.clientY)}),null):Domma.elements.contextMenu(e,{inherit:!1,cursor:!1,render:n=>{m();const t=z(e,O);M(e,t,n.x,n.y)}})}function m(){c&&(v(),c.root.remove(),document.removeEventListener("mousedown",B,!0),document.removeEventListener("keydown",K,!0),window.removeEventListener("scroll",F,!0),c=null)}const B=e=>{c&&!c.root.contains(e.target)&&!(c.submenu&&c.submenu.contains(e.target))&&m()},F=e=>{c&&!c.root.contains(e.target)&&!(c.submenu&&c.submenu.contains(e.target))&&m()},K=e=>{e.key==="Escape"&&m()};function M(e,n,t,o){ne();const s=document.createElement("div");s.className="dm-sc-menu",s.style.cssText=W,c={root:s,textarea:e,span:n,submenu:null};const i=n&&(le.get(n.name)||H.get(n.name)),l=n&&j.has(n.name)?w.filter(r=>r.name in n.attrs):[];i&&ue(s,i);for(const r of l)ye(s,r);(i||l.length)&&s.appendChild(U()),s.appendChild(y("Celebrations\u2026",()=>Ee())),s.appendChild(y("Add effect\u2026",()=>ge())),s.appendChild(y("Insert\u2026",()=>Ce()));const a=e.selectionStart!==e.selectionEnd;(n||a)&&s.appendChild(y(a?"Wrap selection in\u2026":"Wrap in\u2026",()=>ve())),document.body.appendChild(s),q(s,t,o),document.addEventListener("mousedown",B,!0),document.addEventListener("keydown",K,!0),window.addEventListener("scroll",F,!0),window.I&&I.scan(s)}function U(){const e=document.createElement("div");return e.style.cssText=de,e}function C(e){const n=document.createElement("div");return n.style.cssText=k,n.textContent=e,n}function ue(e,n){e.appendChild(C(`${n.label} options`));const t=f(document.createElement("button"));t.type="button",t.style.cssText=p,t.innerHTML='<span data-icon="edit" data-icon-size="13" style="display:inline-flex;align-items:center;"></span>',t.appendChild(document.createTextNode(`Edit ${n.label}\u2026`)),t.addEventListener("click",()=>{const{textarea:o,span:s}=c;m(),A(o,{...n,attrs:R(n,s.attrs)},{span:s})}),e.appendChild(t);for(const o of R(n,c.span.attrs))e.appendChild(V(o))}function V(e,n=e.name){const t=c.span.attrs[n];switch(e.type){case"checkbox":return pe(e,n,Q(t,e.default));case"select":return he(e,n,t);case"lookup":return xe(e,n,t);case"colour":return fe(e,n,t);case"icon":return be(e,n,t);default:return me(e,n,t)}}function x(e,n){if(c.span=P(c.textarea,c.span,e,n),e==="theme"){const{textarea:t,span:o,root:s}=c,i=s.getBoundingClientRect();m(),M(t,o,i.left,i.top)}}function pe(e,n,t){const o=f(document.createElement("label"));o.style.cssText=p;const s=document.createElement("input");return s.type="checkbox",s.checked=t,s.addEventListener("change",()=>x(n,Z(e,s.checked))),o.appendChild(s),o.appendChild(document.createTextNode(e.label)),o}function me(e,n,t){const o=document.createElement("div");o.style.cssText=p+"cursor:default;";const s=document.createElement("span");s.textContent=e.label,s.style.cssText="white-space:nowrap;color:var(--dm-text-muted,#9a9aac);";const i=document.createElement("input");i.type=e.type==="number"?"number":"text",i.value=t===!0||t==null?"":t,e.default!=null&&e.default!==""&&(i.placeholder=String(e.default)),i.style.cssText=Y;let l=null;return i.addEventListener("input",()=>{clearTimeout(l),l=setTimeout(()=>x(n,i.value.trim()),180)}),i.addEventListener("mousedown",a=>a.stopPropagation()),o.appendChild(s),o.appendChild(i),o}function fe(e,n,t){const o=document.createElement("div");o.style.cssText=p+"cursor:default;";const s=document.createElement("span");s.textContent=e.label,s.style.flex="1";const i=document.createElement("input");return i.type="color",typeof t=="string"&&/^#/.test(t)?i.value=t:typeof e.default=="string"&&/^#/.test(e.default)&&(i.value=e.default),i.addEventListener("input",()=>x(n,i.value)),o.appendChild(s),o.appendChild(i),o}function he(e,n,t){const o=t==null||t===!0?e.default??"":t;return y(`${e.label}${o!==""?`: ${o}`:""}`,()=>{const s=g();for(const[i,l]of e.options||[]){const a=f(document.createElement("button"));a.type="button",a.style.cssText=p,a.textContent=l,i===o&&(a.style.fontWeight="700"),a.addEventListener("click",()=>{x(n,i),v()}),s.appendChild(a)}return s})}function be(e,n,t){const o=t==null||t===!0?"":String(t),s=o?`${e.label}: ${o}`:e.label;return y(s,()=>{const i=g();i.style.width="232px";const l=document.createElement("input");l.type="text",l.placeholder="Search icons\u2026",l.style.cssText=Y+"margin:6px;width:calc(100% - 12px);",l.addEventListener("mousedown",L=>L.stopPropagation()),i.appendChild(l);const a=f(document.createElement("button"));a.type="button",a.style.cssText=p,a.textContent="- none -",o||(a.style.fontWeight="700"),a.addEventListener("click",()=>{x(n,null),v()}),i.appendChild(a);const r=document.createElement("div");r.style.cssText="display:flex;flex-wrap:wrap;gap:2px;padding:4px 6px;max-height:220px;overflow-y:auto;",i.appendChild(r);const u=96,T=()=>{const L=typeof I<"u"&&typeof I.list=="function"?I.list():[],N=l.value.trim().toLowerCase(),_=N?L.filter(d=>d.toLowerCase().includes(N)):L;r.replaceChildren();for(const d of _.slice(0,u)){const b=f(document.createElement("button"));b.type="button",b.title=d,b.style.cssText="width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;background:none;border:1px solid transparent;border-radius:4px;cursor:pointer;color:inherit;",d===o&&(b.style.borderColor="var(--dm-primary,#5b8cff)");const S=document.createElement("span");S.setAttribute("data-icon",d),S.setAttribute("data-icon-size","15"),S.style.cssText="display:inline-flex;align-items:center;",b.appendChild(S),b.addEventListener("click",()=>{x(n,d),v()}),r.appendChild(b)}if(_.length>u){const d=document.createElement("div");d.style.cssText=k,d.textContent=`${_.length-u} more - refine search`,r.appendChild(d)}if(!_.length){const d=document.createElement("div");d.style.cssText=k,d.textContent="No icons match",r.appendChild(d)}window.I&&I.scan(r)};let h=null;return l.addEventListener("input",()=>{clearTimeout(h),h=setTimeout(T,120)}),T(),setTimeout(()=>l.focus(),0),i})}function xe(e,n,t){const o=t==null||t===!0?e.default??"":t;return y(`${e.label}${o!==""?`: ${o}`:""}`,()=>{const s=g();s.style.minHeight="48px";const i=typeof E<"u"&&E.loader?E.loader(s,{type:"dots"}):null;return ce(e.source).then(l=>{if(i&&i.destroy(),!c||c.submenu!==s)return;if(s.style.minHeight="",!l.length){const u=document.createElement("div");u.style.cssText=k,u.textContent="Nothing found",s.appendChild(u)}const a=l.some(([u])=>u===o),r=[["","- none -"],...l];o!==""&&!a&&r.push([o,`${o} (missing)`]);for(const[u,T]of r){const h=f(document.createElement("button"));h.type="button",h.style.cssText=p,h.textContent=T,u===o&&(h.style.fontWeight="700"),h.addEventListener("click",()=>{x(n,u===""?null:u),v()}),s.appendChild(h)}}),s})}function ye(e,n){e.appendChild(C(`Effect: ${n.label}`));for(const o of n.attrs||[])e.appendChild(V(o,`${n.name}-${o.name}`));const t=f(document.createElement("button"));t.type="button",t.style.cssText=p,t.innerHTML='<span data-icon="trash" data-icon-size="13" style="display:inline-flex;align-items:center;"></span> Remove effect',t.addEventListener("click",()=>{const o={};for(const s of te(c.span.attrs,n.name))o[s]=null;ee(c.textarea,c.span,o),m()}),e.appendChild(t)}function Ee(){const e=g();e.appendChild(C("Seasonal themes"));for(const[n,t]of[["auto","Auto (date-detected)"],...Object.entries(J).map(([o,s])=>[o,s.label])]){const o=f(document.createElement("button"));o.type="button",o.style.cssText=p,o.textContent=t,o.addEventListener("click",()=>{D(c.textarea,`[celebrate theme="${n}" /]`),m()}),e.appendChild(o)}e.appendChild(U()),e.appendChild(C("Celebration effects"));for(const n of["butterflies","ticker-tape","firework","fireworks"]){const t=H.get(n),o=f(document.createElement("button"));o.type="button",o.style.cssText=p,o.textContent=t.label,o.addEventListener("click",()=>{const s=c.textarea;m(),A(s,t)}),e.appendChild(o)}return e}function ge(){const e=g(),n=c.span,t=n&&j.has(n.name)?n.name:null;for(const o of[...new Set(w.map(s=>s.category))]){const s=w.filter(i=>i.category===o);if(s.length){e.appendChild(C(o));for(const i of s){const l=t&&(i.hosts||[]).includes(t)&&!(i.name in n.attrs),a=f(document.createElement("button"));if(a.type="button",a.style.cssText=p+"justify-content:space-between;",a.appendChild(document.createTextNode(i.label)),l){const r=document.createElement("span");r.textContent=`inject \u2192 [${t}]`,r.style.cssText="opacity:.6;font-size:.7rem;",a.appendChild(r)}a.addEventListener("click",()=>{if(l)P(c.textarea,c.span,i.name,!0);else{const r=i.selfClosing?`[${i.name} /]`:`[${i.name}]
1
+ import{SHORTCODES as A}from"./shortcode-registry.js";import{EFFECTS as w,CELEBRATION_THEMES as Q,getEffectAttrs as z}from"./effect-defs.js";import{boolState as Z,boolValue as ee,findEnclosingShortcode as H,setAttr as P,setAttrs as D,effectAttrKeys as te}from"./shortcode-tags.js";import{insertAtCursor as O,replaceRange as ne}from"./markdown-toolbar.js";import{clearLookupCache as oe,lookupOptions as se}from"./shortcode-lookups.js";import{insertMenuGroups as ie,insertShortcodeByName as ae}from"./shortcode-insert.js";import{openShortcodeForm as $}from"./shortcode-form.js";function ce(e){return se(e,a?.span?.attrs||{})}const le=A.map(e=>({name:e.name,selfClosing:!!e.selfClosing})),re=new Map(A.map(e=>[e.name,e])),j=new Map(w.map(e=>[e.name,e])),de=w.map(e=>({name:e.name,selfClosing:!!e.selfClosing})),W=[...le,...de],Y=new Set(["card","box","hero","banner","col","button"]);let a=null;const B="position:fixed;z-index:10000;min-width:210px;max-height:70vh;overflow-y:auto;background:var(--dm-surface,#1c1c30);border:1px solid var(--dm-border,#2a2a40);border-radius:6px;box-shadow:var(--dm-shadow-lg);padding:.3rem;font-size:.83rem;color:var(--dm-text,#e5e5e5);",p="display:flex;align-items:center;gap:.5rem;width:100%;text-align:left;padding:.35rem .55rem;border:none;background:transparent;color:inherit;border-radius:4px;cursor:pointer;font-size:.83rem;",S="padding:.3rem .55rem;color:var(--dm-text-muted,#8a8a9a);font-size:.68rem;text-transform:uppercase;letter-spacing:.06em;",F="flex:1;min-width:0;padding:.2rem .4rem;background:var(--dm-input-bg,#12121e);border:1px solid var(--dm-border,#333);border-radius:3px;color:var(--dm-text,#eee);font-size:.8rem;",ue="height:1px;background:var(--dm-border,#2a2a40);margin:.3rem .2rem;";function f(e){return e.addEventListener("mouseenter",()=>{e.style.background="var(--dm-primary,#4338ca)",e.style.color="var(--dm-primary-text,#fff)"}),e.addEventListener("mouseleave",()=>{e.style.background="transparent",e.style.color="inherit"}),e}export function attachContextMenu(e){return typeof Domma>"u"||!Domma.elements?.contextMenu?(e.addEventListener("contextmenu",n=>{n.preventDefault(),d();const t=H(e,W);M(e,t,n.clientX,n.clientY)}),null):Domma.elements.contextMenu(e,{inherit:!1,cursor:!1,render:n=>{d();const t=H(e,W);M(e,t,n.x,n.y)}})}function d(){a&&(v(),a.root.remove(),document.removeEventListener("mousedown",q,!0),document.removeEventListener("keydown",U,!0),window.removeEventListener("scroll",K,!0),a=null)}const q=e=>{a&&!a.root.contains(e.target)&&!(a.submenu&&a.submenu.contains(e.target))&&d()},K=e=>{a&&!a.root.contains(e.target)&&!(a.submenu&&a.submenu.contains(e.target))&&d()},U=e=>{e.key==="Escape"&&d()};function M(e,n,t,o){oe();const s=document.createElement("div");s.className="dm-sc-menu",s.style.cssText=B,a={root:s,textarea:e,span:n,submenu:null};const i=n&&(re.get(n.name)||j.get(n.name)),c=n&&Y.has(n.name)?w.filter(r=>r.name in n.attrs):[];i&&pe(s,i);for(const r of c)Ee(s,r);(i||c.length)&&s.appendChild(N()),s.appendChild(y("Celebrations\u2026",()=>ge())),s.appendChild(y("Add effect\u2026",()=>Ce())),s.appendChild(y("Insert\u2026",()=>ve()));const l=e.selectionStart!==e.selectionEnd;(n||l)&&s.appendChild(y(l?"Wrap selection in\u2026":"Wrap in\u2026",()=>we())),document.body.appendChild(s),J(s,t,o),document.addEventListener("mousedown",q,!0),document.addEventListener("keydown",U,!0),window.addEventListener("scroll",K,!0),window.I&&I.scan(s)}function N(){const e=document.createElement("div");return e.style.cssText=ue,e}function C(e){const n=document.createElement("div");return n.style.cssText=S,n.textContent=e,n}function pe(e,n){e.appendChild(C(`${n.label} options`));const t=f(document.createElement("button"));t.type="button",t.style.cssText=p,t.innerHTML='<span data-icon="edit" data-icon-size="13" style="display:inline-flex;align-items:center;"></span>',t.appendChild(document.createTextNode(`Edit ${n.label}\u2026`)),t.addEventListener("click",()=>{const{textarea:i,span:c}=a;d(),$(i,{...n,attrs:z(n,c.attrs)},{span:c})}),e.appendChild(t);for(const i of n.actions||[]){const c=f(document.createElement("button"));c.type="button",c.style.cssText=p,c.innerHTML=`<span data-icon="${i.icon||"zap"}" data-icon-size="13" style="display:inline-flex;align-items:center;"></span>`,c.appendChild(document.createTextNode(i.label)),c.addEventListener("click",()=>{const{textarea:l,span:r}=a;if(i.remove){ne(l,r.openTagStart,r.openTagEnd,""),d();return}a.span=D(l,r,i.set||{}),d()}),e.appendChild(c)}const o=Array.isArray(n.quickAttrs)?new Set(n.quickAttrs):null,s=z(n,a.span.attrs).filter(i=>!o||o.has(i.name));(n.actions||[]).length&&s.length&&e.appendChild(N());for(const i of s)e.appendChild(V(i))}function V(e,n=e.name){const t=a.span.attrs[n];switch(e.type){case"checkbox":return me(e,n,Z(t,e.default));case"select":return be(e,n,t);case"lookup":return ye(e,n,t);case"colour":return he(e,n,t);case"icon":return xe(e,n,t);default:return fe(e,n,t)}}function x(e,n){if(a.span=P(a.textarea,a.span,e,n),e==="theme"){const{textarea:t,span:o,root:s}=a,i=s.getBoundingClientRect();d(),M(t,o,i.left,i.top)}}function me(e,n,t){const o=f(document.createElement("label"));o.style.cssText=p;const s=document.createElement("input");return s.type="checkbox",s.checked=t,s.addEventListener("change",()=>x(n,ee(e,s.checked))),o.appendChild(s),o.appendChild(document.createTextNode(e.label)),o}function fe(e,n,t){const o=document.createElement("div");o.style.cssText=p+"cursor:default;";const s=document.createElement("span");s.textContent=e.label,s.style.cssText="white-space:nowrap;color:var(--dm-text-muted,#9a9aac);";const i=document.createElement("input");i.type=e.type==="number"?"number":"text",i.value=t===!0||t==null?"":t,e.default!=null&&e.default!==""&&(i.placeholder=String(e.default)),i.style.cssText=F;let c=null;return i.addEventListener("input",()=>{clearTimeout(c),c=setTimeout(()=>x(n,i.value.trim()),180)}),i.addEventListener("mousedown",l=>l.stopPropagation()),o.appendChild(s),o.appendChild(i),o}function he(e,n,t){const o=document.createElement("div");o.style.cssText=p+"cursor:default;";const s=document.createElement("span");s.textContent=e.label,s.style.flex="1";const i=document.createElement("input");return i.type="color",typeof t=="string"&&/^#/.test(t)?i.value=t:typeof e.default=="string"&&/^#/.test(e.default)&&(i.value=e.default),i.addEventListener("input",()=>x(n,i.value)),o.appendChild(s),o.appendChild(i),o}function be(e,n,t){const o=t==null||t===!0?e.default??"":t;return y(`${e.label}${o!==""?`: ${o}`:""}`,()=>{const s=g();for(const[i,c]of e.options||[]){const l=f(document.createElement("button"));l.type="button",l.style.cssText=p,l.textContent=c,i===o&&(l.style.fontWeight="700"),l.addEventListener("click",()=>{x(n,i),v()}),s.appendChild(l)}return s})}function xe(e,n,t){const o=t==null||t===!0?"":String(t),s=o?`${e.label}: ${o}`:e.label;return y(s,()=>{const i=g();i.style.width="232px";const c=document.createElement("input");c.type="text",c.placeholder="Search icons\u2026",c.style.cssText=F+"margin:6px;width:calc(100% - 12px);",c.addEventListener("mousedown",L=>L.stopPropagation()),i.appendChild(c);const l=f(document.createElement("button"));l.type="button",l.style.cssText=p,l.textContent="- none -",o||(l.style.fontWeight="700"),l.addEventListener("click",()=>{x(n,null),v()}),i.appendChild(l);const r=document.createElement("div");r.style.cssText="display:flex;flex-wrap:wrap;gap:2px;padding:4px 6px;max-height:220px;overflow-y:auto;",i.appendChild(r);const m=96,T=()=>{const L=typeof I<"u"&&typeof I.list=="function"?I.list():[],R=c.value.trim().toLowerCase(),_=R?L.filter(u=>u.toLowerCase().includes(R)):L;r.replaceChildren();for(const u of _.slice(0,m)){const b=f(document.createElement("button"));b.type="button",b.title=u,b.style.cssText="width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;background:none;border:1px solid transparent;border-radius:4px;cursor:pointer;color:inherit;",u===o&&(b.style.borderColor="var(--dm-primary,#5b8cff)");const k=document.createElement("span");k.setAttribute("data-icon",u),k.setAttribute("data-icon-size","15"),k.style.cssText="display:inline-flex;align-items:center;",b.appendChild(k),b.addEventListener("click",()=>{x(n,u),v()}),r.appendChild(b)}if(_.length>m){const u=document.createElement("div");u.style.cssText=S,u.textContent=`${_.length-m} more - refine search`,r.appendChild(u)}if(!_.length){const u=document.createElement("div");u.style.cssText=S,u.textContent="No icons match",r.appendChild(u)}window.I&&I.scan(r)};let h=null;return c.addEventListener("input",()=>{clearTimeout(h),h=setTimeout(T,120)}),T(),setTimeout(()=>c.focus(),0),i})}function ye(e,n,t){const o=t==null||t===!0?e.default??"":t;return y(`${e.label}${o!==""?`: ${o}`:""}`,()=>{const s=g();s.style.minHeight="48px";const i=typeof E<"u"&&E.loader?E.loader(s,{type:"dots"}):null;return ce(e.source).then(c=>{if(i&&i.destroy(),!a||a.submenu!==s)return;if(s.style.minHeight="",!c.length){const m=document.createElement("div");m.style.cssText=S,m.textContent="Nothing found",s.appendChild(m)}const l=c.some(([m])=>m===o),r=[["","- none -"],...c];o!==""&&!l&&r.push([o,`${o} (missing)`]);for(const[m,T]of r){const h=f(document.createElement("button"));h.type="button",h.style.cssText=p,h.textContent=T,m===o&&(h.style.fontWeight="700"),h.addEventListener("click",()=>{x(n,m===""?null:m),v()}),s.appendChild(h)}}),s})}function Ee(e,n){e.appendChild(C(`Effect: ${n.label}`));for(const o of n.attrs||[])e.appendChild(V(o,`${n.name}-${o.name}`));const t=f(document.createElement("button"));t.type="button",t.style.cssText=p,t.innerHTML='<span data-icon="trash" data-icon-size="13" style="display:inline-flex;align-items:center;"></span> Remove effect',t.addEventListener("click",()=>{const o={};for(const s of te(a.span.attrs,n.name))o[s]=null;D(a.textarea,a.span,o),d()}),e.appendChild(t)}function ge(){const e=g();e.appendChild(C("Seasonal themes"));for(const[n,t]of[["auto","Auto (date-detected)"],...Object.entries(Q).map(([o,s])=>[o,s.label])]){const o=f(document.createElement("button"));o.type="button",o.style.cssText=p,o.textContent=t,o.addEventListener("click",()=>{O(a.textarea,`[celebrate theme="${n}" /]`),d()}),e.appendChild(o)}e.appendChild(N()),e.appendChild(C("Celebration effects"));for(const n of["butterflies","ticker-tape","firework","fireworks"]){const t=j.get(n),o=f(document.createElement("button"));o.type="button",o.style.cssText=p,o.textContent=t.label,o.addEventListener("click",()=>{const s=a.textarea;d(),$(s,t)}),e.appendChild(o)}return e}function Ce(){const e=g(),n=a.span,t=n&&Y.has(n.name)?n.name:null;for(const o of[...new Set(w.map(s=>s.category))]){const s=w.filter(i=>i.category===o);if(s.length){e.appendChild(C(o));for(const i of s){const c=t&&(i.hosts||[]).includes(t)&&!(i.name in n.attrs),l=f(document.createElement("button"));if(l.type="button",l.style.cssText=p+"justify-content:space-between;",l.appendChild(document.createTextNode(i.label)),c){const r=document.createElement("span");r.textContent=`inject \u2192 [${t}]`,r.style.cssText="opacity:.6;font-size:.7rem;",l.appendChild(r)}l.addEventListener("click",()=>{if(c)P(a.textarea,a.span,i.name,!0);else{const r=i.selfClosing?`[${i.name} /]`:`[${i.name}]
2
2
  Content
3
- [/${i.name}]`;D(c.textarea,r)}m()}),e.appendChild(a)}}}return e}function y(e,n){const t=f(document.createElement("button"));t.type="button",t.style.cssText=p+"justify-content:space-between;",t.appendChild(document.createTextNode(e));const o=document.createElement("span");return o.setAttribute("data-icon","chevron-right"),o.setAttribute("data-icon-size","12"),o.style.cssText="opacity:.6;display:inline-flex;align-items:center;flex-shrink:0;",t.appendChild(o),t.addEventListener("mouseenter",()=>G(t,n)),t.addEventListener("click",s=>{s.stopPropagation(),G(t,n)}),t}function g(){const e=document.createElement("div");return e.className="dm-sc-submenu",e.style.cssText=W,e}function G(e,n){v();const t=n();c.submenu=t,document.body.appendChild(t);const o=e.getBoundingClientRect();q(t,o.right+2,o.top,o.left-2),window.I&&I.scan(t)}function v(){c&&c.submenu&&(c.submenu.remove(),c.submenu=null)}function X(e){const n=f(document.createElement("button"));if(n.type="button",n.style.cssText=p,e.icon){const t=document.createElement("span");t.setAttribute("data-icon",e.icon),t.setAttribute("data-icon-size","14"),t.style.cssText="opacity:.75;display:inline-flex;align-items:center;flex-shrink:0;",n.appendChild(t)}return n.appendChild(document.createTextNode(e.label)),n}function Ce(){const e=g();for(const{category:n,items:t}of se()){e.appendChild(C(n));for(const o of t){const s=X(o);s.addEventListener("click",()=>{const i=c.textarea;m(),ie(o.name,i)}),e.appendChild(s)}}return e}function ve(){const e=g();for(const n of $.filter(t=>t.wrappable||t.container)){const t=X(n);t.addEventListener("click",()=>{const o=c.textarea;m(),A(o,n)}),e.appendChild(t)}return e}function q(e,n,t,o){e.style.left="0px",e.style.top="0px";const s=e.getBoundingClientRect();let i=n;n+s.width>window.innerWidth-8&&(i=o!=null?o-s.width:n-s.width);let l=t;t+s.height>window.innerHeight-8&&(l=Math.max(8,window.innerHeight-s.height-8)),e.style.left=`${Math.max(8,i)}px`,e.style.top=`${Math.max(8,l)}px`}
3
+ [/${i.name}]`;O(a.textarea,r)}d()}),e.appendChild(l)}}}return e}function y(e,n){const t=f(document.createElement("button"));t.type="button",t.style.cssText=p+"justify-content:space-between;",t.appendChild(document.createTextNode(e));const o=document.createElement("span");return o.setAttribute("data-icon","chevron-right"),o.setAttribute("data-icon-size","12"),o.style.cssText="opacity:.6;display:inline-flex;align-items:center;flex-shrink:0;",t.appendChild(o),t.addEventListener("mouseenter",()=>G(t,n)),t.addEventListener("click",s=>{s.stopPropagation(),G(t,n)}),t}function g(){const e=document.createElement("div");return e.className="dm-sc-submenu",e.style.cssText=B,e}function G(e,n){v();const t=n();a.submenu=t,document.body.appendChild(t);const o=e.getBoundingClientRect();J(t,o.right+2,o.top,o.left-2),window.I&&I.scan(t)}function v(){a&&a.submenu&&(a.submenu.remove(),a.submenu=null)}function X(e){const n=f(document.createElement("button"));if(n.type="button",n.style.cssText=p,e.icon){const t=document.createElement("span");t.setAttribute("data-icon",e.icon),t.setAttribute("data-icon-size","14"),t.style.cssText="opacity:.75;display:inline-flex;align-items:center;flex-shrink:0;",n.appendChild(t)}return n.appendChild(document.createTextNode(e.label)),n}function ve(){const e=g();for(const{category:n,items:t}of ie()){e.appendChild(C(n));for(const o of t){const s=X(o);s.addEventListener("click",()=>{const i=a.textarea;d(),ae(o.name,i)}),e.appendChild(s)}}return e}function we(){const e=g();for(const n of A.filter(t=>t.wrappable||t.container)){const t=X(n);t.addEventListener("click",()=>{const o=a.textarea;d(),$(o,n)}),e.appendChild(t)}return e}function J(e,n,t,o){e.style.left="0px",e.style.top="0px";const s=e.getBoundingClientRect();let i=n;n+s.width>window.innerWidth-8&&(i=o!=null?o-s.width:n-s.width);let c=t;t+s.height>window.innerHeight-8&&(c=Math.max(8,window.innerHeight-s.height-8)),e.style.left=`${Math.max(8,i)}px`,e.style.top=`${Math.max(8,c)}px`}
@@ -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:"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"}];
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:"navbar",label:"Navbar settings (this page)",icon:"sliders",category:"Layout",selfClosing:!0,container:!1,wrappable:!1,template:'[navbar collapsed="true" /]',actions:[{label:"Collapse the rail on this page",icon:"chevrons-right",set:{collapsible:"true",collapsed:"true",autohide:"false"}},{label:"Autohide the rail on this page",icon:"eye-off",set:{autohide:"true",collapsed:null}},{label:"Keep the rail open on this page",icon:"eye",set:{autohide:"false",collapsed:"false"}},{label:"Hide the bar on scroll on this page",icon:"arrow-up",set:{"hide-on-scroll":"true"}},{label:"Remove this override",icon:"trash",remove:!0}],quickAttrs:["position","width","collapsible","collapsed","autohide","hide-on-scroll"],attrs:[{name:"position",type:"select",label:"Position",options:[["","Menu default"],["sticky","Sticky"],["fixed","Fixed"],["static","Static"],["floating","Floating"]]},{name:"orientation",type:"select",label:"Orientation",options:[["","Menu default"],["horizontal","Horizontal"],["vertical","Vertical"]]},{name:"side",type:"select",label:"Rail side",options:[["","Menu default"],["left","Left"],["right","Right"]]},{name:"align",type:"select",label:"Alignment",options:[["","Menu default"],["left","Left"],["centre","Centre"],["right","Right"],["spread","Spread"]]},{name:"width",type:"number",label:"Rail width (px, 160-480)"},{name:"collapsible",type:"select",label:"Collapsible to icons",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"collapsed",type:"select",label:"Start collapsed",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"autohide",type:"select",label:"Autohide off-screen",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"hide-on-scroll",type:"select",label:"Hide on scroll (bar)",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"hover-open",type:"select",label:"Open dropdowns on hover",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"hover",type:"text",label:"Hover effect"},{name:"selected",type:"text",label:"When selected"},{name:"accent",type:"colour",label:"Highlight colour"},{name:"variant",type:"text",label:"Variant"},{name:"font",type:"text",label:"Font family"},{name:"font-size",type:"text",label:"Font size"},{name:"font-weight",type:"text",label:"Font weight"},{name:"letter-spacing",type:"text",label:"Letter spacing"},{name:"icon-size",type:"text",label:"Icon size"},{name:"float-anchor",type:"select",label:"Float anchor",options:[["","Menu default"],["TL","Top left"],["TR","Top right"],["BL","Bottom left"],["BR","Bottom right"],["L","Left"],["R","Right"]]},{name:"float-x",type:"number",label:"Float offset X (px)"},{name:"float-y",type:"number",label:"Float offset Y (px)"},{name:"float-width",type:"number",label:"Float width (px)"},{name:"float-collapsible",type:"select",label:"Float: collapsible",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"float-movable",type:"select",label:"Float: movable",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"class",type:"text",label:"CSS classes"}]},{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"}];
@@ -90,6 +90,23 @@
90
90
  </select>
91
91
  <small class="form-hint">Which edge a vertical navbar docks to.</small>
92
92
  </div>
93
+ <div class="form-group dm-dock-rail-field" style="display:none">
94
+ <label for="me-dock-width">Rail width (px)</label>
95
+ <input type="number" id="me-dock-width" name="dockWidth" class="form-input" min="160" max="480" step="10" placeholder="240">
96
+ <small class="form-hint">How wide the docked rail is, 160 to 480. The page's gutter follows it.</small>
97
+ </div>
98
+ <div class="form-group dm-dock-rail-field" style="display:none">
99
+ <label>Rail behaviour</label>
100
+ <label class="flex items-center gap-2"><input type="checkbox" id="me-dock-collapsible" name="dockCollapsible" style="width:auto;margin:0"> Collapsible to icons</label>
101
+ <label class="flex items-center gap-2 dm-dock-collapsed-field"><input type="checkbox" id="me-dock-collapsed" name="dockCollapsed" style="width:auto;margin:0"> Start collapsed</label>
102
+ <label class="flex items-center gap-2"><input type="checkbox" id="me-dock-autohide" name="dockAutohide" style="width:auto;margin:0"> Autohide off-screen</label>
103
+ <small class="form-hint"><strong>Collapsible</strong> adds a toggle that shrinks the rail to its icons (labels become tooltips); each visitor's choice is remembered. <strong>Autohide</strong> slides the rail off its edge, leaving a strip - it comes back when the pointer reaches that edge, on a tap, or when focus moves into it - and the page takes the full width. Desktop only: below 993px the menu is the usual drawer.</small>
104
+ </div>
105
+ <div class="form-group dm-dock-bar-field" style="display:none">
106
+ <label>Bar behaviour</label>
107
+ <label class="flex items-center gap-2"><input type="checkbox" id="me-dock-hideOnScroll" name="dockHideOnScroll" style="width:auto;margin:0"> Hide on scroll</label>
108
+ <small class="form-hint">The bar slides away while the page scrolls down and comes back as soon as it scrolls up. Best with Position <strong>Sticky</strong>.</small>
109
+ </div>
93
110
  <div class="form-group dm-float-field" style="display:none">
94
111
  <label for="me-float-anchor">Float anchor</label>
95
112
  <select id="me-float-anchor" name="floatAnchor" class="form-input">
@@ -1,30 +1,30 @@
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}>
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 ct,cssClassProblems as rt,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 At,ensureLockedTools as Et,isLockedUrl as Ct,groupKeyFor as Lt,slugify as _t,GROUP_ICON as Ht,TOOLS_FOLDER_KEY as It}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 Ot,bindingFrom as Nt,duplicateRow as Pt,indentRow as Ft,insertBelow as Bt,isStructural as ae,menuFromForm as zt,menuProblems as Mt,moveRow as ut,outdentRow as Wt,siblingsOf as Kt,snapshot as He}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 y(o){return String(o||"").replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;")}const Ie=o=>String(o??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;");function Yt(o,r){let l=0,i=r.find(s=>s.id===o);for(;i&&i.parentId;)l++,i=r.find(s=>s.id===i.parentId);return l}function mt(o){const r=new Set,l=new Set,i=s=>{for(const g of o.filter(k=>k.parentId===s))!g.type&&Ct(g.url)&&!l.has(g.url)&&(l.add(g.url),r.add(g.id)),i(g.id)};return i(null),r}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(o,r,l=!1){const i=l?mt(r):new Set,s=[];function g(k){for(const p of r.filter(F=>F.parentId===k)){const A=`style="margin-left:${Yt(p.id,r)*28}px"`;if(p.type==="separator"){s.push(`
2
+ <div class="menu-tree-row menu-tree-row--separator" data-id="${p.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
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}>
6
+ </div>`);continue}if(p.type==="spacer"){const oe=["xs","sm","md","lg","xl","flex"].map(B=>`<option value="${B}"${(p.size||"md")===B?" selected":""}>${B==="flex"?"flexible":B}</option>`).join("");s.push(`
7
+ <div class="menu-tree-row menu-tree-row--spacer" data-id="${p.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
- <select class="form-input me-spacer-size" title="Spacer size - flexible fills the leftover room" aria-label="Spacer size">${te}</select>
11
+ <select class="form-input me-spacer-size" title="Spacer size - flexible fills the leftover room" aria-label="Spacer size">${oe}</select>
12
12
  </span>
13
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}>
14
+ </div>`);continue}if(p.type==="theme-switcher"){s.push(`
15
+ <div class="menu-tree-row menu-tree-row--switcher" data-id="${p.id}" tabindex="0" ${A}>
16
16
  <span class="menu-tree-grip" data-icon="grip-vertical" data-icon-size="18" title="Drag to move"></span>
17
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
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}>
19
+ </div>`);continue}const J=p.badge&&(p.badge.text||p.badge.countFrom)?`<span class="dm-menu-badge">${y(String(p.badge.text||"#"))}</span>`:"",C=p.tool,v=C&&(C.persisted||p.touched),K=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="${v?"A Tool, placed here by this menu":"A Tool in its default place - move or edit it to place it here"}">${y(C.source||"Tool")}${v?"":" \xB7 default"}</span>`:"",se=i.has(p.id),te=kt(p.colour),q=te?` style="color:${y(te)}"`:"",le=se?'<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 ${p.hidden?"active":""}" title="${p.hidden?"Hidden - click to show":"Hide"}" aria-label="${p.hidden?"Show":"Hide"}" aria-pressed="${p.hidden?"true":"false"}"><span data-icon="${p.hidden?"eye-off":"eye"}"></span></button>`;s.push(`
20
+ <div class="menu-tree-row menu-tree-row--slim${p.hidden?" menu-tree-row--hidden":""}${C?" menu-tree-row--tool":""}${C&&!v?" menu-tree-row--default":""}" data-id="${p.id}" tabindex="0" ${A}>
21
21
  <span class="menu-tree-grip" data-icon="grip-vertical" data-icon-size="18" title="Drag to move"></span>
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}
22
+ <span class="me-row-label${!p.text&&!C?" mex-untitled":""}"${q}>${y(p.text||C?.label||"(no label - click to add one)")}${J}${K}</span>
23
+ <span class="me-row-url">${y(p.url||"")}</span>
24
+ ${le}
25
25
  <button type="button" class="btn btn-sm btn-ghost me-edit" title="Edit" aria-label="Edit"><span data-icon="edit"></span></button>
26
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(`
29
- `).map(a=>a.trim()).filter(Boolean);n.includes(e)||n.push(e),Ae.val(n.join(`
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())}}};
27
+ </div>`),g(p.id)}}g(null),o.innerHTML=s.join("")||'<p class="mex-note">No items yet - Add item, or right-click an item for more.</p>',I.scan(o)}function w(o,r={},...l){const i=document.createElement(o);Object.assign(i,r);for(const s of l)s!=null&&i.append(s);return i}function T(o,r,l){const i=w("label",{className:"form-field"},w("span",{className:"form-label",textContent:o}),r);return l&&i.append(w("small",{className:"form-hint",textContent:l,style:"display:block;font-size:.78em;opacity:.7;margin-top:.25rem;line-height:1.35"})),i}function Jt(o,r){const l=[["","- Everyone -"],["public","Public - anyone, incl. logged-out"],...(r||[]).map(s=>[s.name,`${s.label||s.name} and above`]),...(r||[]).filter(s=>s.level!==0).map(s=>[`=${s.name}`,`${s.label||s.name} only`]),["private","Private - super-admin only"]];o&&!l.some(([s])=>s===o)&&l.push([o,`${o} (custom)`]);const i=ee(l,o||"");return i.name="visibility",i}function ee(o,r){const l=w("select",{className:"form-select"});return o.forEach(([i,s])=>l.add(new Option(s,i))),l.value=r??"",l}function Re(o,r){const l=typeof r=="string"&&r.startsWith("#"),i=ee([["","- none -"],["primary","primary"],["success","success"],["danger","danger"],["warning","warning"],["info","info"],["neutral","neutral"],["__custom__","Custom\u2026"]],l?"__custom__":r||""),s=w("input",{type:"color",className:"form-input me-colour-hex",value:l?r:"#000000"});s.style.display=l?"":"none",i.addEventListener("change",()=>{s.style.display=i.value==="__custom__"?"":"none"});const g=T(o,i);return g.append(s),g._read=()=>i.value==="__custom__"?s.value:i.value,g}function Qt(o,r){const l=w("input",{className:"form-input me-icon",name:"icon",value:r||""}),i=w("span",{className:"me-icon-preview"});r&&i.setAttribute("data-icon",r);const s=w("button",{type:"button",className:"btn btn-sm btn-ghost",textContent:"Browse\u2026"}),g=k=>{k?i.setAttribute("data-icon",k):i.removeAttribute("data-icon"),Domma.icons.scan(i.parentNode||i)};return s.addEventListener("click",()=>$t(k=>{l.value=k,g(k)})),l.addEventListener("input",()=>g(l.value.trim())),T(o,w("div",{className:"me-icon-field"},l,s,i))}function je(...o){return w("div",{className:"me-field-grid"},...o)}async function Zt(o,r,l,i,s){const g=o.text||o.tool?.label||"",k=u=>w("h4",{className:"me-form-section",textContent:u}),p=T("Label",w("input",{className:"form-input",name:"text",value:o.text||""}),"The text shown for this item."),F=w("input",{className:"form-input",name:"url",value:o.url||""}),A=T("URL",F,o.tool||o.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.");(o.tool||o.locked)&&(F.readOnly=!0);const J=Qt("Icon",o.icon||""),C=Jt(o.visibility||"",i),v=T("Visibility",C,o.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.');o.locked&&(C.disabled=!0);const K=ee([["","- no permission gate -"],...(r||[]).map(u=>[u.key,u.label||u.key])],o.permission||"");K.name="permission";const se=T("Permission",K,o.tool||o.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.");(o.tool||o.locked)&&(K.disabled=!0);const te=T("Badge text",w("input",{className:"form-input",name:"badgeText",value:o.badge?.text||""})),q=ee([["","- none -"],...(l||[]).map(u=>[u.slug,u.title||u.slug])],o.badge?.countFrom||"");q.name="badgeCount";const le=T("Badge count from",q,"Show a live count of this collection\u2019s entries as the badge."),oe=Re("Badge colour",o.badge?.variant||""),B=ee([["link","Link"],["pill","Pill"]],o.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",ee([["filled","Filled"],["outline","Outline"]],o.pill?.style||"filled"));D.querySelector("select").name="pillStyle";const pe=Re("Pill colour",o.pill?.variant||""),ie=w("div",{className:"me-pill-wrap"},je(D,pe));ie.style.display=o.pill?"":"none",B.addEventListener("change",()=>{ie.style.display=B.value==="pill"?"":"none"});const h=Re("Text colour",o.colour||""),O=ee([["","- use the menu default -"],...st.map(u=>[u,it[u]||u])],o.highlight?.hover||"");O.name="hlHover";const ce=T("Hover effect",O,"Overrides the menu-wide hover effect for this item only."),he=ee([["","- use the menu default -"],...lt.map(u=>[u,ct[u]||u])],o.highlight?.active||"");he.name="hlActive";const Se=T("When selected",he,"How this item is marked when it points at the page being viewed."),z=Re("Highlight colour",o.highlight?.accent||""),L=w("input",{className:"form-input",name:"class",value:o.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."),_=w("small",{className:"text-danger",style:"display:none;margin-top:.25rem"});re.append(_);const U=w("button",{className:"btn btn-primary",textContent:"Apply",title:"Apply to the item (Ctrl+Enter) - Save the menu to keep it"}),N=w("div",{className:"me-item-form"},k("Link"),p,A,J,k("Access"),je(v,se),k("Badge"),je(te,oe),le,k("Appearance"),De,ie,h,k("Highlighting"),je(ce,Se),z,k("Styling"),re,U),x=E.slideover({title:g?`Edit ${Ie(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 de=!1,ue=!1;jt(x);const G=x.close.bind(x);x.close=async u=>{if(!de&&u!==!0&&fe()!==$e){if(ue)return;ue=!0;const Q=await E.confirm("Discard the changes to this item?");if(ue=!1,!Q)return}return de=!0,G()},N.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(N);const $=u=>N.querySelector(`[name="${u}"]`)?.value||"";U.addEventListener("click",()=>{const u=rt($("class"));if(u.length){_.textContent=u.join("; "),_.style.display="block",L.focus();return}o.class=St($("class")),o.text=$("text"),o.icon=$("icon"),o.locked||(o.visibility=$("visibility")),!o.tool&&!o.locked?(o.url=$("url"),o.permission=$("permission")):o.touched=!0;const Q=$("badgeText"),me=$("badgeCount"),ve=oe._read();if(o.badge=Q||me?{...Q&&{text:Q},...me&&{countFrom:me},...ve&&{variant:ve}}:null,$("renderAs")==="pill"){const we=pe._read();o.pill={style:$("pillStyle")||"filled",...we&&{variant:we}}}else o.pill=null;o.colour=h._read();const ge=$("hlHover"),be=$("hlActive"),ye=z._read();o.highlight=ge||be||ye?{...ge&&{hover:ge},...be&&{active:be},...ye&&{accent:ye}}:null,de=!0,x.close(!0),s()})}let W=null,pt=!1;function eo(){pt||!window.R?.use||(pt=!0,R.use((o,r,l)=>{const i=r?.path||"";return!(Vt.test(i)&&(o?.path||"")!==i)||!W?.dirty()?l():E.confirm(`You have unsaved changes to the menu "${Ie(W.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:o}={})=>{if(W&&(o?.path||"")!==W.path){const r=W;W=null,r.dispose()}}))}function ht(o,r){const l=o.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(r))}}export const menuEditorView={templateUrl:"/admin/js/templates/menu-editor.html",async onMount(o){if(eo(),W){const e=W;W=null,e.dispose()}const r=[],l=e=>(r.push(e),e);let i=!1;const s=o.get(0).querySelector(".mex")||o.get(0),g=()=>!i&&s.isConnected,k=location.hash.replace(/^#/,""),p=location.hash.match(/^#\/menus\/edit\/([^?#]+)/),F=p?decodeURIComponent(p[1]):null,A=!F,J=`menu-editor.draft.${F||"new"}`,C=E.loader(o.get(0),{type:"dots"});let v,K,se,te,q,le,oe;try{if(A)v={slug:"",name:"",description:"",items:[]};else try{v=await j.menus.get(F)}catch(e){E.toast(`Failed to load menu: ${e.message||e}`,{type:"error"}),location.hash="#/menus";return}[K,se,te,q,le,oe]=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,t)=>t.level-e.level),D=!A&&F===Gt;let pe=[];if(D&&(pe=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?Et(At(e||[],pe,{forEditor:!0}),{leaveInHiddenFolders:!0}):e);let h=ie(v.items);const O=M.observable(!1),ce=M.observable(!1),he=M.observable(""),Se=M.observable(0);let z="";const L=s.querySelector("#me-items-list");function re(){Xt(L,h,D),Se.value=h.filter(e=>!ae(e)).length}const _=()=>{re(),Y()},U=()=>D?mt(h):new Set,N=e=>h.find(t=>t.id===e);function x(e){requestAnimationFrame(()=>L.querySelector(`.menu-tree-row[data-id="${CSS.escape(e)}"]`)?.focus())}function fe(e){const t=f=>h.find(c=>!c.type&&!c.url&&!c.tool&&(c.key||"").toLowerCase()===f)||h.find(c=>!c.type&&!c.url&&!c.tool&&!c.key&&(c.text||"").toLowerCase()===f);let a=t(String(e.tool.folder||"").toLowerCase())||t(It);const m=String(e.tool.group||"").trim();if(a&&m){const f=Lt(a.key||_t(a.text),m),c=d=>d!==e&&!d.type&&!d.url&&!d.tool;let b=h.find(d=>c(d)&&(d.key||"").toLowerCase()===f)||h.find(d=>c(d)&&d.parentId===a.id&&(!d.key||d.key.toLowerCase()===f)&&(d.text||"").toLowerCase()===m.toLowerCase());b||([b]=dt([{text:m,key:f,icon:Ht,synthesised:!0}],a.id),h.push(b)),a=b}e.parentId=a?a.id:null,h.splice(h.indexOf(e),1),h.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 de(e){const t=N(e);if(!t||!$e(t))return;const a=d=>{let X=d;for(;X;){if(X.id===e)return!0;X=h.find(P=>P.id===X.parentId)}return!1},m=h.filter(d=>d.id!==e&&a(d));if(m.length&&!await E.confirm(`Remove "${Ie(t.text||t.type||"this item")}" and the ${m.length} item${m.length===1?"":"s"} beneath it?`))return;const f=U(),c=m.filter(d=>d.tool),b=m.filter(d=>!d.tool&&f.has(d.id));h=h.filter(d=>!a(d)||c.includes(d)||b.includes(d));for(const d of b)d.parentId=t.parentId??null;for(const d of c)d.tool.orphan?d.parentId=t.parentId??null:fe(d);_()}function ue(e){const t=N(e);!t||ae(t)||U().has(e)||(t.hidden=!t.hidden,t.touched=!0,_())}function G(e){const t=N(e);!t||ae(t)||(t.locked=D&&U().has(t.id),Zt(t,se.resources||[],te,De,()=>{_(),x(e)}))}function ne(e){const t={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}};h.push(t),_(),ae(t)?x(t.id):G(t.id)}const $=e=>t=>{e(t)&&(_(),x(t))},u=$(e=>ut(h,e,-1)),Q=$(e=>ut(h,e,1)),me=$(e=>Ft(h,e)),ve=$(e=>Wt(h,e));function ge(e){const t=N(e);if(!t)return[];const a=ae(t),m=U().has(e),f=Kt(h,e),c=f.findIndex(P=>P.id===e),b=t.tool,d=b&&(b.persisted||t.touched),X=P=>()=>{const ke=Bt(h,e,P,_e());_(),ke&&!ae(ke)?G(ke.id):ke&&x(ke.id)};return[{header:a?{separator:"Separator",spacer:"Spacer","theme-switcher":"Theme switcher"}[t.type]:t.text||b?.label||"(untitled)"},...a?[]:[{label:"Edit\u2026",icon:"edit",action:()=>G(e)}],{type:"divider"},{label:"Add item below",icon:"plus",action:X("item")},{label:"Add sub-item",icon:"corner-down-right",disabled:a,action:()=>{const P=Ot(h,e,_e());_(),P&&G(P.id)}},{label:"Add separator below",icon:"minus",action:X("separator")},{label:"Add spacer below",icon:"move",action:X("spacer")},...D?[]:[{label:"Add theme switcher below",icon:"palette",action:X("theme-switcher")}],{type:"divider"},{label:"Move up",icon:"arrow-up",shortcut:"Alt+\u2191",disabled:c<=0,action:()=>u(e)},{label:"Move down",icon:"arrow-down",shortcut:"Alt+\u2193",disabled:c<0||c>=f.length-1,action:()=>Q(e)},{label:"Indent",icon:"chevron-right",shortcut:"Alt+\u2192",disabled:c<=0||ae(f[c-1]),action:()=>me(e)},{label:"Outdent",icon:"chevron-left",shortcut:"Alt+\u2190",disabled:t.parentId==null,action:()=>ve(e)},{type:"divider"},...a?[]:[{label:t.hidden?"Show":"Hide",icon:t.hidden?"eye":"eye-off",disabled:m,action:()=>ue(e)}],{label:"Duplicate",icon:"copy",disabled:!!b,action:()=>{const P=Pt(h,e,_e);_(),P&&x(P.id)}},...b&&!b.orphan&&d?[{label:"Back to its default place",icon:"rotate-ccw",action:()=>{fe(t),_()}}]:[],{label:m?"Remove - always in the sidebar":b&&!b.orphan?"Remove - a Tool is hidden, not removed":"Remove\u2026",icon:"trash",danger:!0,disabled:!$e(t),action:()=>de(e)}]}const be=E.contextMenu(L,{cursor:"glyph",match:".menu-tree-row",inherit:!1,density:"compact",items:e=>ge(e.dataset.id)});l(()=>be?.destroy?.());const ye=xt(L,()=>h,_,{items:".menu-tree-row",handle:".menu-tree-grip"});l(()=>ye?.destroy());const we=e=>{const t=e.target.closest(".menu-tree-row");if(!t)return;const a=t.dataset.id;if(e.target.closest(".mex-kebab")){Ue(e.target.closest(".mex-kebab"));return}if(e.target.closest(".me-hidden")){ue(a);return}if(e.target.closest(".me-edit")){G(a);return}e.target.closest("select, .menu-tree-grip, button")||G(a)},Ge=e=>{const t=e.target.closest(".menu-tree-row");if(!t||e.target!==t){t&&e.key==="F10"&&e.shiftKey&&(e.preventDefault(),Ue(t));return}const a=t.dataset.id;if(e.key==="F10"&&e.shiftKey)e.preventDefault(),Ue(t);else if(e.key==="Enter")e.preventDefault(),G(a);else if(e.key==="Delete")e.preventDefault(),de(a);else if(e.altKey&&e.key==="ArrowUp")e.preventDefault(),u(a);else if(e.altKey&&e.key==="ArrowDown")e.preventDefault(),Q(a);else if(e.altKey&&e.key==="ArrowRight")e.preventDefault(),me(a);else if(e.altKey&&e.key==="ArrowLeft")e.preventDefault(),ve(a);else if(e.key==="ArrowDown"||e.key==="ArrowUp"){const m=[...L.querySelectorAll(".menu-tree-row")],f=m.indexOf(t)+(e.key==="ArrowDown"?1:-1);m[f]&&(e.preventDefault(),m[f].focus())}},Ve=e=>{if(!e.target.matches(".me-spacer-size"))return;const t=N(e.target.closest(".menu-tree-row")?.dataset.id);t&&(t.size=e.target.value,Y())};L.addEventListener("click",we),L.addEventListener("keydown",Ge),L.addEventListener("change",Ve),l(()=>{L.removeEventListener("click",we),L.removeEventListener("keydown",Ge),L.removeEventListener("change",Ve)});const n=e=>o.find(e),xe=n("#me-float-accent"),Oe=n("#me-float-accent-hex"),Ne=()=>{Oe.get(0).style.display=xe.val()==="__custom__"?"":"none"},ft=()=>xe.val()==="__custom__"?Oe.val():xe.val(),Ae=n("#me-highlight-accent"),Pe=n("#me-highlight-accent-hex"),Ye=()=>{Pe.get(0).style.display=Ae.val()==="__custom__"?"":"none"},vt=()=>Ae.val()==="__custom__"?Pe.val():Ae.val(),Xe=()=>{const e=n("#me-position").val()==="floating";n(".dm-float-field").each(function(){this.style.display=e?"":"none"}),e&&Ne();const t=n("#me-orientation").val()==="vertical";n(".dm-vertical-field").each(function(){this.style.display=t?"":"none"}),n(".dm-dock-rail-field").each(function(){this.style.display=t&&!e?"":"none"}),n(".dm-dock-bar-field").each(function(){this.style.display=!t&&!e?"":"none"});const a=!!(n("#me-dock-collapsible").get(0)||{}).checked,m=!!(n("#me-dock-autohide").get(0)||{}).checked;n(".dm-dock-collapsed-field").each(function(){this.style.display=a&&!m?"":"none"});const f=!!(n("#me-float-collapsible").get(0)||{}).checked;n(".dm-float-collapsed-field").each(function(){this.style.display=e&&f?"":"none"})},Je=(e,t,a,m)=>{const f=n(e).get(0);f&&(f.innerHTML="",f.add(new Option(m,"")),t.forEach(c=>f.add(new Option(a[c]||c,c))))};Je("#me-highlight-hover",st,it,"- theme default -"),Je("#me-highlight-active",lt,ct,"- theme default -");const Fe=n("#me-bind-slot"),Be=n("#me-bind-projects"),Ee=n("#me-bind-pages");Fe.html('<option value="">- not bound -</option>'+(le||[]).map(e=>`<option value="${y(e.slot)}">${y(e.label||e.slot)} (${y(e.slot)})</option>`).join("")),Be.html(K.map(e=>`<option value="${y(e.slug)}">${y(e.name||e.slug)}</option>`).join("")),n("#me-bind-page-pick").html('<option value="">- pick a page -</option>'+oe.map(e=>{const t=e.urlPath||e.url||"";return t?`<option value="${y(t)}">${y(e.title||t)} - ${y(t)}</option>`:""}).join(""));const Qe=()=>Nt({slot:Fe.val(),projects:Array.from(Be.get(0).selectedOptions).map(e=>e.value),pagesText:Ee.val()});function ze(){const e=Qe(),t=n("#me-bind-summary");if(!e){t.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(c=>`<code>${y(c)}</code>`).join(", ")}`),e.pages.length&&a.push(`pages matching ${e.pages.map(c=>`<code>${y(c)}</code>`).join(", ")}`);const m=e.pages.filter(c=>!/^\/[^\s?#*]*(\/\*)?$|^\/\*$/.test(c)),f=m.length?`<div class="text-danger" style="margin-top:.4rem">Not valid page patterns: ${m.map(c=>`<code>${y(c)}</code>`).join(", ")}</div>`:"";t.html(a.length?`Takes over the <code>${y(e.slot)}</code> slot on ${a.join(" and ")}.${f}`:`<span class="text-warning">Slot <code>${y(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;n("#me-project").html('<option value="">- none -</option>'+K.map(e=>`<option value="${y(e.slug)}">${y(e.name||e.slug)}</option>`).join(""));function Me(e){h=ie(e.items),n("#me-variant").val(e.variant||""),n("#me-position").val(e.position||"");const t=n("#me-appearOnHover").get(0);t&&(t.checked=e.appearOnHover===!0),n("#me-float-anchor").val(e.float?.anchor||"TL"),n("#me-float-offsetX").val(e.float?.offsetX??""),n("#me-float-offsetY").val(e.float?.offsetY??""),n("#me-float-width").val(e.float?.width??""),n("#me-float-radius").val(e.float?.radius??""),n("#me-float-shadow").val(e.float?.shadow||""),n("#me-float-opacity").val(e.float?.opacity??""),n("#me-float-title").val(e.float?.title||"");for(const[c,b]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=n(c).get(0);d&&(d.checked=b===!0)}const a=e.float?.accent||"";xe.val(a.startsWith("#")?"__custom__":a),a.startsWith("#")&&Oe.val(a),Ne(),n("#me-orientation").val(e.orientation||""),n("#me-align").val(e.align||""),n("#me-side").val(e.side||"left"),n("#me-dock-width").val(e.dock?.width??"");for(const c of["collapsible","collapsed","autohide","hideOnScroll"]){const b=n(`#me-dock-${c}`).get(0);b&&(b.checked=e.dock?.[c]===!0)}n("#me-class").val(e.class||""),n("#me-highlight-hover").val(e.highlight?.hover||""),n("#me-highlight-active").val(e.highlight?.active||"");const m=e.highlight?.accent||"";Ae.val(m.startsWith("#")?"__custom__":m),m.startsWith("#")&&Pe.val(m),Ye(),n("#me-fontFamily").val(e.style?.fontFamily||""),n("#me-fontSize").val(e.style?.fontSize||""),n("#me-fontWeight").val(e.style?.fontWeight||""),n("#me-letterSpacing").val(e.style?.letterSpacing||""),n("#me-iconSize").val(e.style?.iconSize||""),Fe.val(e.binding?.slot||"");const f=new Set(e.binding?.projects||[]);Array.from(Be.get(0).options).forEach(c=>{c.selected=f.has(c.value)}),Ee.val((e.binding?.pages||[]).join(`
28
+ `)),n("#me-slug").val(e.slug||""),n("#me-name").val(e.name||""),n("#me-description").val(e.description||""),n("#me-project").val(e.meta?.project||bt||""),Xe(),ze(),re()}const V=e=>!!(n(e).get(0)||{}).checked;function Ce(){const e={variant:n("#me-variant").val(),position:n("#me-position").val(),appearOnHover:V("#me-appearOnHover"),floatAnchor:n("#me-float-anchor").val(),floatOffsetX:n("#me-float-offsetX").val(),floatOffsetY:n("#me-float-offsetY").val(),floatWidth:n("#me-float-width").val(),floatRadius:n("#me-float-radius").val(),floatShadow:n("#me-float-shadow").val(),floatAccent:ft(),floatOpacity:n("#me-float-opacity").val(),floatTitle:n("#me-float-title").val(),floatCollapsible:V("#me-float-collapsible"),floatCollapsed:V("#me-float-collapsed"),floatMovable:V("#me-float-movable"),floatResizable:V("#me-float-resizable"),highlightHover:n("#me-highlight-hover").val(),highlightActive:n("#me-highlight-active").val(),highlightAccent:vt(),class:n("#me-class").val(),orientation:n("#me-orientation").val(),align:n("#me-align").val(),side:n("#me-side").val(),fontFamily:n("#me-fontFamily").val(),fontSize:n("#me-fontSize").val(),fontWeight:n("#me-fontWeight").val(),letterSpacing:n("#me-letterSpacing").val(),iconSize:n("#me-iconSize").val(),dockWidth:n("#me-dock-width").val(),dockCollapsible:V("#me-dock-collapsible"),dockCollapsed:V("#me-dock-collapsed"),dockAutohide:V("#me-dock-autohide"),dockHideOnScroll:V("#me-dock-hideOnScroll")},t={slug:n("#me-slug").val(),name:n("#me-name").val(),description:n("#me-description").val(),project:n("#me-project").val()};return zt(e,t,Qe(),Tt(h),v.meta||{})}const Ze=e=>[...Mt(e),...rt(e.class).map(t=>`CSS classes: ${t} (Behaviour tab).`)];let et=!1,We=null;function Y(){if(!z)return;const e=Ce();We=e,O.value=He(e)!==z,he.value=O.peek()||et?Ze(e).join(" "):""}const Le=e=>{e.target.closest("#me-items-list")||(e.target.matches("#me-position, #me-orientation, #me-float-collapsible, #me-dock-collapsible, #me-dock-autohide")&&Xe(),e.target.matches("#me-float-accent")&&Ne(),e.target.matches("#me-highlight-accent")&&Ye(),e.target.closest("#me-binding")&&ze(),Y())};s.addEventListener("input",Le),s.addEventListener("change",Le),l(()=>{s.removeEventListener("input",Le),s.removeEventListener("change",Le)}),n("#me-bind-page-add").on("click",()=>{const e=n("#me-bind-page-pick").val();if(!e)return;const t=Ee.val().split(`
29
+ `).map(a=>a.trim()).filter(Boolean);t.includes(e)||t.push(e),Ee.val(t.join(`
30
+ `)),ze(),Y()}),Me(v),z=He(Ce()),Y();const tt=()=>{try{S.remove?S.remove(J):S.set(J,null)}catch{}};async function Ke(){if(ce.peek())return;const e=Ce(),t=Ze(e);if(t.length){et=!0,Y(),E.toast(t.join(" "),{type:"warning",duration:7e3});return}ce.value=!0;try{if(A)await j.menus.create(e);else if(e.slug!==v.slug){await j.menus.create(e);const a=await j.menuLocations.get();let m=!1;for(const[f,c]of Object.entries(a))c===v.slug&&(a[f]=e.slug,m=!0);m&&await j.menuLocations.save(a),await j.menus.remove(v.slug)}else await j.menus.update(v.slug,e);if(z=He(e),O.value=!1,tt(),Dt("#/menus"),E.toast("Saved.",{type:"success"}),A||e.slug!==v.slug)location.hash="#/menus/edit/"+encodeURIComponent(e.slug);else{v={...v,...e};for(const a of h)a.tool&&a.touched&&(a.tool={...a.tool,persisted:!0});re(),z=He(Ce()),Y(),ht(o.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{ce.value=!1}}async function yt(){!O.peek()||!await E.confirm("Throw away the changes you have not saved?")||(Me(v),Y())}E.tabs(n("#me-tabs").get(0));const wt=M.applyBindings({dirty:O,saving:ce,problems:he,itemCount:Se},s,{methods:{save:()=>Ke(),discard:()=>yt(),addItem:()=>ne("item"),addSeparator:()=>ne("separator"),addSpacer:()=>ne("spacer"),addSwitcher:()=>ne("theme-switcher")}});l(()=>wt.dispose()),l(Rt(s,{position:"bottom"})),D&&n("#me-add-switcher").hide(),ht(o.get(0),A?"New menu":v.name||v.slug),qe(o,{id:"menu-editor-back",icon:"arrow-left",title:"All menus",onClick:()=>{location.hash="#/menus"}}),qe(o,{id:"menu-editor-locations",icon:"map-pin",title:"Menu locations",onClick:()=>{location.hash="#/menu-locations"}}),qe(o,{id:"menu-editor-save",icon:"save",title:"Save (Ctrl+S)",onClick:()=>Ke()});const ot=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",ot,!0),l(()=>document.removeEventListener("keydown",ot,!0));const nt=()=>{if(!(!O.peek()||!We))try{S.set(J,{menu:We,base:z,at:new Date().toISOString()})}catch{}},at=e=>{O.peek()&&(nt(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",at),l(()=>window.removeEventListener("beforeunload",at)),W={path:k,dirty:()=>O.peek(),name:()=>n("#me-name").val()||v.name||v.slug||"new menu",dispose(){i=!0,nt(),r.splice(0).reverse().forEach(e=>{try{e()}catch{}})}},I.scan(s);let Z=null;try{Z=S.get(J)}catch{}if(Z?.menu){tt();const e=Z.base&&Z.base!==z,t=Z.at?` (${qt(Z.at)})`:"",a=A?"the new menu you started":`the menu "${Ie(v.name||v.slug)}"`,m=e?`You left unsaved changes to ${a}${t}, but the saved menu has changed since. Restore your version anyway?`:`You left unsaved changes to ${a}${t}. Restore them?`;await E.confirm(m)&&g()&&(Me({...Z.menu,meta:{...v.meta||{},...Z.menu.meta||{}}}),Y())}}};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-cms",
3
- "version": "1.6.11",
3
+ "version": "1.6.13",
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",