domma-cms 0.109.4 → 0.110.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/css/calculator.css +1 -0
- package/admin/index.html +1 -0
- package/admin/js/app.js +1 -1
- package/admin/js/lib/calculator-engine.js +1 -0
- package/admin/js/lib/calculator.js +17 -0
- package/admin/js/lib/sidebar-renderer.js +2 -2
- package/admin/js/templates/docs/usage-tools.html +3 -0
- package/admin/js/views/index.js +1 -1
- package/package.json +1 -1
- package/server/routes/api/calculator.js +5 -0
- package/server/server.js +3 -0
- package/server/services/hooks.js +1 -1
- package/server/services/permissionRegistry.js +8 -0
- package/server/services/roles.js +1 -1
- package/server/services/tools.js +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.calculator-page{max-width:760px;margin:24px auto}.calculator-panel{--calc-accent: #16b8a6;--calc-rgb: 22,184,166;color:var(--dm-text);background:var(--dm-surface);border:1px solid var(--dm-border);border-radius:18px;padding:20px;max-width:440px;margin:auto;box-shadow:0 12px 36px #0000001f}.calculator-panel.accent-blue{--calc-accent: #5099f5;--calc-rgb: 80,153,245}.calculator-panel.accent-violet{--calc-accent: #a480ef;--calc-rgb: 164,128,239}.calculator-panel.accent-amber{--calc-accent: #eca83e;--calc-rgb: 236,168,62}.calculator-panel.is-scientific{max-width:740px}.calculator-floating{position:fixed;z-index:900;width:440px;max-width:calc(100vw - 24px);max-height:calc(100dvh - 24px);overflow:auto;border:1px solid var(--dm-border);border-radius:18px;background:var(--dm-surface);box-shadow:0 24px 80px #00000059}.calculator-floating:has(.is-scientific){width:680px}.calculator-floating .dm-plugin-banner{cursor:move;touch-action:none;padding:14px 18px;margin:0;border-radius:0}.calculator-floating .dm-plugin-banner-name{font-size:1.1rem}.calculator-floating .dm-plugin-body{padding:0}.calculator-floating .calculator-panel{border:0;box-shadow:none;border-radius:0}.calculator-toolbar{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:16px}.calculator-layout{display:flex;padding:4px;gap:4px;border-radius:10px;background:rgb(var(--calc-rgb),.08)}.calculator-layout button{border:0;border-radius:7px;padding:7px 12px;color:var(--dm-text);background:transparent;cursor:pointer}.calculator-layout .is-active{background:var(--calc-accent);color:#071f24;font-weight:700}.calculator-angle{max-width:120px}.calculator-panel:not(.is-scientific) .calculator-angle,.calculator-panel:not(.is-scientific) .calculator-scientific{display:none}.calculator-screen{border-radius:12px;border:1px solid rgb(var(--calc-rgb),.35);background:linear-gradient(135deg,rgb(var(--calc-rgb),.16),rgb(var(--calc-rgb),.04));padding:14px 18px 10px;margin-bottom:14px;box-shadow:inset 0 2px 8px #00000014}.calculator-screen-meta{display:flex;justify-content:space-between;gap:12px;color:var(--calc-accent);font-size:.72rem;font-weight:700;letter-spacing:.06em;overflow-wrap:anywhere}.calculator-history{min-height:22px;text-align:right;margin-top:8px;font-size:.75rem;opacity:.6;overflow-wrap:anywhere}.calculator-expression{width:100%;box-sizing:border-box;border:0;border-radius:4px;background:transparent;color:var(--dm-text);font-size:1rem;text-align:right;padding:8px 0;outline-offset:3px}.calculator-result{display:block;min-height:52px;font-size:clamp(1.9rem,4vw,2.6rem);font-weight:600;font-variant-numeric:tabular-nums;letter-spacing:-.04em;text-align:right;overflow-wrap:anywhere}.calculator-error{color:var(--dm-danger, #ef7186);min-height:20px;font-size:.8rem;text-align:right}.calculator-memory{display:grid;grid-template-columns:repeat(5,1fr);gap:6px;margin-bottom:16px}.calculator-memory button{border:0;border-radius:7px;padding:8px;background:rgb(var(--calc-rgb),.1);color:var(--calc-accent);font-weight:700;cursor:pointer}.calculator-memory button:disabled{opacity:.35;cursor:default}.calculator-keypads{display:grid;gap:16px}.is-scientific .calculator-keypads{grid-template-columns:1fr 1.4fr}.calculator-keys{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}.calculator-scientific{grid-template-columns:repeat(3,1fr);gap:6px}.calculator-key{min-height:48px;border:1px solid var(--dm-border);border-radius:10px;background:var(--dm-surface-alt, rgb(128 128 128 / .09));color:var(--dm-text);font-size:1.15rem;font-weight:600;cursor:pointer;box-shadow:0 2px #00000014;transition:filter .12s,transform .12s}.calculator-key:hover{filter:brightness(1.15);transform:translateY(-1px)}.calculator-key:active{transform:translateY(1px);box-shadow:none}.calculator-key.key-operator{color:var(--calc-accent);background:rgb(var(--calc-rgb),.15);border-color:rgb(var(--calc-rgb),.2)}.calculator-key.key-clear{color:#e4788e;background:#e4788e1f;border-color:#e4788e33}.calculator-key.key-equals{background:var(--calc-accent);color:#071f24;border-color:var(--calc-accent)}.calculator-key.key-science{min-height:38px;font-size:.9rem;color:#aa91e9;background:#9678dc17;border-color:#9678dc26}.calculator-hint{font-size:.7rem;opacity:.5;text-align:center;margin-top:16px}.calculator-dialog{width:400px;max-width:calc(100vw - 48px);border:1px solid var(--dm-border);border-radius:12px;background:var(--dm-surface);color:var(--dm-text);padding:24px}.calculator-dialog::backdrop{background:#00000080}.calculator-dialog-actions{display:flex;gap:12px;justify-content:flex-end;margin-top:20px}.calculator-launch-options{display:grid;gap:12px;margin-top:20px}.calculator-launch-options button{text-align:left;padding:14px;cursor:pointer}.calculator-launch-options small{display:block;margin-top:4px;white-space:normal}.calculator-settings{padding:20px}.calculator-settings label{display:block;margin:20px 0}.calculator-settings select{display:block;width:100%;margin-top:8px}@media(max-width:580px){.is-scientific .calculator-keypads{grid-template-columns:1fr}.calculator-scientific{grid-template-columns:repeat(6,1fr)}.calculator-panel{padding:14px}.calculator-key.key-science{font-size:.75rem}}.calculator-panel{box-sizing:border-box;width:100%;min-width:0}.calculator-keypads,.calculator-keys,.calculator-memory{min-width:0}.calculator-keys{grid-template-columns:repeat(4,minmax(0,1fr))}.calculator-scientific{grid-template-columns:repeat(3,minmax(0,1fr))}.calculator-expression{min-width:0}@media(max-width:580px){.calculator-scientific{grid-template-columns:repeat(6,minmax(0,1fr))}}
|
package/admin/index.html
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
<!-- Menu item hover / current-page effects - the admin sidebar is a menu
|
|
27
27
|
like any other, so it honours the same `highlight` block. -->
|
|
28
28
|
<link rel="stylesheet" href="/public/css/menu-highlight.css?v=20260905-menu-highlight">
|
|
29
|
+
<link rel="stylesheet" href="/admin/css/calculator.css">
|
|
29
30
|
</head>
|
|
30
31
|
<body class="dm-cloaked dm-theme-charcoal-dark dashboard-layout">
|
|
31
32
|
|
package/admin/js/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{views as q}from"./views/index.js";import{withPluginChrome as B}from"./lib/plugin-chrome.js";import{api as r,getUser as O,isAuthenticated as u,logout as G,sendToLogin as z}from"./api.js";import{isAuthScreen as C,loginHashFor as K}from"./lib/login-arrange.js";import{installHttpInterceptor as J}from"./http-interceptor.js";import{installResizableSlideovers as Y}from"./lib/slideover-resizable.js";import{installActivityThrobber as Q}from"./lib/activity-throbber.js";import{isPathAllowed as X,redirectFor as Z}from"./lib/admin-scope.js";import{deliverDesktop as tt,desktopActive as et,notificationSettings as it}from"./lib/desktop-notify.js";import{avatarUrl as ot}from"./lib/avatar.js";$(()=>{J(),Q(),Y(),(async()=>{const{applyAdminTheme:t,refreshAdminTheme:e}=await import("./lib/admin-theme.js");u()?await e(r,{init:!0}):t({dommaTheme:"charcoal-dark"},{init:!0})})();let c=null,l="/",h=!1,w=null;function D(t){if(y[t])return E.toast(`${y[t]} is switched off on this site.`,{type:"info"}),"/";const e=b[t]||Z(c,t);return e||((t.split("?")[0]||"/")==="/"&&l&&l!=="/"?b[l]||l:null)}const b={},y={};R.use(async(t,e,i)=>{if(C(t.path))return i();if(!u()){z();return}const o=D(t.path);if(o){R.navigate(o);return}i()});async function g(){try{const t=await r.get("/auth/permissions");c=t.adminScope||null,l=t.adminHome||"/";const e=t.permissions||[];return h=X(c,"/settings")&&e.some(i=>i==="settings"||i.startsWith("settings.")),w=e.some(i=>i==="notifications"||i.startsWith("notifications.")),e}catch{return c=null,l="/",h=!1,w=null,[]}}document.addEventListener("dm:admin-home-changed",()=>{g()});let P=null,N=!1;document.addEventListener("dm:sidebar-rebuild",()=>{if(P){N=!0;return}P=(async()=>{do{N=!1;try{await j(await g())}catch{}}while(N)})().finally(()=>{P=null})});async function j(t){const{renderAdminSidebar:e}=await import("./lib/sidebar-renderer.js");await e({mount:"#admin-sidebar",permissions:t,adminScope:c})}M.subscribe("router:afterChange",({to:t})=>{C(t.path)&&(d&&(clearInterval(d),d=null),c=null,l="/",h=!1,w=null)});const F=[{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/usage/editions",view:"usageEditions",title:"Editions & Licences - Usage - Domma CMS"},{path:"/docs/usage/collections",view:"usageCollections",title:"Collections & Forms - Usage - Domma CMS"},{path:"/docs/usage/tools",view:"usageTools",title:"Built-in Tools - Usage - Domma CMS"},{path:"/docs/plugins",view:"pluginGuides",title:"Plugin Guides - Domma CMS"},{path:"/docs/plugins/*",view:"pluginGuide",title:"Plugin Guide - Domma CMS"},{path:"/docs/api/authentication",view:"apiRefAuthentication",title:"Authentication - API - Domma CMS"},{path:"/docs/api/pages",view:"apiRefPages",title:"Pages - API - Domma CMS"},{path:"/docs/api/settings",view:"apiRefSettings",title:"Settings - API - Domma CMS"},{path:"/docs/api/layouts",view:"apiRefLayouts",title:"Layouts - API - Domma CMS"},{path:"/docs/api/navigation",view:"apiRefNavigation",title:"Navigation - API - Domma CMS"},{path:"/docs/api/media",view:"apiRefMedia",title:"Media - API - Domma CMS"},{path:"/docs/api/users",view:"apiRefUsers",title:"Users - API - Domma CMS"},{path:"/docs/api/plugins",view:"apiRefPlugins",title:"Plugins - API - Domma CMS"},{path:"/docs/api/collections",view:"apiRefCollections",title:"Collections - API - Domma CMS"},{path:"/docs/api/views",view:"apiRefViews",title:"Views API - Domma CMS"},{path:"/docs/api/actions",view:"apiRefActions",title:"Actions API - Domma CMS"},{path:"/docs/api/forms",view:"apiRefForms",title:"Forms API - Domma CMS"},{path:"/docs/api/scaffold",view:"apiRefScaffold",title:"Scaffold API - Domma CMS"},{path:"/docs/api/external",view:"apiRefExternal",title:"External API & Tokens - Domma CMS"},{path:"/docs/api/builder",view:"apiRefBuilder",title:"API Builder - Domma CMS"},{path:"/api-reference",view:"apiReference",title:"API Reference - Domma CMS"},{path:"/collections",view:"collections",title:"Collections - Domma CMS"},{path:"/collections/new",view:"collectionEditor",title:"New Collection - Domma CMS"},{path:"/collections/edit/:slug",view:"collectionEditor",title:"Edit Collection - Domma CMS"},{path:"/collections/:slug/entries",view:"collectionEntries",title:"Entries - Domma CMS"},{path:"/forms",view:"forms",title:"Forms - Domma CMS"},{path:"/forms/new",view:"formEditor",title:"New Form - Domma CMS"},{path:"/forms/edit/:slug",view:"formEditor",title:"Edit Form - Domma CMS"},{path:"/forms/:slug/submissions",view:"formSubmissions",title:"Submissions - Domma CMS"},{path:"/views",view:"viewsList",title:"Views - Domma CMS"},{path:"/views/new",view:"viewEditor",title:"New View - Domma CMS"},{path:"/views/edit/:slug",view:"viewEditor",title:"Edit View - Domma CMS"},{path:"/views/:slug/preview",view:"viewPreview",title:"View Preview - Domma CMS"},{path:"/actions",view:"actionsList",title:"Actions - Domma CMS"},{path:"/actions/new",view:"actionEditor",title:"New Action - Domma CMS"},{path:"/actions/edit/:slug",view:"actionEditor",title:"Edit Action - Domma CMS"},{path:"/pro/docs",view:"proDocs",title:"Pro Documentation - Domma CMS"},{path:"/blocks",view:"blocks",title:"Blocks - Domma CMS"},{path:"/blocks/new",view:"blockEditor",title:"New Block - Domma CMS"},{path:"/blocks/edit/:name",view:"blockEditor",title:"Edit Block - Domma CMS"},{path:"/components",view:"components",title:"Components - Domma CMS"},{path:"/components/new",view:"componentEditor",title:"New Component - Domma CMS"},{path:"/components/edit/:name",view:"componentEditor",title:"Edit Component - Domma CMS"},{path:"/my-profile",view:"myProfile",title:"My Profile - Domma CMS"},{path:"/roles",view:"roles",title:"Roles & Permissions - Domma CMS"},{path:"/roles/edit/:id",view:"roleEditor",title:"Edit Role - Domma CMS"},{path:"/effects",view:"effects",title:"Effects - Domma CMS"},{path:"/search",view:"search",title:"Search - Domma CMS"},{path:"/notes",view:"notes",title:"Notes - Domma CMS"},{path:"/todo",view:"todo",title:"Todo - Domma CMS"},{path:"/analytics",view:"analytics",title:"Analytics - Domma CMS"},{path:"/seo",view:"seo",title:"SEO - Domma CMS"},{path:"/seo/:tab",view:"seo",title:"SEO - Domma CMS"},{path:"/contacts",view:"contacts",title:"Contacts - Domma CMS"},{path:"/calls/new",view:"callNew",title:"Call now - Domma CMS"},{path:"/system/notifications",view:"notifications",title:"Notifications - Domma CMS"},{path:"/api-tokens",view:"apiTokens",title:"API Tokens - Domma CMS"},{path:"/api-endpoints",view:"apiEndpoints",title:"API Builder - Domma CMS"},{path:"/api-endpoints/new",view:"apiEndpointEditor",title:"New API Endpoint - Domma CMS"},{path:"/api-endpoints/edit/:id",view:"apiEndpointEditor",title:"Edit API Endpoint - Domma CMS"},{path:"/projects",view:"projects",title:"Projects - Domma CMS"},{path:"/projects/new",view:"projects",title:"New Project - Domma CMS"},{path:"/projects/edit/:slug",view:"projects",title:"Edit Project - Domma CMS"},{path:"/projects/:slug/settings",view:"projectSettings",title:"Project settings - Domma CMS"},{path:"/projects/:slug/pages",view:"pages",title:"Project pages - Domma CMS"},{path:"/projects/:slug/collections",view:"collections",title:"Project collections - Domma CMS"},{path:"/projects/:slug/forms",view:"forms",title:"Project forms - Domma CMS"},{path:"/projects/:slug/actions",view:"actionsList",title:"Project actions - Domma CMS"},{path:"/projects/:slug/menus",view:"menus",title:"Project menus - Domma CMS"},{path:"/projects/:slug/blocks",view:"blocks",title:"Project blocks - Domma CMS"},{path:"/projects/:slug/components",view:"components",title:"Project components - Domma CMS"},{path:"/projects/:slug/views",view:"viewsList",title:"Project views - Domma CMS"},{path:"/projects/:slug/roles",view:"roles",title:"Project roles - Domma CMS"},{path:"/projects/:slug/users",view:"users",title:"Project users - Domma CMS"},{path:"/projects/:slug/apis",view:"apiEndpoints",title:"Project APIs - Domma CMS"},{path:"/projects/:slug",view:"projectDetail",title:"Project - Domma CMS"},{path:"/login",view:"login",title:"Sign in - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}},{path:"/reset-password",view:"login",title:"Reset Password - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}}];M.subscribe("router:afterChange",async({to:t,from:e})=>{if(!C(t.path)){if($("#admin-sidebar").show(),$("#admin-topbar").show(),C(e?.path)){$("#topbar-user-name").remove(),import("./lib/admin-theme.js").then(a=>a.refreshAdminTheme(r)),await x();const i=await g(),o=D(t.path);if(o&&R.navigate(o),j(i),it.read().signInToasts)try{const a=await r.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=p(s.data?.title||""),k=p((s.data?.body||"").slice(0,120));E.toast(`${f} - ${k}`,{type:s.data?.severity==="critical"?"error":"warning",duration:0})}}catch{}}T()}}),M.subscribe("router:afterChange",()=>{setTimeout(()=>{$(".btn-primary, .btn-danger").length&&Domma.effects.reveal(".btn-primary, .btn-danger",{animation:"fade",stagger:40,duration:300})},50)});const V=["#field-project","#menu-project","#collection-project","#endpoint-project","#action-project","#view-project","#block-project","#component-project","#role-project",'[name="project"]','[name="ownedByProject"]'];function W(t){return/^\/(pages|menus|context-menus|collections|forms|actions|views|blocks|components|users|roles|api-endpoints)\/(new|edit\/)/.test(t)}M.subscribe("router:afterChange",({to:t})=>{const e=t?String(t.path).split("?")[0]:"";if(!t||!W(e))return;const o=new URLSearchParams(location.hash.split("?")[1]||"").get("project");if(!o||!/\/new$/.test(e))return;let a=0;const n=60,s=1500,f=()=>{a+=n;for(const k of V){const v=document.querySelector(k);if(!v)continue;if(Array.from(v.options||[]).find(H=>H.value===o)){v.value=o,v.dispatchEvent(new Event("change",{bubbles:!0}));return}}a<s&&setTimeout(f,n)};setTimeout(f,n)});const I="cms_card_states",A=S.get(I)||{};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&&A[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 i=$(this).find("h2, h3").first().text().trim();i&&(A[i]=e.hasClass("card-collapsed")?"collapsed":"open",S.set(I,A))}),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash.startsWith("#/login")||window.location.hash.startsWith("#/reset-password"))return;const e=document.querySelector("#view-container .view-header button.btn-primary");e&&(t.preventDefault(),e.click())});const m={...q},_={notes:{displayName:"Notes",icon:"edit-3"},todo:{displayName:"Todo",icon:"check-square"},analytics:{displayName:"Analytics",icon:"chart-bar"},seo:{displayName:"SEO",icon:"search"},contacts:{displayName:"Contacts",icon:"users"},projects:{displayName:"Projects",icon:"folder"},pages:{displayName:"Pages",icon:"file-text"},media:{displayName:"Media",icon:"image"},menus:{displayName:"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"},dashboard:{displayName:"Dashboard",icon:"home"},pageEditor:{displayName:"Pages",section:"Edit",icon:"file-text"},menuEditor:{displayName:"Menus",section:"Edit",icon:"menu"},menuLocations:{displayName:"Menus",section:"Locations",icon:"map-pin"},contextMenuEditor:{displayName:"Context Menus",section:"Edit",icon:"menu"},collectionEditor:{displayName:"Collections",section:"Edit",icon:"database"},collectionEntries:{displayName:"Collections",section:"Entries",icon:"database"},formEditor:{displayName:"Forms",section:"Edit",icon:"layout"},formSubmissions:{displayName:"Forms",section:"Submissions",icon:"inbox"},viewEditor:{displayName:"Views",section:"Edit",icon:"eye"},viewPreview:{displayName:"Views",section:"Preview",icon:"eye"},actionEditor:{displayName:"Actions",section:"Edit",icon:"zap"},apiEndpointEditor:{displayName:"API Builder",section:"Edit",icon:"code"},blockEditor:{displayName:"Blocks",section:"Edit",icon:"box"},componentEditor:{displayName:"Components",section:"Edit",icon:"component"},roleEditor:{displayName:"Roles",section:"Edit",icon:"shield"},userEditor:{displayName:"Users",section:"Edit",icon:"users"},projectDetail:{displayName:"Projects",section:"Overview",icon:"folder"},projectSettings:{displayName:"Projects",section:"Settings",icon:"folder"},pluginCode:{displayName:"Marketplace",section:"Code",icon:"code"}};for(const[t,e]of Object.entries(_))m[t]&&(m[t]=B(m[t],{plugin:t,licence:"Built in",...e}));const U=[...F];async function x(){if(u())try{const t=await r.plugins.adminConfig();t.routes?.length&&U.push(...t.routes);for(const[e,i]of Object.entries(t.views||{}))try{const o=await import(`/plugins/${i.entry}`);m[e]=B(o[i.exportName],(t.meta||{})[e])}catch{}for(const[e,{route:i}]of Object.entries(t.takeovers||{}))i&&(b[`/${e}`]=i);for(const[e,{enabled:i,displayName:o}]of Object.entries(t.tools||{}))i===!1&&(y[`/${e}`]=o);for(const{id:e,href:i}of t.css||[])if(!document.getElementById(e)){const o=document.createElement("link");o.id=e,o.rel="stylesheet",o.href=i,document.head.appendChild(o)}}catch{}}function T(){const t=O();if(!t||$("#topbar-user-name").length)return;const i={"super-admin":"Super Admin",admin:"Admin",user:"User"}[t.role]||t.role,o=ot(t);$("#topbar-user").html(`
|
|
1
|
+
import{views as q}from"./views/index.js";import{withPluginChrome as B}from"./lib/plugin-chrome.js";import{api as r,getUser as O,isAuthenticated as u,logout as G,sendToLogin as z}from"./api.js";import{isAuthScreen as C,loginHashFor as K}from"./lib/login-arrange.js";import{installHttpInterceptor as J}from"./http-interceptor.js";import{installResizableSlideovers as Y}from"./lib/slideover-resizable.js";import{installActivityThrobber as Q}from"./lib/activity-throbber.js";import{isPathAllowed as X,redirectFor as Z}from"./lib/admin-scope.js";import{deliverDesktop as tt,desktopActive as et,notificationSettings as it}from"./lib/desktop-notify.js";import{avatarUrl as ot}from"./lib/avatar.js";import{installCalculator as at}from"./lib/calculator.js";$(()=>{J(),at(),Q(),Y(),(async()=>{const{applyAdminTheme:t,refreshAdminTheme:e}=await import("./lib/admin-theme.js");u()?await e(r,{init:!0}):t({dommaTheme:"charcoal-dark"},{init:!0})})();let c=null,l="/",h=!1,w=null;function D(t){if(y[t])return E.toast(`${y[t]} is switched off on this site.`,{type:"info"}),"/";const e=b[t]||Z(c,t);return e||((t.split("?")[0]||"/")==="/"&&l&&l!=="/"?b[l]||l:null)}const b={},y={};R.use(async(t,e,i)=>{if(C(t.path))return i();if(!u()){z();return}const o=D(t.path);if(o){R.navigate(o);return}i()});async function g(){try{const t=await r.get("/auth/permissions");c=t.adminScope||null,l=t.adminHome||"/";const e=t.permissions||[];return h=X(c,"/settings")&&e.some(i=>i==="settings"||i.startsWith("settings.")),w=e.some(i=>i==="notifications"||i.startsWith("notifications.")),e}catch{return c=null,l="/",h=!1,w=null,[]}}document.addEventListener("dm:admin-home-changed",()=>{g()});let P=null,N=!1;document.addEventListener("dm:sidebar-rebuild",()=>{if(P){N=!0;return}P=(async()=>{do{N=!1;try{await j(await g())}catch{}}while(N)})().finally(()=>{P=null})});async function j(t){const{renderAdminSidebar:e}=await import("./lib/sidebar-renderer.js");await e({mount:"#admin-sidebar",permissions:t,adminScope:c})}M.subscribe("router:afterChange",({to:t})=>{C(t.path)&&(d&&(clearInterval(d),d=null),c=null,l="/",h=!1,w=null)});const F=[{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/usage/editions",view:"usageEditions",title:"Editions & Licences - Usage - Domma CMS"},{path:"/docs/usage/collections",view:"usageCollections",title:"Collections & Forms - Usage - Domma CMS"},{path:"/docs/usage/tools",view:"usageTools",title:"Built-in Tools - Usage - Domma CMS"},{path:"/docs/plugins",view:"pluginGuides",title:"Plugin Guides - Domma CMS"},{path:"/docs/plugins/*",view:"pluginGuide",title:"Plugin Guide - Domma CMS"},{path:"/docs/api/authentication",view:"apiRefAuthentication",title:"Authentication - API - Domma CMS"},{path:"/docs/api/pages",view:"apiRefPages",title:"Pages - API - Domma CMS"},{path:"/docs/api/settings",view:"apiRefSettings",title:"Settings - API - Domma CMS"},{path:"/docs/api/layouts",view:"apiRefLayouts",title:"Layouts - API - Domma CMS"},{path:"/docs/api/navigation",view:"apiRefNavigation",title:"Navigation - API - Domma CMS"},{path:"/docs/api/media",view:"apiRefMedia",title:"Media - API - Domma CMS"},{path:"/docs/api/users",view:"apiRefUsers",title:"Users - API - Domma CMS"},{path:"/docs/api/plugins",view:"apiRefPlugins",title:"Plugins - API - Domma CMS"},{path:"/docs/api/collections",view:"apiRefCollections",title:"Collections - API - Domma CMS"},{path:"/docs/api/views",view:"apiRefViews",title:"Views API - Domma CMS"},{path:"/docs/api/actions",view:"apiRefActions",title:"Actions API - Domma CMS"},{path:"/docs/api/forms",view:"apiRefForms",title:"Forms API - Domma CMS"},{path:"/docs/api/scaffold",view:"apiRefScaffold",title:"Scaffold API - Domma CMS"},{path:"/docs/api/external",view:"apiRefExternal",title:"External API & Tokens - Domma CMS"},{path:"/docs/api/builder",view:"apiRefBuilder",title:"API Builder - Domma CMS"},{path:"/api-reference",view:"apiReference",title:"API Reference - Domma CMS"},{path:"/collections",view:"collections",title:"Collections - Domma CMS"},{path:"/collections/new",view:"collectionEditor",title:"New Collection - Domma CMS"},{path:"/collections/edit/:slug",view:"collectionEditor",title:"Edit Collection - Domma CMS"},{path:"/collections/:slug/entries",view:"collectionEntries",title:"Entries - Domma CMS"},{path:"/forms",view:"forms",title:"Forms - Domma CMS"},{path:"/forms/new",view:"formEditor",title:"New Form - Domma CMS"},{path:"/forms/edit/:slug",view:"formEditor",title:"Edit Form - Domma CMS"},{path:"/forms/:slug/submissions",view:"formSubmissions",title:"Submissions - Domma CMS"},{path:"/views",view:"viewsList",title:"Views - Domma CMS"},{path:"/views/new",view:"viewEditor",title:"New View - Domma CMS"},{path:"/views/edit/:slug",view:"viewEditor",title:"Edit View - Domma CMS"},{path:"/views/:slug/preview",view:"viewPreview",title:"View Preview - Domma CMS"},{path:"/actions",view:"actionsList",title:"Actions - Domma CMS"},{path:"/actions/new",view:"actionEditor",title:"New Action - Domma CMS"},{path:"/actions/edit/:slug",view:"actionEditor",title:"Edit Action - Domma CMS"},{path:"/pro/docs",view:"proDocs",title:"Pro Documentation - Domma CMS"},{path:"/blocks",view:"blocks",title:"Blocks - Domma CMS"},{path:"/blocks/new",view:"blockEditor",title:"New Block - Domma CMS"},{path:"/blocks/edit/:name",view:"blockEditor",title:"Edit Block - Domma CMS"},{path:"/components",view:"components",title:"Components - Domma CMS"},{path:"/components/new",view:"componentEditor",title:"New Component - Domma CMS"},{path:"/components/edit/:name",view:"componentEditor",title:"Edit Component - Domma CMS"},{path:"/my-profile",view:"myProfile",title:"My Profile - Domma CMS"},{path:"/roles",view:"roles",title:"Roles & Permissions - Domma CMS"},{path:"/roles/edit/:id",view:"roleEditor",title:"Edit Role - Domma CMS"},{path:"/effects",view:"effects",title:"Effects - Domma CMS"},{path:"/search",view:"search",title:"Search - Domma CMS"},{path:"/notes",view:"notes",title:"Notes - Domma CMS"},{path:"/todo",view:"todo",title:"Todo - Domma CMS"},{path:"/calculator",view:"calculator",title:"Calculator - Domma CMS"},{path:"/analytics",view:"analytics",title:"Analytics - Domma CMS"},{path:"/seo",view:"seo",title:"SEO - Domma CMS"},{path:"/seo/:tab",view:"seo",title:"SEO - Domma CMS"},{path:"/contacts",view:"contacts",title:"Contacts - Domma CMS"},{path:"/calls/new",view:"callNew",title:"Call now - Domma CMS"},{path:"/system/notifications",view:"notifications",title:"Notifications - Domma CMS"},{path:"/api-tokens",view:"apiTokens",title:"API Tokens - Domma CMS"},{path:"/api-endpoints",view:"apiEndpoints",title:"API Builder - Domma CMS"},{path:"/api-endpoints/new",view:"apiEndpointEditor",title:"New API Endpoint - Domma CMS"},{path:"/api-endpoints/edit/:id",view:"apiEndpointEditor",title:"Edit API Endpoint - Domma CMS"},{path:"/projects",view:"projects",title:"Projects - Domma CMS"},{path:"/projects/new",view:"projects",title:"New Project - Domma CMS"},{path:"/projects/edit/:slug",view:"projects",title:"Edit Project - Domma CMS"},{path:"/projects/:slug/settings",view:"projectSettings",title:"Project settings - Domma CMS"},{path:"/projects/:slug/pages",view:"pages",title:"Project pages - Domma CMS"},{path:"/projects/:slug/collections",view:"collections",title:"Project collections - Domma CMS"},{path:"/projects/:slug/forms",view:"forms",title:"Project forms - Domma CMS"},{path:"/projects/:slug/actions",view:"actionsList",title:"Project actions - Domma CMS"},{path:"/projects/:slug/menus",view:"menus",title:"Project menus - Domma CMS"},{path:"/projects/:slug/blocks",view:"blocks",title:"Project blocks - Domma CMS"},{path:"/projects/:slug/components",view:"components",title:"Project components - Domma CMS"},{path:"/projects/:slug/views",view:"viewsList",title:"Project views - Domma CMS"},{path:"/projects/:slug/roles",view:"roles",title:"Project roles - Domma CMS"},{path:"/projects/:slug/users",view:"users",title:"Project users - Domma CMS"},{path:"/projects/:slug/apis",view:"apiEndpoints",title:"Project APIs - Domma CMS"},{path:"/projects/:slug",view:"projectDetail",title:"Project - Domma CMS"},{path:"/login",view:"login",title:"Sign in - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}},{path:"/reset-password",view:"login",title:"Reset Password - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}}];M.subscribe("router:afterChange",async({to:t,from:e})=>{if(!C(t.path)){if($("#admin-sidebar").show(),$("#admin-topbar").show(),C(e?.path)){$("#topbar-user-name").remove(),import("./lib/admin-theme.js").then(a=>a.refreshAdminTheme(r)),await x();const i=await g(),o=D(t.path);if(o&&R.navigate(o),j(i),it.read().signInToasts)try{const a=await r.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=p(s.data?.title||""),k=p((s.data?.body||"").slice(0,120));E.toast(`${f} - ${k}`,{type:s.data?.severity==="critical"?"error":"warning",duration:0})}}catch{}}T()}}),M.subscribe("router:afterChange",()=>{setTimeout(()=>{$(".btn-primary, .btn-danger").length&&Domma.effects.reveal(".btn-primary, .btn-danger",{animation:"fade",stagger:40,duration:300})},50)});const V=["#field-project","#menu-project","#collection-project","#endpoint-project","#action-project","#view-project","#block-project","#component-project","#role-project",'[name="project"]','[name="ownedByProject"]'];function W(t){return/^\/(pages|menus|context-menus|collections|forms|actions|views|blocks|components|users|roles|api-endpoints)\/(new|edit\/)/.test(t)}M.subscribe("router:afterChange",({to:t})=>{const e=t?String(t.path).split("?")[0]:"";if(!t||!W(e))return;const o=new URLSearchParams(location.hash.split("?")[1]||"").get("project");if(!o||!/\/new$/.test(e))return;let a=0;const n=60,s=1500,f=()=>{a+=n;for(const k of V){const v=document.querySelector(k);if(!v)continue;if(Array.from(v.options||[]).find(H=>H.value===o)){v.value=o,v.dispatchEvent(new Event("change",{bubbles:!0}));return}}a<s&&setTimeout(f,n)};setTimeout(f,n)});const I="cms_card_states",A=S.get(I)||{};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&&A[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 i=$(this).find("h2, h3").first().text().trim();i&&(A[i]=e.hasClass("card-collapsed")?"collapsed":"open",S.set(I,A))}),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash.startsWith("#/login")||window.location.hash.startsWith("#/reset-password"))return;const e=document.querySelector("#view-container .view-header button.btn-primary");e&&(t.preventDefault(),e.click())});const m={...q},_={notes:{displayName:"Notes",icon:"edit-3"},todo:{displayName:"Todo",icon:"check-square"},calculator:{displayName:"Calculator",icon:"calculator"},analytics:{displayName:"Analytics",icon:"chart-bar"},seo:{displayName:"SEO",icon:"search"},contacts:{displayName:"Contacts",icon:"users"},projects:{displayName:"Projects",icon:"folder"},pages:{displayName:"Pages",icon:"file-text"},media:{displayName:"Media",icon:"image"},menus:{displayName:"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"},dashboard:{displayName:"Dashboard",icon:"home"},pageEditor:{displayName:"Pages",section:"Edit",icon:"file-text"},menuEditor:{displayName:"Menus",section:"Edit",icon:"menu"},menuLocations:{displayName:"Menus",section:"Locations",icon:"map-pin"},contextMenuEditor:{displayName:"Context Menus",section:"Edit",icon:"menu"},collectionEditor:{displayName:"Collections",section:"Edit",icon:"database"},collectionEntries:{displayName:"Collections",section:"Entries",icon:"database"},formEditor:{displayName:"Forms",section:"Edit",icon:"layout"},formSubmissions:{displayName:"Forms",section:"Submissions",icon:"inbox"},viewEditor:{displayName:"Views",section:"Edit",icon:"eye"},viewPreview:{displayName:"Views",section:"Preview",icon:"eye"},actionEditor:{displayName:"Actions",section:"Edit",icon:"zap"},apiEndpointEditor:{displayName:"API Builder",section:"Edit",icon:"code"},blockEditor:{displayName:"Blocks",section:"Edit",icon:"box"},componentEditor:{displayName:"Components",section:"Edit",icon:"component"},roleEditor:{displayName:"Roles",section:"Edit",icon:"shield"},userEditor:{displayName:"Users",section:"Edit",icon:"users"},projectDetail:{displayName:"Projects",section:"Overview",icon:"folder"},projectSettings:{displayName:"Projects",section:"Settings",icon:"folder"},pluginCode:{displayName:"Marketplace",section:"Code",icon:"code"}};for(const[t,e]of Object.entries(_))m[t]&&(m[t]=B(m[t],{plugin:t,licence:"Built in",...e}));const U=[...F];async function x(){if(u())try{const t=await r.plugins.adminConfig();t.routes?.length&&U.push(...t.routes);for(const[e,i]of Object.entries(t.views||{}))try{const o=await import(`/plugins/${i.entry}`);m[e]=B(o[i.exportName],(t.meta||{})[e])}catch{}for(const[e,{route:i}]of Object.entries(t.takeovers||{}))i&&(b[`/${e}`]=i);for(const[e,{enabled:i,displayName:o}]of Object.entries(t.tools||{}))i===!1&&(y[`/${e}`]=o);for(const{id:e,href:i}of t.css||[])if(!document.getElementById(e)){const o=document.createElement("link");o.id=e,o.rel="stylesheet",o.href=i,document.head.appendChild(o)}}catch{}}function T(){const t=O();if(!t||$("#topbar-user-name").length)return;const i={"super-admin":"Super Admin",admin:"Admin",user:"User"}[t.role]||t.role,o=ot(t);$("#topbar-user").html(`
|
|
2
2
|
${o?`<img id="topbar-avatar" class="topbar-avatar" src="${p(o)}" alt="">`:""}
|
|
3
3
|
<span id="topbar-user-name" class="topbar-user-name">${p(t.name)}</span>
|
|
4
4
|
<span class="topbar-role-badge topbar-role-badge--${p(t.role)}">${i}</span>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function calculate(o,g="deg"){const h=String(o).replace(/×/g,"*").replace(/[÷]/g,"/").replace(/[−]/g,"-").replace(/π/g,"pi");if(!h.trim()||h.length>256)throw new Error("Enter a calculation (up to 256 characters).");const i=h.match(/(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?|[a-z]+|[+*/^()%!,-]/gi)||[];if(i.join("")!==h.replace(/\s/g,""))throw new Error("Use numbers and calculator operations only.");let n=0,d=0;const b=e=>{if(!Number.isFinite(e))throw new Error("Result is outside the supported range.");return e},f=e=>g==="deg"?e*Math.PI/180:e,p=e=>g==="deg"?e*180/Math.PI:e,w={sin:e=>Math.sin(f(e)),cos:e=>Math.cos(f(e)),tan:e=>{if(Math.abs(Math.cos(f(e)))<1e-14)throw new Error("Tangent is undefined at this angle.");return Math.tan(f(e))},asin:e=>p(Math.asin(e)),acos:e=>p(Math.acos(e)),atan:e=>p(Math.atan(e)),sqrt:Math.sqrt,abs:Math.abs,ln:Math.log,log:Math.log10,exp:Math.exp},M=e=>{if(!Number.isInteger(e)||e<0||e>170)throw new Error("Factorial needs a whole number from 0 to 170.");let r=1;for(let t=2;t<=e;t++)r*=t;return r};function l(e=0){if(++d>32)throw new Error("Calculation is too deeply nested.");let r=i[n++],t;if(r==="+"||r==="-")t=(r==="-"?-1:1)*l(25);else if(r==="("){if(t=l(),i[n++]!==")")throw new Error("Close the parentheses.")}else if(/^(?:\d|\.)/.test(r||""))t=Number(r);else if(r?.toLowerCase()==="pi")t=Math.PI;else if(r?.toLowerCase()==="e")t=Math.E;else if(Object.hasOwn(w,r?.toLowerCase())){if(i[n++]!=="(")throw new Error("Put the function value in parentheses.");if(t=w[r.toLowerCase()](l()),i[n++]!==")")throw new Error("Close the parentheses.")}else throw new Error("Enter a number or a supported function.");for(t=b(t);n<i.length;){const a=i[n];if(a==="%"||a==="!"){if(40<e)break;n++,t=a==="%"?t/100:M(t);continue}const m=a==="("||/^[a-z]/i.test(a),s=m?"*":a,u={"+":10,"-":10,"*":20,"/":20,"^":30}[s];if(u===void 0||u<e)break;m||n++;const c=l(s==="^"?u:u+1);if(s==="/"&&c===0)throw new Error("Cannot divide by zero.");t=b(s==="+"?t+c:s==="-"?t-c:s==="*"?t*c:s==="/"?t/c:t**c)}return d--,t}const E=l();if(n!==i.length)throw new Error("Check the calculation.");return Number(E.toPrecision(14))}export const POSITIONS=["top-left","top-center","top-right","center-left","center","center-right","bottom-left","bottom-center","bottom-right"];export function calculatorSettings(o){return{accent:["teal","blue","violet","amber"].includes(o?.accent)?o.accent:"teal",layout:o?.layout==="scientific"?"scientific":"basic",angle:o?.angle==="rad"?"rad":"deg",position:POSITIONS.includes(o?.position)?o.position:"bottom-right",offset:o?.offset&&Number.isFinite(o.offset.x)&&Number.isFinite(o.offset.y)?{x:Math.max(0,o.offset.x),y:Math.max(0,o.offset.y)}:null}}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import{api as q,isAuthenticated as D}from"../api.js";import{settingsStore as W,onceClosed as z}from"./tool-kit.js";import{addBannerAction as y,buildBanner as H}from"./plugin-chrome.js";import{calculate as B,calculatorSettings as $,POSITIONS as P}from"./calculator-engine.js";const N=W("calculator.settings",$);let g;function k(){if(g)return g;const e=M.observable(N.read()),t=M.observable(""),a=M.observable("0"),n=M.observable(""),o=M.observable(""),c=M.observable(null),u=M.computed(()=>{if(!t.value)return a.value;try{return String(B(t.value,e.value.angle))}catch{return a.value}}),r=M.effect(()=>{N.write(e.value),requestAnimationFrame(S)});return g={settings:e,expression:t,result:a,error:n,history:o,memory:c,display:u,persist:r,finished:!1,panelClass:M.computed(()=>`${e.value.layout==="scientific"?"is-scientific":""} accent-${e.value.accent}`),basic:M.computed(()=>e.value.layout==="basic"),scientific:M.computed(()=>e.value.layout==="scientific"),memoryEmpty:M.computed(()=>c.value===null),memoryLabel:M.computed(()=>c.value===null?"Memory empty":`M ${c.value}`),angleLabel:M.computed(()=>e.value.angle==="deg"?"DEG":"RAD")},g}function h(e,t){const a=k();a.settings.value=$({...a.settings.value,[e]:t,...e==="position"?{offset:null}:{}})}let d=null,v=null,x=null;function j(e,t){x?.close();const a=document.createElement("dialog");a.className="calculator-dialog",a.setAttribute("aria-label",e),a.innerHTML=`<h2>${e}</h2>${t}`;const n=document.activeElement;return a.addEventListener("close",()=>{a.remove(),x===a&&(x=null),n?.isConnected&&n.focus()}),a.addEventListener("click",o=>{o.target===a&&a.close()}),document.body.append(a),x=a,a.showModal(),a}function S(){if(!d)return;const e=k().settings.value,t=d.getBoundingClientRect(),a=Math.max(12,window.innerWidth-t.width-12),n=Math.max(12,window.innerHeight-t.height-12),o=e.position,c=e.offset?.x??(o.endsWith("left")?12:o.endsWith("right")?a:(window.innerWidth-t.width)/2),u=e.offset?.y??(o.startsWith("top")?12:o.startsWith("bottom")?n:(window.innerHeight-t.height)/2);d.style.left=`${Math.max(12,Math.min(a,c))}px`,d.style.top=`${Math.max(12,Math.min(n,u))}px`}function F(){const e=k(),t=E.slideover({title:"Calculator settings",size:"sm",position:"right"}),a=document.createElement("div");a.className="calculator-settings";const n=u=>u.map(r=>`<option value="${r}">${r.replace(/-/g," ").replace(/^./,p=>p.toUpperCase())}</option>`).join("");a.innerHTML=`<p class="text-muted">Saved in this browser as you change them.</p>
|
|
2
|
+
<label>Calculator layout<select class="form-input" name="layout" data-model="layout.value">${n(["basic","scientific"])}</select></label>
|
|
3
|
+
<label>Angle units<select class="form-input" name="angle" data-model="angle.value"><option value="deg">Degrees</option><option value="rad">Radians</option></select></label>
|
|
4
|
+
<label>Accent colour<select class="form-input" name="accent" data-model="accent.value">${n(["teal","blue","violet","amber"])}</select></label>
|
|
5
|
+
<label>Floating position<select class="form-input" name="position" data-model="position.value">${n(P)}</select></label>
|
|
6
|
+
<p class="text-muted">You can also drag the floating calculator by its card header.</p>
|
|
7
|
+
<div class="calculator-dialog-actions"><button class="btn btn-secondary" data-on-click="reset">Reset defaults</button><button class="btn btn-primary" data-on-click="done">Done</button></div>`,t.element.append(a);const o=Object.fromEntries(["layout","angle","accent","position"].map(u=>[u,M.computed({read:()=>e.settings.value[u],write:r=>h(u,r)})])),c=M.applyBindings(o,a,{methods:{reset:()=>{e.settings.value=$(null)},done:()=>t.close()}});z(t,()=>c.dispose()),t.open()}function w(e){R._transitioning?M.once("router:afterChange",()=>{D()&&R.navigate(e)}):R.navigate(e)}function C(){d?.dispose(),d?.remove(),d=null}function K(){const e=k(),t=document.createElement("section");t.className="calculator-panel",t.setAttribute("aria-label","Calculator"),t.setAttribute("data-bind-class","panelClass.value");const a=[["sin(","sin"],["cos(","cos"],["tan(","tan"],["asin(","sin\u207B\xB9"],["acos(","cos\u207B\xB9"],["atan(","tan\u207B\xB9"],["sqrt(","\u221A"],["^2","x\xB2"],["^","x\u02B8"],["log(","log"],["ln(","ln"],["!","n!"],["pi","\u03C0"],["e","e"],["exp(","e\u02E3"],["(","("],[")",")"],["abs(","|x|"]],n=[["clear","AC"],["backspace","\u232B"],["%","%"],["/","\xF7"],["7","7"],["8","8"],["9","9"],["*","\xD7"],["4","4"],["5","5"],["6","6"],["-","\u2212"],["1","1"],["2","2"],["3","3"],["+","+"],["sign","\xB1"],["0","0"],[".","."],["equals","="]],o=(l,m=!1)=>l.map(([s,i])=>`<button type="button" class="calculator-key ${m?"key-science":s==="equals"?"key-equals":["clear","backspace"].includes(s)?"key-clear":["/","*","-","+","%"].includes(s)?"key-operator":""}" data-key="${s}"${s==="backspace"?' aria-label="Backspace"':s==="sign"?' aria-label="Change sign"':""}>${i}</button>`).join("");t.innerHTML=`<div class="calculator-toolbar"><div class="calculator-layout"><button type="button" data-layout="basic" data-bind-class="basic.value && 'is-active'" data-on-click="selectBasic">Basic</button><button type="button" data-layout="scientific" data-bind-class="scientific.value && 'is-active'" data-on-click="selectScientific">Scientific</button></div><select class="form-input calculator-angle" aria-label="Angle units" data-model="angle.value"><option value="deg">Degrees</option><option value="rad">Radians</option></select></div>
|
|
8
|
+
<div class="calculator-screen"><div class="calculator-screen-meta"><span data-bind-text="memoryLabel.value"></span><span data-bind-text="angleLabel.value"></span></div>
|
|
9
|
+
<div class="calculator-history" data-bind-text="history.value"></div>
|
|
10
|
+
<input class="calculator-expression" aria-label="Calculation" data-model="expression.value" autocomplete="off" spellcheck="false" maxlength="256" placeholder="Enter a calculation">
|
|
11
|
+
<output class="calculator-result" aria-live="polite" data-bind-text="display.value"></output><div class="calculator-error" role="alert" data-bind-text="error.value"></div></div>
|
|
12
|
+
<div class="calculator-memory">${[["MC","Clear memory"],["MR","Recall memory"],["MS","Store in memory"],["M+","Add to memory"],["M\u2212","Subtract from memory"]].map(([l,m])=>`<button type="button" data-memory="${l}" title="${m}" aria-label="${m}"${["MC","MR"].includes(l)?' data-bind-disabled="memoryEmpty.value"':""}>${l}</button>`).join("")}</div>
|
|
13
|
+
<div class="calculator-keypads"><div class="calculator-keys calculator-scientific">${o(a,!0)}</div><div class="calculator-keys">${o(n)}</div></div>
|
|
14
|
+
<div class="calculator-hint">Enter to calculate \xB7 Escape to clear</div>`;const c=M.computed({read:()=>e.settings.value.angle,write:l=>h("angle",l)}),u=M.applyBindings({...e,angle:c},t,{methods:{selectBasic:()=>h("layout","basic"),selectScientific:()=>h("layout","scientific")}}),r=t.querySelector("input"),p=new AbortController;t.dispose=()=>{u.dispose(),p.abort()};const f=()=>B(e.expression.value||e.result.value,e.settings.value.angle),b=()=>{try{const l=String(f());e.history.value=`${e.expression.value||e.result.value} = ${l}`,e.result.value=l,e.error.value="",e.finished=!0}catch(l){e.error.value=l.message,e.finished=!1}},L=()=>{e.expression.value="",e.result.value="0",e.error.value="",e.finished=!1};return r.addEventListener("input",()=>{e.error.value="",e.finished=!1},{signal:p.signal}),r.addEventListener("keydown",l=>{e.finished&&!l.ctrlKey&&!l.metaKey&&!l.altKey&&/^[0-9.a-z()+*/^%!-]$/i.test(l.key)&&(e.expression.value=/^[+*/^%!-]$/.test(l.key)?e.result.value:"",r.value=e.expression.value,e.finished=!1),(l.key==="Enter"||l.key==="=")&&(l.preventDefault(),b()),l.key==="Escape"&&(l.preventDefault(),L())},{signal:p.signal}),t.addEventListener("click",l=>{const m=l.target.closest("[data-key], [data-memory]");if(m){if(m.dataset.memory){const s=m.dataset.memory;try{if(s==="MC")e.memory.value=null;else if(s==="MR"&&e.memory.value!==null){const i=e.memory.value<0?`(${e.memory.value})`:String(e.memory.value);e.expression.value=!e.finished&&/[+*/^(-]$/.test(e.expression.value)?e.expression.value+i:i,e.finished=!1}else if(s!=="MR"){const i=f(),A=s==="MS"?i:(e.memory.value??0)+(s==="M\u2212"?-i:i);if(!Number.isFinite(A))throw new Error("Memory value is too large.");e.memory.value=Number(A.toPrecision(14))}e.error.value=""}catch(i){e.error.value=i.message}}else{const s=m.dataset.key;if(s==="equals")b();else if(s==="clear")L();else{let i=e.expression.value;s==="backspace"?i=i.slice(0,-1):s==="sign"?i=i?`-(${i})`:"-":(e.finished&&(i=/^[+*/^%!\-]/.test(s)?e.result.value:""),i.length+s.length<=256&&(i+=s)),e.expression.value=i,e.finished=!1,e.error.value=""}}r.focus(),r.setSelectionRange(r.value.length,r.value.length)}},{signal:p.signal}),t}function O(){if(!d){const e=document.createElement("section");e.className="calculator-floating dm-plugin-shell",e.setAttribute("aria-label","Floating calculator"),e.append(H({plugin:"calculator",displayName:"Calculator",icon:"calculator"}));const t=document.createElement("div");t.className="dm-plugin-body";const a=K();t.append(a),e.append(t),d=e,document.body.append(e),y(e,{id:"calculator-settings",icon:"settings",title:"Calculator settings",onClick:F}),y(e,{id:"calculator-standard",icon:"maximize",title:"Open on its own page",onClick:()=>{C(),w("/calculator")}}),y(e,{id:"calculator-close",icon:"x",title:"Close calculator",onClick:C});const n=new AbortController;e.dispose=()=>{n.abort(),a.dispose()};const o=e.querySelector(".dm-plugin-banner");o.addEventListener("pointerdown",c=>{if(c.target.closest("button")||c.button!==0)return;c.preventDefault();const u=e.getBoundingClientRect(),r={x:c.clientX,y:c.clientY};o.setPointerCapture(c.pointerId);const p=b=>h("offset",{x:u.left+b.clientX-r.x,y:u.top+b.clientY-r.y}),f=()=>o.removeEventListener("pointermove",p);o.addEventListener("pointermove",p,{signal:n.signal}),o.addEventListener("pointerup",f,{once:!0,signal:n.signal}),o.addEventListener("pointercancel",f,{once:!0,signal:n.signal})},{signal:n.signal}),window.addEventListener("resize",S,{signal:n.signal}),Domma.icons.scan(e)}S(),d.querySelector("input").focus()}export async function launchCalculator(){try{await q.get("/calculator/access")}catch{E.toast("Calculator is unavailable for this account.",{type:"warning"});return}const e=j("Open Calculator",`<p>Choose how you want to use Calculator.</p><div class="calculator-launch-options">
|
|
15
|
+
<button type="button" class="btn btn-primary" data-floating>Floating<small>Keep it alongside the page you are working on.</small></button>
|
|
16
|
+
<button type="button" class="btn btn-secondary" data-standard>Standard<small>Open Calculator on its own page.</small></button>
|
|
17
|
+
</div><div class="calculator-dialog-actions"><button type="button" class="btn btn-secondary" data-cancel>Cancel</button></div>`);e.querySelector("[data-floating]").onclick=()=>{e.close(),O(),/^#\/calculator(?:\?|$)/.test(location.hash)&&w("/")},e.querySelector("[data-standard]").onclick=()=>{e.close(),C(),w("/calculator")},e.querySelector("[data-cancel]").onclick=()=>e.close()}export function installCalculator(){document.addEventListener("click",e=>{!e.target.closest('a[href="#/calculator"]')||e.ctrlKey||e.metaKey||e.shiftKey||e.altKey||e.button!==0||(e.preventDefault(),e.stopImmediatePropagation(),launchCalculator())},!0),window.addEventListener("hashchange",()=>{(!D()||/^#\/(login|register|forgot|reset)/.test(location.hash))&&(C(),x?.close(),g?.persist?.(),g=null),/^#\/calculator(?:\?|$)/.test(location.hash)||(v?.dispose(),v=null)})}export const calculatorView={template:'<div class="calculator-page"></div>',async onMount(e){const t=e.get(0).querySelector(".calculator-page");try{await q.get("/calculator/access")}catch{t.textContent="Calculator is unavailable for this account.";return}!t.isConnected||!/^#\/calculator(?:\?|$)/.test(location.hash)||(C(),v?.dispose(),v=K(),t.append(v),y(e,{id:"calculator-settings",icon:"settings",title:"Calculator settings",onClick:F}),y(e,{id:"calculator-float",icon:"minimize",title:"Open floating calculator",onClick:()=>{O(),w("/")}}),v.querySelector("input").focus())}};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{api as h}from"../api.js";import{isPathAllowed as
|
|
1
|
+
import{api as h}from"../api.js";import{isPathAllowed as J}from"./admin-scope.js";import{colourToCss as U,applyFloat as Y,applyHighlight as Q,itemHighlightAttrs as ee}from"/public/js/menu-decor.mjs?v=20260918-item-highlight";import{placeTools as te,ensureLockedTools as ne,managePluginsItem as se,stripItemByUrl as ie,insertFoldersBeforeSystem as ae,pruneEmptyToolsFolder as oe,sidebarStateKey as re,canAny as B,filterByPermission as le,MANAGE_PLUGINS_URL as z}from"./sidebar-grouping.js";import{arrangeButtonMarkup as ce,canArrange as de,startArrange as ue}from"./sidebar-arrange.js";import{collapseButtonMarkup as me,wireSidebarSize as fe,COLLAPSED_WIDTH as ge}from"./sidebar-size.js";function p(e){return String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}const pe=[{text:"Dashboard",url:"#/",icon:"home"},{text:"Menus",url:"#/menus",icon:"menu",permission:"menus"}];async function he(e){if(!B(e,"projects"))return[];let s=[];try{s=await h.projects.listWithCounts()}catch{return[]}if(!Array.isArray(s))return[];const i=[{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(t=>{const d="#/projects/"+encodeURIComponent(t.slug),c=t.counts||{},o=[{text:"Overview",url:d,icon:t.icon||"folder"}];for(const r of i){const l=Number(c[r.key])||0;l<=0||o.push({text:`${r.text} (${l})`,url:d+r.path,icon:r.icon})}o.push({text:"Settings",url:d+"/settings",icon:"settings"});const m=t.enabled===!1;return{text:t.name||t.slug,key:`project.${t.slug}`,icon:t.icon||"folder",items:o,...m&&{colour:"neutral",badge:{text:"off"}}}})}function be(e,s){for(const i of e)if(!i.hidden&&(i.key==="projects"||!i.key&&(i.text||"").toLowerCase()==="projects")){i.items=Array.isArray(i.items)?i.items.slice():[],i.items.push(...s);return}}function O(e,s){const i=D.includes(s.tone)?s.tone:"info",t=s.label||String(s.count);s.details?E.set(e,s.details):E.delete(e);const n=!s.inventory&&A.some(d=>d.url===e&&!d.inventory)?" data-live":"";return`<span class="sidebar-badge sidebar-badge--${i}" data-count-for="${p(e)}"${n} data-badge-label="${p(t)}" aria-label="${p(t)}">${p(String(s.count))}</span>`}function W(e){for(const s of e.querySelectorAll("details")){const i=s.querySelector(":scope > summary");if(!i)continue;let t=0,n=!1;const d=[];for(const l of s.querySelectorAll(":scope > .sidebar-children .sidebar-badge[data-live]")){const u=Number(l.textContent)||0;u&&(t+=u,n||(n=l.classList.contains("sidebar-badge--danger")),d.push({text:l.closest("a")?.querySelector(".sidebar-text")?.textContent.trim()??"",meta:l.dataset.badgeLabel,tone:l.classList.contains("sidebar-badge--danger")?"danger":void 0}))}const c=`folder:${s.dataset.stateKey||i.textContent.trim()}`;let o=i.querySelector(":scope > .sidebar-badge--sum");if(!t){o?.remove(),E.delete(c);continue}o||(o=document.createElement("span"),i.querySelector(".sidebar-text")?.after(o));const m=i.querySelector(".sidebar-text")?.textContent.trim()||"This folder",r=d.map(l=>`${l.text}: ${l.meta}`).join(" \xB7 ");o.className=`sidebar-badge sidebar-badge--sum sidebar-badge--${n?"danger":"info"}`,o.dataset.countFor=c,o.dataset.badgeLabel=r,o.setAttribute("aria-label",r),o.textContent=String(t),E.set(c,{title:m,items:d})}}const E=new Map;function ye(e,s){if(!e.url||!s)return"";const i=s[e.url];return!i||!(i.count>0)&&!i.showZero?"":O(e.url,i)}const D=["info","attention","danger","live"],ve={"#/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 _(e,{fallbackLabel:s="",tone:i="info"}={}){const t=Number(typeof e=="object"&&e!==null?e.count:e);if(!Number.isFinite(t)||t<0)return null;const n=typeof e?.label=="string"&&e.label||(s?`${t} - ${s}`:String(t)),d={count:Math.round(t),label:n,tone:D.includes(e?.tone)?e.tone:i};return e?.details&&typeof e.details=="object"&&(d.details=e.details),e?.inventory===!0&&(d.inventory=!0),e?.showZero===!0&&(d.showZero=!0),d}function G(e,s,i=null){if(e&&e.type==="separator")return'<hr class="sidebar-divider">';if(e&&e.type==="spacer"){const b=["xs","sm","md","lg","xl","flex"].includes(e.size)?e.size:"md";return`<div class="sidebar-spacer sidebar-spacer--${p(b)}" aria-hidden="true"></div>`}const t=String(e.text||"?").trim().charAt(0).toUpperCase()||"?",n=e.icon?`<span data-icon="${p(e.icon)}"></span>`:`<span class="sidebar-initial" aria-hidden="true">${p(t)}</span>`,d=p(e.text||""),c=e.url?p(e.url):"",o=Array.isArray(e.items)&&e.items.length>0,m=e.colour?U(e.colour):"",r=ee(e),l=[r.hover&&` data-menu-item-hover="${p(r.hover)}"`,r.active&&` data-menu-item-active="${p(r.active)}"`].filter(Boolean).join(""),u=[m&&`color:${p(m)}`,r.accent&&`--dm-menu-highlight:${p(r.accent)}`].filter(Boolean).join(";"),y=u?` style="${u}"`:"";let v="";if(e.badge&&e.badge.text!=null&&e.badge.text!==""){const b=e.badge.variant?U(e.badge.variant):"";v=`<span class="dm-menu-badge"${b?` style="background:${p(b)};color:var(--dm-text-inverse,#fff)"`:""}>${p(String(e.badge.text))}</span>`}if(o){const b=re(e,i),C=S.get(b)!==!1,L=e.items.map(j=>G(j,s,b)).join("");return`<details data-state-key="${p(b)}"${C?" open":""}>
|
|
2
2
|
<summary${y}>${n} <span class="sidebar-text">${d}</span>${v}</summary>
|
|
3
3
|
<div class="sidebar-children">${L}</div>
|
|
4
|
-
</details>`}return`<a href="${c}" class="sidebar-link" data-url="${c}"${y}${l}>${n} <span class="sidebar-text">${d}</span>${ye(e,s)}${v}</a>`}async function xe(e=[],s=[],i=null){const t=g=>g.then(a=>Array.isArray(a)?a.length:Array.isArray(a?.entries)?a.entries.length:0).catch(()=>0);w=Ce(e,i);const n=new Set(w.map(g=>g.url)),d={pages:"#/pages",media:"#/media",menus:"#/menus","context-menus":"#/context-menus",theme:"#/theme",collections:"#/collections",forms:"#/forms",views:"#/views",actions:"#/actions",blocks:"#/blocks",components:"#/components",users:"#/users",plugins:"#/plugins",notifications:"#/system/notifications"},c=(g,a)=>B(s,g)&&!n.has(d[g])?a():Promise.resolve(0),[o,m,r,l,u,y,v,b,C,L,j,F,N,P]=await Promise.all([c("pages",()=>t(h.pages.list())),c("media",()=>t(h.media.list())),c("menus",()=>t(h.menus.list())),c("context-menus",()=>t(h.contextMenus.list())),c("theme",()=>h.theme.get().then(g=>(g?.overrides||[]).length).catch(()=>0)),c("collections",()=>t(h.collections.list())),c("forms",()=>t(h.forms.list())),c("views",()=>t(h.views.list())),c("actions",()=>t(h.actions.list())),c("blocks",()=>t(h.blocks.list())),c("components",()=>t(h.components.list())),c("users",()=>t(h.users.list())),c("plugins",()=>t(h.plugins.list())),c("notifications",()=>h.system?.notifications?.unreadCount?.().then(g=>g?.count??0).catch(()=>0)??Promise.resolve(0))]),q={};await Promise.all(w.map(async g=>{const a=await K(g);a&&(q[g.url]=a)}));const T={"#/pages":o,"#/media":m,"#/menus":r,"#/context-menus":l,"#/theme":u,"#/collections":y,"#/forms":v,"#/views":b,"#/actions":C,"#/blocks":L,"#/components":j,"#/users":F,"#/plugins":N,"#/system/notifications":P},A={};for(const[g,a]of Object.entries(T)){if(n.has(g))continue;const[x,f]=ve[g]??["item","items"],k=_(a);k&&(A[g]={...k,label:`${k.count} ${k.count===1?x:f}`})}return{...A,...q}}let w=[];const $e="dm:sidebar-count",Se=6e4;function Ce(e=[],s=null){const i=[],t=n=>{if(n&&!(s&&typeof n.url=="string"&&n.url.startsWith("#/")&&!Y(s,n.url.slice(1)))){n.url&&typeof n.countUrl=="string"&&n.countUrl&&i.push({url:n.url,countUrl:n.countUrl,text:n.text||"",countLabel:n.countLabel||"",inventory:n.inventory===!0});for(const d of[...n.children||[],...n.items||[]])t(d)}};for(const n of e??[])t(n?.item??n);return i}async function K(e){try{const s=new Date,i=`${s.getFullYear()}-${String(s.getMonth()+1).padStart(2,"0")}-${String(s.getDate()).padStart(2,"0")}`,t=e.countUrl+(e.countUrl.includes("?")?"&":"?")+`today=${i}`;return _(await H.get(t),{fallbackLabel:e.countLabel||e.text})}catch{return null}}function V(e,s,i){const t=[...e.querySelectorAll("a.sidebar-link")].find(d=>d.dataset.url===s);if(!t)return;const n=t.querySelector(".sidebar-badge[data-count-for]");if(!i||!(i.count>0))n?.remove();else{const d=document.createRange();d.selectNodeContents(t);const c=d.createContextualFragment(O(s,i)).firstElementChild;n?n.replaceWith(c):t.querySelector(".sidebar-text")?.after(c)}R.set(s,i),W(e)}const R=new Map;async function I(e,s=null){const i=s?w.filter(t=>t.url===s):w;await Promise.all(i.map(async t=>{const n=await K(t),d=R.get(t.url);JSON.stringify(d??null)!==JSON.stringify(n)&&V(e,t.url,n)}))}function ke(e){e.dataset.liveCounts||(e.dataset.liveCounts="1",window.addEventListener($e,s=>{const i=s.detail||{};if(i.url)if(i.count!==void 0){const t=w.find(n=>n.url===i.url);V(e,i.url,_(i,{fallbackLabel:t?.countLabel||t?.text||""}))}else I(e,i.url)}),setInterval(()=>{!document.hidden&&document.body.contains(e)&&I(e)},Se),document.addEventListener("visibilitychange",()=>{document.hidden||I(e)}),Ae(e))}function X(e,s,i){const t=(c,o,m)=>{const r=document.createElement(c);return o&&(r.className=o),m!=null&&(r.textContent=String(m)),r};if(!i){e.textContent=s;return}e.appendChild(t("strong","dm-badge-pop-title",i.title||s)),(i.rows?.length||!i.items?.length)&&e.appendChild(t("p","dm-badge-pop-sub",s));const n=Array.isArray(i.rows)?i.rows.slice(0,6):[];if(n.length){const c=t("dl","dm-badge-pop-rows");for(const o of n)c.appendChild(t("dt",null,o.label)),c.appendChild(t("dd",o.tone==="danger"?"is-danger":null,o.value));e.appendChild(c)}const d=Array.isArray(i.items)?i.items.slice(0,6):[];if(d.length){const c=t("ul","dm-badge-pop-items");for(const o of d){const m=t("li",o.tone==="danger"?"is-danger":null);m.appendChild(t("span","dm-badge-pop-text",o.text)),o.meta&&m.appendChild(t("span","dm-badge-pop-meta",o.meta)),c.appendChild(m)}e.appendChild(c)}else i.empty&&e.appendChild(t("p","dm-badge-pop-empty",i.empty))}function Ae(e){let s=null,i=null;const t=()=>e.classList.contains("is-collapsed"),n=()=>{clearTimeout(i),s?.remove(),s=null},d=(r,l)=>{n(),i=setTimeout(()=>{s=document.createElement("div"),s.className="dm-help-pop dm-badge-pop",s.setAttribute("role","tooltip"),l(s),document.body.appendChild(s);const u=r.getBoundingClientRect(),y=s.getBoundingClientRect(),v=Math.min(u.right+10,innerWidth-y.width-8);s.style.left=`${Math.max(8,v)}px`,s.style.top=`${Math.max(8,Math.min(u.top+u.height/2-y.height/2,innerHeight-y.height-8))}px`},150)},c=r=>{const l=r?.dataset.badgeLabel;if(!l)return n();d(r,u=>X(u,l,E.get(r.dataset.countFor)))},o=r=>{const l=r.querySelector(":scope > .sidebar-text")?.textContent.trim()||"",u=r.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");d(r,y=>{const v=u&&E.get(u.dataset.countFor);if(v)return X(y,u.dataset.badgeLabel,{...v,title:v.title||l});if(!u){y.textContent=l;return}const b=document.createElement("strong");b.className="dm-badge-pop-title",b.textContent=l;const C=document.createElement("p");C.className="dm-badge-pop-sub",C.textContent=u.dataset.badgeLabel,y.append(b,C)})},m=r=>{const l=r.closest?.("a.sidebar-link, summary");return l&&e.contains(l)?l:null};e.addEventListener("mouseover",r=>{if(t()){const u=m(r.target);u&&!u.contains(r.relatedTarget)&&o(u);return}const l=r.target.closest?.(".sidebar-badge[data-badge-label]");l&&c(l)}),e.addEventListener("mouseout",r=>{if(t()){const l=m(r.target);l&&!l.contains(r.relatedTarget)&&n();return}r.target.closest?.(".sidebar-badge[data-badge-label]")&&n()}),e.addEventListener("focusin",r=>{const l=r.target.closest?.("a, summary");if(t()&&l&&e.contains(l)&&l.querySelector(":scope > .sidebar-text"))return o(l);const u=l?.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");u?c(u):n()}),e.addEventListener("focusout",n),e.addEventListener("click",n),window.addEventListener("scroll",n,!0),window.addEventListener("hashchange",n)}async function we(e=[]){if(!B(e,"settings"))return"Admin";try{const s=await h.settings.get();return s?.adminBrand?.title||s?.title||"Admin"}catch{return"Admin"}}export async function renderAdminSidebar({mount:e,permissions:s,adminScope:i=null}){const t=$(e).get(0);if(!t)return;let n,d=null,c=null,o=null,m=null,r=null;try{const a=await h.get("/sidebar/menu");n=Array.isArray(a?.items)?a.items:null,d=a?.variant||null,c=a?.position||null,o=a?.style||null,m=a?.float||null,r=a?.highlight||null}catch{n=null}(!n||!n.some(a=>a&&(!a.hidden||a.restricted)))&&(console.warn("[admin-sidebar] No admin-sidebar menu found; using fallback tree"),n=pe.slice());let l=[];try{l=await H.get("/api/sidebar/registered-items")||[]}catch{}n=ie(n,z),n=te(n,l.filter(a=>(a?.item??a)?.url!==z)),n=ne(n),n=ae(n,[se()]);const u=await he(s);u.length&&be(n,u),n=le(n,s,i),n=oe(n);const[y,v]=await Promise.all([xe(l.filter(a=>B(s,a?.item?.permission)),s,i).catch(()=>({})),we(s)]),b=d?` dm-admin-sidebar--${p(d)}`:"";function C(a,x="px"){if(a==null)return a;const f=String(a).trim();return/^\d+(\.\d+)?$/.test(f)?`${f}${x}`:f}let L="";if(o){const a=[];o.fontFamily&&a.push(`font-family: ${o.fontFamily}, sans-serif`),o.fontSize&&a.push(`font-size: ${C(o.fontSize)}`),o.fontWeight&&a.push(`font-weight: ${o.fontWeight}`),o.letterSpacing&&a.push(`letter-spacing: ${C(o.letterSpacing,"em")}`);const x=[];if(a.length&&x.push(`#admin-sidebar .dm-admin-sidebar, #admin-sidebar .dm-admin-sidebar .sidebar-link, #admin-sidebar .dm-admin-sidebar summary, #admin-sidebar .dm-admin-sidebar .sidebar-text { ${a.join("; ")} }`),o.iconSize){const f=C(o.iconSize);x.push(`#admin-sidebar .dm-admin-sidebar [data-icon], #admin-sidebar .dm-admin-sidebar [data-icon] svg { width: ${f} !important; height: ${f} !important; }`)}x.length&&(L=`<style data-admin-sidebar-style>${x.join(" ")}</style>`)}const j=de(s)?ce():"",F=`<div class="dm-admin-sidebar-header"><span data-icon="layout"></span><span class="dm-admin-sidebar-title">${p(v)}</span>${j}${me()}</div>`,N=n.some(a=>a&&a.type==="spacer"&&a.size==="flex")?" dm-admin-sidebar--has-flex-spacer":"",P=`${L}<nav class="dm-admin-sidebar${b}${N}">${F}${n.map(a=>G(a,y)).join("")}</nav>`,q=document.createRange();q.selectNodeContents(t);const T=q.createContextualFragment(P);t.replaceChildren(T),Domma.icons.scan(t),t.querySelector(".dm-admin-sidebar-arrange")?.addEventListener("click",()=>ue(t));for(const a of w)R.set(a.url,y[a.url]??null);W(t),ke(t),t.removeAttribute("data-menu-hover"),t.removeAttribute("data-menu-active"),t.style.removeProperty("--dm-menu-highlight"),Z(t,{highlight:r});const A=document.querySelector(".dashboard-wrapper");["position","top","bottom","left","right","zIndex","maxHeight","transform"].forEach(a=>{t.style[a]=""}),t.classList.remove("dm-menu-floating"),A&&(A.style.paddingLeft="",A.style.paddingRight=""),t.__dmGutter=()=>{if(c!=="floating"||!A)return;const a=m&&m.anchor||"TL",x=Number.isFinite(Number(m?.offsetX))?Number(m.offsetX):16,f=t.classList.contains("is-collapsed")?ge:parseInt(t.style.width,10)||t.getBoundingClientRect().width||260,k=Math.round(f+x*2)+"px";a==="R"||a==="TR"||a==="BR"?A.style.paddingRight=k:A.style.paddingLeft=k},c==="floating"&&Q(t,{position:"floating",float:m||{}}),t.querySelectorAll("details").forEach(a=>{a.addEventListener("toggle",function(){const x=this.getAttribute("data-state-key");x&&S.set(x,this.open);const f=this.querySelector(":scope > .sidebar-children");if(f)if(this.open){const k=f.scrollHeight;f.style.maxHeight="0",requestAnimationFrame(()=>{f.style.maxHeight=k+"px",f.addEventListener("transitionend",function J(){f.style.maxHeight="none",f.removeEventListener("transitionend",J)})})}else{const k=f.scrollHeight;f.style.maxHeight=k+"px",requestAnimationFrame(()=>{f.style.maxHeight="0"})}})}),fe(t),t.dataset.routeWired||(t.dataset.routeWired="1",M.subscribe("router:afterChange",()=>t.__dmSetActive?.()));function g(){const a=location.hash||"#/";$(t).find(".sidebar-link").removeClass("active"),$(t).find(`.sidebar-link[data-url="${a}"]`).addClass("active")}g(),t.__dmSetActive=g}
|
|
4
|
+
</details>`}return`<a href="${c}" class="sidebar-link" data-url="${c}"${y}${l}>${n} <span class="sidebar-text">${d}</span>${ye(e,s)}${v}</a>`}async function xe(e=[],s=[],i=null){const t=g=>g.then(a=>Array.isArray(a)?a.length:Array.isArray(a?.entries)?a.entries.length:0).catch(()=>0);A=Ce(e,i);const n=new Set(A.map(g=>g.url)),d={pages:"#/pages",media:"#/media",menus:"#/menus","context-menus":"#/context-menus",theme:"#/theme",collections:"#/collections",forms:"#/forms",views:"#/views",actions:"#/actions",blocks:"#/blocks",components:"#/components",users:"#/users",plugins:"#/plugins",notifications:"#/system/notifications"},c=(g,a)=>B(s,g)&&!n.has(d[g])?a():Promise.resolve(0),[o,m,r,l,u,y,v,b,C,L,j,F,N,P]=await Promise.all([c("pages",()=>t(h.pages.list())),c("media",()=>t(h.media.list())),c("menus",()=>t(h.menus.list())),c("context-menus",()=>t(h.contextMenus.list())),c("theme",()=>h.theme.get().then(g=>(g?.overrides||[]).length).catch(()=>0)),c("collections",()=>t(h.collections.list())),c("forms",()=>t(h.forms.list())),c("views",()=>t(h.views.list())),c("actions",()=>t(h.actions.list())),c("blocks",()=>t(h.blocks.list())),c("components",()=>t(h.components.list())),c("users",()=>t(h.users.list())),c("plugins",()=>t(h.plugins.list())),c("notifications",()=>h.system?.notifications?.unreadCount?.().then(g=>g?.count??0).catch(()=>0)??Promise.resolve(0))]),q={};await Promise.all(A.map(async g=>{const a=await K(g);a&&(q[g.url]=a)}));const T={"#/pages":o,"#/media":m,"#/menus":r,"#/context-menus":l,"#/theme":u,"#/collections":y,"#/forms":v,"#/views":b,"#/actions":C,"#/blocks":L,"#/components":j,"#/users":F,"#/plugins":N,"#/system/notifications":P},k={};for(const[g,a]of Object.entries(T)){if(n.has(g))continue;const[x,f]=ve[g]??["item","items"],w=_(a);w&&(k[g]={...w,label:`${w.count} ${w.count===1?x:f}`})}return{...k,...q}}let A=[];const $e="dm:sidebar-count",Se=6e4;function Ce(e=[],s=null){const i=[],t=n=>{if(n&&!(s&&typeof n.url=="string"&&n.url.startsWith("#/")&&!J(s,n.url.slice(1)))){n.url&&typeof n.countUrl=="string"&&n.countUrl&&i.push({url:n.url,countUrl:n.countUrl,text:n.text||"",countLabel:n.countLabel||"",inventory:n.inventory===!0});for(const d of[...n.children||[],...n.items||[]])t(d)}};for(const n of e??[])t(n?.item??n);return i}async function K(e){try{const s=new Date,i=`${s.getFullYear()}-${String(s.getMonth()+1).padStart(2,"0")}-${String(s.getDate()).padStart(2,"0")}`,t=e.countUrl+(e.countUrl.includes("?")?"&":"?")+`today=${i}`;return _(await H.get(t),{fallbackLabel:e.countLabel||e.text})}catch{return null}}function V(e,s,i){const t=[...e.querySelectorAll("a.sidebar-link")].find(d=>d.dataset.url===s);if(!t)return;const n=t.querySelector(".sidebar-badge[data-count-for]");if(!i||!(i.count>0)&&!i.showZero)n?.remove();else{const d=document.createRange();d.selectNodeContents(t);const c=d.createContextualFragment(O(s,i)).firstElementChild;n?n.replaceWith(c):t.querySelector(".sidebar-text")?.after(c)}R.set(s,i),W(e)}const R=new Map;async function I(e,s=null){const i=s?A.filter(t=>t.url===s):A;await Promise.all(i.map(async t=>{const n=await K(t),d=R.get(t.url);JSON.stringify(d??null)!==JSON.stringify(n)&&V(e,t.url,n)}))}function we(e){e.dataset.liveCounts||(e.dataset.liveCounts="1",window.addEventListener($e,s=>{const i=s.detail||{};if(i.url)if(i.count!==void 0){const t=A.find(n=>n.url===i.url);V(e,i.url,_(i,{fallbackLabel:t?.countLabel||t?.text||""}))}else I(e,i.url)}),setInterval(()=>{!document.hidden&&document.body.contains(e)&&I(e)},Se),document.addEventListener("visibilitychange",()=>{document.hidden||I(e)}),ke(e))}function Z(e,s,i){const t=(c,o,m)=>{const r=document.createElement(c);return o&&(r.className=o),m!=null&&(r.textContent=String(m)),r};if(!i){e.textContent=s;return}e.appendChild(t("strong","dm-badge-pop-title",i.title||s)),(i.rows?.length||!i.items?.length)&&e.appendChild(t("p","dm-badge-pop-sub",s));const n=Array.isArray(i.rows)?i.rows.slice(0,6):[];if(n.length){const c=t("dl","dm-badge-pop-rows");for(const o of n)c.appendChild(t("dt",null,o.label)),c.appendChild(t("dd",o.tone==="danger"?"is-danger":null,o.value));e.appendChild(c)}const d=Array.isArray(i.items)?i.items.slice(0,6):[];if(d.length){const c=t("ul","dm-badge-pop-items");for(const o of d){const m=t("li",o.tone==="danger"?"is-danger":null);m.appendChild(t("span","dm-badge-pop-text",o.text)),o.meta&&m.appendChild(t("span","dm-badge-pop-meta",o.meta)),c.appendChild(m)}e.appendChild(c)}else i.empty&&e.appendChild(t("p","dm-badge-pop-empty",i.empty))}function ke(e){let s=null,i=null;const t=()=>e.classList.contains("is-collapsed"),n=()=>{clearTimeout(i),s?.remove(),s=null},d=(r,l)=>{n(),i=setTimeout(()=>{s=document.createElement("div"),s.className="dm-help-pop dm-badge-pop",s.setAttribute("role","tooltip"),l(s),document.body.appendChild(s);const u=r.getBoundingClientRect(),y=s.getBoundingClientRect(),v=Math.min(u.right+10,innerWidth-y.width-8);s.style.left=`${Math.max(8,v)}px`,s.style.top=`${Math.max(8,Math.min(u.top+u.height/2-y.height/2,innerHeight-y.height-8))}px`},150)},c=r=>{const l=r?.dataset.badgeLabel;if(!l)return n();d(r,u=>Z(u,l,E.get(r.dataset.countFor)))},o=r=>{const l=r.querySelector(":scope > .sidebar-text")?.textContent.trim()||"",u=r.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");d(r,y=>{const v=u&&E.get(u.dataset.countFor);if(v)return Z(y,u.dataset.badgeLabel,{...v,title:v.title||l});if(!u){y.textContent=l;return}const b=document.createElement("strong");b.className="dm-badge-pop-title",b.textContent=l;const C=document.createElement("p");C.className="dm-badge-pop-sub",C.textContent=u.dataset.badgeLabel,y.append(b,C)})},m=r=>{const l=r.closest?.("a.sidebar-link, summary");return l&&e.contains(l)?l:null};e.addEventListener("mouseover",r=>{if(t()){const u=m(r.target);u&&!u.contains(r.relatedTarget)&&o(u);return}const l=r.target.closest?.(".sidebar-badge[data-badge-label]");l&&c(l)}),e.addEventListener("mouseout",r=>{if(t()){const l=m(r.target);l&&!l.contains(r.relatedTarget)&&n();return}r.target.closest?.(".sidebar-badge[data-badge-label]")&&n()}),e.addEventListener("focusin",r=>{const l=r.target.closest?.("a, summary");if(t()&&l&&e.contains(l)&&l.querySelector(":scope > .sidebar-text"))return o(l);const u=l?.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");u?c(u):n()}),e.addEventListener("focusout",n),e.addEventListener("click",n),window.addEventListener("scroll",n,!0),window.addEventListener("hashchange",n)}async function Ae(e=[]){if(!B(e,"settings"))return"Admin";try{const s=await h.settings.get();return s?.adminBrand?.title||s?.title||"Admin"}catch{return"Admin"}}export async function renderAdminSidebar({mount:e,permissions:s,adminScope:i=null}){const t=$(e).get(0);if(!t)return;let n,d=null,c=null,o=null,m=null,r=null;try{const a=await h.get("/sidebar/menu");n=Array.isArray(a?.items)?a.items:null,d=a?.variant||null,c=a?.position||null,o=a?.style||null,m=a?.float||null,r=a?.highlight||null}catch{n=null}(!n||!n.some(a=>a&&(!a.hidden||a.restricted)))&&(console.warn("[admin-sidebar] No admin-sidebar menu found; using fallback tree"),n=pe.slice());let l=[];try{l=await H.get("/api/sidebar/registered-items")||[]}catch{}n=ie(n,z),n=te(n,l.filter(a=>(a?.item??a)?.url!==z)),n=ne(n),n=ae(n,[se()]);const u=await he(s);u.length&&be(n,u),n=le(n,s,i),n=oe(n);const[y,v]=await Promise.all([xe(l.filter(a=>B(s,a?.item?.permission)),s,i).catch(()=>({})),Ae(s)]),b=d?` dm-admin-sidebar--${p(d)}`:"";function C(a,x="px"){if(a==null)return a;const f=String(a).trim();return/^\d+(\.\d+)?$/.test(f)?`${f}${x}`:f}let L="";if(o){const a=[];o.fontFamily&&a.push(`font-family: ${o.fontFamily}, sans-serif`),o.fontSize&&a.push(`font-size: ${C(o.fontSize)}`),o.fontWeight&&a.push(`font-weight: ${o.fontWeight}`),o.letterSpacing&&a.push(`letter-spacing: ${C(o.letterSpacing,"em")}`);const x=[];if(a.length&&x.push(`#admin-sidebar .dm-admin-sidebar, #admin-sidebar .dm-admin-sidebar .sidebar-link, #admin-sidebar .dm-admin-sidebar summary, #admin-sidebar .dm-admin-sidebar .sidebar-text { ${a.join("; ")} }`),o.iconSize){const f=C(o.iconSize);x.push(`#admin-sidebar .dm-admin-sidebar [data-icon], #admin-sidebar .dm-admin-sidebar [data-icon] svg { width: ${f} !important; height: ${f} !important; }`)}x.length&&(L=`<style data-admin-sidebar-style>${x.join(" ")}</style>`)}const j=de(s)?ce():"",F=`<div class="dm-admin-sidebar-header"><span data-icon="layout"></span><span class="dm-admin-sidebar-title">${p(v)}</span>${j}${me()}</div>`,N=n.some(a=>a&&a.type==="spacer"&&a.size==="flex")?" dm-admin-sidebar--has-flex-spacer":"",P=`${L}<nav class="dm-admin-sidebar${b}${N}">${F}${n.map(a=>G(a,y)).join("")}</nav>`,q=document.createRange();q.selectNodeContents(t);const T=q.createContextualFragment(P);t.replaceChildren(T),Domma.icons.scan(t),t.querySelector(".dm-admin-sidebar-arrange")?.addEventListener("click",()=>ue(t));for(const a of A)R.set(a.url,y[a.url]??null);W(t),we(t),t.removeAttribute("data-menu-hover"),t.removeAttribute("data-menu-active"),t.style.removeProperty("--dm-menu-highlight"),Q(t,{highlight:r});const k=document.querySelector(".dashboard-wrapper");["position","top","bottom","left","right","zIndex","maxHeight","transform"].forEach(a=>{t.style[a]=""}),t.classList.remove("dm-menu-floating"),k&&(k.style.paddingLeft="",k.style.paddingRight=""),t.__dmGutter=()=>{if(c!=="floating"||!k)return;const a=m&&m.anchor||"TL",x=Number.isFinite(Number(m?.offsetX))?Number(m.offsetX):16,f=t.classList.contains("is-collapsed")?ge:parseInt(t.style.width,10)||t.getBoundingClientRect().width||260,w=Math.round(f+x*2)+"px";a==="R"||a==="TR"||a==="BR"?k.style.paddingRight=w:k.style.paddingLeft=w},c==="floating"&&Y(t,{position:"floating",float:m||{}}),t.querySelectorAll("details").forEach(a=>{a.addEventListener("toggle",function(){const x=this.getAttribute("data-state-key");x&&S.set(x,this.open);const f=this.querySelector(":scope > .sidebar-children");if(f)if(this.open){const w=f.scrollHeight;f.style.maxHeight="0",requestAnimationFrame(()=>{f.style.maxHeight=w+"px",f.addEventListener("transitionend",function X(){f.style.maxHeight="none",f.removeEventListener("transitionend",X)})})}else{const w=f.scrollHeight;f.style.maxHeight=w+"px",requestAnimationFrame(()=>{f.style.maxHeight="0"})}})}),fe(t),t.dataset.routeWired||(t.dataset.routeWired="1",M.subscribe("router:afterChange",()=>t.__dmSetActive?.()));function g(){const a=location.hash||"#/";$(t).find(".sidebar-link").removeClass("active"),$(t).find(`.sidebar-link[data-url="${a}"]`).addClass("active")}g(),t.__dmSetActive=g}
|
|
@@ -103,6 +103,9 @@
|
|
|
103
103
|
its own. See <a href="#/docs/plugins">Plugin guides</a> for each one you have
|
|
104
104
|
installed, and <a href="#/docs/usage/plugins">Plugins</a> for installing and switching.</p>
|
|
105
105
|
|
|
106
|
+
|
|
107
|
+
<section><h3>Calculator</h3><p>Choose Floating to keep Calculator alongside your work, or Standard to open its own page. Both offer Basic and Scientific layouts. Use the settings cog in the card header to choose the layout, accent colour, angle units and floating position, or drag the floating card header. MC clears memory, MR recalls it, MS stores the current value, and M+/M− add or subtract from memory. The display previews valid calculations as you type. Scientific functions support degrees or radians. Preferences are remembered in this browser.</p></section>
|
|
108
|
+
|
|
106
109
|
</div>
|
|
107
110
|
</div>
|
|
108
111
|
</div>
|
package/admin/js/views/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dashboardView as o}from"./dashboard.js";import{pagesView as i}from"./pages.js";import{pageEditorView as r}from"./page-editor.js";import{settingsView as
|
|
1
|
+
import{dashboardView as o}from"./dashboard.js";import{pagesView as i}from"./pages.js";import{pageEditorView as r}from"./page-editor.js";import{settingsView as t}from"./settings.js";import{themeView as e}from"./theme.js";import{notificationsView as m}from"./notifications.js";import{layoutsView as p}from"./layouts.js";import{mediaView as n}from"./media.js";import{loginView as s}from"./login.js";import{usersView as f}from"./users.js";import{userEditorView as w}from"./user-editor.js";import{pluginsView as c}from"./plugins.js";import{pluginCodeView as V}from"./plugin-code.js";import{documentationView as a}from"./documentation.js";import{tutorialsView as l}from"./tutorials.js";import{docViews as d}from"./doc-pages.js";import{pluginGuidesView as u,pluginGuideView as E}from"./plugin-guides.js";import{apiReferenceView as g}from"./api-reference.js";import{collectionsView as v}from"./collections.js";import{collectionEditorView as b}from"./collection-editor.js";import{collectionEntriesView as h}from"./collection-entries.js";import{formsView as y}from"./forms.js";import{formEditorView as j}from"./form-editor.js";import{formSubmissionsView as k}from"./form-submissions.js";import{viewsListView as L}from"./views-list.js";import{viewEditorView as x}from"./view-editor.js";import{viewPreviewView as M}from"./view-preview.js";import{actionsListView as D}from"./actions-list.js";import{actionEditorView as G}from"./action-editor.js";import{proDocsView as P}from"./pro-docs.js";import{blocksView as R}from"./blocks.js";import{blockEditorView as S}from"./block-editor.js";import{componentsView as C}from"./components.js";import{componentEditorView as N}from"./component-editor.js";import{myProfileView as T}from"./my-profile.js";import{rolesView as U}from"./roles.js";import{roleEditorView as q}from"./role-editor.js";import{effectsView as z}from"./effects.js";import{searchView as A}from"./search.js";import{notesView as B}from"./notes.js";import{todoView as F}from"./todo.js";import{calculatorView as H}from"../lib/calculator.js";import{analyticsView as I}from"./analytics.js";import{seoView as J}from"./seo.js";import{contactsView as K}from"./contacts.js";import{callNewView as O}from"./call-new.js";import{menusView as Q}from"./menus.js";import{contextMenusView as W}from"./context-menus.js";import{contextMenuEditorView as X}from"./context-menu-editor.js";import{menuEditorView as Y}from"./menu-editor.js";import{menuLocationsView as Z}from"./menu-locations.js";import{projectsView as _}from"./projects.js";import{projectDetailView as $}from"./project-detail.js";import{projectSettingsView as oo}from"./project-settings.js";import{apiTokensView as io}from"./api-tokens.js";import{apiEndpointsView as ro}from"./api-endpoints.js";import{apiEndpointEditorView as to}from"./api-endpoint-editor.js";const eo={templateUrl:"",async onMount(){location.hash="#/menus"}};export const views={projects:_,projectDetail:$,projectSettings:oo,apiTokens:io,apiEndpoints:ro,apiEndpointEditor:to,dashboard:o,pages:i,pageEditor:r,settings:t,theme:e,layouts:p,media:n,login:s,users:f,userEditor:w,plugins:c,pluginCode:V,documentation:a,tutorials:l,apiReference:g,collections:v,collectionEditor:b,collectionEntries:h,forms:y,formEditor:j,formSubmissions:k,viewsList:L,viewEditor:x,viewPreview:M,actionsList:D,actionEditor:G,proDocs:P,blocks:R,blockEditor:S,components:C,componentEditor:N,myProfile:T,roles:U,roleEditor:q,effects:z,search:A,notes:B,todo:F,calculator:H,analytics:I,seo:J,contacts:K,callNew:O,notifications:m,menus:Q,contextMenus:W,contextMenuEditor:X,menuEditor:Y,menuLocations:Z,menusRedirect:eo,pluginGuides:u,pluginGuide:E,...d};
|
package/package.json
CHANGED
package/server/server.js
CHANGED
|
@@ -560,6 +560,8 @@ const {isToolEnabled} = await import('./services/tools.js');
|
|
|
560
560
|
const {toolGate, toolsRoutes} = await import('./routes/api/tools.js');
|
|
561
561
|
await app.register(toolGate('notes', notesRoutes), {prefix: '/api/notes'});
|
|
562
562
|
await app.register(toolGate('todo', todoRoutes), {prefix: '/api/todo'});
|
|
563
|
+
const {calculatorRoutes} = await import('./routes/api/calculator.js');
|
|
564
|
+
await app.register(toolGate('calculator', calculatorRoutes), {prefix: '/api/calculator'});
|
|
563
565
|
await app.register(toolGate('analytics', analyticsRoutes), {prefix: '/api/analytics'});
|
|
564
566
|
await app.register(toolGate('contacts', contactsRoutes), {prefix: '/api/contacts'});
|
|
565
567
|
// SEO (0.90.0; the `seo` plugin in 0.89.0): a global plugin - its redirect guard
|
|
@@ -606,6 +608,7 @@ const {registerSidebarItem} = await import('./services/hooks.js');
|
|
|
606
608
|
const coreToolItems = [
|
|
607
609
|
{text: 'Notes', url: '#/notes', icon: 'edit-3', permission: 'notes', countUrl: '/api/notes/count'},
|
|
608
610
|
{text: 'Todo', url: '#/todo', icon: 'check-square', permission: 'todo', countUrl: '/api/todo/count'},
|
|
611
|
+
{text: 'Calculator', url: '#/calculator', icon: 'calculator', permission: 'calculator'},
|
|
609
612
|
{text: 'Analytics', url: '#/analytics', icon: 'chart-bar', permission: 'analytics', countUrl: '/api/analytics/active-count'},
|
|
610
613
|
{text: 'Contacts', url: '#/contacts', icon: 'users', permission: 'contacts'},
|
|
611
614
|
{text: 'SEO', url: '#/seo', icon: 'search', permission: 'seo', countUrl: '/api/seo/count'}
|
package/server/services/hooks.js
CHANGED
|
@@ -75,7 +75,7 @@ const _registeredSidebarItems = [];
|
|
|
75
75
|
* Unknown plugins remain directly in Tools until they declare their own group.
|
|
76
76
|
*/
|
|
77
77
|
const TOOL_GROUPS = new Map(Object.entries({
|
|
78
|
-
Productivity: ['#/notes', '#/todo', '#/plugins/calendar', '#/plugins/waypoint-pro'],
|
|
78
|
+
Productivity: ['#/notes', '#/todo', '#/calculator', '#/plugins/calendar', '#/plugins/waypoint-pro'],
|
|
79
79
|
Communication: ['#/contacts', '#/plugins/contacts-pro', '#/plugins/mail-reader', '#/plugins/email-pro', '#/plugins/messages'],
|
|
80
80
|
Marketing: ['#/analytics', '#/seo', '#/plugins/analytics-pro/dashboard', '#/plugins/social', '#/plugins/feedback', '#/plugins/surveys'],
|
|
81
81
|
Publishing: ['#/plugins/blog', '#/plugins/blog-pro', '#/plugins/docs', '#/plugins/theme-roller'],
|
|
@@ -277,6 +277,14 @@ export const REGISTRY = [
|
|
|
277
277
|
{key: 'delete', label: 'Delete', description: 'Delete own tasks'}
|
|
278
278
|
]
|
|
279
279
|
},
|
|
280
|
+
{
|
|
281
|
+
key: 'calculator',
|
|
282
|
+
label: 'Calculator',
|
|
283
|
+
description: 'Basic and scientific calculations in a floating panel or on a page.',
|
|
284
|
+
icon: 'calculator',
|
|
285
|
+
group: 'Tools',
|
|
286
|
+
actions: [{key: 'read', label: 'Use', description: 'Use Calculator'}]
|
|
287
|
+
},
|
|
280
288
|
{
|
|
281
289
|
key: 'plugins',
|
|
282
290
|
label: 'Plugins',
|
package/server/services/roles.js
CHANGED
|
@@ -477,7 +477,7 @@ export async function grantPermissionOnce(roleName, permission, source) {
|
|
|
477
477
|
* a later removal in System > Roles sticks. `contacts.manageGroups` is granted
|
|
478
478
|
* the same way, separately (server.js).
|
|
479
479
|
*/
|
|
480
|
-
export const ADMIN_TOOL_GRANTS = ['notes', 'todo', 'contacts', 'analytics', 'seo'];
|
|
480
|
+
export const ADMIN_TOOL_GRANTS = ['notes', 'todo', 'contacts', 'analytics', 'seo', 'calculator'];
|
|
481
481
|
|
|
482
482
|
/**
|
|
483
483
|
* Grant the seeded `admin` role the built-in Tool permissions - once each.
|
package/server/services/tools.js
CHANGED
|
@@ -48,6 +48,7 @@ export const CORE_TOOLS = Object.freeze([
|
|
|
48
48
|
{name: 'contacts', displayName: 'Contacts', icon: 'users', description: 'An address book for each user, with groups.'},
|
|
49
49
|
{name: 'notes', displayName: 'Notes', icon: 'edit-3', description: 'Private notes for each user.'},
|
|
50
50
|
{name: 'todo', displayName: 'Todo', icon: 'check-square', description: 'To-do lists with due dates and reminders.'},
|
|
51
|
+
{name: 'calculator', displayName: 'Calculator', icon: 'calculator', description: 'Basic and scientific calculations, on a page or in a floating panel.'},
|
|
51
52
|
{name: 'analytics', displayName: 'Analytics', icon: 'chart-bar', description: 'Page views and visitors, counted on this server.'},
|
|
52
53
|
{name: 'seo', displayName: 'SEO', icon: 'search', description: 'How the site looks to search engines: an audit of every page, redirects, the sitemap and robots.txt.'}
|
|
53
54
|
]);
|