domma-cms 1.6.48 → 1.6.50

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/admin/index.html CHANGED
@@ -35,7 +35,7 @@
35
35
  <header id="admin-topbar">
36
36
  <div class="topbar-brand">
37
37
  <span data-icon="layout"></span>
38
- <span class="topbar-brand-text">Domma CMS</span>
38
+ <span class="topbar-brand-text"></span>
39
39
  </div>
40
40
  <div class="topbar-user" id="topbar-user">
41
41
  <!-- Populated after auth: name + role badge -->
package/admin/js/api.js CHANGED
@@ -1 +1 @@
1
- import{loginHashFor as $}from"./lib/login-arrange.js";const c="/api";function a(){return S.get("auth_token")}function u(){return S.get("auth_refresh_token")}function y(e){S.set("auth_token",e)}function f(){S.remove("auth_token"),S.remove("auth_refresh_token"),S.remove("auth_user")}async function T(){const e=u();if(!e)throw new Error("No refresh token");const o=await fetch(`${c}/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(!o.ok)throw f(),sendToLogin({expired:!0}),new Error("Token refresh failed");const{token:n}=await o.json();return y(n),n}async function t(e,o={}){let n=a();const r=i=>({...o.body!==void 0?{"Content-Type":"application/json"}:{},...o.headers,...i?{Authorization:`Bearer ${i}`}:{}});let s=await fetch(`${c}${e}`,{...o,headers:r(n)});if(s.status===401&&u())try{n=await T(),s=await fetch(`${c}${e}`,{...o,headers:r(n)})}catch{return}if(!s.ok){const i=await s.json().catch(()=>({error:"Request failed"})),d=new Error(i.error||i.message||`HTTP ${s.status}`);throw Array.isArray(i.validation)&&(d.validation=i.validation),d.status=s.status,d.body=i,d}return s.status===204?null:s.json()}async function O(e,o){const n=a(),r=n?{Authorization:`Bearer ${n}`}:{},s=await fetch(`${c}${e}`,{method:"POST",headers:r,body:o});if(!s.ok){const i=await s.json().catch(()=>({error:"Upload failed"}));throw new Error(i.error||i.message||`HTTP ${s.status}`)}return s.json()}export const api={auth:{setupStatus:()=>t("/auth/setup-status",{method:"GET"}),setup:e=>t("/auth/setup",{method:"POST",body:JSON.stringify(e)}),login:e=>t("/auth/login",{method:"POST",body:JSON.stringify(e)}),verifyLogin:e=>t("/auth/login/verify",{method:"POST",body:JSON.stringify(e)}),me:()=>t("/auth/me",{method:"GET"}),updateMe:e=>t("/auth/me",{method:"PUT",body:JSON.stringify(e)}),refresh:e=>t("/auth/refresh",{method:"POST",body:JSON.stringify({refreshToken:e})}),forgotPassword:e=>t("/auth/forgot-password",{method:"POST",body:JSON.stringify({email:e})}),checkReset:(e,o)=>t("/auth/reset-password/check",{method:"POST",body:JSON.stringify({token:e,...o&&{password:o}})}),resetPassword:(e,o)=>t("/auth/reset-password",{method:"POST",body:JSON.stringify({token:e,password:o})})},pages:{list:()=>t("/pages",{method:"GET"}),get:e=>t(`/pages${e}`,{method:"GET"}),create:e=>t("/pages",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/pages${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/pages${e}`,{method:"DELETE"}),preview:e=>t("/pages/preview",{method:"POST",body:JSON.stringify({markdown:e})}),previewFull:(e,o,n)=>t("/pages/preview/full",{method:"POST",body:JSON.stringify({urlPath:e,frontmatter:o,body:n})}),previewLinks:{list:e=>t(`/pages/preview-links${e?`?urlPath=${encodeURIComponent(e)}`:""}`,{method:"GET"}).then(o=>o.links||[]),create:(e,o,n)=>t("/pages/preview-links",{method:"POST",body:JSON.stringify({urlPath:e,expiresIn:o,label:n})}),revoke:e=>t(`/pages/preview-links/${e}`,{method:"DELETE"})},tags:()=>t("/pages/tags",{method:"GET"}).then(e=>e.tags||[])},settings:{get:()=>t("/settings",{method:"GET"}),save:e=>t("/settings",{method:"PUT",body:JSON.stringify(e)}),getCustomCss:()=>t("/settings/custom-css",{method:"GET"}),saveCustomCss:e=>t("/settings/custom-css",{method:"PUT",body:JSON.stringify({css:e})})},theme:{get:()=>t("/theme",{method:"GET"}),save:e=>t("/theme",{method:"PUT",body:JSON.stringify(e)}),catalog:()=>t("/theme/catalog",{method:"GET"}),admin:e=>t(`/theme/admin${e?`?id=${encodeURIComponent(e)}`:""}`,{method:"GET"}),themeTokens:e=>t(`/theme/themes/${encodeURIComponent(e)}`,{method:"GET"}),compile:e=>t("/theme/compile",{method:"POST",body:JSON.stringify(e)})},navigation:{get:()=>t("/navigation",{method:"GET"}),save:e=>t("/navigation",{method:"PUT",body:JSON.stringify(e)})},menus:{list:()=>t("/menus",{method:"GET"}),get:e=>t(`/menus/${encodeURIComponent(e)}`,{method:"GET"}),create:e=>t("/menus",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/menus/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/menus/${encodeURIComponent(e)}`,{method:"DELETE"}),duplicate:e=>t(`/menus/${encodeURIComponent(e)}/duplicate`,{method:"POST"})},contextMenus:{list:()=>t("/context-menus",{method:"GET"}),get:e=>t(`/context-menus/${encodeURIComponent(e)}`,{method:"GET"}),create:e=>t("/context-menus",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/context-menus/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/context-menus/${encodeURIComponent(e)}`,{method:"DELETE"}),duplicate:e=>t(`/context-menus/${encodeURIComponent(e)}/duplicate`,{method:"POST"}),actionTypes:()=>t("/context-menus/action-types",{method:"GET"})},menuLocations:{get:()=>t("/menu-locations",{method:"GET"}),save:e=>t("/menu-locations",{method:"PUT",body:JSON.stringify(e)}),registry:()=>t("/menu-locations/registry",{method:"GET"})},projects:{list:()=>t("/projects",{method:"GET"}),listWithCounts:()=>t("/projects?counts=1",{method:"GET"}),get:e=>t(`/projects/${encodeURIComponent(e)}`,{method:"GET"}),create:e=>t("/projects",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/projects/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/projects/${encodeURIComponent(e)}`,{method:"DELETE"}),artefacts:e=>t(`/projects/${encodeURIComponent(e)}/artefacts`,{method:"GET"}),untagAll:e=>t(`/projects/${encodeURIComponent(e)}/untag-all`,{method:"POST"}),removeContents:(e,o)=>t(`/projects/${encodeURIComponent(e)}/remove-contents`,{method:"POST",body:JSON.stringify({confirm:o})})},apiTokens:{list:()=>t("/api-tokens",{method:"GET"}),create:e=>t("/api-tokens",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/api-tokens/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),revoke:e=>t(`/api-tokens/${encodeURIComponent(e)}`,{method:"DELETE"})},apiEndpoints:{list:()=>t("/api-endpoints",{method:"GET"}),get:e=>t(`/api-endpoints/${encodeURIComponent(e)}`,{method:"GET"}),create:e=>t("/api-endpoints",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/api-endpoints/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/api-endpoints/${encodeURIComponent(e)}`,{method:"DELETE"})},layouts:{get:()=>t("/layouts",{method:"GET"}),save:e=>t("/layouts",{method:"PUT",body:JSON.stringify(e)}),create:e=>t("/layouts",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/layouts/${e}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/layouts/${e}`,{method:"DELETE"}),getOptions:()=>t("/layouts/options",{method:"GET"}),saveOptions:e=>t("/layouts/options",{method:"PUT",body:JSON.stringify(e)})},media:{list:()=>t("/media",{method:"GET"}),upload:e=>O("/media",e),delete:e=>t(`/media/${encodeURIComponent(e)}`,{method:"DELETE"}),rename:(e,o)=>t(`/media/${encodeURIComponent(e)}`,{method:"PATCH",body:JSON.stringify({newName:o})}),info:e=>t(`/media/${encodeURIComponent(e)}/info`,{method:"GET"}),transform:(e,o)=>t(`/media/${encodeURIComponent(e)}/transform`,{method:"POST",body:JSON.stringify(o)})},users:{list:()=>t("/users",{method:"GET"}),get:e=>t(`/users/${e}`,{method:"GET"}),create:e=>t("/users",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/users/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/users/${e}`,{method:"DELETE"}),passwordReset:(e,o)=>t(`/users/${encodeURIComponent(e)}/password-reset`,{method:"POST",body:JSON.stringify({via:o})}),cancelPasswordReset:e=>t(`/users/${encodeURIComponent(e)}/password-reset`,{method:"DELETE"})},tools:{list:()=>t("/tools",{method:"GET"}),update:(e,o)=>t(`/tools/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)})},plugins:{list:()=>t("/plugins",{method:"GET"}),scaffold:e=>t("/plugins/scaffold",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/plugins/${e}`,{method:"PUT",body:JSON.stringify(o)}),files:e=>t(`/plugins/${e}/files`,{method:"GET"}),readFile:(e,o)=>t(`/plugins/${e}/file?path=${encodeURIComponent(o)}`,{method:"GET"}),writeFile:(e,o,n)=>t(`/plugins/${e}/file`,{method:"PUT",body:JSON.stringify({path:o,content:n})}),deleteFile:(e,o)=>t(`/plugins/${e}/file?path=${encodeURIComponent(o)}`,{method:"DELETE"}),restart:()=>t("/plugins/restart",{method:"POST"}),adminConfig:()=>t("/plugins/admin-config",{method:"GET"}),async exportBundle(e,{includeData:o=!1}={}){const n=a(),r=o?"?includeData=1":"",s=await fetch(`${c}/plugins/${encodeURIComponent(e)}/export${r}`,{headers:n?{Authorization:`Bearer ${n}`}:{}});if(!s.ok){const E=await s.json().catch(()=>({}));throw new Error(E.error||`Export failed (${s.status})`)}const i=s.headers.get("Content-Disposition")||"",d=/filename="([^"]+)"/.exec(i)?.[1],m=await s.blob(),h=URL.createObjectURL(m),l=document.createElement("a");l.href=h,l.download=d||`${e}.dcmsplugin`,document.body.appendChild(l),l.click(),l.remove(),URL.revokeObjectURL(h)},async installFile(e,{confirmUnsigned:o=!1,confirmUpgrade:n=!1,confirmDowngrade:r=!1}={}){const s=new FormData;o&&s.append("confirmUnsigned","true"),n&&s.append("confirmUpgrade","true"),r&&s.append("confirmDowngrade","true"),s.append("file",e);const i=a(),d=await fetch(`${c}/plugins/install-upload`,{method:"POST",headers:i?{Authorization:`Bearer ${i}`}:{},body:s}),m=await d.json().catch(()=>({}));if(d.status===409){const h=new Error(m.error||"Plugin already installed");throw h.code="CONFLICT",h.name=m.name,h.installedVersion=m.installedVersion,h.incomingVersion=m.incomingVersion,h}if(!d.ok)throw new Error(m.error||`Install failed (${d.status})`);return m}},marketplace:{catalogue:()=>t("/plugins/marketplace",{method:"GET"}),install:(e,o,{update:n=!1}={})=>t("/plugins/marketplace/install",{method:"POST",body:JSON.stringify({slug:e,version:o,update:n})}),uninstall:e=>t(`/plugins/marketplace/${encodeURIComponent(e)}`,{method:"DELETE"})},collections:{list:()=>t("/collections",{method:"GET"}),proStatus:()=>t("/collections/pro-status",{method:"GET"}),get:e=>t(`/collections/${e}`,{method:"GET"}),create:e=>t("/collections",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/collections/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/collections/${e}`,{method:"DELETE"}),listEntries:(e,o={})=>{const n=new URLSearchParams(o).toString();return t(`/collections/${e}/entries${n?"?"+n:""}`,{method:"GET"})},getEntry:(e,o)=>t(`/collections/${e}/entries/${o}`,{method:"GET"}),createEntry:(e,o)=>t(`/collections/${e}/entries`,{method:"POST",body:JSON.stringify({data:o})}),updateEntry:(e,o,n)=>t(`/collections/${e}/entries/${o}`,{method:"PUT",body:JSON.stringify({data:n})}),deleteEntry:(e,o)=>t(`/collections/${e}/entries/${o}`,{method:"DELETE"}),clearEntries:e=>t(`/collections/${e}/entries`,{method:"DELETE"}),import:(e,o)=>t(`/collections/${e}/import`,{method:"POST",body:JSON.stringify({entries:o})}),publicList:(e,o={})=>{const n=new URLSearchParams(o).toString();return t(`/collections/${e}/public${n?"?"+n:""}`,{method:"GET"})},getConnections:()=>t("/collections/connections",{method:"GET"}),saveConnections:e=>t("/collections/connections",{method:"PUT",body:JSON.stringify(e)}),migrateStorage:(e,o)=>t(`/collections/${e}/migrate-storage`,{method:"POST",body:JSON.stringify({storage:o})})},forms:{list:()=>t("/forms",{method:"GET"}),create:e=>t("/forms",{method:"POST",body:JSON.stringify(e)}),get:e=>t(`/forms/${e}`,{method:"GET"}),update:(e,o)=>t(`/forms/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/forms/${e}`,{method:"DELETE"}),listSubmissions:e=>t(`/forms/${e}/submissions`,{method:"GET"}),clearSubmissions:e=>t(`/forms/${e}/submissions`,{method:"DELETE"}),deleteSubmission:(e,o)=>t(`/forms/${e}/submissions/${o}`,{method:"DELETE"}),testEmail:e=>t("/forms/test-email",{method:"POST",body:JSON.stringify({to:e})})},views:{list:()=>t("/views",{method:"GET"}),get:e=>t(`/views/${e}`,{method:"GET"}),create:e=>t("/views",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/views/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/views/${e}`,{method:"DELETE"}),execute:(e,o={})=>{const n=new URLSearchParams(o).toString();return t(`/views/${e}/execute${n?"?"+n:""}`,{method:"GET"})},forCollection:e=>t(`/views/collection/${e}`,{method:"GET"})},actions:{list:()=>t("/actions",{method:"GET"}),get:e=>t(`/actions/${e}`,{method:"GET"}),create:e=>t("/actions",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/actions/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/actions/${e}`,{method:"DELETE"}),execute:(e,o)=>t(`/actions/${e}/execute`,{method:"POST",body:JSON.stringify({entryId:o})}),forCollection:e=>t(`/actions/collection/${e}`,{method:"GET"}),checkAccess:(e,o)=>t(`/actions/${e}/check-access`,{method:"POST",body:JSON.stringify({entryIds:o})})},versions:{list:e=>t(`/versions/list${e}`),get:(e,o)=>t(`/versions/get/${encodeURIComponent(o)}${e}`),create:(e,o)=>t(`/versions/create${e}`,{method:"POST",body:JSON.stringify({label:o})}),restore:(e,o)=>t(`/versions/restore/${encodeURIComponent(o)}${e}`,{method:"POST"}),delete:(e,o)=>t(`/versions/delete/${encodeURIComponent(o)}${e}`,{method:"DELETE"}),bulkDelete:(e,o)=>t(`/versions/bulk-delete${e}`,{method:"POST",body:JSON.stringify({filenames:o})}),prune:(e,o)=>t(`/versions/prune${e}`,{method:"POST",body:JSON.stringify({keep:o})})},blocks:{list:()=>t("/blocks",{method:"GET"}),get:e=>t(`/blocks/${encodeURIComponent(e)}`,{method:"GET"}),put:(e,o)=>t(`/blocks/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/blocks/${encodeURIComponent(e)}`,{method:"DELETE"}),async exportBundle(e){const o=a(),n=await fetch(`${c}/blocks/${encodeURIComponent(e)}/export`,{headers:o?{Authorization:`Bearer ${o}`}:{}});if(!n.ok){const d=await n.json().catch(()=>({}));throw new Error(d.error||`Export failed (${n.status})`)}const r=await n.blob(),s=URL.createObjectURL(r),i=document.createElement("a");i.href=s,i.download=`${e}.dmblock.json`,document.body.appendChild(i),i.click(),i.remove(),URL.revokeObjectURL(s)},async importBundle(e,{overwrite:o=!1}={}){const n=await fetch(`${c}/blocks/import`,{method:"POST",headers:{"Content-Type":"application/json",...a()?{Authorization:`Bearer ${a()}`}:{}},body:JSON.stringify({...e,overwrite:o})});if(n.status===409){const r=await n.json().catch(()=>({})),s=new Error(r.error||"Block already exists");throw s.code="CONFLICT",s.name=r.name,s}if(!n.ok){const r=await n.json().catch(()=>({}));throw new Error(r.error||`Import failed (${n.status})`)}return n.json()}},components:{list:()=>t("/components",{method:"GET"}),get:e=>t(`/components/${encodeURIComponent(e)}`,{method:"GET"}),compile:(e,o)=>t("/components/compile",{method:"POST",body:JSON.stringify({name:e,source:o})}),put:(e,o)=>t(`/components/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/components/${encodeURIComponent(e)}`,{method:"DELETE"}),async exportBundle(e){const o=a(),n=await fetch(`${c}/components/${encodeURIComponent(e)}/export`,{headers:o?{Authorization:`Bearer ${o}`}:{}});if(!n.ok){const d=await n.json().catch(()=>({}));throw new Error(d.error||`Export failed (${n.status})`)}const r=await n.blob(),s=URL.createObjectURL(r),i=document.createElement("a");i.href=s,i.download=`${e}.dmcomponent.json`,document.body.appendChild(i),i.click(),i.remove(),URL.revokeObjectURL(s)},async importBundle(e,{overwrite:o=!1}={}){const n=await fetch(`${c}/components/import`,{method:"POST",headers:{"Content-Type":"application/json",...a()?{Authorization:`Bearer ${a()}`}:{}},body:JSON.stringify({...e,overwrite:o})});if(n.status===409){const r=await n.json().catch(()=>({})),s=new Error(r.error||"Component already exists");throw s.code="CONFLICT",s.name=r.name,s}if(!n.ok){const r=await n.json().catch(()=>({}));throw new Error(r.error||`Import failed (${n.status})`)}return n.json()}},get:e=>t(e,{method:"GET"}),post:(e,o)=>t(e,{method:"POST",body:JSON.stringify(o)}),put:(e,o)=>t(e,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(e,{method:"DELETE"}),settingsExt:{testEmail:e=>t("/settings/test-email",{method:"POST",body:JSON.stringify({to:e})})},system:{notifications:{list:()=>t("/system/notifications",{method:"GET"}),unreadCount:()=>t("/system/notifications/unread-count",{method:"GET"}),markRead:e=>t(`/system/notifications/${e}/read`,{method:"POST"}),dismiss:e=>t(`/system/notifications/${e}/dismiss`,{method:"POST"}),remove:e=>t(`/system/notifications/${e}`,{method:"DELETE"})}},dashboard:{summary:()=>t("/dashboard/summary",{method:"GET"}),summaryLite:()=>t("/dashboard/summary?lite=1",{method:"GET"})}};export function isAuthenticated(){return!!a()}export function getUser(){return S.get("auth_user")}export function setAuthData({token:e,refreshToken:o,user:n}){e&&y(e),o&&S.set("auth_refresh_token",o),n&&S.set("auth_user",n)}let p=!1,g=!1;export function sendToLogin({expired:e=!1}={}){/^#\/(login|reset-password)(\?|\/|$)/.test(location.hash)||(location.hash=$(location.hash,{expired:e}))}export function logout(){!g&&window.M?.subscribe&&(g=!0,M.subscribe("router:afterChange",({to:e}={})=>{/^\/login(\?|$)/.test(e?.path||"")||(p=!1)})),p=!0,R.navigate("/login")}export function takePendingLogout(){return p?(p=!1,b(),!0):!1}function b(){const e=u();e&&fetch(`${c}/auth/logout`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})}).catch(()=>{}),f()}export{t as apiRequest,T as refreshAccessToken,a as getToken,f as clearAuth};
1
+ import{loginHashFor as $}from"./lib/login-arrange.js";const m="/api";function a(){return S.get("auth_token")}function u(){return S.get("auth_refresh_token")}function f(e){S.set("auth_token",e)}function y(){S.remove("auth_token"),S.remove("auth_refresh_token"),S.remove("auth_user")}async function g(){const e=u();if(!e)throw new Error("No refresh token");const o=await fetch(`${m}/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(!o.ok)throw y(),sendToLogin({expired:!0}),new Error("Token refresh failed");const{token:n}=await o.json();return f(n),n}async function t(e,o={}){let n=a();const r=i=>({...o.body!==void 0?{"Content-Type":"application/json"}:{},...o.headers,...i?{Authorization:`Bearer ${i}`}:{}});let s=await fetch(`${m}${e}`,{...o,headers:r(n)});if(s.status===401&&u())try{n=await g(),s=await fetch(`${m}${e}`,{...o,headers:r(n)})}catch{return}if(!s.ok){const i=await s.json().catch(()=>({error:"Request failed"})),d=new Error(i.error||i.message||`HTTP ${s.status}`);throw Array.isArray(i.validation)&&(d.validation=i.validation),d.status=s.status,d.body=i,d}return s.status===204?null:s.json()}async function O(e,o){const n=a(),r=n?{Authorization:`Bearer ${n}`}:{},s=await fetch(`${m}${e}`,{method:"POST",headers:r,body:o});if(!s.ok){const i=await s.json().catch(()=>({error:"Upload failed"}));throw new Error(i.error||i.message||`HTTP ${s.status}`)}return s.json()}export const api={auth:{setupStatus:()=>t("/auth/setup-status",{method:"GET"}),setup:e=>t("/auth/setup",{method:"POST",body:JSON.stringify(e)}),login:e=>t("/auth/login",{method:"POST",body:JSON.stringify(e)}),verifyLogin:e=>t("/auth/login/verify",{method:"POST",body:JSON.stringify(e)}),me:()=>t("/auth/me",{method:"GET"}),updateMe:e=>t("/auth/me",{method:"PUT",body:JSON.stringify(e)}),refresh:e=>t("/auth/refresh",{method:"POST",body:JSON.stringify({refreshToken:e})}),forgotPassword:e=>t("/auth/forgot-password",{method:"POST",body:JSON.stringify({email:e})}),checkReset:(e,o)=>t("/auth/reset-password/check",{method:"POST",body:JSON.stringify({token:e,...o&&{password:o}})}),resetPassword:(e,o)=>t("/auth/reset-password",{method:"POST",body:JSON.stringify({token:e,password:o})})},pages:{list:()=>t("/pages",{method:"GET"}),get:e=>t(`/pages${e}`,{method:"GET"}),create:e=>t("/pages",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/pages${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/pages${e}`,{method:"DELETE"}),preview:e=>t("/pages/preview",{method:"POST",body:JSON.stringify({markdown:e})}),previewFull:(e,o,n)=>t("/pages/preview/full",{method:"POST",body:JSON.stringify({urlPath:e,frontmatter:o,body:n})}),previewLinks:{list:e=>t(`/pages/preview-links${e?`?urlPath=${encodeURIComponent(e)}`:""}`,{method:"GET"}).then(o=>o.links||[]),create:(e,o,n)=>t("/pages/preview-links",{method:"POST",body:JSON.stringify({urlPath:e,expiresIn:o,label:n})}),revoke:e=>t(`/pages/preview-links/${e}`,{method:"DELETE"})},tags:()=>t("/pages/tags",{method:"GET"}).then(e=>e.tags||[])},listTags:{list:e=>t(`/list-tags/${e}`,{method:"GET"}).then(o=>o.tags||[]),add:(e,o)=>t(`/list-tags/${e}`,{method:"POST",body:JSON.stringify({name:o})}),changeItem:(e,o,n)=>t(`/list-tags/${e}/item`,{method:"PUT",body:JSON.stringify({id:o,...n})}).then(r=>r.tags||[]),rename:(e,o,n)=>t(`/list-tags/${e}/rename`,{method:"POST",body:JSON.stringify({from:o,to:n})}),remove:(e,o)=>t(`/list-tags/${e}/delete`,{method:"POST",body:JSON.stringify({name:o})})},settings:{get:()=>t("/settings",{method:"GET"}),save:e=>t("/settings",{method:"PUT",body:JSON.stringify(e)}),getCustomCss:()=>t("/settings/custom-css",{method:"GET"}),saveCustomCss:e=>t("/settings/custom-css",{method:"PUT",body:JSON.stringify({css:e})})},theme:{get:()=>t("/theme",{method:"GET"}),save:e=>t("/theme",{method:"PUT",body:JSON.stringify(e)}),catalog:()=>t("/theme/catalog",{method:"GET"}),admin:e=>t(`/theme/admin${e?`?id=${encodeURIComponent(e)}`:""}`,{method:"GET"}),themeTokens:e=>t(`/theme/themes/${encodeURIComponent(e)}`,{method:"GET"}),compile:e=>t("/theme/compile",{method:"POST",body:JSON.stringify(e)})},navigation:{get:()=>t("/navigation",{method:"GET"}),save:e=>t("/navigation",{method:"PUT",body:JSON.stringify(e)})},menus:{list:()=>t("/menus",{method:"GET"}),get:e=>t(`/menus/${encodeURIComponent(e)}`,{method:"GET"}),create:e=>t("/menus",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/menus/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/menus/${encodeURIComponent(e)}`,{method:"DELETE"}),duplicate:e=>t(`/menus/${encodeURIComponent(e)}/duplicate`,{method:"POST"})},contextMenus:{list:()=>t("/context-menus",{method:"GET"}),get:e=>t(`/context-menus/${encodeURIComponent(e)}`,{method:"GET"}),create:e=>t("/context-menus",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/context-menus/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/context-menus/${encodeURIComponent(e)}`,{method:"DELETE"}),duplicate:e=>t(`/context-menus/${encodeURIComponent(e)}/duplicate`,{method:"POST"}),actionTypes:()=>t("/context-menus/action-types",{method:"GET"})},menuLocations:{get:()=>t("/menu-locations",{method:"GET"}),save:e=>t("/menu-locations",{method:"PUT",body:JSON.stringify(e)}),registry:()=>t("/menu-locations/registry",{method:"GET"})},projects:{list:()=>t("/projects",{method:"GET"}),listWithCounts:()=>t("/projects?counts=1",{method:"GET"}),get:e=>t(`/projects/${encodeURIComponent(e)}`,{method:"GET"}),create:e=>t("/projects",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/projects/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/projects/${encodeURIComponent(e)}`,{method:"DELETE"}),artefacts:e=>t(`/projects/${encodeURIComponent(e)}/artefacts`,{method:"GET"}),untagAll:e=>t(`/projects/${encodeURIComponent(e)}/untag-all`,{method:"POST"}),removeContents:(e,o)=>t(`/projects/${encodeURIComponent(e)}/remove-contents`,{method:"POST",body:JSON.stringify({confirm:o})})},apiTokens:{list:()=>t("/api-tokens",{method:"GET"}),create:e=>t("/api-tokens",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/api-tokens/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),revoke:e=>t(`/api-tokens/${encodeURIComponent(e)}`,{method:"DELETE"})},apiEndpoints:{list:()=>t("/api-endpoints",{method:"GET"}),get:e=>t(`/api-endpoints/${encodeURIComponent(e)}`,{method:"GET"}),create:e=>t("/api-endpoints",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/api-endpoints/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/api-endpoints/${encodeURIComponent(e)}`,{method:"DELETE"})},layouts:{get:()=>t("/layouts",{method:"GET"}),save:e=>t("/layouts",{method:"PUT",body:JSON.stringify(e)}),create:e=>t("/layouts",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/layouts/${e}`,{method:"PUT",body:JSON.stringify(o)}),remove:e=>t(`/layouts/${e}`,{method:"DELETE"}),getOptions:()=>t("/layouts/options",{method:"GET"}),saveOptions:e=>t("/layouts/options",{method:"PUT",body:JSON.stringify(e)})},media:{list:()=>t("/media",{method:"GET"}),upload:e=>O("/media",e),delete:e=>t(`/media/${encodeURIComponent(e)}`,{method:"DELETE"}),rename:(e,o)=>t(`/media/${encodeURIComponent(e)}`,{method:"PATCH",body:JSON.stringify({newName:o})}),info:e=>t(`/media/${encodeURIComponent(e)}/info`,{method:"GET"}),transform:(e,o)=>t(`/media/${encodeURIComponent(e)}/transform`,{method:"POST",body:JSON.stringify(o)})},users:{list:()=>t("/users",{method:"GET"}),get:e=>t(`/users/${e}`,{method:"GET"}),create:e=>t("/users",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/users/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/users/${e}`,{method:"DELETE"}),passwordReset:(e,o)=>t(`/users/${encodeURIComponent(e)}/password-reset`,{method:"POST",body:JSON.stringify({via:o})}),cancelPasswordReset:e=>t(`/users/${encodeURIComponent(e)}/password-reset`,{method:"DELETE"})},tools:{list:()=>t("/tools",{method:"GET"}),update:(e,o)=>t(`/tools/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)})},plugins:{list:()=>t("/plugins",{method:"GET"}),scaffold:e=>t("/plugins/scaffold",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/plugins/${e}`,{method:"PUT",body:JSON.stringify(o)}),files:e=>t(`/plugins/${e}/files`,{method:"GET"}),readFile:(e,o)=>t(`/plugins/${e}/file?path=${encodeURIComponent(o)}`,{method:"GET"}),writeFile:(e,o,n)=>t(`/plugins/${e}/file`,{method:"PUT",body:JSON.stringify({path:o,content:n})}),deleteFile:(e,o)=>t(`/plugins/${e}/file?path=${encodeURIComponent(o)}`,{method:"DELETE"}),restart:()=>t("/plugins/restart",{method:"POST"}),adminConfig:()=>t("/plugins/admin-config",{method:"GET"}),async exportBundle(e,{includeData:o=!1}={}){const n=a(),r=o?"?includeData=1":"",s=await fetch(`${m}/plugins/${encodeURIComponent(e)}/export${r}`,{headers:n?{Authorization:`Bearer ${n}`}:{}});if(!s.ok){const E=await s.json().catch(()=>({}));throw new Error(E.error||`Export failed (${s.status})`)}const i=s.headers.get("Content-Disposition")||"",d=/filename="([^"]+)"/.exec(i)?.[1],c=await s.blob(),h=URL.createObjectURL(c),l=document.createElement("a");l.href=h,l.download=d||`${e}.dcmsplugin`,document.body.appendChild(l),l.click(),l.remove(),URL.revokeObjectURL(h)},async installFile(e,{confirmUnsigned:o=!1,confirmUpgrade:n=!1,confirmDowngrade:r=!1}={}){const s=new FormData;o&&s.append("confirmUnsigned","true"),n&&s.append("confirmUpgrade","true"),r&&s.append("confirmDowngrade","true"),s.append("file",e);const i=a(),d=await fetch(`${m}/plugins/install-upload`,{method:"POST",headers:i?{Authorization:`Bearer ${i}`}:{},body:s}),c=await d.json().catch(()=>({}));if(d.status===409){const h=new Error(c.error||"Plugin already installed");throw h.code="CONFLICT",h.name=c.name,h.installedVersion=c.installedVersion,h.incomingVersion=c.incomingVersion,h}if(!d.ok)throw new Error(c.error||`Install failed (${d.status})`);return c}},marketplace:{catalogue:()=>t("/plugins/marketplace",{method:"GET"}),install:(e,o,{update:n=!1}={})=>t("/plugins/marketplace/install",{method:"POST",body:JSON.stringify({slug:e,version:o,update:n})}),uninstall:e=>t(`/plugins/marketplace/${encodeURIComponent(e)}`,{method:"DELETE"})},collections:{list:()=>t("/collections",{method:"GET"}),proStatus:()=>t("/collections/pro-status",{method:"GET"}),get:e=>t(`/collections/${e}`,{method:"GET"}),create:e=>t("/collections",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/collections/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/collections/${e}`,{method:"DELETE"}),listEntries:(e,o={})=>{const n=new URLSearchParams(o).toString();return t(`/collections/${e}/entries${n?"?"+n:""}`,{method:"GET"})},getEntry:(e,o)=>t(`/collections/${e}/entries/${o}`,{method:"GET"}),createEntry:(e,o)=>t(`/collections/${e}/entries`,{method:"POST",body:JSON.stringify({data:o})}),updateEntry:(e,o,n)=>t(`/collections/${e}/entries/${o}`,{method:"PUT",body:JSON.stringify({data:n})}),deleteEntry:(e,o)=>t(`/collections/${e}/entries/${o}`,{method:"DELETE"}),clearEntries:e=>t(`/collections/${e}/entries`,{method:"DELETE"}),import:(e,o)=>t(`/collections/${e}/import`,{method:"POST",body:JSON.stringify({entries:o})}),publicList:(e,o={})=>{const n=new URLSearchParams(o).toString();return t(`/collections/${e}/public${n?"?"+n:""}`,{method:"GET"})},getConnections:()=>t("/collections/connections",{method:"GET"}),saveConnections:e=>t("/collections/connections",{method:"PUT",body:JSON.stringify(e)}),migrateStorage:(e,o)=>t(`/collections/${e}/migrate-storage`,{method:"POST",body:JSON.stringify({storage:o})})},forms:{list:()=>t("/forms",{method:"GET"}),create:e=>t("/forms",{method:"POST",body:JSON.stringify(e)}),get:e=>t(`/forms/${e}`,{method:"GET"}),update:(e,o)=>t(`/forms/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/forms/${e}`,{method:"DELETE"}),listSubmissions:e=>t(`/forms/${e}/submissions`,{method:"GET"}),clearSubmissions:e=>t(`/forms/${e}/submissions`,{method:"DELETE"}),deleteSubmission:(e,o)=>t(`/forms/${e}/submissions/${o}`,{method:"DELETE"}),testEmail:e=>t("/forms/test-email",{method:"POST",body:JSON.stringify({to:e})})},views:{list:()=>t("/views",{method:"GET"}),get:e=>t(`/views/${e}`,{method:"GET"}),create:e=>t("/views",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/views/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/views/${e}`,{method:"DELETE"}),execute:(e,o={})=>{const n=new URLSearchParams(o).toString();return t(`/views/${e}/execute${n?"?"+n:""}`,{method:"GET"})},forCollection:e=>t(`/views/collection/${e}`,{method:"GET"})},actions:{list:()=>t("/actions",{method:"GET"}),get:e=>t(`/actions/${e}`,{method:"GET"}),create:e=>t("/actions",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/actions/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/actions/${e}`,{method:"DELETE"}),execute:(e,o)=>t(`/actions/${e}/execute`,{method:"POST",body:JSON.stringify({entryId:o})}),forCollection:e=>t(`/actions/collection/${e}`,{method:"GET"}),checkAccess:(e,o)=>t(`/actions/${e}/check-access`,{method:"POST",body:JSON.stringify({entryIds:o})})},versions:{list:e=>t(`/versions/list${e}`),get:(e,o)=>t(`/versions/get/${encodeURIComponent(o)}${e}`),create:(e,o)=>t(`/versions/create${e}`,{method:"POST",body:JSON.stringify({label:o})}),restore:(e,o)=>t(`/versions/restore/${encodeURIComponent(o)}${e}`,{method:"POST"}),delete:(e,o)=>t(`/versions/delete/${encodeURIComponent(o)}${e}`,{method:"DELETE"}),bulkDelete:(e,o)=>t(`/versions/bulk-delete${e}`,{method:"POST",body:JSON.stringify({filenames:o})}),prune:(e,o)=>t(`/versions/prune${e}`,{method:"POST",body:JSON.stringify({keep:o})})},blocks:{list:()=>t("/blocks",{method:"GET"}),get:e=>t(`/blocks/${encodeURIComponent(e)}`,{method:"GET"}),put:(e,o)=>t(`/blocks/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/blocks/${encodeURIComponent(e)}`,{method:"DELETE"}),async exportBundle(e){const o=a(),n=await fetch(`${m}/blocks/${encodeURIComponent(e)}/export`,{headers:o?{Authorization:`Bearer ${o}`}:{}});if(!n.ok){const d=await n.json().catch(()=>({}));throw new Error(d.error||`Export failed (${n.status})`)}const r=await n.blob(),s=URL.createObjectURL(r),i=document.createElement("a");i.href=s,i.download=`${e}.dmblock.json`,document.body.appendChild(i),i.click(),i.remove(),URL.revokeObjectURL(s)},async importBundle(e,{overwrite:o=!1}={}){const n=await fetch(`${m}/blocks/import`,{method:"POST",headers:{"Content-Type":"application/json",...a()?{Authorization:`Bearer ${a()}`}:{}},body:JSON.stringify({...e,overwrite:o})});if(n.status===409){const r=await n.json().catch(()=>({})),s=new Error(r.error||"Block already exists");throw s.code="CONFLICT",s.name=r.name,s}if(!n.ok){const r=await n.json().catch(()=>({}));throw new Error(r.error||`Import failed (${n.status})`)}return n.json()}},components:{list:()=>t("/components",{method:"GET"}),get:e=>t(`/components/${encodeURIComponent(e)}`,{method:"GET"}),compile:(e,o)=>t("/components/compile",{method:"POST",body:JSON.stringify({name:e,source:o})}),put:(e,o)=>t(`/components/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/components/${encodeURIComponent(e)}`,{method:"DELETE"}),async exportBundle(e){const o=a(),n=await fetch(`${m}/components/${encodeURIComponent(e)}/export`,{headers:o?{Authorization:`Bearer ${o}`}:{}});if(!n.ok){const d=await n.json().catch(()=>({}));throw new Error(d.error||`Export failed (${n.status})`)}const r=await n.blob(),s=URL.createObjectURL(r),i=document.createElement("a");i.href=s,i.download=`${e}.dmcomponent.json`,document.body.appendChild(i),i.click(),i.remove(),URL.revokeObjectURL(s)},async importBundle(e,{overwrite:o=!1}={}){const n=await fetch(`${m}/components/import`,{method:"POST",headers:{"Content-Type":"application/json",...a()?{Authorization:`Bearer ${a()}`}:{}},body:JSON.stringify({...e,overwrite:o})});if(n.status===409){const r=await n.json().catch(()=>({})),s=new Error(r.error||"Component already exists");throw s.code="CONFLICT",s.name=r.name,s}if(!n.ok){const r=await n.json().catch(()=>({}));throw new Error(r.error||`Import failed (${n.status})`)}return n.json()}},get:e=>t(e,{method:"GET"}),post:(e,o)=>t(e,{method:"POST",body:JSON.stringify(o)}),put:(e,o)=>t(e,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(e,{method:"DELETE"}),settingsExt:{testEmail:e=>t("/settings/test-email",{method:"POST",body:JSON.stringify({to:e})})},system:{notifications:{list:()=>t("/system/notifications",{method:"GET"}),unreadCount:()=>t("/system/notifications/unread-count",{method:"GET"}),markRead:e=>t(`/system/notifications/${e}/read`,{method:"POST"}),dismiss:e=>t(`/system/notifications/${e}/dismiss`,{method:"POST"}),remove:e=>t(`/system/notifications/${e}`,{method:"DELETE"})}},dashboard:{summary:()=>t("/dashboard/summary",{method:"GET"}),summaryLite:()=>t("/dashboard/summary?lite=1",{method:"GET"})}};export function isAuthenticated(){return!!a()}export function getUser(){return S.get("auth_user")}export function setAuthData({token:e,refreshToken:o,user:n}){e&&f(e),o&&S.set("auth_refresh_token",o),n&&S.set("auth_user",n)}let p=!1,T=!1;export function sendToLogin({expired:e=!1}={}){/^#\/(login|reset-password)(\?|\/|$)/.test(location.hash)||(location.hash=$(location.hash,{expired:e}))}export function logout(){!T&&window.M?.subscribe&&(T=!0,M.subscribe("router:afterChange",({to:e}={})=>{/^\/login(\?|$)/.test(e?.path||"")||(p=!1)})),p=!0,R.navigate("/login")}export function takePendingLogout(){return p?(p=!1,b(),!0):!1}function b(){const e=u();e&&fetch(`${m}/auth/logout`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})}).catch(()=>{}),y()}export{t as apiRequest,g as refreshAccessToken,a as getToken,y as clearAuth};
package/admin/js/app.js CHANGED
@@ -1,4 +1,4 @@
1
- import{views as G}from"./views/index.js";import{withPluginChrome as B}from"./lib/plugin-chrome.js";import{api as c,getUser as F,isAuthenticated as m,logout as z,sendToLogin as K}from"./api.js";import{isAuthScreen as C,loginHashFor as J}from"./lib/login-arrange.js";import{installHttpInterceptor as Y}from"./http-interceptor.js";import{installResizableSlideovers as Q}from"./lib/slideover-resizable.js";import{installActivityThrobber as X}from"./lib/activity-throbber.js";import{installUpdateNotice as Z}from"./lib/update-notice.js";import{isPathAllowed as tt,redirectFor as et}from"./lib/admin-scope.js";import{deliverDesktop as it,desktopActive as ot,notificationSettings as at}from"./lib/desktop-notify.js";import{avatarUrl as st}from"./lib/avatar.js";import{installCalculator as nt}from"./lib/calculator.js";import{takeOverviewPass as lt,withDashboardStrip as O}from"./lib/dashboards-strip.js";import{installHotkeys as rt}from"./lib/hotkeys-ui.js";$(()=>{Y(),nt(),Z(),X(),Q(),(async()=>{const{applyAdminTheme:t,refreshAdminTheme:i}=await import("./lib/admin-theme.js");m()?await i(c,{init:!0}):t({dommaTheme:"charcoal-dark"},{init:!0})})();let p=null,r="/",h=!1,w=null;function b(t){if(y[t])return E.toast(`${y[t]} is switched off on this site.`,{type:"info"}),"/";if(t.split("?")[0]==="/my-dashboard")return"/dashboards";const i=D[t]||et(p,t);return i||((t.split("?")[0]||"/")==="/"&&r&&r!=="/"&&!lt()?D[r]||r:null)}const D={},y={};R.use(async(t,i,e)=>{if(C(t.path))return e();if(!m()){K();return}const o=b(t.path);if(o){R.navigate(o);return}e()});async function g(){try{const t=await c.get("/auth/permissions");p=t.adminScope||null,r=t.adminHome||"/";const i=t.permissions||[];return h=tt(p,"/settings")&&i.some(e=>e==="settings"||e.startsWith("settings.")),w=i.some(e=>e==="notifications"||e.startsWith("notifications.")),i}catch{return p=null,r="/",h=!1,w=null,[]}}document.addEventListener("dm:admin-home-changed",()=>{g()});let N=null,P=!1;document.addEventListener("dm:sidebar-rebuild",()=>{if(N){P=!0;return}N=(async()=>{do{P=!1;try{await A(await g())}catch{}}while(P)})().finally(()=>{N=null})});async function A(t){const{renderAdminSidebar:i}=await import("./lib/sidebar-renderer.js");await i({mount:"#admin-sidebar",permissions:t,adminScope:p})}M.subscribe("router:afterChange",({to:t})=>{C(t.path)&&(u&&(clearInterval(u),u=null),p=null,r="/",h=!1,w=null)});const _=[{path:"/",view:"dashboard",title:"Dashboard - Domma CMS"},{path:"/dashboards",view:"dashboards",title:"Dashboards - Domma CMS"},{path:"/my-dashboard",view:"dashboards",title:"Dashboards - Domma CMS"},{path:"/pages",view:"pages",title:"Pages - Domma CMS"},{path:"/pages/new",view:"pageEditor",title:"New Page - Domma CMS"},{path:"/pages/edit/*",view:"pageEditor",title:"Edit Page - Domma CMS"},{path:"/media",view:"media",title:"Media - Domma CMS"},{path:"/menus",view:"menus",title:"Menus - Domma CMS"},{path:"/context-menus",view:"contextMenus",title:"Context Menus - Domma CMS"},{path:"/context-menus/new",view:"contextMenuEditor",title:"New Context Menu - Domma CMS"},{path:"/context-menus/edit/:slug",view:"contextMenuEditor",title:"Edit Context Menu - Domma CMS"},{path:"/menus/new",view:"menuEditor",title:"New Menu - Domma CMS"},{path:"/menus/edit/:slug",view:"menuEditor",title:"Edit Menu - Domma CMS"},{path:"/menu-locations",view:"menuLocations",title:"Menu Locations - Domma CMS"},{path:"/navigation",view:"menusRedirect",title:"Menus - Domma CMS"},{path:"/layouts",view:"layouts",title:"Layouts - Domma CMS"},{path:"/settings",view:"settings",title:"Settings - Domma CMS"},{path:"/theme",view:"theme",title:"Theme - Domma CMS"},{path:"/users",view:"users",title:"Users - Domma CMS"},{path:"/users/new",view:"userEditor",title:"New User - Domma CMS"},{path:"/users/edit/:id",view:"userEditor",title:"Edit User - Domma CMS"},{path:"/plugins",view:"plugins",title:"Plugins - Domma CMS"},{path:"/plugins/code/:name",view:"pluginCode",title:"Plugin Code - Domma CMS"},{path:"/documentation",view:"documentation",title:"Usage - Domma CMS"},{path:"/tutorials",view:"tutorials",title:"Tutorials - Domma CMS"},{path:"/tutorials/crud",view:"tutorialCrud",title:"Building a CRUD App - Domma CMS"},{path:"/tutorials/plugin",view:"tutorialPlugin",title:"Writing a Tool - Domma CMS"},{path:"/tutorials/forms",view:"tutorialForms",title:"Form Follow-Up - Domma CMS"},{path:"/docs/components",view:"componentsReference",title:"Components Reference - Domma CMS"},{path:"/docs/components-howto",view:"componentsHowto",title:"Components How-To - Domma CMS"},{path:"/docs/components-walkthrough",view:"componentsWalkthrough",title:"Components Walkthrough - Domma CMS"},{path:"/docs/components-rules",view:"componentsRules",title:"Components Rules - Domma CMS"},{path:"/docs/usage/pages",view:"usagePages",title:"Pages - Usage - Domma CMS"},{path:"/docs/usage/media",view:"usageMedia",title:"Media - Usage - Domma CMS"},{path:"/docs/usage/navigation",view:"usageNavigation",title:"Navigation - Usage - Domma CMS"},{path:"/docs/usage/dconfig",view:"usageDconfig",title:"DConfig - Usage - Domma CMS"},{path:"/docs/usage/shortcodes",view:"usageShortcodes",title:"Shortcodes - Usage - Domma CMS"},{path:"/docs/usage/site-settings",view:"usageSiteSettings",title:"Site Settings - Usage - Domma CMS"},{path:"/docs/usage/plugins",view:"usagePlugins",title:"Plugins - Usage - Domma CMS"},{path:"/docs/usage/users-roles",view:"usageUsersRoles",title:"Users & Roles - Usage - Domma CMS"},{path:"/docs/usage/views",view:"usageViews",title:"Views - Usage - Domma CMS"},{path:"/docs/usage/actions",view:"usageActions",title:"Actions - Usage - Domma CMS"},{path:"/docs/usage/cta-shortcode",view:"usageCtaShortcode",title:"CTA Shortcode - Usage - Domma CMS"},{path:"/docs/usage/editions",view:"usageEditions",title:"Editions & Licences - Usage - Domma CMS"},{path:"/docs/usage/collections",view:"usageCollections",title:"Collections & Forms - Usage - Domma CMS"},{path:"/docs/usage/blocks",view:"usageBlocks",title:"Blocks - Usage - Domma CMS"},{path:"/docs/usage/tools",view:"usageTools",title:"Built-in Tools - Usage - Domma CMS"},{path:"/docs/usage/dashboard",view:"usageDashboard",title:"Dashboard - Usage - Domma CMS"},{path:"/docs/plugins",view:"pluginGuides",title:"Plugin Guides - Domma CMS"},{path:"/docs/plugins/*",view:"pluginGuide",title:"Plugin Guide - Domma CMS"},{path:"/docs/api/authentication",view:"apiRefAuthentication",title:"Authentication - API - Domma CMS"},{path:"/docs/api/pages",view:"apiRefPages",title:"Pages - API - Domma CMS"},{path:"/docs/api/settings",view:"apiRefSettings",title:"Settings - API - Domma CMS"},{path:"/docs/api/layouts",view:"apiRefLayouts",title:"Layouts - API - Domma CMS"},{path:"/docs/api/navigation",view:"apiRefNavigation",title:"Navigation - API - Domma CMS"},{path:"/docs/api/media",view:"apiRefMedia",title:"Media - API - Domma CMS"},{path:"/docs/api/users",view:"apiRefUsers",title:"Users - API - Domma CMS"},{path:"/docs/api/plugins",view:"apiRefPlugins",title:"Plugins - API - Domma CMS"},{path:"/docs/api/collections",view:"apiRefCollections",title:"Collections - API - Domma CMS"},{path:"/docs/api/views",view:"apiRefViews",title:"Views API - Domma CMS"},{path:"/docs/api/actions",view:"apiRefActions",title:"Actions API - Domma CMS"},{path:"/docs/api/forms",view:"apiRefForms",title:"Forms API - Domma CMS"},{path:"/docs/api/scaffold",view:"apiRefScaffold",title:"Scaffold API - Domma CMS"},{path:"/docs/api/external",view:"apiRefExternal",title:"External API & Tokens - Domma CMS"},{path:"/docs/api/content",view:"apiRefContent",title:"Content API - Domma CMS"},{path:"/docs/api/builder",view:"apiRefBuilder",title:"API Builder - Domma CMS"},{path:"/api-reference",view:"apiReference",title:"API Reference - Domma CMS"},{path:"/collections",view:"collections",title:"Collections - Domma CMS"},{path:"/collections/new",view:"collectionEditor",title:"New Collection - Domma CMS"},{path:"/collections/edit/:slug",view:"collectionEditor",title:"Edit Collection - Domma CMS"},{path:"/collections/:slug/entries",view:"collectionEntries",title:"Entries - Domma CMS"},{path:"/forms",view:"forms",title:"Forms - Domma CMS"},{path:"/forms/new",view:"formEditor",title:"New Form - Domma CMS"},{path:"/forms/edit/:slug",view:"formEditor",title:"Edit Form - Domma CMS"},{path:"/forms/:slug/submissions",view:"formSubmissions",title:"Submissions - Domma CMS"},{path:"/views",view:"viewsList",title:"Views - Domma CMS"},{path:"/views/new",view:"viewEditor",title:"New View - Domma CMS"},{path:"/views/edit/:slug",view:"viewEditor",title:"Edit View - Domma CMS"},{path:"/views/:slug/preview",view:"viewPreview",title:"View Preview - Domma CMS"},{path:"/actions",view:"actionsList",title:"Actions - Domma CMS"},{path:"/actions/new",view:"actionEditor",title:"New Action - Domma CMS"},{path:"/actions/edit/:slug",view:"actionEditor",title:"Edit Action - Domma CMS"},{path:"/pro/docs",view:"proDocs",title:"Pro Documentation - Domma CMS"},{path:"/blocks",view:"blocks",title:"Blocks - Domma CMS"},{path:"/blocks/new",view:"blockEditor",title:"New Block - Domma CMS"},{path:"/blocks/edit/:name",view:"blockEditor",title:"Edit Block - Domma CMS"},{path:"/components",view:"components",title:"Components - Domma CMS"},{path:"/components/new",view:"componentEditor",title:"New Component - Domma CMS"},{path:"/components/edit/:name",view:"componentEditor",title:"Edit Component - Domma CMS"},{path:"/my-profile",view:"myProfile",title:"My Profile - Domma CMS"},{path:"/roles",view:"roles",title:"Roles & Permissions - Domma CMS"},{path:"/roles/edit/:id",view:"roleEditor",title:"Edit Role - Domma CMS"},{path:"/effects",view:"effects",title:"Effects - Domma CMS"},{path:"/search",view:"search",title:"Search - Domma CMS"},{path:"/content-api",view:"contentApi",title:"Content API - Domma CMS"},{path:"/notes",view:"notes",title:"Notes - Domma CMS"},{path:"/todo",view:"todo",title:"Todo - Domma CMS"},{path:"/calculator",view:"calculator",title:"Calculator - Domma CMS"},{path:"/analytics",view:"analytics",title:"Analytics - Domma CMS"},{path:"/seo",view:"seo",title:"SEO - Domma CMS"},{path:"/seo/:tab",view:"seo",title:"SEO - Domma CMS"},{path:"/contacts",view:"contacts",title:"Contacts - Domma CMS"},{path:"/calls/new",view:"callNew",title:"Call now - Domma CMS"},{path:"/system/notifications",view:"notifications",title:"Notifications - Domma CMS"},{path:"/api-tokens",view:"apiTokens",title:"API Tokens - Domma CMS"},{path:"/api-endpoints",view:"apiEndpoints",title:"API Builder - Domma CMS"},{path:"/api-endpoints/new",view:"apiEndpointEditor",title:"New API Endpoint - Domma CMS"},{path:"/api-endpoints/edit/:id",view:"apiEndpointEditor",title:"Edit API Endpoint - Domma CMS"},{path:"/projects",view:"projects",title:"Projects - Domma CMS"},{path:"/projects/new",view:"projects",title:"New Project - Domma CMS"},{path:"/projects/edit/:slug",view:"projects",title:"Edit Project - Domma CMS"},{path:"/projects/:slug/settings",view:"projectSettings",title:"Project settings - Domma CMS"},{path:"/projects/:slug/pages",view:"pages",title:"Project pages - Domma CMS"},{path:"/projects/:slug/collections",view:"collections",title:"Project collections - Domma CMS"},{path:"/projects/:slug/forms",view:"forms",title:"Project forms - Domma CMS"},{path:"/projects/:slug/actions",view:"actionsList",title:"Project actions - Domma CMS"},{path:"/projects/:slug/menus",view:"menus",title:"Project menus - Domma CMS"},{path:"/projects/:slug/blocks",view:"blocks",title:"Project blocks - Domma CMS"},{path:"/projects/:slug/components",view:"components",title:"Project components - Domma CMS"},{path:"/projects/:slug/views",view:"viewsList",title:"Project views - Domma CMS"},{path:"/projects/:slug/roles",view:"roles",title:"Project roles - Domma CMS"},{path:"/projects/:slug/users",view:"users",title:"Project users - Domma CMS"},{path:"/projects/:slug/apis",view:"apiEndpoints",title:"Project APIs - Domma CMS"},{path:"/projects/:slug",view:"projectDetail",title:"Project - Domma CMS"},{path:"/login",view:"login",title:"Sign in - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}},{path:"/reset-password",view:"login",title:"Reset Password - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}}];M.subscribe("router:afterChange",async({to:t,from:i})=>{if(!C(t.path)){if($("#admin-sidebar").show(),$("#admin-topbar").show(),C(i?.path)){$("#topbar-user-name").remove(),import("./lib/admin-theme.js").then(a=>a.refreshAdminTheme(c)),await I();const e=await g(),o=b(t.path);if(o&&R.navigate(o),A(e),at.read().signInToasts)try{const a=await c.system.notifications.list().catch(()=>[]),n=(Array.isArray(a)?a:[]).filter(s=>s.unread&&["warning","critical"].includes(s.data?.severity)).slice(0,3);for(const s of n){const f=d(s.data?.title||""),U=d((s.data?.body||"").slice(0,120));E.toast(`${f} - ${U}`,{type:s.data?.severity==="critical"?"error":"warning",duration:0})}}catch{}}x()}}),M.subscribe("router:afterChange",()=>{setTimeout(()=>{$(".btn-primary, .btn-danger").length&&Domma.effects.reveal(".btn-primary, .btn-danger",{animation:"fade",stagger:40,duration:300})},50)});const V=["#field-project","#menu-project","#collection-project","#endpoint-project","#action-project","#view-project","#block-project","#component-project","#role-project",'[name="project"]','[name="ownedByProject"]'];function W(t){return/^\/(pages|menus|context-menus|collections|forms|actions|views|blocks|components|users|roles|api-endpoints)\/(new|edit\/)/.test(t)}M.subscribe("router:afterChange",({to:t})=>{const i=t?String(t.path).split("?")[0]:"";if(!t||!W(i))return;const o=new URLSearchParams(location.hash.split("?")[1]||"").get("project");if(!o||!/\/new$/.test(i))return;let a=0;const n=60,s=1500,f=()=>{a+=n;for(const U of V){const v=document.querySelector(U);if(!v)continue;if(Array.from(v.options||[]).find(q=>q.value===o)){v.value=o,v.dispatchEvent(new Event("change",{bubbles:!0}));return}}a<s&&setTimeout(f,n)};setTimeout(f,n)});const T="cms_card_states",j=S.get(T)||{};M.subscribe("router:afterChange",()=>{setTimeout(()=>{$("#view-container .card-collapsible").each(function(){const t=$(this).find(".card-header h2, .card-header h3").first().text().trim();t&&j[t]==="collapsed"&&$(this).addClass("card-collapsed")})},200)}),$("#view-container").on("click",".card-collapsible .card-header",function(t){if($(t.target).closest("button, a").length)return;const i=$(this).closest(".card");i.toggleClass("card-collapsed");const e=$(this).find("h2, h3").first().text().trim();e&&(j[e]=i.hasClass("card-collapsed")?"collapsed":"open",S.set(T,j))}),rt(),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash.startsWith("#/login")||window.location.hash.startsWith("#/reset-password"))return;const i=document.querySelector("#view-container .view-header button.btn-primary");i&&(t.preventDefault(),i.click())});const l={...G},H={notes:{displayName:"Notes",icon:"edit-3"},todo:{displayName:"Todo",icon:"check-square"},calculator:{displayName:"Calculator",icon:"calculator"},analytics:{displayName:"Analytics",icon:"chart-bar"},seo:{displayName:"SEO",icon:"search"},contacts:{displayName:"Contacts",icon:"users"},projects:{displayName:"Projects",icon:"folder"},pages:{displayName:"Pages",icon:"file-text"},media:{displayName:"Media",icon:"image"},menus:{displayName:"Site menus",icon:"menu"},contextMenus:{displayName:"Context Menus",icon:"menu"},theme:{displayName:"Theme",icon:"droplet"},collections:{displayName:"Collections",icon:"database"},forms:{displayName:"Forms",icon:"layout"},viewsList:{displayName:"Views",icon:"eye"},actionsList:{displayName:"Actions",icon:"zap"},apiEndpoints:{displayName:"API Builder",icon:"code"},blocks:{displayName:"Blocks",icon:"box"},components:{displayName:"Components",icon:"component"},notifications:{displayName:"Notifications",icon:"bell"},roles:{displayName:"Roles",icon:"shield"},users:{displayName:"Users",icon:"users"},settings:{displayName:"Site Settings",icon:"settings"},effects:{displayName:"Effects",icon:"sparkles"},layouts:{displayName:"Layouts",icon:"layout"},plugins:{displayName:"Marketplace",icon:"store"},myProfile:{displayName:"My Profile",icon:"user"},apiTokens:{displayName:"API Tokens",icon:"key"},search:{displayName:"Search",icon:"search"},contentApi:{displayName:"Content API",icon:"file-text"},dashboard:{displayName:"Dashboard",icon:"home"},dashboards:{displayName:"Dashboards",icon:"home"},pageEditor:{displayName:"Pages",section:"Edit",icon:"file-text"},menuEditor:{displayName:"Site menus",section:"Edit",icon:"menu"},menuLocations:{displayName:"Site menus",section:"Locations",icon:"map-pin"},contextMenuEditor:{displayName:"Context Menus",section:"Edit",icon:"menu"},collectionEditor:{displayName:"Collections",section:"Edit",icon:"database"},collectionEntries:{displayName:"Collections",section:"Entries",icon:"database"},formEditor:{displayName:"Forms",section:"Edit",icon:"layout"},formSubmissions:{displayName:"Forms",section:"Submissions",icon:"inbox"},viewEditor:{displayName:"Views",section:"Edit",icon:"eye"},viewPreview:{displayName:"Views",section:"Preview",icon:"eye"},actionEditor:{displayName:"Actions",section:"Edit",icon:"zap"},apiEndpointEditor:{displayName:"API Builder",section:"Edit",icon:"code"},blockEditor:{displayName:"Blocks",section:"Edit",icon:"box"},componentEditor:{displayName:"Components",section:"Edit",icon:"component"},roleEditor:{displayName:"Roles",section:"Edit",icon:"shield"},userEditor:{displayName:"Users",section:"Edit",icon:"users"},projectDetail:{displayName:"Projects",section:"Overview",icon:"folder"},projectSettings:{displayName:"Projects",section:"Settings",icon:"folder"},pluginCode:{displayName:"Marketplace",section:"Code",icon:"code"}};for(const[t,i]of Object.entries(H))l[t]&&(l[t]=B(l[t],{plugin:t,licence:"Built in",...i}));l.dashboard=O(l.dashboard);const k=[..._];async function I(){if(m())try{const t=await c.plugins.adminConfig(),i=new Set(k.map(e=>e.path));for(const e of t.routes||[])i.has(e.path)||k.push(e);for(const[e,o]of Object.entries(t.views||{}))try{const a=await import(`/plugins/${o.entry}`);l[e]=B(a[o.exportName],(t.meta||{})[e])}catch{}l.dashboard=O(l.dashboard);for(const[e,{route:o}]of Object.entries(t.takeovers||{}))o&&(D[`/${e}`]=o);for(const[e,{enabled:o,displayName:a}]of Object.entries(t.tools||{}))o===!1&&(y[`/${e}`]=a);for(const{id:e,href:o}of t.css||[])if(!document.getElementById(e)){const a=document.createElement("link");a.id=e,a.rel="stylesheet",a.href=o,document.head.appendChild(a)}}catch{}}function x(){const t=F();if(!t||$("#topbar-user-name").length)return;const e={"super-admin":"Super Admin",admin:"Admin",user:"User"}[t.role]||t.role,o=st(t);$("#topbar-user").html(`
1
+ import{views as K}from"./views/index.js";import{withPluginChrome as F}from"./lib/plugin-chrome.js";import{api as c,getUser as O,isAuthenticated as m,logout as J,sendToLogin as Y}from"./api.js";import{isAuthScreen as b,loginHashFor as Q}from"./lib/login-arrange.js";import{installHttpInterceptor as X}from"./http-interceptor.js";import{installResizableSlideovers as Z}from"./lib/slideover-resizable.js";import{installActivityThrobber as tt}from"./lib/activity-throbber.js";import{installUpdateNotice as et}from"./lib/update-notice.js";import{isPathAllowed as it,redirectFor as ot}from"./lib/admin-scope.js";import{deliverDesktop as at,desktopActive as st,notificationSettings as nt}from"./lib/desktop-notify.js";import{avatarUrl as lt}from"./lib/avatar.js";import{installCalculator as rt}from"./lib/calculator.js";import{takeOverviewPass as ct,withDashboardStrip as _}from"./lib/dashboards-strip.js";import{installHotkeys as mt}from"./lib/hotkeys-ui.js";import{applyAdminBrand as V}from"./lib/admin-brand.js";$(()=>{X(),rt(),et(),tt(),Z(),(async()=>{const{applyAdminTheme:t,refreshAdminTheme:i}=await import("./lib/admin-theme.js");m()?await i(c,{init:!0}):t({dommaTheme:"charcoal-dark"},{init:!0})})();let p=null,h=null,r="/",w=!1,g=null;function D(t){if(N[t])return E.toast(`${N[t]} is switched off on this site.`,{type:"info"}),"/";if(t.split("?")[0]==="/my-dashboard")return"/dashboards";const i=y[t]||ot(p,t);return i||((t.split("?")[0]||"/")==="/"&&r&&r!=="/"&&!ct()?y[r]||r:null)}const y={},N={};R.use(async(t,i,e)=>{if(b(t.path))return e();if(!m()){Y();return}const o=D(t.path);if(o){R.navigate(o);return}e()});async function f(){try{const t=await c.get("/auth/permissions");p=t.adminScope||null,h=t.adminBrand||null,V(h),r=t.adminHome||"/";const i=t.permissions||[];return w=it(p,"/settings")&&i.some(e=>e==="settings"||e.startsWith("settings.")),g=i.some(e=>e==="notifications"||e.startsWith("notifications.")),i}catch{return p=null,r="/",h||V(null),w=!1,g=null,[]}}document.addEventListener("dm:admin-home-changed",()=>{f()});let P=null,A=!1;document.addEventListener("dm:sidebar-rebuild",()=>{if(P){A=!0;return}P=(async()=>{do{A=!1;try{await j(await f())}catch{}}while(A)})().finally(()=>{P=null})});async function j(t){const{renderAdminSidebar:i}=await import("./lib/sidebar-renderer.js");await i({mount:"#admin-sidebar",permissions:t,adminScope:p,brand:h})}M.subscribe("router:afterChange",({to:t})=>{b(t.path)&&(u&&(clearInterval(u),u=null),p=null,r="/",w=!1,g=null)});const W=[{path:"/",view:"dashboard",title:"Dashboard - Domma CMS"},{path:"/dashboards",view:"dashboards",title:"Dashboards - Domma CMS"},{path:"/my-dashboard",view:"dashboards",title:"Dashboards - Domma CMS"},{path:"/pages",view:"pages",title:"Pages - Domma CMS"},{path:"/pages/new",view:"pageEditor",title:"New Page - Domma CMS"},{path:"/pages/edit/*",view:"pageEditor",title:"Edit Page - Domma CMS"},{path:"/media",view:"media",title:"Media - Domma CMS"},{path:"/menus",view:"menus",title:"Menus - Domma CMS"},{path:"/context-menus",view:"contextMenus",title:"Context Menus - Domma CMS"},{path:"/context-menus/new",view:"contextMenuEditor",title:"New Context Menu - Domma CMS"},{path:"/context-menus/edit/:slug",view:"contextMenuEditor",title:"Edit Context Menu - Domma CMS"},{path:"/menus/new",view:"menuEditor",title:"New Menu - Domma CMS"},{path:"/menus/edit/:slug",view:"menuEditor",title:"Edit Menu - Domma CMS"},{path:"/menu-locations",view:"menuLocations",title:"Menu Locations - Domma CMS"},{path:"/navigation",view:"menusRedirect",title:"Menus - Domma CMS"},{path:"/layouts",view:"layouts",title:"Layouts - Domma CMS"},{path:"/settings",view:"settings",title:"Settings - Domma CMS"},{path:"/theme",view:"theme",title:"Theme - Domma CMS"},{path:"/users",view:"users",title:"Users - Domma CMS"},{path:"/users/new",view:"userEditor",title:"New User - Domma CMS"},{path:"/users/edit/:id",view:"userEditor",title:"Edit User - Domma CMS"},{path:"/plugins",view:"plugins",title:"Plugins - Domma CMS"},{path:"/plugins/code/:name",view:"pluginCode",title:"Plugin Code - Domma CMS"},{path:"/documentation",view:"documentation",title:"Usage - Domma CMS"},{path:"/tutorials",view:"tutorials",title:"Tutorials - Domma CMS"},{path:"/tutorials/crud",view:"tutorialCrud",title:"Building a CRUD App - Domma CMS"},{path:"/tutorials/plugin",view:"tutorialPlugin",title:"Writing a Tool - Domma CMS"},{path:"/tutorials/forms",view:"tutorialForms",title:"Form Follow-Up - Domma CMS"},{path:"/docs/components",view:"componentsReference",title:"Components Reference - Domma CMS"},{path:"/docs/components-howto",view:"componentsHowto",title:"Components How-To - Domma CMS"},{path:"/docs/components-walkthrough",view:"componentsWalkthrough",title:"Components Walkthrough - Domma CMS"},{path:"/docs/components-rules",view:"componentsRules",title:"Components Rules - Domma CMS"},{path:"/docs/usage/pages",view:"usagePages",title:"Pages - Usage - Domma CMS"},{path:"/docs/usage/media",view:"usageMedia",title:"Media - Usage - Domma CMS"},{path:"/docs/usage/navigation",view:"usageNavigation",title:"Navigation - Usage - Domma CMS"},{path:"/docs/usage/dconfig",view:"usageDconfig",title:"DConfig - Usage - Domma CMS"},{path:"/docs/usage/shortcodes",view:"usageShortcodes",title:"Shortcodes - Usage - Domma CMS"},{path:"/docs/usage/site-settings",view:"usageSiteSettings",title:"Site Settings - Usage - Domma CMS"},{path:"/docs/usage/plugins",view:"usagePlugins",title:"Plugins - Usage - Domma CMS"},{path:"/docs/usage/users-roles",view:"usageUsersRoles",title:"Users & Roles - Usage - Domma CMS"},{path:"/docs/usage/views",view:"usageViews",title:"Views - Usage - Domma CMS"},{path:"/docs/usage/actions",view:"usageActions",title:"Actions - Usage - Domma CMS"},{path:"/docs/usage/cta-shortcode",view:"usageCtaShortcode",title:"CTA Shortcode - Usage - Domma CMS"},{path:"/docs/usage/editions",view:"usageEditions",title:"Editions & Licences - Usage - Domma CMS"},{path:"/docs/usage/collections",view:"usageCollections",title:"Collections & Forms - Usage - Domma CMS"},{path:"/docs/usage/blocks",view:"usageBlocks",title:"Blocks - Usage - Domma CMS"},{path:"/docs/usage/tools",view:"usageTools",title:"Built-in Tools - Usage - Domma CMS"},{path:"/docs/usage/dashboard",view:"usageDashboard",title:"Dashboard - Usage - Domma CMS"},{path:"/docs/plugins",view:"pluginGuides",title:"Plugin Guides - Domma CMS"},{path:"/docs/plugins/*",view:"pluginGuide",title:"Plugin Guide - Domma CMS"},{path:"/docs/api/authentication",view:"apiRefAuthentication",title:"Authentication - API - Domma CMS"},{path:"/docs/api/pages",view:"apiRefPages",title:"Pages - API - Domma CMS"},{path:"/docs/api/settings",view:"apiRefSettings",title:"Settings - API - Domma CMS"},{path:"/docs/api/layouts",view:"apiRefLayouts",title:"Layouts - API - Domma CMS"},{path:"/docs/api/navigation",view:"apiRefNavigation",title:"Navigation - API - Domma CMS"},{path:"/docs/api/media",view:"apiRefMedia",title:"Media - API - Domma CMS"},{path:"/docs/api/users",view:"apiRefUsers",title:"Users - API - Domma CMS"},{path:"/docs/api/plugins",view:"apiRefPlugins",title:"Plugins - API - Domma CMS"},{path:"/docs/api/collections",view:"apiRefCollections",title:"Collections - API - Domma CMS"},{path:"/docs/api/views",view:"apiRefViews",title:"Views API - Domma CMS"},{path:"/docs/api/actions",view:"apiRefActions",title:"Actions API - Domma CMS"},{path:"/docs/api/forms",view:"apiRefForms",title:"Forms API - Domma CMS"},{path:"/docs/api/scaffold",view:"apiRefScaffold",title:"Scaffold API - Domma CMS"},{path:"/docs/api/external",view:"apiRefExternal",title:"External API & Tokens - Domma CMS"},{path:"/docs/api/content",view:"apiRefContent",title:"Content API - Domma CMS"},{path:"/docs/api/builder",view:"apiRefBuilder",title:"API Builder - Domma CMS"},{path:"/api-reference",view:"apiReference",title:"API Reference - Domma CMS"},{path:"/collections",view:"collections",title:"Collections - Domma CMS"},{path:"/collections/new",view:"collectionEditor",title:"New Collection - Domma CMS"},{path:"/collections/edit/:slug",view:"collectionEditor",title:"Edit Collection - Domma CMS"},{path:"/collections/:slug/entries",view:"collectionEntries",title:"Entries - Domma CMS"},{path:"/forms",view:"forms",title:"Forms - Domma CMS"},{path:"/forms/new",view:"formEditor",title:"New Form - Domma CMS"},{path:"/forms/edit/:slug",view:"formEditor",title:"Edit Form - Domma CMS"},{path:"/forms/:slug/submissions",view:"formSubmissions",title:"Submissions - Domma CMS"},{path:"/views",view:"viewsList",title:"Views - Domma CMS"},{path:"/views/new",view:"viewEditor",title:"New View - Domma CMS"},{path:"/views/edit/:slug",view:"viewEditor",title:"Edit View - Domma CMS"},{path:"/views/:slug/preview",view:"viewPreview",title:"View Preview - Domma CMS"},{path:"/actions",view:"actionsList",title:"Actions - Domma CMS"},{path:"/actions/new",view:"actionEditor",title:"New Action - Domma CMS"},{path:"/actions/edit/:slug",view:"actionEditor",title:"Edit Action - Domma CMS"},{path:"/pro/docs",view:"proDocs",title:"Pro Documentation - Domma CMS"},{path:"/blocks",view:"blocks",title:"Blocks - Domma CMS"},{path:"/blocks/new",view:"blockEditor",title:"New Block - Domma CMS"},{path:"/blocks/edit/:name",view:"blockEditor",title:"Edit Block - Domma CMS"},{path:"/components",view:"components",title:"Components - Domma CMS"},{path:"/components/new",view:"componentEditor",title:"New Component - Domma CMS"},{path:"/components/edit/:name",view:"componentEditor",title:"Edit Component - Domma CMS"},{path:"/my-profile",view:"myProfile",title:"My Profile - Domma CMS"},{path:"/roles",view:"roles",title:"Roles & Permissions - Domma CMS"},{path:"/roles/edit/:id",view:"roleEditor",title:"Edit Role - Domma CMS"},{path:"/effects",view:"effects",title:"Effects - Domma CMS"},{path:"/search",view:"search",title:"Search - Domma CMS"},{path:"/content-api",view:"contentApi",title:"Content API - Domma CMS"},{path:"/notes",view:"notes",title:"Notes - Domma CMS"},{path:"/todo",view:"todo",title:"Todo - Domma CMS"},{path:"/calculator",view:"calculator",title:"Calculator - Domma CMS"},{path:"/analytics",view:"analytics",title:"Analytics - Domma CMS"},{path:"/seo",view:"seo",title:"SEO - Domma CMS"},{path:"/seo/:tab",view:"seo",title:"SEO - Domma CMS"},{path:"/contacts",view:"contacts",title:"Contacts - Domma CMS"},{path:"/calls/new",view:"callNew",title:"Call now - Domma CMS"},{path:"/system/notifications",view:"notifications",title:"Notifications - Domma CMS"},{path:"/api-tokens",view:"apiTokens",title:"API Tokens - Domma CMS"},{path:"/api-endpoints",view:"apiEndpoints",title:"API Builder - Domma CMS"},{path:"/api-endpoints/new",view:"apiEndpointEditor",title:"New API Endpoint - Domma CMS"},{path:"/api-endpoints/edit/:id",view:"apiEndpointEditor",title:"Edit API Endpoint - Domma CMS"},{path:"/projects",view:"projects",title:"Projects - Domma CMS"},{path:"/projects/new",view:"projects",title:"New Project - Domma CMS"},{path:"/projects/edit/:slug",view:"projects",title:"Edit Project - Domma CMS"},{path:"/projects/:slug/settings",view:"projectSettings",title:"Project settings - Domma CMS"},{path:"/projects/:slug/pages",view:"pages",title:"Project pages - Domma CMS"},{path:"/projects/:slug/collections",view:"collections",title:"Project collections - Domma CMS"},{path:"/projects/:slug/forms",view:"forms",title:"Project forms - Domma CMS"},{path:"/projects/:slug/actions",view:"actionsList",title:"Project actions - Domma CMS"},{path:"/projects/:slug/menus",view:"menus",title:"Project menus - Domma CMS"},{path:"/projects/:slug/blocks",view:"blocks",title:"Project blocks - Domma CMS"},{path:"/projects/:slug/components",view:"components",title:"Project components - Domma CMS"},{path:"/projects/:slug/views",view:"viewsList",title:"Project views - Domma CMS"},{path:"/projects/:slug/roles",view:"roles",title:"Project roles - Domma CMS"},{path:"/projects/:slug/users",view:"users",title:"Project users - Domma CMS"},{path:"/projects/:slug/apis",view:"apiEndpoints",title:"Project APIs - Domma CMS"},{path:"/projects/:slug",view:"projectDetail",title:"Project - Domma CMS"},{path:"/login",view:"login",title:"Sign in - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}},{path:"/reset-password",view:"login",title:"Reset Password - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}}];M.subscribe("router:afterChange",async({to:t,from:i})=>{if(!b(t.path)){if($("#admin-sidebar").show(),$("#admin-topbar").show(),b(i?.path)){$("#topbar-user-name").remove(),import("./lib/admin-theme.js").then(a=>a.refreshAdminTheme(c)),await U();const e=await f(),o=D(t.path);if(o&&R.navigate(o),j(e),nt.read().signInToasts)try{const a=await c.system.notifications.list().catch(()=>[]),n=(Array.isArray(a)?a:[]).filter(s=>s.unread&&["warning","critical"].includes(s.data?.severity)).slice(0,3);for(const s of n){const v=d(s.data?.title||""),T=d((s.data?.body||"").slice(0,120));E.toast(`${v} - ${T}`,{type:s.data?.severity==="critical"?"error":"warning",duration:0})}}catch{}}L()}}),M.subscribe("router:afterChange",()=>{setTimeout(()=>{$(".btn-primary, .btn-danger").length&&Domma.effects.reveal(".btn-primary, .btn-danger",{animation:"fade",stagger:40,duration:300})},50)});const H=["#field-project","#menu-project","#collection-project","#endpoint-project","#action-project","#view-project","#block-project","#component-project","#role-project",'[name="project"]','[name="ownedByProject"]'];function q(t){return/^\/(pages|menus|context-menus|collections|forms|actions|views|blocks|components|users|roles|api-endpoints)\/(new|edit\/)/.test(t)}M.subscribe("router:afterChange",({to:t})=>{const i=t?String(t.path).split("?")[0]:"";if(!t||!q(i))return;const o=new URLSearchParams(location.hash.split("?")[1]||"").get("project");if(!o||!/\/new$/.test(i))return;let a=0;const n=60,s=1500,v=()=>{a+=n;for(const T of H){const C=document.querySelector(T);if(!C)continue;if(Array.from(C.options||[]).find(z=>z.value===o)){C.value=o,C.dispatchEvent(new Event("change",{bubbles:!0}));return}}a<s&&setTimeout(v,n)};setTimeout(v,n)});const x="cms_card_states",k=S.get(x)||{};M.subscribe("router:afterChange",()=>{setTimeout(()=>{$("#view-container .card-collapsible").each(function(){const t=$(this).find(".card-header h2, .card-header h3").first().text().trim();t&&k[t]==="collapsed"&&$(this).addClass("card-collapsed")})},200)}),$("#view-container").on("click",".card-collapsible .card-header",function(t){if($(t.target).closest("button, a").length)return;const i=$(this).closest(".card");i.toggleClass("card-collapsed");const e=$(this).find("h2, h3").first().text().trim();e&&(k[e]=i.hasClass("card-collapsed")?"collapsed":"open",S.set(x,k))}),mt(),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash.startsWith("#/login")||window.location.hash.startsWith("#/reset-password"))return;const i=document.querySelector("#view-container .view-header button.btn-primary");i&&(t.preventDefault(),i.click())});const l={...K},G={notes:{displayName:"Notes",icon:"edit-3"},todo:{displayName:"Todo",icon:"check-square"},calculator:{displayName:"Calculator",icon:"calculator"},analytics:{displayName:"Analytics",icon:"chart-bar"},seo:{displayName:"SEO",icon:"search"},contacts:{displayName:"Contacts",icon:"users"},projects:{displayName:"Projects",icon:"folder"},pages:{displayName:"Pages",icon:"file-text"},media:{displayName:"Media",icon:"image"},menus:{displayName:"Site menus",icon:"menu"},contextMenus:{displayName:"Context Menus",icon:"menu"},theme:{displayName:"Theme",icon:"droplet"},collections:{displayName:"Collections",icon:"database"},forms:{displayName:"Forms",icon:"layout"},viewsList:{displayName:"Views",icon:"eye"},actionsList:{displayName:"Actions",icon:"zap"},apiEndpoints:{displayName:"API Builder",icon:"code"},blocks:{displayName:"Blocks",icon:"box"},components:{displayName:"Components",icon:"component"},notifications:{displayName:"Notifications",icon:"bell"},roles:{displayName:"Roles",icon:"shield"},users:{displayName:"Users",icon:"users"},settings:{displayName:"Site Settings",icon:"settings"},effects:{displayName:"Effects",icon:"sparkles"},layouts:{displayName:"Layouts",icon:"layout"},plugins:{displayName:"Marketplace",icon:"store"},myProfile:{displayName:"My Profile",icon:"user"},apiTokens:{displayName:"API Tokens",icon:"key"},search:{displayName:"Search",icon:"search"},contentApi:{displayName:"Content API",icon:"file-text"},dashboard:{displayName:"Dashboard",icon:"home"},dashboards:{displayName:"Dashboards",icon:"home"},pageEditor:{displayName:"Pages",section:"Edit",icon:"file-text"},menuEditor:{displayName:"Site menus",section:"Edit",icon:"menu"},menuLocations:{displayName:"Site menus",section:"Locations",icon:"map-pin"},contextMenuEditor:{displayName:"Context Menus",section:"Edit",icon:"menu"},collectionEditor:{displayName:"Collections",section:"Edit",icon:"database"},collectionEntries:{displayName:"Collections",section:"Entries",icon:"database"},formEditor:{displayName:"Forms",section:"Edit",icon:"layout"},formSubmissions:{displayName:"Forms",section:"Submissions",icon:"inbox"},viewEditor:{displayName:"Views",section:"Edit",icon:"eye"},viewPreview:{displayName:"Views",section:"Preview",icon:"eye"},actionEditor:{displayName:"Actions",section:"Edit",icon:"zap"},apiEndpointEditor:{displayName:"API Builder",section:"Edit",icon:"code"},blockEditor:{displayName:"Blocks",section:"Edit",icon:"box"},componentEditor:{displayName:"Components",section:"Edit",icon:"component"},roleEditor:{displayName:"Roles",section:"Edit",icon:"shield"},userEditor:{displayName:"Users",section:"Edit",icon:"users"},projectDetail:{displayName:"Projects",section:"Overview",icon:"folder"},projectSettings:{displayName:"Projects",section:"Settings",icon:"folder"},pluginCode:{displayName:"Marketplace",section:"Code",icon:"code"}};for(const[t,i]of Object.entries(G))l[t]&&(l[t]=F(l[t],{plugin:t,licence:"Built in",...i}));l.dashboard=_(l.dashboard);const I=[...W];async function U(){if(m())try{const t=await c.plugins.adminConfig(),i=new Set(I.map(e=>e.path));for(const e of t.routes||[])i.has(e.path)||I.push(e);for(const[e,o]of Object.entries(t.views||{}))try{const a=await import(`/plugins/${o.entry}`);l[e]=F(a[o.exportName],(t.meta||{})[e])}catch{}l.dashboard=_(l.dashboard);for(const[e,{route:o}]of Object.entries(t.takeovers||{}))o&&(y[`/${e}`]=o);for(const[e,{enabled:o,displayName:a}]of Object.entries(t.tools||{}))o===!1&&(N[`/${e}`]=a);for(const{id:e,href:o}of t.css||[])if(!document.getElementById(e)){const a=document.createElement("link");a.id=e,a.rel="stylesheet",a.href=o,document.head.appendChild(a)}}catch{}}function L(){const t=O();if(!t||$("#topbar-user-name").length)return;const e={"super-admin":"Super Admin",admin:"Admin",user:"User"}[t.role]||t.role,o=lt(t);$("#topbar-user").html(`
2
2
  ${o?`<img id="topbar-avatar" class="topbar-avatar" src="${d(o)}" alt="">`:""}
3
3
  <span id="topbar-user-name" class="topbar-user-name">${d(t.name)}</span>
4
4
  <span class="topbar-role-badge topbar-role-badge--${d(t.role)}">${e}</span>
@@ -20,4 +20,4 @@ import{views as G}from"./views/index.js";import{withPluginChrome as B}from"./lib
20
20
  <span data-icon="log-out"></span>
21
21
  <span>Sign out</span>
22
22
  </a>
23
- `);const a=$("#topbar-bell").get(0);a&&(a.hidden=!0);const n=$("#topbar-settings").get(0);n&&(n.hidden=!h),$("#topbar-logout-btn").on("click",s=>{s.preventDefault(),z()}),Domma.icons.scan("#admin-topbar"),E.tooltip("#topbar-actions [data-tooltip]",{placement:"bottom"}),L(),u||(u=setInterval(L,6e4))}function d(t){return String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}let u=null;async function L(){if(!(!m()||w===!1))try{const{count:t}=await c.system.notifications.unreadCount();$("#topbar-bell").removeAttr("hidden");const i=$("#topbar-bell-badge");t>0?i.text(t>99?"99+":String(t)).removeAttr("hidden"):i.attr("hidden","").text(""),t>0&&ot()&&it(await c.system.notifications.list().catch(()=>[]))}catch{}}(async()=>{if(!m()&&!window.location.hash.startsWith("#/reset-password"))window.location.hash.startsWith("#/login")||(window.location.hash=J(window.location.hash));else if(await I(),F()){const a=await g(),n=(window.location.hash||"#/").slice(1)||"/",s=b(n);s&&(window.location.hash=`#${s}`),await A(a),x()}const t=R._extractParams.bind(R);R._extractParams=function(o,a){const n=a.indexOf("?");if(n!==-1&&(a=a.slice(0,n)),o.endsWith("/*")){const s=o.slice(0,-2);return a.startsWith(s+"/")?{}:null}return t(o,a)};const i=R._renderNotFound.bind(R);let e="";R._renderNotFound=async function(o){return m()&&e!==o&&(e=o,await I(),R._matchRoute(o).route)?(e="",R._handleRouteChange()):(e="",m()&&$("#admin-sidebar").get(0)?.style.display==="none"?(E.toast("That link goes nowhere - here is your home page.",{type:"warning"}),R.navigate("/")):i(o))},R.init({container:"#view-container",routes:k,views:l,default:"/",transitions:{enter:"fadeIn",leave:"fadeOut",duration:150}})})()});
23
+ `);const a=$("#topbar-bell").get(0);a&&(a.hidden=!0);const n=$("#topbar-settings").get(0);n&&(n.hidden=!w),$("#topbar-logout-btn").on("click",s=>{s.preventDefault(),J()}),Domma.icons.scan("#admin-topbar"),E.tooltip("#topbar-actions [data-tooltip]",{placement:"bottom"}),B(),u||(u=setInterval(B,6e4))}function d(t){return String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}let u=null;async function B(){if(!(!m()||g===!1))try{const{count:t}=await c.system.notifications.unreadCount();$("#topbar-bell").removeAttr("hidden");const i=$("#topbar-bell-badge");t>0?i.text(t>99?"99+":String(t)).removeAttr("hidden"):i.attr("hidden","").text(""),t>0&&st()&&at(await c.system.notifications.list().catch(()=>[]))}catch{}}(async()=>{if(!m()&&!window.location.hash.startsWith("#/reset-password"))window.location.hash.startsWith("#/login")||(window.location.hash=Q(window.location.hash));else if(await U(),O()){const a=await f(),n=(window.location.hash||"#/").slice(1)||"/",s=D(n);s&&(window.location.hash=`#${s}`),await j(a),L()}const t=R._extractParams.bind(R);R._extractParams=function(o,a){const n=a.indexOf("?");if(n!==-1&&(a=a.slice(0,n)),o.endsWith("/*")){const s=o.slice(0,-2);return a.startsWith(s+"/")?{}:null}return t(o,a)};const i=R._renderNotFound.bind(R);let e="";R._renderNotFound=async function(o){return m()&&e!==o&&(e=o,await U(),R._matchRoute(o).route)?(e="",R._handleRouteChange()):(e="",m()&&$("#admin-sidebar").get(0)?.style.display==="none"?(E.toast("That link goes nowhere - here is your home page.",{type:"warning"}),R.navigate("/")):i(o))},R.init({container:"#view-container",routes:I,views:l,default:"/",transitions:{enter:"fadeIn",leave:"fadeOut",duration:150}})})()});
@@ -0,0 +1 @@
1
+ export const PRODUCT_NAME="Domma CMS";let n=null,s="",u=null;export function currentAdminBrand(){return n}export function brandedTabTitle(t,c,i=""){const o=l=>String(l||"").replace(/[ \t\n\f\r]+/g," ").trim(),e=o(c?.title),r=o(t);if(!e)return String(t||"");const m=l=>l&&(r===l||r.endsWith(` - ${l}`)),a=[e,o(i),PRODUCT_NAME].filter(m).sort((l,f)=>f.length-l.length)[0];return!a||a===e?String(t||""):r===a?e:r.slice(0,-a.length)+e}export function applyAdminBrand(t){const c=t&&t.title?t:{title:PRODUCT_NAME};n&&n.title!==c.title&&(s=n.title,u=null),n=c;const i=document.querySelector("#admin-topbar .topbar-brand");if(i){const o=String(n.logo||"");let e;/^(?:\/(?!\/)|https?:\/\/)/i.test(o)?(e=document.createElement("img"),e.src=o,e.alt="",e.className="topbar-brand-logo"):(e=document.createElement("span"),e.setAttribute("data-icon",n.icon||"layout"));const r=document.createElement("span");r.className="topbar-brand-text",r.textContent=n.title,i.replaceChildren(e,r);try{I.scan(i)}catch{}}p(),retitleTab()}export function retitleTab(){if(!n||u!==null&&document.title===u)return;const t=brandedTabTitle(document.title,n,s);t!==document.title&&(document.title=t,u=document.title)}let d=!1;function p(){if(d||typeof MutationObserver>"u")return;let t=document.querySelector("head > title");t||(t=document.createElement("title"),document.head.appendChild(t)),new MutationObserver(retitleTab).observe(t,{childList:!0,characterData:!0,subtree:!0}),d=!0}
@@ -1,4 +1,4 @@
1
- import{fmtDate as M}from"./dates.js";export const NAME_RE=/^[a-z0-9][a-z0-9-]*$/,MAX_CSS=50*1024,FORM_FIELDS=["name","project","bundled","content","css"];export function formFromBlock(e,{project:r=""}={}){return e?{name:String(e.name||""),project:String(e.meta?.project||""),bundled:e.bundled===!0,content:String(e.content??""),css:String(e.css??"")}:{name:"",project:String(r||""),bundled:!1,content:"",css:""}}export function snapshot(e){const r=e||{};return JSON.stringify(FORM_FIELDS.map(n=>n==="bundled"?r[n]===!0:String(r[n]??"")))}export function checkForm(e,{isNew:r,taken:n=[]}={}){const t=String(e?.name||"").trim(),o={};r&&(t?NAME_RE.test(t)?n.includes(t)&&(o.name=`"${t}" is already a block - open it from the list, or choose another name.`):o.name="Lowercase letters, digits and hyphens, starting with a letter or digit.":o.name="A block needs a name.");const i=String(e?.css??"");return i.length>MAX_CSS&&(o.css=`The CSS is ${Math.ceil(i.length/1024)} KB - a block's CSS is limited to 50 KB.`),{errors:o,name:t}}export function payloadFromForm(e,r={}){return{content:String(e?.content??""),bundled:e?.bundled===!0,css:String(e?.css??""),meta:{...r||{},project:String(e?.project||"")||null}}}export function extractPlaceholders(e){const r=new Set;for(const[,n]of y(e).matchAll(/\{\{([\w_]+)(?::[^{}]+)?\}\}/g))r.add(n);return[...r]}export function rawPlaceholders(e){return new Set([...y(e).matchAll(/\{\{\{(\w+)\}\}\}/g)].map(r=>r[1]))}const y=e=>String(e??"").replace(/<!--[\s\S]*?-->/g,""),d=e=>String(e??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;");export{d as escapeHtml};export function formatDate(e,r){if(e==null||e==="")return null;const n=e instanceof Date?e:new Date(e);if(Number.isNaN(n.getTime()))return null;const t=(a,l=2)=>String(a).padStart(l,"0"),o=["January","February","March","April","May","June","July","August","September","October","November","December"],i=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],s=()=>(n.getHours()+11)%12+1,c={YYYY:()=>n.getFullYear(),YY:()=>t(n.getFullYear()%100),MMMM:()=>o[n.getMonth()],MMM:()=>o[n.getMonth()].slice(0,3),MM:()=>t(n.getMonth()+1),M:()=>n.getMonth()+1,DD:()=>t(n.getDate()),D:()=>n.getDate(),dddd:()=>i[n.getDay()],ddd:()=>i[n.getDay()].slice(0,3),HH:()=>t(n.getHours()),H:()=>n.getHours(),hh:()=>t(s()),h:()=>s(),mm:()=>t(n.getMinutes()),m:()=>n.getMinutes(),ss:()=>t(n.getSeconds()),s:()=>n.getSeconds(),A:()=>n.getHours()<12?"AM":"PM",a:()=>n.getHours()<12?"am":"pm"};return String(r).replace(/YYYY|YY|MMMM|MMM|MM|M|DD|D|dddd|ddd|HH|H|hh|h|mm|m|ss|s|A|a/g,a=>String(c[a]()))}const S={icon:"box","facebook-icon":"facebook","instagram-icon":"instagram"};export const SAMPLE_IMAGE="data:image/svg+xml,"+encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="800" height="500" viewBox="0 0 800 500"><rect width="800" height="500" fill="#94a3b8"/><path d="M0 500 L260 230 L420 390 L540 290 L800 500 Z" fill="#64748b"/><circle cx="610" cy="140" r="52" fill="#cbd5e1"/></svg>');const v=/(image|img|photo|picture|avatar|logo|thumb|thumbnail|cover|banner)(url|src)?$/;export function sampleValue(e,r=new Date,{raw:n=!1}={}){const t=String(e).toLowerCase();return S[t]?S[t]:/(class|style)$/.test(t)?"":t==="_id"?"a1b2c3d4-e5f6-7890-abcd-ef1234567890":t==="_createdat"||t==="_updatedat"?r.toISOString():t.replace(/update(?!d)/g,"").includes("date")||/(published|created|updated)at$/.test(t)?M(r):v.test(t)?n?t.includes("avatar")?`<img src="${SAMPLE_IMAGE}" alt="" width="40" height="40">`:`<img src="${SAMPLE_IMAGE}" alt="">`:SAMPLE_IMAGE:/(url|href|link)$/.test(t)?"#":t.includes("email")?"user@example.com":t.includes("phone")?"+44 7700 900000":t.includes("name")?"Jane Smith":t.includes("title")?"Sample Title":["message","content","description","summary","excerpt"].some(o=>t.includes(o))?"This is a sample value for preview purposes.":t.includes("rating")?"excellent":t.includes("status")?"active":t.includes("priority")?"high":t.includes("tag")?"tag1, tag2":t.includes("subject")?"Sample Subject":t.includes("categor")?n?'<a href="#">General</a>':"general":`Sample ${e}`}export const scopeName=e=>String(e||"").trim()||"preview",PREVIEW_REPEATS=[1,3];export function renderPreview(e,r={},{repeat:n=1,sanitize:t=null}={}){if(typeof t!="function")return"";const o=u=>r[u]??`[${u}]`,i=String(e?.content??"").replace(/<!--[\s\S]*?-->/g,"").replace(/\{\{\{(\w+)\}\}\}/g,(u,g)=>String(o(g))).replace(/\{\{([\w_]+)(?::([^{}]+))?\}\}/g,(u,g,p)=>{const f=o(g);if(p){const h=formatDate(f,p);if(h!==null)return d(h)}return d(f)}),s=d(scopeName(e?.name)),c=String(e?.css??""),a=c.trim()?`<style>[data-block="${s}"] { ${c.replace(/<\/style/gi,"<\\/style")} }</style>`:"",l=PREVIEW_REPEATS.includes(Number(n))?Number(n):1,m=String(t(i)??"");if(l===1)return`${a}<div class="dm-static-block" data-block="${s}">${m}</div>`;const b=`<div data-block="${s}">${m}</div>`;return`${a}<div class="dm-collection-display dm-collection-blocks grid grid-cols-1 grid-cols-md-2 grid-cols-lg-3 gap-4">`+Array.from({length:l},()=>b).join("")+"</div>"}export function previewBridge(e,r){const n=JSON.stringify(Number(e)||0),t=JSON.stringify(String(r)).replace(/</g,"\\u003c");return`<script>(function () {
1
+ import{fmtDate as M}from"./dates.js";export const NAME_RE=/^[a-z0-9][a-z0-9-]*$/,MAX_CSS=50*1024,FORM_FIELDS=["name","project","bundled","content","css"];export function formFromBlock(e,{project:r=""}={}){return e?{name:String(e.name||""),project:String(e.meta?.project||""),bundled:e.bundled===!0,content:String(e.content??""),css:String(e.css??"")}:{name:"",project:String(r||""),bundled:!1,content:"",css:""}}export function snapshot(e){const r=e||{};return JSON.stringify(FORM_FIELDS.map(n=>n==="bundled"?r[n]===!0:String(r[n]??"")))}export function checkForm(e,{isNew:r,taken:n=[]}={}){const t=String(e?.name||"").trim(),o={};r&&(t?NAME_RE.test(t)?n.includes(t)&&(o.name=`"${t}" is already a block - open it from the list, or choose another name.`):o.name="Lowercase letters, digits and hyphens, starting with a letter or digit.":o.name="A block needs a name.");const i=String(e?.css??"");return i.length>MAX_CSS&&(o.css=`The CSS is ${Math.ceil(i.length/1024)} KB - a block's CSS is limited to 50 KB.`),{errors:o,name:t}}export function payloadFromForm(e,r={}){const{adminTags:n,...t}=r||{};return{content:String(e?.content??""),bundled:e?.bundled===!0,css:String(e?.css??""),meta:{...t,project:String(e?.project||"")||null}}}export function extractPlaceholders(e){const r=new Set;for(const[,n]of y(e).matchAll(/\{\{([\w_]+)(?::[^{}]+)?\}\}/g))r.add(n);return[...r]}export function rawPlaceholders(e){return new Set([...y(e).matchAll(/\{\{\{(\w+)\}\}\}/g)].map(r=>r[1]))}const y=e=>String(e??"").replace(/<!--[\s\S]*?-->/g,""),d=e=>String(e??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;");export{d as escapeHtml};export function formatDate(e,r){if(e==null||e==="")return null;const n=e instanceof Date?e:new Date(e);if(Number.isNaN(n.getTime()))return null;const t=(a,l=2)=>String(a).padStart(l,"0"),o=["January","February","March","April","May","June","July","August","September","October","November","December"],i=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],s=()=>(n.getHours()+11)%12+1,c={YYYY:()=>n.getFullYear(),YY:()=>t(n.getFullYear()%100),MMMM:()=>o[n.getMonth()],MMM:()=>o[n.getMonth()].slice(0,3),MM:()=>t(n.getMonth()+1),M:()=>n.getMonth()+1,DD:()=>t(n.getDate()),D:()=>n.getDate(),dddd:()=>i[n.getDay()],ddd:()=>i[n.getDay()].slice(0,3),HH:()=>t(n.getHours()),H:()=>n.getHours(),hh:()=>t(s()),h:()=>s(),mm:()=>t(n.getMinutes()),m:()=>n.getMinutes(),ss:()=>t(n.getSeconds()),s:()=>n.getSeconds(),A:()=>n.getHours()<12?"AM":"PM",a:()=>n.getHours()<12?"am":"pm"};return String(r).replace(/YYYY|YY|MMMM|MMM|MM|M|DD|D|dddd|ddd|HH|H|hh|h|mm|m|ss|s|A|a/g,a=>String(c[a]()))}const S={icon:"box","facebook-icon":"facebook","instagram-icon":"instagram"};export const SAMPLE_IMAGE="data:image/svg+xml,"+encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="800" height="500" viewBox="0 0 800 500"><rect width="800" height="500" fill="#94a3b8"/><path d="M0 500 L260 230 L420 390 L540 290 L800 500 Z" fill="#64748b"/><circle cx="610" cy="140" r="52" fill="#cbd5e1"/></svg>');const v=/(image|img|photo|picture|avatar|logo|thumb|thumbnail|cover|banner)(url|src)?$/;export function sampleValue(e,r=new Date,{raw:n=!1}={}){const t=String(e).toLowerCase();return S[t]?S[t]:/(class|style)$/.test(t)?"":t==="_id"?"a1b2c3d4-e5f6-7890-abcd-ef1234567890":t==="_createdat"||t==="_updatedat"?r.toISOString():t.replace(/update(?!d)/g,"").includes("date")||/(published|created|updated)at$/.test(t)?M(r):v.test(t)?n?t.includes("avatar")?`<img src="${SAMPLE_IMAGE}" alt="" width="40" height="40">`:`<img src="${SAMPLE_IMAGE}" alt="">`:SAMPLE_IMAGE:/(url|href|link)$/.test(t)?"#":t.includes("email")?"user@example.com":t.includes("phone")?"+44 7700 900000":t.includes("name")?"Jane Smith":t.includes("title")?"Sample Title":["message","content","description","summary","excerpt"].some(o=>t.includes(o))?"This is a sample value for preview purposes.":t.includes("rating")?"excellent":t.includes("status")?"active":t.includes("priority")?"high":t.includes("tag")?"tag1, tag2":t.includes("subject")?"Sample Subject":t.includes("categor")?n?'<a href="#">General</a>':"general":`Sample ${e}`}export const scopeName=e=>String(e||"").trim()||"preview",PREVIEW_REPEATS=[1,3];export function renderPreview(e,r={},{repeat:n=1,sanitize:t=null}={}){if(typeof t!="function")return"";const o=u=>r[u]??`[${u}]`,i=String(e?.content??"").replace(/<!--[\s\S]*?-->/g,"").replace(/\{\{\{(\w+)\}\}\}/g,(u,g)=>String(o(g))).replace(/\{\{([\w_]+)(?::([^{}]+))?\}\}/g,(u,g,p)=>{const f=o(g);if(p){const h=formatDate(f,p);if(h!==null)return d(h)}return d(f)}),s=d(scopeName(e?.name)),c=String(e?.css??""),a=c.trim()?`<style>[data-block="${s}"] { ${c.replace(/<\/style/gi,"<\\/style")} }</style>`:"",l=PREVIEW_REPEATS.includes(Number(n))?Number(n):1,m=String(t(i)??"");if(l===1)return`${a}<div class="dm-static-block" data-block="${s}">${m}</div>`;const b=`<div data-block="${s}">${m}</div>`;return`${a}<div class="dm-collection-display dm-collection-blocks grid grid-cols-1 grid-cols-md-2 grid-cols-lg-3 gap-4">`+Array.from({length:l},()=>b).join("")+"</div>"}export function previewBridge(e,r){const n=JSON.stringify(Number(e)||0),t=JSON.stringify(String(r)).replace(/</g,"\\u003c");return`<script>(function () {
2
2
  var P = window.parent, TICK = ${n}, HOST = ${t};
3
3
  function host() { return document.getElementById(HOST); }
4
4
  function up(msg) { msg.source = 'dm-block-preview'; msg.tick = TICK; P.postMessage(msg, '*'); }
@@ -1 +1 @@
1
- import{fmtDate as u}from"./dates.js";export const NAME_RE=/^[a-z0-9][a-z0-9-]*$/,FILTERS=["all","used","unused"];const i=(e,t,n=`${t}s`)=>`${e} ${e===1?t:n}`;export function formatSize(e){const t=Number(e)||0;return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(t<10*1024?1:0)} KB`:`${(t/1024/1024).toFixed(1)} MB`}export function usageCount(e){return e?.usage?(e.usage.pages?.length||0)+(e.usage.views?.length||0):null}export function usageText(e){const t=usageCount(e);if(t===null)return"";if(!t)return"Not used on any page or view";const n=e.usage.pages?.length||0,r=e.usage.views?.length||0;return`Used on ${[n&&i(n,"page"),r&&i(r,"view")].filter(Boolean).join(" and ")}`}export function shortcodeFor(e){return e?.example||`[block template="${e?.name??""}" /]`}export function arrangeBlocks(e,{filter:t="all",query:n=""}={}){const r=String(n).trim().toLowerCase();return[...e||[]].filter(o=>t==="used"?usageCount(o)>0:t==="unused"?usageCount(o)===0:!0).filter(o=>!r||[o.name,o.description,o.meta?.project].some(s=>String(s||"").toLowerCase().includes(r))).sort((o,s)=>String(o.name).localeCompare(String(s.name)))}export function blockRow(e,t=u){const n=usageCount(e);return{name:e.name,description:e.description||"",meta:[`${e.name}.html`,formatSize(e.size),e.updatedAt?`changed ${t(e.updatedAt)}`:"",usageText(e)].filter(Boolean).join(" \xB7 "),hasCss:!!e.hasCss,bundled:!!e.bundled,project:e.meta?.project||"",unused:n===0,uses:n===null?"":String(n)}}export function checkNewName(e,t=[]){const n=String(e??"").trim();return n?NAME_RE.test(n)?t.includes(n)?`"${n}" already exists.`:"":"Lowercase letters, digits and hyphens; starting with a letter or digit.":"A block needs a name."}export function copyName(e,t=[]){const n=`${e}-copy`;if(!t.includes(n))return n;for(let r=2;r<1e3;r++)if(!t.includes(`${n}-${r}`))return`${n}-${r}`;return n}
1
+ import{fmtDate as u}from"./dates.js";import{matchesTag as l,tagsOf as c,tagsText as d}from"./list-tags-arrange.js";export const NAME_RE=/^[a-z0-9][a-z0-9-]*$/,FILTERS=["all","used","unused"];const i=(e,t,n=`${t}s`)=>`${e} ${e===1?t:n}`;export function formatSize(e){const t=Number(e)||0;return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(t<10*1024?1:0)} KB`:`${(t/1024/1024).toFixed(1)} MB`}export function usageCount(e){return e?.usage?(e.usage.pages?.length||0)+(e.usage.views?.length||0):null}export function usageText(e){const t=usageCount(e);if(t===null)return"";if(!t)return"Not used on any page or view";const n=e.usage.pages?.length||0,o=e.usage.views?.length||0;return`Used on ${[n&&i(n,"page"),o&&i(o,"view")].filter(Boolean).join(" and ")}`}export function shortcodeFor(e){return e?.example||`[block template="${e?.name??""}" /]`}export function arrangeBlocks(e,{filter:t="all",query:n="",tag:o=""}={}){const a=String(n).trim().toLowerCase();return[...e||[]].filter(r=>t==="used"?usageCount(r)>0:t==="unused"?usageCount(r)===0:!0).filter(r=>l(r,o)).filter(r=>!a||[r.name,r.description,r.meta?.project,...c(r)].some(s=>String(s||"").toLowerCase().includes(a))).sort((r,s)=>String(r.name).localeCompare(String(s.name)))}export function blockRow(e,t=u){const n=usageCount(e);return{name:e.name,description:e.description||"",meta:[`${e.name}.html`,formatSize(e.size),e.updatedAt?`changed ${t(e.updatedAt)}`:"",usageText(e)].filter(Boolean).join(" \xB7 "),hasCss:!!e.hasCss,bundled:!!e.bundled,project:e.meta?.project||"",listTags:d(e),unused:n===0,uses:n===null?"":String(n)}}export function checkNewName(e,t=[]){const n=String(e??"").trim();return n?NAME_RE.test(n)?t.includes(n)?`"${n}" already exists.`:"":"Lowercase letters, digits and hyphens; starting with a letter or digit.":"A block needs a name."}export function copyName(e,t=[]){const n=`${e}-copy`;if(!t.includes(n))return n;for(let o=2;o<1e3;o++)if(!t.includes(`${n}-${o}`))return`${n}-${o}`;return n}
@@ -0,0 +1 @@
1
+ export const UNTAGGED=" untagged",MAX_TAG=40,same=(t,e)=>String(t).toLowerCase()===String(e).toLowerCase();export function normaliseTag(t){return typeof t!="string"?"":t.replace(/[\u0000-\u001f\u007f]/g,"").replace(/\s+/g," ").trim().slice(0,40)}export function normaliseActive(t){return t===UNTAGGED?UNTAGGED:normaliseTag(t)}export function tagsOf(t){const e=Array.isArray(t?.adminTags)?t.adminTags:t?.meta?.adminTags;return Array.isArray(e)?e.filter(o=>typeof o=="string"&&o):[]}export function matchesTag(t,e){if(!e)return!0;const o=tagsOf(t);return e===UNTAGGED?o.length===0:o.some(r=>same(r,e))}export const tagsText=t=>tagsOf(t).join(", ");export function tagCounts(t,e){const o=[],r=n=>{n&&!o.some(i=>same(i,n))&&o.push(n)};return(t||[]).forEach(r),(e||[]).forEach(n=>tagsOf(n).forEach(r)),o.sort((n,i)=>n.localeCompare(i,void 0,{sensitivity:"base"})).map(n=>({name:n,count:(e||[]).filter(i=>tagsOf(i).some(s=>same(s,n))).length}))}export function activeNote(t,e,o){if(!t)return"";const r=`${e} ${e===1?o.replace(/s$/,""):o}`;return t===UNTAGGED?`Showing ${r} with no tag.`:`Showing ${r} tagged "${t}".`}export function tagMenuItems({all:t,current:e,change:o,newTag:r}){const n=(t||[]).filter(a=>!e.some(l=>same(l,a))),s=[{label:"Add tag",icon:"tag",submenu:[...n.map(a=>({label:a,icon:"tag",action:()=>o({add:[a]})})),...n.length?[{type:"divider"}]:[],{label:"New tag\u2026",icon:"plus",action:r}]}];return e.length?s.push({label:"Remove tag",icon:"x",submenu:[...e.map(a=>({label:a,icon:"tag",action:()=>o({remove:[a]})})),...e.length>1?[{type:"divider"},{label:"Remove all tags",icon:"x",action:()=>o({tags:[]})}]:[]]}):s.push({label:"Remove tag",icon:"x",disabled:!0}),s}
@@ -0,0 +1,37 @@
1
+ import{api as x}from"../api.js";import{helpPopovers as H}from"./help-popover.js";import{onceClosed as P}from"./tool-kit.js";import{MAX_TAG as S,UNTAGGED as U,normaliseActive as K,normaliseTag as C,same as N}from"./list-tags-arrange.js";export*from"./list-tags-arrange.js";const u=a=>String(a??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;");export async function askTagName({title:a="New tag",value:l="",confirmText:f="Add"}={}){const p=await E.prompt(`Up to ${S} characters. Tags are only seen here in the admin.`,{title:a,inputPlaceholder:"e.g. Spring campaign",inputValue:u(l),confirmText:f});return p==null?"":C(String(p))}let w=!1;function V(){if(w||document.getElementById("ltg-styles")){w=!0;return}const a=document.createElement("style");a.id="ltg-styles",a.textContent=`
2
+ .ltg-panel { display: flex; flex-direction: column; gap: 1rem; padding: .25rem 0 .5rem; }
3
+ .ltg-panel .form-label { display: flex; align-items: center; gap: .3rem; font-weight: 500; margin-bottom: .3rem; }
4
+ .ltg-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md); }
5
+ .ltg-list li { display: flex; align-items: center; gap: .5rem; padding: .4rem .6rem; border-bottom: 1px solid var(--dm-border); }
6
+ .ltg-list li:last-child { border-bottom: 0; }
7
+ .ltg-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
8
+ .ltg-count { color: var(--dm-text-muted); font-size: var(--dm-text-xs); font-variant-numeric: tabular-nums; }
9
+ .ltg-list button { border: 0; background: none; cursor: pointer; color: var(--dm-text-muted); padding: .15rem .3rem; border-radius: var(--dm-radius-sm); display: inline-flex; }
10
+ .ltg-list button:hover { color: var(--dm-primary); }
11
+ .ltg-list button.is-danger:hover { color: var(--dm-danger); }
12
+ .ltg-empty { margin: 0; color: var(--dm-text-muted); font-size: var(--dm-text-sm); }
13
+ .ltg-add { display: flex; gap: .4rem; }
14
+ .ltg-add .form-input { flex: 1; }
15
+ .ltg-meta { font-size: var(--dm-text-xs); color: var(--dm-text-muted); }
16
+ .ltg-actions { display: flex; align-items: center; gap: .5rem; padding-top: .75rem; border-top: 1px solid var(--dm-border); }
17
+ .ltg-actions .spacer { flex: 1; }`,document.head.appendChild(a),w=!0}export function openTagSettings({kind:a,noun:l,title:f,tags:p,active:i,reload:A}){V();const d=E.slideover({title:f,size:"sm",position:"right"}),$=document.createElement("template");$.innerHTML=`
18
+ <div class="ltg-panel">
19
+ <div>
20
+ <label class="form-label" for="ltg-show">Show
21
+ <span data-help="Narrow the list to the ${l} wearing one tag. Remembered in this browser; everyone else keeps their own choice."></span></label>
22
+ <select id="ltg-show" class="form-input"></select>
23
+ </div>
24
+ <div>
25
+ <label class="form-label">Tags
26
+ <span data-help="Tags link ${l} together - an item can wear several. They are seen only here in the admin, never on the site. Right-click any of the ${l} to add or remove one. Deleting a tag takes it off its ${l}; nothing else changes."></span></label>
27
+ <ul class="ltg-list"></ul>
28
+ <p class="ltg-empty">No tags yet.</p>
29
+ </div>
30
+ <div class="ltg-add">
31
+ <input type="text" class="form-input" maxlength="${S}" placeholder="New tag" aria-label="New tag">
32
+ <button type="button" class="btn btn-sm btn-primary">Add</button>
33
+ </div>
34
+ <small class="ltg-meta">Tags are shared by everyone who edits ${l}.</small>
35
+ <div class="ltg-actions"><span class="spacer"></span>
36
+ <button type="button" class="btn btn-sm btn-primary ltg-done">Done</button></div>
37
+ </div>`;const o=$.content.firstElementChild,g=o.querySelector("#ltg-show"),h=o.querySelector(".ltg-list"),q=o.querySelector(".ltg-empty"),m=o.querySelector(".ltg-add input");let y=!1;const v=(e,n)=>{const t=document.createElement("option");return t.value=e,t.textContent=n,t},T=(e,n,t,c)=>{const r=document.createElement("button");r.type="button",r.title=n,r.setAttribute("aria-label",n),t&&(r.className=t);const s=document.createElement("span");return s.setAttribute("data-icon",e),s.setAttribute("data-icon-size","14"),r.appendChild(s),r.addEventListener("click",c),r};async function b(e,n){if(!y){y=!0;try{const t=await e();return await A(),n&&E.toast(n(t),{type:"success"}),!0}catch(t){return E.toast(t.message||"That did not work.",{type:"error"}),!1}finally{y=!1}}}async function D(e){const n=await askTagName({title:`Rename "${u(e)}"`,value:e,confirmText:"Rename"});if(!n||n===e)return;await b(()=>x.listTags.rename(a,e,n),c=>`Renamed - ${c.changed} ${l} updated.`)&&N(i.peek(),e)&&(i.value=n)}async function R(e){const n=e.count?`Delete the tag "${u(e.name)}"? It comes off ${e.count} ${e.count===1?l.replace(/s$/,""):l}; nothing else about them changes.`:`Delete the tag "${u(e.name)}"?`;if(!await E.confirm(n))return;await b(()=>x.listTags.remove(a,e.name),()=>"Tag deleted.")&&N(i.peek(),e.name)&&(i.value="")}async function k(){const e=C(m.value);if(!e){m.focus();return}await b(()=>x.listTags.add(a,e)),m.value="",m.focus()}const L=M.effect(()=>{const e=p.value,n=i.value;g.replaceChildren(v("",`All ${l}`),v(U,"No tag"),...e.map(t=>v(t.name,`${t.name} (${t.count})`))),g.value=n,h.replaceChildren(...e.map(t=>{const c=document.createElement("li"),r=document.createElement("span");r.className="ltg-name",r.textContent=t.name;const s=document.createElement("span");return s.className="ltg-count",s.textContent=String(t.count),c.append(r,s,T("edit",`Rename ${t.name}`,"",()=>D(t.name)),T("trash",`Delete ${t.name}`,"is-danger",()=>R(t))),c})),h.hidden=!e.length,q.hidden=e.length>0,I.scan(h)}),z=()=>{i.value=K(g.value)},G=e=>{e.key==="Enter"&&e.target===m&&(e.preventDefault(),k())};g.addEventListener("change",z),o.addEventListener("keydown",G),o.querySelector(".ltg-add button").addEventListener("click",k),o.querySelector(".ltg-done").addEventListener("click",()=>d.close()),d.element.appendChild(o);const B=H(o);return P(d,()=>{L(),B()}),I.scan(o),d.open(),d}
@@ -1 +1 @@
1
- import{fmtDate as a}from"./dates.js";export const STATUSES=["all","published","draft"],SORTS=["order","updated","title","url"],MODES=["list","tree"],DEFAULT_SETTINGS={status:"all",sort:"order",mode:"list"};const l=(t,e,r=`${e}s`)=>`${t} ${t===1?e:r}`;export function normaliseSettings(t){const e=t&&typeof t=="object"?t:{};return{status:STATUSES.includes(e.status)?e.status:DEFAULT_SETTINGS.status,sort:SORTS.includes(e.sort)?e.sort:DEFAULT_SETTINGS.sort,mode:MODES.includes(e.mode)?e.mode:DEFAULT_SETTINGS.mode}}export function ago(t,e=Date.now()){const r=new Date(t).getTime();if(!Number.isFinite(r))return"";const o=Math.max(0,Math.round((e-r)/1e3));return o<60?"just now":o<3600?`${Math.round(o/60)} min ago`:o<86400?`${l(Math.round(o/3600),"hour")} ago`:o<86400*14?`${l(Math.round(o/86400),"day")} ago`:a(r)}export function visibilityLabel(t){const e=String(t??"public").trim();return!e||e==="public"?"":e==="private"?"Private":e.startsWith("=")?`Only ${e.slice(1)}`:`${e}+`}const d=t=>`${t.title||""} ${t.urlPath||""} ${(t.tags||[]).join(" ")} ${t.layout||""}`.toLowerCase();export function arrangePages(t,{status:e="all",query:r="",sort:o="order"}={}){const s=String(r).trim().toLowerCase(),i=(t||[]).filter(n=>e==="all"||(e==="draft"?n.status!=="published":n.status==="published")).filter(n=>!s||d(n).includes(s));return o==="updated"?[...i].sort((n,u)=>String(u.updatedAt||"").localeCompare(String(n.updatedAt||""))):o==="title"?[...i].sort((n,u)=>String(n.title||"").localeCompare(String(u.title||""))):o==="url"?[...i].sort((n,u)=>String(n.urlPath).localeCompare(String(u.urlPath))):i}export function statusCounts(t){const e=(t||[]).length,r=(t||[]).filter(o=>o.status==="published").length;return{all:e,published:r,draft:e-r}}export function pageRow(t,e=Date.now()){const r=t.status!=="published",o=Number.isFinite(t.versionCount)?t.versionCount:0,s=t.resolvedProject&&t.resolvedProject!=="core"?t.resolvedProject:"";return{urlPath:t.urlPath,title:t.title||t.urlPath,statusText:t.status==="review"?"Review":r?"Draft":"Published",statusCls:r?"is-warn":"is-ok",visibility:visibilityLabel(t.visibility),project:s,plugin:t.plugin?String(t.plugin):"",bundled:t.bundled===!0,tagsText:(t.tags||[]).filter(Boolean).map(i=>`#${i}`).join(" "),meta:[t.urlPath,t.layout||"default",l(o,"revision"),t.updatedAt?`updated ${ago(t.updatedAt,e)}`:""].filter(Boolean).join(" \xB7 "),revisions:o,rowCls:r?"is-draft":""}}export function treeRows(t){const e=new Set((t||[]).map(r=>r.urlPath));return(t||[]).map(r=>{const o=String(r.urlPath).split("/").filter(Boolean);let s=null;for(let i=o.length-1;i>0&&!s;i--){const n="/"+o.slice(0,i).join("/");e.has(n)&&(s=n)}return{id:r.urlPath,parent_id:s,name:r.title||r.urlPath,icon:r.status==="published"?"check-circle":"file-text"}})}export function checkPrune(t,e){const r=String(t??"").trim();if(!/^\d+$/.test(r))return{error:"A whole number, 0 or more.",keep:0,deletes:0};const o=Number.parseInt(r,10);return{error:"",keep:o,deletes:Math.max(0,(Number(e)||0)-o)}}
1
+ import{fmtDate as d}from"./dates.js";import{matchesTag as c,normaliseActive as f,tagsOf as h,tagsText as g}from"./list-tags-arrange.js";export const STATUSES=["all","published","draft"],SORTS=["order","updated","title","url"],MODES=["list","tree"],DEFAULT_SETTINGS={status:"all",sort:"order",mode:"list",tag:""};const u=(t,e,r=`${e}s`)=>`${t} ${t===1?e:r}`;export function normaliseSettings(t){const e=t&&typeof t=="object"?t:{};return{status:STATUSES.includes(e.status)?e.status:DEFAULT_SETTINGS.status,sort:SORTS.includes(e.sort)?e.sort:DEFAULT_SETTINGS.sort,mode:MODES.includes(e.mode)?e.mode:DEFAULT_SETTINGS.mode,tag:f(e.tag)}}export function ago(t,e=Date.now()){const r=new Date(t).getTime();if(!Number.isFinite(r))return"";const o=Math.max(0,Math.round((e-r)/1e3));return o<60?"just now":o<3600?`${Math.round(o/60)} min ago`:o<86400?`${u(Math.round(o/3600),"hour")} ago`:o<86400*14?`${u(Math.round(o/86400),"day")} ago`:d(r)}export function visibilityLabel(t){const e=String(t??"public").trim();return!e||e==="public"?"":e==="private"?"Private":e.startsWith("=")?`Only ${e.slice(1)}`:`${e}+`}const m=t=>`${t.title||""} ${t.urlPath||""} ${(t.tags||[]).join(" ")} ${h(t).join(" ")} ${t.layout||""}`.toLowerCase();export function arrangePages(t,{status:e="all",query:r="",sort:o="order",tag:s=""}={}){const n=String(r).trim().toLowerCase(),a=(t||[]).filter(i=>e==="all"||(e==="draft"?i.status!=="published":i.status==="published")).filter(i=>c(i,s)).filter(i=>!n||m(i).includes(n));return o==="updated"?[...a].sort((i,l)=>String(l.updatedAt||"").localeCompare(String(i.updatedAt||""))):o==="title"?[...a].sort((i,l)=>String(i.title||"").localeCompare(String(l.title||""))):o==="url"?[...a].sort((i,l)=>String(i.urlPath).localeCompare(String(l.urlPath))):a}export function statusCounts(t){const e=(t||[]).length,r=(t||[]).filter(o=>o.status==="published").length;return{all:e,published:r,draft:e-r}}export function pageRow(t,e=Date.now()){const r=t.status!=="published",o=Number.isFinite(t.versionCount)?t.versionCount:0,s=t.resolvedProject&&t.resolvedProject!=="core"?t.resolvedProject:"";return{urlPath:t.urlPath,title:t.title||t.urlPath,statusText:t.status==="review"?"Review":r?"Draft":"Published",statusCls:r?"is-warn":"is-ok",visibility:visibilityLabel(t.visibility),project:s,plugin:t.plugin?String(t.plugin):"",bundled:t.bundled===!0,tagsText:(t.tags||[]).filter(Boolean).map(n=>`#${n}`).join(" "),listTags:g(t),meta:[t.urlPath,t.layout||"default",u(o,"revision"),t.updatedAt?`updated ${ago(t.updatedAt,e)}`:""].filter(Boolean).join(" \xB7 "),revisions:o,rowCls:r?"is-draft":""}}export function treeRows(t){const e=new Set((t||[]).map(r=>r.urlPath));return(t||[]).map(r=>{const o=String(r.urlPath).split("/").filter(Boolean);let s=null;for(let n=o.length-1;n>0&&!s;n--){const a="/"+o.slice(0,n).join("/");e.has(a)&&(s=a)}return{id:r.urlPath,parent_id:s,name:r.title||r.urlPath,icon:r.status==="published"?"check-circle":"file-text"}})}export function checkPrune(t,e){const r=String(t??"").trim();if(!/^\d+$/.test(r))return{error:"A whole number, 0 or more.",keep:0,deletes:0};const o=Number.parseInt(r,10);return{error:"",keep:o,deletes:Math.max(0,(Number(e)||0)-o)}}
@@ -1,7 +1,7 @@
1
- import{api as g,getUser as B}from"../api.js";import{isPathAllowed as pe}from"./admin-scope.js";import{colourToCss as Y,applyFloat as he,applyHighlight as ge,itemHighlightAttrs as be}from"/public/js/menu-decor.mjs?v=20261014-menu-entrance";import{placeTools as ye,ensureLockedTools as ve,managePluginsItem as xe,stripItemByUrl as $e,insertFoldersBeforeSystem as we,pruneEmptyToolsFolder as ke,sidebarStateKey as J,canAny as U,filterByPermission as Se,MANAGE_PLUGINS_URL as Q}from"./sidebar-grouping.js";import{arrangeButtonMarkup as qe,canArrange as Ce,startArrange as Ae}from"./sidebar-arrange.js";import{collapseButtonMarkup as Fe,wireSidebarSize as Le,COLLAPSED_WIDTH as Ee}from"./sidebar-size.js";import{FREQUENT_MAX as ee,addFrequent as Te,frequentFolder as Be,moveFrequent as te,readFrequent as W,readFrequentHidden as He,removeFrequent as Me,saveFrequent as je,syncFrequent as Ne,writeFrequent as Pe,writeFrequentHidden as ne}from"./sidebar-frequent.js";import{hotkeyFor as se,hotkeyTitle as Re,setHotkeyTree as _e}from"./hotkeys-ui.js";import{hotkeyLabel as Ie}from"./hotkeys.js";function f(e){return String(e??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}const Oe=[{text:"Dashboard",url:"#/",icon:"home"},{text:"Site menus",url:"#/menus",icon:"menu",permission:"menus"}];async function Ue(e){if(!U(e,"projects"))return[];let n=[];try{n=await g.projects.listWithCounts()}catch{return[]}if(!Array.isArray(n))return[];const s=[{key:"pages",text:"Pages",icon:"file-text",path:"/pages"},{key:"collections",text:"Collections",icon:"database",path:"/collections"},{key:"forms",text:"Forms",icon:"layout",path:"/forms"},{key:"actions",text:"Actions",icon:"zap",path:"/actions"},{key:"menus",text:"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(t=>{const o="#/projects/"+encodeURIComponent(t.slug),a=t.counts||{},d=[{text:"Overview",url:o,icon:t.icon||"folder"}];for(const c of s){const r=Number(a[c.key])||0;r<=0||d.push({text:`${c.text} (${r})`,url:o+c.path,icon:c.icon})}d.push({text:"Settings",url:o+"/settings",icon:"settings"});const u=t.enabled===!1;return{text:t.name||t.slug,key:`project.${t.slug}`,icon:t.icon||"folder",items:d,...u&&{colour:"neutral",badge:{text:"off"}}}})}function ze(e,n){for(const s of e)if(!s.hidden&&(s.key==="projects"||!s.key&&(s.text||"").toLowerCase()==="projects")){s.items=Array.isArray(s.items)?s.items.slice():[],s.items.push(...n);return}}function oe(e,n){const s=ae.includes(n.tone)?n.tone:"info",t=n.label||String(n.count);n.details?P.set(e,n.details):P.delete(e);const i=!n.inventory&&F.some(o=>o.url===e&&!o.inventory)?" data-live":"";return`<span class="sidebar-badge sidebar-badge--${s}" data-count-for="${f(e)}"${i} data-badge-label="${f(t)}" aria-label="${f(t)}">${f(String(n.count))}</span>`}function ie(e){for(const n of e.querySelectorAll("details")){const s=n.querySelector(":scope > summary");if(!s)continue;let t=0,i=!1;const o=[];for(const r of n.querySelectorAll(":scope > .sidebar-children .sidebar-badge[data-live]")){const m=Number(r.textContent)||0;m&&(t+=m,i||(i=r.classList.contains("sidebar-badge--danger")),o.push({text:r.closest("a")?.querySelector(".sidebar-text")?.textContent.trim()??"",meta:r.dataset.badgeLabel,tone:r.classList.contains("sidebar-badge--danger")?"danger":void 0}))}const a=`folder:${n.dataset.stateKey||s.textContent.trim()}`;let d=s.querySelector(":scope > .sidebar-badge--sum");if(!t){d?.remove(),P.delete(a);continue}d||(d=document.createElement("span"),s.querySelector(".sidebar-text")?.after(d));const u=s.querySelector(".sidebar-text")?.textContent.trim()||"This folder",c=o.map(r=>`${r.text}: ${r.meta}`).join(" \xB7 ");d.className=`sidebar-badge sidebar-badge--sum sidebar-badge--${i?"danger":"info"}`,d.dataset.countFor=a,d.dataset.badgeLabel=c,d.setAttribute("aria-label",c),d.textContent=String(t),P.set(a,{title:u,items:o})}}const P=new Map;function We(e,n){if(!e.url||!n)return"";const s=n[e.url];return!s||!(s.count>0)&&!s.showZero?"":oe(e.url,s)}const ae=["info","attention","danger","live"],De={"#/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:s="info"}={}){const t=Number(typeof e=="object"&&e!==null?e.count:e);if(!Number.isFinite(t)||t<0)return null;const i=typeof e?.label=="string"&&e.label||(n?`${t} - ${n}`:String(t)),o={count:Math.round(t),label:i,tone:ae.includes(e?.tone)?e.tone:s};return e?.details&&typeof e.details=="object"&&(o.details=e.details),e?.inventory===!0&&(o.inventory=!0),e?.showZero===!0&&(o.showZero=!0),o}function re(e,n,s=null,{inFrequent:t=!1}={}){if(e&&e.type==="separator")return'<hr class="sidebar-divider">';if(e&&e.type==="theme-switcher")return"";if(e&&e.type==="spacer"){const b=["xs","sm","md","lg","xl","flex"].includes(e.size)?e.size:"md";return`<div class="sidebar-spacer sidebar-spacer--${f(b)}" aria-hidden="true"></div>`}const i=String(e.text||"?").trim().charAt(0).toUpperCase()||"?",o=e.icon?`<span data-icon="${f(e.icon)}"></span>`:`<span class="sidebar-initial" aria-hidden="true">${f(i)}</span>`,a=f(e.text||""),d=e.url?f(e.url):"",u=Array.isArray(e.items)&&e.items.length>0,c=e.colour?Y(e.colour):"",r=be(e),m=[r.hover&&` data-menu-item-hover="${f(r.hover)}"`,r.active&&` data-menu-item-active="${f(r.active)}"`].filter(Boolean).join(""),y=[c&&`color:${f(c)}`,r.accent&&`--dm-menu-highlight:${f(r.accent)}`].filter(Boolean).join(";"),v=y?` style="${y}"`:"",k=e.url?Re(e.url,e.text||"",{inFrequent:t}):"",w=k?` title="${f(k)}"`:"";let L="";if(e.badge&&e.badge.text!=null&&e.badge.text!==""){const b=e.badge.variant?Y(e.badge.variant):"";L=`<span class="dm-menu-badge"${b?` style="background:${f(b)};color:var(--dm-text-inverse,#fff)"`:""}>${f(String(e.badge.text))}</span>`}if(e.frequent&&e.empty){const b=J(e,s),A=S.get(b)!==!1;return`<details data-state-key="${f(b)}"${A?" open":""} data-frequent data-frequent-empty>
2
- <summary${v}>${o} <span class="sidebar-text">${a}</span></summary>
1
+ import{api as g,getUser as T}from"../api.js";import{isPathAllowed as he}from"./admin-scope.js";import{colourToCss as J,applyFloat as ge,applyHighlight as be,itemHighlightAttrs as ye}from"/public/js/menu-decor.mjs?v=20261014-menu-entrance";import{placeTools as ve,ensureLockedTools as xe,managePluginsItem as $e,stripItemByUrl as we,insertFoldersBeforeSystem as Se,pruneEmptyToolsFolder as ke,sidebarStateKey as Q,canAny as z,filterByPermission as qe,MANAGE_PLUGINS_URL as ee}from"./sidebar-grouping.js";import{arrangeButtonMarkup as Ce,canArrange as Ae,startArrange as Fe}from"./sidebar-arrange.js";import{collapseButtonMarkup as Le,wireSidebarSize as Ee,COLLAPSED_WIDTH as Me}from"./sidebar-size.js";import{FREQUENT_MAX as te,addFrequent as Te,frequentFolder as Be,moveFrequent as ne,readFrequent as G,readFrequentHidden as He,removeFrequent as je,saveFrequent as Ne,syncFrequent as Pe,writeFrequent as Re,writeFrequentHidden as se}from"./sidebar-frequent.js";import{hotkeyFor as oe,hotkeyTitle as _e,setHotkeyTree as Ie}from"./hotkeys-ui.js";import{hotkeyLabel as Oe}from"./hotkeys.js";function f(e){return String(e??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}const Ue=[{text:"Dashboard",url:"#/",icon:"home"},{text:"Site menus",url:"#/menus",icon:"menu",permission:"menus"}];async function ze(e){if(!z(e,"projects"))return[];let t=[];try{t=await g.projects.listWithCounts()}catch{return[]}if(!Array.isArray(t))return[];const s=[{key:"pages",text:"Pages",icon:"file-text",path:"/pages"},{key:"collections",text:"Collections",icon:"database",path:"/collections"},{key:"forms",text:"Forms",icon:"layout",path:"/forms"},{key:"actions",text:"Actions",icon:"zap",path:"/actions"},{key:"menus",text:"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 t.map(o=>{const d="#/projects/"+encodeURIComponent(o.slug),i=o.counts||{},c=[{text:"Overview",url:d,icon:o.icon||"folder"}];for(const r of s){const a=Number(i[r.key])||0;a<=0||c.push({text:`${r.text} (${a})`,url:d+r.path,icon:r.icon})}c.push({text:"Settings",url:d+"/settings",icon:"settings"});const u=o.enabled===!1;return{text:o.name||o.slug,key:`project.${o.slug}`,icon:o.icon||"folder",items:c,...u&&{colour:"neutral",badge:{text:"off"}}}})}function We(e,t){for(const s of e)if(!s.hidden&&(s.key==="projects"||!s.key&&(s.text||"").toLowerCase()==="projects")){s.items=Array.isArray(s.items)?s.items.slice():[],s.items.push(...t);return}}function ie(e,t){const s=re.includes(t.tone)?t.tone:"info",o=t.label||String(t.count);t.details?R.set(e,t.details):R.delete(e);const n=!t.inventory&&F.some(d=>d.url===e&&!d.inventory)?" data-live":"";return`<span class="sidebar-badge sidebar-badge--${s}" data-count-for="${f(e)}"${n} data-badge-label="${f(o)}" aria-label="${f(o)}">${f(String(t.count))}</span>`}function ae(e){for(const t of e.querySelectorAll("details")){const s=t.querySelector(":scope > summary");if(!s)continue;let o=0,n=!1;const d=[];for(const a of t.querySelectorAll(":scope > .sidebar-children .sidebar-badge[data-live]")){const m=Number(a.textContent)||0;m&&(o+=m,n||(n=a.classList.contains("sidebar-badge--danger")),d.push({text:a.closest("a")?.querySelector(".sidebar-text")?.textContent.trim()??"",meta:a.dataset.badgeLabel,tone:a.classList.contains("sidebar-badge--danger")?"danger":void 0}))}const i=`folder:${t.dataset.stateKey||s.textContent.trim()}`;let c=s.querySelector(":scope > .sidebar-badge--sum");if(!o){c?.remove(),R.delete(i);continue}c||(c=document.createElement("span"),s.querySelector(".sidebar-text")?.after(c));const u=s.querySelector(".sidebar-text")?.textContent.trim()||"This folder",r=d.map(a=>`${a.text}: ${a.meta}`).join(" \xB7 ");c.className=`sidebar-badge sidebar-badge--sum sidebar-badge--${n?"danger":"info"}`,c.dataset.countFor=i,c.dataset.badgeLabel=r,c.setAttribute("aria-label",r),c.textContent=String(o),R.set(i,{title:u,items:d})}}const R=new Map;function De(e,t){if(!e.url||!t)return"";const s=t[e.url];return!s||!(s.count>0)&&!s.showZero?"":ie(e.url,s)}const re=["info","attention","danger","live"],Ge={"#/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 K(e,{fallbackLabel:t="",tone:s="info"}={}){const o=Number(typeof e=="object"&&e!==null?e.count:e);if(!Number.isFinite(o)||o<0)return null;const n=typeof e?.label=="string"&&e.label||(t?`${o} - ${t}`:String(o)),d={count:Math.round(o),label:n,tone:re.includes(e?.tone)?e.tone:s};return e?.details&&typeof e.details=="object"&&(d.details=e.details),e?.inventory===!0&&(d.inventory=!0),e?.showZero===!0&&(d.showZero=!0),d}function le(e,t,s=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 y=["xs","sm","md","lg","xl","flex"].includes(e.size)?e.size:"md";return`<div class="sidebar-spacer sidebar-spacer--${f(y)}" aria-hidden="true"></div>`}const n=String(e.text||"?").trim().charAt(0).toUpperCase()||"?",d=e.icon?`<span data-icon="${f(e.icon)}"></span>`:`<span class="sidebar-initial" aria-hidden="true">${f(n)}</span>`,i=f(e.text||""),c=e.url?f(e.url):"",u=Array.isArray(e.items)&&e.items.length>0,r=e.colour?J(e.colour):"",a=ye(e),m=[a.hover&&` data-menu-item-hover="${f(a.hover)}"`,a.active&&` data-menu-item-active="${f(a.active)}"`].filter(Boolean).join(""),b=[r&&`color:${f(r)}`,a.accent&&`--dm-menu-highlight:${f(a.accent)}`].filter(Boolean).join(";"),v=b?` style="${b}"`:"",w=e.url?_e(e.url,e.text||"",{inFrequent:o}):"",k=w?` title="${f(w)}"`:"";let A="";if(e.badge&&e.badge.text!=null&&e.badge.text!==""){const y=e.badge.variant?J(e.badge.variant):"";A=`<span class="dm-menu-badge"${y?` style="background:${f(y)};color:var(--dm-text-inverse,#fff)"`:""}>${f(String(e.badge.text))}</span>`}if(e.frequent&&e.empty){const y=Q(e,s),C=S.get(y)!==!1;return`<details data-state-key="${f(y)}"${C?" open":""} data-frequent data-frequent-empty>
2
+ <summary${v}>${d} <span class="sidebar-text">${i}</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(u){const b=J(e,s),A=S.get(b)!==!1,R=e.items.map(T=>re(T,n,b,{inFrequent:!!e.frequent})).join(""),j=e.frequent?' data-frequent title="Your Frequent Tools - right-click any Tool to add or remove it"':"";return`<details data-state-key="${f(b)}"${A?" open":""}${j}>
5
- <summary${v}${w}>${o} <span class="sidebar-text">${a}</span>${L}</summary>
6
- <div class="sidebar-children">${R}</div>
7
- </details>`}return`<a href="${d}" class="sidebar-link" data-url="${d}"${v}${m}${w}>${o} <span class="sidebar-text">${a}</span>${We(e,n)}${L}</a>`}async function Ge(e=[],n=[],s=null){const t=p=>p.then(x=>Array.isArray(x)?x.length:Array.isArray(x?.entries)?x.entries.length:0).catch(()=>0);F=Xe(e,s);const i=new Set(F.map(p=>p.url)),o={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"},a=(p,x)=>U(n,p)&&!i.has(o[p])?x():Promise.resolve(0),[d,u,c,r,m,y,v,k,w,L,b,A,R,j]=await Promise.all([a("pages",()=>t(g.pages.list())),a("media",()=>t(g.media.list())),a("menus",()=>t(g.menus.list())),a("context-menus",()=>t(g.contextMenus.list())),a("theme",()=>g.theme.get().then(p=>(p?.overrides||[]).length).catch(()=>0)),a("collections",()=>t(g.collections.list())),a("forms",()=>t(g.forms.list())),a("views",()=>t(g.views.list())),a("actions",()=>t(g.actions.list())),a("blocks",()=>t(g.blocks.list())),a("components",()=>t(g.components.list())),a("users",()=>t(g.users.list())),a("plugins",()=>t(g.plugins.list())),a("notifications",()=>g.system?.notifications?.unreadCount?.().then(p=>p?.count??0).catch(()=>0)??Promise.resolve(0))]),T={};await Promise.all(F.map(async p=>{const x=await le(p);x&&(T[p.url]=x)}));const X={"#/pages":d,"#/media":u,"#/menus":c,"#/context-menus":r,"#/theme":m,"#/collections":y,"#/forms":v,"#/views":k,"#/actions":w,"#/blocks":L,"#/components":b,"#/users":A,"#/plugins":R,"#/system/notifications":j},I={};for(const[p,x]of Object.entries(X)){if(i.has(p))continue;const[O,z]=De[p]??["item","items"],q=D(x);q&&(I[p]={...q,label:`${q.count} ${q.count===1?O:z}`})}return{...I,...T}}let F=[];const Ke="dm:sidebar-count",Ve=6e4;function Xe(e=[],n=null){const s=[],t=i=>{if(i&&!(n&&typeof i.url=="string"&&i.url.startsWith("#/")&&!pe(n,i.url.slice(1)))){i.url&&typeof i.countUrl=="string"&&i.countUrl&&s.push({url:i.url,countUrl:i.countUrl,text:i.text||"",countLabel:i.countLabel||"",inventory:i.inventory===!0});for(const o of[...i.children||[],...i.items||[]])t(o)}};for(const i of e??[])t(i?.item??i);return s}async function le(e){try{const n=new Date,s=`${n.getFullYear()}-${String(n.getMonth()+1).padStart(2,"0")}-${String(n.getDate()).padStart(2,"0")}`,t=e.countUrl+(e.countUrl.includes("?")?"&":"?")+`today=${s}`;return D(await H.get(t),{fallbackLabel:e.countLabel||e.text})}catch{return null}}function ce(e,n,s){const t=[...e.querySelectorAll("a.sidebar-link")].filter(i=>i.dataset.url===n);if(t.length){for(const i of t){const o=i.querySelector(".sidebar-badge[data-count-for]");if(!s||!(s.count>0)&&!s.showZero)o?.remove();else{const a=document.createRange();a.selectNodeContents(i);const d=a.createContextualFragment(oe(n,s)).firstElementChild;o?o.replaceWith(d):i.querySelector(".sidebar-text")?.after(d)}}G.set(n,s),ie(e)}}const G=new Map;async function K(e,n=null){const s=n?F.filter(t=>t.url===n):F;await Promise.all(s.map(async t=>{const i=await le(t),o=G.get(t.url);JSON.stringify(o??null)!==JSON.stringify(i)&&ce(e,t.url,i)}))}function Ze(e){e.dataset.liveCounts||(e.dataset.liveCounts="1",window.addEventListener(Ke,n=>{const s=n.detail||{};if(s.url)if(s.count!==void 0){const t=F.find(i=>i.url===s.url);ce(e,s.url,D(s,{fallbackLabel:t?.countLabel||t?.text||""}))}else K(e,s.url)}),setInterval(()=>{!document.hidden&&document.body.contains(e)&&K(e)},Ve),document.addEventListener("visibilitychange",()=>{document.hidden||K(e)}),Ye(e))}function de(e,n,s){const t=(a,d,u)=>{const c=document.createElement(a);return d&&(c.className=d),u!=null&&(c.textContent=String(u)),c};if(!s){e.textContent=n;return}e.appendChild(t("strong","dm-badge-pop-title",s.title||n)),(s.rows?.length||!s.items?.length)&&e.appendChild(t("p","dm-badge-pop-sub",n));const i=Array.isArray(s.rows)?s.rows.slice(0,6):[];if(i.length){const a=t("dl","dm-badge-pop-rows");for(const d of i)a.appendChild(t("dt",null,d.label)),a.appendChild(t("dd",d.tone==="danger"?"is-danger":null,d.value));e.appendChild(a)}const o=Array.isArray(s.items)?s.items.slice(0,6):[];if(o.length){const a=t("ul","dm-badge-pop-items");for(const d of o){const u=t("li",d.tone==="danger"?"is-danger":null);u.appendChild(t("span","dm-badge-pop-text",d.text)),d.meta&&u.appendChild(t("span","dm-badge-pop-meta",d.meta)),a.appendChild(u)}e.appendChild(a)}else s.empty&&e.appendChild(t("p","dm-badge-pop-empty",s.empty))}function Ye(e){let n=null,s=null;const t=()=>e.classList.contains("is-collapsed"),i=()=>{clearTimeout(s),n?.remove(),n=null},o=(c,r)=>{i(),s=setTimeout(()=>{n=document.createElement("div"),n.className="dm-help-pop dm-badge-pop",n.setAttribute("role","tooltip"),r(n),document.body.appendChild(n);const m=c.getBoundingClientRect(),y=n.getBoundingClientRect(),v=Math.min(m.right+10,innerWidth-y.width-8);n.style.left=`${Math.max(8,v)}px`,n.style.top=`${Math.max(8,Math.min(m.top+m.height/2-y.height/2,innerHeight-y.height-8))}px`},150)},a=c=>{const r=c?.dataset.badgeLabel;if(!r)return i();o(c,m=>de(m,r,P.get(c.dataset.countFor)))},d=c=>{const r=c.querySelector(":scope > .sidebar-text")?.textContent.trim()||"",m=c.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");o(c,y=>{const v=m&&P.get(m.dataset.countFor);if(v)return de(y,m.dataset.badgeLabel,{...v,title:v.title||r});if(!m){y.textContent=r;return}const k=document.createElement("strong");k.className="dm-badge-pop-title",k.textContent=r;const w=document.createElement("p");w.className="dm-badge-pop-sub",w.textContent=m.dataset.badgeLabel,y.append(k,w)})},u=c=>{const r=c.closest?.("a.sidebar-link, summary");return r&&e.contains(r)?r:null};e.addEventListener("mouseover",c=>{if(t()){const m=u(c.target);m&&!m.contains(c.relatedTarget)&&d(m);return}const r=c.target.closest?.(".sidebar-badge[data-badge-label]");r&&a(r)}),e.addEventListener("mouseout",c=>{if(t()){const r=u(c.target);r&&!r.contains(c.relatedTarget)&&i();return}c.target.closest?.(".sidebar-badge[data-badge-label]")&&i()}),e.addEventListener("focusin",c=>{const r=c.target.closest?.("a, summary");if(t()&&r&&e.contains(r)&&r.querySelector(":scope > .sidebar-text"))return d(r);const m=r?.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");m?a(m):i()}),e.addEventListener("focusout",i),e.addEventListener("click",i),window.addEventListener("scroll",i,!0),window.addEventListener("hashchange",i)}async function Je(e=[]){if(!U(e,"settings"))return{title:"Admin"};try{const n=await g.settings.get();return{...n?.adminBrand,title:n?.adminBrand?.title||n?.title||"Admin"}}catch{return{title:"Admin"}}}export async function renderAdminSidebar({mount:e,permissions:n,adminScope:s=null}){const t=$(e).get(0);if(!t)return;const i=Ne(g,B()).catch(()=>{});let o,a=null,d=null,u=null,c=null,r=null;try{const l=await g.get("/sidebar/menu");o=Array.isArray(l?.items)?l.items:null,a=l?.variant||null,d=l?.position||null,u=l?.style||null,c=l?.float||null,r=l?.highlight||null}catch{o=null}(!o||!o.some(l=>l&&(!l.hidden||l.restricted)))&&(console.warn("[admin-sidebar] No admin-sidebar menu found; using fallback tree"),o=Oe.slice());let m=[];try{m=await H.get("/api/sidebar/registered-items")||[]}catch{}o=$e(o,Q),o=ye(o,m.filter(l=>(l?.item??l)?.url!==Q)),o=ve(o),o=we(o,[xe()]);const y=await Ue(n);y.length&&ze(o,y),o=Se(o,n,s),o=ke(o),await i;const v=Be(o,W(B()),{hidden:He(B())});v&&(o=[v,...o]),_e(o);const[k,w]=await Promise.all([Ge(m.filter(l=>U(n,l?.item?.permission)),n,s).catch(()=>({})),Je(n)]),L=a?` dm-admin-sidebar--${f(a)}`:"";function b(l,C="px"){if(l==null)return l;const h=String(l).trim();return/^\d+(\.\d+)?$/.test(h)?`${h}${C}`:h}let A="";if(u){const l=[];u.fontFamily&&l.push(`font-family: ${u.fontFamily}, sans-serif`),u.fontSize&&l.push(`font-size: ${b(u.fontSize)}`),u.fontWeight&&l.push(`font-weight: ${u.fontWeight}`),u.letterSpacing&&l.push(`letter-spacing: ${b(u.letterSpacing,"em")}`);const C=[];if(l.length&&C.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 { ${l.join("; ")} }`),u.iconSize){const h=b(u.iconSize);C.push(`#admin-sidebar .dm-admin-sidebar [data-icon], #admin-sidebar .dm-admin-sidebar [data-icon] svg { width: ${h} !important; height: ${h} !important; }`)}C.length&&(A=`<style data-admin-sidebar-style>${C.join(" ")}</style>`)}const R=Ce(n)?qe():"",j=String(w.logo||"").trim(),T=/^(?:\/(?!\/)|https?:\/\/)/i.test(j)?j:"",I=`<div class="dm-admin-sidebar-header">${T?`<img src="${f(T)}" alt="" style="width:28px;height:28px;object-fit:contain;flex-shrink:0;">`:`<span data-icon="${f(w.icon||"layout")}"></span>`}<span class="dm-admin-sidebar-title">${f(w.title)}</span>${R}${Fe()}</div>`,p=o.some(l=>l&&l.type==="spacer"&&l.size==="flex")?" dm-admin-sidebar--has-flex-spacer":"",x=`${A}<nav class="dm-admin-sidebar${L}${p}">${I}${o.map(l=>re(l,k)).join("")}</nav>`,O=document.createRange();O.selectNodeContents(t);const z=O.createContextualFragment(x);t.replaceChildren(z),Domma.icons.scan(t),t.querySelector(".dm-admin-sidebar-arrange")?.addEventListener("click",()=>Ae(t));for(const l of F)G.set(l.url,k[l.url]??null);ie(t),Ze(t),t.removeAttribute("data-menu-hover"),t.removeAttribute("data-menu-active"),t.style.removeProperty("--dm-menu-highlight"),ge(t,{highlight:r});const q=document.querySelector(".dashboard-wrapper");["position","top","bottom","left","right","zIndex","maxHeight","transform"].forEach(l=>{t.style[l]=""}),t.classList.remove("dm-menu-floating"),q&&(q.style.paddingLeft="",q.style.paddingRight=""),t.__dmGutter=()=>{if(d!=="floating"||!q)return;const l=c&&c.anchor||"TL",C=Number.isFinite(Number(c?.offsetX))?Number(c.offsetX):16,h=t.classList.contains("is-collapsed")?Ee:parseInt(t.style.width,10)||t.getBoundingClientRect().width||260,N=Math.round(h+C*2)+"px";l==="R"||l==="TR"||l==="BR"?q.style.paddingRight=N:q.style.paddingLeft=N},d==="floating"&&he(t,{position:"floating",float:c||{}}),t.querySelectorAll("details").forEach(l=>{l.addEventListener("toggle",function(){const C=this.getAttribute("data-state-key");C&&S.set(C,this.open);const h=this.querySelector(":scope > .sidebar-children");if(h)if(this.open){const N=h.scrollHeight;h.style.maxHeight="0",requestAnimationFrame(()=>{h.style.maxHeight=N+"px",h.addEventListener("transitionend",function fe(){h.style.maxHeight="none",h.removeEventListener("transitionend",fe)})})}else{const N=h.scrollHeight;h.style.maxHeight=N+"px",requestAnimationFrame(()=>{h.style.maxHeight="0"})}})}),Le(t),nt(t),t.dataset.routeWired||(t.dataset.routeWired="1",M.subscribe("router:afterChange",()=>t.__dmSetActive?.()));function Z(){const l=/^#\/(dashboards|my-dashboard)\b/.test(location.hash)?"#/":location.hash||"#/";$(t).find(".sidebar-link").removeClass("active"),$(t).find(`.sidebar-link[data-url="${l}"]`).addClass("active")}Z(),t.__dmSetActive=Z}function ue(){try{document.dispatchEvent(new CustomEvent("dm:sidebar-rebuild"))}catch{}}function V(e){return`${location.origin}${location.pathname}${e}`}function _(e,n){const s=B(),t=e(W(s));if(!Pe(s,t)){E.toast("Frequent could not be saved - this browser is blocking storage.",{type:"error"});return}t.length&&ne(s,!1),me(s),n&&E.toast(n,{type:"success",duration:2500}),ue()}function me(e){je(g,e).catch(()=>E.toast("Frequent is saved in this browser only - the server could not keep it.",{type:"warning"}))}function Qe(e){if(!ne(B(),e)){E.toast("Frequent could not be saved - this browser is blocking storage.",{type:"error"});return}me(B()),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}),ue()}function et(e){const n=e.dataset.url;if(!n)return[];const s=e.querySelector(".sidebar-text")?.textContent.trim()||n,t=W(B()),i=t.includes(n),o=!!e.closest("details[data-frequent]"),a=t.indexOf(n),d=!i&&t.length>=ee;return[{header:s},{label:"Open",icon:"arrow-right",...se(n)&&{shortcut:Ie(se(n))},action:()=>{location.hash=n}},{label:"Open in a new tab",icon:"external-link",action:()=>window.open(V(n),"_blank","noopener")},{type:"divider"},i?{label:"Remove from Frequent",icon:"star",action:()=>_(u=>Me(u,n),`${s} removed from Frequent.`)}:{label:d?`Frequent is full (${ee})`:"Add to Frequent",icon:"star",disabled:d,action:()=>_(u=>Te(u,n),`${s} added to Frequent.`)},...o?[{label:"Move up",icon:"arrow-up",disabled:a<=0,action:()=>_(u=>te(u,n,-1))},{label:"Move down",icon:"arrow-down",disabled:a<0||a>=t.length-1,action:()=>_(u=>te(u,n,1))}]:[],{type:"divider"},{label:"Copy link",icon:"link",action:async()=>{try{await navigator.clipboard.writeText(V(n)),E.toast("Link copied.",{type:"success",duration:2e3})}catch{E.toast(V(n),{type:"info",duration:8e3})}}}]}function tt(e,n){const s=e.closest("details"),t=e.querySelector(".sidebar-text")?.textContent.trim()||"Folder",i=o=>n.querySelectorAll("details").forEach(a=>{a.open!==o&&(a.open=o)});return[{header:t},{label:s?.open?"Close":"Open",icon:s?.open?"chevron-up":"chevron-down",action:()=>{s&&(s.open=!s.open)}},{label:"Open every folder",icon:"chevrons-down",action:()=>i(!0)},{label:"Close every folder",icon:"chevrons-up",action:()=>i(!1)},...s?.hasAttribute("data-frequent-empty")?[{type:"divider"},{label:"Hide Frequent until I add a Tool",icon:"eye-off",action:()=>Qe(!0)}]:s?.hasAttribute("data-frequent")?[{type:"divider"},{label:"Clear Frequent",icon:"x",action:()=>_(()=>[],"Frequent cleared.")}]:[]]}function nt(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")?tt(n,e):et(n)}))}
4
+ </details>`}if(u){const y=Q(e,s),C=S.get(y)!==!1,B=e.items.map(L=>le(L,t,y,{inFrequent:!!e.frequent})).join(""),_=e.frequent?' data-frequent title="Your Frequent Tools - right-click any Tool to add or remove it"':"";return`<details data-state-key="${f(y)}"${C?" open":""}${_}>
5
+ <summary${v}${k}>${d} <span class="sidebar-text">${i}</span>${A}</summary>
6
+ <div class="sidebar-children">${B}</div>
7
+ </details>`}return`<a href="${c}" class="sidebar-link" data-url="${c}"${v}${m}${k}>${d} <span class="sidebar-text">${i}</span>${De(e,t)}${A}</a>`}async function Ke(e=[],t=[],s=null){const o=p=>p.then(x=>Array.isArray(x)?x.length:Array.isArray(x?.entries)?x.entries.length:0).catch(()=>0);F=Ze(e,s);const n=new Set(F.map(p=>p.url)),d={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=(p,x)=>z(t,p)&&!n.has(d[p])?x():Promise.resolve(0),[c,u,r,a,m,b,v,w,k,A,y,C,B,_]=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(p=>(p?.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(p=>p?.count??0).catch(()=>0)??Promise.resolve(0))]),L={};await Promise.all(F.map(async p=>{const x=await ce(p);x&&(L[p.url]=x)}));const O={"#/pages":c,"#/media":u,"#/menus":r,"#/context-menus":a,"#/theme":m,"#/collections":b,"#/forms":v,"#/views":w,"#/actions":k,"#/blocks":A,"#/components":y,"#/users":C,"#/plugins":B,"#/system/notifications":_},W={};for(const[p,x]of Object.entries(O)){if(n.has(p))continue;const[D,U]=Ge[p]??["item","items"],j=K(x);j&&(W[p]={...j,label:`${j.count} ${j.count===1?D:U}`})}return{...W,...L}}let F=[];const Ve="dm:sidebar-count",Xe=6e4;function Ze(e=[],t=null){const s=[],o=n=>{if(n&&!(t&&typeof n.url=="string"&&n.url.startsWith("#/")&&!he(t,n.url.slice(1)))){n.url&&typeof n.countUrl=="string"&&n.countUrl&&s.push({url:n.url,countUrl:n.countUrl,text:n.text||"",countLabel:n.countLabel||"",inventory:n.inventory===!0});for(const d of[...n.children||[],...n.items||[]])o(d)}};for(const n of e??[])o(n?.item??n);return s}async function ce(e){try{const t=new Date,s=`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`,o=e.countUrl+(e.countUrl.includes("?")?"&":"?")+`today=${s}`;return K(await H.get(o),{fallbackLabel:e.countLabel||e.text})}catch{return null}}function de(e,t,s){const o=[...e.querySelectorAll("a.sidebar-link")].filter(n=>n.dataset.url===t);if(o.length){for(const n of o){const d=n.querySelector(".sidebar-badge[data-count-for]");if(!s||!(s.count>0)&&!s.showZero)d?.remove();else{const i=document.createRange();i.selectNodeContents(n);const c=i.createContextualFragment(ie(t,s)).firstElementChild;d?d.replaceWith(c):n.querySelector(".sidebar-text")?.after(c)}}V.set(t,s),ae(e)}}const V=new Map;async function X(e,t=null){const s=t?F.filter(o=>o.url===t):F;await Promise.all(s.map(async o=>{const n=await ce(o),d=V.get(o.url);JSON.stringify(d??null)!==JSON.stringify(n)&&de(e,o.url,n)}))}function Ye(e){e.dataset.liveCounts||(e.dataset.liveCounts="1",window.addEventListener(Ve,t=>{const s=t.detail||{};if(s.url)if(s.count!==void 0){const o=F.find(n=>n.url===s.url);de(e,s.url,K(s,{fallbackLabel:o?.countLabel||o?.text||""}))}else X(e,s.url)}),setInterval(()=>{!document.hidden&&document.body.contains(e)&&X(e)},Xe),document.addEventListener("visibilitychange",()=>{document.hidden||X(e)}),Je(e))}function ue(e,t,s){const o=(i,c,u)=>{const r=document.createElement(i);return c&&(r.className=c),u!=null&&(r.textContent=String(u)),r};if(!s){e.textContent=t;return}e.appendChild(o("strong","dm-badge-pop-title",s.title||t)),(s.rows?.length||!s.items?.length)&&e.appendChild(o("p","dm-badge-pop-sub",t));const n=Array.isArray(s.rows)?s.rows.slice(0,6):[];if(n.length){const i=o("dl","dm-badge-pop-rows");for(const c of n)i.appendChild(o("dt",null,c.label)),i.appendChild(o("dd",c.tone==="danger"?"is-danger":null,c.value));e.appendChild(i)}const d=Array.isArray(s.items)?s.items.slice(0,6):[];if(d.length){const i=o("ul","dm-badge-pop-items");for(const c of d){const u=o("li",c.tone==="danger"?"is-danger":null);u.appendChild(o("span","dm-badge-pop-text",c.text)),c.meta&&u.appendChild(o("span","dm-badge-pop-meta",c.meta)),i.appendChild(u)}e.appendChild(i)}else s.empty&&e.appendChild(o("p","dm-badge-pop-empty",s.empty))}function Je(e){let t=null,s=null;const o=()=>e.classList.contains("is-collapsed"),n=()=>{clearTimeout(s),t?.remove(),t=null},d=(r,a)=>{n(),s=setTimeout(()=>{t=document.createElement("div"),t.className="dm-help-pop dm-badge-pop",t.setAttribute("role","tooltip"),a(t),document.body.appendChild(t);const m=r.getBoundingClientRect(),b=t.getBoundingClientRect(),v=Math.min(m.right+10,innerWidth-b.width-8);t.style.left=`${Math.max(8,v)}px`,t.style.top=`${Math.max(8,Math.min(m.top+m.height/2-b.height/2,innerHeight-b.height-8))}px`},150)},i=r=>{const a=r?.dataset.badgeLabel;if(!a)return n();d(r,m=>ue(m,a,R.get(r.dataset.countFor)))},c=r=>{const a=r.querySelector(":scope > .sidebar-text")?.textContent.trim()||"",m=r.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");d(r,b=>{const v=m&&R.get(m.dataset.countFor);if(v)return ue(b,m.dataset.badgeLabel,{...v,title:v.title||a});if(!m){b.textContent=a;return}const w=document.createElement("strong");w.className="dm-badge-pop-title",w.textContent=a;const k=document.createElement("p");k.className="dm-badge-pop-sub",k.textContent=m.dataset.badgeLabel,b.append(w,k)})},u=r=>{const a=r.closest?.("a.sidebar-link, summary");return a&&e.contains(a)?a:null};e.addEventListener("mouseover",r=>{if(o()){const m=u(r.target);m&&!m.contains(r.relatedTarget)&&c(m);return}const a=r.target.closest?.(".sidebar-badge[data-badge-label]");a&&i(a)}),e.addEventListener("mouseout",r=>{if(o()){const a=u(r.target);a&&!a.contains(r.relatedTarget)&&n();return}r.target.closest?.(".sidebar-badge[data-badge-label]")&&n()}),e.addEventListener("focusin",r=>{const a=r.target.closest?.("a, summary");if(o()&&a&&e.contains(a)&&a.querySelector(":scope > .sidebar-text"))return c(a);const m=a?.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");m?i(m):n()}),e.addEventListener("focusout",n),e.addEventListener("click",n),window.addEventListener("scroll",n,!0),window.addEventListener("hashchange",n)}async function Qe(e=[],t=null){if(t?.title)return t;if(!z(e,"settings"))return{title:"Domma CMS"};try{const s=await g.settings.get();return{...s?.adminBrand,title:s?.adminBrand?.title||s?.title||"Domma CMS"}}catch{return{title:"Domma CMS"}}}export async function renderAdminSidebar({mount:e,permissions:t,adminScope:s=null,brand:o=null}){const n=$(e).get(0);if(!n)return;const d=Pe(g,T()).catch(()=>{});let i,c=null,u=null,r=null,a=null,m=null;try{const l=await g.get("/sidebar/menu");i=Array.isArray(l?.items)?l.items:null,c=l?.variant||null,u=l?.position||null,r=l?.style||null,a=l?.float||null,m=l?.highlight||null}catch{i=null}(!i||!i.some(l=>l&&(!l.hidden||l.restricted)))&&(console.warn("[admin-sidebar] No admin-sidebar menu found; using fallback tree"),i=Ue.slice());let b=[];try{b=await H.get("/api/sidebar/registered-items")||[]}catch{}i=we(i,ee),i=ve(i,b.filter(l=>(l?.item??l)?.url!==ee)),i=xe(i),i=Se(i,[$e()]);const v=await ze(t);v.length&&We(i,v),i=qe(i,t,s),i=ke(i),await d;const w=Be(i,G(T()),{hidden:He(T())});w&&(i=[w,...i]),Ie(i);const[k,A]=await Promise.all([Ke(b.filter(l=>z(t,l?.item?.permission)),t,s).catch(()=>({})),Qe(t,o)]),y=c?` dm-admin-sidebar--${f(c)}`:"";function C(l,q="px"){if(l==null)return l;const h=String(l).trim();return/^\d+(\.\d+)?$/.test(h)?`${h}${q}`:h}let B="";if(r){const l=[];r.fontFamily&&l.push(`font-family: ${r.fontFamily}, sans-serif`),r.fontSize&&l.push(`font-size: ${C(r.fontSize)}`),r.fontWeight&&l.push(`font-weight: ${r.fontWeight}`),r.letterSpacing&&l.push(`letter-spacing: ${C(r.letterSpacing,"em")}`);const q=[];if(l.length&&q.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 { ${l.join("; ")} }`),r.iconSize){const h=C(r.iconSize);q.push(`#admin-sidebar .dm-admin-sidebar [data-icon], #admin-sidebar .dm-admin-sidebar [data-icon] svg { width: ${h} !important; height: ${h} !important; }`)}q.length&&(B=`<style data-admin-sidebar-style>${q.join(" ")}</style>`)}const _=Ae(t)?Ce():"",L=String(A.logo||"").trim(),O=/^(?:\/(?!\/)|https?:\/\/)/i.test(L)?L:"",p=`<div class="dm-admin-sidebar-header">${O?`<img src="${f(O)}" alt="" style="width:28px;height:28px;object-fit:contain;flex-shrink:0;">`:`<span data-icon="${f(A.icon||"layout")}"></span>`}<span class="dm-admin-sidebar-title">${f(A.title)}</span>${_}${Le()}</div>`,x=i.some(l=>l&&l.type==="spacer"&&l.size==="flex")?" dm-admin-sidebar--has-flex-spacer":"",D=`${B}<nav class="dm-admin-sidebar${y}${x}">${p}${i.map(l=>le(l,k)).join("")}</nav>`,U=document.createRange();U.selectNodeContents(n);const j=U.createContextualFragment(D);n.replaceChildren(j),Domma.icons.scan(n),n.querySelector(".dm-admin-sidebar-arrange")?.addEventListener("click",()=>Fe(n));for(const l of F)V.set(l.url,k[l.url]??null);ae(n),Ye(n),n.removeAttribute("data-menu-hover"),n.removeAttribute("data-menu-active"),n.style.removeProperty("--dm-menu-highlight"),be(n,{highlight:m});const N=document.querySelector(".dashboard-wrapper");["position","top","bottom","left","right","zIndex","maxHeight","transform"].forEach(l=>{n.style[l]=""}),n.classList.remove("dm-menu-floating"),N&&(N.style.paddingLeft="",N.style.paddingRight=""),n.__dmGutter=()=>{if(u!=="floating"||!N)return;const l=a&&a.anchor||"TL",q=Number.isFinite(Number(a?.offsetX))?Number(a.offsetX):16,h=n.classList.contains("is-collapsed")?Me:parseInt(n.style.width,10)||n.getBoundingClientRect().width||260,P=Math.round(h+q*2)+"px";l==="R"||l==="TR"||l==="BR"?N.style.paddingRight=P:N.style.paddingLeft=P},u==="floating"&&ge(n,{position:"floating",float:a||{}}),n.querySelectorAll("details").forEach(l=>{l.addEventListener("toggle",function(){const q=this.getAttribute("data-state-key");q&&S.set(q,this.open);const h=this.querySelector(":scope > .sidebar-children");if(h)if(this.open){const P=h.scrollHeight;h.style.maxHeight="0",requestAnimationFrame(()=>{h.style.maxHeight=P+"px",h.addEventListener("transitionend",function pe(){h.style.maxHeight="none",h.removeEventListener("transitionend",pe)})})}else{const P=h.scrollHeight;h.style.maxHeight=P+"px",requestAnimationFrame(()=>{h.style.maxHeight="0"})}})}),Ee(n),st(n),n.dataset.routeWired||(n.dataset.routeWired="1",M.subscribe("router:afterChange",()=>n.__dmSetActive?.()));function Y(){const l=/^#\/(dashboards|my-dashboard)\b/.test(location.hash)?"#/":location.hash||"#/";$(n).find(".sidebar-link").removeClass("active"),$(n).find(`.sidebar-link[data-url="${l}"]`).addClass("active")}Y(),n.__dmSetActive=Y}function me(){try{document.dispatchEvent(new CustomEvent("dm:sidebar-rebuild"))}catch{}}function Z(e){return`${location.origin}${location.pathname}${e}`}function I(e,t){const s=T(),o=e(G(s));if(!Re(s,o)){E.toast("Frequent could not be saved - this browser is blocking storage.",{type:"error"});return}o.length&&se(s,!1),fe(s),t&&E.toast(t,{type:"success",duration:2500}),me()}function fe(e){Ne(g,e).catch(()=>E.toast("Frequent is saved in this browser only - the server could not keep it.",{type:"warning"}))}function et(e){if(!se(T(),e)){E.toast("Frequent could not be saved - this browser is blocking storage.",{type:"error"});return}fe(T()),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}),me()}function tt(e){const t=e.dataset.url;if(!t)return[];const s=e.querySelector(".sidebar-text")?.textContent.trim()||t,o=G(T()),n=o.includes(t),d=!!e.closest("details[data-frequent]"),i=o.indexOf(t),c=!n&&o.length>=te;return[{header:s},{label:"Open",icon:"arrow-right",...oe(t)&&{shortcut:Oe(oe(t))},action:()=>{location.hash=t}},{label:"Open in a new tab",icon:"external-link",action:()=>window.open(Z(t),"_blank","noopener")},{type:"divider"},n?{label:"Remove from Frequent",icon:"star",action:()=>I(u=>je(u,t),`${s} removed from Frequent.`)}:{label:c?`Frequent is full (${te})`:"Add to Frequent",icon:"star",disabled:c,action:()=>I(u=>Te(u,t),`${s} added to Frequent.`)},...d?[{label:"Move up",icon:"arrow-up",disabled:i<=0,action:()=>I(u=>ne(u,t,-1))},{label:"Move down",icon:"arrow-down",disabled:i<0||i>=o.length-1,action:()=>I(u=>ne(u,t,1))}]:[],{type:"divider"},{label:"Copy link",icon:"link",action:async()=>{try{await navigator.clipboard.writeText(Z(t)),E.toast("Link copied.",{type:"success",duration:2e3})}catch{E.toast(Z(t),{type:"info",duration:8e3})}}}]}function nt(e,t){const s=e.closest("details"),o=e.querySelector(".sidebar-text")?.textContent.trim()||"Folder",n=d=>t.querySelectorAll("details").forEach(i=>{i.open!==d&&(i.open=d)});return[{header:o},{label:s?.open?"Close":"Open",icon:s?.open?"chevron-up":"chevron-down",action:()=>{s&&(s.open=!s.open)}},{label:"Open every folder",icon:"chevrons-down",action:()=>n(!0)},{label:"Close every folder",icon:"chevrons-up",action:()=>n(!1)},...s?.hasAttribute("data-frequent-empty")?[{type:"divider"},{label:"Hide Frequent until I add a Tool",icon:"eye-off",action:()=>et(!0)}]:s?.hasAttribute("data-frequent")?[{type:"divider"},{label:"Clear Frequent",icon:"x",action:()=>I(()=>[],"Frequent cleared.")}]:[]]}function st(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:t=>t.matches("summary")?nt(t,e):tt(t)}))}
@@ -9,6 +9,10 @@
9
9
  <!-- Hidden file picker driven by the Import banner action -->
10
10
  <input class="blx-import-file" type="file" accept=".dmblock.json,application/json" hidden>
11
11
 
12
+ <p class="blx-notice" data-bind-hidden="!tagNote.value">
13
+ <span data-bind-text="tagNote.value"></span> <button type="button" class="blx-link" data-on-click="showAllTags">Show all blocks</button>
14
+ </p>
15
+
12
16
  <div class="blx-card">
13
17
  <div class="blx-bar">
14
18
  <div class="blx-seg" role="group" aria-label="Show">
@@ -17,7 +21,7 @@
17
21
  <button type="button" data-on-click="showUnused" data-bind-class="filter.value === 'unused' && 'is-on'">Unused <b data-bind-text="counts.value.unused"></b></button>
18
22
  </div>
19
23
  <input type="search" class="form-input blx-search" placeholder="Search blocks…" aria-label="Search blocks" data-model="query.value">
20
- <span data-help-title="The list" data-help="Click a block to edit it. Right-click it (or ⋮, or Shift+F10) to copy its shortcode, see where it is used, duplicate, export or delete it. In use counts pages and views that place it; a block a plugin uses from its own code does not count, so check before deleting one marked unused."></span>
24
+ <span data-help-title="The list" data-help="Click a block to edit it. Right-click it (or ⋮, or Shift+F10) to copy its shortcode, see where it is used, duplicate, export or delete it. In use counts pages and views that place it; a block a plugin uses from its own code does not count, so check before deleting one marked unused. Tags link blocks together - add or remove one from the right-click menu, and pick which tag to show (or manage the tags) with the cog above."></span>
21
25
  </div>
22
26
  <p class="blx-note" data-bind-hidden="!loading.value">Loading…</p>
23
27
  <p class="blx-note is-bad" data-bind-hidden="!loadError.value" data-bind-text="loadError.value"></p>
@@ -55,6 +59,10 @@
55
59
  .blx-chip { display: inline-flex; align-items: center; font-size: var(--dm-text-xs); font-weight: 600; padding: .1rem .5rem; border-radius: 999px; white-space: nowrap;
56
60
  background: color-mix(in srgb, var(--dm-text) 10%, transparent); color: var(--dm-text-muted); }
57
61
  .blx-chip.is-ok { background: color-mix(in srgb, var(--dm-success) 16%, transparent); color: var(--dm-success); }
62
+ .blx-chip.is-tag { background: color-mix(in srgb, var(--dm-primary) 12%, transparent); color: var(--dm-primary); max-width: 14rem; overflow: hidden; text-overflow: ellipsis; }
63
+ .blx-notice { margin: 0; padding: .5rem .75rem; border-radius: var(--dm-radius-md); font-size: var(--dm-text-sm);
64
+ background: color-mix(in srgb, var(--dm-info, var(--dm-primary)) 12%, transparent); color: var(--dm-text); }
65
+ .blx-link { border: 0; background: none; padding: 0; font: inherit; color: var(--dm-primary); text-decoration: underline; cursor: pointer; }
58
66
  .blx-chip.is-info { background: color-mix(in srgb, var(--dm-info, var(--dm-primary)) 14%, transparent); color: var(--dm-info, var(--dm-primary)); }
59
67
  .blx-chip.is-warn { background: color-mix(in srgb, var(--dm-warning) 18%, transparent); color: var(--dm-warning); }
60
68
  .blx-uses { min-width: 2.5rem; text-align: right; font-variant-numeric: tabular-nums; color: var(--dm-text-muted); font-size: var(--dm-text-sm); }