domma-cms 0.74.0 → 0.76.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/CLAUDE.md +3 -1
  2. package/admin/js/app.js +4 -4
  3. package/admin/js/lib/actions-arrange.js +1 -0
  4. package/admin/js/lib/api-endpoints-arrange.js +1 -0
  5. package/admin/js/lib/api-tokens-arrange.js +3 -0
  6. package/admin/js/lib/blocks-arrange.js +1 -0
  7. package/admin/js/lib/collections-arrange.js +1 -0
  8. package/admin/js/lib/components-arrange.js +1 -0
  9. package/admin/js/lib/context-menus-arrange.js +1 -0
  10. package/admin/js/lib/effects-arrange.js +3 -0
  11. package/admin/js/lib/forms-arrange.js +1 -0
  12. package/admin/js/lib/layouts-arrange.js +1 -0
  13. package/admin/js/lib/media-arrange.js +1 -0
  14. package/admin/js/lib/menus-arrange.js +1 -0
  15. package/admin/js/lib/my-profile-arrange.js +1 -0
  16. package/admin/js/lib/notifications-arrange.js +1 -0
  17. package/admin/js/lib/pages-arrange.js +1 -0
  18. package/admin/js/lib/plugins-arrange.js +2 -0
  19. package/admin/js/lib/roles-arrange.js +1 -0
  20. package/admin/js/lib/search-settings-arrange.js +1 -0
  21. package/admin/js/lib/sidebar-renderer.js +3 -3
  22. package/admin/js/lib/site-settings-arrange.js +1 -0
  23. package/admin/js/lib/theme-settings-arrange.js +1 -0
  24. package/admin/js/lib/users-arrange.js +1 -0
  25. package/admin/js/lib/views-arrange.js +1 -0
  26. package/admin/js/templates/actions-list.html +73 -14
  27. package/admin/js/templates/api-endpoints.html +63 -10
  28. package/admin/js/templates/api-tokens.html +83 -10
  29. package/admin/js/templates/blocks.html +76 -15
  30. package/admin/js/templates/collections.html +108 -52
  31. package/admin/js/templates/components.html +82 -15
  32. package/admin/js/templates/context-menus.html +55 -13
  33. package/admin/js/templates/effects.html +155 -72
  34. package/admin/js/templates/forms.html +67 -14
  35. package/admin/js/templates/layouts.html +126 -43
  36. package/admin/js/templates/media.html +99 -13
  37. package/admin/js/templates/menus.html +57 -11
  38. package/admin/js/templates/my-profile.html +100 -12
  39. package/admin/js/templates/notifications.html +70 -10
  40. package/admin/js/templates/pages.html +78 -23
  41. package/admin/js/templates/plugins.html +100 -28
  42. package/admin/js/templates/roles.html +78 -7
  43. package/admin/js/templates/search.html +193 -216
  44. package/admin/js/templates/settings.html +404 -472
  45. package/admin/js/templates/theme.html +164 -132
  46. package/admin/js/templates/users.html +69 -9
  47. package/admin/js/templates/views-list.html +63 -9
  48. package/admin/js/views/actions-list.js +37 -1
  49. package/admin/js/views/api-endpoints.js +11 -7
  50. package/admin/js/views/api-tokens.js +62 -8
  51. package/admin/js/views/blocks.js +34 -5
  52. package/admin/js/views/collections.js +36 -1
  53. package/admin/js/views/components.js +27 -5
  54. package/admin/js/views/context-menus.js +10 -5
  55. package/admin/js/views/effects.js +31 -1
  56. package/admin/js/views/forms.js +57 -1
  57. package/admin/js/views/layouts.js +81 -1
  58. package/admin/js/views/media.js +40 -1
  59. package/admin/js/views/menus.js +11 -5
  60. package/admin/js/views/my-profile.js +1 -1
  61. package/admin/js/views/notifications.js +18 -1
  62. package/admin/js/views/pages.js +28 -8
  63. package/admin/js/views/plugins.js +97 -39
  64. package/admin/js/views/roles.js +49 -4
  65. package/admin/js/views/search.js +7 -1
  66. package/admin/js/views/settings.js +13 -3
  67. package/admin/js/views/theme.js +55 -1
  68. package/admin/js/views/users.js +13 -4
  69. package/admin/js/views/views-list.js +11 -1
  70. package/package.json +1 -1
  71. package/plugins/_lib/admin/mail/compose-window.js +97 -40
  72. package/plugins/_lib/admin/mail/contacts.js +4 -1
  73. package/plugins/_lib/admin/mail/diagnostics-section.js +4 -1
  74. package/plugins/_lib/admin/mail/folder-tree.js +1 -1
  75. package/plugins/_lib/admin/mail/image-senders-section.js +69 -90
  76. package/plugins/_lib/admin/mail/mail.css +1 -1
  77. package/plugins/_lib/admin/mail/mail.html +1 -1
  78. package/plugins/_lib/admin/mail/reader-view.js +303 -44
  79. package/plugins/_lib/admin/mail/when-closed.js +26 -0
  80. package/plugins/_lib/mail/readRoutes.js +43 -1
  81. package/plugins/_lib/mail/unreadBadge.js +42 -0
  82. package/plugins/free-tier.lock.json +5 -5
  83. package/plugins/mail-reader/CLAUDE.md +1 -0
  84. package/plugins/mail-reader/plugin.js +3 -1
  85. package/plugins/mail-reader/plugin.json +5 -4
  86. package/server/routes/api/actions.js +64 -2
  87. package/server/routes/api/api-endpoints.js +41 -0
  88. package/server/routes/api/api-tokens.js +56 -0
  89. package/server/routes/api/auth.js +7 -1
  90. package/server/routes/api/blocks.js +170 -8
  91. package/server/routes/api/collections.js +66 -2
  92. package/server/routes/api/components.js +46 -0
  93. package/server/routes/api/context-menus.js +8 -0
  94. package/server/routes/api/effects.js +68 -13
  95. package/server/routes/api/forms.js +123 -10
  96. package/server/routes/api/layouts.js +93 -2
  97. package/server/routes/api/media.js +56 -1
  98. package/server/routes/api/menus.js +53 -6
  99. package/server/routes/api/notifications.js +140 -14
  100. package/server/routes/api/pages.js +46 -0
  101. package/server/routes/api/plugins.js +135 -4
  102. package/server/routes/api/roles.js +111 -0
  103. package/server/routes/api/search.js +38 -4
  104. package/server/routes/api/settings.js +52 -0
  105. package/server/routes/api/theme.js +55 -0
  106. package/server/routes/api/users.js +53 -1
  107. package/server/routes/api/views.js +27 -3
  108. package/server/server.js +28 -1
  109. package/server/services/contextMenus.js +41 -0
  110. package/server/services/plugins.js +7 -0
  111. package/server/services/roles.js +3 -0
  112. package/server/services/search.js +58 -0
  113. package/server/services/users.js +14 -0
  114. package/server/services/views.js +38 -0
  115. package/admin/js/templates/plugin-marketplace.html +0 -29
  116. package/admin/js/views/plugin-marketplace.js +0 -1
package/CLAUDE.md CHANGED
@@ -201,7 +201,9 @@ changed here or uncommitted. `tests/plugins/free-tier-lock.test.js` fails on a d
201
201
  ships them exactly as locked (no minify, no ?v= stamping), and the updater (`bin/update.js`) never
202
202
  replaces a site's plugin with an OLDER version than it has (`bin/lib/plugin-version.js`).
203
203
 
