domma-cms 1.15.0 → 1.16.1
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/api-navigation.html +9 -3
- package/admin/js/templates/docs/usage-navigation.html +4 -0
- package/admin/js/templates/docs/usage-site-settings.html +7 -5
- package/admin/js/templates/settings.html +8 -41
- package/admin/js/views/analytics.js +8 -6
- package/admin/js/views/menu-editor.js +16 -16
- 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 +2 -1
- 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/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/menus.js +8 -4
- package/server/routes/api/seo.js +14 -3
- package/server/services/adminBrand.js +1 -0
- package/server/services/googleBusiness.js +225 -0
- package/server/services/menus.js +27 -2
- package/server/services/renderer.js +2 -0
- package/server/templates/page.html +1 -1
|
@@ -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(
|
|
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})}
|
|
@@ -81,14 +81,16 @@
|
|
|
81
81
|
|
|
82
82
|
<h3><span class="method-badge method-get">GET</span><span class="endpoint-path">/api/menus/:slug</span></h3>
|
|
83
83
|
<p class="auth-note">Requires Bearer token + <code>menus</code> read permission.</p>
|
|
84
|
-
<p>One menu, with its items. Sub-items go under <code>items</code
|
|
84
|
+
<p>One menu, with its items. Sub-items go under <code>items</code>. <code>revision</code> is a fingerprint of the
|
|
85
|
+
menu as stored; send it back on <code>PUT</code> to be warned of changes made since.</p>
|
|
85
86
|
<pre class="code-block"><code>// Response 200
|
|
86
87
|
{ "slug": "main", "name": "Main navigation", "variant": "dark", "position": "sticky",
|
|
87
88
|
"items": [
|
|
88
89
|
{ "text": "Home", "url": "/", "icon": "home" },
|
|
89
90
|
{ "text": "Services", "items": [ { "text": "Design", "url": "/design" } ] }
|
|
90
91
|
],
|
|
91
|
-
"meta": { "createdAt": "...", "updatedAt": "..." }
|
|
92
|
+
"meta": { "createdAt": "...", "updatedAt": "..." },
|
|
93
|
+
"revision": "3f9a1c0e5b7d2a46" }</code></pre>
|
|
92
94
|
|
|
93
95
|
<h3><span class="method-badge method-post">POST</span><span class="endpoint-path">/api/menus</span></h3>
|
|
94
96
|
<p class="auth-note">Requires Bearer token + <code>menus</code> create permission.</p>
|
|
@@ -102,7 +104,11 @@
|
|
|
102
104
|
|
|
103
105
|
<h3><span class="method-badge method-put">PUT</span><span class="endpoint-path">/api/menus/:slug</span></h3>
|
|
104
106
|
<p class="auth-note">Requires Bearer token + <code>menus</code> update permission.</p>
|
|
105
|
-
<p>Save a menu. The public pages that show it are refreshed
|
|
107
|
+
<p>Save a menu. The public pages that show it are refreshed. Include the <code>revision</code> you loaded and the
|
|
108
|
+
save is refused if the menu has changed since (leave it out to overwrite regardless). The response carries the
|
|
109
|
+
new <code>revision</code>.</p>
|
|
110
|
+
<pre class="code-block"><code>// Error 409 - changed since it was loaded; nothing is written
|
|
111
|
+
{ "error": "Menu \"main\" has changed since it was opened", "stale": true }</code></pre>
|
|
106
112
|
|
|
107
113
|
<h3><span class="method-badge method-delete">DELETE</span><span class="endpoint-path">/api/menus/:slug</span></h3>
|
|
108
114
|
<p class="auth-note">Requires Bearer token + <code>menus</code> delete permission.</p>
|
|
@@ -63,6 +63,10 @@
|
|
|
63
63
|
<li><strong>Meta</strong> - the menu's id, which is its file name and what
|
|
64
64
|
<code>[menu slug="..."]</code> names.</li>
|
|
65
65
|
</ul>
|
|
66
|
+
<p>If the menu was changed somewhere else after you opened it - in another tab, by someone else, or
|
|
67
|
+
on the server - Save warns you instead of quietly undoing that change. Choose <strong>OK</strong>
|
|
68
|
+
to save yours over it, or <strong>Cancel</strong> to load the latest version; your changes are
|
|
69
|
+
then offered back as a draft.</p>
|
|
66
70
|
|
|
67
71
|
<h3>Pill bar and card dropdowns</h3>
|
|
68
72
|
<p>Two choices on the <strong>Behaviour</strong> tab change how the navbar looks. Both are for a
|
|
@@ -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,11 +42,6 @@
|
|
|
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
47
|
<td>Copyright text, layout, blurb, footer blocks and social links.
|
|
@@ -18,15 +18,14 @@
|
|
|
18
18
|
<button type="button" class="tab-item active">General <i class="stx-dot" data-bind-hidden="!tabs.value.general" title="Changed"></i></button>
|
|
19
19
|
<button type="button" class="tab-item">Branding <i class="stx-dot" data-bind-hidden="!tabs.value.branding" title="Changed"></i></button>
|
|
20
20
|
<button type="button" class="tab-item">Favicon <i class="stx-dot" data-bind-hidden="!tabs.value.favicon" title="Changed"></i></button>
|
|
21
|
-
<button type="button" class="tab-item">SEO <i class="stx-dot" data-bind-hidden="!tabs.value.seo" title="Changed"></i></button>
|
|
22
21
|
<button type="button" class="tab-item">Footer <i class="stx-dot" data-bind-hidden="!tabs.value.footer" title="Changed"></i></button>
|
|
23
22
|
<button type="button" class="tab-item">Email <i class="stx-dot" data-bind-hidden="!tabs.value.email" title="Changed"></i><i class="stx-dot is-bad" data-bind-hidden="!noSmtp.value || tabs.value.email" title="No SMTP host"></i></button>
|
|
24
23
|
<button type="button" class="tab-item">Page navigation <i class="stx-dot" data-bind-hidden="!tabs.value.pageNavigation" title="Changed"></i></button>
|
|
25
24
|
<button type="button" class="tab-item">Cookie consent <i class="stx-dot" data-bind-hidden="!tabs.value.cookies" title="Changed"></i></button>
|
|
26
|
-
<button type="button" class="tab-item">Analytics <i class="stx-dot" data-bind-hidden="!tabs.value.analytics" title="Changed"></i></button>
|
|
27
25
|
<button type="button" class="tab-item">Breadcrumbs <i class="stx-dot" data-bind-hidden="!tabs.value.breadcrumbs" title="Changed"></i></button>
|
|
28
26
|
<button type="button" class="tab-item">Reduce motion <i class="stx-dot" data-bind-hidden="!tabs.value.motion" title="Changed"></i></button>
|
|
29
27
|
<button type="button" class="tab-item">Cache</button>
|
|
28
|
+
<button type="button" class="tab-item" data-site-search-tab>Search <i class="stx-dot" data-site-search-dirty hidden title="Changed"></i></button>
|
|
30
29
|
</div>
|
|
31
30
|
|
|
32
31
|
<!-- General -->
|
|
@@ -92,6 +91,9 @@
|
|
|
92
91
|
<div class="tab-panel">
|
|
93
92
|
<div class="stx-card-columns">
|
|
94
93
|
<div class="stx-card"><div class="stx-card-title"><span data-icon="globe" data-icon-size="16" aria-hidden="true"></span>Public site</div><div class="stx-grid">
|
|
94
|
+
<div class="stx-setting stx-wide"><label class="form-label" for="stx-brand-enabled">Show public site branding
|
|
95
|
+
<span data-help="Shows the name and mark in the navbar and footer. Off hides them and keeps the saved values for next time."></span></label>
|
|
96
|
+
<input id="stx-brand-enabled" type="checkbox" class="form-switch" data-model="brandEnabled.value"></div>
|
|
95
97
|
<div><label class="form-label" for="stx-brandText">Brand name</label>
|
|
96
98
|
<input id="stx-brandText" type="text" class="form-input" placeholder="My Site" data-model="brandText.value">
|
|
97
99
|
</div>
|
|
@@ -111,6 +113,9 @@
|
|
|
111
113
|
</div><p class="text-muted">The logo takes priority over the icon. Set the browser icon in the Favicon tab.</p></div>
|
|
112
114
|
<div class="stx-card"><div class="stx-card-title"><span data-icon="layout" data-icon-size="16" aria-hidden="true"></span>Admin
|
|
113
115
|
<span data-help-title="Admin name and mark" data-help="The name and logo or icon at the top of the admin - in the top bar, the sidebar and the browser tab - for everyone who signs in. Leave the name empty to use the site title."></span></div><div class="stx-grid">
|
|
116
|
+
<div class="stx-setting stx-wide"><label class="form-label" for="stx-admin-brand-enabled">Show admin branding
|
|
117
|
+
<span data-help="Shows the name and mark in the admin top bar and sidebar, and the name in browser tab titles. Off hides them and keeps the saved values."></span></label>
|
|
118
|
+
<input id="stx-admin-brand-enabled" type="checkbox" class="form-switch" data-model="adminBrandEnabled.value"></div>
|
|
114
119
|
<div><label class="form-label" for="stx-adminBrandTitle">Admin name</label>
|
|
115
120
|
<input id="stx-adminBrandTitle" type="text" class="form-input" placeholder="Uses the site title when empty" data-model="adminBrandTitle.value">
|
|
116
121
|
</div>
|
|
@@ -151,30 +156,6 @@
|
|
|
151
156
|
</div>
|
|
152
157
|
</div>
|
|
153
158
|
|
|
154
|
-
<!-- SEO -->
|
|
155
|
-
<div class="tab-panel">
|
|
156
|
-
<div class="stx-card">
|
|
157
|
-
<div class="stx-card-title"><span data-icon="search" data-icon-size="16" aria-hidden="true"></span>Search engines
|
|
158
|
-
<span data-help-title="SEO defaults" data-help="Used on every page that does not set its own. A page's title becomes “Page title” + separator + default site title."></span></div>
|
|
159
|
-
<div class="stx-grid">
|
|
160
|
-
<div>
|
|
161
|
-
<label class="form-label" for="stx-seo-title">Default site title</label>
|
|
162
|
-
<input id="stx-seo-title" type="text" class="form-input" placeholder="My Site" data-model="seoTitle.value">
|
|
163
|
-
</div>
|
|
164
|
-
<div>
|
|
165
|
-
<label class="form-label" for="stx-seo-sep">Title separator</label>
|
|
166
|
-
<input id="stx-seo-sep" type="text" class="form-input" placeholder=" | " data-model="seoSeparator.value">
|
|
167
|
-
</div>
|
|
168
|
-
<div class="stx-wide">
|
|
169
|
-
<label class="form-label" for="stx-seo-desc">Default meta description
|
|
170
|
-
<span data-help="The summary search engines show under a page that has no description of its own. About 150 characters is what they display."></span></label>
|
|
171
|
-
<input id="stx-seo-desc" type="text" class="form-input" placeholder="Brief description of your site" data-model="seoDescription.value">
|
|
172
|
-
<small class="stx-hint" data-bind-text="seoLength.value"></small>
|
|
173
|
-
</div>
|
|
174
|
-
</div>
|
|
175
|
-
</div>
|
|
176
|
-
</div>
|
|
177
|
-
|
|
178
159
|
<!-- Footer -->
|
|
179
160
|
<div class="tab-panel">
|
|
180
161
|
<div class="stx-card-columns">
|
|
@@ -513,21 +494,6 @@
|
|
|
513
494
|
</div>
|
|
514
495
|
</div>
|
|
515
496
|
|
|
516
|
-
<!-- Analytics -->
|
|
517
|
-
<div class="tab-panel">
|
|
518
|
-
<div class="stx-card">
|
|
519
|
-
<div class="stx-card-title"><span data-icon="bar-chart" data-icon-size="16" aria-hidden="true"></span>Google Analytics
|
|
520
|
-
<span data-help-title="Google Analytics" data-help="Paste the measurement ID of your Google Analytics 4 web stream (Admin > Data streams). Google Analytics sets cookies, so it loads only for visitors who accept Analytics cookies in the cookie banner - the banner is shown whenever an ID is set. Leave it empty to switch Google Analytics off. The built-in Analytics Tool is separate and needs no cookies."></span></div>
|
|
521
|
-
<div class="stx-grid">
|
|
522
|
-
<div class="stx-wide"><label class="form-label" for="stx-ga-id">Measurement ID</label>
|
|
523
|
-
<input id="stx-ga-id" type="text" class="form-input" placeholder="G-XXXXXXXXXX" autocomplete="off" spellcheck="false" data-model="gaId.value">
|
|
524
|
-
<small class="text-danger" data-bind-hidden="!gaIdError.value" data-bind-text="gaIdError.value"></small></div>
|
|
525
|
-
</div>
|
|
526
|
-
<p class="stx-note">Loads only after a visitor accepts Analytics cookies. Visitors who reject them, or have not answered yet, are not sent to Google.</p>
|
|
527
|
-
<p class="stx-note" data-bind-hidden="!gaForcesBanner.value">The cookie banner is switched off under Cookie consent, but it will still be shown while Google Analytics is set - it is how visitors give consent.</p>
|
|
528
|
-
</div>
|
|
529
|
-
</div>
|
|
530
|
-
|
|
531
497
|
<!-- Breadcrumbs -->
|
|
532
498
|
<div class="tab-panel">
|
|
533
499
|
<div class="stx-card-columns">
|
|
@@ -678,6 +644,7 @@
|
|
|
678
644
|
<div class="stx-list" data-each="cacheRows.value key=id"></div>
|
|
679
645
|
</div>
|
|
680
646
|
</div>
|
|
647
|
+
<div class="tab-panel" data-site-search-panel><p role="status">Open Search to load its settings.</p></div>
|
|
681
648
|
</div>
|
|
682
649
|
</div>
|
|
683
650
|
|