domma-cms 1.14.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/admin/css/admin.css +1 -1
- package/admin/js/lib/admin-brand.js +1 -1
- package/admin/js/lib/google-analytics-panel.js +9 -0
- package/admin/js/lib/google-business-callback.js +1 -0
- package/admin/js/lib/google-business-import.js +3 -0
- package/admin/js/lib/google-business-panel.js +32 -0
- package/admin/js/lib/seo-defaults.js +1 -0
- package/admin/js/lib/sidebar-renderer.js +5 -5
- package/admin/js/lib/site-search-section.js +1 -0
- package/admin/js/lib/site-settings-arrange.js +3 -3
- package/admin/js/lib/topbar-account.js +1 -1
- package/admin/js/lib/topbar-cache.js +6 -0
- package/admin/js/lib/topbar-notifications.js +4 -4
- package/admin/js/templates/docs/usage-site-settings.html +20 -7
- package/admin/js/templates/menu-editor.html +1 -1
- package/admin/js/templates/settings.html +104 -63
- package/admin/js/views/analytics.js +8 -6
- package/admin/js/views/search.js +2 -2
- package/admin/js/views/seo.js +50 -42
- package/admin/js/views/settings.js +3 -3
- package/docs/DommaFeatures.md +3 -2
- package/docs/configuration.md +53 -8
- package/docs/google-business-profile.md +44 -0
- package/docs/screenshots/google-business-profile-review.png +0 -0
- package/docs/screenshots/notifications-cache.png +0 -0
- package/docs/screenshots/site-settings-search.png +0 -0
- package/package.json +1 -1
- package/public/css/motion.css +1 -0
- package/public/js/motion-control.mjs +1 -0
- package/public/js/site.js +1 -1
- package/server/routes/api/analytics.js +19 -0
- package/server/routes/api/google-business.js +64 -0
- package/server/routes/api/seo.js +14 -3
- package/server/routes/api/settings.js +23 -2
- package/server/services/adminBrand.js +1 -0
- package/server/services/googleBusiness.js +225 -0
- package/server/services/motionSettings.js +107 -0
- package/server/services/renderer.js +36 -6
- package/server/templates/page.html +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{api as g,getUser as w,isAuthenticated as
|
|
2
|
-
<summary${v}>${
|
|
1
|
+
import{api as g,getUser as w,isAuthenticated as ke}from"../api.js";import{createRequestQueue as qe}from"./background-requests.js";import{requestSidebarCounts as Ce}from"./sidebar-counts.js";import{beginSidebarCounts as Ae,publishSidebarCount as Fe}from"./sidebar-count-cache.js";import{isPathAllowed as Le}from"./admin-scope.js";import{cleanNotify as Ee,noticeFromCount as je}from"./sidebar-notify.js";import{colourToCss as te,applyFloat as Be,applyHighlight as Oe,itemHighlightAttrs as Me}from"/public/js/menu-decor.mjs?v=20261014-menu-entrance";import{placeTools as Te,ensureLockedTools as Ne,managePluginsItem as He,stripItemByUrl as Re,insertFoldersBeforeSystem as Ie,pruneEmptyToolsFolder as Pe,sidebarStateKey as ne,canAny as W,filterByPermission as _e,MANAGE_PLUGINS_URL as se}from"./sidebar-grouping.js";import{arrangeButtonMarkup as Ue,canArrange as ze,startArrange as We}from"./sidebar-arrange.js";import{collapseButtonMarkup as De,wireSidebarSize as Ge,COLLAPSED_WIDTH as Ke}from"./sidebar-size.js";import{FREQUENT_MAX as oe,addFrequent as Ve,frequentFolder as Xe,moveFrequent as ie,readFrequent as K,readFrequentHidden as Ze,removeFrequent as Je,saveFrequent as Ye,syncFrequent as Qe,writeFrequent as et,writeFrequentHidden as ae}from"./sidebar-frequent.js";import{hotkeyFor as re,hotkeyTitle as tt,setHotkeyTree as nt}from"./hotkeys-ui.js";import{hotkeyLabel as st}from"./hotkeys.js";function m(e){return String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}const ot=[{text:"Dashboard",url:"#/",icon:"home"},{text:"Site menus",url:"#/menus",icon:"menu",permission:"menus"}];async function it(e){if(!W(e,"projects"))return[];let n=[];try{n=await g.projects.listWithCounts()}catch{return[]}if(!Array.isArray(n))return[];const t=[{key:"pages",text:"Pages",icon:"file-text",path:"/pages"},{key:"collections",text:"Collections",icon:"database",path:"/collections"},{key:"forms",text:"Forms",icon:"layout",path:"/forms"},{key:"actions",text:"Actions",icon:"zap",path:"/actions"},{key:"menus",text:"Site menus",icon:"menu",path:"/menus"},{key:"blocks",text:"Blocks",icon:"box",path:"/blocks"},{key:"components",text:"Components",icon:"component",path:"/components"},{key:"views",text:"Views",icon:"eye",path:"/views"},{key:"roles",text:"Roles",icon:"shield",path:"/roles"},{key:"users",text:"Users",icon:"users",path:"/users"},{key:"apis",text:"APIs",icon:"code",path:"/apis"}];return n.map(o=>{const r="#/projects/"+encodeURIComponent(o.slug),u=o.counts||{},i=[{text:"Overview",url:r,icon:o.icon||"folder"}];for(const d of t){const l=Number(u[d.key])||0;l<=0||i.push({text:`${d.text} (${l})`,url:r+d.path,icon:d.icon})}i.push({text:"Settings",url:r+"/settings",icon:"settings"});const a=o.enabled===!1;return{text:o.name||o.slug,key:`project.${o.slug}`,icon:o.icon||"folder",items:i,...a&&{colour:"neutral",badge:{text:"off"}}}})}function at(e,n){for(const t of e)if(!t.hidden&&(t.key==="projects"||!t.key&&(t.text||"").toLowerCase()==="projects")){t.items=Array.isArray(t.items)?t.items.slice():[],t.items.push(...n);return}}function le(e,n){const t=de.includes(n.tone)?n.tone:"info",o=n.label||String(n.count);n.details?R.set(e,n.details):R.delete(e);const s=!n.inventory&&j.some(r=>r.url===e&&!r.inventory)?" data-live":"";return`<span class="sidebar-badge sidebar-badge--${t}" data-count-for="${m(e)}"${s} data-badge-label="${m(o)}" aria-label="${m(o)}">${m(String(n.count))}</span>`}function ce(e){for(const n of e.querySelectorAll("details")){const t=n.querySelector(":scope > summary");if(!t)continue;let o=0,s=!1;const r=[];for(const l of n.querySelectorAll(":scope > .sidebar-children .sidebar-badge[data-live]")){const f=Number(l.textContent)||0;f&&(o+=f,s||(s=l.classList.contains("sidebar-badge--danger")),r.push({text:l.closest("a")?.querySelector(".sidebar-text")?.textContent.trim()??"",meta:l.dataset.badgeLabel,tone:l.classList.contains("sidebar-badge--danger")?"danger":void 0}))}const u=`folder:${n.dataset.stateKey||t.textContent.trim()}`;let i=t.querySelector(":scope > .sidebar-badge--sum");if(!o){i?.remove(),R.delete(u);continue}i||(i=document.createElement("span"),t.querySelector(".sidebar-text")?.after(i));const a=t.querySelector(".sidebar-text")?.textContent.trim()||"This folder",d=r.map(l=>`${l.text}: ${l.meta}`).join(" \xB7 ");i.className=`sidebar-badge sidebar-badge--sum sidebar-badge--${s?"danger":"info"}`,i.dataset.countFor=u,i.dataset.badgeLabel=d,i.setAttribute("aria-label",d),i.textContent=String(o),R.set(u,{title:a,items:r})}}const R=new Map;function rt(e,n){if(!e.url||!n)return"";const t=n[e.url];return!t||!(t.count>0)&&!t.showZero?"":le(e.url,t)}const de=["info","attention","danger","live"],lt={"#/pages":["page","pages"],"#/media":["file in the media library","files in the media library"],"#/menus":["menu","menus"],"#/context-menus":["context menu","context menus"],"#/theme":["element override in the theme","element overrides in the theme"],"#/collections":["collection","collections"],"#/forms":["form","forms"],"#/views":["view","views"],"#/actions":["action","actions"],"#/blocks":["block","blocks"],"#/components":["component","components"],"#/users":["user","users"],"#/plugins":["plugin installed","plugins installed"],"#/system/notifications":["unread notification","unread notifications"]};function D(e,{fallbackLabel:n="",tone:t="info"}={}){const o=Number(typeof e=="object"&&e!==null?e.count:e);if(!Number.isFinite(o)||o<0)return null;const s=typeof e?.label=="string"&&e.label||(n?`${o} - ${n}`:String(o)),r={count:Math.round(o),label:s,tone:de.includes(e?.tone)?e.tone:t};e?.details&&typeof e.details=="object"&&(r.details=e.details),e?.inventory===!0&&(r.inventory=!0),e?.showZero===!0&&(r.showZero=!0);const u=Ee(e?.notify);return u&&(r.notify=u),r}function ue(e,n,t=null,{inFrequent:o=!1}={}){if(e&&e.type==="separator")return'<hr class="sidebar-divider">';if(e&&e.type==="theme-switcher")return"";if(e&&e.type==="spacer"){const b=["xs","sm","md","lg","xl","flex"].includes(e.size)?e.size:"md";return`<div class="sidebar-spacer sidebar-spacer--${m(b)}" aria-hidden="true"></div>`}const s=String(e.text||"?").trim().charAt(0).toUpperCase()||"?",r=e.icon?`<span data-icon="${m(e.icon)}"></span>`:`<span class="sidebar-initial" aria-hidden="true">${m(s)}</span>`,u=m(e.text||""),i=e.url?m(e.url):"",a=Array.isArray(e.items)&&e.items.length>0,d=e.colour?te(e.colour):"",l=Me(e),f=[l.hover&&` data-menu-item-hover="${m(l.hover)}"`,l.active&&` data-menu-item-active="${m(l.active)}"`].filter(Boolean).join(""),h=[d&&`color:${m(d)}`,l.accent&&`--dm-menu-highlight:${m(l.accent)}`].filter(Boolean).join(";"),v=h?` style="${h}"`:"",k=e.url?tt(e.url,e.text||"",{inFrequent:o}):"",q=k?` title="${m(k)}"`:"";let F="";if(e.badge&&e.badge.text!=null&&e.badge.text!==""){const b=e.badge.variant?te(e.badge.variant):"";F=`<span class="dm-menu-badge"${b?` style="background:${m(b)};color:var(--dm-text-inverse,#fff)"`:""}>${m(String(e.badge.text))}</span>`}if(e.frequent&&e.empty){const b=ne(e,t),A=S.get(b)!==!1;return`<details data-state-key="${m(b)}"${A?" open":""} data-frequent data-frequent-empty>
|
|
2
|
+
<summary${v}>${r} <span class="sidebar-text">${u}</span></summary>
|
|
3
3
|
<div class="sidebar-children"><p class="sidebar-frequent-empty">Right-click any Tool and choose <strong>Add to Frequent</strong> to keep it here.</p></div>
|
|
4
|
-
</details>`}if(
|
|
5
|
-
<summary${v}${q}>${
|
|
4
|
+
</details>`}if(a){const b=ne(e,t),A=S.get(b)!==!1,B=e.items.map(I=>ue(I,n,b,{inFrequent:!!e.frequent})).join(""),L=e.frequent?' data-frequent title="Your Frequent Tools - right-click any Tool to add or remove it"':"";return`<details data-state-key="${m(b)}"${A?" open":""}${L}>
|
|
5
|
+
<summary${v}${q}>${r} <span class="sidebar-text">${u}</span>${F}</summary>
|
|
6
6
|
<div class="sidebar-children">${B}</div>
|
|
7
|
-
</details>`}return`<a href="${i}" class="sidebar-link" data-url="${i}"${v}${f}${q}>${a} <span class="sidebar-text">${u}</span>${rt(e,n)}${F}</a>`}async function lt(e=[],n=[],t=null){const o=y=>y.then(C=>Array.isArray(C)?C.length:Array.isArray(C?.entries)?C.entries.length:0).catch(()=>0),s=fe(e,t),a=new Set(s.map(y=>y.url)),u={pages:"#/pages",media:"#/media",menus:"#/menus","context-menus":"#/context-menus",theme:"#/theme",collections:"#/collections",forms:"#/forms",views:"#/views",actions:"#/actions",blocks:"#/blocks",components:"#/components",users:"#/users",plugins:"#/plugins",notifications:"#/system/notifications"},i=(y,C)=>W(n,y)&&!a.has(u[y])?C():Promise.resolve(0),[r,d,l,f,h,v,k,q,F,b,A,B,j,I]=await Promise.all([i("pages",()=>o(g.pages.list())),i("media",()=>o(g.media.list())),i("menus",()=>o(g.menus.list())),i("context-menus",()=>o(g.contextMenus.list())),i("theme",()=>g.theme.get().then(y=>(y?.overrides||[]).length).catch(()=>0)),i("collections",()=>o(g.collections.list())),i("forms",()=>o(g.forms.list())),i("views",()=>o(g.views.list())),i("actions",()=>o(g.actions.list())),i("blocks",()=>o(g.blocks.list())),i("components",()=>o(g.components.list())),i("users",()=>o(g.users.list())),i("plugins",()=>o(g.plugins.list())),i("notifications",()=>g.system?.notifications?.unreadCount?.().then(y=>y?.count??0).catch(()=>0)??Promise.resolve(0))]),P=await pe(s),U={"#/pages":r,"#/media":d,"#/menus":l,"#/context-menus":f,"#/theme":h,"#/collections":v,"#/forms":k,"#/views":q,"#/actions":F,"#/blocks":b,"#/components":A,"#/users":B,"#/plugins":j,"#/system/notifications":I},z={};for(const[y,C]of Object.entries(U)){if(a.has(y))continue;const[Y,G]=at[y]??["item","items"],O=D(C);O&&(z[y]={...O,label:`${O.count} ${O.count===1?Y:G}`})}return{...z,...P}}let L=[];const ct="dm:sidebar-count",dt=6e4,ut=ke(4);function fe(e=[],n=null){const t=[],o=s=>{if(s&&!(n&&typeof s.url=="string"&&s.url.startsWith("#/")&&!Fe(n,s.url.slice(1)))){s.url&&typeof s.countUrl=="string"&&s.countUrl&&t.push({url:s.url,countUrl:s.countUrl,text:s.text||"",countLabel:s.countLabel||"",inventory:s.inventory===!0});for(const a of[...s.children||[],...s.items||[]])o(a)}};for(const s of e??[])o(s?.item??s);return t}async function me(e,{priority:n=!1}={}){try{const t=new Date,o=`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`,s=e.countUrl+(e.countUrl.includes("?")?"&":"?")+`today=${o}`,a=w()?.id,u=await ut(()=>!Se()||w()?.id!==a?null:H.get(s,{priority:"low"}),{key:JSON.stringify([a,s]),priority:n});return D(u,{fallbackLabel:e.countLabel||e.text})}catch{return null}}function V(e,n,t){Ae(n,t),t?.notify&&Ee(w()?.id??null,n,t.notify);const o=[...e.querySelectorAll("a.sidebar-link")].filter(s=>s.dataset.url===n);if(o.length){for(const s of o){const a=s.querySelector(".sidebar-badge[data-count-for]");if(!t||!(t.count>0)&&!t.showZero)a?.remove();else{const u=document.createRange();u.selectNodeContents(s);const i=u.createContextualFragment(le(n,t)).firstElementChild;a?a.replaceWith(i):s.querySelector(".sidebar-text")?.after(i)}}X.set(n,t),ce(e)}}const X=new Map;async function pe(e){const n=await qe(e,{legacy:async t=>{const o=await Promise.all(t.map(async s=>[s.url,await me(s)]));return Object.fromEntries(o)}});return Object.fromEntries(e.map(t=>[t.url,n[t.url]==null?null:D(n[t.url],{fallbackLabel:t.countLabel||t.text})]))}async function Z(e,n=null){const t=w()?.id,o=L;try{const s=n?{[n]:await me(L.find(a=>a.url===n),{priority:!0})}:await pe(L);if(w()?.id!==t||L!==o)return;for(const[a,u]of Object.entries(s)){const i=X.get(a);JSON.stringify(i??null)!==JSON.stringify(u)&&V(e,a,u)}}catch{}}function ft(e){e.dataset.liveCounts||(e.dataset.liveCounts="1",window.addEventListener(ct,n=>{const t=n.detail||{};if(t.url)if(t.count!==void 0){const o=L.find(s=>s.url===t.url);V(e,t.url,D(t,{fallbackLabel:o?.countLabel||o?.text||""}))}else Z(e,t.url)}),setInterval(()=>{!document.hidden&&document.body.contains(e)&&Z(e)},dt),document.addEventListener("visibilitychange",()=>{document.hidden||Z(e)}),mt(e))}function he(e,n,t){const o=(u,i,r)=>{const d=document.createElement(u);return i&&(d.className=i),r!=null&&(d.textContent=String(r)),d};if(!t){e.textContent=n;return}e.appendChild(o("strong","dm-badge-pop-title",t.title||n)),(t.rows?.length||!t.items?.length)&&e.appendChild(o("p","dm-badge-pop-sub",n));const s=Array.isArray(t.rows)?t.rows.slice(0,6):[];if(s.length){const u=o("dl","dm-badge-pop-rows");for(const i of s)u.appendChild(o("dt",null,i.label)),u.appendChild(o("dd",i.tone==="danger"?"is-danger":null,i.value));e.appendChild(u)}const a=Array.isArray(t.items)?t.items.slice(0,6):[];if(a.length){const u=o("ul","dm-badge-pop-items");for(const i of a){const r=o("li",i.tone==="danger"?"is-danger":null);r.appendChild(o("span","dm-badge-pop-text",i.text)),i.meta&&r.appendChild(o("span","dm-badge-pop-meta",i.meta)),u.appendChild(r)}e.appendChild(u)}else t.empty&&e.appendChild(o("p","dm-badge-pop-empty",t.empty))}function mt(e){let n=null,t=null;const o=()=>e.classList.contains("is-collapsed"),s=()=>{clearTimeout(t),n?.remove(),n=null},a=(d,l)=>{s(),t=setTimeout(()=>{n=document.createElement("div"),n.className="dm-help-pop dm-badge-pop",n.setAttribute("role","tooltip"),l(n),document.body.appendChild(n);const f=d.getBoundingClientRect(),h=n.getBoundingClientRect(),v=Math.min(f.right+10,innerWidth-h.width-8);n.style.left=`${Math.max(8,v)}px`,n.style.top=`${Math.max(8,Math.min(f.top+f.height/2-h.height/2,innerHeight-h.height-8))}px`},150)},u=d=>{const l=d?.dataset.badgeLabel;if(!l)return s();a(d,f=>he(f,l,R.get(d.dataset.countFor)))},i=d=>{const l=d.querySelector(":scope > .sidebar-text")?.textContent.trim()||"",f=d.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");a(d,h=>{const v=f&&R.get(f.dataset.countFor);if(v)return he(h,f.dataset.badgeLabel,{...v,title:v.title||l});if(!f){h.textContent=l;return}const k=document.createElement("strong");k.className="dm-badge-pop-title",k.textContent=l;const q=document.createElement("p");q.className="dm-badge-pop-sub",q.textContent=f.dataset.badgeLabel,h.append(k,q)})},r=d=>{const l=d.closest?.("a.sidebar-link, summary");return l&&e.contains(l)?l:null};e.addEventListener("mouseover",d=>{if(o()){const f=r(d.target);f&&!f.contains(d.relatedTarget)&&i(f);return}const l=d.target.closest?.(".sidebar-badge[data-badge-label]");l&&u(l)}),e.addEventListener("mouseout",d=>{if(o()){const l=r(d.target);l&&!l.contains(d.relatedTarget)&&s();return}d.target.closest?.(".sidebar-badge[data-badge-label]")&&s()}),e.addEventListener("focusin",d=>{const l=d.target.closest?.("a, summary");if(o()&&l&&e.contains(l)&&l.querySelector(":scope > .sidebar-text"))return i(l);const f=l?.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");f?u(f):s()}),e.addEventListener("focusout",s),e.addEventListener("click",s),window.addEventListener("scroll",s,!0),window.addEventListener("hashchange",s)}async function pt(e=[],n=null){if(n?.title)return n;if(!W(e,"settings"))return{title:"Domma CMS"};try{const t=await g.settings.get();return{...t?.adminBrand,title:t?.adminBrand?.title||t?.title||"Domma CMS"}}catch{return{title:"Domma CMS"}}}export async function renderAdminSidebar({mount:e,permissions:n,adminScope:t=null,brand:o=null}){const s=$(e).get(0);if(!s)return;const a=Ye(g,w()).catch(()=>{}),u=Promise.resolve().then(()=>H.get("/api/sidebar/registered-items")).catch(()=>[]),i=ot(n);let r,d=null,l=null,f=null,h=null,v=null;try{const c=await g.get("/sidebar/menu");r=Array.isArray(c?.items)?c.items:null,d=c?.variant||null,l=c?.position||null,f=c?.style||null,h=c?.float||null,v=c?.highlight||null}catch{r=null}(!r||!r.some(c=>c&&(!c.hidden||c.restricted)))&&(console.warn("[admin-sidebar] No admin-sidebar menu found; using fallback tree"),r=st.slice());let k=[];try{k=await u||[]}catch{}r=He(r,se),r=Me(r,k.filter(c=>(c?.item??c)?.url!==se)),r=Te(r),r=Re(r,[Ne()]);const q=await i;q.length&&it(r,q),r=Pe(r,n,t),r=Ie(r),await a;const F=Ve(r,K(w()),{hidden:Xe(w())});F&&(r=[F,...r]),tt(r);const b=k.filter(c=>W(n,c?.item?.permission));L=fe(b,t);const A=Ce({user:w(),permissions:n,adminScope:t,registered:b,tree:r,actions:{linkItems:ye,folderItems:ve},fetcher:()=>lt(b,n,t)}),B=A.counts,j=await pt(n,o),I=d?` dm-admin-sidebar--${m(d)}`:"";function P(c,x="px"){if(c==null)return c;const p=String(c).trim();return/^\d+(\.\d+)?$/.test(p)?`${p}${x}`:p}let U="";if(f){const c=[];f.fontFamily&&c.push(`font-family: ${f.fontFamily}, sans-serif`),f.fontSize&&c.push(`font-size: ${P(f.fontSize)}`),f.fontWeight&&c.push(`font-weight: ${f.fontWeight}`),f.letterSpacing&&c.push(`letter-spacing: ${P(f.letterSpacing,"em")}`);const x=[];if(c.length&&x.push(`#admin-sidebar .dm-admin-sidebar, #admin-sidebar .dm-admin-sidebar .sidebar-link, #admin-sidebar .dm-admin-sidebar summary, #admin-sidebar .dm-admin-sidebar .sidebar-text { ${c.join("; ")} }`),f.iconSize){const p=P(f.iconSize);x.push(`#admin-sidebar .dm-admin-sidebar [data-icon], #admin-sidebar .dm-admin-sidebar [data-icon] svg { width: ${p} !important; height: ${p} !important; }`)}x.length&&(U=`<style data-admin-sidebar-style>${x.join(" ")}</style>`)}const z=Ue(n)?_e():"",y=String(j.logo||"").trim(),C=/^(?:\/(?!\/)|https?:\/\/)/i.test(y)?y:"",G=`<div class="dm-admin-sidebar-header">${C?`<img src="${m(C)}" alt="" style="width:28px;height:28px;object-fit:contain;flex-shrink:0;">`:`<span data-icon="${m(j.icon||"layout")}"></span>`}<span class="dm-admin-sidebar-title">${m(j.title)}</span>${z}${We()}</div>`,O=r.some(c=>c&&c.type==="spacer"&&c.size==="flex")?" dm-admin-sidebar--has-flex-spacer":"",xe=`${U}<nav class="dm-admin-sidebar${I}${O}">${G}${r.map(c=>ue(c,B)).join("")}</nav>`,Q=document.createRange();Q.selectNodeContents(s);const $e=Q.createContextualFragment(xe);s.replaceChildren($e),Domma.icons.scan(s),s.querySelector(".dm-admin-sidebar-arrange")?.addEventListener("click",()=>ze(s));for(const c of L)X.set(c.url,B[c.url]??null);ce(s),ft(s),A.refresh.then(c=>{if(!(!A.isActive()||!s.isConnected))for(const[x,p]of Object.entries(c))V(s,x,p)}),s.removeAttribute("data-menu-hover"),s.removeAttribute("data-menu-active"),s.style.removeProperty("--dm-menu-highlight"),Be(s,{highlight:v});const T=document.querySelector(".dashboard-wrapper");["position","top","bottom","left","right","zIndex","maxHeight","transform"].forEach(c=>{s.style[c]=""}),s.classList.remove("dm-menu-floating"),T&&(T.style.paddingLeft="",T.style.paddingRight=""),s.__dmGutter=()=>{if(l!=="floating"||!T)return;const c=h&&h.anchor||"TL",x=Number.isFinite(Number(h?.offsetX))?Number(h.offsetX):16,p=s.classList.contains("is-collapsed")?Ge:parseInt(s.style.width,10)||s.getBoundingClientRect().width||260,N=Math.round(p+x*2)+"px";c==="R"||c==="TR"||c==="BR"?T.style.paddingRight=N:T.style.paddingLeft=N},l==="floating"&&je(s,{position:"floating",float:h||{}}),s.querySelectorAll("details").forEach(c=>{c.addEventListener("toggle",function(){const x=this.getAttribute("data-state-key");x&&S.set(x,this.open);const p=this.querySelector(":scope > .sidebar-children");if(p)if(this.open){const N=p.scrollHeight;p.style.maxHeight="0",requestAnimationFrame(()=>{p.style.maxHeight=N+"px",p.addEventListener("transitionend",function we(){p.style.maxHeight="none",p.removeEventListener("transitionend",we)})})}else{const N=p.scrollHeight;p.style.maxHeight=N+"px",requestAnimationFrame(()=>{p.style.maxHeight="0"})}})}),De(s),gt(s),s.dataset.routeWired||(s.dataset.routeWired="1",M.subscribe("router:afterChange",()=>s.__dmSetActive?.()));function ee(){const c=/^#\/(dashboards|my-dashboard)\b/.test(location.hash)?"#/":location.hash||"#/";$(s).find(".sidebar-link").removeClass("active"),$(s).find(`.sidebar-link[data-url="${c}"]`).addClass("active")}ee(),s.__dmSetActive=ee}function ge(){try{document.dispatchEvent(new CustomEvent("dm:sidebar-rebuild"))}catch{}}function J(e){return`${location.origin}${location.pathname}${e}`}function _(e,n){const t=w(),o=e(K(t));if(!Qe(t,o)){E.toast("Frequent could not be saved - this browser is blocking storage.",{type:"error"});return}o.length&&re(t,!1),be(t),n&&E.toast(n,{type:"success",duration:2500}),ge()}function be(e){Je(g,e).catch(()=>E.toast("Frequent is saved in this browser only - the server could not keep it.",{type:"warning"}))}function ht(e){if(!re(w(),e)){E.toast("Frequent could not be saved - this browser is blocking storage.",{type:"error"});return}be(w()),E.toast(e?"Frequent hidden. Add a Tool to it from its right-click menu to bring it back.":"Frequent shown.",{type:"success",duration:3500}),ge()}function ye(e){const n=e.dataset.url;if(!n)return[];const t=e.querySelector(".sidebar-text")?.textContent.trim()||n,o=K(w()),s=o.includes(n),a=!!e.closest("[data-frequent]"),u=o.indexOf(n),i=!s&&o.length>=oe;return[{header:t},{label:"Open",icon:"arrow-right",...ae(n)&&{shortcut:nt(ae(n))},action:()=>{location.hash=n}},{label:"Open in a new tab",icon:"external-link",action:()=>window.open(J(n),"_blank","noopener")},{type:"divider"},s?{label:"Remove from Frequent",icon:"star",action:()=>_(r=>Ze(r,n),`${t} removed from Frequent.`)}:{label:i?`Frequent is full (${oe})`:"Add to Frequent",icon:"star",disabled:i,action:()=>_(r=>Ke(r,n),`${t} added to Frequent.`)},...a?[{label:"Move up",icon:"arrow-up",disabled:u<=0,action:()=>_(r=>ie(r,n,-1))},{label:"Move down",icon:"arrow-down",disabled:u<0||u>=o.length-1,action:()=>_(r=>ie(r,n,1))}]:[],{type:"divider"},{label:"Copy link",icon:"link",action:async()=>{try{await navigator.clipboard.writeText(J(n)),E.toast("Link copied.",{type:"success",duration:2e3})}catch{E.toast(J(n),{type:"info",duration:8e3})}}}]}function ve(e,n,t={}){const o=e.closest("details, [data-frequent]"),s=t.isOpen||(()=>!!o?.open),a=t.setOpen||(r=>{o&&(o.open=r)}),u=e.querySelector(".sidebar-text")?.textContent.trim()||"Folder",i=t.setAll||(r=>n.querySelectorAll("details").forEach(d=>{d.open!==r&&(d.open=r)}));return[{header:u},{label:s()?"Close":"Open",icon:s()?"chevron-up":"chevron-down",action:()=>a(!s())},...t.singleBranch?[]:[{label:"Open every folder",icon:"chevrons-down",action:()=>i(!0)}],{label:t.singleBranch?"Close all menus":"Close every folder",icon:"chevrons-up",action:()=>i(!1)},...o?.hasAttribute("data-frequent-empty")?[{type:"divider"},{label:"Hide Frequent until I add a Tool",icon:"eye-off",action:()=>ht(!0)}]:o?.hasAttribute("data-frequent")?[{type:"divider"},{label:"Clear Frequent",icon:"x",action:()=>_(()=>[],"Frequent cleared.")}]:[]]}function gt(e){e.dataset.ctxWired||typeof E>"u"||!E.contextMenu||(e.dataset.ctxWired="1",E.contextMenu(e,{match:"a.sidebar-link, details > summary",inherit:!1,density:"compact",items:n=>n.matches("summary")?ve(n,e):ye(n)}))}
|
|
7
|
+
</details>`}return`<a href="${i}" class="sidebar-link" data-url="${i}"${v}${f}${q}>${r} <span class="sidebar-text">${u}</span>${rt(e,n)}${F}</a>`}async function ct(e=[],n=[],t=null){const o=y=>y.then(C=>Array.isArray(C)?C.length:Array.isArray(C?.entries)?C.entries.length:0).catch(()=>0),s=fe(e,t),r=new Set(s.map(y=>y.url)),u={pages:"#/pages",media:"#/media",menus:"#/menus","context-menus":"#/context-menus",theme:"#/theme",collections:"#/collections",forms:"#/forms",views:"#/views",actions:"#/actions",blocks:"#/blocks",components:"#/components",users:"#/users",plugins:"#/plugins",notifications:"#/system/notifications"},i=(y,C)=>W(n,y)&&!r.has(u[y])?C():Promise.resolve(0),[a,d,l,f,h,v,k,q,F,b,A,B,L,I]=await Promise.all([i("pages",()=>o(g.pages.list())),i("media",()=>o(g.media.list())),i("menus",()=>o(g.menus.list())),i("context-menus",()=>o(g.contextMenus.list())),i("theme",()=>g.theme.get().then(y=>(y?.overrides||[]).length).catch(()=>0)),i("collections",()=>o(g.collections.list())),i("forms",()=>o(g.forms.list())),i("views",()=>o(g.views.list())),i("actions",()=>o(g.actions.list())),i("blocks",()=>o(g.blocks.list())),i("components",()=>o(g.components.list())),i("users",()=>o(g.users.list())),i("plugins",()=>o(g.plugins.list())),i("notifications",()=>g.system?.notifications?.unreadCount?.().then(y=>y?.count??0).catch(()=>0)??Promise.resolve(0))]),P=await pe(s),U={"#/pages":a,"#/media":d,"#/menus":l,"#/context-menus":f,"#/theme":h,"#/collections":v,"#/forms":k,"#/views":q,"#/actions":F,"#/blocks":b,"#/components":A,"#/users":B,"#/plugins":L,"#/system/notifications":I},z={};for(const[y,C]of Object.entries(U)){if(r.has(y))continue;const[G,Y]=lt[y]??["item","items"],O=D(C);O&&(z[y]={...O,label:`${O.count} ${O.count===1?G:Y}`})}return{...z,...P}}let j=[];const dt="dm:sidebar-count",ut=6e4,ft=qe(4);function fe(e=[],n=null){const t=[],o=s=>{if(s&&!(n&&typeof s.url=="string"&&s.url.startsWith("#/")&&!Le(n,s.url.slice(1)))){s.url&&typeof s.countUrl=="string"&&s.countUrl&&t.push({url:s.url,countUrl:s.countUrl,text:s.text||"",countLabel:s.countLabel||"",inventory:s.inventory===!0});for(const r of[...s.children||[],...s.items||[]])o(r)}};for(const s of e??[])o(s?.item??s);return t}async function me(e,{priority:n=!1}={}){try{const t=new Date,o=`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`,s=e.countUrl+(e.countUrl.includes("?")?"&":"?")+`today=${o}`,r=w()?.id,u=await ft(()=>!ke()||w()?.id!==r?null:H.get(s,{priority:"low"}),{key:JSON.stringify([r,s]),priority:n});return D(u,{fallbackLabel:e.countLabel||e.text})}catch{return null}}function V(e,n,t){Fe(n,t),t?.notify&&je(w()?.id??null,n,t.notify);const o=[...e.querySelectorAll("a.sidebar-link")].filter(s=>s.dataset.url===n);if(o.length){for(const s of o){const r=s.querySelector(".sidebar-badge[data-count-for]");if(!t||!(t.count>0)&&!t.showZero)r?.remove();else{const u=document.createRange();u.selectNodeContents(s);const i=u.createContextualFragment(le(n,t)).firstElementChild;r?r.replaceWith(i):s.querySelector(".sidebar-text")?.after(i)}}X.set(n,t),ce(e)}}const X=new Map;async function pe(e){const n=await Ce(e,{legacy:async t=>{const o=await Promise.all(t.map(async s=>[s.url,await me(s)]));return Object.fromEntries(o)}});return Object.fromEntries(e.map(t=>[t.url,n[t.url]==null?null:D(n[t.url],{fallbackLabel:t.countLabel||t.text})]))}async function Z(e,n=null){const t=w()?.id,o=j;try{const s=n?{[n]:await me(j.find(r=>r.url===n),{priority:!0})}:await pe(j);if(w()?.id!==t||j!==o)return;for(const[r,u]of Object.entries(s)){const i=X.get(r);JSON.stringify(i??null)!==JSON.stringify(u)&&V(e,r,u)}}catch{}}function mt(e){e.dataset.liveCounts||(e.dataset.liveCounts="1",window.addEventListener(dt,n=>{const t=n.detail||{};if(t.url)if(t.count!==void 0){const o=j.find(s=>s.url===t.url);V(e,t.url,D(t,{fallbackLabel:o?.countLabel||o?.text||""}))}else Z(e,t.url)}),setInterval(()=>{!document.hidden&&document.body.contains(e)&&Z(e)},ut),document.addEventListener("visibilitychange",()=>{document.hidden||Z(e)}),pt(e))}function he(e,n,t){const o=(u,i,a)=>{const d=document.createElement(u);return i&&(d.className=i),a!=null&&(d.textContent=String(a)),d};if(!t){e.textContent=n;return}e.appendChild(o("strong","dm-badge-pop-title",t.title||n)),(t.rows?.length||!t.items?.length)&&e.appendChild(o("p","dm-badge-pop-sub",n));const s=Array.isArray(t.rows)?t.rows.slice(0,6):[];if(s.length){const u=o("dl","dm-badge-pop-rows");for(const i of s)u.appendChild(o("dt",null,i.label)),u.appendChild(o("dd",i.tone==="danger"?"is-danger":null,i.value));e.appendChild(u)}const r=Array.isArray(t.items)?t.items.slice(0,6):[];if(r.length){const u=o("ul","dm-badge-pop-items");for(const i of r){const a=o("li",i.tone==="danger"?"is-danger":null);a.appendChild(o("span","dm-badge-pop-text",i.text)),i.meta&&a.appendChild(o("span","dm-badge-pop-meta",i.meta)),u.appendChild(a)}e.appendChild(u)}else t.empty&&e.appendChild(o("p","dm-badge-pop-empty",t.empty))}function pt(e){let n=null,t=null;const o=()=>e.classList.contains("is-collapsed"),s=()=>{clearTimeout(t),n?.remove(),n=null},r=(d,l)=>{s(),t=setTimeout(()=>{n=document.createElement("div"),n.className="dm-help-pop dm-badge-pop",n.setAttribute("role","tooltip"),l(n),document.body.appendChild(n);const f=d.getBoundingClientRect(),h=n.getBoundingClientRect(),v=Math.min(f.right+10,innerWidth-h.width-8);n.style.left=`${Math.max(8,v)}px`,n.style.top=`${Math.max(8,Math.min(f.top+f.height/2-h.height/2,innerHeight-h.height-8))}px`},150)},u=d=>{const l=d?.dataset.badgeLabel;if(!l)return s();r(d,f=>he(f,l,R.get(d.dataset.countFor)))},i=d=>{const l=d.querySelector(":scope > .sidebar-text")?.textContent.trim()||"",f=d.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");r(d,h=>{const v=f&&R.get(f.dataset.countFor);if(v)return he(h,f.dataset.badgeLabel,{...v,title:v.title||l});if(!f){h.textContent=l;return}const k=document.createElement("strong");k.className="dm-badge-pop-title",k.textContent=l;const q=document.createElement("p");q.className="dm-badge-pop-sub",q.textContent=f.dataset.badgeLabel,h.append(k,q)})},a=d=>{const l=d.closest?.("a.sidebar-link, summary");return l&&e.contains(l)?l:null};e.addEventListener("mouseover",d=>{if(o()){const f=a(d.target);f&&!f.contains(d.relatedTarget)&&i(f);return}const l=d.target.closest?.(".sidebar-badge[data-badge-label]");l&&u(l)}),e.addEventListener("mouseout",d=>{if(o()){const l=a(d.target);l&&!l.contains(d.relatedTarget)&&s();return}d.target.closest?.(".sidebar-badge[data-badge-label]")&&s()}),e.addEventListener("focusin",d=>{const l=d.target.closest?.("a, summary");if(o()&&l&&e.contains(l)&&l.querySelector(":scope > .sidebar-text"))return i(l);const f=l?.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");f?u(f):s()}),e.addEventListener("focusout",s),e.addEventListener("click",s),window.addEventListener("scroll",s,!0),window.addEventListener("hashchange",s)}async function ht(e=[],n=null){if(n?.title||n?.enabled===!1)return n;if(!W(e,"settings"))return{title:"Domma CMS"};try{const t=await g.settings.get();return{...t?.adminBrand,title:t?.adminBrand?.title||t?.title||"Domma CMS"}}catch{return{title:"Domma CMS"}}}export async function renderAdminSidebar({mount:e,permissions:n,adminScope:t=null,brand:o=null}){const s=$(e).get(0);if(!s)return;const r=Qe(g,w()).catch(()=>{}),u=Promise.resolve().then(()=>H.get("/api/sidebar/registered-items")).catch(()=>[]),i=it(n);let a,d=null,l=null,f=null,h=null,v=null;try{const c=await g.get("/sidebar/menu");a=Array.isArray(c?.items)?c.items:null,d=c?.variant||null,l=c?.position||null,f=c?.style||null,h=c?.float||null,v=c?.highlight||null}catch{a=null}(!a||!a.some(c=>c&&(!c.hidden||c.restricted)))&&(console.warn("[admin-sidebar] No admin-sidebar menu found; using fallback tree"),a=ot.slice());let k=[];try{k=await u||[]}catch{}a=Re(a,se),a=Te(a,k.filter(c=>(c?.item??c)?.url!==se)),a=Ne(a),a=Ie(a,[He()]);const q=await i;q.length&&at(a,q),a=_e(a,n,t),a=Pe(a),await r;const F=Xe(a,K(w()),{hidden:Ze(w())});F&&(a=[F,...a]),nt(a);const b=k.filter(c=>W(n,c?.item?.permission));j=fe(b,t);const A=Ae({user:w(),permissions:n,adminScope:t,registered:b,tree:a,actions:{linkItems:ye,folderItems:ve},fetcher:()=>ct(b,n,t)}),B=A.counts,L=await ht(n,o),I=d?` dm-admin-sidebar--${m(d)}`:"";function P(c,x="px"){if(c==null)return c;const p=String(c).trim();return/^\d+(\.\d+)?$/.test(p)?`${p}${x}`:p}let U="";if(f){const c=[];f.fontFamily&&c.push(`font-family: ${f.fontFamily}, sans-serif`),f.fontSize&&c.push(`font-size: ${P(f.fontSize)}`),f.fontWeight&&c.push(`font-weight: ${f.fontWeight}`),f.letterSpacing&&c.push(`letter-spacing: ${P(f.letterSpacing,"em")}`);const x=[];if(c.length&&x.push(`#admin-sidebar .dm-admin-sidebar, #admin-sidebar .dm-admin-sidebar .sidebar-link, #admin-sidebar .dm-admin-sidebar summary, #admin-sidebar .dm-admin-sidebar .sidebar-text { ${c.join("; ")} }`),f.iconSize){const p=P(f.iconSize);x.push(`#admin-sidebar .dm-admin-sidebar [data-icon], #admin-sidebar .dm-admin-sidebar [data-icon] svg { width: ${p} !important; height: ${p} !important; }`)}x.length&&(U=`<style data-admin-sidebar-style>${x.join(" ")}</style>`)}const z=ze(n)?Ue():"",y=String(L.logo||"").trim(),C=/^(?:\/(?!\/)|https?:\/\/)/i.test(y)?y:"",G=C?`<img src="${m(C)}" alt="" style="width:28px;height:28px;object-fit:contain;flex-shrink:0;">`:`<span data-icon="${m(L.icon||"layout")}"></span>`,O=`<div class="dm-admin-sidebar-header">${L.enabled===!1?"":`${G}<span class="dm-admin-sidebar-title">${m(L.title)}</span>`}${z}${De()}</div>`,xe=a.some(c=>c&&c.type==="spacer"&&c.size==="flex")?" dm-admin-sidebar--has-flex-spacer":"",$e=`${U}<nav class="dm-admin-sidebar${I}${xe}">${O}${a.map(c=>ue(c,B)).join("")}</nav>`,Q=document.createRange();Q.selectNodeContents(s);const we=Q.createContextualFragment($e);s.replaceChildren(we),Domma.icons.scan(s),s.querySelector(".dm-admin-sidebar-arrange")?.addEventListener("click",()=>We(s));for(const c of j)X.set(c.url,B[c.url]??null);ce(s),mt(s),A.refresh.then(c=>{if(!(!A.isActive()||!s.isConnected))for(const[x,p]of Object.entries(c))V(s,x,p)}),s.removeAttribute("data-menu-hover"),s.removeAttribute("data-menu-active"),s.style.removeProperty("--dm-menu-highlight"),Oe(s,{highlight:v});const T=document.querySelector(".dashboard-wrapper");["position","top","bottom","left","right","zIndex","maxHeight","transform"].forEach(c=>{s.style[c]=""}),s.classList.remove("dm-menu-floating"),T&&(T.style.paddingLeft="",T.style.paddingRight=""),s.__dmGutter=()=>{if(l!=="floating"||!T)return;const c=h&&h.anchor||"TL",x=Number.isFinite(Number(h?.offsetX))?Number(h.offsetX):16,p=s.classList.contains("is-collapsed")?Ke:parseInt(s.style.width,10)||s.getBoundingClientRect().width||260,N=Math.round(p+x*2)+"px";c==="R"||c==="TR"||c==="BR"?T.style.paddingRight=N:T.style.paddingLeft=N},l==="floating"&&Be(s,{position:"floating",float:h||{}}),s.querySelectorAll("details").forEach(c=>{c.addEventListener("toggle",function(){const x=this.getAttribute("data-state-key");x&&S.set(x,this.open);const p=this.querySelector(":scope > .sidebar-children");if(p)if(this.open){const N=p.scrollHeight;p.style.maxHeight="0",requestAnimationFrame(()=>{p.style.maxHeight=N+"px",p.addEventListener("transitionend",function Se(){p.style.maxHeight="none",p.removeEventListener("transitionend",Se)})})}else{const N=p.scrollHeight;p.style.maxHeight=N+"px",requestAnimationFrame(()=>{p.style.maxHeight="0"})}})}),Ge(s),bt(s),s.dataset.routeWired||(s.dataset.routeWired="1",M.subscribe("router:afterChange",()=>s.__dmSetActive?.()));function ee(){const c=/^#\/(dashboards|my-dashboard)\b/.test(location.hash)?"#/":location.hash||"#/";$(s).find(".sidebar-link").removeClass("active"),$(s).find(`.sidebar-link[data-url="${c}"]`).addClass("active")}ee(),s.__dmSetActive=ee}function ge(){try{document.dispatchEvent(new CustomEvent("dm:sidebar-rebuild"))}catch{}}function J(e){return`${location.origin}${location.pathname}${e}`}function _(e,n){const t=w(),o=e(K(t));if(!et(t,o)){E.toast("Frequent could not be saved - this browser is blocking storage.",{type:"error"});return}o.length&&ae(t,!1),be(t),n&&E.toast(n,{type:"success",duration:2500}),ge()}function be(e){Ye(g,e).catch(()=>E.toast("Frequent is saved in this browser only - the server could not keep it.",{type:"warning"}))}function gt(e){if(!ae(w(),e)){E.toast("Frequent could not be saved - this browser is blocking storage.",{type:"error"});return}be(w()),E.toast(e?"Frequent hidden. Add a Tool to it from its right-click menu to bring it back.":"Frequent shown.",{type:"success",duration:3500}),ge()}function ye(e){const n=e.dataset.url;if(!n)return[];const t=e.querySelector(".sidebar-text")?.textContent.trim()||n,o=K(w()),s=o.includes(n),r=!!e.closest("[data-frequent]"),u=o.indexOf(n),i=!s&&o.length>=oe;return[{header:t},{label:"Open",icon:"arrow-right",...re(n)&&{shortcut:st(re(n))},action:()=>{location.hash=n}},{label:"Open in a new tab",icon:"external-link",action:()=>window.open(J(n),"_blank","noopener")},{type:"divider"},s?{label:"Remove from Frequent",icon:"star",action:()=>_(a=>Je(a,n),`${t} removed from Frequent.`)}:{label:i?`Frequent is full (${oe})`:"Add to Frequent",icon:"star",disabled:i,action:()=>_(a=>Ve(a,n),`${t} added to Frequent.`)},...r?[{label:"Move up",icon:"arrow-up",disabled:u<=0,action:()=>_(a=>ie(a,n,-1))},{label:"Move down",icon:"arrow-down",disabled:u<0||u>=o.length-1,action:()=>_(a=>ie(a,n,1))}]:[],{type:"divider"},{label:"Copy link",icon:"link",action:async()=>{try{await navigator.clipboard.writeText(J(n)),E.toast("Link copied.",{type:"success",duration:2e3})}catch{E.toast(J(n),{type:"info",duration:8e3})}}}]}function ve(e,n,t={}){const o=e.closest("details, [data-frequent]"),s=t.isOpen||(()=>!!o?.open),r=t.setOpen||(a=>{o&&(o.open=a)}),u=e.querySelector(".sidebar-text")?.textContent.trim()||"Folder",i=t.setAll||(a=>n.querySelectorAll("details").forEach(d=>{d.open!==a&&(d.open=a)}));return[{header:u},{label:s()?"Close":"Open",icon:s()?"chevron-up":"chevron-down",action:()=>r(!s())},...t.singleBranch?[]:[{label:"Open every folder",icon:"chevrons-down",action:()=>i(!0)}],{label:t.singleBranch?"Close all menus":"Close every folder",icon:"chevrons-up",action:()=>i(!1)},...o?.hasAttribute("data-frequent-empty")?[{type:"divider"},{label:"Hide Frequent until I add a Tool",icon:"eye-off",action:()=>gt(!0)}]:o?.hasAttribute("data-frequent")?[{type:"divider"},{label:"Clear Frequent",icon:"x",action:()=>_(()=>[],"Frequent cleared.")}]:[]]}function bt(e){e.dataset.ctxWired||typeof E>"u"||!E.contextMenu||(e.dataset.ctxWired="1",E.contextMenu(e,{match:"a.sidebar-link, details > summary",inherit:!1,density:"compact",items:n=>n.matches("summary")?ve(n,e):ye(n)}))}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function siteSearchSection(a){const r=a.querySelector("[data-site-search-tab]"),n=a.querySelector("[data-site-search-panel]"),h=a.querySelector("[data-site-search-dirty]");let s=null,d=!1,i=!1;const f=[];function u(e){for(const t of e.querySelectorAll("button, a")){if(t.dataset.siteTooltip||t.classList.contains("dm-help-btn")||!t.querySelector("svg, [data-icon]"))continue;const o=t.getAttribute("aria-label")||t.title||t.textContent.trim();if(!o)continue;t.dataset.siteTooltip="1",t.dataset.tooltip=o,t.setAttribute("aria-label",o);const c=E.tooltip(t,{content:o,placement:"bottom"});f.push(()=>{for(const l of Array.isArray(c)?c:[c])l?.destroy?.()})}}async function p(){if(!(d||s||i)){d=!0,n.textContent="Loading Search settings...";try{const[e,t]=await Promise.all([fetch("/admin/js/templates/search.html",{cache:"no-cache"}),import("../views/search.js")]);if(!e.ok)throw new Error("Could not load Search settings.");const o=await e.text();if(i||!a.isConnected)return;const c=document.createElement("template");c.innerHTML=o,n.replaceChildren(c.content.cloneNode(!0)),await t.mountSearchSettings($(n),{onDirty:l=>{i||(h.hidden=!l)},onReady:l=>{i?l.dispose():(s=l,u(n))}}),i||u(n)}catch(e){i||(n.textContent=e.message||"Could not load Search settings. Reopen Site Settings to try again.",n.setAttribute("role","alert"))}finally{d=!1}}}return r.addEventListener("click",p),u(a.closest(".dm-plugin-shell")||a),r.classList.contains("active")&&p(),{dirty:()=>s?.dirty()||!1,save:()=>s?.save(),discard:()=>s?.discard(),dispose(){i=!0,r.removeEventListener("click",p),s?.dispose(),f.splice(0).forEach(e=>e())}}}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export const TAB_FIELDS={general:["title","tagline","siteUrl","adminHome","spacerSize","bodyClass"],branding:["brandText","brandLogo","brandIcon","brandUrl","adminBrandTitle","adminBrandLogo","adminBrandIcon"],favicon:["favicon"],
|
|
2
|
-
`):"",footerTagline:o(t.footer?.tagline),footerEmail:o(t.footer?.email),footerAddress:o(t.footer?.address),footerGradFrom:o(t.footer?.gradient?.from),footerGradTo:o(t.footer?.gradient?.to),socialTwitter:o(t.social?.twitter),socialFacebook:o(t.social?.facebook),socialInstagram:o(t.social?.instagram),socialLinkedin:o(t.social?.linkedin),socialGithub:o(t.social?.github),socialYoutube:o(t.social?.youtube),smtpHost:o(t.smtp?.host),smtpPort:o(t.smtp?.port||587),smtpUser:o(t.smtp?.user),smtpPass:"",smtpSecure:t.smtp?.secure===!0,smtpInsecureTLS:t.smtp?.allowInsecureTLS===!0,smtpFromAddress:o(t.smtp?.fromAddress),smtpFromName:o(t.smtp?.fromName),bttEnabled:r.enabled!==!1,bttSmooth:r.smooth!==!1,bttLabel:o(r.label),bttThreshold:o(r.scrollThreshold??300),bttPosition:o(r.position,"bottom-right"),bttOffset:o(r.offset??32),bttBottomOffset:o(r.bottomOffset??r.offset??32),gtbEnabled:
|
|
3
|
-
`).map(i=>i.trim()).filter(Boolean).slice(0,FOOTER_FACTS_MAX),tagline:e(t.footerTagline),email:e(t.footerEmail),address:e(t.footerAddress),gradient:{from:e(t.footerGradFrom),to:e(t.footerGradTo)}},social:{twitter:e(t.socialTwitter),facebook:e(t.socialFacebook),instagram:e(t.socialInstagram),linkedin:e(t.socialLinkedin),github:e(t.socialGithub),youtube:e(t.socialYoutube)},smtp:{host:e(t.smtpHost),port:s(t.smtpPort,587)||587,user:e(t.smtpUser),pass:o(t.smtpPass),secure:t.smtpSecure===!0,allowInsecureTLS:t.smtpInsecureTLS===!0,fromAddress:e(t.smtpFromAddress),fromName:e(t.smtpFromName)},backToTop:{enabled:t.bttEnabled===!0,scrollThreshold:s(t.bttThreshold,300),position:o(t.bttPosition)||"bottom-right",offset:s(t.bttOffset,32),bottomOffset:s(t.bttBottomOffset,32),label:e(t.bttLabel),smooth:t.bttSmooth===!0},goToBottom:{enabled:t.gtbEnabled===!0,scrollThreshold:s(t.gtbThreshold,300),position:o(t.gtbPosition)||"bottom-right",offset:s(t.gtbOffset,32),bottomOffset:s(t.gtbBottomOffset,32),label:e(t.gtbLabel),smooth:t.gtbSmooth===!0},cookieConsent:{enabled:t.ccEnabled===!0,message:e(t.ccMessage),acceptAllText:e(t.ccAcceptAll),rejectAllText:e(t.ccRejectAll),customizeText:e(t.ccCustomize),savePreferencesText:e(t.ccSavePrefs),privacyPolicyText:e(t.ccPrivacyText),privacyPolicyUrl:e(t.ccPrivacyUrl),cookiePolicyText:e(t.ccCookieText),cookiePolicyUrl:e(t.ccCookieUrl),position:o(t.ccPosition)||"bottom",layout:o(t.ccLayout)||"bar",theme:o(t.ccTheme)||"dark",showFunctional:t.ccShowFunctional===!0,showAnalytics:t.ccShowAnalytics===!0,showMarketing:t.ccShowMarketing===!0,consentVersion:e(t.ccVersion)||"1.0",showSettingsLink:t.ccSettingsLink===!0,settingsLinkText:e(t.ccSettingsLinkText)||"Cookie settings"},
|
|
1
|
+
export const TAB_FIELDS={general:["title","tagline","siteUrl","adminHome","spacerSize","bodyClass"],branding:["brandEnabled","brandText","brandLogo","brandIcon","brandUrl","adminBrandEnabled","adminBrandTitle","adminBrandLogo","adminBrandIcon"],favicon:["favicon"],footer:["footerCopyright","footerLayout","footerBlurb","footerBlocks","footerClass","footerSocialClass","footerLook","footerCtaText","footerCtaHighlight","footerCtaUrl","footerCtaSub","footerWatermark","footerStatus","footerFacts","footerTagline","footerEmail","footerAddress","footerGradFrom","footerGradTo","socialTwitter","socialFacebook","socialInstagram","socialLinkedin","socialGithub","socialYoutube"],email:["smtpHost","smtpPort","smtpUser","smtpPass","smtpSecure","smtpInsecureTLS","smtpFromAddress","smtpFromName"],pageNavigation:["bttEnabled","bttSmooth","bttLabel","bttThreshold","bttPosition","bttOffset","bttBottomOffset","gtbEnabled","gtbSmooth","gtbLabel","gtbThreshold","gtbPosition","gtbOffset","gtbBottomOffset"],cookies:["ccEnabled","ccMessage","ccAcceptAll","ccRejectAll","ccCustomize","ccSavePrefs","ccPrivacyText","ccPrivacyUrl","ccCookieText","ccCookieUrl","ccPosition","ccLayout","ccTheme","ccShowFunctional","ccShowAnalytics","ccShowMarketing","ccVersion","ccSettingsLink","ccSettingsLinkText"],breadcrumbs:["bcEnabled","bcHomeLabel","bcPosition","bcOffsetX","bcOffsetY"],motion:["mtEnabled","mtLabel","mtDefault","mtControl","mtPlacement","mtTriggerStyle","mtPosition","mtOffsetX","mtOffsetY","mtIconSize","mtIconRadius","mtClass"]},SECRET_FIELDS=["smtpPass"],BREADCRUMB_POSITIONS=["TL","TR","BL","BR"],MOTION_PLACEMENTS=["footer","floating","navbar"],MOTION_LABEL="Reduce motion";const d=["system","off","on"],c=(t,r,n,e)=>{const a=Number.parseInt(t,10);return Number.isFinite(a)?Math.min(e,Math.max(n,a)):r};export function motionFromSettings(t={}){const r=t.motion&&typeof t.motion=="object"?t.motion:{},n=t.footer||{},e=b=>r[b]!==void 0&&r[b]!==null,a=e("default")?r.default:n.motionDefault,i=e("control")?r.control:n.motionControl,m=c(r.iconSize,52,32,96);return{mtEnabled:(e("enabled")?r.enabled:n.showMotionToggle)!==!1,mtLabel:typeof r.label=="string"&&r.label.trim()?r.label.trim():MOTION_LABEL,mtDefault:d.includes(a)?a:"system",mtControl:i==="checkbox"?"checkbox":"switch",mtPlacement:MOTION_PLACEMENTS.includes(r.placement)?r.placement:"footer",mtTriggerStyle:r.triggerStyle==="icon-text"?"icon-text":"icon",mtPosition:BREADCRUMB_POSITIONS.includes(r.position)?r.position:"BL",mtOffsetX:o(c(r.offsetX,32,0,500)),mtOffsetY:o(c(r.offsetY,32,0,500)),mtIconSize:o(m),mtIconRadius:o(c(r.iconRadius,26,0,Math.floor(m/2))),mtClass:o(r.class)}}export function motionFromForm(t){const r=c(t.mtIconSize,52,32,96);return{enabled:t.mtEnabled===!0,default:d.includes(t.mtDefault)?t.mtDefault:"system",placement:MOTION_PLACEMENTS.includes(t.mtPlacement)?t.mtPlacement:"footer",control:t.mtControl==="checkbox"?"checkbox":"switch",triggerStyle:t.mtTriggerStyle==="icon-text"?"icon-text":"icon",label:o(t.mtLabel).trim().replace(/\s+/g," ").slice(0,40)||MOTION_LABEL,position:BREADCRUMB_POSITIONS.includes(t.mtPosition)?t.mtPosition:"BL",offsetX:c(t.mtOffsetX,32,0,500),offsetY:c(t.mtOffsetY,32,0,500),iconSize:r,iconRadius:c(t.mtIconRadius,26,0,Math.floor(r/2)),class:o(t.mtClass).trim()}}export function footerBlockNames(t){return[...new Set(String(t??"").split(/[\s,]+/).map(r=>r.trim().toLowerCase()).filter(Boolean))]}export function gaIdError(t){const r=o(t).trim();return!r||/^G-[A-Z0-9]{4,20}$/i.test(r)?"":"A measurement ID looks like G-XXXXXXXXXX (Google Analytics > Admin > Data streams)."}export const FOOTER_LAYOUTS=["auto","columns","row"],FOOTER_LOOKS=["","showcase"],FOOTER_FACTS_MAX=4;const u=/^(\/|#|https?:\/\/|mailto:)/,f=/^#[0-9a-f]{6}$/i;export function footerShowcaseErrors(t){const r=[],n=o(t.footerCtaUrl).trim();n&&!u.test(n)&&r.push("The footer call to action link must start with /, #, https:// or mailto: (Footer tab).");for(const[e,a]of[["from",t.footerGradFrom],["to",t.footerGradTo]]){const i=o(a).trim();i&&!f.test(i)&&r.push(`The footer gradient "${e}" colour must look like #f26b3a (Footer tab).`)}return r}const o=(t,r="")=>t==null?r:String(t),s=(t,r)=>{const n=Number.parseInt(t,10);return Number.isFinite(n)?n:r};export function homeValue(t){return/^\/[A-Za-z0-9\-_/]*$/.test(t||"")&&String(t).replace(/\/+$/,"")||"/"}export function formFromSettings(t={}){const r=t.backToTop||{},n=t.goToBottom||{},e=t.cookieConsent||{},a=t.breadcrumbs||{};return{title:o(t.title),tagline:o(t.tagline),siteUrl:o(t.baseUrl),adminHome:homeValue(t.adminHome),spacerSize:o(t.layoutOptions?.spacerSize??40),bodyClass:o(t.bodyClass),brandEnabled:t.brand?.enabled!==!1,brandText:o(t.brand?.text),brandLogo:o(t.brand?.logo),brandIcon:o(t.brand?.icon),brandUrl:o(t.brand?.url,"/"),favicon:o(t.favicon),adminBrandEnabled:t.adminBrand?.enabled!==!1,adminBrandTitle:o(t.adminBrand?.title),adminBrandLogo:o(t.adminBrand?.logo),adminBrandIcon:o(t.adminBrand?.icon),footerCopyright:o(t.footer?.copyright),footerLayout:FOOTER_LAYOUTS.includes(t.footer?.layout)?t.footer.layout:"auto",footerBlurb:o(t.footer?.blurb),footerBlocks:Array.isArray(t.footer?.blocks)?t.footer.blocks.filter(i=>typeof i=="string").join(", "):"",footerClass:o(t.footer?.class),footerSocialClass:o(t.footer?.socialClass),footerLook:FOOTER_LOOKS.includes(t.footer?.look)?t.footer.look:"",footerCtaText:o(t.footer?.cta?.text),footerCtaHighlight:o(t.footer?.cta?.highlight),footerCtaUrl:o(t.footer?.cta?.url),footerCtaSub:o(t.footer?.cta?.sub),footerWatermark:o(t.footer?.watermark),footerStatus:o(t.footer?.status),footerFacts:Array.isArray(t.footer?.facts)?t.footer.facts.filter(i=>typeof i=="string").join(`
|
|
2
|
+
`):"",footerTagline:o(t.footer?.tagline),footerEmail:o(t.footer?.email),footerAddress:o(t.footer?.address),footerGradFrom:o(t.footer?.gradient?.from),footerGradTo:o(t.footer?.gradient?.to),socialTwitter:o(t.social?.twitter),socialFacebook:o(t.social?.facebook),socialInstagram:o(t.social?.instagram),socialLinkedin:o(t.social?.linkedin),socialGithub:o(t.social?.github),socialYoutube:o(t.social?.youtube),smtpHost:o(t.smtp?.host),smtpPort:o(t.smtp?.port||587),smtpUser:o(t.smtp?.user),smtpPass:"",smtpSecure:t.smtp?.secure===!0,smtpInsecureTLS:t.smtp?.allowInsecureTLS===!0,smtpFromAddress:o(t.smtp?.fromAddress),smtpFromName:o(t.smtp?.fromName),bttEnabled:r.enabled!==!1,bttSmooth:r.smooth!==!1,bttLabel:o(r.label),bttThreshold:o(r.scrollThreshold??300),bttPosition:o(r.position,"bottom-right"),bttOffset:o(r.offset??32),bttBottomOffset:o(r.bottomOffset??r.offset??32),gtbEnabled:n.enabled===!0,gtbSmooth:n.smooth!==!1,gtbLabel:o(n.label),gtbThreshold:o(n.scrollThreshold??300),gtbPosition:o(n.position,"bottom-right"),gtbOffset:o(n.offset??32),gtbBottomOffset:o(n.bottomOffset??n.offset??32),ccEnabled:e.enabled!==!1,ccMessage:o(e.message),ccAcceptAll:o(e.acceptAllText,"Accept All"),ccRejectAll:o(e.rejectAllText,"Reject All"),ccCustomize:o(e.customizeText,"Customize"),ccSavePrefs:o(e.savePreferencesText,"Save Preferences"),ccPrivacyText:o(e.privacyPolicyText,"Privacy Policy"),ccPrivacyUrl:o(e.privacyPolicyUrl),ccCookieText:o(e.cookiePolicyText,"Cookie Policy"),ccCookieUrl:o(e.cookiePolicyUrl),ccPosition:o(e.position,"bottom"),ccLayout:o(e.layout,"bar"),ccTheme:o(e.theme,"dark"),ccShowFunctional:e.showFunctional!==!1,ccShowAnalytics:e.showAnalytics!==!1,ccShowMarketing:e.showMarketing!==!1,ccVersion:o(e.consentVersion,"1.0"),ccSettingsLink:e.showSettingsLink!==!1,ccSettingsLinkText:o(e.settingsLinkText,"Cookie settings"),bcEnabled:a.enabled===!0,bcHomeLabel:o(a.homeLabel,"Home"),bcPosition:BREADCRUMB_POSITIONS.includes(a.position)?a.position:"TL",bcOffsetX:o(a.offsetX??8),bcOffsetY:o(a.offsetY??8),...motionFromSettings(t)}}export function payloadFromForm(t,r={},{adminHomeReady:n=!0}={}){const e=i=>o(i).trim(),a=motionFromForm(t);return{title:e(t.title),tagline:e(t.tagline),baseUrl:e(t.siteUrl).replace(/\/+$/,""),...n&&{adminHome:e(t.adminHome)||"/"},layoutOptions:{spacerSize:s(t.spacerSize,40)||40},bodyClass:e(t.bodyClass),brand:{...r.brand,enabled:t.brandEnabled!==!1,text:e(t.brandText),logo:e(t.brandLogo),icon:e(t.brandIcon),url:e(t.brandUrl)||"/"},favicon:e(t.favicon),adminBrand:{...r.adminBrand,enabled:t.adminBrandEnabled!==!1,title:e(t.adminBrandTitle),logo:e(t.adminBrandLogo),icon:e(t.adminBrandIcon)},footer:{copyright:e(t.footerCopyright),layout:FOOTER_LAYOUTS.includes(t.footerLayout)?t.footerLayout:"auto",blurb:e(t.footerBlurb),blocks:footerBlockNames(t.footerBlocks),links:r.footer?.links||[],showMotionToggle:a.enabled,motionDefault:a.default,motionControl:a.control,class:e(t.footerClass),socialClass:e(t.footerSocialClass),look:FOOTER_LOOKS.includes(t.footerLook)?t.footerLook:"",cta:{text:e(t.footerCtaText),highlight:e(t.footerCtaHighlight),url:e(t.footerCtaUrl),sub:e(t.footerCtaSub)},watermark:e(t.footerWatermark),status:e(t.footerStatus),facts:String(t.footerFacts??"").split(`
|
|
3
|
+
`).map(i=>i.trim()).filter(Boolean).slice(0,FOOTER_FACTS_MAX),tagline:e(t.footerTagline),email:e(t.footerEmail),address:e(t.footerAddress),gradient:{from:e(t.footerGradFrom),to:e(t.footerGradTo)}},social:{twitter:e(t.socialTwitter),facebook:e(t.socialFacebook),instagram:e(t.socialInstagram),linkedin:e(t.socialLinkedin),github:e(t.socialGithub),youtube:e(t.socialYoutube)},smtp:{host:e(t.smtpHost),port:s(t.smtpPort,587)||587,user:e(t.smtpUser),pass:o(t.smtpPass),secure:t.smtpSecure===!0,allowInsecureTLS:t.smtpInsecureTLS===!0,fromAddress:e(t.smtpFromAddress),fromName:e(t.smtpFromName)},backToTop:{enabled:t.bttEnabled===!0,scrollThreshold:s(t.bttThreshold,300),position:o(t.bttPosition)||"bottom-right",offset:s(t.bttOffset,32),bottomOffset:s(t.bttBottomOffset,32),label:e(t.bttLabel),smooth:t.bttSmooth===!0},goToBottom:{enabled:t.gtbEnabled===!0,scrollThreshold:s(t.gtbThreshold,300),position:o(t.gtbPosition)||"bottom-right",offset:s(t.gtbOffset,32),bottomOffset:s(t.gtbBottomOffset,32),label:e(t.gtbLabel),smooth:t.gtbSmooth===!0},cookieConsent:{enabled:t.ccEnabled===!0,message:e(t.ccMessage),acceptAllText:e(t.ccAcceptAll),rejectAllText:e(t.ccRejectAll),customizeText:e(t.ccCustomize),savePreferencesText:e(t.ccSavePrefs),privacyPolicyText:e(t.ccPrivacyText),privacyPolicyUrl:e(t.ccPrivacyUrl),cookiePolicyText:e(t.ccCookieText),cookiePolicyUrl:e(t.ccCookieUrl),position:o(t.ccPosition)||"bottom",layout:o(t.ccLayout)||"bar",theme:o(t.ccTheme)||"dark",showFunctional:t.ccShowFunctional===!0,showAnalytics:t.ccShowAnalytics===!0,showMarketing:t.ccShowMarketing===!0,consentVersion:e(t.ccVersion)||"1.0",showSettingsLink:t.ccSettingsLink===!0,settingsLinkText:e(t.ccSettingsLinkText)||"Cookie settings"},breadcrumbs:{enabled:t.bcEnabled===!0,homeLabel:e(t.bcHomeLabel)||"Home",position:BREADCRUMB_POSITIONS.includes(t.bcPosition)?t.bcPosition:"TL",offsetX:s(t.bcOffsetX,8),offsetY:s(t.bcOffsetY,8)},motion:a}}export function siteUrlError(t){const r=o(t).trim().replace(/\/+$/,"");return r?/^https?:\/\//i.test(r)?/^https?:\/\/([a-z0-9.\-]+|\[[0-9a-f:.]+\])(:\d{1,5})?$/i.test(r)?"":"Just the address, like https://example.com - no page path after it.":"Start with https:// (or http://).":""}const l=t=>typeof t=="boolean"?t:o(t);export function snapshot(t){return JSON.stringify(Object.keys(t).sort().map(r=>[r,l(t[r])]))}export function changedTabs(t,r){const n=[];for(const[e,a]of Object.entries(TAB_FIELDS))a.some(i=>l(t[i])!==l(r[i]))&&n.push(e);return n}export function draftOf(t){return Object.fromEntries(Object.entries(t).filter(([r])=>!SECRET_FIELDS.includes(r)))}const p={footerMotionToggle:"mtEnabled",footerMotionDefault:"mtDefault",footerMotionControl:"mtControl"};export function formFromDraft(t,r={}){const n={...r};for(const[a,i]of Object.entries(p))a in n&&!(i in n)&&(n[i]=n[a]),delete n[a];const e={...t};for(const[a,i]of Object.entries(n))a in e&&!SECRET_FIELDS.includes(a)&&(e[a]=i);return e}export function settingsProblems(t,r={}){const n=[];return o(t.smtpHost).trim()?o(t.smtpFromAddress).trim()||n.push({tone:"warn",tab:"email",text:"No From address - mail has no sender and many servers refuse it."}):n.push({tone:"danger",tab:"email",text:"No SMTP host - mail goes to a throwaway test inbox and reaches no one (password resets, form notifications)."}),(t.ccEnabled===!0||o(r.googleAnalytics?.measurementId).trim())&&!o(t.ccPrivacyUrl).trim()&&n.push({tone:"warn",tab:"cookies",text:"The cookie banner is on but links to no privacy policy."}),n}export function describeCacheKey(t){const r=/^page:(.*?):m\d+:role([^:]*):/.exec(String(t||""));return r?{path:r[1]||"/",role:r[2]||"anon",page:!0}:{path:String(t||""),role:"",page:!1}}export function expiresIn(t,r=Date.now()){if(t==null)return"no expiry";const n=t-r;return n<=0?"expired":n<6e4?`${Math.round(n/1e3)}s`:n<36e5?`${Math.round(n/6e4)}m`:`${Math.round(n/36e5)}h`}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
let s={};export function renderFrameworkVersions(r){r&&(s=r);for(const[e,i,o]of[["topbar-reactive-version","Reactive",s.reactive],["topbar-js-version","JS",globalThis.Domma?.version||s.js]]){const n=document.getElementById(e);n&&(n.textContent=o||"",n.parentElement.hidden=!o,n.hidden=!o)}}export function sidebarPreferenceKey(r){return"sidebar.visible."+(r?.id||"anonymous")}export function wireAccountMenu(r){c("topbar-account"),c("topbar-versions-menu");const e=document.getElementById("topbar-show-sidebar");if(!e||e.dataset.wired)return;e.dataset.wired="1";const i=sidebarPreferenceKey(r);let o=!0;try{o=S.get(i)!==!1}catch{}const n=a=>{!a&&document.body.classList.contains("dm-sidebar-open")&&document.getElementById("topbar-sidebar-toggle")?.click(),document.body.classList.toggle("dm-sidebar-hidden",!a),e.checked=a};n(o),e.addEventListener("change",()=>{n(e.checked);try{S.set(i,e.checked)}catch{}})}const d=new Map;function c(r){const e=document.getElementById(r);if(!e||e.dataset.wired)return;e.dataset.wired="1";const i=e.querySelector("button"),o=e.querySelector(".topbar-account-menu"),n=t=>{t&&document.dispatchEvent(new CustomEvent("dm:topbar-menu-open",{detail:{owner:e}})),o.hidden=!t,i.setAttribute("aria-expanded",String(t))};i.addEventListener("click",()=>n(o.hidden)),i.addEventListener("keydown",t=>{t.key==="ArrowDown"&&(t.preventDefault(),n(!0),o.querySelector("a, input")?.focus())}),e.addEventListener("click",t=>{t.target.closest("a")&&n(!1)}),d.get(r)?.abort();const a=new AbortController;d.set(r,a),document.addEventListener("click",t=>{e.contains(t.target)||n(!1)},{signal:a.signal}),document.addEventListener("dm:topbar-menu-open",t=>{t.detail.owner!==e&&n(!1)},{signal:a.signal}),document.addEventListener("keydown",t=>{!t.defaultPrevented&&t.key==="Escape"&&!o.hidden&&(n(!1),i.focus(),t.preventDefault())},{signal:a.signal}),window.addEventListener("hashchange",()=>n(!1),{signal:a.signal})}
|
|
1
|
+
let s={};export function renderFrameworkVersions(r){r&&(s=r);for(const[e,i,o]of[["topbar-reactive-version","Reactive",s.reactive],["topbar-js-version","JS",globalThis.Domma?.version||s.js]]){const n=document.getElementById(e);n&&(n.textContent=o||"",n.parentElement.hidden=!o,n.hidden=!o)}}export function sidebarPreferenceKey(r){return"sidebar.visible."+(r?.id||"anonymous")}export function wireAccountMenu(r){c("topbar-account"),c("topbar-versions-menu");const e=document.getElementById("topbar-show-sidebar");if(!e||e.dataset.wired)return;e.dataset.wired="1";const i=sidebarPreferenceKey(r);let o=!0;try{o=S.get(i)!==!1}catch{}const n=a=>{!a&&document.body.classList.contains("dm-sidebar-open")&&document.getElementById("topbar-sidebar-toggle")?.click(),document.body.classList.toggle("dm-sidebar-hidden",!a),e.checked=a};n(o),e.addEventListener("change",()=>{n(e.checked);try{S.set(i,e.checked)}catch{}})}const d=new Map;function c(r){const e=document.getElementById(r);if(!e||e.dataset.wired)return;e.dataset.wired="1";const i=e.querySelector("button"),o=e.querySelector(".topbar-account-menu"),n=t=>{t&&document.dispatchEvent(new CustomEvent("dm:topbar-menu-open",{detail:{owner:e}})),o.hidden=!t,i.setAttribute("aria-expanded",String(t))};i.addEventListener("click",()=>n(o.hidden)),i.addEventListener("keydown",t=>{t.key==="ArrowDown"&&(t.preventDefault(),n(!0),o.querySelector("a, input, button:not(:disabled)")?.focus())}),e.addEventListener("click",t=>{t.target.closest("a")&&n(!1)}),d.get(r)?.abort();const a=new AbortController;d.set(r,a),document.addEventListener("click",t=>{e.contains(t.target)||n(!1)},{signal:a.signal}),document.addEventListener("dm:topbar-menu-open",t=>{t.detail.owner!==e&&n(!1)},{signal:a.signal}),document.addEventListener("keydown",t=>{!t.defaultPrevented&&t.key==="Escape"&&!o.hidden&&(n(!1),i.focus(),t.preventDefault())},{signal:a.signal}),window.addEventListener("hashchange",()=>n(!1),{signal:a.signal})}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import{apiRequest as g}from"../api.js";const C=e=>e>0&&e<.001?"<0.1%":`${Math.round(Math.min(1,Math.max(0,e))*1e3)/10}%`;export function cacheSummary(e={}){if(!e.enabled)return{usage:"Off",detail:"Response caching is off.",hitRate:""};const n=Number.isFinite(e.size)&&e.size>=0?e.size:null,c=Number.isFinite(e.maxItems)&&e.maxItems>0?e.maxItems:null;return{usage:n!==null&&c?C(n/c):"On",detail:n===null?"Capacity unavailable.":c?`${n.toLocaleString()} / ${c.toLocaleString()} entries`:`${n.toLocaleString()} entries`,hitRate:Number.isFinite(e.hitRate)?C(e.hitRate):"No requests yet"}}let b;export function wireNotificationCacheMenu(){b?.abort(),b=new AbortController;const{signal:e}=b,n=document.getElementById("topbar-notifications"),c=document.getElementById("topbar-notifications-menu");if(!n||!c)return;c.querySelector("[data-topbar-cache]")?.remove();const a=document.createElement("section");a.className="topbar-cache",a.dataset.topbarCache="",a.hidden=!0,a.setAttribute("aria-label","Response cache"),a.innerHTML=`<div class="topbar-cache-row"><span>Cache usage</span><strong class="topbar-version-badge" data-cache-usage></strong></div>
|
|
2
|
+
<p class="topbar-cache-detail" data-cache-detail></p>
|
|
3
|
+
<div class="topbar-cache-row" data-cache-hits-row><span>Hit rate</span><span data-cache-hits></span></div>
|
|
4
|
+
<div class="topbar-cache-actions"><button type="button" class="btn btn-sm btn-ghost" data-cache-refresh>Refresh</button>
|
|
5
|
+
<button type="button" class="btn btn-sm btn-outline" data-cache-clear disabled>Clear cache</button></div>
|
|
6
|
+
<p class="topbar-cache-detail" role="status" aria-live="polite" data-cache-message></p>`,c.appendChild(a);const i=t=>a.querySelector(t),r=i("[data-cache-clear]"),o=i("[data-cache-refresh]"),l=i("[data-cache-message]");let h=null,u=!1,d=!1,s=null;const m=t=>{s=cacheSummary(t),i("[data-cache-usage]").textContent=s.usage,i("[data-cache-detail]").textContent=s.detail,i("[data-cache-hits]").textContent=s.hitRate,i("[data-cache-hits-row]").hidden=!s.hitRate,a.hidden=!1,r.disabled=d},f=t=>{[401,403].includes(t.status)?(h=!1,a.hidden=!0):(a.hidden=!1,l.textContent=t.message||"Cache unavailable. Try Refresh.")},p=async()=>{if(!(e.aborted||h===!1||u||d)){u=!0,o.disabled=!0,r.disabled=!0,l.textContent="Refreshing cache...";try{const t=await g("/cache/status");if(e.aborted)return;h=!0,m(t),l.textContent=""}catch(t){e.aborted||f(t)}finally{u=!1,e.aborted||(o.disabled=!1,r.disabled=!s)}}};document.addEventListener("dm:topbar-menu-open",t=>{t.detail?.owner===n&&p()},{signal:e}),o.addEventListener("click",p,{signal:e}),r.addEventListener("click",async()=>{if(!(d||u||h!==!0||!s)){d=!0,r.disabled=!0,o.disabled=!0;try{if(!await E.confirm("Clear the response cache? Public pages will be rendered again as visitors open them.")||e.aborted)return;l.textContent="Clearing cache...";const t=await g("/cache/clear",{method:"POST"});if(e.aborted)return;m(t),l.textContent="Cache cleared.",E.toast("Cache cleared.",{type:"success"}),window.dispatchEvent(new CustomEvent("dm:cache-cleared"))}catch(t){e.aborted||f(t)}finally{d=!1,e.aborted||(o.disabled=!1,r.disabled=!s)}}},{signal:e})}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import{api as
|
|
2
|
-
${
|
|
1
|
+
import{api as A,apiRequest as k}from"../api.js";import{sidebarCount as S}from"./tool-kit.js";import{severityOf as C}from"./notifications-arrange.js";import{wireNotificationCacheMenu as L}from"./topbar-cache.js";const q=[["critical","Critical","alert-circle","danger"],["warning","Warnings","alert-triangle","warn"],["info","Information","info","info"],["success","Success","check-circle","ok"]];export function notificationGroups(p=[]){return q.map(([i,c,u,e])=>{const d=p.filter(l=>C(l)===i);return{type:i,label:c,icon:u,tone:e,total:d.length,unread:d.filter(l=>l.unread).length}})}let y;export function wireNotificationMenu({refreshBadge:p}){y?.abort(),y=new AbortController;const{signal:i}=y,c=document.getElementById("topbar-notifications");if(!c)return;const u=c.querySelector("#topbar-bell"),e=c.querySelector("#topbar-notifications-menu"),d=document.createElement("div");d.dataset.notificationItems="",e.replaceChildren(d),L();let l=0,m=!1;const f=()=>{l++,e.hidden=!0,u.setAttribute("aria-expanded","false")},h=t=>{const n=document.createElement("p");n.className="nt-menu-status",n.setAttribute("role","status"),n.textContent=t,d.replaceChildren(n)},g=t=>{const n=d.contains(document.activeElement)?document.activeElement:null,o=n?.dataset.dismiss,s=n?.getAttribute("href"),r=n?.hasAttribute("data-read-all"),v=notificationGroups(t);d.innerHTML=`<div class="nt-menu-heading" aria-live="polite">Notifications <span>${v.reduce((a,$)=>a+$.unread,0)} unread</span></div>
|
|
2
|
+
${v.map(a=>`<div class="nt-menu-row">
|
|
3
3
|
<a class="nt-menu-type" href="#/system/notifications?type=${a.type}">
|
|
4
4
|
<span class="nt-menu-icon is-${a.tone}"><span data-icon="${a.icon}"></span></span>
|
|
5
5
|
<span>${a.label}<small>${a.unread} unread / ${a.total} total</small></span>
|
|
6
6
|
</a>
|
|
7
|
-
<button type="button" class="nt-menu-dismiss" data-dismiss="${a.type}" data-count="${a.total}" aria-label="Dismiss ${a.total} ${a.label.toLowerCase()} notifications" ${!a.total||
|
|
7
|
+
<button type="button" class="nt-menu-dismiss" data-dismiss="${a.type}" data-count="${a.total}" aria-label="Dismiss ${a.total} ${a.label.toLowerCase()} notifications" ${!a.total||m?"disabled":""}>Dismiss</button>
|
|
8
8
|
</div>`).join("")}
|
|
9
9
|
${t.length?"":`<p class="nt-menu-status">No notifications - you're all caught up.</p>`}
|
|
10
10
|
<div class="nt-menu-footer"><a href="#/system/notifications?type=all">View all notifications</a>
|
|
11
|
-
<button type="button" data-read-all ${
|
|
11
|
+
<button type="button" data-read-all ${m||!t.some(a=>a.unread)?"disabled":""}>Mark all as read</button></div>`,Domma.icons.scan(e),o?(e.querySelector(`[data-dismiss="${o}"]:not(:disabled)`)||e.querySelector(`a[href$="type=${o}"]`))?.focus():s?[...e.querySelectorAll("a")].find(a=>a.getAttribute("href")===s)?.focus():r&&(e.querySelector("[data-read-all]:not(:disabled)")||e.querySelector(".nt-menu-footer a"))?.focus()},b=async()=>{const t=++l;try{const n=await A.system.notifications.list();if(i.aborted||t!==l||e.hidden)return;g(Array.isArray(n)?n:[])}catch{!i.aborted&&t===l&&!e.hidden&&h("Could not load notifications. Close and reopen to try again.")}},w=async t=>{document.dispatchEvent(new CustomEvent("dm:topbar-menu-open",{detail:{owner:c}})),e.hidden=!1,u.setAttribute("aria-expanded","true"),h("Loading notifications..."),await b(),t&&!e.hidden&&e.querySelector("a, button:not(:disabled)")?.focus()};u.addEventListener("click",()=>e.hidden?w(!1):f(),{signal:i}),u.addEventListener("keydown",t=>{t.key==="ArrowDown"&&(t.preventDefault(),w(!0))},{signal:i}),e.addEventListener("click",async t=>{const n=t.target.closest("a");if(n){const r=new URLSearchParams(n.hash.split("?")[1]||"").get("type");document.dispatchEvent(new CustomEvent("dm:notifications-filter",{detail:{type:r}})),f();return}const o=t.target.closest("button");if(!o||o.disabled||m||!o.hasAttribute("data-dismiss")&&!o.hasAttribute("data-read-all"))return;const s=o.dataset.dismiss;m=!0;try{if(s==="critical"&&!await E.confirm(`Dismiss all ${o.dataset.count} critical notifications? They are hidden for you only.`))return;d.querySelectorAll("button").forEach(r=>{r.disabled=!0}),await k(`/system/notifications/${s?"dismiss-all":"read-all"}`,{method:"POST",...s?{body:JSON.stringify({severity:s})}:{}}),document.dispatchEvent(new CustomEvent("dm:notifications-changed")),S("#/system/notifications"),await p()}catch(r){E.toast(`Could not update notifications: ${r.message||r}`,{type:"error"})}finally{m=!1,!i.aborted&&!e.hidden&&await b()}},{signal:i}),e.addEventListener("keydown",t=>{const n=[...e.querySelectorAll("a, button:not(:disabled)")].filter(r=>!r.closest("[hidden]")),o=n.indexOf(document.activeElement);if(o<0)return;let s;t.key==="ArrowDown"&&(s=(o+1)%n.length),t.key==="ArrowUp"&&(s=(o-1+n.length)%n.length),t.key==="Home"&&(s=0),t.key==="End"&&(s=n.length-1),s!==void 0&&(t.preventDefault(),n[s].focus())},{signal:i}),document.addEventListener("click",t=>{c.contains(t.target)||f()},{signal:i}),document.addEventListener("focusin",t=>{c.contains(t.target)||f()},{signal:i}),document.addEventListener("dm:topbar-menu-open",t=>{t.detail.owner!==c&&f()},{signal:i}),document.addEventListener("keydown",t=>{!t.defaultPrevented&&t.key==="Escape"&&!e.hidden&&(f(),u.focus(),t.preventDefault())},{signal:i}),window.addEventListener("hashchange",f,{signal:i})}
|
|
@@ -11,6 +11,13 @@
|
|
|
11
11
|
<code>config/site.json</code>. Themes and fonts have their own screen, <strong>Content >
|
|
12
12
|
Theme</strong>.</p>
|
|
13
13
|
|
|
14
|
+
<p>Search defaults, the default share image and Google Business Profile are in the
|
|
15
|
+
<strong>SEO</strong> tool's settings. The Google Analytics measurement ID is in
|
|
16
|
+
<strong>Analytics</strong> settings. Cookie consent stays here.</p>
|
|
17
|
+
|
|
18
|
+
<p>Public site search is configured in the <strong>Search</strong> tab, including its trigger,
|
|
19
|
+
position and query preview. Existing Search links open that tab.</p>
|
|
20
|
+
|
|
14
21
|
<table class="table table-sm">
|
|
15
22
|
<thead>
|
|
16
23
|
<tr>
|
|
@@ -35,17 +42,23 @@
|
|
|
35
42
|
browser tab, for everyone who signs in; empty, it uses the site title. The admin logo
|
|
36
43
|
takes priority over the admin icon.</td>
|
|
37
44
|
</tr>
|
|
38
|
-
<tr>
|
|
39
|
-
<td>SEO</td>
|
|
40
|
-
<td>Default page title, title separator and default description. The full SEO Tool is
|
|
41
|
-
<strong>SEO</strong> in the sidebar.</td>
|
|
42
|
-
</tr>
|
|
43
45
|
<tr>
|
|
44
46
|
<td>Footer</td>
|
|
45
|
-
<td>Copyright text, layout, blurb,
|
|
46
|
-
|
|
47
|
+
<td>Copyright text, layout, blurb, footer blocks and social links.
|
|
48
|
+
Footer links are menus - see <a href="#/docs/usage/navigation">Navigation</a>.
|
|
47
49
|
The <a href="#showcase-footer">Showcase footer</a> card adds a big call to action and more.</td>
|
|
48
50
|
</tr>
|
|
51
|
+
<tr>
|
|
52
|
+
<td>Reduce motion</td>
|
|
53
|
+
<td>The accessibility control that lets visitors turn animations off: whether it is shown,
|
|
54
|
+
its label, and what a visitor who has not chosen gets (follow their device, full motion,
|
|
55
|
+
or calm). <strong>Placement</strong> puts it in the footer (a switch or a checkbox), as a
|
|
56
|
+
floating button in a corner you pick - with its size, corners and distance from the
|
|
57
|
+
edges - or at the end of the navbar (floating where there is no navbar, or on a phone
|
|
58
|
+
when the navbar shows only its menu button). A floating button sharing a corner with Search,
|
|
59
|
+
Back to top or the theme switcher stacks clear of it. Hiding the control does not take
|
|
60
|
+
the choice away from visitors who already made it.</td>
|
|
61
|
+
</tr>
|
|
49
62
|
<tr>
|
|
50
63
|
<td>Email</td>
|
|
51
64
|
<td>The SMTP server the site sends mail through (form notifications, password resets,
|
|
@@ -291,7 +291,7 @@
|
|
|
291
291
|
</div>
|
|
292
292
|
<div class="form-group">
|
|
293
293
|
<label>Staggered entrance
|
|
294
|
-
<span data-help-title="Staggered entrance" data-help="The items arrive one after another when the menu is drawn, like a row of cards revealing. A dropdown or footer column staggers in on its own when it opens. Visitors who have asked for less motion (their device setting, or the site's motion
|
|
294
|
+
<span data-help-title="Staggered entrance" data-help="The items arrive one after another when the menu is drawn, like a row of cards revealing. A dropdown or footer column staggers in on its own when it opens. Visitors who have asked for less motion (their device setting, or the site's Reduce motion control) see the items straight away."></span></label>
|
|
295
295
|
<label style="display:flex;align-items:center;gap:.5rem;font-weight:400;cursor:pointer">
|
|
296
296
|
<input type="checkbox" id="me-entrance-enabled" name="entranceEnabled" style="width:auto;margin:0">
|
|
297
297
|
Bring the items in one after another
|