204
- **Sidebar badges** come from `countUrl` on a sidebar entry (core via `registerSidebarItem`, plugins in
204
+ **Sidebar badges** come from `countUrl` on a sidebar entry (core registers one for every Content and Data
205
+ screen, Projects too - `inventory: true` on those keeps a how-many count out of a closed folder's total, and
206
+ a registered countUrl replaces the core list-fetch count for that url, so it must keep that number) (core via `registerSidebarItem`, plugins in
205
207
  `admin.sidebar`), answering `{count, label, tone, details}`. Only two tones change the look: `danger`
206
208
  (red - late) and `live` (pulsing dot - happening now); `info`/`attention` draw the ordinary badge.
207
209
  `details` - `{title, rows: [{label, value, tone?}], items: [{text, meta?, tone?}], empty}` - fills the
package/admin/js/app.js CHANGED
@@ -1,6 +1,6 @@
1
- import{views as V}from"./views/index.js";import{withPluginChrome as T}from"./lib/plugin-chrome.js";import{api as c,getUser as L,isAuthenticated as d,logout as F}from"./api.js";import{installHttpInterceptor as H}from"./http-interceptor.js";import{installResizableSlideovers as q}from"./lib/slideover-resizable.js";import{installActivityThrobber as K}from"./lib/activity-throbber.js";import{isPathAllowed as z,redirectFor as J}from"./lib/admin-scope.js";$(()=>{H(),K(),q(),(async()=>{const{applyAdminTheme:t,refreshAdminTheme:e}=await import("./lib/admin-theme.js");d()?await e(c,{init:!0}):t({dommaTheme:"charcoal-dark"},{init:!0})})();let l=null,r="/",u=!1;function v(t){const e=C[t]||J(l,t);return e||((t.split("?")[0]||"/")==="/"&&r&&r!=="/"?C[r]||r:null)}const C={};R.use(async(t,e,o)=>{if(t.path==="/login"||t.path==="/reset-password")return o();if(!d()){R.navigate("/login");return}const i=v(t.path);if(i){R.navigate(i);return}o()});async function h(){try{const t=await c.get("/auth/permissions");l=t.adminScope||null,r=t.adminHome||"/";const e=t.permissions||[];return u=z(l,"/settings")&&e.some(o=>o==="settings"||o.startsWith("settings.")),e}catch{return l=null,r="/",u=!1,[]}}document.addEventListener("dm:admin-home-changed",()=>{h()});let D=null,b=!1;document.addEventListener("dm:sidebar-rebuild",()=>{if(D){b=!0;return}D=(async()=>{do{b=!1;try{await P(await h())}catch{}}while(b)})().finally(()=>{D=null})});async function P(t){const{renderAdminSidebar:e}=await import("./lib/sidebar-renderer.js");await e({mount:"#admin-sidebar",permissions:t,adminScope:l})}M.subscribe("router:afterChange",({to:t})=>{(t.path==="/login"||t.path==="/reset-password")&&(p&&(clearInterval(p),p=null),l=null,r="/",u=!1)});const x=[{path:"/",view:"dashboard",title:"Dashboard - 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 Plugin - 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/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:"/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:"/notes",view:"notes",title:"Notes - Domma CMS"},{path:"/todo",view:"todo",title:"Todo - Domma CMS"},{path:"/analytics",view:"analytics",title:"Analytics - Domma CMS"},{path:"/contacts",view:"contacts",title:"Contacts - 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:e})=>{if(!(t.path==="/login"||t.path==="/reset-password")){if($("#admin-sidebar").show(),$("#admin-topbar").show(),e?.path==="/login"||e?.path==="/reset-password"){$("#topbar-user-name").remove(),import("./lib/admin-theme.js").then(a=>a.refreshAdminTheme(c)),await N();const o=await h(),i=v(t.path);i&&R.navigate(i),P(o);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 g=w(s.data?.title||""),A=w((s.data?.body||"").slice(0,120));E.toast(`${g} - ${A}`,{type:s.data?.severity==="critical"?"error":"warning",duration:0})}}catch{}}I()}}),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 W=["#field-project","#menu-project","#collection-project","#endpoint-project","#action-project","#view-project","#block-project","#component-project","#role-project",'[name="project"]','[name="ownedByProject"]'];function _(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 e=t?String(t.path).split("?")[0]:"";if(!t||!_(e))return;const i=new URLSearchParams(location.hash.split("?")[1]||"").get("project");if(!i||!/\/new$/.test(e))return;let a=0;const n=60,s=1500,g=()=>{a+=n;for(const A of W){const f=document.querySelector(A);if(!f)continue;if(Array.from(f.options||[]).find(O=>O.value===i)){f.value=i,f.dispatchEvent(new Event("change",{bubbles:!0}));return}}a<s&&setTimeout(g,n)};setTimeout(g,n)});const k="cms_card_states",y=S.get(k)||{};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&&y[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 e=$(this).closest(".card");e.toggleClass("card-collapsed");const o=$(this).find("h2, h3").first().text().trim();o&&(y[o]=e.hasClass("card-collapsed")?"collapsed":"open",S.set(k,y))}),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash==="#/login"||window.location.hash.startsWith("#/reset-password"))return;const e=document.querySelector("#view-container .view-header button.btn-primary");e&&(t.preventDefault(),e.click())});const m={...V},B={notes:{displayName:"Notes",icon:"edit-3"},todo:{displayName:"Todo",icon:"check-square"},analytics:{displayName:"Analytics",icon:"chart-bar"},contacts:{displayName:"Contacts",icon:"users"},projects:{displayName:"Projects",icon:"folder"}};for(const[t,e]of Object.entries(B))m[t]&&(m[t]=T(m[t],{plugin:t,licence:"Built in",...e}));const j=[...x];async function N(){if(d())try{const t=await c.plugins.adminConfig();t.routes?.length&&j.push(...t.routes);for(const[e,o]of Object.entries(t.views||{}))try{const i=await import(`/plugins/${o.entry}`);m[e]=T(i[o.exportName],(t.meta||{})[e])}catch{}for(const[e,{route:o}]of Object.entries(t.takeovers||{}))o&&(C[`/${e}`]=o);for(const{id:e,href:o}of t.css||[])if(!document.getElementById(e)){const i=document.createElement("link");i.id=e,i.rel="stylesheet",i.href=o,document.head.appendChild(i)}}catch{}}function I(){const t=L();if(!t||$("#topbar-user-name").length)return;const o={"super-admin":"Super Admin",admin:"Admin",user:"User"}[t.role]||t.role;$("#topbar-user").html(`
2
- <span id="topbar-user-name" class="topbar-user-name">${w(t.name)}</span>
3
- <span class="topbar-role-badge topbar-role-badge--${w(t.role)}">${o}</span>
1
+ import{views as F}from"./views/index.js";import{withPluginChrome as x}from"./lib/plugin-chrome.js";import{api as c,getUser as L,isAuthenticated as d,logout as H}from"./api.js";import{installHttpInterceptor as q}from"./http-interceptor.js";import{installResizableSlideovers as K}from"./lib/slideover-resizable.js";import{installActivityThrobber as z}from"./lib/activity-throbber.js";import{isPathAllowed as J,redirectFor as Y}from"./lib/admin-scope.js";$(()=>{q(),z(),K(),(async()=>{const{applyAdminTheme:t,refreshAdminTheme:e}=await import("./lib/admin-theme.js");d()?await e(c,{init:!0}):t({dommaTheme:"charcoal-dark"},{init:!0})})();let l=null,r="/",u=!1,h=null;function C(t){const e=D[t]||Y(l,t);return e||((t.split("?")[0]||"/")==="/"&&r&&r!=="/"?D[r]||r:null)}const D={};R.use(async(t,e,o)=>{if(t.path==="/login"||t.path==="/reset-password")return o();if(!d()){R.navigate("/login");return}const i=C(t.path);if(i){R.navigate(i);return}o()});async function w(){try{const t=await c.get("/auth/permissions");l=t.adminScope||null,r=t.adminHome||"/";const e=t.permissions||[];return u=J(l,"/settings")&&e.some(o=>o==="settings"||o.startsWith("settings.")),h=e.some(o=>o==="notifications"||o.startsWith("notifications.")),e}catch{return l=null,r="/",u=!1,h=null,[]}}document.addEventListener("dm:admin-home-changed",()=>{w()});let b=null,y=!1;document.addEventListener("dm:sidebar-rebuild",()=>{if(b){y=!0;return}b=(async()=>{do{y=!1;try{await P(await w())}catch{}}while(y)})().finally(()=>{b=null})});async function P(t){const{renderAdminSidebar:e}=await import("./lib/sidebar-renderer.js");await e({mount:"#admin-sidebar",permissions:t,adminScope:l})}M.subscribe("router:afterChange",({to:t})=>{(t.path==="/login"||t.path==="/reset-password")&&(p&&(clearInterval(p),p=null),l=null,r="/",u=!1,h=null)});const B=[{path:"/",view:"dashboard",title:"Dashboard - 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 Plugin - 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/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:"/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:"/notes",view:"notes",title:"Notes - Domma CMS"},{path:"/todo",view:"todo",title:"Todo - Domma CMS"},{path:"/analytics",view:"analytics",title:"Analytics - Domma CMS"},{path:"/contacts",view:"contacts",title:"Contacts - 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:e})=>{if(!(t.path==="/login"||t.path==="/reset-password")){if($("#admin-sidebar").show(),$("#admin-topbar").show(),e?.path==="/login"||e?.path==="/reset-password"){$("#topbar-user-name").remove(),import("./lib/admin-theme.js").then(a=>a.refreshAdminTheme(c)),await I();const o=await w(),i=C(t.path);i&&R.navigate(i),P(o);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=g(s.data?.title||""),A=g((s.data?.body||"").slice(0,120));E.toast(`${f} - ${A}`,{type:s.data?.severity==="critical"?"error":"warning",duration:0})}}catch{}}U()}}),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 W=["#field-project","#menu-project","#collection-project","#endpoint-project","#action-project","#view-project","#block-project","#component-project","#role-project",'[name="project"]','[name="ownedByProject"]'];function _(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 e=t?String(t.path).split("?")[0]:"";if(!t||!_(e))return;const i=new URLSearchParams(location.hash.split("?")[1]||"").get("project");if(!i||!/\/new$/.test(e))return;let a=0;const n=60,s=1500,f=()=>{a+=n;for(const A of W){const v=document.querySelector(A);if(!v)continue;if(Array.from(v.options||[]).find(O=>O.value===i)){v.value=i,v.dispatchEvent(new Event("change",{bubbles:!0}));return}}a<s&&setTimeout(f,n)};setTimeout(f,n)});const k="cms_card_states",j=S.get(k)||{};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 e=$(this).closest(".card");e.toggleClass("card-collapsed");const o=$(this).find("h2, h3").first().text().trim();o&&(j[o]=e.hasClass("card-collapsed")?"collapsed":"open",S.set(k,j))}),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash==="#/login"||window.location.hash.startsWith("#/reset-password"))return;const e=document.querySelector("#view-container .view-header button.btn-primary");e&&(t.preventDefault(),e.click())});const m={...F},V={notes:{displayName:"Notes",icon:"edit-3"},todo:{displayName:"Todo",icon:"check-square"},analytics:{displayName:"Analytics",icon:"chart-bar"},contacts:{displayName:"Contacts",icon:"users"},projects:{displayName:"Projects",icon:"folder"},pages:{displayName:"Pages",icon:"file-text"},media:{displayName:"Media",icon:"image"},menus:{displayName:"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"}};for(const[t,e]of Object.entries(V))m[t]&&(m[t]=x(m[t],{plugin:t,licence:"Built in",...e}));const N=[...B];async function I(){if(d())try{const t=await c.plugins.adminConfig();t.routes?.length&&N.push(...t.routes);for(const[e,o]of Object.entries(t.views||{}))try{const i=await import(`/plugins/${o.entry}`);m[e]=x(i[o.exportName],(t.meta||{})[e])}catch{}for(const[e,{route:o}]of Object.entries(t.takeovers||{}))o&&(D[`/${e}`]=o);for(const{id:e,href:o}of t.css||[])if(!document.getElementById(e)){const i=document.createElement("link");i.id=e,i.rel="stylesheet",i.href=o,document.head.appendChild(i)}}catch{}}function U(){const t=L();if(!t||$("#topbar-user-name").length)return;const o={"super-admin":"Super Admin",admin:"Admin",user:"User"}[t.role]||t.role;$("#topbar-user").html(`
2
+ <span id="topbar-user-name" class="topbar-user-name">${g(t.name)}</span>
3
+ <span class="topbar-role-badge topbar-role-badge--${g(t.role)}">${o}</span>
4
4
  `),$("#topbar-actions").html(`
5
5
  <a href="#/system/notifications" id="topbar-bell" class="topbar-action-link" data-tooltip="Notifications" data-tooltip-placement="bottom" hidden>
6
6
  <span data-icon="bell"></span>
@@ -18,4 +18,4 @@ import{views as V}from"./views/index.js";import{withPluginChrome as T}from"./lib
18
18
  <span data-icon="log-out"></span>
19
19
  <span>Sign out</span>
20
20
  </a>
21
- `);const i=$("#topbar-bell").get(0);i&&(i.hidden=!0);const a=$("#topbar-settings").get(0);a&&(a.hidden=!u),$("#topbar-logout-btn").on("click",n=>{n.preventDefault(),F()}),Domma.icons.scan("#admin-topbar"),E.tooltip("#topbar-actions [data-tooltip]",{placement:"bottom"}),U(),p||(p=setInterval(U,6e4))}function w(t){return String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}let p=null;async function U(){if(d())try{const{count:t}=await c.system.notifications.unreadCount();$("#topbar-bell").removeAttr("hidden");const e=$("#topbar-bell-badge");t>0?e.text(t>99?"99+":String(t)).removeAttr("hidden"):e.attr("hidden","").text("")}catch{}}(async()=>{if(!d()&&!window.location.hash.startsWith("#/reset-password"))window.location.hash="#/login";else if(await N(),L()){const a=await h(),n=(window.location.hash||"#/").slice(1)||"/",s=v(n);s&&(window.location.hash=`#${s}`),await P(a),I()}R.init({container:"#view-container",routes:j,views:m,default:"/",transitions:{enter:"fadeIn",leave:"fadeOut",duration:150}});const t=R._extractParams.bind(R);R._extractParams=function(i,a){const n=a.indexOf("?");if(n!==-1&&(a=a.slice(0,n)),i.endsWith("/*")){const s=i.slice(0,-2);return a.startsWith(s+"/")?{}:null}return t(i,a)};const e=(window.location.hash||"#/").slice(1)||"/";j.filter(i=>i.path.endsWith("/*")).some(i=>e.startsWith(i.path.slice(0,-2)+"/"))&&R._handleRouteChange()})()});
21
+ `);const i=$("#topbar-bell").get(0);i&&(i.hidden=!0);const a=$("#topbar-settings").get(0);a&&(a.hidden=!u),$("#topbar-logout-btn").on("click",n=>{n.preventDefault(),H()}),Domma.icons.scan("#admin-topbar"),E.tooltip("#topbar-actions [data-tooltip]",{placement:"bottom"}),T(),p||(p=setInterval(T,6e4))}function g(t){return String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}let p=null;async function T(){if(!(!d()||h===!1))try{const{count:t}=await c.system.notifications.unreadCount();$("#topbar-bell").removeAttr("hidden");const e=$("#topbar-bell-badge");t>0?e.text(t>99?"99+":String(t)).removeAttr("hidden"):e.attr("hidden","").text("")}catch{}}(async()=>{if(!d()&&!window.location.hash.startsWith("#/reset-password"))window.location.hash="#/login";else if(await I(),L()){const a=await w(),n=(window.location.hash||"#/").slice(1)||"/",s=C(n);s&&(window.location.hash=`#${s}`),await P(a),U()}R.init({container:"#view-container",routes:N,views:m,default:"/",transitions:{enter:"fadeIn",leave:"fadeOut",duration:150}});const t=R._extractParams.bind(R);R._extractParams=function(i,a){const n=a.indexOf("?");if(n!==-1&&(a=a.slice(0,n)),i.endsWith("/*")){const s=i.slice(0,-2);return a.startsWith(s+"/")?{}:null}return t(i,a)};const e=(window.location.hash||"#/").slice(1)||"/";N.filter(i=>i.path.endsWith("/*")).some(i=>e.startsWith(i.path.slice(0,-2)+"/"))&&R._handleRouteChange()})()});
@@ -0,0 +1 @@
1
+ export function slugify(t){return String(t??"").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}export function isMongoMissing(t){if(!t)return!1;const e=String(t.message||t);return t.status===503||/MongoDB|pro mode|connection/i.test(e)}const g=(t,e,n=`${e}s`)=>`${t} ${t===1?e:n}`;export function transitionText(t){if(!t||!t.field)return"";const e=(Array.isArray(t.from)?t.from:[t.from]).filter(n=>n!=null&&n!=="");return`${t.field}: ${e.length?e.join(" / "):"any"} \u2192 ${t.to??"?"}`}export function actionRow(t){const e=Array.isArray(t.steps)?t.steps.length:0,n=Array.isArray(t.access?.roles)?t.access.roles:[],r=[...new Set((t.steps||[]).map(o=>o?.type).filter(Boolean))];return{slug:t.slug,title:t.title||t.slug,meta:[t.slug,t.description].filter(Boolean).join(" \xB7 "),collection:t.collection||"",trigger:t.trigger?.label||t.trigger?.type||"manual",steps:g(e,"step"),stepsTitle:r.length?r.join(", "):"No steps yet",transition:transitionText(t.transition),roles:n.length?n.join(", "):"admins",project:t.meta?.project||"",noSteps:e===0}}export function collectionsOf(t){return[...new Set((t||[]).map(e=>e.collection).filter(Boolean))].sort()}export function arrangeActions(t,{query:e="",collection:n=""}={}){const r=String(e).trim().toLowerCase();return[...t||[]].filter(o=>!n||o.collection===n).filter(o=>!r||[o.title,o.slug,o.description,o.collection,o.trigger?.label].some(i=>String(i||"").toLowerCase().includes(r))).sort((o,i)=>String(o.title||o.slug).localeCompare(String(i.title||i.slug)))}export function duplicateAction(t,e,n=[]){const r=String(e?.title??"").trim(),o=slugify(e?.slug||r),i={};r||(i.title="The copy needs a title."),o?n.includes(o)&&(i.slug=`"${o}" is already an action.`):i.slug="Needs at least one letter or number.";const{id:u,slug:p,title:a,meta:s,...l}=t||{},c={...l,title:r,slug:o,...s?.project&&{meta:{project:s.project}}};return{errors:i,payload:c}}export function projectNotice(t,e,n){const r=t-e;return!n||r<1?"":`Showing only actions in ${n}. ${r} other action${r===1?" is":"s are"} filed elsewhere.`}
@@ -0,0 +1 @@
1
+ export const FILTERS=["all","enabled","disabled","public"];export function endpointPath(t){const n=String(t?.path||"");return`/api/x/${t?.project||""}${n.startsWith("/")?n:`/${n}`}`}export function endpointUrl(t,n=""){return`${String(n).replace(/\/+$/,"")}${endpointPath(t)}`}export function authLabel(t){return t==="public"?"Public":t==="token"?"API token":`Role: ${t||"unknown"}`}export function pathParams(t){return[...String(t||"").matchAll(/:([A-Za-z0-9_]+)/g)].map(n=>n[1])}export function projectsOf(t){return[...new Set((t||[]).map(n=>n.project).filter(Boolean))].sort()}export function arrangeEndpoints(t,{filter:n="all",project:o="",query:r=""}={}){const a=String(r).trim().toLowerCase(),i={all:()=>!0,enabled:e=>e.enabled!==!1,disabled:e=>e.enabled===!1,public:e=>e.auth==="public"}[n]||(()=>!0);return[...t||[]].filter(i).filter(e=>!o||e.project===o).filter(e=>!a||[e.name,e.path,e.project,e.collection,endpointPath(e)].some(l=>String(l||"").toLowerCase().includes(a))).sort((e,l)=>String(e.project).localeCompare(String(l.project))||String(e.path).localeCompare(String(l.path)))}export function countEndpoints(t){const n=t||[],o=n.filter(r=>r.enabled===!1).length;return{all:n.length,enabled:n.length-o,disabled:o,public:n.filter(r=>r.auth==="public").length}}export function endpointRow(t){const n=pathParams(t.path),o=t.enabled===!1;return{id:t.id,name:t.name||t.path||"(unnamed)",url:endpointPath(t),meta:[`from ${t.collection||"?"}`,t.mode==="single"?"one entry":`list, up to ${t.limit??50}`,n.length?`params: ${n.join(", ")}`:""].filter(Boolean).join(" \xB7 "),project:t.project||"",auth:authLabel(t.auth),authCls:t.auth==="public"?"is-warn":t.auth==="token"?"is-info":"",off:o,rowCls:o?"is-off":""}}
@@ -0,0 +1,3 @@
1
+ export const VERBS=["create","read","update","delete"],FILTERS=["all","active","expiring","expired","disabled"],SOON_DAYS=14;const x=864e5,d=(e,t,r=`${t}s`)=>`${e} ${e===1?t:r}`;export function tokenState(e,t=Date.now()){const r=e?.expiresAt?Date.parse(e.expiresAt):NaN,n=Number.isFinite(r)?Math.ceil((r-t)/864e5):null;return Number.isFinite(r)&&r<=t?{state:"expired",days:n}:e?.enabled===!1?{state:"disabled",days:n}:n!==null&&n<=14?{state:"expiring",days:n}:{state:"active",days:n}}export function expiresIn(e){return e==null?"":e<=0?"today":e===1?"tomorrow":`in ${e} days`}export function ago(e,t=Date.now()){const r=Date.parse(e);if(!Number.isFinite(r))return"";const n=Math.max(0,Math.round((t-r)/1e3));return n<60?"just now":n<3600?`${Math.round(n/60)} min ago`:n<86400?`${d(Math.round(n/3600),"hour")} ago`:n<86400*30?`${d(Math.round(n/86400),"day")} ago`:new Date(r).toISOString().slice(0,10)}export function scopesToText(e){return(Array.isArray(e)?e:[]).map(t=>Array.isArray(t.verbs)&&t.verbs.length?`${t.collection}: ${t.verbs.join(", ")}`:t.collection).join(`
2
+ `)}export function parseScopes(e){const t=[],r=new Set;for(const n of String(e??"").split(`
3
+ `)){const i=n.trim();if(!i)continue;const s=i.indexOf(":"),a=(s<0?i:i.slice(0,s)).trim(),o=s<0?"":i.slice(s+1).trim();if(!a)return{error:`"${i}" names no collection.`};if(r.has(a))return{error:`"${a}" is listed twice.`};r.add(a);const c={collection:a};if(o){const p=o.split(",").map(l=>l.trim().toLowerCase()).filter(Boolean),u=p.find(l=>!VERBS.includes(l));if(u)return{error:`"${u}" is not a verb - use ${VERBS.join(", ")}.`};c.verbs=[...new Set(p)]}t.push(c)}return{scopes:t}}export function scopesSummary(e){return!Array.isArray(e)||!e.length?"every collection in the project":e.map(t=>`${t.collection}: ${Array.isArray(t.verbs)&&t.verbs.length?t.verbs.join(", "):"all"}`).join(" \xB7 ")}export function arrangeTokens(e,{filter:t="all",project:r="",query:n=""}={},i=Date.now()){const s=String(n).trim().toLowerCase(),a={expired:0,expiring:1,active:2,disabled:3};return(e||[]).map(o=>({t:o,s:tokenState(o,i)})).filter(({s:o})=>t==="all"||o.state===t).filter(({t:o})=>!r||o.project===r).filter(({t:o})=>!s||[o.name,o.project,o.tokenHint,o.createdBy,scopesSummary(o.scopes)].some(c=>String(c||"").toLowerCase().includes(s))).sort((o,c)=>a[o.s.state]-a[c.s.state]||(o.s.state==="expiring"?o.s.days-c.s.days:0)||String(o.t.name||"").localeCompare(String(c.t.name||""))).map(({t:o})=>o)}export function countStates(e,t=Date.now()){const r={all:0,active:0,expiring:0,expired:0,disabled:0};for(const n of e||[])r.all++,r[tokenState(n,t).state]++;return r}const f={active:["Active","is-ok"],disabled:["Off",""],expired:["Expired","is-danger"],expiring:["","is-warn"]};export function tokenRow(e,t=Date.now()){const{state:r,days:n}=tokenState(e,t),[i,s]=f[r],a=e.expiresAt?r==="expired"?`expired ${ago(e.expiresAt,t)}`:`expires ${expiresIn(n)}`:"never expires";return{id:e.id,name:e.name||"(unnamed)",hint:`dcms_\u2026${e.tokenHint||"????"}`,project:e.project||"",meta:[scopesSummary(e.scopes),a,e.createdBy?`by ${e.createdBy}`:""].filter(Boolean).join(" \xB7 "),used:e.lastUsedAt?`used ${ago(e.lastUsedAt,t)}`:"never used",stateLabel:r==="expiring"?`Expires ${expiresIn(n)}`:i,stateCls:s,rowCls:r==="disabled"||r==="expired"?"is-off":""}}export function toLocalInput(e){const t=Date.parse(e);if(!Number.isFinite(t))return"";const r=new Date(t),n=i=>String(i).padStart(2,"0");return`${r.getFullYear()}-${n(r.getMonth()+1)}-${n(r.getDate())}T${n(r.getHours())}:${n(r.getMinutes())}`}export function checkToken(e,{isNew:t,projects:r=[],now:n=Date.now()}={}){const i=e||{},s={},a=String(i.name??"").trim();a||(s.name="A token needs a name - who or what will use it."),t&&!String(i.project||"")?s.project="Choose the project it works in.":t&&r.length&&!r.includes(i.project)&&(s.project="Choose one of your projects.");const{scopes:o,error:c}=parseScopes(i.scopes);c&&(s.scopes=c);let p=null;if(String(i.expires||"").trim()){const l=Date.parse(i.expires);Number.isFinite(l)?(p=new Date(l).toISOString(),t&&l<=n&&(s.expires="That is already past - a new token would be dead on arrival.")):s.expires="Not a date."}const u=t?{name:a,project:i.project,scopes:o||[],expiresAt:p}:{name:a,enabled:i.enabled!==!1,scopes:o||[],expiresAt:p};return{errors:s,payload:u}}
@@ -0,0 +1 @@
1
+ export const NAME_RE=/^[a-z0-9][a-z0-9-]*$/,FILTERS=["all","used","unused"];const i=(e,n,t=`${n}s`)=>`${e} ${e===1?n:t}`;export function formatSize(e){const n=Number(e)||0;return n<1024?`${n} B`:n<1024*1024?`${(n/1024).toFixed(n<10*1024?1:0)} KB`:`${(n/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 n=usageCount(e);if(n===null)return"";if(!n)return"Not used on any page or view";const t=e.usage.pages?.length||0,r=e.usage.views?.length||0;return`Used on ${[t&&i(t,"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:n="all",query:t=""}={}){const r=String(t).trim().toLowerCase();return[...e||[]].filter(o=>n==="used"?usageCount(o)>0:n==="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,n=t=>String(t||"").slice(0,10)){const t=usageCount(e);return{name:e.name,description:e.description||"",meta:[`${e.name}.html`,formatSize(e.size),e.updatedAt?`changed ${n(e.updatedAt)}`:"",usageText(e)].filter(Boolean).join(" \xB7 "),hasCss:!!e.hasCss,bundled:!!e.bundled,project:e.meta?.project||"",unused:t===0,uses:t===null?"":String(t)}}export function checkNewName(e,n=[]){const t=String(e??"").trim();return t?NAME_RE.test(t)?n.includes(t)?`"${t}" already exists.`:"":"Lowercase letters, digits and hyphens; starting with a letter or digit.":"A block needs a name."}export function copyName(e,n=[]){const t=`${e}-copy`;if(!n.includes(t))return t;for(let r=2;r<1e3;r++)if(!n.includes(`${t}-${r}`))return`${t}-${r}`;return t}
@@ -0,0 +1 @@
1
+ export const SYSTEM_SLUGS=["roles","user-profiles"],FILTERS=["all","own","builtin","plugin"];const u="core";export function slugify(n){return String(n??"").toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}export function kindOf(n){return n?.builtIn===!0||n?.preset===!0||SYSTEM_SLUGS.includes(n?.slug)?"builtin":n?.plugin?"plugin":"own"}export const projectOf=n=>n?.meta?.project||u;export function arrangeCollections(n,{filter:e="all",query:t="",project:r=null}={}){const i=String(t).trim().toLowerCase();return[...n||[]].filter(o=>!r||projectOf(o)===r).filter(o=>e==="all"||kindOf(o)===e).filter(o=>!i||[o.title,o.slug,o.description,o.plugin].some(l=>String(l||"").toLowerCase().includes(i))).sort((o,l)=>String(o.title||o.slug).localeCompare(String(l.title||l.slug)))}export function kindCounts(n,{project:e=null}={}){const t=(n||[]).filter(i=>!e||projectOf(i)===e),r={all:t.length,own:0,builtin:0,plugin:0};for(const i of t)r[kindOf(i)]++;return r}const a=(n,e,t=`${e}s`)=>`${n} ${n===1?e:t}`;export function collectionRow(n){const e=kindOf(n),t=Array.isArray(n.fields)?n.fields.length:0,r=Number(n.entryCount)||0,i=n.storage?.adapter==="mongodb",o=n.meta?.project||"";return{slug:n.slug,title:n.title||n.slug,meta:[n.slug,a(t,"field"),o?`project ${o}`:""].filter(Boolean).join(" \xB7 "),entries:r,entriesText:r.toLocaleString("en-GB"),mongo:i,mongoTitle:i?`MongoDB - connection "${n.storage.connection||"default"}"`:"",builtin:e==="builtin",bundled:e!=="builtin"&&n.bundled===!0,plugin:n.plugin||"",pluginTitle:n.plugin?`Managed by the ${n.plugin} plugin`:"",canDelete:e!=="builtin",hasForm:!SYSTEM_SLUGS.includes(n.slug)}}export function checkNewCollection(n,e=[]){const t=String(n??"").trim(),r=slugify(t);return t?r?e.includes(r)?{slug:r,error:`"${r}" is already a collection.`}:{slug:r,error:""}:{slug:r,error:"The title needs at least one letter or number."}:{slug:"",error:""}}let s=0;export function connectionRows(n){return Object.entries(n||{}).filter(([e])=>e!=="_comment").map(([e,t])=>({id:`c${++s}`,name:e,uri:String(t?.uri||""),database:String(t?.database||"")}))}export function newConnectionRow(n){const e=new Set((n||[]).map(r=>r.name));let t=(n||[]).length+1;for(;e.has(`connection${t}`);)t++;return{id:`c${++s}`,name:t===1&&!e.has("default")?"default":`connection${t}`,uri:"",database:""}}export function connectionsFromRows(n){const e=[],t={};for(const r of n||[]){const i=String(r.name??"").trim();if(!i){e.push("Every connection needs a name.");continue}if(t[i]){e.push(`Two connections are called "${i}".`);continue}const o=String(r.uri??"").trim(),l=String(r.database??"").trim();o||e.push(`"${i}" needs a URI.`),l||e.push(`"${i}" needs a database.`),t[i]={type:"mongodb",uri:o,database:l,options:{}}}return{errors:e,config:t}}export function parseConnectionsJson(n){let e;try{e=JSON.parse(String(n??"").trim()||"{}")}catch(t){return{error:`Not valid JSON: ${t.message}`,config:null}}return!e||typeof e!="object"||Array.isArray(e)?{error:"The JSON must be an object of named connections.",config:null}:{error:"",config:e}}export function maskUri(n){return String(n??"").replace(/(\/\/[^:/@]+:)([^@]+)(@)/,"$1\u2022\u2022\u2022$3")}
@@ -0,0 +1 @@
1
+ export const NAME_RE=/^[a-z][a-z0-9-]*$/,FILTERS=["all","mine","builtin","plugin"],SORTS=["name","updated"];export function kindOf(t){return t?.origin==="plugin"?"plugin":t?.bundled?"builtin":"mine"}export const KIND_LABEL={mine:"Yours",builtin:"Built in",plugin:"Plugin"};const s=(t,n,e=`${n}s`)=>`${t} ${t===1?n:e}`;export function formatSize(t){const n=Number(t)||0;return n<1024?`${n} B`:n<1024*1024?`${(n/1024).toFixed(1)} KB`:`${(n/1024/1024).toFixed(1)} MB`}export function ago(t,n=Date.now()){const e=new Date(t??"").getTime();if(!t||!Number.isFinite(e))return"";const o=Math.max(0,Math.round((n-e)/1e3));return o<60?"just now":o<3600?`${Math.round(o/60)} min ago`:o<86400?`${s(Math.round(o/3600),"hour")} ago`:o<86400*14?`${s(Math.round(o/86400),"day")} ago`:new Date(e).toISOString().slice(0,10)}export function propsSummary(t){const n=Object.keys(t&&typeof t=="object"?t:{});if(!n.length)return{count:0,text:"no props"};const e=n.slice(0,4).join(", ");return{count:n.length,text:n.length>4?`${e} +${n.length-4}`:e}}export const usageTag=t=>`<dm-${t}></dm-${t}>`;export function arrangeComponents(t,{filter:n="all",query:e="",sort:o="name"}={}){const u=String(e).trim().toLowerCase();return(t||[]).filter(r=>n==="all"||kindOf(r)===n).filter(r=>!u||[r.name,...Object.keys(r.props||{}),r.meta?.project].some(i=>String(i||"").toLowerCase().includes(u))).sort(o==="updated"?(r,i)=>String(i.updatedAt||"").localeCompare(String(r.updatedAt||""))||r.name.localeCompare(i.name):(r,i)=>r.name.localeCompare(i.name))}export function kindCounts(t){const n={all:0,mine:0,builtin:0,plugin:0};for(const e of t||[])n.all++,n[kindOf(e)]++;return n}export function componentRow(t,n=Date.now()){const e=kindOf(t),{count:o,text:u}=propsSummary(t.props),a=ago(t.updatedAt,n);return{name:t.name,file:`${t.name}.dmc`,tag:`<dm-${t.name}>`,meta:[o?`${s(o,"prop")}: ${u}`:"no props",formatSize(t.size),a&&`updated ${a}`].filter(Boolean).join(" \xB7 "),kind:e,kindLabel:KIND_LABEL[e],kindCls:e==="mine"?"":`is-${e}`,showKind:e!=="mine",project:t.meta?.project||"",locked:e==="plugin"}}export function checkName(t,n=[]){const e=String(t??"").trim();return e?NAME_RE.test(e)?n.includes(e)?`"${e}" is already a component.`:"":"A letter first, then lowercase letters, digits and hyphens.":"A component needs a name."}export function copyName(t,n=[]){const e=`${t}-copy`;if(!n.includes(e))return e;for(let o=2;;o++)if(!n.includes(`${e}-${o}`))return`${e}-${o}`}export function checkBundle(t){return!t||typeof t!="object"||Array.isArray(t)?"Not a component bundle.":!t.name||typeof t.name!="string"?"The bundle has no name.":typeof t.source!="string"?"The bundle has no source.":""}
@@ -0,0 +1 @@
1
+ export const FILTERS=["all","on","off"];const i=(e,t,n=`${t}s`)=>`${e} ${e===1?t:n}`;export function scopeOf(e){const t=e?.binding;if(!t)return{kind:"site",text:"Site-wide",title:"Every page"};const n=[...(t.projects||[]).map(r=>`project:${r}`),...t.pages||[]];return n.length?{kind:"scoped",text:i(n.length,"rule"),title:n.join(", ")}:{kind:"never",text:"Never matches",title:"Its binding lists no projects and no pages, so it never appears"}}export function targetText(e){const t=String(e?.selector||"").trim(),n=String(e?.match||"").trim();return!t&&!n?"no target":n?`${t||"page"} \u2192 ${n}`:t}export function arrangeMenus(e,{filter:t="all",query:n=""}={}){const r=String(n).trim().toLowerCase();return[...e||[]].filter(o=>t==="on"?o.enabled!==!1:t==="off"?o.enabled===!1:!0).filter(o=>!r||[o.slug,o.name,o.description,o.selector,o.match].some(s=>String(s||"").toLowerCase().includes(r))).sort((o,s)=>String(o.name||o.slug).localeCompare(String(s.name||s.slug))||String(o.slug).localeCompare(String(s.slug)))}export function menuRow(e){const t=scopeOf(e),n=e.enabled===!1;return{slug:e.slug,name:e.name||e.slug,meta:[e.slug,targetText(e),i(Number(e.itemsCount)||0,"item")].join(" \xB7 "),description:e.description||"",scopeText:t.text,scopeTitle:t.title,scopeCls:t.kind==="never"?"is-warn":t.kind==="scoped"?"is-info":"",off:n,rowCls:n?"is-off":""}}export function countMenus(e){const t=(e||[]).length,n=(e||[]).filter(r=>r.enabled===!1).length;return{all:t,on:t-n,off:n}}
@@ -0,0 +1,3 @@
1
+ export const ANIMATIONS=["fade","slide-up","slide-down","zoom","flip"],DEFAULT_SETTINGS={respectMotion:!0,defaultDuration:600,defaultAnimation:"fade",defaultThreshold:.1},DURATION_MAX=1e4;export function formFromSettings(t){const e=t&&typeof t=="object"?t:{};return{respectMotion:e.respectMotion!==!1,defaultDuration:String(Number.isFinite(Number(e.defaultDuration))&&e.defaultDuration!==""&&e.defaultDuration!=null?e.defaultDuration:DEFAULT_SETTINGS.defaultDuration),defaultAnimation:ANIMATIONS.includes(e.defaultAnimation)?e.defaultAnimation:DEFAULT_SETTINGS.defaultAnimation,defaultThreshold:String(Number.isFinite(Number(e.defaultThreshold))&&e.defaultThreshold!==""&&e.defaultThreshold!=null?e.defaultThreshold:DEFAULT_SETTINGS.defaultThreshold)}}export function checkSettings(t){const e=t||{},o={},r=Number(String(e.defaultDuration??"").trim());(String(e.defaultDuration??"").trim()===""||!Number.isInteger(r)||r<1||r>DURATION_MAX)&&(o.defaultDuration=`A whole number of milliseconds, 1 to ${DURATION_MAX}.`);const n=Number(String(e.defaultThreshold??"").trim());return(String(e.defaultThreshold??"").trim()===""||!Number.isFinite(n)||n<0||n>1)&&(o.defaultThreshold="A number from 0 (as soon as any of it shows) to 1 (only when all of it shows)."),ANIMATIONS.includes(e.defaultAnimation)||(o.defaultAnimation="Pick one of the listed animations."),{errors:o,payload:{respectMotion:e.respectMotion!==!1,defaultDuration:Number.isInteger(r)?r:DEFAULT_SETTINGS.defaultDuration,defaultAnimation:ANIMATIONS.includes(e.defaultAnimation)?e.defaultAnimation:DEFAULT_SETTINGS.defaultAnimation,defaultThreshold:Number.isFinite(n)?n:DEFAULT_SETTINGS.defaultThreshold}}}export function settingsSnapshot(t){const e=t||{};return JSON.stringify([e.respectMotion!==!1,String(e.defaultDuration??"").trim(),String(e.defaultAnimation??""),String(e.defaultThreshold??"").trim()])}export function changedFromDefaults(t){return Object.keys(DEFAULT_SETTINGS).filter(e=>t?.[e]!==DEFAULT_SETTINGS[e])}export function categoriesOf(t){const e=[];for(const o of t||[])o.category&&!e.includes(o.category)&&e.push(o.category);return e}export function arrangeEffects(t,{category:e="all",query:o=""}={}){const r=String(o).trim().toLowerCase();return(t||[]).filter(n=>e==="all"||n.category===e).filter(n=>!r||[n.name,n.label,n.description,...(n.attrs||[]).map(i=>i.name)].some(i=>String(i||"").toLowerCase().includes(r)))}const a=(t,e)=>t.type==="checkbox"?e?"true":"false":String(e).replace(/"/g,"&quot;");export function exampleShortcode(t,{full:e=!1}={}){if(!t?.name)return"";const o=e?(t.attrs||[]).filter(n=>n.default!==void 0&&n.default!=="").map(n=>`${n.name}="${a(n,n.default)}"`):[],r=o.length?` ${o.join(" ")}`:"";return t.selfClosing?`[${t.name}${r} /]`:`[${t.name}${r}]
2
+ Content
3
+ [/${t.name}]`}export function attrRow(t){let e;t.type==="select"?e=(t.options||[]).map(r=>Array.isArray(r)?r[0]:r).join(" \xB7 "):t.type==="checkbox"?e="true / false":t.type==="colour"?e="a colour (#rrggbb)":t.type==="number"?e=`a number${[t.min,t.max].every(n=>n!==void 0)?` ${t.min} to ${t.max}`:""}`:e="text";const o=t.default===void 0||t.default===""?"-":t.type==="checkbox"?t.default?"true":"false":String(t.default);return{name:t.name,label:t.label||t.name,takes:e,def:o,hint:t.hint||""}}export function effectRow(t){const e=(t.hosts||[]).length?`works inside ${t.hosts.map(r=>`[${r}]`).join(" ")}`:"on its own",o=(t.attrs||[]).length;return{name:t.name,label:t.label||t.name,tag:`[${t.name}]`,description:t.description||"",meta:[`${o} setting${o===1?"":"s"}`,e,t.selfClosing?"self-closing":"wraps content"].join(" \xB7 "),category:t.category||"",fullPage:t.previewable===!1}}
@@ -0,0 +1 @@
1
+ export const FILTERS=["all","recent","empty"];const u=new Set(["page-break","spacer"]),a=(t,o,i=`${o}s`)=>`${t} ${t===1?o:i}`;export function ago(t,o=Date.now()){const i=new Date(t).getTime();if(!Number.isFinite(i))return"";const e=Math.max(0,Math.round((o-i)/1e3));return e<60?"just now":e<3600?`${Math.round(e/60)} min ago`:e<86400?`${a(Math.round(e/3600),"hour")} ago`:e<86400*14?`${a(Math.round(e/86400),"day")} ago`:new Date(i).toISOString().slice(0,10)}export function formShape(t){const o=Array.isArray(t?.fields)?t.fields:[],i=o.filter(e=>e?.type==="page-break").length;return{questions:o.filter(e=>e&&!u.has(e.type)).length,steps:i?i+1:0,logic:o.some(e=>e&&(e.logic||Array.isArray(e.triggers)&&e.triggers.length))}}export function actionsOn(t){const o=t?.actions||{};return Object.entries({email:"email",collection:"saved",webhook:"webhook",action:"action"}).filter(([e])=>o[e]?.enabled).map(([,e])=>e)}export function formRow(t,o=Date.now()){const{questions:i,steps:e,logic:n}=formShape(t),s=Number(t.submissionCount)||0,r=Number(t.submissionsThisWeek)||0,l=[t.slug,a(i,"question"),e?`${e}-step wizard`:"",n?"conditional logic":"",t.lastSubmissionAt?`last submission ${ago(t.lastSubmissionAt,o)}`:"no submissions yet"].filter(Boolean).join(" \xB7 ");return{slug:t.slug,title:t.title||t.slug,meta:l,total:s,totalText:String(s),week:r,weekText:r?`${r} new this week`:"",plugin:t.plugin||"",project:t.meta?.project||"",wizard:e>0,rowCls:r?"is-recent":""}}export function arrangeForms(t,{filter:o="all",query:i=""}={}){const e=String(i).trim().toLowerCase();return[...t||[]].filter(n=>o==="recent"?Number(n.submissionsThisWeek)>0:o==="empty"?!Number(n.submissionCount):!0).filter(n=>!e||[n.title,n.slug,n.description,n.plugin,n.meta?.project].some(s=>String(s||"").toLowerCase().includes(e))).sort((n,s)=>String(s.lastSubmissionAt||"").localeCompare(String(n.lastSubmissionAt||""))||String(n.title||n.slug).localeCompare(String(s.title||s.slug)))}export function duplicatePayload(t,o){const{collection:i,...e}=t?.actions||{};return{title:String(o||`${t?.title||t?.slug||"Form"} (Copy)`).trim(),description:t?.description||"",fields:t?.fields||[],settings:t?.settings||{},actions:e}}export const shortcodeFor=t=>`[form name="${t}" /]`;
@@ -0,0 +1 @@
1
+ export const WIDTHS=["narrow","normal","wide","full"],WIDTH_LABELS={narrow:"Narrow",normal:"Normal",wide:"Wide",full:"Full width"},WIDTH_HINTS={narrow:"768px - a reading column",normal:"1100px",wide:"75% of the window, up to 2000px",full:"edge to edge"},WIDTH_SHARE={narrow:"40%",normal:"62%",wide:"82%",full:"100%"},FILTERS=["all","builtin","custom"];const u=/^[a-zA-Z0-9#(),.\s%/-]+$/;export function layoutKey(e){return String(e??"").toLowerCase().trim().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"")}const d=(e,r,n=`${r}s`)=>`${e} ${e===1?r:n}`;export function chromeText(e){const r=[e.navbar!==!1&&"navbar",e.footer!==!1&&"footer",e.sidebar===!0&&"sidebar"].filter(Boolean);return r.length?r.join(", "):"no navbar or footer"}export function arrangeLayouts(e,r={},{filter:n="all",query:o=""}={}){const s=String(o).trim().toLowerCase(),l=Object.entries(e||{}).map(([t,a])=>({...a,key:t})),i=l.filter(t=>t.builtin),c=l.filter(t=>!t.builtin).sort((t,a)=>String(t.label||t.key).localeCompare(String(a.label||a.key)));return[...i,...c].filter(t=>n==="builtin"?t.builtin:n==="custom"?!t.builtin:!0).filter(t=>!s||[t.key,t.label,t.description,t.class].some(a=>String(a||"").toLowerCase().includes(s))).map(t=>layoutRow(t,(r.byKey||{})[t.key]||[]))}export function layoutRow(e,r=[]){const n=WIDTHS.includes(e.width)?e.width:"normal";return{key:e.key,label:e.label||e.key,meta:[e.key,e.description,chromeText(e)].filter(Boolean).join(" \xB7 "),builtin:e.builtin===!0,widthLabel:WIDTH_LABELS[n],used:r.length,usedText:r.length?d(r.length,"page"):"Unused",usedTone:r.length?"is-info":"is-muted",hasNav:e.navbar!==!1,hasFoot:e.footer!==!1,hasSide:e.sidebar===!0,share:WIDTH_SHARE[n],bg:e.bgColor&&u.test(e.bgColor)?e.bgColor:""}}export function formFromPreset(e={}){return{label:e.label||"",description:e.description||"",width:WIDTHS.includes(e.width)?e.width:"normal",navbar:e.navbar!==!1,footer:e.footer!==!1,sidebar:e.sidebar===!0,useBg:!!e.bgColor,bgColor:e.bgColor&&/^#[0-9a-f]{6}$/i.test(e.bgColor)?e.bgColor:e.bgColor||"#ffffff",bgImage:e.bgImage||"",cssClass:e.class||""}}export function checkLayout(e,{isNew:r,taken:n=[]}={}){const o=e||{},s={},l=String(o.label??"").trim(),i=r?layoutKey(l):"";l?l.length>60?s.label="Up to 60 characters.":r&&!i?s.label="Needs at least one letter or number.":r&&n.includes(i)&&(s.label=`"${i}" is already a layout - choose another name.`):s.label="A layout needs a name.";const c=o.useBg?String(o.bgColor||"").trim():"";c&&!u.test(c)&&(s.bgColor="Not a colour the server accepts.");const t=String(o.bgImage??"").trim(),a=String(o.cssClass??"").trim();/[^a-zA-Z0-9\s_-]/.test(a)&&(s.cssClass="Letters, numbers, spaces, hyphens and underscores only.");const b={label:l,description:String(o.description??"").trim().slice(0,200),width:WIDTHS.includes(o.width)?o.width:"normal",navbar:o.navbar!==!1,footer:o.footer!==!1,sidebar:o.sidebar===!0,bgColor:c,bgImage:t,class:a};return{errors:s,payload:b,key:i}}export function checkOptions(e){const r={},n=String(e?.spacerSize??"").trim(),o=Number(n);(n===""||!Number.isInteger(o)||o<0||o>500)&&(r.spacerSize="A whole number of pixels, 0 to 500.");const s=String(e?.spacerClass??"").trim();return/[^a-zA-Z0-9\s_-]/.test(s)&&(r.spacerClass="Letters, numbers, spaces, hyphens and underscores only."),{errors:r,payload:{spacerSize:Number.isInteger(o)?o:8,spacerClass:s}}}
@@ -0,0 +1 @@
1
+ export const KINDS=["all","image","video","audio","document","other"],KIND_LABEL={all:"All",image:"Images",video:"Video",audio:"Audio",document:"Documents",other:"Other"},SORTS=["newest","oldest","name","size"];const c={image:["png","jpg","jpeg","gif","webp","svg","avif","ico","bmp","tif","tiff"],video:["mp4","webm","ogv","mov"],audio:["mp3","ogg","oga","wav","weba","m4a"],document:["pdf","txt","csv","doc","docx","xls","xlsx"]},u=new Set(["png","jpg","jpeg","gif","webp","svg","avif","ico","bmp"]),m=new Set(["jpg","jpeg","png","webp","gif","tiff"]);export const extOf=e=>{const t=/\.([a-z0-9]+)$/i.exec(String(e??""));return t?t[1].toLowerCase():""};export function kindOf(e){const t=extOf(e);for(const[o,n]of Object.entries(c))if(n.includes(t))return o;return"other"}export const isPreviewable=e=>u.has(extOf(e)),isEditable=e=>m.has(extOf(e));export function formatBytes(e){const t=Number(e)||0;if(t<1024)return`${t} B`;const o=["KB","MB","GB","TB"];let n=t/1024,r=0;for(;n>=1024&&r<o.length-1;)n/=1024,r++;return`${n.toFixed(1)} ${o[r]}`}export function ago(e,t=Date.now()){const o=new Date(e).getTime();if(!Number.isFinite(o))return"";const n=Math.max(0,Math.round((t-o)/1e3));if(n<60)return"just now";if(n<3600)return`${Math.round(n/60)} min ago`;if(n<86400){const r=Math.round(n/3600);return`${r} hour${r===1?"":"s"} ago`}if(n<86400*14){const r=Math.round(n/86400);return`${r} day${r===1?"":"s"} ago`}return new Date(o).toISOString().slice(0,10)}export function safeName(e){return String(e??"").split(/[\\/]/).pop().replace(/[^a-zA-Z0-9._-]/g,"_")}export function clashes(e,t){const o=new Set(t);return[...new Set(e.map(safeName))].filter(n=>o.has(n))}export function altFromName(e){return String(e??"").replace(/\.[^.]+$/,"").replace(/[-_]+/g," ").replace(/\s+/g," ").trim()}export function markdownFor(e){return isPreviewable(e.name)?`![${altFromName(e.name)}](${e.url})`:`[${e.name}](${e.url})`}export function kindCounts(e){const t=Object.fromEntries(KINDS.map(o=>[o,0]));for(const o of e||[])t.all++,t[kindOf(o.name)]++;return t}export function arrangeMedia(e,{kind:t="all",query:o="",sort:n="newest"}={}){const r=String(o).trim().toLowerCase(),s={newest:(a,i)=>String(i.createdAt).localeCompare(String(a.createdAt)),oldest:(a,i)=>String(a.createdAt).localeCompare(String(i.createdAt)),name:(a,i)=>a.name.localeCompare(i.name,void 0,{numeric:!0,sensitivity:"base"}),size:(a,i)=>(i.size||0)-(a.size||0)}[n]||(()=>0);return[...e||[]].filter(a=>t==="all"||kindOf(a.name)===t).filter(a=>!r||a.name.toLowerCase().includes(r)).sort((a,i)=>s(a,i)||a.name.localeCompare(i.name))}export function mediaTile(e,{now:t=Date.now(),stamp:o=""}={}){const n=kindOf(e.name),r=isPreviewable(e.name);return{name:e.name,url:e.url,thumb:r?`${e.url}${o?`?t=${encodeURIComponent(o)}`:""}`:null,preview:r,icon:{video:"video",audio:"music",document:"file-text"}[n]||(n==="image"?"image":"paperclip"),kind:n,kindLabel:(extOf(e.name)||n).toUpperCase(),meta:[formatBytes(e.size),ago(e.createdAt,t)].filter(Boolean).join(" \xB7 ")}}export function normalisePrefs(e){const t=e&&typeof e=="object"?e:{};return{kind:KINDS.includes(t.kind)?t.kind:"all",sort:SORTS.includes(t.sort)?t.sort:"newest"}}
@@ -0,0 +1 @@
1
+ export const FILTERS=["all","placed","unplaced"];export function slotsByMenu(t){const o={};for(const[l,n]of Object.entries(t||{}))n&&(o[n]??(o[n]=[])).push(l);return o}export function slotLabel(t,o=[]){return o.find(l=>l.slot===t)?.label||t}export function bindingScope(t){if(!t?.slot)return"";const o=[...(t.projects||[]).map(l=>`project ${l}`),...t.pages||[]];return o.length?o.join(", "):"nothing - this binding never matches"}export const isPlaced=(t,o)=>!!((o[t.slug]||[]).length||t.binding?.slot);export function menuRow(t,{bySlug:o={},registry:l=[]}={}){const n=o[t.slug]||[],r=Number(t.itemsCount??(Array.isArray(t.items)?t.items.length:0))||0,i=t.meta?.project||"",e=t.binding?.slot?t.binding:null;return{slug:t.slug,name:t.name||t.slug,meta:[t.slug,`${r} item${r===1?"":"s"}`,i?`project ${i}`:"",t.description||""].filter(Boolean).join(" \xB7 "),slotsText:n.map(s=>slotLabel(s,l)).join(" \xB7 "),hasSlots:n.length>0,bindingText:e?`Bound: ${slotLabel(e.slot,l)}`:"",bindingTitle:e?`Takes over ${slotLabel(e.slot,l)} on: ${bindingScope(e)}`:"",bindingDead:!!(e&&!(e.projects||[]).length&&!(e.pages||[]).length),bundled:!!t.meta?.bundled,placed:n.length>0||!!e,canDelete:n.length===0,deleteWhy:n.length?`In ${n.map(s=>slotLabel(s,l)).join(", ")} - take it out of the location first`:""}}export function arrangeMenus(t,{bySlug:o={},registry:l=[],filter:n="all",query:r=""}={}){const i=String(r).trim().toLowerCase();return[...t||[]].filter(e=>n==="placed"?isPlaced(e,o):n==="unplaced"?!isPlaced(e,o):!0).filter(e=>!i||[e.slug,e.name,e.description,...(o[e.slug]||[]).map(s=>slotLabel(s,l))].some(s=>String(s||"").toLowerCase().includes(i))).sort((e,s)=>String(e.name||e.slug).localeCompare(String(s.name||s.slug)))}export const menuShortcode=t=>`[menu slug="${t}" /]`;
@@ -0,0 +1 @@
1
+ export const PASSWORD_MIN=8;const m=/^[^\s@]+@[^\s@]+\.[^\s@]+$/;export function fieldKind(t){return{email:"email",tel:"tel",number:"number",url:"url",date:"date",time:"time",textarea:"textarea",select:"select",radio:"select",checkbox:"checkbox","checkbox-group":"multi",hidden:"hidden"}[t]||"text"}export function fieldOptions(t){return(Array.isArray(t?.options)?t.options:[]).map(e=>e&&typeof e=="object"?{value:String(e.value??e.label??""),label:String(e.label??e.value??"")}:{value:String(e),label:String(e)}).filter(e=>e.value!=="")}export function profileValue(t,e){const r=fieldKind(t?.type);return r==="checkbox"?e===!0||e==="true"||e===1:r==="multi"?Array.isArray(e)?e.map(String):e==null||e===""?[]:[String(e)]:e==null?"":String(e)}export function formFromMe(t,e=[]){return{name:String(t?.name??""),email:String(t?.email??""),password:"",confirm:"",profile:Object.fromEntries((e||[]).filter(r=>r?.name).map(r=>[r.name,profileValue(r,t?.profile?.[r.name])]))}}export function draftOf(t){const{password:e,confirm:r,...n}=t||{};return n}export function checkForm(t,e=[],{saved:r}={}){const n={},i=String(t?.name??"").trim(),l=String(t?.email??"").trim();i.length<2&&(n.name="Your name needs at least 2 characters."),m.test(l)||(n.email="That does not look like an email address.");const o=String(t?.password??""),a=String(t?.confirm??"");o&&o.length<8?n.password="At least 8 characters.":o&&o!==a?n.confirm="The two passwords are not the same.":!o&&a&&(n.password="Type the new password here too.");const p=!r||JSON.stringify(t?.profile||{})!==JSON.stringify(r.profile||{});for(const s of p?e||[]:[]){if(!s?.name||!s.required||fieldKind(s.type)==="hidden")continue;const c=t?.profile?.[s.name];(Array.isArray(c)?!c.length:fieldKind(s.type)==="checkbox"?c!==!0:String(c??"").trim()==="")&&(n[`profile.${s.name}`]=`${s.label||s.name} is required.`)}return n}export function snapshot(t){const e=t||{};return JSON.stringify({name:String(e.name??"").trim(),email:String(e.email??"").trim(),password:!!e.password,confirm:!!e.confirm,profile:e.profile||{}})}export function buildPayload(t,e,r=[]){const n={},i=[],l=String(e?.name??"").trim(),o=String(e?.email??"").trim();return l!==String(t?.name??"").trim()&&(n.name=l,i.push("name")),o!==String(t?.email??"").trim()&&(n.email=o,i.push("email")),e?.password&&(n.password=String(e.password),i.push("password")),JSON.stringify(e?.profile||{})!==JSON.stringify(t?.profile||{})&&(n.profile=Object.fromEntries((r||[]).filter(a=>a?.name).map(a=>[a.name,e.profile?.[a.name]??profileValue(a,void 0)])),i.push("profile")),i.length?{payload:n,parts:i}:null}export function initials(t){const e=String(t??"").trim().split(/\s+/).filter(Boolean);return e.length?(e[0][0]+(e.length>1?e[e.length-1][0]:"")).toUpperCase():"?"}export function accountFacts(t){const e=[t?.role,...Array.isArray(t?.additionalRoles)?t.additionalRoles:[]].filter(Boolean),r=Array.isArray(t?.projects)?t.projects:[];return{roles:e.length?e:["-"],access:r.length?`Projects: ${r.join(", ")}`:"Every project",active:t?.isActive!==!1}}
@@ -0,0 +1 @@
1
+ export const SEVERITIES=["critical","warning","info","success"],FILTERS=["all","new",...SEVERITIES],SEVERITY_TONE={critical:"danger",warning:"warn",success:"ok",info:"info"};const s=(e,i,r=`${i}s`)=>`${e} ${e===1?i:r}`;export function severityOf(e){const i=e?.data?.severity;return SEVERITIES.includes(i)?i:"info"}export function ago(e,i=Date.now()){const r=new Date(e).getTime();if(!Number.isFinite(r))return"";const t=Math.max(0,Math.round((i-r)/1e3));return t<60?"just now":t<3600?`${Math.round(t/60)} min ago`:t<86400?`${s(Math.round(t/3600),"hour")} ago`:t<86400*14?`${s(Math.round(t/86400),"day")} ago`:new Date(r).toISOString().slice(0,10)}export function arrangeNotifications(e,{filter:i="all",query:r=""}={}){const t=String(r).trim().toLowerCase();return[...e||[]].filter(n=>i==="all"?!0:i==="new"?n.isNew:severityOf(n)===i).filter(n=>!t||[n.data?.title,n.data?.body,n.data?.link].some(o=>String(o||"").toLowerCase().includes(t))).sort((n,o)=>String(o.data?.createdAt||"").localeCompare(String(n.data?.createdAt||"")))}export function filterCounts(e){const i={all:0,new:0,critical:0,warning:0,info:0,success:0};for(const r of e||[])i.all++,r.isNew&&i.new++,i[severityOf(r)]++;return i}export function notificationRow(e,{now:i=Date.now(),expanded:r=""}={}){const t=e?.data||{},n=severityOf(e),o=String(t.body||""),a=o.replace(/\s+/g," ").trim();return{id:String(e.id),title:String(t.title||"(no title)"),severity:n,severityLabel:n[0].toUpperCase()+n.slice(1),tone:SEVERITY_TONE[n],meta:[t.createdAt?ago(t.createdAt,i):"",t.source==="manager"?"from the manager":"",/^https?:\/\//i.test(String(t.link||""))?"has a link":"",t.expiresAt&&Date.parse(t.expiresAt)>i?`until ${String(t.expiresAt).slice(0,10)}`:""].filter(Boolean).join(" \xB7 "),preview:a.length>140?`${a.slice(0,139)}\u2026`:a,body:o,link:/^https?:\/\//i.test(String(t.link||""))?String(t.link):"",isNew:!!e.isNew,unread:!!e.unread,open:r===String(e.id),rowCls:[e.unread?"is-unread":"",r===String(e.id)?"is-open":"",`is-${n}`].filter(Boolean).join(" ")}}
@@ -0,0 +1 @@
1
+ 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 n=Math.max(0,Math.round((e-r)/1e3));return n<60?"just now":n<3600?`${Math.round(n/60)} min ago`:n<86400?`${l(Math.round(n/3600),"hour")} ago`:n<86400*14?`${l(Math.round(n/86400),"day")} ago`:new Date(r).toISOString().slice(0,10)}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 a=t=>`${t.title||""} ${t.urlPath||""} ${(t.tags||[]).join(" ")} ${t.layout||""}`.toLowerCase();export function arrangePages(t,{status:e="all",query:r="",sort:n="order"}={}){const s=String(r).trim().toLowerCase(),i=(t||[]).filter(o=>e==="all"||(e==="draft"?o.status!=="published":o.status==="published")).filter(o=>!s||a(o).includes(s));return n==="updated"?[...i].sort((o,u)=>String(u.updatedAt||"").localeCompare(String(o.updatedAt||""))):n==="title"?[...i].sort((o,u)=>String(o.title||"").localeCompare(String(u.title||""))):n==="url"?[...i].sort((o,u)=>String(o.urlPath).localeCompare(String(u.urlPath))):i}export function statusCounts(t){const e=(t||[]).length,r=(t||[]).filter(n=>n.status==="published").length;return{all:e,published:r,draft:e-r}}export function pageRow(t,e=Date.now()){const r=t.status!=="published",n=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: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(n,"revision"),t.updatedAt?`updated ${ago(t.updatedAt,e)}`:""].filter(Boolean).join(" \xB7 "),revisions:n,rowCls:r?"is-draft":""}}export function treeRows(t){const e=new Set((t||[]).map(r=>r.urlPath));return(t||[]).map(r=>{const n=String(r.urlPath).split("/").filter(Boolean);let s=null;for(let i=n.length-1;i>0&&!s;i--){const o="/"+n.slice(0,i).join("/");e.has(o)&&(s=o)}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 n=Number.parseInt(r,10);return{error:"",keep:n,deletes:Math.max(0,(Number(e)||0)-n)}}
@@ -0,0 +1,2 @@
1
+ export const FILTERS=["all","on","off","attention"],BROWSE_FILTERS=["all","installed","available","updates"],BLOCKING=new Set(["expired","unlicensed"]);const c={grace:{text:"Renew",tone:"warn",title:"The rental term has ended - still running during the grace period"},"support-ended":{text:"Support ended",tone:"muted",title:"Owned outright and still running, but no longer entitled to updates"},expired:{text:"Expired",tone:"danger",title:"The rental term and grace period have ended - this plugin is not running"},unlicensed:{text:"Unlicensed",tone:"danger",title:"No valid entitlement for this site - this plugin is not running"}};export function compareVersions(e,a){const i=l=>String(l??"").split(".").map(r=>parseInt(r,10)||0),t=i(e),n=i(a);for(let l=0;l<Math.max(t.length,n.length);l++){const r=(t[l]??0)-(n[l]??0);if(r)return r>0?1:-1}return 0}export function pluginStatus(e){return e.core?{key:"core",text:"Built in",tone:"info",running:!0}:e.enabled&&BLOCKING.has(e.entitlement)?{key:"blocked",text:"Not running",tone:"danger",running:!1}:e.enabled&&e.supersededBy?{key:"replaced",text:"Replaced",tone:"muted",running:!1}:e.enabled?{key:"on",text:"On",tone:"ok",running:!0}:{key:"off",text:"Off",tone:"muted",running:!1}}export function updateFor(e,a){const i=a?.get?.(e.name);return i&&compareVersions(i,e.version)>0?i:""}export function needsAttention(e,a){return pluginStatus(e).key==="blocked"||e.entitlement==="grace"||!!updateFor(e,a)}export function fmtReleased(e){if(!e||!/^\d{4}-\d{2}-\d{2}/.test(String(e)))return"";const a=new Date(e);if(Number.isNaN(a.getTime()))return"";const i=t=>String(t).padStart(2,"0");return`${i(a.getDate())}.${i(a.getMonth()+1)}.${a.getFullYear()}`}export function arrangePlugins(e,{filter:a="all",query:i="",latest:t=new Map}={}){const n=String(i).trim().toLowerCase(),l={all:()=>!0,on:s=>pluginStatus(s).running,off:s=>!pluginStatus(s).running,attention:s=>needsAttention(s,t)}[a]||(()=>!0),r=s=>needsAttention(s,t)?0:pluginStatus(s).running?1:2;return[...e||[]].filter(l).filter(s=>!n||[s.name,s.displayName,s.description,s.author].some(o=>String(o||"").toLowerCase().includes(n))).sort((s,o)=>r(s)-r(o)||String(s.displayName||s.name).localeCompare(String(o.displayName||o.name)))}export function pluginRow(e,a=new Map){const i=pluginStatus(e),t=c[e.entitlement]||null,n=updateFor(e,a),l=fmtReleased(e.date);return{name:e.name,displayName:e.displayName||e.name,icon:e.icon||"package",meta:[`v${e.version}`,e.author?`by ${e.author}`:"",l?`released ${l}`:"",e.supersededBy?`replaced by ${e.supersededBy}`:""].filter(Boolean).join(" \xB7 "),description:e.description||"",statusText:i.text,statusCls:`is-${i.tone}`,licensed:e.closedSource===!0,entText:t?.text||"",entCls:t?`is-${t.tone}`:"",entTitle:t?.title||"",update:n,updateText:n?`Update to ${n}`:"",configurable:settingsFields(e).length>0,rowCls:[i.running?"":"is-off",needsAttention(e,a)?"is-attention":""].filter(Boolean).join(" ")}}export function formatPrice(e){if(e?.tier!=="paid")return"Free";const a=e.terms?.managed?.monthly;return a==null||!Number.isFinite(Number(a))?"Paid":`${e.terms?.currency==="GBP"?"\xA3":`${e.terms?.currency||""} `}${(Number(a)/100).toFixed(2)}/mo`}export function catalogueAction(e){return e.installed&&e.updatable&&e.entitled?"update":e.installed?"installed":e.entitled?"install":"locked"}export function arrangeCatalogue(e,{filter:a="all",query:i=""}={}){const t=String(i).trim().toLowerCase(),n={all:()=>!0,installed:r=>r.installed,available:r=>!r.installed&&r.entitled,updates:r=>catalogueAction(r)==="update"}[a]||(()=>!0),l={update:0,install:1,installed:2,locked:3};return[...e||[]].filter(n).filter(r=>!t||[r.slug,r.displayName,r.description].some(s=>String(s||"").toLowerCase().includes(t))).sort((r,s)=>l[catalogueAction(r)]-l[catalogueAction(s)]||String(r.displayName||r.slug).localeCompare(String(s.displayName||s.slug)))}export function catalogueRow(e){const a=catalogueAction(e),i=e.entitled&&e.tier==="paid";return{slug:e.slug,displayName:e.displayName||e.slug,icon:e.icon||"package",meta:[e.installed&&e.installedVersion&&e.installedVersion!==e.version?`v${e.installedVersion} \u2192 v${e.version}`:`v${e.version}`,e.installed?"installed":""].filter(Boolean).join(" \xB7 "),description:e.description||"",price:formatPrice(e),licText:i?e.licenceStatus==="grace"?"Renew":"Licensed":"",licCls:e.licenceStatus==="grace"?"is-warn":"is-ok",action:a,actionText:{update:"Update",install:"Install",installed:"Installed",locked:e.licenceStatus==="expired"?"Licence expired":"Not licensed"}[a],rowCls:a==="locked"?"is-off":a==="update"?"is-attention":""}}export const isSecretKey=e=>/pass(word)?|secret|token|api[-_]?key|private|credential|uri$|dsn/i.test(String(e));export function labelFor(e){const a=String(e).replace(/[_-]+/g," ").replace(/([a-z0-9])([A-Z])/g,"$1 $2").trim().toLowerCase();return a?a[0].toUpperCase()+a.slice(1):String(e)}export function settingsFields(e){const a=e?.settings||{},i={...e?.effectiveSettings||{},...a};return Array.isArray(e?.settingsSchema)&&e.settingsSchema.length?e.settingsSchema.filter(t=>t&&t.key).map(t=>{const n=t.type==="select"?"select":t.type==="number"?"number":t.type==="boolean"||t.type==="checkbox"?"checkbox":t.type==="textarea"?"textarea":"text",l=i[t.key]??t.default??(n==="checkbox"?!1:"");return{key:t.key,label:t.label||labelFor(t.key),type:n,value:n==="checkbox"?l===!0:String(l??""),options:(t.options||[]).map(r=>typeof r=="object"?{value:String(r.value),label:String(r.label??r.value)}:{value:String(r),label:String(r)}),min:t.min,max:t.max,help:t.help||"",secret:t.secret===!0||isSecretKey(t.key)}}):Object.entries(i).map(([t,n])=>{const l=isSecretKey(t);if(typeof n=="boolean")return{key:t,label:labelFor(t),type:"checkbox",value:n,secret:l};if(typeof n=="number")return{key:t,label:labelFor(t),type:"number",value:String(n),secret:l};if(n!==null&&typeof n=="object")return{key:t,label:labelFor(t),type:"json",value:JSON.stringify(n,null,2),secret:l};const r=String(n??"");return{key:t,label:labelFor(t),type:r.includes(`
2
+ `)||r.length>80?"textarea":"text",value:r,secret:l}})}export function readSettings(e,a){const i={},t={};for(const n of e){const l=a[n.key];if(n.type==="checkbox"){!!l!=!!n.value&&(t[n.key]=!!l);continue}const r=String(l??"");if(r!==String(n.value))if(n.type==="number"){const s=Number(r);if(r.trim()===""||!Number.isFinite(s)){i[n.key]="A number, please.";continue}if(n.min!=null&&s<n.min){i[n.key]=`At least ${n.min}.`;continue}if(n.max!=null&&s>n.max){i[n.key]=`At most ${n.max}.`;continue}t[n.key]=s}else if(n.type==="json")try{t[n.key]=JSON.parse(r)}catch{i[n.key]="Not valid JSON."}else t[n.key]=r}return{errors:i,changed:t}}export function slugifyPlugin(e){return String(e??"").toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^[^a-z]+/,"").replace(/-+$/g,"").slice(0,40).replace(/-+$/,"")}export function checkScaffold(e,a=[]){const i=r=>String(e?.[r]??"").trim(),t={displayName:i("displayName"),slug:i("slug"),description:i("description"),author:i("author"),icon:i("icon")||"box"},n={},l=/[<>"\\]|\*\//;t.displayName?t.displayName.length>60&&(n.displayName="60 characters at most."):n.displayName="A plugin needs a name.",/^[a-z][a-z0-9-]{1,39}$/.test(t.slug)?a.includes(t.slug)&&(n.slug=`"${t.slug}" is already installed.`):n.slug="2-40 characters: lowercase letters, digits and hyphens, starting with a letter.",t.description.length>300&&(n.description="300 characters at most."),t.author.length>80&&(n.author="80 characters at most."),/^[a-z][a-z0-9-]{0,39}$/.test(t.icon)||(n.icon="A lowercase icon name (letters, digits, hyphens).");for(const r of["displayName","description","author"])!n[r]&&l.test(t[r])&&(n[r]='No <, >, ", backslash or */.');return{errors:n,payload:t}}
@@ -0,0 +1 @@
1
+ export const BASE_ROLES=["super-admin","admin","user"],FILTERS=["all","built-in","custom","plugins"],NAME_RE=/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/,BADGE_OPTIONS=[{value:"badge-danger",label:"Red (Admin)"},{value:"badge-warning",label:"Orange (Manager)"},{value:"badge-info",label:"Blue (Editor)"},{value:"badge-secondary",label:"Grey (Subscriber)"},{value:"badge-success",label:"Green"},{value:"badge-primary",label:"Primary"}];const s=(e,l,t=`${l}s`)=>`${e} ${e===1?l:t}`;export function slugify(e){return String(e??"").toLowerCase().trim().replace(/[^a-z0-9-]+/g,"-").replace(/^-+|-+$/g,"").replace(/-{2,}/g,"-")}export function permSummary(e){if(!Array.isArray(e)||!e.length)return"no permissions";const l=new Set(e.map(a=>String(a).split(".")[0])),t=e.every(a=>!String(a).includes("."));return`${s(l.size,"resource")} (${t?"full":"partial"})`}export function scopeText(e){const l=Array.isArray(e?.allow)?e.allow.filter(Boolean):[];return l.length?l.length===1?`Admin: ${l[0]} only`:`Admin: ${l.length} screens only`:""}export function roleKind(e){return e?.plugin?"plugins":BASE_ROLES.includes(e?.name)||e?.level===0?"built-in":"custom"}export function deleteBlock(e){return e?.level===0?"The root role holds every permission and cannot be deleted":BASE_ROLES.includes(e?.name)?"A built-in role cannot be deleted":e?.plugin?`Belongs to the ${e.plugin} plugin - it goes when the plugin is uninstalled`:""}export function arrangeRoles(e,{filter:l="all",query:t="",holders:a={}}={}){const r=String(t).trim().toLowerCase();return(e||[]).map(n=>({id:n.id,...n.data||{}})).filter(n=>n.name).filter(n=>l==="all"||roleKind(n)===l).filter(n=>!r||[n.name,n.label,n.plugin].some(i=>String(i||"").toLowerCase().includes(r))).sort((n,i)=>(n.level??99)-(i.level??99)||String(n.name).localeCompare(String(i.name))).map(n=>roleRow(n,a))}export function roleRow(e,l={}){const t=l[e.name]??null,a=e.level===0;return{id:e.id,name:e.name,label:e.label||e.name,badgeClass:/^badge-[a-z]+$/.test(e.badgeClass||"")?e.badgeClass:"badge-secondary",level:String(e.level??""),levelText:a?"Level 0 \xB7 everything":`Level ${e.level??"?"}`,meta:a?"every permission, plugins included":permSummary(e.permissions),holdersText:t===null?"":s(t,"user"),unheld:t===0&&!a,plugin:e.plugin||"",scope:scopeText(e.admin),builtIn:roleKind(e)==="built-in",project:e.meta?.project||""}}export function filterCounts(e){const l={all:0,"built-in":0,custom:0,plugins:0};for(const t of e||[]){const a=t.data||{};a.name&&(l.all++,l[roleKind(a)]++)}return l}export function checkNewRole(e,{taken:l=[],project:t=""}={}){const a=e||{},r={},n=String(a.name??"").trim(),i=String(a.label??"").trim(),o=Number.parseInt(a.level,10);NAME_RE.test(n)?l.includes(n)&&(r.name=`"${n}" is already a role.`):r.name="Lowercase letters, numbers and hyphens; no hyphen at either end.",i||(r.label="A role needs a label."),(!Number.isFinite(o)||o<1)&&(r.level="Level 1 or more - level 0 is the root role's.");const u=BADGE_OPTIONS.some(c=>c.value===a.badgeClass)?a.badgeClass:"badge-secondary";return{errors:r,payload:{name:n,label:i,level:Number.isFinite(o)?o:1,permissions:[],badgeClass:u,...t&&t!=="core"&&{meta:{project:t}}}}}export function duplicateOf(e,l=[]){let t=`${e.name}-copy`;for(let o=2;l.includes(t);o++)t=`${e.name}-copy-${o}`;const{id:a,plugin:r,grantedBy:n,...i}=e;return{...i,name:t,label:`${e.label||e.name} (copy)`,level:Math.max(1,Number(e.level)||1)}}
@@ -0,0 +1 @@
1
+ export const LIMITS={maxResults:[1,50],minQueryLength:[1,5],debounceMs:[100,1e3],offsetX:[0,500],offsetY:[0,500],iconSize:[32,96]},DEFAULTS={enabled:!0,placeholder:"Search pages...",keyboardShortcut:!0,maxResults:10,minQueryLength:2,debounceMs:300,displayMode:"navbar",position:"bottom-right",offsetX:32,offsetY:32,iconOnly:!0,iconSize:52,iconRadius:26},POSITIONS=["top-left","top-right","left-center","right-center","bottom-left","bottom-right"],POSITION_LABEL={"top-left":"Top left","top-right":"Top right","left-center":"Left side","right-center":"Right side","bottom-left":"Bottom left","bottom-right":"Bottom right"};const u={maxResults:"Max results",minQueryLength:"Minimum query length",debounceMs:"Debounce delay",offsetX:"Horizontal offset",offsetY:"Vertical offset",iconSize:"Icon size"};export const maxRadius=e=>Math.floor((Number(e)||DEFAULTS.iconSize)/2);export function radiusForSize(e,t,o){const n=Number(e)||0,s=maxRadius(o);return n>=maxRadius(t)||n>s?s:n}const r=e=>{const t=typeof e=="number"?e:parseInt(e,10);return Number.isFinite(t)?Math.round(t):null},a=(e,[t,o])=>Math.min(o,Math.max(t,e));export function formFromSettings(e){const t={...DEFAULTS,...e&&typeof e=="object"?e:{}};return{enabled:t.enabled!==!1,placeholder:String(t.placeholder??DEFAULTS.placeholder),keyboardShortcut:t.keyboardShortcut!==!1,maxResults:String(t.maxResults),minQueryLength:String(t.minQueryLength),debounceMs:String(t.debounceMs),displayMode:t.displayMode==="floating"?"floating":"navbar",position:POSITIONS.includes(t.position)?t.position:DEFAULTS.position,offsetX:String(t.offsetX),offsetY:String(t.offsetY),iconOnly:t.iconOnly!==!1,iconSize:String(t.iconSize),iconRadius:String(t.iconRadius)}}export function payloadFromForm(e){const t={...formFromSettings(null),...e||{}},o=i=>{const l=r(t[i]);return l===null?DEFAULTS[i]:a(l,LIMITS[i])},n=o("iconSize"),s=r(t.iconRadius);return{enabled:t.enabled!==!1,placeholder:String(t.placeholder??"").trim().slice(0,120)||DEFAULTS.placeholder,keyboardShortcut:t.keyboardShortcut!==!1,maxResults:o("maxResults"),minQueryLength:o("minQueryLength"),debounceMs:o("debounceMs"),displayMode:t.displayMode==="floating"?"floating":"navbar",position:POSITIONS.includes(t.position)?t.position:DEFAULTS.position,offsetX:o("offsetX"),offsetY:o("offsetY"),iconOnly:t.iconOnly!==!1,iconSize:n,iconRadius:s===null?Math.min(DEFAULTS.iconRadius,maxRadius(n)):a(s,[0,maxRadius(n)])}}export const snapshot=e=>JSON.stringify(payloadFromForm(e));export function clampNotes(e){const t=[];for(const[o,n]of Object.entries(LIMITS)){const s=String(e?.[o]??"").trim(),i=r(s);s===""||i===null?t.push(`${u[o]} is empty - it will be saved as ${DEFAULTS[o]}.`):(i<n[0]||i>n[1])&&t.push(`${u[o]} is ${n[0]}-${n[1]} - ${i} will be saved as ${a(i,n)}.`)}return t}export function liveChips(e,t=null){const o={...DEFAULTS,...e||{}};if(!o.enabled)return[{text:"Off - nothing is added to public pages",tone:"warn"}];const n=[{text:"On",tone:"ok"}];return n.push({text:o.displayMode==="floating"?`Floating button, ${(POSITION_LABEL[o.position]||o.position).toLowerCase()}`:"Icon in the navigation bar",tone:""}),o.keyboardShortcut&&n.push({text:"Ctrl+K / \u2318K",tone:""}),n.push({text:`Up to ${o.maxResults} results`,tone:""}),t&&n.push({text:`${t.searchable} page${t.searchable===1?"":"s"} searchable`,tone:""}),n}export function previewState(e,t){const o=String(e??"").trim(),n=r(t)??DEFAULTS.minQueryLength;return o?o.length<n?{run:!0,note:`Visitors need at least ${n} characters - on the site this would not search yet.`}:{run:!0,note:""}:{run:!1,note:"Type a word or two - results appear as you type."}}export function resultRow(e,t){return{id:`${t}|${e.url}`,url:String(e.url||"/"),title:String(e.title||"Untitled"),snippet:String(e.snippet||e.description||""),meta:[e.url,(e.tags||[]).length?`tags: ${(e.tags||[]).join(", ")}`:""].filter(Boolean).join(" \xB7 "),score:`score ${Number(e.score)||0}`,rank:String(t+1)}}
@@ -1,4 +1,4 @@
1
- import{api as p}from"../api.js";import{isPathAllowed as ee}from"./admin-scope.js";import{colourToCss as X,applyFloat as te,applyHighlight as ne,itemHighlightAttrs as se}from"/public/js/menu-decor.mjs?v=20260918-item-highlight";import{placeTools as ie,ensureLockedTools as oe,managePluginsItem as ae,stripItemByUrl as re,insertFoldersBeforeSystem as le,pruneEmptyToolsFolder as ce,MANAGE_PLUGINS_URL as de}from"./sidebar-grouping.js";function h(t){return String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}const ue=[{text:"Dashboard",url:"#/",icon:"home"},{text:"Menus",url:"#/menus",icon:"menu",permission:"menus"}];function _(t,s){return s?!t||!t.length?!1:t.includes(s)?!0:["read","create","update","delete"].some(n=>t.includes(`${s}.${n}`)):!0}function K(t,s,n=null){const e=[];for(const i of t){if(i.hidden||i.permission&&!_(s,i.permission))continue;const r=typeof i.url=="string"?i.url:"";if(n&&r.startsWith("#/")&&!ee(n,r.slice(1)))continue;const l=Array.isArray(i.items)&&i.items.length>0,o=l?K(i.items,s,n):[];l&&!o.length&&!r||e.push({...i,items:o})}return e}async function me(t){if(!_(t,"projects"))return[];let s=[];try{s=await p.projects.listWithCounts()}catch{return[]}if(!Array.isArray(s))return[];const n=[{key:"pages",text:"Pages",icon:"file-text",path:"/pages"},{key:"collections",text:"Collections",icon:"database",path:"/collections"},{key:"forms",text:"Forms",icon:"layout",path:"/forms"},{key:"actions",text:"Actions",icon:"zap",path:"/actions"},{key:"menus",text:"Menus",icon:"menu",path:"/menus"},{key:"blocks",text:"Blocks",icon:"box",path:"/blocks"},{key:"components",text:"Components",icon:"component",path:"/components"},{key:"views",text:"Views",icon:"eye",path:"/views"},{key:"roles",text:"Roles",icon:"shield",path:"/roles"},{key:"users",text:"Users",icon:"users",path:"/users"},{key:"apis",text:"APIs",icon:"code",path:"/apis"}];return s.map(e=>{const r="#/projects/"+encodeURIComponent(e.slug),l=e.counts||{},o=[{text:"Overview",url:r,icon:e.icon||"folder"}];for(const d of n){const u=Number(l[d.key])||0;u<=0||o.push({text:`${d.text} (${u})`,url:r+d.path,icon:d.icon})}o.push({text:"Settings",url:r+"/settings",icon:"settings"});const c=e.enabled===!1;return{text:e.name||e.slug,icon:e.icon||"folder",items:o,...c&&{colour:"neutral",badge:{text:"off"}}}})}function fe(t,s){for(const n of t)if(!n.hidden&&(n.key==="projects"||!n.key&&(n.text||"").toLowerCase()==="projects")){n.items=Array.isArray(n.items)?n.items.slice():[],n.items.push(...s);return}}function V(t,s){const n=J.includes(s.tone)?s.tone:"info",e=s.label||String(s.count);s.details?P.set(t,s.details):P.delete(t);const i=A.some(r=>r.url===t)?" data-live":"";return`<span class="sidebar-badge sidebar-badge--${n}" data-count-for="${h(t)}"${i} data-badge-label="${h(e)}" aria-label="${h(e)}">${h(String(s.count))}</span>`}function G(t){for(const s of t.querySelectorAll("details")){const n=s.querySelector(":scope > summary");if(!n)continue;let e=0,i=!1;const r=[];for(const u of s.querySelectorAll(":scope > .sidebar-children .sidebar-badge[data-live]")){const v=Number(u.textContent)||0;v&&(e+=v,i||(i=u.classList.contains("sidebar-badge--danger")),r.push({text:u.closest("a")?.querySelector(".sidebar-text")?.textContent.trim()??"",meta:u.dataset.badgeLabel,tone:u.classList.contains("sidebar-badge--danger")?"danger":void 0}))}const l=`folder:${s.dataset.stateKey||n.textContent.trim()}`;let o=n.querySelector(":scope > .sidebar-badge--sum");if(!e){o?.remove(),P.delete(l);continue}o||(o=document.createElement("span"),n.querySelector(".sidebar-text")?.after(o));const c=n.querySelector(".sidebar-text")?.textContent.trim()||"This folder",d=r.map(u=>`${u.text}: ${u.meta}`).join(" \xB7 ");o.className=`sidebar-badge sidebar-badge--sum sidebar-badge--${i?"danger":"info"}`,o.dataset.countFor=l,o.dataset.badgeLabel=d,o.setAttribute("aria-label",d),o.textContent=String(e),P.set(l,{title:c,items:r})}}const P=new Map;function pe(t,s){if(!t.url||!s)return"";const n=s[t.url];return!n||!(n.count>0)?"":V(t.url,n)}const J=["info","attention","danger","live"],he={"#/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 T(t,{fallbackLabel:s="",tone:n="info"}={}){const e=Number(typeof t=="object"&&t!==null?t.count:t);if(!Number.isFinite(e)||e<0)return null;const i=typeof t?.label=="string"&&t.label||(s?`${e} - ${s}`:String(e)),r={count:Math.round(e),label:i,tone:J.includes(t?.tone)?t.tone:n};return t?.details&&typeof t.details=="object"&&(r.details=t.details),r}function ge(t){const s=(t.text||"").replace(/\s*\(\d+\)\s*$/,"").toLowerCase().replace(/\s+/g,"-"),n=(t.url||"").match(/^#\/projects\/([^/]+)/);return`sidebar.expanded.${n?`project-${decodeURIComponent(n[1])}.`:""}${s}`}function Y(t,s){if(t&&t.type==="separator")return'<hr class="sidebar-divider">';if(t&&t.type==="spacer"){const y=["xs","sm","md","lg","xl","flex"].includes(t.size)?t.size:"md";return`<div class="sidebar-spacer sidebar-spacer--${h(y)}" aria-hidden="true"></div>`}const n=t.icon?`<span data-icon="${h(t.icon)}"></span>`:"",e=h(t.text||""),i=t.url?h(t.url):"",r=Array.isArray(t.items)&&t.items.length>0,l=t.colour?X(t.colour):"",o=se(t),c=[o.hover&&` data-menu-item-hover="${h(o.hover)}"`,o.active&&` data-menu-item-active="${h(o.active)}"`].filter(Boolean).join(""),d=[l&&`color:${h(l)}`,o.accent&&`--dm-menu-highlight:${h(o.accent)}`].filter(Boolean).join(";"),u=d?` style="${d}"`:"";let v="";if(t.badge&&t.badge.text!=null&&t.badge.text!==""){const y=t.badge.variant?X(t.badge.variant):"";v=`<span class="dm-menu-badge"${y?` style="background:${h(y)};color:var(--dm-text-inverse,#fff)"`:""}>${h(String(t.badge.text))}</span>`}if(r){const y=ge(t),L=S.get(y)!==!1,E=t.items.map(w=>Y(w,s)).join("");return`<details data-state-key="${h(y)}"${L?" open":""}>
2
- <summary${u}>${n} <span class="sidebar-text">${e}</span>${v}</summary>
1
+ import{api as p}from"../api.js";import{isPathAllowed as te}from"./admin-scope.js";import{colourToCss as X,applyFloat as ne,applyHighlight as se,itemHighlightAttrs as ie}from"/public/js/menu-decor.mjs?v=20260918-item-highlight";import{placeTools as oe,ensureLockedTools as ae,managePluginsItem as re,stripItemByUrl as le,insertFoldersBeforeSystem as ce,pruneEmptyToolsFolder as de,MANAGE_PLUGINS_URL as K}from"./sidebar-grouping.js";function h(t){return String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}const ue=[{text:"Dashboard",url:"#/",icon:"home"},{text:"Menus",url:"#/menus",icon:"menu",permission:"menus"}];function N(t,s){return s?!t||!t.length?!1:t.includes(s)?!0:["read","create","update","delete"].some(n=>t.includes(`${s}.${n}`)):!0}function V(t,s,n=null){const e=[];for(const i of t){if(i.hidden||i.permission&&!N(s,i.permission))continue;const o=typeof i.url=="string"?i.url:"";if(n&&o.startsWith("#/")&&!te(n,o.slice(1)))continue;const l=Array.isArray(i.items)&&i.items.length>0,a=l?V(i.items,s,n):[];l&&!a.length&&!o||e.push({...i,items:a})}return e}async function me(t){if(!N(t,"projects"))return[];let s=[];try{s=await p.projects.listWithCounts()}catch{return[]}if(!Array.isArray(s))return[];const n=[{key:"pages",text:"Pages",icon:"file-text",path:"/pages"},{key:"collections",text:"Collections",icon:"database",path:"/collections"},{key:"forms",text:"Forms",icon:"layout",path:"/forms"},{key:"actions",text:"Actions",icon:"zap",path:"/actions"},{key:"menus",text:"Menus",icon:"menu",path:"/menus"},{key:"blocks",text:"Blocks",icon:"box",path:"/blocks"},{key:"components",text:"Components",icon:"component",path:"/components"},{key:"views",text:"Views",icon:"eye",path:"/views"},{key:"roles",text:"Roles",icon:"shield",path:"/roles"},{key:"users",text:"Users",icon:"users",path:"/users"},{key:"apis",text:"APIs",icon:"code",path:"/apis"}];return s.map(e=>{const o="#/projects/"+encodeURIComponent(e.slug),l=e.counts||{},a=[{text:"Overview",url:o,icon:e.icon||"folder"}];for(const u of n){const m=Number(l[u.key])||0;m<=0||a.push({text:`${u.text} (${m})`,url:o+u.path,icon:u.icon})}a.push({text:"Settings",url:o+"/settings",icon:"settings"});const c=e.enabled===!1;return{text:e.name||e.slug,icon:e.icon||"folder",items:a,...c&&{colour:"neutral",badge:{text:"off"}}}})}function fe(t,s){for(const n of t)if(!n.hidden&&(n.key==="projects"||!n.key&&(n.text||"").toLowerCase()==="projects")){n.items=Array.isArray(n.items)?n.items.slice():[],n.items.push(...s);return}}function G(t,s){const n=Y.includes(s.tone)?s.tone:"info",e=s.label||String(s.count);s.details?P.set(t,s.details):P.delete(t);const i=C.some(o=>o.url===t&&!o.inventory)?" data-live":"";return`<span class="sidebar-badge sidebar-badge--${n}" data-count-for="${h(t)}"${i} data-badge-label="${h(e)}" aria-label="${h(e)}">${h(String(s.count))}</span>`}function J(t){for(const s of t.querySelectorAll("details")){const n=s.querySelector(":scope > summary");if(!n)continue;let e=0,i=!1;const o=[];for(const m of s.querySelectorAll(":scope > .sidebar-children .sidebar-badge[data-live]")){const v=Number(m.textContent)||0;v&&(e+=v,i||(i=m.classList.contains("sidebar-badge--danger")),o.push({text:m.closest("a")?.querySelector(".sidebar-text")?.textContent.trim()??"",meta:m.dataset.badgeLabel,tone:m.classList.contains("sidebar-badge--danger")?"danger":void 0}))}const l=`folder:${s.dataset.stateKey||n.textContent.trim()}`;let a=n.querySelector(":scope > .sidebar-badge--sum");if(!e){a?.remove(),P.delete(l);continue}a||(a=document.createElement("span"),n.querySelector(".sidebar-text")?.after(a));const c=n.querySelector(".sidebar-text")?.textContent.trim()||"This folder",u=o.map(m=>`${m.text}: ${m.meta}`).join(" \xB7 ");a.className=`sidebar-badge sidebar-badge--sum sidebar-badge--${i?"danger":"info"}`,a.dataset.countFor=l,a.dataset.badgeLabel=u,a.setAttribute("aria-label",u),a.textContent=String(e),P.set(l,{title:c,items:o})}}const P=new Map;function pe(t,s){if(!t.url||!s)return"";const n=s[t.url];return!n||!(n.count>0)?"":G(t.url,n)}const Y=["info","attention","danger","live"],he={"#/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 U(t,{fallbackLabel:s="",tone:n="info"}={}){const e=Number(typeof t=="object"&&t!==null?t.count:t);if(!Number.isFinite(e)||e<0)return null;const i=typeof t?.label=="string"&&t.label||(s?`${e} - ${s}`:String(e)),o={count:Math.round(e),label:i,tone:Y.includes(t?.tone)?t.tone:n};return t?.details&&typeof t.details=="object"&&(o.details=t.details),o}function ge(t){const s=(t.text||"").replace(/\s*\(\d+\)\s*$/,"").toLowerCase().replace(/\s+/g,"-"),n=(t.url||"").match(/^#\/projects\/([^/]+)/);return`sidebar.expanded.${n?`project-${decodeURIComponent(n[1])}.`:""}${s}`}function Q(t,s){if(t&&t.type==="separator")return'<hr class="sidebar-divider">';if(t&&t.type==="spacer"){const y=["xs","sm","md","lg","xl","flex"].includes(t.size)?t.size:"md";return`<div class="sidebar-spacer sidebar-spacer--${h(y)}" aria-hidden="true"></div>`}const n=t.icon?`<span data-icon="${h(t.icon)}"></span>`:"",e=h(t.text||""),i=t.url?h(t.url):"",o=Array.isArray(t.items)&&t.items.length>0,l=t.colour?X(t.colour):"",a=ie(t),c=[a.hover&&` data-menu-item-hover="${h(a.hover)}"`,a.active&&` data-menu-item-active="${h(a.active)}"`].filter(Boolean).join(""),u=[l&&`color:${h(l)}`,a.accent&&`--dm-menu-highlight:${h(a.accent)}`].filter(Boolean).join(";"),m=u?` style="${u}"`:"";let v="";if(t.badge&&t.badge.text!=null&&t.badge.text!==""){const y=t.badge.variant?X(t.badge.variant):"";v=`<span class="dm-menu-badge"${y?` style="background:${h(y)};color:var(--dm-text-inverse,#fff)"`:""}>${h(String(t.badge.text))}</span>`}if(o){const y=ge(t),k=S.get(y)!==!1,E=t.items.map(w=>Q(w,s)).join("");return`<details data-state-key="${h(y)}"${k?" open":""}>
2
+ <summary${m}>${n} <span class="sidebar-text">${e}</span>${v}</summary>
3
3
  <div class="sidebar-children">${E}</div>
4
- </details>`}return`<a href="${i}" class="sidebar-link" data-url="${i}"${u}${c}>${n} <span class="sidebar-text">${e}</span>${pe(t,s)}${v}</a>`}async function be(t=[],s=[]){const n=g=>g.then(f=>Array.isArray(f)?f.length:Array.isArray(f?.entries)?f.entries.length:0).catch(()=>0),e=(g,f)=>_(s,g)?f():Promise.resolve(0),[i,r,l,o,c,d,u,v,y,L,E,w,F,B]=await Promise.all([e("pages",()=>n(p.pages.list())),e("media",()=>n(p.media.list())),e("menus",()=>n(p.menus.list())),e("context-menus",()=>n(p.contextMenus.list())),e("theme",()=>p.theme.get().then(g=>(g?.overrides||[]).length).catch(()=>0)),e("collections",()=>n(p.collections.list())),e("forms",()=>n(p.forms.list())),e("views",()=>n(p.views.list())),e("actions",()=>n(p.actions.list())),e("blocks",()=>n(p.blocks.list())),e("components",()=>n(p.components.list())),e("users",()=>n(p.users.list())),e("plugins",()=>n(p.plugins.list())),e("notifications",()=>p.system?.notifications?.unreadCount?.().then(g=>g?.count??0).catch(()=>0)??Promise.resolve(0))]);A=xe(t);const R={};await Promise.all(A.map(async g=>{const f=await Q(g);f&&(R[g.url]=f)}));const q={"#/pages":i,"#/media":r,"#/menus":l,"#/context-menus":o,"#/theme":c,"#/collections":d,"#/forms":u,"#/views":v,"#/actions":y,"#/blocks":L,"#/components":E,"#/users":w,"#/plugins":F,"#/system/notifications":B},j={};for(const[g,f]of Object.entries(q)){const[C,k]=he[g]??["item","items"],x=T(f);x&&(j[g]={...x,label:`${x.count} ${x.count===1?C:k}`})}return{...j,...R}}let A=[];const ye="dm:sidebar-count",ve=6e4;function xe(t=[]){const s=[],n=e=>{if(e){e.url&&typeof e.countUrl=="string"&&e.countUrl&&s.push({url:e.url,countUrl:e.countUrl,text:e.text||"",countLabel:e.countLabel||""});for(const i of[...e.children||[],...e.items||[]])n(i)}};for(const e of t??[])n(e?.item??e);return s}async function Q(t){try{const s=new Date,n=`${s.getFullYear()}-${String(s.getMonth()+1).padStart(2,"0")}-${String(s.getDate()).padStart(2,"0")}`,e=t.countUrl+(t.countUrl.includes("?")?"&":"?")+`today=${n}`;return T(await H.get(e),{fallbackLabel:t.countLabel||t.text})}catch{return null}}function Z(t,s,n){const e=[...t.querySelectorAll("a.sidebar-link")].find(r=>r.dataset.url===s);if(!e)return;const i=e.querySelector(".sidebar-badge[data-count-for]");if(!n||!(n.count>0))i?.remove();else{const r=document.createRange();r.selectNodeContents(e);const l=r.createContextualFragment(V(s,n)).firstElementChild;i?i.replaceWith(l):e.querySelector(".sidebar-text")?.after(l)}I.set(s,n),G(t)}const I=new Map;async function U(t,s=null){const n=s?A.filter(e=>e.url===s):A;await Promise.all(n.map(async e=>{const i=await Q(e),r=I.get(e.url);JSON.stringify(r??null)!==JSON.stringify(i)&&Z(t,e.url,i)}))}function $e(t){t.dataset.liveCounts||(t.dataset.liveCounts="1",window.addEventListener(ye,s=>{const n=s.detail||{};if(n.url)if(n.count!==void 0){const e=A.find(i=>i.url===n.url);Z(t,n.url,T(n,{fallbackLabel:e?.countLabel||e?.text||""}))}else U(t,n.url)}),setInterval(()=>{!document.hidden&&document.body.contains(t)&&U(t)},ve),document.addEventListener("visibilitychange",()=>{document.hidden||U(t)}),Se(t))}function Ce(t,s,n){const e=(l,o,c)=>{const d=document.createElement(l);return o&&(d.className=o),c!=null&&(d.textContent=String(c)),d};if(!n){t.textContent=s;return}t.appendChild(e("strong","dm-badge-pop-title",n.title||s)),(n.rows?.length||!n.items?.length)&&t.appendChild(e("p","dm-badge-pop-sub",s));const i=Array.isArray(n.rows)?n.rows.slice(0,6):[];if(i.length){const l=e("dl","dm-badge-pop-rows");for(const o of i)l.appendChild(e("dt",null,o.label)),l.appendChild(e("dd",o.tone==="danger"?"is-danger":null,o.value));t.appendChild(l)}const r=Array.isArray(n.items)?n.items.slice(0,6):[];if(r.length){const l=e("ul","dm-badge-pop-items");for(const o of r){const c=e("li",o.tone==="danger"?"is-danger":null);c.appendChild(e("span","dm-badge-pop-text",o.text)),o.meta&&c.appendChild(e("span","dm-badge-pop-meta",o.meta)),l.appendChild(c)}t.appendChild(l)}else n.empty&&t.appendChild(e("p","dm-badge-pop-empty",n.empty))}function Se(t){let s=null,n=null;const e=()=>{clearTimeout(n),s?.remove(),s=null},i=r=>{e();const l=r?.dataset.badgeLabel;l&&(n=setTimeout(()=>{s=document.createElement("div"),s.className="dm-help-pop dm-badge-pop",s.setAttribute("role","tooltip"),Ce(s,l,P.get(r.dataset.countFor)),document.body.appendChild(s);const o=r.getBoundingClientRect(),c=s.getBoundingClientRect(),d=Math.min(o.right+10,innerWidth-c.width-8);s.style.left=`${Math.max(8,d)}px`,s.style.top=`${Math.max(8,Math.min(o.top+o.height/2-c.height/2,innerHeight-c.height-8))}px`},150))};t.addEventListener("mouseover",r=>{const l=r.target.closest?.(".sidebar-badge[data-badge-label]");l&&i(l)}),t.addEventListener("mouseout",r=>{r.target.closest?.(".sidebar-badge[data-badge-label]")&&e()}),t.addEventListener("focusin",r=>{const l=r.target.closest?.("a, summary")?.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");l?i(l):e()}),t.addEventListener("focusout",e),window.addEventListener("scroll",e,!0),window.addEventListener("hashchange",e)}async function we(){try{const t=await p.settings.get();return t?.adminBrand?.title||t?.title||"Admin"}catch{return"Admin"}}export async function renderAdminSidebar({mount:t,permissions:s,adminScope:n=null}){const e=$(t).get(0);if(!e)return;let i,r=null,l=null,o=null,c=null,d=null;try{const a=await p.get("/sidebar/menu");i=Array.isArray(a?.items)?a.items:null,r=a?.variant||null,l=a?.position||null,o=a?.style||null,c=a?.float||null,d=a?.highlight||null}catch{i=null}(!i||!i.some(a=>a&&(!a.hidden||a.restricted)))&&(console.warn("[admin-sidebar] No admin-sidebar menu found; using fallback tree"),i=ue.slice());let u=[];try{u=await H.get("/api/sidebar/registered-items")||[]}catch{}i=re(i,de),i=ie(i,u),i=oe(i),i=le(i,[ae()]);const v=await me(s);v.length&&fe(i,v),i=K(i,s,n),i=ce(i);const[y,L]=await Promise.all([be(u.filter(a=>_(s,a?.item?.permission)),s).catch(()=>({})),we()]),E=r?` dm-admin-sidebar--${h(r)}`:"";function w(a,b="px"){if(a==null)return a;const m=String(a).trim();return/^\d+(\.\d+)?$/.test(m)?`${m}${b}`:m}let F="";if(o){const a=[];o.fontFamily&&a.push(`font-family: ${o.fontFamily}, sans-serif`),o.fontSize&&a.push(`font-size: ${w(o.fontSize)}`),o.fontWeight&&a.push(`font-weight: ${o.fontWeight}`),o.letterSpacing&&a.push(`letter-spacing: ${w(o.letterSpacing,"em")}`);const b=[];if(a.length&&b.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 { ${a.join("; ")} }`),o.iconSize){const m=w(o.iconSize);b.push(`#admin-sidebar .dm-admin-sidebar [data-icon], #admin-sidebar .dm-admin-sidebar [data-icon] svg { width: ${m} !important; height: ${m} !important; }`)}b.length&&(F=`<style data-admin-sidebar-style>${b.join(" ")}</style>`)}const B=`<div class="dm-admin-sidebar-header"><span data-icon="layout"></span> ${h(L)}</div>`,R=i.some(a=>a&&a.type==="spacer"&&a.size==="flex")?" dm-admin-sidebar--has-flex-spacer":"",q=`${F}<nav class="dm-admin-sidebar${E}${R}">${B}${i.map(a=>Y(a,y)).join("")}</nav>`,j=document.createRange();j.selectNodeContents(e);const g=j.createContextualFragment(q);e.replaceChildren(g),Domma.icons.scan(e);for(const a of A)I.set(a.url,y[a.url]??null);G(e),$e(e);const f=Number.parseInt(S.get("sidebar.width"),10);Number.isFinite(f)&&f>=180&&f<=480&&(e.style.width=f+"px"),e.removeAttribute("data-menu-hover"),e.removeAttribute("data-menu-active"),e.style.removeProperty("--dm-menu-highlight"),ne(e,{highlight:d});const C=document.querySelector(".dashboard-wrapper");if(["position","top","bottom","left","right","zIndex","maxHeight","transform"].forEach(a=>{e.style[a]=""}),e.classList.remove("dm-menu-floating"),C&&(C.style.paddingLeft="",C.style.paddingRight=""),l==="floating"){te(e,{position:"floating",float:c||{}});const b=c&&c.anchor||"TL",m=Number.isFinite(Number(c?.offsetX))?Number(c.offsetX):16,N=e.getBoundingClientRect().width||f||260;if(C){const z=Math.round(N+m*2)+"px";b==="R"||b==="TR"||b==="BR"?C.style.paddingRight=z:C.style.paddingLeft=z}}const k=document.createElement("div");k.className="dm-admin-sidebar-handle",k.setAttribute("aria-label","Resize sidebar"),e.appendChild(k),e.querySelectorAll("details").forEach(a=>{a.addEventListener("toggle",function(){const b=this.getAttribute("data-state-key");b&&S.set(b,this.open);const m=this.querySelector(":scope > .sidebar-children");if(m)if(this.open){const N=m.scrollHeight;m.style.maxHeight="0",requestAnimationFrame(()=>{m.style.maxHeight=N+"px",m.addEventListener("transitionend",function z(){m.style.maxHeight="none",m.removeEventListener("transitionend",z)})})}else{const N=m.scrollHeight;m.style.maxHeight=N+"px",requestAnimationFrame(()=>{m.style.maxHeight="0"})}})});let x=!1,W=0,O=0;k.addEventListener("mousedown",a=>{x=!0,W=a.clientX,O=e.getBoundingClientRect().width,document.body.style.cursor="col-resize",document.body.style.userSelect="none",a.preventDefault()}),e.dataset.resizeWired||(e.dataset.resizeWired="1",document.addEventListener("mousemove",a=>e.__dmResize?.move(a)),document.addEventListener("mouseup",()=>e.__dmResize?.up()),M.subscribe("router:afterChange",()=>e.__dmSetActive?.())),e.__dmResize={move(a){if(!x)return;const b=Math.max(180,Math.min(480,O+(a.clientX-W)));e.style.width=b+"px"},up(){x&&(x=!1,document.body.style.cursor="",document.body.style.userSelect="",S.set("sidebar.width",parseInt(e.style.width,10)))}};function D(){const a=location.hash||"#/";$(e).find(".sidebar-link").removeClass("active"),$(e).find(`.sidebar-link[data-url="${a}"]`).addClass("active")}D(),e.__dmSetActive=D}
4
+ </details>`}return`<a href="${i}" class="sidebar-link" data-url="${i}"${m}${c}>${n} <span class="sidebar-text">${e}</span>${pe(t,s)}${v}</a>`}async function be(t=[],s=[]){const n=d=>d.then(g=>Array.isArray(g)?g.length:Array.isArray(g?.entries)?g.entries.length:0).catch(()=>0);C=xe(t);const e=new Set(C.map(d=>d.url)),i={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"},o=(d,g)=>N(s,d)&&!e.has(i[d])?g():Promise.resolve(0),[l,a,c,u,m,v,y,k,E,w,R,B,q,T]=await Promise.all([o("pages",()=>n(p.pages.list())),o("media",()=>n(p.media.list())),o("menus",()=>n(p.menus.list())),o("context-menus",()=>n(p.contextMenus.list())),o("theme",()=>p.theme.get().then(d=>(d?.overrides||[]).length).catch(()=>0)),o("collections",()=>n(p.collections.list())),o("forms",()=>n(p.forms.list())),o("views",()=>n(p.views.list())),o("actions",()=>n(p.actions.list())),o("blocks",()=>n(p.blocks.list())),o("components",()=>n(p.components.list())),o("users",()=>n(p.users.list())),o("plugins",()=>n(p.plugins.list())),o("notifications",()=>p.system?.notifications?.unreadCount?.().then(d=>d?.count??0).catch(()=>0)??Promise.resolve(0))]),j={};await Promise.all(C.map(async d=>{const g=await Z(d);g&&(j[d.url]=g)}));const I={"#/pages":l,"#/media":a,"#/menus":c,"#/context-menus":u,"#/theme":m,"#/collections":v,"#/forms":y,"#/views":k,"#/actions":E,"#/blocks":w,"#/components":R,"#/users":B,"#/plugins":q,"#/system/notifications":T},x={};for(const[d,g]of Object.entries(I)){if(e.has(d))continue;const[L,_]=he[d]??["item","items"],A=U(g);A&&(x[d]={...A,label:`${A.count} ${A.count===1?L:_}`})}return{...x,...j}}let C=[];const ye="dm:sidebar-count",ve=6e4;function xe(t=[]){const s=[],n=e=>{if(e){e.url&&typeof e.countUrl=="string"&&e.countUrl&&s.push({url:e.url,countUrl:e.countUrl,text:e.text||"",countLabel:e.countLabel||"",inventory:e.inventory===!0});for(const i of[...e.children||[],...e.items||[]])n(i)}};for(const e of t??[])n(e?.item??e);return s}async function Z(t){try{const s=new Date,n=`${s.getFullYear()}-${String(s.getMonth()+1).padStart(2,"0")}-${String(s.getDate()).padStart(2,"0")}`,e=t.countUrl+(t.countUrl.includes("?")?"&":"?")+`today=${n}`;return U(await H.get(e),{fallbackLabel:t.countLabel||t.text})}catch{return null}}function ee(t,s,n){const e=[...t.querySelectorAll("a.sidebar-link")].find(o=>o.dataset.url===s);if(!e)return;const i=e.querySelector(".sidebar-badge[data-count-for]");if(!n||!(n.count>0))i?.remove();else{const o=document.createRange();o.selectNodeContents(e);const l=o.createContextualFragment(G(s,n)).firstElementChild;i?i.replaceWith(l):e.querySelector(".sidebar-text")?.after(l)}O.set(s,n),J(t)}const O=new Map;async function W(t,s=null){const n=s?C.filter(e=>e.url===s):C;await Promise.all(n.map(async e=>{const i=await Z(e),o=O.get(e.url);JSON.stringify(o??null)!==JSON.stringify(i)&&ee(t,e.url,i)}))}function $e(t){t.dataset.liveCounts||(t.dataset.liveCounts="1",window.addEventListener(ye,s=>{const n=s.detail||{};if(n.url)if(n.count!==void 0){const e=C.find(i=>i.url===n.url);ee(t,n.url,U(n,{fallbackLabel:e?.countLabel||e?.text||""}))}else W(t,n.url)}),setInterval(()=>{!document.hidden&&document.body.contains(t)&&W(t)},ve),document.addEventListener("visibilitychange",()=>{document.hidden||W(t)}),Se(t))}function Ce(t,s,n){const e=(l,a,c)=>{const u=document.createElement(l);return a&&(u.className=a),c!=null&&(u.textContent=String(c)),u};if(!n){t.textContent=s;return}t.appendChild(e("strong","dm-badge-pop-title",n.title||s)),(n.rows?.length||!n.items?.length)&&t.appendChild(e("p","dm-badge-pop-sub",s));const i=Array.isArray(n.rows)?n.rows.slice(0,6):[];if(i.length){const l=e("dl","dm-badge-pop-rows");for(const a of i)l.appendChild(e("dt",null,a.label)),l.appendChild(e("dd",a.tone==="danger"?"is-danger":null,a.value));t.appendChild(l)}const o=Array.isArray(n.items)?n.items.slice(0,6):[];if(o.length){const l=e("ul","dm-badge-pop-items");for(const a of o){const c=e("li",a.tone==="danger"?"is-danger":null);c.appendChild(e("span","dm-badge-pop-text",a.text)),a.meta&&c.appendChild(e("span","dm-badge-pop-meta",a.meta)),l.appendChild(c)}t.appendChild(l)}else n.empty&&t.appendChild(e("p","dm-badge-pop-empty",n.empty))}function Se(t){let s=null,n=null;const e=()=>{clearTimeout(n),s?.remove(),s=null},i=o=>{e();const l=o?.dataset.badgeLabel;l&&(n=setTimeout(()=>{s=document.createElement("div"),s.className="dm-help-pop dm-badge-pop",s.setAttribute("role","tooltip"),Ce(s,l,P.get(o.dataset.countFor)),document.body.appendChild(s);const a=o.getBoundingClientRect(),c=s.getBoundingClientRect(),u=Math.min(a.right+10,innerWidth-c.width-8);s.style.left=`${Math.max(8,u)}px`,s.style.top=`${Math.max(8,Math.min(a.top+a.height/2-c.height/2,innerHeight-c.height-8))}px`},150))};t.addEventListener("mouseover",o=>{const l=o.target.closest?.(".sidebar-badge[data-badge-label]");l&&i(l)}),t.addEventListener("mouseout",o=>{o.target.closest?.(".sidebar-badge[data-badge-label]")&&e()}),t.addEventListener("focusin",o=>{const l=o.target.closest?.("a, summary")?.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");l?i(l):e()}),t.addEventListener("focusout",e),window.addEventListener("scroll",e,!0),window.addEventListener("hashchange",e)}async function we(t=[]){if(!N(t,"settings"))return"Admin";try{const s=await p.settings.get();return s?.adminBrand?.title||s?.title||"Admin"}catch{return"Admin"}}export async function renderAdminSidebar({mount:t,permissions:s,adminScope:n=null}){const e=$(t).get(0);if(!e)return;let i,o=null,l=null,a=null,c=null,u=null;try{const r=await p.get("/sidebar/menu");i=Array.isArray(r?.items)?r.items:null,o=r?.variant||null,l=r?.position||null,a=r?.style||null,c=r?.float||null,u=r?.highlight||null}catch{i=null}(!i||!i.some(r=>r&&(!r.hidden||r.restricted)))&&(console.warn("[admin-sidebar] No admin-sidebar menu found; using fallback tree"),i=ue.slice());let m=[];try{m=await H.get("/api/sidebar/registered-items")||[]}catch{}i=le(i,K),i=oe(i,m.filter(r=>(r?.item??r)?.url!==K)),i=ae(i),i=ce(i,[re()]);const v=await me(s);v.length&&fe(i,v),i=V(i,s,n),i=de(i);const[y,k]=await Promise.all([be(m.filter(r=>N(s,r?.item?.permission)),s).catch(()=>({})),we(s)]),E=o?` dm-admin-sidebar--${h(o)}`:"";function w(r,b="px"){if(r==null)return r;const f=String(r).trim();return/^\d+(\.\d+)?$/.test(f)?`${f}${b}`:f}let R="";if(a){const r=[];a.fontFamily&&r.push(`font-family: ${a.fontFamily}, sans-serif`),a.fontSize&&r.push(`font-size: ${w(a.fontSize)}`),a.fontWeight&&r.push(`font-weight: ${a.fontWeight}`),a.letterSpacing&&r.push(`letter-spacing: ${w(a.letterSpacing,"em")}`);const b=[];if(r.length&&b.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 { ${r.join("; ")} }`),a.iconSize){const f=w(a.iconSize);b.push(`#admin-sidebar .dm-admin-sidebar [data-icon], #admin-sidebar .dm-admin-sidebar [data-icon] svg { width: ${f} !important; height: ${f} !important; }`)}b.length&&(R=`<style data-admin-sidebar-style>${b.join(" ")}</style>`)}const B=`<div class="dm-admin-sidebar-header"><span data-icon="layout"></span> ${h(k)}</div>`,q=i.some(r=>r&&r.type==="spacer"&&r.size==="flex")?" dm-admin-sidebar--has-flex-spacer":"",T=`${R}<nav class="dm-admin-sidebar${E}${q}">${B}${i.map(r=>Q(r,y)).join("")}</nav>`,j=document.createRange();j.selectNodeContents(e);const I=j.createContextualFragment(T);e.replaceChildren(I),Domma.icons.scan(e);for(const r of C)O.set(r.url,y[r.url]??null);J(e),$e(e);const x=Number.parseInt(S.get("sidebar.width"),10);Number.isFinite(x)&&x>=180&&x<=480&&(e.style.width=x+"px"),e.removeAttribute("data-menu-hover"),e.removeAttribute("data-menu-active"),e.style.removeProperty("--dm-menu-highlight"),se(e,{highlight:u});const d=document.querySelector(".dashboard-wrapper");if(["position","top","bottom","left","right","zIndex","maxHeight","transform"].forEach(r=>{e.style[r]=""}),e.classList.remove("dm-menu-floating"),d&&(d.style.paddingLeft="",d.style.paddingRight=""),l==="floating"){ne(e,{position:"floating",float:c||{}});const b=c&&c.anchor||"TL",f=Number.isFinite(Number(c?.offsetX))?Number(c.offsetX):16,F=e.getBoundingClientRect().width||x||260;if(d){const z=Math.round(F+f*2)+"px";b==="R"||b==="TR"||b==="BR"?d.style.paddingRight=z:d.style.paddingLeft=z}}const g=document.createElement("div");g.className="dm-admin-sidebar-handle",g.setAttribute("aria-label","Resize sidebar"),e.appendChild(g),e.querySelectorAll("details").forEach(r=>{r.addEventListener("toggle",function(){const b=this.getAttribute("data-state-key");b&&S.set(b,this.open);const f=this.querySelector(":scope > .sidebar-children");if(f)if(this.open){const F=f.scrollHeight;f.style.maxHeight="0",requestAnimationFrame(()=>{f.style.maxHeight=F+"px",f.addEventListener("transitionend",function z(){f.style.maxHeight="none",f.removeEventListener("transitionend",z)})})}else{const F=f.scrollHeight;f.style.maxHeight=F+"px",requestAnimationFrame(()=>{f.style.maxHeight="0"})}})});let L=!1,_=0,A=0;g.addEventListener("mousedown",r=>{L=!0,_=r.clientX,A=e.getBoundingClientRect().width,document.body.style.cursor="col-resize",document.body.style.userSelect="none",r.preventDefault()}),e.dataset.resizeWired||(e.dataset.resizeWired="1",document.addEventListener("mousemove",r=>e.__dmResize?.move(r)),document.addEventListener("mouseup",()=>e.__dmResize?.up()),M.subscribe("router:afterChange",()=>e.__dmSetActive?.())),e.__dmResize={move(r){if(!L)return;const b=Math.max(180,Math.min(480,A+(r.clientX-_)));e.style.width=b+"px"},up(){L&&(L=!1,document.body.style.cursor="",document.body.style.userSelect="",S.set("sidebar.width",parseInt(e.style.width,10)))}};function D(){const r=location.hash||"#/";$(e).find(".sidebar-link").removeClass("active"),$(e).find(`.sidebar-link[data-url="${r}"]`).addClass("active")}D(),e.__dmSetActive=D}
@@ -0,0 +1 @@
1
+ export const TAB_FIELDS={general:["title","tagline","adminHome","spacerSize"],seo:["seoTitle","seoSeparator","seoDescription"],footer:["footerCopyright","footerMotionToggle","socialTwitter","socialFacebook","socialInstagram","socialLinkedin","socialGithub","socialYoutube"],email:["smtpHost","smtpPort","smtpUser","smtpPass","smtpSecure","smtpFromAddress","smtpFromName"],backToTop:["bttEnabled","bttSmooth","bttLabel","bttThreshold","bttPosition","bttOffset","bttBottomOffset"],cookies:["ccEnabled","ccMessage","ccAcceptAll","ccRejectAll","ccCustomize","ccSavePrefs","ccPrivacyText","ccPrivacyUrl","ccCookieText","ccCookieUrl","ccPosition","ccLayout","ccTheme","ccShowFunctional","ccShowAnalytics","ccShowMarketing","ccVersion"],breadcrumbs:["bcEnabled","bcHomeLabel","bcPosition","bcOffsetX","bcOffsetY"],css:["css"]},SECRET_FIELDS=["smtpPass"],BREADCRUMB_POSITIONS=["TL","TR","BL","BR"];const t=(e,c="")=>e==null?c:String(e),r=(e,c)=>{const s=Number.parseInt(e,10);return Number.isFinite(s)?s:c};export function homeValue(e){return/^\/[A-Za-z0-9\-_/]*$/.test(e||"")&&String(e).replace(/\/+$/,"")||"/"}export function formFromSettings(e={},c=""){const s=e.backToTop||{},o=e.cookieConsent||{},i=e.breadcrumbs||{};return{title:t(e.title),tagline:t(e.tagline),adminHome:homeValue(e.adminHome),spacerSize:t(e.layoutOptions?.spacerSize??40),seoTitle:t(e.seo?.defaultTitle),seoSeparator:t(e.seo?.titleSeparator," | "),seoDescription:t(e.seo?.defaultDescription),footerCopyright:t(e.footer?.copyright),footerMotionToggle:e.footer?.showMotionToggle!==!1,socialTwitter:t(e.social?.twitter),socialFacebook:t(e.social?.facebook),socialInstagram:t(e.social?.instagram),socialLinkedin:t(e.social?.linkedin),socialGithub:t(e.social?.github),socialYoutube:t(e.social?.youtube),smtpHost:t(e.smtp?.host),smtpPort:t(e.smtp?.port||587),smtpUser:t(e.smtp?.user),smtpPass:"",smtpSecure:e.smtp?.secure===!0,smtpFromAddress:t(e.smtp?.fromAddress),smtpFromName:t(e.smtp?.fromName),bttEnabled:s.enabled!==!1,bttSmooth:s.smooth!==!1,bttLabel:t(s.label),bttThreshold:t(s.scrollThreshold??300),bttPosition:t(s.position,"bottom-right"),bttOffset:t(s.offset??32),bttBottomOffset:t(s.bottomOffset??s.offset??32),ccEnabled:o.enabled!==!1,ccMessage:t(o.message),ccAcceptAll:t(o.acceptAllText,"Accept All"),ccRejectAll:t(o.rejectAllText,"Reject All"),ccCustomize:t(o.customizeText,"Customize"),ccSavePrefs:t(o.savePreferencesText,"Save Preferences"),ccPrivacyText:t(o.privacyPolicyText,"Privacy Policy"),ccPrivacyUrl:t(o.privacyPolicyUrl),ccCookieText:t(o.cookiePolicyText,"Cookie Policy"),ccCookieUrl:t(o.cookiePolicyUrl),ccPosition:t(o.position,"bottom"),ccLayout:t(o.layout,"bar"),ccTheme:t(o.theme,"dark"),ccShowFunctional:o.showFunctional!==!1,ccShowAnalytics:o.showAnalytics!==!1,ccShowMarketing:o.showMarketing!==!1,ccVersion:t(o.consentVersion,"1.0"),bcEnabled:i.enabled===!0,bcHomeLabel:t(i.homeLabel,"Home"),bcPosition:BREADCRUMB_POSITIONS.includes(i.position)?i.position:"TL",bcOffsetX:t(i.offsetX??8),bcOffsetY:t(i.offsetY??8),css:t(c)}}export function payloadFromForm(e,c={},{adminHomeReady:s=!0}={}){const o=i=>t(i).trim();return{title:o(e.title),tagline:o(e.tagline),...s&&{adminHome:o(e.adminHome)||"/"},layoutOptions:{spacerSize:r(e.spacerSize,40)||40},seo:{defaultTitle:o(e.seoTitle),titleSeparator:t(e.seoSeparator)||" | ",defaultDescription:o(e.seoDescription)},footer:{copyright:o(e.footerCopyright),showMotionToggle:e.footerMotionToggle===!0,links:c.footer?.links||[]},social:{twitter:o(e.socialTwitter),facebook:o(e.socialFacebook),instagram:o(e.socialInstagram),linkedin:o(e.socialLinkedin),github:o(e.socialGithub),youtube:o(e.socialYoutube)},smtp:{host:o(e.smtpHost),port:r(e.smtpPort,587)||587,user:o(e.smtpUser),pass:t(e.smtpPass),secure:e.smtpSecure===!0,fromAddress:o(e.smtpFromAddress),fromName:o(e.smtpFromName)},backToTop:{enabled:e.bttEnabled===!0,scrollThreshold:r(e.bttThreshold,300),position:t(e.bttPosition)||"bottom-right",offset:r(e.bttOffset,32),bottomOffset:r(e.bttBottomOffset,32),label:o(e.bttLabel),smooth:e.bttSmooth===!0},cookieConsent:{enabled:e.ccEnabled===!0,message:o(e.ccMessage),acceptAllText:o(e.ccAcceptAll),rejectAllText:o(e.ccRejectAll),customizeText:o(e.ccCustomize),savePreferencesText:o(e.ccSavePrefs),privacyPolicyText:o(e.ccPrivacyText),privacyPolicyUrl:o(e.ccPrivacyUrl),cookiePolicyText:o(e.ccCookieText),cookiePolicyUrl:o(e.ccCookieUrl),position:t(e.ccPosition)||"bottom",layout:t(e.ccLayout)||"bar",theme:t(e.ccTheme)||"dark",showFunctional:e.ccShowFunctional===!0,showAnalytics:e.ccShowAnalytics===!0,showMarketing:e.ccShowMarketing===!0,consentVersion:o(e.ccVersion)||"1.0"},breadcrumbs:{enabled:e.bcEnabled===!0,homeLabel:o(e.bcHomeLabel)||"Home",position:BREADCRUMB_POSITIONS.includes(e.bcPosition)?e.bcPosition:"TL",offsetX:r(e.bcOffsetX,8),offsetY:r(e.bcOffsetY,8)}}}const a=e=>typeof e=="boolean"?e:t(e);export function snapshot(e){return JSON.stringify(Object.keys(e).sort().map(c=>[c,a(e[c])]))}export function changedTabs(e,c){const s=[];for(const[o,i]of Object.entries(TAB_FIELDS))i.some(n=>a(e[n])!==a(c[n]))&&s.push(o);return s}export function draftOf(e){return Object.fromEntries(Object.entries(e).filter(([c])=>!SECRET_FIELDS.includes(c)))}export function settingsProblems(e){const c=[];return t(e.smtpHost).trim()?t(e.smtpFromAddress).trim()||c.push({tone:"warn",tab:"email",text:"No From address - mail has no sender and many servers refuse it."}):c.push({tone:"danger",tab:"email",text:"No SMTP host - mail goes to a throwaway test inbox and reaches no one (password resets, form notifications)."}),e.ccEnabled===!0&&!t(e.ccPrivacyUrl).trim()&&c.push({tone:"warn",tab:"cookies",text:"The cookie banner is on but links to no privacy policy."}),c}export function describeCacheKey(e){const c=/^page:(.*?):m\d+:role([^:]*):/.exec(String(e||""));return c?{path:c[1]||"/",role:c[2]||"anon",page:!0}:{path:String(e||""),role:"",page:!1}}export function expiresIn(e,c=Date.now()){if(e==null)return"no expiry";const s=e-c;return s<=0?"expired":s<6e4?`${Math.round(s/1e3)}s`:s<36e5?`${Math.round(s/6e4)}m`:`${Math.round(s/36e5)}h`}
@@ -0,0 +1 @@
1
+ import{THEMES as l}from"./themes.js";export const FILL_TOKENS=["--dm-card-bg","--dm-surface"],VISIBLE_TOKENS=[...FILL_TOKENS,"--dm-text","--dm-primary","--dm-background"],SWITCHER_POSITIONS=["right-center","left-center","bottom-right","bottom-left","top-right","top-left"],newOverrideId=()=>`ov-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;export function cloneOverride(e={},t=newOverrideId){return{id:e.id||t(),label:String(e.label??""),enabled:e.enabled!==!1,scope:String(e.scope??""),theme:String(e.theme??""),tokens:{...e.tokens||{}},css:String(e.css??"")}}export function formFromConfig(e={}){const t=e.autoTheme||{},r=e.switcher||{};return{theme:e.theme||"charcoal-dark",adminTheme:e.adminTheme||"charcoal-dark",autoEnabled:!!t.enabled,dayTheme:t.dayTheme||"charcoal-light",nightTheme:t.nightTheme||"charcoal-dark",dayStart:t.dayStart||"07:00",nightStart:t.nightStart||"19:00",fontFamily:e.font?.family||"Roboto",fontSize:String(e.font?.size??16),swEnabled:r.enabled===!0,swPosition:SWITCHER_POSITIONS.includes(r.position)?r.position:"right-center",swOffsetX:String(r.offsetX??32),swOffsetY:String(r.offsetY??32),overrides:(Array.isArray(e.overrides)?e.overrides:[]).map(n=>cloneOverride(n))}}const h=(e,t)=>{const r=Number.parseInt(e,10);return Number.isFinite(r)?r:t};export function payloadFromForm(e,t={}){const r={enabled:!!e.autoEnabled,dayTheme:e.dayTheme||"charcoal-light",nightTheme:e.nightTheme||"charcoal-dark",dayStart:e.dayStart||"07:00",nightStart:e.nightStart||"19:00"};return{theme:r.enabled?r.dayTheme:e.theme,adminTheme:e.adminTheme,autoTheme:r,font:{family:e.fontFamily||"Roboto",size:h(e.fontSize,16)||16},switcher:{enabled:!!e.swEnabled,position:e.swPosition||"right-center",offsetX:h(e.swOffsetX,0),offsetY:h(e.swOffsetY,0),themes:Array.isArray(t.switcher?.themes)?t.switcher.themes:[]},overrides:(e.overrides||[]).map(n=>({id:n.id,label:String(n.label??"").trim(),enabled:n.enabled!==!1,scope:String(n.scope??"").trim(),theme:n.theme||"",tokens:Object.fromEntries(Object.entries(n.tokens||{}).map(([i,s])=>[String(i).trim(),String(s??"").trim()]).filter(([i,s])=>i&&s)),css:String(n.css??"")}))}}export function snapshot(e,t={}){const r=payloadFromForm(e,t);return JSON.stringify(r)}function c(e){const t=/^(\d{1,2}):(\d{2})$/.exec(String(e||""));return t?Number(t[1])*60+Number(t[2]):null}export function dayOrNight(e={},t=new Date){const r=c(e.dayStart)??420,n=c(e.nightStart)??1140,i=t.getHours()*60+t.getMinutes();return r===n?"day":r<n?i>=r&&i<n?"day":"night":i>=n&&i<r?"night":"day"}export function themeLabel(e,t=[]){if(!e)return"";const r=t.find(i=>i.id===e);if(r)return r.label;const n=l.find(i=>i.id===e);return n?n.admin?`${n.label} ${n.name}`:`${n.label} (${n.dark?"dark":"light"})`:e}export function themeDiff(e,t,r=6){const n=e||{},i=t||{},s=Object.keys(i).filter(a=>String(n[a]??"").toLowerCase()!==String(i[a]).toLowerCase()),o=[...VISIBLE_TOKENS.filter(a=>s.includes(a)),...s.filter(a=>!VISIBLE_TOKENS.includes(a))];return{changed:s.length,total:Object.keys(i).length,keepsFill:!FILL_TOKENS.some(a=>s.includes(a)),shown:o.slice(0,r).map(a=>({name:a,from:n[a]||"",to:i[a]})),more:Math.max(0,o.length-r)}}export const swatchColour=e=>/^#[0-9a-f]{3,8}$/i.test(String(e||""))?String(e):"";export function knownThemeIds(e={}){return new Set([...e.themes||[],...(e.custom||[]).map(t=>t.id)])}export function themeProblems(e,t){if(!t||!t.size)return[];t=new Set([...t,"core-light","core-dark"]);const r=[],n=(i,s)=>{s&&!t.has(s)&&r.push({where:i,id:s})};return e.autoEnabled?(n("Day theme",e.dayTheme),n("Night theme",e.nightTheme)):n("Site theme",e.theme),n("Admin theme",e.adminTheme),(e.overrides||[]).forEach((i,s)=>n(`Override "${i.label||i.scope||s+1}"`,i.theme)),r}export function overrideRow(e,t,{custom:r=[],known:n=null}={}){const i=Object.keys(e.tokens||{}).filter(o=>o&&String(e.tokens[o]??"").trim()).length,s=[e.scope||"no selector yet",e.theme?`adopts ${themeLabel(e.theme,r)}`:"",i?`${i} token${i===1?"":"s"}`:"",String(e.css||"").trim()?"custom CSS":""].filter(Boolean);return{id:e.id,name:e.label||e.scope||`Override ${t+1}`,meta:s.join(" \xB7 "),off:e.enabled===!1,missing:!!(e.theme&&n&&n.size&&!n.has(e.theme)),empty:!e.theme&&!i&&!String(e.css||"").trim()}}export function moveItem(e,t,r){const n=t+r;if(t<0||t>=e.length||n<0||n>=e.length)return e;const i=[...e],[s]=i.splice(t,1);return i.splice(n,0,s),i}
@@ -0,0 +1 @@
1
+ export const FILTERS=["all","active","inactive"],SORTS=["name","recent"];const f={"badge-danger":"danger","badge-warning":"warn","badge-success":"ok","badge-primary":"primary","badge-info":"primary"};export function roleIndex(e=[]){const t={};for(const a of e||[]){const i=a?.data||a||{};i.name&&(t[i.name]={label:i.label||i.name,level:Number.isFinite(i.level)?i.level:99,tone:f[i.badgeClass]||""})}return t}const d=(e,t)=>e[t]?.level??99;export function canManage(e,t,a={}){const i=!!e?.id&&e.id===t?.id,o=d(a,e?.role)<d(a,t?.role);return{self:i,edit:i||o,other:!i&&o}}export function initials(e){const t=i=>(String(i).match(/[\p{L}\p{N}]/u)||[""])[0],a=String(e?.name||"").split(/\s+/).map(t).filter(Boolean);return a.length?(a[0]+(a.length>1?a.at(-1):"")).toUpperCase():(t(e?.email||"")||"?").toUpperCase()}export function arrangeUsers(e,{filter:t="all",role:a="",query:i="",sort:o="name"}={}){const l=String(i).trim().toLowerCase(),c=(e||[]).filter(n=>t==="active"&&n.isActive===!1||t==="inactive"&&n.isActive!==!1||a&&n.role!==a&&!(Array.isArray(n.additionalRoles)&&n.additionalRoles.includes(a))?!1:l?[n.name,n.email,n.role,...n.additionalRoles||[],...n.projects||[]].some(r=>String(r||"").toLowerCase().includes(l)):!0),s=(n,r)=>String(n.name||n.email||"").localeCompare(String(r.name||r.email||""));return o==="recent"?c.sort((n,r)=>(n.lastLogin?0:1)-(r.lastLogin?0:1)||String(r.lastLogin||"").localeCompare(String(n.lastLogin||""))||s(n,r)):c.sort(s)}export function userRow(e,{roles:t={},me:a=null,when:i=o=>o}={}){const o=canManage(a,e,t),l=t[e.role]||{label:e.role||"no role",tone:""},c=(Array.isArray(e.additionalRoles)?e.additionalRoles:[]).filter(n=>n&&n!==e.role).map(n=>t[n]?.label||n),s=Array.isArray(e.projects)&&e.projects.length?e.projects:[];return{id:e.id,name:e.name||e.email||"Unnamed",initials:initials(e),meta:[e.email,e.lastLogin?`signed in ${i(e.lastLogin)}`:"never signed in"].filter(Boolean).join(" \xB7 "),roleLabel:l.label,roleTone:l.tone?`is-${l.tone}`:"",extraRoles:c.join(", "),scope:s.length?`Access: ${s.join(", ")}`:"",inactive:e.isActive===!1,self:o.self,locked:!o.edit,rowCls:[e.isActive===!1&&"is-off",!o.edit&&"is-locked"].filter(Boolean).join(" ")}}
@@ -0,0 +1 @@
1
+ export const FILTERS=["all","public","restricted","broken"];const l={table:"Table",cards:"Cards",list:"List",block:"Block",accordion:"Accordion",timeline:"Timeline",carousel:"Carousel",listgroup:"List group"},u=(e,r,s=`${r}s`)=>`${e} ${e===1?r:s}`;export function projectScope(e=""){const r=String(e).split("?")[0].match(/^#\/projects\/([^/]+)\/views\/?$/);if(!r)return null;try{return decodeURIComponent(r[1])}catch{return r[1]}}export const projectOf=e=>e?.meta?.project||"core",newViewHref=e=>e?`#/views/new?project=${encodeURIComponent(e)}`:"#/views/new",shortcodeFor=e=>`[view slug="${e}" /]`,isBroken=e=>e?.sourceInfo?.exists===!1;export function arrangeViews(e,{filter:r="all",query:s="",project:o=null}={}){const i=String(s).trim().toLowerCase(),n={all:()=>!0,public:t=>t.access?.public===!0,restricted:t=>t.access?.public!==!0,broken:isBroken}[r]||(()=>!0);return[...e||[]].filter(t=>!o||projectOf(t)===o).filter(n).filter(t=>!i||[t.title,t.slug,t.description,t.pipeline?.source,t.sourceInfo?.title].some(c=>String(c||"").toLowerCase().includes(i))).sort((t,c)=>Number(isBroken(c))-Number(isBroken(t))||String(t.title||t.slug).localeCompare(String(c.title||c.slug)))}export function countViews(e,{project:r=null}={}){const s=(e||[]).filter(o=>!r||projectOf(o)===r);return{all:s.length,public:s.filter(o=>o.access?.public===!0).length,restricted:s.filter(o=>o.access?.public!==!0).length,broken:s.filter(isBroken).length}}export function viewRow(e){const r=Array.isArray(e.pipeline?.stages)?e.pipeline.stages.length:0,s=e.pipeline?.source||"-",o=e.sourceInfo,i=isBroken(e),n=!o||i?"":o.adapter==="mongodb"?"MongoDB aggregation":"JS pipeline",t=Array.isArray(e.access?.roles)?e.access.roles:[];return{slug:e.slug,title:e.title||e.slug,meta:[e.slug,`from ${o?.title&&!i?o.title:s}`,u(r,"stage"),l[e.display?.mode]||e.display?.mode||"Table"].join(" \xB7 "),description:e.description||"",access:e.access?.public?"Public":t.length?t.join(", "):"Admins",accessCls:e.access?.public?"is-ok":"",engine:n,project:e.meta?.project&&e.meta.project!=="core"?e.meta.project:"",broken:i,brokenText:i?`Source "${s}" is gone`:"",rowCls:i?"is-broken":""}}