domma-cms 0.79.1 → 0.81.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/CLAUDE.md +6 -1
- package/admin/index.html +2 -2
- package/admin/js/app.js +2 -2
- package/admin/js/lib/action-editor-arrange.js +1 -1
- package/admin/js/lib/desktop-notify.js +1 -0
- package/admin/js/lib/notifications-arrange.js +1 -1
- package/admin/js/lib/sidebar-renderer.js +1 -1
- package/admin/js/templates/action-editor.html +1 -0
- package/admin/js/templates/docs/usage-actions.html +5 -0
- package/admin/js/templates/form-editor.html +72 -0
- package/admin/js/templates/notifications.html +21 -1
- package/admin/js/views/form-editor.js +7 -7
- package/admin/js/views/notifications.js +67 -4
- package/package.json +1 -1
- package/public/css/forms.css +1 -1
- package/public/js/form-logic-engine.js +1 -1
- package/public/js/forms.js +2 -2
- package/public/js/site.js +1 -1
- package/server/routes/api/api-tokens.js +5 -2
- package/server/routes/api/auth.js +15 -1
- package/server/routes/api/effects.js +36 -0
- package/server/routes/api/forms.js +40 -9
- package/server/routes/api/layouts.js +6 -2
- package/server/routes/api/notifications.js +97 -20
- package/server/routes/api/users.js +11 -0
- package/server/server.js +29 -5
- package/server/services/actions.js +28 -0
- package/server/services/adapterRegistry.js +1 -0
- package/server/services/collections.js +10 -4
- package/server/services/connectionManager.js +1 -0
- package/server/services/email.js +3 -0
- package/server/services/notification-sources.js +361 -0
- package/server/services/notify.js +420 -0
- package/server/services/plugins.js +6 -2
- package/server/services/sidebarBadges.js +33 -0
- package/server/templates/page.html +4 -4
package/CLAUDE.md
CHANGED
|
@@ -278,7 +278,12 @@ render. `then` and `otherwise` are separate branch keys, which is what lets an a
|
|
|
278
278
|
re-fire.
|
|
279
279
|
|
|
280
280
|
Anything a trigger decides about submission is re-enforced in `routes/api/forms.js` from the same
|
|
281
|
-
function - a browser-only block is a suggestion.
|
|
281
|
+
function - a browser-only block is a suggestion. `end-form` (state) masks the rest and turns the submit
|
|
282
|
+
into Finish: the runtime captures that click on the wrapper and announces `dm-form-end` with the
|
|
283
|
+
answers so far, which every embed (site.js, the three forms.js paths, the editor preview) posts its
|
|
284
|
+
own way. The route excuses the masked fields - in `createEntry` too, via `excused`, because
|
|
285
|
+
the collection mirrors the form's `required` - and stamps `meta.outcome`. `notify` is server-only:
|
|
286
|
+
`formTriggerNotice()`, source `core:form-triggers`. Four places attach the runtime (three in
|
|
282
287
|
`public/js/forms.js`, one in `site.js`) plus the editor's live preview; they all ask
|
|
283
288
|
`needsLogicRuntime`/`fbNeedsRuntime` rather than spelling the predicate out, because one of them
|
|
284
289
|
forgetting triggers is the obvious failure. `goto-step` is announced as a `dm-form-trigger` DOM event
|
package/admin/index.html
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<link rel="stylesheet" href="/admin/css/dashboard.css">
|
|
22
22
|
|
|
23
23
|
<!-- Forms CSS - needed for conditional logic preview (.fb-field-hidden, etc.) -->
|
|
24
|
-
<link rel="stylesheet" href="/public/css/forms.css?v=
|
|
24
|
+
<link rel="stylesheet" href="/public/css/forms.css?v=20260926-end-panel">
|
|
25
25
|
|
|
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. -->
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
81
81
|
|
|
82
82
|
<!-- Form Logic Engine - needed for conditional logic preview -->
|
|
83
|
-
<script src="/public/js/form-logic-engine.js?v=
|
|
83
|
+
<script src="/public/js/form-logic-engine.js?v=20260926-end-no-finish"></script>
|
|
84
84
|
|
|
85
85
|
<!-- Admin App (ES module entry) -->
|
|
86
86
|
<script type="module" src="/admin/js/app.js"></script>
|
package/admin/js/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{views as O}from"./views/index.js";import{withPluginChrome as T}from"./lib/plugin-chrome.js";import{api as c,getUser as L,isAuthenticated as d,logout as H}from"./api.js";import{installHttpInterceptor as q}from"./http-interceptor.js";import{installResizableSlideovers as z}from"./lib/slideover-resizable.js";import{installActivityThrobber as K}from"./lib/activity-throbber.js";import{isPathAllowed as J,redirectFor as Y}from"./lib/admin-scope.js";$(()=>{q(),K(),z(),(async()=>{const{applyAdminTheme:t,refreshAdminTheme:e}=await import("./lib/admin-theme.js");d()?await e(c,{init:!0}):t({dommaTheme:"charcoal-dark"},{init:!0})})();let l=null,r="/",u=!1,h=null;function C(t){const e=b[t]||Y(l,t);return e||((t.split("?")[0]||"/")==="/"&&r&&r!=="/"?b[r]||r:null)}const b={};R.use(async(t,e,o)=>{if(t.path==="/login"||t.path==="/reset-password")return o();if(!d()){R.navigate("/login");return}const i=C(t.path);if(i){R.navigate(i);return}o()});async function w(){try{const t=await c.get("/auth/permissions");l=t.adminScope||null,r=t.adminHome||"/";const e=t.permissions||[];return u=J(l,"/settings")&&e.some(o=>o==="settings"||o.startsWith("settings.")),h=e.some(o=>o==="notifications"||o.startsWith("notifications.")),e}catch{return l=null,r="/",u=!1,h=null,[]}}document.addEventListener("dm:admin-home-changed",()=>{w()});let D=null,y=!1;document.addEventListener("dm:sidebar-rebuild",()=>{if(D){y=!0;return}D=(async()=>{do{y=!1;try{await P(await w())}catch{}}while(y)})().finally(()=>{D=null})});async function P(t){const{renderAdminSidebar:e}=await import("./lib/sidebar-renderer.js");await e({mount:"#admin-sidebar",permissions:t,adminScope:l})}M.subscribe("router:afterChange",({to:t})=>{(t.path==="/login"||t.path==="/reset-password")&&(p&&(clearInterval(p),p=null),l=null,r="/",u=!1,h=null)});const B=[{path:"/",view:"dashboard",title:"Dashboard - Domma CMS"},{path:"/pages",view:"pages",title:"Pages - Domma CMS"},{path:"/pages/new",view:"pageEditor",title:"New Page - Domma CMS"},{path:"/pages/edit/*",view:"pageEditor",title:"Edit Page - Domma CMS"},{path:"/media",view:"media",title:"Media - Domma CMS"},{path:"/menus",view:"menus",title:"Menus - Domma CMS"},{path:"/context-menus",view:"contextMenus",title:"Context Menus - Domma CMS"},{path:"/context-menus/new",view:"contextMenuEditor",title:"New Context Menu - Domma CMS"},{path:"/context-menus/edit/:slug",view:"contextMenuEditor",title:"Edit Context Menu - Domma CMS"},{path:"/menus/new",view:"menuEditor",title:"New Menu - Domma CMS"},{path:"/menus/edit/:slug",view:"menuEditor",title:"Edit Menu - Domma CMS"},{path:"/menu-locations",view:"menuLocations",title:"Menu Locations - Domma CMS"},{path:"/navigation",view:"menusRedirect",title:"Menus - Domma CMS"},{path:"/layouts",view:"layouts",title:"Layouts - Domma CMS"},{path:"/settings",view:"settings",title:"Settings - Domma CMS"},{path:"/theme",view:"theme",title:"Theme - Domma CMS"},{path:"/users",view:"users",title:"Users - Domma CMS"},{path:"/users/new",view:"userEditor",title:"New User - Domma CMS"},{path:"/users/edit/:id",view:"userEditor",title:"Edit User - Domma CMS"},{path:"/plugins",view:"plugins",title:"Plugins - Domma CMS"},{path:"/plugins/code/:name",view:"pluginCode",title:"Plugin Code - Domma CMS"},{path:"/documentation",view:"documentation",title:"Usage - Domma CMS"},{path:"/tutorials",view:"tutorials",title:"Tutorials - Domma CMS"},{path:"/tutorials/crud",view:"tutorialCrud",title:"Building a CRUD App - Domma CMS"},{path:"/tutorials/plugin",view:"tutorialPlugin",title:"Writing a Plugin - Domma CMS"},{path:"/tutorials/forms",view:"tutorialForms",title:"Form Follow-Up - Domma CMS"},{path:"/docs/components",view:"componentsReference",title:"Components Reference - Domma CMS"},{path:"/docs/components-howto",view:"componentsHowto",title:"Components How-To - Domma CMS"},{path:"/docs/components-walkthrough",view:"componentsWalkthrough",title:"Components Walkthrough - Domma CMS"},{path:"/docs/components-rules",view:"componentsRules",title:"Components Rules - Domma CMS"},{path:"/docs/usage/pages",view:"usagePages",title:"Pages - Usage - Domma CMS"},{path:"/docs/usage/media",view:"usageMedia",title:"Media - Usage - Domma CMS"},{path:"/docs/usage/navigation",view:"usageNavigation",title:"Navigation - Usage - Domma CMS"},{path:"/docs/usage/dconfig",view:"usageDconfig",title:"DConfig - Usage - Domma CMS"},{path:"/docs/usage/shortcodes",view:"usageShortcodes",title:"Shortcodes - Usage - Domma CMS"},{path:"/docs/usage/site-settings",view:"usageSiteSettings",title:"Site Settings - Usage - Domma CMS"},{path:"/docs/usage/plugins",view:"usagePlugins",title:"Plugins - Usage - Domma CMS"},{path:"/docs/usage/users-roles",view:"usageUsersRoles",title:"Users & Roles - Usage - Domma CMS"},{path:"/docs/usage/views",view:"usageViews",title:"Views - Usage - Domma CMS"},{path:"/docs/usage/actions",view:"usageActions",title:"Actions - Usage - Domma CMS"},{path:"/docs/usage/cta-shortcode",view:"usageCtaShortcode",title:"CTA Shortcode - Usage - Domma CMS"},{path:"/docs/api/authentication",view:"apiRefAuthentication",title:"Authentication - API - Domma CMS"},{path:"/docs/api/pages",view:"apiRefPages",title:"Pages - API - Domma CMS"},{path:"/docs/api/settings",view:"apiRefSettings",title:"Settings - API - Domma CMS"},{path:"/docs/api/layouts",view:"apiRefLayouts",title:"Layouts - API - Domma CMS"},{path:"/docs/api/navigation",view:"apiRefNavigation",title:"Navigation - API - Domma CMS"},{path:"/docs/api/media",view:"apiRefMedia",title:"Media - API - Domma CMS"},{path:"/docs/api/users",view:"apiRefUsers",title:"Users - API - Domma CMS"},{path:"/docs/api/plugins",view:"apiRefPlugins",title:"Plugins - API - Domma CMS"},{path:"/docs/api/collections",view:"apiRefCollections",title:"Collections - API - Domma CMS"},{path:"/docs/api/views",view:"apiRefViews",title:"Views API - Domma CMS"},{path:"/docs/api/actions",view:"apiRefActions",title:"Actions API - Domma CMS"},{path:"/api-reference",view:"apiReference",title:"API Reference - Domma CMS"},{path:"/collections",view:"collections",title:"Collections - Domma CMS"},{path:"/collections/new",view:"collectionEditor",title:"New Collection - Domma CMS"},{path:"/collections/edit/:slug",view:"collectionEditor",title:"Edit Collection - Domma CMS"},{path:"/collections/:slug/entries",view:"collectionEntries",title:"Entries - Domma CMS"},{path:"/forms",view:"forms",title:"Forms - Domma CMS"},{path:"/forms/new",view:"formEditor",title:"New Form - Domma CMS"},{path:"/forms/edit/:slug",view:"formEditor",title:"Edit Form - Domma CMS"},{path:"/forms/:slug/submissions",view:"formSubmissions",title:"Submissions - Domma CMS"},{path:"/views",view:"viewsList",title:"Views - Domma CMS"},{path:"/views/new",view:"viewEditor",title:"New View - Domma CMS"},{path:"/views/edit/:slug",view:"viewEditor",title:"Edit View - Domma CMS"},{path:"/views/:slug/preview",view:"viewPreview",title:"View Preview - Domma CMS"},{path:"/actions",view:"actionsList",title:"Actions - Domma CMS"},{path:"/actions/new",view:"actionEditor",title:"New Action - Domma CMS"},{path:"/actions/edit/:slug",view:"actionEditor",title:"Edit Action - Domma CMS"},{path:"/pro/docs",view:"proDocs",title:"Pro Documentation - Domma CMS"},{path:"/blocks",view:"blocks",title:"Blocks - Domma CMS"},{path:"/blocks/new",view:"blockEditor",title:"New Block - Domma CMS"},{path:"/blocks/edit/:name",view:"blockEditor",title:"Edit Block - Domma CMS"},{path:"/components",view:"components",title:"Components - Domma CMS"},{path:"/components/new",view:"componentEditor",title:"New Component - Domma CMS"},{path:"/components/edit/:name",view:"componentEditor",title:"Edit Component - Domma CMS"},{path:"/my-profile",view:"myProfile",title:"My Profile - Domma CMS"},{path:"/roles",view:"roles",title:"Roles & Permissions - Domma CMS"},{path:"/roles/edit/:id",view:"roleEditor",title:"Edit Role - Domma CMS"},{path:"/effects",view:"effects",title:"Effects - Domma CMS"},{path:"/search",view:"search",title:"Search - Domma CMS"},{path:"/notes",view:"notes",title:"Notes - Domma CMS"},{path:"/todo",view:"todo",title:"Todo - Domma CMS"},{path:"/analytics",view:"analytics",title:"Analytics - Domma CMS"},{path:"/contacts",view:"contacts",title:"Contacts - Domma CMS"},{path:"/system/notifications",view:"notifications",title:"Notifications - Domma CMS"},{path:"/api-tokens",view:"apiTokens",title:"API Tokens - Domma CMS"},{path:"/api-endpoints",view:"apiEndpoints",title:"API Builder - Domma CMS"},{path:"/api-endpoints/new",view:"apiEndpointEditor",title:"New API Endpoint - Domma CMS"},{path:"/api-endpoints/edit/:id",view:"apiEndpointEditor",title:"Edit API Endpoint - Domma CMS"},{path:"/projects",view:"projects",title:"Projects - Domma CMS"},{path:"/projects/new",view:"projects",title:"New Project - Domma CMS"},{path:"/projects/edit/:slug",view:"projects",title:"Edit Project - Domma CMS"},{path:"/projects/:slug/settings",view:"projectSettings",title:"Project settings - Domma CMS"},{path:"/projects/:slug/pages",view:"pages",title:"Project pages - Domma CMS"},{path:"/projects/:slug/collections",view:"collections",title:"Project collections - Domma CMS"},{path:"/projects/:slug/forms",view:"forms",title:"Project forms - Domma CMS"},{path:"/projects/:slug/actions",view:"actionsList",title:"Project actions - Domma CMS"},{path:"/projects/:slug/menus",view:"menus",title:"Project menus - Domma CMS"},{path:"/projects/:slug/blocks",view:"blocks",title:"Project blocks - Domma CMS"},{path:"/projects/:slug/components",view:"components",title:"Project components - Domma CMS"},{path:"/projects/:slug/views",view:"viewsList",title:"Project views - Domma CMS"},{path:"/projects/:slug/roles",view:"roles",title:"Project roles - Domma CMS"},{path:"/projects/:slug/users",view:"users",title:"Project users - Domma CMS"},{path:"/projects/:slug/apis",view:"apiEndpoints",title:"Project APIs - Domma CMS"},{path:"/projects/:slug",view:"projectDetail",title:"Project - Domma CMS"},{path:"/login",view:"login",title:"Sign in - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}},{path:"/reset-password",view:"login",title:"Reset Password - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}}];M.subscribe("router:afterChange",async({to:t,from:e})=>{if(!(t.path==="/login"||t.path==="/reset-password")){if($("#admin-sidebar").show(),$("#admin-topbar").show(),e?.path==="/login"||e?.path==="/reset-password"){$("#topbar-user-name").remove(),import("./lib/admin-theme.js").then(a=>a.refreshAdminTheme(c)),await I();const o=await w(),i=C(t.path);i&&R.navigate(i),P(o);try{const a=await c.system.notifications.list().catch(()=>[]),n=(Array.isArray(a)?a:[]).filter(s=>s.unread&&["warning","critical"].includes(s.data?.severity)).slice(0,3);for(const s of n){const f=g(s.data?.title||""),A=g((s.data?.body||"").slice(0,120));E.toast(`${f} - ${A}`,{type:s.data?.severity==="critical"?"error":"warning",duration:0})}}catch{}}x()}}),M.subscribe("router:afterChange",()=>{setTimeout(()=>{$(".btn-primary, .btn-danger").length&&Domma.effects.reveal(".btn-primary, .btn-danger",{animation:"fade",stagger:40,duration:300})},50)});const V=["#field-project","#menu-project","#collection-project","#endpoint-project","#action-project","#view-project","#block-project","#component-project","#role-project",'[name="project"]','[name="ownedByProject"]'];function W(t){return/^\/(pages|menus|context-menus|collections|forms|actions|views|blocks|components|users|roles|api-endpoints)\/(new|edit\/)/.test(t)}M.subscribe("router:afterChange",({to:t})=>{const e=t?String(t.path).split("?")[0]:"";if(!t||!W(e))return;const i=new URLSearchParams(location.hash.split("?")[1]||"").get("project");if(!i||!/\/new$/.test(e))return;let a=0;const n=60,s=1500,f=()=>{a+=n;for(const A of V){const v=document.querySelector(A);if(!v)continue;if(Array.from(v.options||[]).find(F=>F.value===i)){v.value=i,v.dispatchEvent(new Event("change",{bubbles:!0}));return}}a<s&&setTimeout(f,n)};setTimeout(f,n)});const k="cms_card_states",N=S.get(k)||{};M.subscribe("router:afterChange",()=>{setTimeout(()=>{$("#view-container .card-collapsible").each(function(){const t=$(this).find(".card-header h2, .card-header h3").first().text().trim();t&&N[t]==="collapsed"&&$(this).addClass("card-collapsed")})},200)}),$("#view-container").on("click",".card-collapsible .card-header",function(t){if($(t.target).closest("button, a").length)return;const e=$(this).closest(".card");e.toggleClass("card-collapsed");const o=$(this).find("h2, h3").first().text().trim();o&&(N[o]=e.hasClass("card-collapsed")?"collapsed":"open",S.set(k,N))}),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash==="#/login"||window.location.hash.startsWith("#/reset-password"))return;const e=document.querySelector("#view-container .view-header button.btn-primary");e&&(t.preventDefault(),e.click())});const m={...O},_={notes:{displayName:"Notes",icon:"edit-3"},todo:{displayName:"Todo",icon:"check-square"},analytics:{displayName:"Analytics",icon:"chart-bar"},contacts:{displayName:"Contacts",icon:"users"},projects:{displayName:"Projects",icon:"folder"},pages:{displayName:"Pages",icon:"file-text"},media:{displayName:"Media",icon:"image"},menus:{displayName:"Menus",icon:"menu"},contextMenus:{displayName:"Context Menus",icon:"menu"},theme:{displayName:"Theme",icon:"droplet"},collections:{displayName:"Collections",icon:"database"},forms:{displayName:"Forms",icon:"layout"},viewsList:{displayName:"Views",icon:"eye"},actionsList:{displayName:"Actions",icon:"zap"},apiEndpoints:{displayName:"API Builder",icon:"code"},blocks:{displayName:"Blocks",icon:"box"},components:{displayName:"Components",icon:"component"},notifications:{displayName:"Notifications",icon:"bell"},roles:{displayName:"Roles",icon:"shield"},users:{displayName:"Users",icon:"users"},settings:{displayName:"Site Settings",icon:"settings"},effects:{displayName:"Effects",icon:"sparkles"},layouts:{displayName:"Layouts",icon:"layout"},plugins:{displayName:"Marketplace",icon:"store"},myProfile:{displayName:"My Profile",icon:"user"},apiTokens:{displayName:"API Tokens",icon:"key"},search:{displayName:"Search",icon:"search"},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]=T(m[t],{plugin:t,licence:"Built in",...e}));const j=[...B];async function I(){if(d())try{const t=await c.plugins.adminConfig();t.routes?.length&&j.push(...t.routes);for(const[e,o]of Object.entries(t.views||{}))try{const i=await import(`/plugins/${o.entry}`);m[e]=T(i[o.exportName],(t.meta||{})[e])}catch{}for(const[e,{route:o}]of Object.entries(t.takeovers||{}))o&&(b[`/${e}`]=o);for(const{id:e,href:o}of t.css||[])if(!document.getElementById(e)){const i=document.createElement("link");i.id=e,i.rel="stylesheet",i.href=o,document.head.appendChild(i)}}catch{}}function x(){const t=L();if(!t||$("#topbar-user-name").length)return;const o={"super-admin":"Super Admin",admin:"Admin",user:"User"}[t.role]||t.role;$("#topbar-user").html(`
|
|
1
|
+
import{views as O}from"./views/index.js";import{withPluginChrome as T}from"./lib/plugin-chrome.js";import{api as l,getUser as L,isAuthenticated as d,logout as H}from"./api.js";import{installHttpInterceptor as q}from"./http-interceptor.js";import{installResizableSlideovers as z}from"./lib/slideover-resizable.js";import{installActivityThrobber as K}from"./lib/activity-throbber.js";import{isPathAllowed as J,redirectFor as Y}from"./lib/admin-scope.js";import{deliverDesktop as G,desktopActive as Q,notificationSettings as X}from"./lib/desktop-notify.js";$(()=>{q(),K(),z(),(async()=>{const{applyAdminTheme:t,refreshAdminTheme:e}=await import("./lib/admin-theme.js");d()?await e(l,{init:!0}):t({dommaTheme:"charcoal-dark"},{init:!0})})();let c=null,r="/",u=!1,h=null;function C(t){const e=b[t]||Y(c,t);return e||((t.split("?")[0]||"/")==="/"&&r&&r!=="/"?b[r]||r:null)}const b={};R.use(async(t,e,o)=>{if(t.path==="/login"||t.path==="/reset-password")return o();if(!d()){R.navigate("/login");return}const i=C(t.path);if(i){R.navigate(i);return}o()});async function w(){try{const t=await l.get("/auth/permissions");c=t.adminScope||null,r=t.adminHome||"/";const e=t.permissions||[];return u=J(c,"/settings")&&e.some(o=>o==="settings"||o.startsWith("settings.")),h=e.some(o=>o==="notifications"||o.startsWith("notifications.")),e}catch{return c=null,r="/",u=!1,h=null,[]}}document.addEventListener("dm:admin-home-changed",()=>{w()});let D=null,y=!1;document.addEventListener("dm:sidebar-rebuild",()=>{if(D){y=!0;return}D=(async()=>{do{y=!1;try{await P(await w())}catch{}}while(y)})().finally(()=>{D=null})});async function P(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})=>{(t.path==="/login"||t.path==="/reset-password")&&(p&&(clearInterval(p),p=null),c=null,r="/",u=!1,h=null)});const B=[{path:"/",view:"dashboard",title:"Dashboard - Domma CMS"},{path:"/pages",view:"pages",title:"Pages - Domma CMS"},{path:"/pages/new",view:"pageEditor",title:"New Page - Domma CMS"},{path:"/pages/edit/*",view:"pageEditor",title:"Edit Page - Domma CMS"},{path:"/media",view:"media",title:"Media - Domma CMS"},{path:"/menus",view:"menus",title:"Menus - Domma CMS"},{path:"/context-menus",view:"contextMenus",title:"Context Menus - Domma CMS"},{path:"/context-menus/new",view:"contextMenuEditor",title:"New Context Menu - Domma CMS"},{path:"/context-menus/edit/:slug",view:"contextMenuEditor",title:"Edit Context Menu - Domma CMS"},{path:"/menus/new",view:"menuEditor",title:"New Menu - Domma CMS"},{path:"/menus/edit/:slug",view:"menuEditor",title:"Edit Menu - Domma CMS"},{path:"/menu-locations",view:"menuLocations",title:"Menu Locations - Domma CMS"},{path:"/navigation",view:"menusRedirect",title:"Menus - Domma CMS"},{path:"/layouts",view:"layouts",title:"Layouts - Domma CMS"},{path:"/settings",view:"settings",title:"Settings - Domma CMS"},{path:"/theme",view:"theme",title:"Theme - Domma CMS"},{path:"/users",view:"users",title:"Users - Domma CMS"},{path:"/users/new",view:"userEditor",title:"New User - Domma CMS"},{path:"/users/edit/:id",view:"userEditor",title:"Edit User - Domma CMS"},{path:"/plugins",view:"plugins",title:"Plugins - Domma CMS"},{path:"/plugins/code/:name",view:"pluginCode",title:"Plugin Code - Domma CMS"},{path:"/documentation",view:"documentation",title:"Usage - Domma CMS"},{path:"/tutorials",view:"tutorials",title:"Tutorials - Domma CMS"},{path:"/tutorials/crud",view:"tutorialCrud",title:"Building a CRUD App - Domma CMS"},{path:"/tutorials/plugin",view:"tutorialPlugin",title:"Writing a Plugin - Domma CMS"},{path:"/tutorials/forms",view:"tutorialForms",title:"Form Follow-Up - Domma CMS"},{path:"/docs/components",view:"componentsReference",title:"Components Reference - Domma CMS"},{path:"/docs/components-howto",view:"componentsHowto",title:"Components How-To - Domma CMS"},{path:"/docs/components-walkthrough",view:"componentsWalkthrough",title:"Components Walkthrough - Domma CMS"},{path:"/docs/components-rules",view:"componentsRules",title:"Components Rules - Domma CMS"},{path:"/docs/usage/pages",view:"usagePages",title:"Pages - Usage - Domma CMS"},{path:"/docs/usage/media",view:"usageMedia",title:"Media - Usage - Domma CMS"},{path:"/docs/usage/navigation",view:"usageNavigation",title:"Navigation - Usage - Domma CMS"},{path:"/docs/usage/dconfig",view:"usageDconfig",title:"DConfig - Usage - Domma CMS"},{path:"/docs/usage/shortcodes",view:"usageShortcodes",title:"Shortcodes - Usage - Domma CMS"},{path:"/docs/usage/site-settings",view:"usageSiteSettings",title:"Site Settings - Usage - Domma CMS"},{path:"/docs/usage/plugins",view:"usagePlugins",title:"Plugins - Usage - Domma CMS"},{path:"/docs/usage/users-roles",view:"usageUsersRoles",title:"Users & Roles - Usage - Domma CMS"},{path:"/docs/usage/views",view:"usageViews",title:"Views - Usage - Domma CMS"},{path:"/docs/usage/actions",view:"usageActions",title:"Actions - Usage - Domma CMS"},{path:"/docs/usage/cta-shortcode",view:"usageCtaShortcode",title:"CTA Shortcode - Usage - Domma CMS"},{path:"/docs/api/authentication",view:"apiRefAuthentication",title:"Authentication - API - Domma CMS"},{path:"/docs/api/pages",view:"apiRefPages",title:"Pages - API - Domma CMS"},{path:"/docs/api/settings",view:"apiRefSettings",title:"Settings - API - Domma CMS"},{path:"/docs/api/layouts",view:"apiRefLayouts",title:"Layouts - API - Domma CMS"},{path:"/docs/api/navigation",view:"apiRefNavigation",title:"Navigation - API - Domma CMS"},{path:"/docs/api/media",view:"apiRefMedia",title:"Media - API - Domma CMS"},{path:"/docs/api/users",view:"apiRefUsers",title:"Users - API - Domma CMS"},{path:"/docs/api/plugins",view:"apiRefPlugins",title:"Plugins - API - Domma CMS"},{path:"/docs/api/collections",view:"apiRefCollections",title:"Collections - API - Domma CMS"},{path:"/docs/api/views",view:"apiRefViews",title:"Views API - Domma CMS"},{path:"/docs/api/actions",view:"apiRefActions",title:"Actions API - Domma CMS"},{path:"/api-reference",view:"apiReference",title:"API Reference - Domma CMS"},{path:"/collections",view:"collections",title:"Collections - Domma CMS"},{path:"/collections/new",view:"collectionEditor",title:"New Collection - Domma CMS"},{path:"/collections/edit/:slug",view:"collectionEditor",title:"Edit Collection - Domma CMS"},{path:"/collections/:slug/entries",view:"collectionEntries",title:"Entries - Domma CMS"},{path:"/forms",view:"forms",title:"Forms - Domma CMS"},{path:"/forms/new",view:"formEditor",title:"New Form - Domma CMS"},{path:"/forms/edit/:slug",view:"formEditor",title:"Edit Form - Domma CMS"},{path:"/forms/:slug/submissions",view:"formSubmissions",title:"Submissions - Domma CMS"},{path:"/views",view:"viewsList",title:"Views - Domma CMS"},{path:"/views/new",view:"viewEditor",title:"New View - Domma CMS"},{path:"/views/edit/:slug",view:"viewEditor",title:"Edit View - Domma CMS"},{path:"/views/:slug/preview",view:"viewPreview",title:"View Preview - Domma CMS"},{path:"/actions",view:"actionsList",title:"Actions - Domma CMS"},{path:"/actions/new",view:"actionEditor",title:"New Action - Domma CMS"},{path:"/actions/edit/:slug",view:"actionEditor",title:"Edit Action - Domma CMS"},{path:"/pro/docs",view:"proDocs",title:"Pro Documentation - Domma CMS"},{path:"/blocks",view:"blocks",title:"Blocks - Domma CMS"},{path:"/blocks/new",view:"blockEditor",title:"New Block - Domma CMS"},{path:"/blocks/edit/:name",view:"blockEditor",title:"Edit Block - Domma CMS"},{path:"/components",view:"components",title:"Components - Domma CMS"},{path:"/components/new",view:"componentEditor",title:"New Component - Domma CMS"},{path:"/components/edit/:name",view:"componentEditor",title:"Edit Component - Domma CMS"},{path:"/my-profile",view:"myProfile",title:"My Profile - Domma CMS"},{path:"/roles",view:"roles",title:"Roles & Permissions - Domma CMS"},{path:"/roles/edit/:id",view:"roleEditor",title:"Edit Role - Domma CMS"},{path:"/effects",view:"effects",title:"Effects - Domma CMS"},{path:"/search",view:"search",title:"Search - Domma CMS"},{path:"/notes",view:"notes",title:"Notes - Domma CMS"},{path:"/todo",view:"todo",title:"Todo - Domma CMS"},{path:"/analytics",view:"analytics",title:"Analytics - Domma CMS"},{path:"/contacts",view:"contacts",title:"Contacts - Domma CMS"},{path:"/system/notifications",view:"notifications",title:"Notifications - Domma CMS"},{path:"/api-tokens",view:"apiTokens",title:"API Tokens - Domma CMS"},{path:"/api-endpoints",view:"apiEndpoints",title:"API Builder - Domma CMS"},{path:"/api-endpoints/new",view:"apiEndpointEditor",title:"New API Endpoint - Domma CMS"},{path:"/api-endpoints/edit/:id",view:"apiEndpointEditor",title:"Edit API Endpoint - Domma CMS"},{path:"/projects",view:"projects",title:"Projects - Domma CMS"},{path:"/projects/new",view:"projects",title:"New Project - Domma CMS"},{path:"/projects/edit/:slug",view:"projects",title:"Edit Project - Domma CMS"},{path:"/projects/:slug/settings",view:"projectSettings",title:"Project settings - Domma CMS"},{path:"/projects/:slug/pages",view:"pages",title:"Project pages - Domma CMS"},{path:"/projects/:slug/collections",view:"collections",title:"Project collections - Domma CMS"},{path:"/projects/:slug/forms",view:"forms",title:"Project forms - Domma CMS"},{path:"/projects/:slug/actions",view:"actionsList",title:"Project actions - Domma CMS"},{path:"/projects/:slug/menus",view:"menus",title:"Project menus - Domma CMS"},{path:"/projects/:slug/blocks",view:"blocks",title:"Project blocks - Domma CMS"},{path:"/projects/:slug/components",view:"components",title:"Project components - Domma CMS"},{path:"/projects/:slug/views",view:"viewsList",title:"Project views - Domma CMS"},{path:"/projects/:slug/roles",view:"roles",title:"Project roles - Domma CMS"},{path:"/projects/:slug/users",view:"users",title:"Project users - Domma CMS"},{path:"/projects/:slug/apis",view:"apiEndpoints",title:"Project APIs - Domma CMS"},{path:"/projects/:slug",view:"projectDetail",title:"Project - Domma CMS"},{path:"/login",view:"login",title:"Sign in - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}},{path:"/reset-password",view:"login",title:"Reset Password - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}}];M.subscribe("router:afterChange",async({to:t,from:e})=>{if(!(t.path==="/login"||t.path==="/reset-password")){if($("#admin-sidebar").show(),$("#admin-topbar").show(),e?.path==="/login"||e?.path==="/reset-password"){$("#topbar-user-name").remove(),import("./lib/admin-theme.js").then(a=>a.refreshAdminTheme(l)),await I();const o=await w(),i=C(t.path);if(i&&R.navigate(i),P(o),X.read().signInToasts)try{const a=await l.system.notifications.list().catch(()=>[]),n=(Array.isArray(a)?a:[]).filter(s=>s.unread&&["warning","critical"].includes(s.data?.severity)).slice(0,3);for(const s of n){const f=g(s.data?.title||""),A=g((s.data?.body||"").slice(0,120));E.toast(`${f} - ${A}`,{type:s.data?.severity==="critical"?"error":"warning",duration:0})}}catch{}}x()}}),M.subscribe("router:afterChange",()=>{setTimeout(()=>{$(".btn-primary, .btn-danger").length&&Domma.effects.reveal(".btn-primary, .btn-danger",{animation:"fade",stagger:40,duration:300})},50)});const V=["#field-project","#menu-project","#collection-project","#endpoint-project","#action-project","#view-project","#block-project","#component-project","#role-project",'[name="project"]','[name="ownedByProject"]'];function W(t){return/^\/(pages|menus|context-menus|collections|forms|actions|views|blocks|components|users|roles|api-endpoints)\/(new|edit\/)/.test(t)}M.subscribe("router:afterChange",({to:t})=>{const e=t?String(t.path).split("?")[0]:"";if(!t||!W(e))return;const i=new URLSearchParams(location.hash.split("?")[1]||"").get("project");if(!i||!/\/new$/.test(e))return;let a=0;const n=60,s=1500,f=()=>{a+=n;for(const A of V){const v=document.querySelector(A);if(!v)continue;if(Array.from(v.options||[]).find(F=>F.value===i)){v.value=i,v.dispatchEvent(new Event("change",{bubbles:!0}));return}}a<s&&setTimeout(f,n)};setTimeout(f,n)});const k="cms_card_states",N=S.get(k)||{};M.subscribe("router:afterChange",()=>{setTimeout(()=>{$("#view-container .card-collapsible").each(function(){const t=$(this).find(".card-header h2, .card-header h3").first().text().trim();t&&N[t]==="collapsed"&&$(this).addClass("card-collapsed")})},200)}),$("#view-container").on("click",".card-collapsible .card-header",function(t){if($(t.target).closest("button, a").length)return;const e=$(this).closest(".card");e.toggleClass("card-collapsed");const o=$(this).find("h2, h3").first().text().trim();o&&(N[o]=e.hasClass("card-collapsed")?"collapsed":"open",S.set(k,N))}),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash==="#/login"||window.location.hash.startsWith("#/reset-password"))return;const e=document.querySelector("#view-container .view-header button.btn-primary");e&&(t.preventDefault(),e.click())});const m={...O},_={notes:{displayName:"Notes",icon:"edit-3"},todo:{displayName:"Todo",icon:"check-square"},analytics:{displayName:"Analytics",icon:"chart-bar"},contacts:{displayName:"Contacts",icon:"users"},projects:{displayName:"Projects",icon:"folder"},pages:{displayName:"Pages",icon:"file-text"},media:{displayName:"Media",icon:"image"},menus:{displayName:"Menus",icon:"menu"},contextMenus:{displayName:"Context Menus",icon:"menu"},theme:{displayName:"Theme",icon:"droplet"},collections:{displayName:"Collections",icon:"database"},forms:{displayName:"Forms",icon:"layout"},viewsList:{displayName:"Views",icon:"eye"},actionsList:{displayName:"Actions",icon:"zap"},apiEndpoints:{displayName:"API Builder",icon:"code"},blocks:{displayName:"Blocks",icon:"box"},components:{displayName:"Components",icon:"component"},notifications:{displayName:"Notifications",icon:"bell"},roles:{displayName:"Roles",icon:"shield"},users:{displayName:"Users",icon:"users"},settings:{displayName:"Site Settings",icon:"settings"},effects:{displayName:"Effects",icon:"sparkles"},layouts:{displayName:"Layouts",icon:"layout"},plugins:{displayName:"Marketplace",icon:"store"},myProfile:{displayName:"My Profile",icon:"user"},apiTokens:{displayName:"API Tokens",icon:"key"},search:{displayName:"Search",icon:"search"},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]=T(m[t],{plugin:t,licence:"Built in",...e}));const j=[...B];async function I(){if(d())try{const t=await l.plugins.adminConfig();t.routes?.length&&j.push(...t.routes);for(const[e,o]of Object.entries(t.views||{}))try{const i=await import(`/plugins/${o.entry}`);m[e]=T(i[o.exportName],(t.meta||{})[e])}catch{}for(const[e,{route:o}]of Object.entries(t.takeovers||{}))o&&(b[`/${e}`]=o);for(const{id:e,href:o}of t.css||[])if(!document.getElementById(e)){const i=document.createElement("link");i.id=e,i.rel="stylesheet",i.href=o,document.head.appendChild(i)}}catch{}}function x(){const t=L();if(!t||$("#topbar-user-name").length)return;const o={"super-admin":"Super Admin",admin:"Admin",user:"User"}[t.role]||t.role;$("#topbar-user").html(`
|
|
2
2
|
<span id="topbar-user-name" class="topbar-user-name">${g(t.name)}</span>
|
|
3
3
|
<span class="topbar-role-badge topbar-role-badge--${g(t.role)}">${o}</span>
|
|
4
4
|
`),$("#topbar-actions").html(`
|
|
@@ -18,4 +18,4 @@ import{views as O}from"./views/index.js";import{withPluginChrome as T}from"./lib
|
|
|
18
18
|
<span data-icon="log-out"></span>
|
|
19
19
|
<span>Sign out</span>
|
|
20
20
|
</a>
|
|
21
|
-
`);const i=$("#topbar-bell").get(0);i&&(i.hidden=!0);const a=$("#topbar-settings").get(0);a&&(a.hidden=!u),$("#topbar-logout-btn").on("click",n=>{n.preventDefault(),H()}),Domma.icons.scan("#admin-topbar"),E.tooltip("#topbar-actions [data-tooltip]",{placement:"bottom"}),U(),p||(p=setInterval(U,6e4))}function g(t){return String(t).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}let p=null;async function U(){if(!(!d()||h===!1))try{const{count:t}=await
|
|
21
|
+
`);const i=$("#topbar-bell").get(0);i&&(i.hidden=!0);const a=$("#topbar-settings").get(0);a&&(a.hidden=!u),$("#topbar-logout-btn").on("click",n=>{n.preventDefault(),H()}),Domma.icons.scan("#admin-topbar"),E.tooltip("#topbar-actions [data-tooltip]",{placement:"bottom"}),U(),p||(p=setInterval(U,6e4))}function g(t){return String(t).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}let p=null;async function U(){if(!(!d()||h===!1))try{const{count:t}=await l.system.notifications.unreadCount();$("#topbar-bell").removeAttr("hidden");const e=$("#topbar-bell-badge");t>0?e.text(t>99?"99+":String(t)).removeAttr("hidden"):e.attr("hidden","").text(""),t>0&&Q()&&G(await l.system.notifications.list().catch(()=>[]))}catch{}}(async()=>{if(!d()&&!window.location.hash.startsWith("#/reset-password"))window.location.hash="#/login";else if(await I(),L()){const a=await w(),n=(window.location.hash||"#/").slice(1)||"/",s=C(n);s&&(window.location.hash=`#${s}`),await P(a),x()}R.init({container:"#view-container",routes:j,views:m,default:"/",transitions:{enter:"fadeIn",leave:"fadeOut",duration:150}});const t=R._extractParams.bind(R);R._extractParams=function(i,a){const n=a.indexOf("?");if(n!==-1&&(a=a.slice(0,n)),i.endsWith("/*")){const s=i.slice(0,-2);return a.startsWith(s+"/")?{}:null}return t(i,a)};const e=(window.location.hash||"#/").slice(1)||"/";j.filter(i=>i.path.endsWith("/*")).some(i=>e.startsWith(i.path.slice(0,-2)+"/"))&&R._handleRouteChange()})()});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{slugify as
|
|
1
|
+
import{slugify as d}from"./actions-arrange.js";export{d as slugify};export const STEP_TYPES={updateField:{label:"Update a field",help:"Sets one field of the entry to a value - fixed, or built from template variables such as {{now}} or {{user.name}}.",fields:[]},deleteEntry:{label:"Delete the entry",help:"Deletes the entry the action runs on. Put it last - nothing after it has an entry to work with.",fields:[]},moveToCollection:{label:"Move to another collection",help:"Copies the entry into another collection and removes it from this one.",fields:[{name:"targetCollection",label:"Target collection slug",placeholder:"e.g. archived-applications"}]},webhook:{label:"Call a webhook",help:"Sends an HTTP request to another service. The body may use template variables; JSON is sent as JSON.",fields:[{name:"url",label:"URL",placeholder:"https://hooks.example.com/notify"},{name:"method",label:"Method",placeholder:"POST"},{name:"body",label:"Body (JSON)",placeholder:'{"email": "{{entry.data.email}}"}',multiline:!0}]},email:{label:"Send an email",help:"Sends an email through the site's SMTP settings. Every field may use template variables.",fields:[{name:"to",label:"To",placeholder:"{{entry.data.email}}"},{name:"subject",label:"Subject",placeholder:"Your application update"},{name:"template",label:"Body",placeholder:"Your application has been approved.",multiline:!0}]},notify:{label:"Notify the admins",help:'Raises a notification in the admin - the bell, and a desktop notification for anyone who turned those on. Who receives it is set under Notifications > settings, source "Actions". Every field may use template variables.',fields:[{name:"title",label:"Title",placeholder:"New application from {{entry.data.name}}"},{name:"body",label:"Body",placeholder:"Submitted {{now}}.",multiline:!0},{name:"severity",label:"Severity (info, success, warning or critical)",placeholder:"info"},{name:"link",label:"Link (an admin route such as #/forms, or a web address)",placeholder:"#/collections/applications"}]}};const f=["info","success","warning","critical"];export const ROW_LEVEL_MODES=["owner","field"],USER_KEYS=["id","email","name","role"];const n=e=>e==null?"":String(e);function y(e){const t=STEP_TYPES[e?.type]?e.type:String(e?.type||"updateField"),i=e?.config&&typeof e.config=="object"?e.config:{},l={};if(t==="updateField")l.field=n(i.field),l.value=n(i.value);else{for(const r of STEP_TYPES[t]?.fields||[]){const o=i[r.name];l[r.name]=o&&typeof o=="object"?JSON.stringify(o,null,2):n(o)}STEP_TYPES[t]||Object.assign(l,i)}return{type:t,config:l}}export function makeStep(e){return y({type:e,config:{}})}export function formFromAction(e,{project:t=""}={}){const i=e?.trigger||{},l=e?.access?.rowLevel||null,r=e?.transition||null;return{title:n(e?.title),slug:n(e?.slug),description:n(e?.description),collection:n(e?.collection),project:n(e?e.meta?.project:t),bundled:e?.bundled===!0,triggerType:n(i.type)||"manual",triggerLabel:e&&n(i.label)||"Run",triggerIcon:e&&n(i.icon)||"zap",triggerConfirm:n(i.confirmMessage),roles:Array.isArray(e?.access?.roles)?[...e.access.roles]:["admin"],rowLevelEnabled:!!l,rowLevelMode:ROW_LEVEL_MODES.includes(l?.mode)?l.mode:"owner",rowLevelUserKey:USER_KEYS.includes(l?.userKey)?l.userKey:"id",rowLevelField:n(l?.field),transitionEnabled:!!(r&&r.field),transitionField:n(r?.field),transitionFrom:(Array.isArray(r?.from)?r.from:[r?.from]).filter(o=>o!=null&&o!=="").join(", "),transitionTo:n(r?.to),steps:(Array.isArray(e?.steps)?e.steps:[]).map(y)}}export function splitList(e){return n(e).split(",").map(t=>t.trim()).filter(Boolean)}function g(e){const t=n(e).trim();if(!t)return"";try{return JSON.parse(t)}catch{return t}}export function payloadFromForm(e,{meta:t={},editing:i=!1}={}){const l=(e.steps||[]).map(o=>{if(o.type==="updateField")return{type:o.type,config:{field:n(o.config.field).trim(),value:n(o.config.value).trim()}};const s=STEP_TYPES[o.type];if(!s)return{type:o.type,config:{...o.config}};const a={};for(const c of s.fields)a[c.name]=c.multiline?g(o.config[c.name]):n(o.config[c.name]).trim();return{type:o.type,config:a}}),r=e.rowLevelEnabled?{mode:e.rowLevelMode,userKey:e.rowLevelUserKey,...e.rowLevelMode==="field"&&{field:n(e.rowLevelField).trim()}}:null;return{title:n(e.title).trim(),...!i&&n(e.slug).trim()&&{slug:n(e.slug).trim()},description:n(e.description).trim(),collection:e.collection,...e.bundled?{bundled:!0}:{bundled:!1},trigger:{type:e.triggerType||"manual",label:n(e.triggerLabel).trim()||"Run",icon:n(e.triggerIcon).trim()||"zap",confirmMessage:n(e.triggerConfirm).trim()||null},steps:l,transition:e.transitionEnabled&&n(e.transitionField).trim()?{field:n(e.transitionField).trim(),from:splitList(e.transitionFrom),to:n(e.transitionTo).trim()}:null,access:{roles:[...e.roles||[]],rowLevel:r},meta:{...t||{},project:e.project||null}}}export function snapshot(e){const{meta:t,...i}=payloadFromForm(e,{editing:!0});return JSON.stringify({...i,slug:n(e.slug).trim(),project:e.project||""})}const b=/^https?:\/\/\S+$/i,h=/\{\{[^}]+\}\}/;export function checkAction(e){const t={};n(e.title).trim()||(t.title="An action needs a title."),e.collection||(t.collection="Choose the collection it runs on.");const i=n(e.slug).trim();i&&d(i)!==i&&(t.slug=`Lowercase letters, numbers and hyphens - it would be saved as "${d(i)||"?"}".`),e.rowLevelEnabled&&e.rowLevelMode==="field"&&!n(e.rowLevelField).trim()&&(t.rowLevelField="Field match needs the entry field to compare."),e.transitionEnabled&&!n(e.transitionField).trim()&&(t.transitionField="A transition needs the field that holds the state."),(e.roles||[]).length||(t.roles="Nobody could run it - choose at least one role.");const l={};return(e.steps||[]).forEach((r,o)=>{const s=r.config||{};if(r.type==="updateField"&&!n(s.field).trim())l[o]="Choose the field to update.";else if(r.type==="moveToCollection"&&!n(s.targetCollection).trim())l[o]="Give the collection to move it to.";else if(r.type==="webhook"){const a=n(s.url).trim();a?!b.test(a)&&!h.test(a)&&(l[o]="The URL must start with http:// or https://."):l[o]="A webhook needs a URL."}else if(r.type==="email"&&!n(s.to).trim())l[o]="An email needs a To address.";else if(r.type==="notify"){const a=n(s.severity).trim();n(s.title).trim()?a&&!f.includes(a)&&!h.test(a)&&(l[o]=`Severity must be one of ${f.join(", ")}.`):l[o]="A notification needs a title."}}),Object.keys(l).length&&(t.steps=l),t}export function problemsText(e){const t=[];for(const i of["title","collection","slug","roles","rowLevelField","transitionField"])e[i]&&t.push(e[i]);for(const[i,l]of Object.entries(e.steps||{}))t.push(`Step ${Number(i)+1}: ${l}`);return t.join(" ")}export function moveStep(e,t,i){const l=t+i;if(t<0||t>=e.length||l<0||l>=e.length)return e;const r=[...e];return[r[t],r[l]]=[r[l],r[t]],r}export function duplicateStep(e,t){if(t<0||t>=e.length)return e;const i={type:e[t].type,config:{...e[t].config}};return[...e.slice(0,t+1),i,...e.slice(t+1)]}export function removeStep(e,t){return e.filter((i,l)=>l!==t)}const u=(e,t=60)=>e.length>t?`${e.slice(0,t-1)}\u2026`:e;export function stepSummary(e){const t=e?.config||{};switch(e?.type){case"updateField":return t.field?u(`${t.field} \u2192 ${n(t.value)||"(empty)"}`):"No field chosen yet";case"deleteEntry":return"Deletes the entry";case"moveToCollection":return t.targetCollection?`To ${t.targetCollection}`:"No collection given yet";case"webhook":return t.url?u(`${(n(t.method).trim()||"POST").toUpperCase()} ${t.url}`):"No URL yet";case"email":return t.to?u(`To ${t.to}${t.subject?` - ${t.subject}`:""}`):"No recipient yet";case"notify":return t.title?u(t.title):"No title yet";default:return"Unknown step type - kept as it is"}}export function rolePicker(e,t=[]){const i=new Map((e||[]).filter(o=>o?.name).map(o=>[o.name,o])),l=new Set(t),r=[...i.values()].sort((o,s)=>(o.level??99)-(s.level??99)||String(o.name).localeCompare(String(s.name))).map(o=>({name:o.name,label:o.label||o.name,level:o.level??"",levelText:o.level===void 0||o.level===null?"":`level ${o.level}`,missing:!1,on:l.has(o.name)}));for(const o of t)i.has(o)||r.push({name:o,label:o,level:"",levelText:"",missing:!0,on:!0});return r}export const draftKey=e=>`action-editor.draft.${e||"new"}`;export function draftOf(e,t){const i=Array.isArray(t?.steps)?t.steps:[];return{...e,steps:(Array.isArray(e?.steps)?e.steps:[]).map(l=>{if(l?.type!=="webhook")return l;const{url:r,...o}=l.config||{},s=r?i.findIndex(a=>a?.type==="webhook"&&a.config?.url===r):-1;return{type:l.type,config:{...o,url:""},...s>=0&&{savedUrl:s},...r&&s<0&&{lostUrl:!0}}})}}export function restoreDraft(e,t){const i=Array.isArray(t?.steps)?t.steps:[];let l=0;const r=(Array.isArray(e?.steps)?e.steps:[]).map(o=>{if(o?.type!=="webhook")return o;const{savedUrl:s,lostUrl:a,...c}=o,p=Number.isInteger(s)?i[s]:null,m=p?.type==="webhook"?n(p.config?.url):"";return(a||Number.isInteger(s)&&!m)&&l++,{...c,config:{...c.config||{},url:m}}});return{form:{...t,...e,steps:r},lost:l}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{settingsStore as f}from"./tool-kit.js";import{desktopPayload as p,desktopWorthy as d,normaliseSettings as l,rememberSeen as i}from"./notifications-arrange.js";export const notificationSettings=f("notifications.settings",l);const r="notifications.desktopSeen",s="#/system/notifications";export function desktopPermission(){return typeof window>"u"||!("Notification"in window)||!window.isSecureContext?"unsupported":Notification.permission}export async function requestDesktopPermission(){if(desktopPermission()==="unsupported")return"unsupported";try{return await Notification.requestPermission()}catch{return desktopPermission()}}function c(){try{const t=S.get(r);return Array.isArray(t)?t:null}catch{return null}}function a(t){try{S.set(r,t)}catch{}}export function primeSeen(t){a(i(c()||[],(t||[]).filter(e=>e?.unread).map(e=>e.id)))}function u({title:t,options:e}){try{const n=new Notification(t,e);return n.onclick=()=>{try{window.focus()}catch{}location.hash!==s&&(location.hash=s),n.close()},!0}catch{return!1}}export function desktopActive(){return notificationSettings.read().desktop&&desktopPermission()==="granted"}export function deliverDesktop(t){if(!desktopActive())return 0;const e=c();if(!e)return primeSeen(t),0;const n=d(t,{seen:e,level:notificationSettings.read().desktopLevel});return n.forEach(o=>u(p(o))),a(i(e,(t||[]).filter(o=>o?.unread).map(o=>o.id))),n.length}export function showTestNotification(){return desktopPermission()!=="granted"?!1:u({title:"Desktop notifications are on",options:{body:"New notifications for this site will appear like this.",tag:"dcms-notification-test"}})}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const SEVERITIES=["critical","warning","info","success"],FILTERS=["all","new",...SEVERITIES],SEVERITY_TONE={critical:"danger",warning:"warn",success:"ok",info:"info"};const s=(e,
|
|
1
|
+
export const SEVERITIES=["critical","warning","info","success"],FILTERS=["all","new",...SEVERITIES],SEVERITY_TONE={critical:"danger",warning:"warn",success:"ok",info:"info"};const s=(t,e,r=`${e}s`)=>`${t} ${t===1?e:r}`;export function severityOf(t){const e=t?.data?.severity;return SEVERITIES.includes(e)?e:"info"}export function ago(t,e=Date.now()){const r=new Date(t).getTime();if(!Number.isFinite(r))return"";const n=Math.max(0,Math.round((e-r)/1e3));return n<60?"just now":n<3600?`${Math.round(n/60)} min ago`:n<86400?`${s(Math.round(n/3600),"hour")} ago`:n<86400*14?`${s(Math.round(n/86400),"day")} ago`:new Date(r).toISOString().slice(0,10)}export function arrangeNotifications(t,{filter:e="all",query:r=""}={}){const n=String(r).trim().toLowerCase();return[...t||[]].filter(o=>e==="all"?!0:e==="new"?o.isNew:severityOf(o)===e).filter(o=>!n||[o.data?.title,o.data?.body,o.data?.link].some(i=>String(i||"").toLowerCase().includes(n))).sort((o,i)=>String(i.data?.createdAt||"").localeCompare(String(o.data?.createdAt||"")))}export function filterCounts(t){const e={all:0,new:0,critical:0,warning:0,info:0,success:0};for(const r of t||[])e.all++,r.isNew&&e.new++,e[severityOf(r)]++;return e}export function safeLink(t){const e=String(t||"").trim();return/^https?:\/\/\S+$/i.test(e)||/^#\/\S*$/.test(e)?e:""}export function sourceLabel(t={}){return t.source==="manager"?"from the manager":t.source==="sample"?"sample":t.sourceLabel?String(t.sourceLabel):t.source?String(t.source).startsWith("core:")?"System":String(t.source).split(":")[0]:""}export function notificationRow(t,{now:e=Date.now(),expanded:r=""}={}){const n=t?.data||{},o=severityOf(t),i=String(n.body||""),a=i.replace(/\s+/g," ").trim();return{id:String(t.id),title:String(n.title||"(no title)"),severity:o,severityLabel:o[0].toUpperCase()+o.slice(1),tone:SEVERITY_TONE[o],meta:[n.createdAt?ago(n.createdAt,e):"",sourceLabel(n),/^https?:\/\//i.test(String(n.link||""))?"has a link":"",n.expiresAt&&Date.parse(n.expiresAt)>e?`until ${String(n.expiresAt).slice(0,10)}`:""].filter(Boolean).join(" \xB7 "),preview:a.length>140?`${a.slice(0,139)}\u2026`:a,body:i,link:safeLink(n.link),linkTarget:/^#\//.test(safeLink(n.link))?"_self":"_blank",linkText:/^#\//.test(safeLink(n.link))?"Go there \u2192":"Open link \u2197",isNew:!!t.isNew,unread:!!t.unread,open:r===String(t.id),rowCls:[t.unread?"is-unread":"",r===String(t.id)?"is-open":"",`is-${o}`].filter(Boolean).join(" ")}}const l={boot:"at start-up","15m":"every 15 minutes",hourly:"hourly",daily:"daily at 08:00"};export function sourceRow(t,e=Date.now()){const r=t.every?`${l[t.every]||t.every}${t.atBoot&&t.every!=="boot"?" and at start-up":""}`:"as it happens";return{id:String(t.id),label:String(t.label||t.id),description:String(t.description||""),meta:[t.ownerLabel||t.owner,r,t.lastRun?`last run ${ago(t.lastRun,e)}`:""].filter(Boolean).join(" \xB7 "),enabled:!!t.enabled,audience:t.audience||"super-admin",every:t.every||"",lastError:t.lastError?`Last run failed: ${t.lastError}`:""}}export const DESKTOP_LEVELS=["all","warning","critical"],DEFAULT_SETTINGS=Object.freeze({desktop:!1,desktopLevel:"all",signInToasts:!0});export function normaliseSettings(t){const e=t&&typeof t=="object"?t:{},r=DEFAULT_SETTINGS;return{desktop:typeof e.desktop=="boolean"?e.desktop:r.desktop,desktopLevel:DESKTOP_LEVELS.includes(e.desktopLevel)?e.desktopLevel:r.desktopLevel,signInToasts:typeof e.signInToasts=="boolean"?e.signInToasts:r.signInToasts}}export function meetsLevel(t,e="all"){return e==="critical"?t==="critical":e==="warning"?t==="critical"||t==="warning":!0}export function desktopWorthy(t,{seen:e=[],level:r="all",max:n=3}={}){const o=new Set((e||[]).map(String));return[...t||[]].filter(i=>i&&i.unread&&!o.has(String(i.id))&&meetsLevel(severityOf(i),r)).sort((i,a)=>String(i.data?.createdAt||"").localeCompare(String(a.data?.createdAt||""))).slice(-n)}export function rememberSeen(t,e,r=200){const n=[...(t||[]).map(String)];for(const o of e||[])n.includes(String(o))||n.push(String(o));return n.slice(-r)}export function desktopPayload(t){const e=t?.data||{},r=severityOf(t),n=String(e.body||"").replace(/\s+/g," ").trim();return{title:String(e.title||"Notification").slice(0,120),options:{body:n.length>240?`${n.slice(0,239)}\u2026`:n,tag:`dcms-notification-${t?.id}`,requireInteraction:r==="critical"}}}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{api as p}from"../api.js";import{isPathAllowed as te}from"./admin-scope.js";import{colourToCss as X,applyFloat as ne,applyHighlight as se,itemHighlightAttrs as ie}from"/public/js/menu-decor.mjs?v=20260918-item-highlight";import{placeTools as oe,ensureLockedTools as ae,managePluginsItem as re,stripItemByUrl as le,insertFoldersBeforeSystem as ce,pruneEmptyToolsFolder as de,MANAGE_PLUGINS_URL as K}from"./sidebar-grouping.js";function h(t){return String(t??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}const ue=[{text:"Dashboard",url:"#/",icon:"home"},{text:"Menus",url:"#/menus",icon:"menu",permission:"menus"}];function N(t,s){return s?!t||!t.length?!1:t.includes(s)?!0:["read","create","update","delete"].some(n=>t.includes(`${s}.${n}`)):!0}function V(t,s,n=null){const e=[];for(const i of t){if(i.hidden||i.permission&&!N(s,i.permission))continue;const o=typeof i.url=="string"?i.url:"";if(n&&o.startsWith("#/")&&!te(n,o.slice(1)))continue;const l=Array.isArray(i.items)&&i.items.length>0,a=l?V(i.items,s,n):[];l&&!a.length&&!o||e.push({...i,items:a})}return e}async function me(t){if(!N(t,"projects"))return[];let s=[];try{s=await p.projects.listWithCounts()}catch{return[]}if(!Array.isArray(s))return[];const n=[{key:"pages",text:"Pages",icon:"file-text",path:"/pages"},{key:"collections",text:"Collections",icon:"database",path:"/collections"},{key:"forms",text:"Forms",icon:"layout",path:"/forms"},{key:"actions",text:"Actions",icon:"zap",path:"/actions"},{key:"menus",text:"Menus",icon:"menu",path:"/menus"},{key:"blocks",text:"Blocks",icon:"box",path:"/blocks"},{key:"components",text:"Components",icon:"component",path:"/components"},{key:"views",text:"Views",icon:"eye",path:"/views"},{key:"roles",text:"Roles",icon:"shield",path:"/roles"},{key:"users",text:"Users",icon:"users",path:"/users"},{key:"apis",text:"APIs",icon:"code",path:"/apis"}];return s.map(e=>{const o="#/projects/"+encodeURIComponent(e.slug),l=e.counts||{},a=[{text:"Overview",url:o,icon:e.icon||"folder"}];for(const u of n){const m=Number(l[u.key])||0;m<=0||a.push({text:`${u.text} (${m})`,url:o+u.path,icon:u.icon})}a.push({text:"Settings",url:o+"/settings",icon:"settings"});const c=e.enabled===!1;return{text:e.name||e.slug,icon:e.icon||"folder",items:a,...c&&{colour:"neutral",badge:{text:"off"}}}})}function fe(t,s){for(const n of t)if(!n.hidden&&(n.key==="projects"||!n.key&&(n.text||"").toLowerCase()==="projects")){n.items=Array.isArray(n.items)?n.items.slice():[],n.items.push(...s);return}}function G(t,s){const n=Y.includes(s.tone)?s.tone:"info",e=s.label||String(s.count);s.details?P.set(t,s.details):P.delete(t);const i
|
|
1
|
+
import{api as p}from"../api.js";import{isPathAllowed as te}from"./admin-scope.js";import{colourToCss as X,applyFloat as ne,applyHighlight as se,itemHighlightAttrs as ie}from"/public/js/menu-decor.mjs?v=20260918-item-highlight";import{placeTools as oe,ensureLockedTools as ae,managePluginsItem as re,stripItemByUrl as le,insertFoldersBeforeSystem as ce,pruneEmptyToolsFolder as de,MANAGE_PLUGINS_URL as K}from"./sidebar-grouping.js";function h(t){return String(t??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}const ue=[{text:"Dashboard",url:"#/",icon:"home"},{text:"Menus",url:"#/menus",icon:"menu",permission:"menus"}];function N(t,s){return s?!t||!t.length?!1:t.includes(s)?!0:["read","create","update","delete"].some(n=>t.includes(`${s}.${n}`)):!0}function V(t,s,n=null){const e=[];for(const i of t){if(i.hidden||i.permission&&!N(s,i.permission))continue;const o=typeof i.url=="string"?i.url:"";if(n&&o.startsWith("#/")&&!te(n,o.slice(1)))continue;const l=Array.isArray(i.items)&&i.items.length>0,a=l?V(i.items,s,n):[];l&&!a.length&&!o||e.push({...i,items:a})}return e}async function me(t){if(!N(t,"projects"))return[];let s=[];try{s=await p.projects.listWithCounts()}catch{return[]}if(!Array.isArray(s))return[];const n=[{key:"pages",text:"Pages",icon:"file-text",path:"/pages"},{key:"collections",text:"Collections",icon:"database",path:"/collections"},{key:"forms",text:"Forms",icon:"layout",path:"/forms"},{key:"actions",text:"Actions",icon:"zap",path:"/actions"},{key:"menus",text:"Menus",icon:"menu",path:"/menus"},{key:"blocks",text:"Blocks",icon:"box",path:"/blocks"},{key:"components",text:"Components",icon:"component",path:"/components"},{key:"views",text:"Views",icon:"eye",path:"/views"},{key:"roles",text:"Roles",icon:"shield",path:"/roles"},{key:"users",text:"Users",icon:"users",path:"/users"},{key:"apis",text:"APIs",icon:"code",path:"/apis"}];return s.map(e=>{const o="#/projects/"+encodeURIComponent(e.slug),l=e.counts||{},a=[{text:"Overview",url:o,icon:e.icon||"folder"}];for(const u of n){const m=Number(l[u.key])||0;m<=0||a.push({text:`${u.text} (${m})`,url:o+u.path,icon:u.icon})}a.push({text:"Settings",url:o+"/settings",icon:"settings"});const c=e.enabled===!1;return{text:e.name||e.slug,icon:e.icon||"folder",items:a,...c&&{colour:"neutral",badge:{text:"off"}}}})}function fe(t,s){for(const n of t)if(!n.hidden&&(n.key==="projects"||!n.key&&(n.text||"").toLowerCase()==="projects")){n.items=Array.isArray(n.items)?n.items.slice():[],n.items.push(...s);return}}function G(t,s){const n=Y.includes(s.tone)?s.tone:"info",e=s.label||String(s.count);s.details?P.set(t,s.details):P.delete(t);const i=!s.inventory&&C.some(o=>o.url===t&&!o.inventory)?" data-live":"";return`<span class="sidebar-badge sidebar-badge--${n}" data-count-for="${h(t)}"${i} data-badge-label="${h(e)}" aria-label="${h(e)}">${h(String(s.count))}</span>`}function J(t){for(const s of t.querySelectorAll("details")){const n=s.querySelector(":scope > summary");if(!n)continue;let e=0,i=!1;const o=[];for(const m of s.querySelectorAll(":scope > .sidebar-children .sidebar-badge[data-live]")){const v=Number(m.textContent)||0;v&&(e+=v,i||(i=m.classList.contains("sidebar-badge--danger")),o.push({text:m.closest("a")?.querySelector(".sidebar-text")?.textContent.trim()??"",meta:m.dataset.badgeLabel,tone:m.classList.contains("sidebar-badge--danger")?"danger":void 0}))}const l=`folder:${s.dataset.stateKey||n.textContent.trim()}`;let a=n.querySelector(":scope > .sidebar-badge--sum");if(!e){a?.remove(),P.delete(l);continue}a||(a=document.createElement("span"),n.querySelector(".sidebar-text")?.after(a));const c=n.querySelector(".sidebar-text")?.textContent.trim()||"This folder",u=o.map(m=>`${m.text}: ${m.meta}`).join(" \xB7 ");a.className=`sidebar-badge sidebar-badge--sum sidebar-badge--${i?"danger":"info"}`,a.dataset.countFor=l,a.dataset.badgeLabel=u,a.setAttribute("aria-label",u),a.textContent=String(e),P.set(l,{title:c,items:o})}}const P=new Map;function pe(t,s){if(!t.url||!s)return"";const n=s[t.url];return!n||!(n.count>0)?"":G(t.url,n)}const Y=["info","attention","danger","live"],he={"#/pages":["page","pages"],"#/media":["file in the media library","files in the media library"],"#/menus":["menu","menus"],"#/context-menus":["context menu","context menus"],"#/theme":["element override in the theme","element overrides in the theme"],"#/collections":["collection","collections"],"#/forms":["form","forms"],"#/views":["view","views"],"#/actions":["action","actions"],"#/blocks":["block","blocks"],"#/components":["component","components"],"#/users":["user","users"],"#/plugins":["plugin installed","plugins installed"],"#/system/notifications":["unread notification","unread notifications"]};function U(t,{fallbackLabel:s="",tone:n="info"}={}){const e=Number(typeof t=="object"&&t!==null?t.count:t);if(!Number.isFinite(e)||e<0)return null;const i=typeof t?.label=="string"&&t.label||(s?`${e} - ${s}`:String(e)),o={count:Math.round(e),label:i,tone:Y.includes(t?.tone)?t.tone:n};return t?.details&&typeof t.details=="object"&&(o.details=t.details),t?.inventory===!0&&(o.inventory=!0),o}function ge(t){const s=(t.text||"").replace(/\s*\(\d+\)\s*$/,"").toLowerCase().replace(/\s+/g,"-"),n=(t.url||"").match(/^#\/projects\/([^/]+)/);return`sidebar.expanded.${n?`project-${decodeURIComponent(n[1])}.`:""}${s}`}function Q(t,s){if(t&&t.type==="separator")return'<hr class="sidebar-divider">';if(t&&t.type==="spacer"){const y=["xs","sm","md","lg","xl","flex"].includes(t.size)?t.size:"md";return`<div class="sidebar-spacer sidebar-spacer--${h(y)}" aria-hidden="true"></div>`}const n=t.icon?`<span data-icon="${h(t.icon)}"></span>`:"",e=h(t.text||""),i=t.url?h(t.url):"",o=Array.isArray(t.items)&&t.items.length>0,l=t.colour?X(t.colour):"",a=ie(t),c=[a.hover&&` data-menu-item-hover="${h(a.hover)}"`,a.active&&` data-menu-item-active="${h(a.active)}"`].filter(Boolean).join(""),u=[l&&`color:${h(l)}`,a.accent&&`--dm-menu-highlight:${h(a.accent)}`].filter(Boolean).join(";"),m=u?` style="${u}"`:"";let v="";if(t.badge&&t.badge.text!=null&&t.badge.text!==""){const y=t.badge.variant?X(t.badge.variant):"";v=`<span class="dm-menu-badge"${y?` style="background:${h(y)};color:var(--dm-text-inverse,#fff)"`:""}>${h(String(t.badge.text))}</span>`}if(o){const y=ge(t),k=S.get(y)!==!1,E=t.items.map(w=>Q(w,s)).join("");return`<details data-state-key="${h(y)}"${k?" open":""}>
|
|
2
2
|
<summary${m}>${n} <span class="sidebar-text">${e}</span>${v}</summary>
|
|
3
3
|
<div class="sidebar-children">${E}</div>
|
|
4
4
|
</details>`}return`<a href="${i}" class="sidebar-link" data-url="${i}"${m}${c}>${n} <span class="sidebar-text">${e}</span>${pe(t,s)}${v}</a>`}async function be(t=[],s=[]){const n=d=>d.then(g=>Array.isArray(g)?g.length:Array.isArray(g?.entries)?g.entries.length:0).catch(()=>0);C=xe(t);const e=new Set(C.map(d=>d.url)),i={pages:"#/pages",media:"#/media",menus:"#/menus","context-menus":"#/context-menus",theme:"#/theme",collections:"#/collections",forms:"#/forms",views:"#/views",actions:"#/actions",blocks:"#/blocks",components:"#/components",users:"#/users",plugins:"#/plugins",notifications:"#/system/notifications"},o=(d,g)=>N(s,d)&&!e.has(i[d])?g():Promise.resolve(0),[l,a,c,u,m,v,y,k,E,w,R,B,q,T]=await Promise.all([o("pages",()=>n(p.pages.list())),o("media",()=>n(p.media.list())),o("menus",()=>n(p.menus.list())),o("context-menus",()=>n(p.contextMenus.list())),o("theme",()=>p.theme.get().then(d=>(d?.overrides||[]).length).catch(()=>0)),o("collections",()=>n(p.collections.list())),o("forms",()=>n(p.forms.list())),o("views",()=>n(p.views.list())),o("actions",()=>n(p.actions.list())),o("blocks",()=>n(p.blocks.list())),o("components",()=>n(p.components.list())),o("users",()=>n(p.users.list())),o("plugins",()=>n(p.plugins.list())),o("notifications",()=>p.system?.notifications?.unreadCount?.().then(d=>d?.count??0).catch(()=>0)??Promise.resolve(0))]),j={};await Promise.all(C.map(async d=>{const g=await Z(d);g&&(j[d.url]=g)}));const I={"#/pages":l,"#/media":a,"#/menus":c,"#/context-menus":u,"#/theme":m,"#/collections":v,"#/forms":y,"#/views":k,"#/actions":E,"#/blocks":w,"#/components":R,"#/users":B,"#/plugins":q,"#/system/notifications":T},x={};for(const[d,g]of Object.entries(I)){if(e.has(d))continue;const[L,_]=he[d]??["item","items"],A=U(g);A&&(x[d]={...A,label:`${A.count} ${A.count===1?L:_}`})}return{...x,...j}}let C=[];const ye="dm:sidebar-count",ve=6e4;function xe(t=[]){const s=[],n=e=>{if(e){e.url&&typeof e.countUrl=="string"&&e.countUrl&&s.push({url:e.url,countUrl:e.countUrl,text:e.text||"",countLabel:e.countLabel||"",inventory:e.inventory===!0});for(const i of[...e.children||[],...e.items||[]])n(i)}};for(const e of t??[])n(e?.item??e);return s}async function Z(t){try{const s=new Date,n=`${s.getFullYear()}-${String(s.getMonth()+1).padStart(2,"0")}-${String(s.getDate()).padStart(2,"0")}`,e=t.countUrl+(t.countUrl.includes("?")?"&":"?")+`today=${n}`;return U(await H.get(e),{fallbackLabel:t.countLabel||t.text})}catch{return null}}function ee(t,s,n){const e=[...t.querySelectorAll("a.sidebar-link")].find(o=>o.dataset.url===s);if(!e)return;const i=e.querySelector(".sidebar-badge[data-count-for]");if(!n||!(n.count>0))i?.remove();else{const o=document.createRange();o.selectNodeContents(e);const l=o.createContextualFragment(G(s,n)).firstElementChild;i?i.replaceWith(l):e.querySelector(".sidebar-text")?.after(l)}O.set(s,n),J(t)}const O=new Map;async function W(t,s=null){const n=s?C.filter(e=>e.url===s):C;await Promise.all(n.map(async e=>{const i=await Z(e),o=O.get(e.url);JSON.stringify(o??null)!==JSON.stringify(i)&&ee(t,e.url,i)}))}function $e(t){t.dataset.liveCounts||(t.dataset.liveCounts="1",window.addEventListener(ye,s=>{const n=s.detail||{};if(n.url)if(n.count!==void 0){const e=C.find(i=>i.url===n.url);ee(t,n.url,U(n,{fallbackLabel:e?.countLabel||e?.text||""}))}else W(t,n.url)}),setInterval(()=>{!document.hidden&&document.body.contains(t)&&W(t)},ve),document.addEventListener("visibilitychange",()=>{document.hidden||W(t)}),Se(t))}function Ce(t,s,n){const e=(l,a,c)=>{const u=document.createElement(l);return a&&(u.className=a),c!=null&&(u.textContent=String(c)),u};if(!n){t.textContent=s;return}t.appendChild(e("strong","dm-badge-pop-title",n.title||s)),(n.rows?.length||!n.items?.length)&&t.appendChild(e("p","dm-badge-pop-sub",s));const i=Array.isArray(n.rows)?n.rows.slice(0,6):[];if(i.length){const l=e("dl","dm-badge-pop-rows");for(const a of i)l.appendChild(e("dt",null,a.label)),l.appendChild(e("dd",a.tone==="danger"?"is-danger":null,a.value));t.appendChild(l)}const o=Array.isArray(n.items)?n.items.slice(0,6):[];if(o.length){const l=e("ul","dm-badge-pop-items");for(const a of o){const c=e("li",a.tone==="danger"?"is-danger":null);c.appendChild(e("span","dm-badge-pop-text",a.text)),a.meta&&c.appendChild(e("span","dm-badge-pop-meta",a.meta)),l.appendChild(c)}t.appendChild(l)}else n.empty&&t.appendChild(e("p","dm-badge-pop-empty",n.empty))}function Se(t){let s=null,n=null;const e=()=>{clearTimeout(n),s?.remove(),s=null},i=o=>{e();const l=o?.dataset.badgeLabel;l&&(n=setTimeout(()=>{s=document.createElement("div"),s.className="dm-help-pop dm-badge-pop",s.setAttribute("role","tooltip"),Ce(s,l,P.get(o.dataset.countFor)),document.body.appendChild(s);const a=o.getBoundingClientRect(),c=s.getBoundingClientRect(),u=Math.min(a.right+10,innerWidth-c.width-8);s.style.left=`${Math.max(8,u)}px`,s.style.top=`${Math.max(8,Math.min(a.top+a.height/2-c.height/2,innerHeight-c.height-8))}px`},150))};t.addEventListener("mouseover",o=>{const l=o.target.closest?.(".sidebar-badge[data-badge-label]");l&&i(l)}),t.addEventListener("mouseout",o=>{o.target.closest?.(".sidebar-badge[data-badge-label]")&&e()}),t.addEventListener("focusin",o=>{const l=o.target.closest?.("a, summary")?.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");l?i(l):e()}),t.addEventListener("focusout",e),window.addEventListener("scroll",e,!0),window.addEventListener("hashchange",e)}async function we(t=[]){if(!N(t,"settings"))return"Admin";try{const s=await p.settings.get();return s?.adminBrand?.title||s?.title||"Admin"}catch{return"Admin"}}export async function renderAdminSidebar({mount:t,permissions:s,adminScope:n=null}){const e=$(t).get(0);if(!e)return;let i,o=null,l=null,a=null,c=null,u=null;try{const r=await p.get("/sidebar/menu");i=Array.isArray(r?.items)?r.items:null,o=r?.variant||null,l=r?.position||null,a=r?.style||null,c=r?.float||null,u=r?.highlight||null}catch{i=null}(!i||!i.some(r=>r&&(!r.hidden||r.restricted)))&&(console.warn("[admin-sidebar] No admin-sidebar menu found; using fallback tree"),i=ue.slice());let m=[];try{m=await H.get("/api/sidebar/registered-items")||[]}catch{}i=le(i,K),i=oe(i,m.filter(r=>(r?.item??r)?.url!==K)),i=ae(i),i=ce(i,[re()]);const v=await me(s);v.length&&fe(i,v),i=V(i,s,n),i=de(i);const[y,k]=await Promise.all([be(m.filter(r=>N(s,r?.item?.permission)),s).catch(()=>({})),we(s)]),E=o?` dm-admin-sidebar--${h(o)}`:"";function w(r,b="px"){if(r==null)return r;const f=String(r).trim();return/^\d+(\.\d+)?$/.test(f)?`${f}${b}`:f}let R="";if(a){const r=[];a.fontFamily&&r.push(`font-family: ${a.fontFamily}, sans-serif`),a.fontSize&&r.push(`font-size: ${w(a.fontSize)}`),a.fontWeight&&r.push(`font-weight: ${a.fontWeight}`),a.letterSpacing&&r.push(`letter-spacing: ${w(a.letterSpacing,"em")}`);const b=[];if(r.length&&b.push(`#admin-sidebar .dm-admin-sidebar, #admin-sidebar .dm-admin-sidebar .sidebar-link, #admin-sidebar .dm-admin-sidebar summary, #admin-sidebar .dm-admin-sidebar .sidebar-text { ${r.join("; ")} }`),a.iconSize){const f=w(a.iconSize);b.push(`#admin-sidebar .dm-admin-sidebar [data-icon], #admin-sidebar .dm-admin-sidebar [data-icon] svg { width: ${f} !important; height: ${f} !important; }`)}b.length&&(R=`<style data-admin-sidebar-style>${b.join(" ")}</style>`)}const B=`<div class="dm-admin-sidebar-header"><span data-icon="layout"></span> ${h(k)}</div>`,q=i.some(r=>r&&r.type==="spacer"&&r.size==="flex")?" dm-admin-sidebar--has-flex-spacer":"",T=`${R}<nav class="dm-admin-sidebar${E}${q}">${B}${i.map(r=>Q(r,y)).join("")}</nav>`,j=document.createRange();j.selectNodeContents(e);const I=j.createContextualFragment(T);e.replaceChildren(I),Domma.icons.scan(e);for(const r of C)O.set(r.url,y[r.url]??null);J(e),$e(e);const x=Number.parseInt(S.get("sidebar.width"),10);Number.isFinite(x)&&x>=180&&x<=480&&(e.style.width=x+"px"),e.removeAttribute("data-menu-hover"),e.removeAttribute("data-menu-active"),e.style.removeProperty("--dm-menu-highlight"),se(e,{highlight:u});const d=document.querySelector(".dashboard-wrapper");if(["position","top","bottom","left","right","zIndex","maxHeight","transform"].forEach(r=>{e.style[r]=""}),e.classList.remove("dm-menu-floating"),d&&(d.style.paddingLeft="",d.style.paddingRight=""),l==="floating"){ne(e,{position:"floating",float:c||{}});const b=c&&c.anchor||"TL",f=Number.isFinite(Number(c?.offsetX))?Number(c.offsetX):16,F=e.getBoundingClientRect().width||x||260;if(d){const z=Math.round(F+f*2)+"px";b==="R"||b==="TR"||b==="BR"?d.style.paddingRight=z:d.style.paddingLeft=z}}const g=document.createElement("div");g.className="dm-admin-sidebar-handle",g.setAttribute("aria-label","Resize sidebar"),e.appendChild(g),e.querySelectorAll("details").forEach(r=>{r.addEventListener("toggle",function(){const b=this.getAttribute("data-state-key");b&&S.set(b,this.open);const f=this.querySelector(":scope > .sidebar-children");if(f)if(this.open){const F=f.scrollHeight;f.style.maxHeight="0",requestAnimationFrame(()=>{f.style.maxHeight=F+"px",f.addEventListener("transitionend",function z(){f.style.maxHeight="none",f.removeEventListener("transitionend",z)})})}else{const F=f.scrollHeight;f.style.maxHeight=F+"px",requestAnimationFrame(()=>{f.style.maxHeight="0"})}})});let L=!1,_=0,A=0;g.addEventListener("mousedown",r=>{L=!0,_=r.clientX,A=e.getBoundingClientRect().width,document.body.style.cursor="col-resize",document.body.style.userSelect="none",r.preventDefault()}),e.dataset.resizeWired||(e.dataset.resizeWired="1",document.addEventListener("mousemove",r=>e.__dmResize?.move(r)),document.addEventListener("mouseup",()=>e.__dmResize?.up()),M.subscribe("router:afterChange",()=>e.__dmSetActive?.())),e.__dmResize={move(r){if(!L)return;const b=Math.max(180,Math.min(480,A+(r.clientX-_)));e.style.width=b+"px"},up(){L&&(L=!1,document.body.style.cursor="",document.body.style.userSelect="",S.set("sidebar.width",parseInt(e.style.width,10)))}};function D(){const r=location.hash||"#/";$(e).find(".sidebar-link").removeClass("active"),$(e).find(`.sidebar-link[data-url="${r}"]`).addClass("active")}D(),e.__dmSetActive=D}
|
|
@@ -148,6 +148,7 @@
|
|
|
148
148
|
<option value="moveToCollection">Move to another collection</option>
|
|
149
149
|
<option value="webhook">Call a webhook</option>
|
|
150
150
|
<option value="email">Send an email</option>
|
|
151
|
+
<option value="notify">Notify the admins</option>
|
|
151
152
|
</select>
|
|
152
153
|
<button type="button" class="btn btn-sm btn-primary" data-on-click="addStep"><span data-icon="plus" data-icon-size="14"></span> Add step</button>
|
|
153
154
|
</div>
|
|
@@ -61,6 +61,11 @@
|
|
|
61
61
|
<td>Send an email via the configured SMTP transport</td>
|
|
62
62
|
<td><code>to</code>, <code>subject</code>, <code>template</code></td>
|
|
63
63
|
</tr>
|
|
64
|
+
<tr>
|
|
65
|
+
<td><code>notify</code></td>
|
|
66
|
+
<td>Raise a notification in the admin (the bell, and desktop notifications where switched on). Who receives it is the "Actions" source in Notifications' settings - the admins, unless changed.</td>
|
|
67
|
+
<td><code>title</code>, <code>body</code>, <code>severity</code> (info, success, warning, critical), <code>link</code></td>
|
|
68
|
+
</tr>
|
|
64
69
|
</tbody>
|
|
65
70
|
</table>
|
|
66
71
|
|
|
@@ -272,4 +272,76 @@
|
|
|
272
272
|
border-radius: var(--dm-radius-sm); padding: 0 .35rem; flex-shrink: 0; }
|
|
273
273
|
.fex-kebab:hover { color: var(--dm-primary); }
|
|
274
274
|
#fields-list .fb-field-card:focus-visible { outline: 2px solid var(--dm-primary); outline-offset: 2px; }
|
|
275
|
+
|
|
276
|
+
/* ---- Triggers builder ------------------------------------------------------
|
|
277
|
+
Trigger card > When / Then / Otherwise sections (coloured left edge) > action
|
|
278
|
+
cards (filled, collapsible, a one-line summary in the head) > a labelled grid
|
|
279
|
+
of controls plus an Options line. Every level is visibly one step in from the
|
|
280
|
+
last, which is what the old all-same-border layout lacked. */
|
|
281
|
+
.fb-field-triggers { margin-top: .75rem; border-top: 1px solid var(--dm-border); padding-top: .5rem; }
|
|
282
|
+
.fb-trig-panel-head { display: flex; align-items: center; justify-content: space-between; cursor: pointer; padding: .15rem 0; }
|
|
283
|
+
.fb-trig-panel-title { font-size: var(--dm-text-sm); font-weight: 600; color: var(--dm-text-muted); }
|
|
284
|
+
.fb-triggers-body { padding: .5rem 0 .25rem; }
|
|
285
|
+
.fb-trig-blurb { font-size: var(--dm-text-xs); color: var(--dm-text-muted); margin: 0 0 .85rem; }
|
|
286
|
+
.fb-trig-list { display: flex; flex-direction: column; gap: 1rem; margin-bottom: .6rem; }
|
|
287
|
+
.fb-trig-empty { font-size: var(--dm-text-sm); color: var(--dm-text-muted); margin: 0; }
|
|
288
|
+
.fb-trig-add { margin-top: .5rem; }
|
|
289
|
+
.fb-trig-remove { flex-shrink: 0; line-height: 1; opacity: .6; }
|
|
290
|
+
.fb-trig-remove:hover { opacity: 1; color: var(--dm-danger); }
|
|
291
|
+
|
|
292
|
+
.fb-trig-card { border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md); background: var(--dm-surface);
|
|
293
|
+
padding: .75rem .85rem .9rem; display: flex; flex-direction: column; gap: .85rem; container-type: inline-size; }
|
|
294
|
+
.fb-trig-card-head { display: flex; align-items: center; gap: .75rem; }
|
|
295
|
+
.fb-trig-card-title { flex: 1; font-weight: 700; font-size: var(--dm-text-sm); }
|
|
296
|
+
|
|
297
|
+
.fb-trig-sec { border-left: 3px solid var(--dm-border); padding: .1rem 0 .1rem .8rem; }
|
|
298
|
+
.fb-trig-sec--when { border-left-color: var(--dm-primary); }
|
|
299
|
+
.fb-trig-sec--then { border-left-color: var(--dm-success); }
|
|
300
|
+
.fb-trig-sec--otherwise { border-left-color: color-mix(in srgb, var(--dm-text-muted) 55%, transparent); }
|
|
301
|
+
.fb-trig-sec-head { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-bottom: .55rem; }
|
|
302
|
+
.fb-trig-sec-title { font-size: var(--dm-text-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; }
|
|
303
|
+
.fb-trig-sec-hint { font-size: var(--dm-text-xs); color: var(--dm-text-muted); }
|
|
304
|
+
.fb-trig-sec-head .fb-trig-match { width: auto; flex: 0 1 220px; }
|
|
305
|
+
|
|
306
|
+
.fb-trig-cond-list { display: flex; flex-direction: column; gap: .45rem; }
|
|
307
|
+
.fb-trig-cond-row { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) minmax(0, 1fr) auto; gap: .5rem; align-items: center; }
|
|
308
|
+
.fb-trig-cond-row .fb-trig-cond-val[hidden] { display: block !important; visibility: hidden; }
|
|
309
|
+
|
|
310
|
+
.fb-trig-action-list { display: flex; flex-direction: column; gap: .55rem; }
|
|
311
|
+
.fb-trig-action-row { border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md);
|
|
312
|
+
background: color-mix(in srgb, var(--dm-text) 3%, transparent); }
|
|
313
|
+
.fb-trig-action-row.is-open { background: color-mix(in srgb, var(--dm-text) 5%, transparent); }
|
|
314
|
+
.fb-trig-action-head { display: flex; align-items: center; gap: .55rem; padding: .5rem .6rem; cursor: pointer; border-radius: var(--dm-radius-md); }
|
|
315
|
+
.fb-trig-action-head:focus-visible { outline: 2px solid var(--dm-primary); outline-offset: -2px; }
|
|
316
|
+
.fb-trig-chevron { flex-shrink: 0; color: var(--dm-text-muted); display: inline-flex; }
|
|
317
|
+
.fb-trig-action-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .1rem; }
|
|
318
|
+
.fb-trig-action-main strong { font-size: var(--dm-text-sm); }
|
|
319
|
+
.fb-trig-action-summary { color: var(--dm-text-muted); font-size: var(--dm-text-xs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
320
|
+
.fb-trig-kind { flex-shrink: 0; font-size: var(--dm-text-xs); padding: .1rem .5rem; border-radius: 999px; white-space: nowrap;
|
|
321
|
+
color: var(--dm-text-muted); background: color-mix(in srgb, var(--dm-text) 7%, transparent); }
|
|
322
|
+
.fb-trig-kind--event { color: var(--dm-info); background: color-mix(in srgb, var(--dm-info) 12%, transparent); }
|
|
323
|
+
.fb-trig-action-body { padding: .75rem .75rem .8rem; border-top: 1px solid var(--dm-border); display: flex; flex-direction: column; gap: .75rem; }
|
|
324
|
+
|
|
325
|
+
.fb-trig-params { display: flex; flex-direction: column; gap: .75rem; }
|
|
326
|
+
.fb-trig-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr)); gap: .7rem .9rem; }
|
|
327
|
+
/* A multi-select is a list box, not a dropdown: no chevron, its own height. */
|
|
328
|
+
.fb-trig-p-fields { background-image: none; height: auto; min-height: 6rem; padding: .3rem; }
|
|
329
|
+
.fb-trig-f { display: flex; flex-direction: column; gap: .3rem; min-width: 0; margin: 0; }
|
|
330
|
+
.fb-trig-f--full { grid-column: 1 / -1; }
|
|
331
|
+
.fb-trig-f-label { font-size: var(--dm-text-xs); font-weight: 600; color: var(--dm-text-muted); }
|
|
332
|
+
.fb-trig-f-hint { font-size: var(--dm-text-xs); color: var(--dm-text-muted); }
|
|
333
|
+
.fb-trig-opts { display: flex; flex-wrap: wrap; gap: .5rem 1.25rem; padding-top: .65rem; border-top: 1px dashed var(--dm-border); }
|
|
334
|
+
.fb-trig-check { display: inline-flex; align-items: center; gap: .4rem; font-size: var(--dm-text-sm); cursor: pointer; margin: 0; }
|
|
335
|
+
|
|
336
|
+
.fb-trig-f[hidden], .fb-trig-check[hidden], .fb-trig-action-body[hidden], .fb-triggers-body[hidden] { display: none !important; }
|
|
337
|
+
|
|
338
|
+
/* Keyed on the card's own width, not the window's: the field panel can be
|
|
339
|
+
narrow on a wide screen (a resized slideover, a two-column editor). */
|
|
340
|
+
@container (max-width: 460px) {
|
|
341
|
+
.fb-trig-cond-row { grid-template-columns: minmax(0, 1fr) auto; }
|
|
342
|
+
.fb-trig-cond-row > .form-select, .fb-trig-cond-row > .form-input { grid-column: 1; }
|
|
343
|
+
.fb-trig-cond-row > .fb-trig-remove { grid-column: 2; grid-row: 1; }
|
|
344
|
+
.fb-trig-cond-row .fb-trig-cond-val[hidden] { display: none !important; }
|
|
345
|
+
.fb-trig-kind { display: none; }
|
|
346
|
+
}
|
|
275
347
|
</style>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<div class="ntx">
|
|
5
5
|
<p class="ntx-note">Messages for this site's administrators - maintenance, updates and warnings, mostly from the
|
|
6
6
|
manager that hosts it.
|
|
7
|
-
<span data-help-title="Notifications" data-help="Opening this screen marks everything in it as read - the ones that were unread stay marked New until you leave. Click a notification to read all of it; right-click it (or ⋮, or Shift+F10) to open its link, copy it, mark it unread again, dismiss it or delete it. Dismissing hides it for you only; deleting removes it for everyone and needs the delete permission. A notification with an end date disappears by itself once it passes."></span></p>
|
|
7
|
+
<span data-help-title="Notifications" data-help="Opening this screen marks everything in it as read - the ones that were unread stay marked New until you leave. Click a notification to read all of it; right-click it (or ⋮, or Shift+F10) to open its link, copy it, mark it unread again, dismiss it or delete it. Dismissing hides it for you only; deleting removes it for everyone and needs the delete permission. A notification with an end date disappears by itself once it passes. The cog in the banner turns on desktop notifications for this browser."></span></p>
|
|
8
8
|
|
|
9
9
|
<div class="ntx-card">
|
|
10
10
|
<div class="ntx-bar">
|
|
@@ -70,5 +70,25 @@
|
|
|
70
70
|
.ntx-chip.is-new { background: var(--dm-primary); color: var(--dm-primary-text); }
|
|
71
71
|
.ntx-kebab { border: 0; background: none; cursor: pointer; font-size: var(--dm-text-lg); color: var(--dm-text-muted); border-radius: var(--dm-radius-sm); padding: 0 .3rem; }
|
|
72
72
|
.ntx-kebab:hover { color: var(--dm-primary); }
|
|
73
|
+
/* The settings slideover (it sits outside .ntx, in the page body). */
|
|
74
|
+
.ntx-panel { display: flex; flex-direction: column; gap: 1rem; padding: .25rem 0 .5rem; }
|
|
75
|
+
.ntx-panel [hidden] { display: none !important; }
|
|
76
|
+
.ntx-panel .form-label { display: flex; align-items: center; font-weight: 500; margin-bottom: .3rem; }
|
|
77
|
+
.ntx-setting-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
|
|
78
|
+
.ntx-panel-meta { font-size: var(--dm-text-xs); color: var(--dm-text-muted); margin-top: -.6rem; }
|
|
79
|
+
.ntx-panel-meta.is-bad { color: var(--dm-danger); }
|
|
80
|
+
.ntx-panel-actions { display: flex; align-items: center; gap: .5rem; padding-top: .75rem; border-top: 1px solid var(--dm-border); }
|
|
81
|
+
.ntx-panel-actions .spacer { flex: 1; }
|
|
82
|
+
.ntx-panel-h { display: flex; align-items: center; gap: .3rem; margin: .5rem 0 0; font-size: var(--dm-text-base); font-weight: 600; }
|
|
83
|
+
.ntx-sources { display: flex; flex-direction: column; gap: .5rem; border-top: 1px solid var(--dm-border); padding-top: .75rem; }
|
|
84
|
+
.ntx-src-list { display: flex; flex-direction: column; margin-top: .6rem; }
|
|
85
|
+
.ntx-src { display: flex; flex-direction: column; gap: .25rem; padding: .55rem 0; border-bottom: 1px solid var(--dm-border); }
|
|
86
|
+
.ntx-src:last-child { border-bottom: 0; }
|
|
87
|
+
.ntx-src-head { display: flex; align-items: flex-start; justify-content: space-between; gap: .75rem; }
|
|
88
|
+
.ntx-src-name { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
|
|
89
|
+
.ntx-src-name small, .ntx-src-desc { color: var(--dm-text-muted); font-size: var(--dm-text-xs); }
|
|
90
|
+
.ntx-src-foot { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
|
|
91
|
+
.ntx-src-aud { max-width: 18rem; padding-top: .2rem; padding-bottom: .2rem; font-size: var(--dm-text-sm); }
|
|
92
|
+
.ntx-src-err { color: var(--dm-danger); font-size: var(--dm-text-xs); }
|
|
73
93
|
@media (max-width: 640px) { .ntx-preview { white-space: normal; } }
|
|
74
94
|
</style>
|