domma-cms 1.6.15 → 1.6.16
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/js/app.js +1 -1
- package/admin/js/lib/effect-defs.js +1 -1
- package/admin/js/lib/layouts-arrange.js +1 -1
- package/admin/js/lib/project-page-arrange.js +1 -1
- package/admin/js/lib/scribe-composer.js +1 -4
- package/admin/js/lib/scribe-model.js +4 -0
- package/admin/js/lib/sidebar-grouping.js +1 -1
- package/admin/js/lib/sidebar-renderer.js +2 -2
- package/admin/js/templates/docs/usage-navigation.html +4 -2
- package/admin/js/templates/layouts.html +1 -1
- package/admin/js/views/layouts.js +8 -8
- package/package.json +1 -1
- package/server/services/markdown.js +11 -5
- package/server/services/permissionRegistry.js +1 -1
- package/server/services/sidebar-migration.js +14 -1
package/admin/js/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{views as G}from"./views/index.js";import{withPluginChrome as B}from"./lib/plugin-chrome.js";import{api as c,getUser as F,isAuthenticated as m,logout as z,sendToLogin as K}from"./api.js";import{isAuthScreen as C,loginHashFor as J}from"./lib/login-arrange.js";import{installHttpInterceptor as Y}from"./http-interceptor.js";import{installResizableSlideovers as Q}from"./lib/slideover-resizable.js";import{installActivityThrobber as X}from"./lib/activity-throbber.js";import{isPathAllowed as Z,redirectFor as tt}from"./lib/admin-scope.js";import{deliverDesktop as et,desktopActive as it,notificationSettings as ot}from"./lib/desktop-notify.js";import{avatarUrl as at}from"./lib/avatar.js";import{installCalculator as st}from"./lib/calculator.js";import{takeOverviewPass as nt,withDashboardStrip as O}from"./lib/dashboards-strip.js";import{installHotkeys as lt}from"./lib/hotkeys-ui.js";$(()=>{Y(),st(),X(),Q(),(async()=>{const{applyAdminTheme:t,refreshAdminTheme:i}=await import("./lib/admin-theme.js");m()?await i(c,{init:!0}):t({dommaTheme:"charcoal-dark"},{init:!0})})();let p=null,r="/",h=!1,w=null;function b(t){if(y[t])return E.toast(`${y[t]} is switched off on this site.`,{type:"info"}),"/";if(t.split("?")[0]==="/my-dashboard")return"/dashboards";const i=D[t]||tt(p,t);return i||((t.split("?")[0]||"/")==="/"&&r&&r!=="/"&&!nt()?D[r]||r:null)}const D={},y={};R.use(async(t,i,e)=>{if(C(t.path))return e();if(!m()){K();return}const o=b(t.path);if(o){R.navigate(o);return}e()});async function g(){try{const t=await c.get("/auth/permissions");p=t.adminScope||null,r=t.adminHome||"/";const i=t.permissions||[];return h=Z(p,"/settings")&&i.some(e=>e==="settings"||e.startsWith("settings.")),w=i.some(e=>e==="notifications"||e.startsWith("notifications.")),i}catch{return p=null,r="/",h=!1,w=null,[]}}document.addEventListener("dm:admin-home-changed",()=>{g()});let P=null,N=!1;document.addEventListener("dm:sidebar-rebuild",()=>{if(P){N=!0;return}P=(async()=>{do{N=!1;try{await A(await g())}catch{}}while(N)})().finally(()=>{P=null})});async function A(t){const{renderAdminSidebar:i}=await import("./lib/sidebar-renderer.js");await i({mount:"#admin-sidebar",permissions:t,adminScope:p})}M.subscribe("router:afterChange",({to:t})=>{C(t.path)&&(u&&(clearInterval(u),u=null),p=null,r="/",h=!1,w=null)});const _=[{path:"/",view:"dashboard",title:"Dashboard - Domma CMS"},{path:"/dashboards",view:"dashboards",title:"Dashboards - Domma CMS"},{path:"/my-dashboard",view:"dashboards",title:"Dashboards - Domma CMS"},{path:"/pages",view:"pages",title:"Pages - Domma CMS"},{path:"/pages/new",view:"pageEditor",title:"New Page - Domma CMS"},{path:"/pages/edit/*",view:"pageEditor",title:"Edit Page - Domma CMS"},{path:"/media",view:"media",title:"Media - Domma CMS"},{path:"/menus",view:"menus",title:"Menus - Domma CMS"},{path:"/context-menus",view:"contextMenus",title:"Context Menus - Domma CMS"},{path:"/context-menus/new",view:"contextMenuEditor",title:"New Context Menu - Domma CMS"},{path:"/context-menus/edit/:slug",view:"contextMenuEditor",title:"Edit Context Menu - Domma CMS"},{path:"/menus/new",view:"menuEditor",title:"New Menu - Domma CMS"},{path:"/menus/edit/:slug",view:"menuEditor",title:"Edit Menu - Domma CMS"},{path:"/menu-locations",view:"menuLocations",title:"Menu Locations - Domma CMS"},{path:"/navigation",view:"menusRedirect",title:"Menus - Domma CMS"},{path:"/layouts",view:"layouts",title:"Layouts - Domma CMS"},{path:"/settings",view:"settings",title:"Settings - Domma CMS"},{path:"/theme",view:"theme",title:"Theme - Domma CMS"},{path:"/users",view:"users",title:"Users - Domma CMS"},{path:"/users/new",view:"userEditor",title:"New User - Domma CMS"},{path:"/users/edit/:id",view:"userEditor",title:"Edit User - Domma CMS"},{path:"/plugins",view:"plugins",title:"Plugins - Domma CMS"},{path:"/plugins/code/:name",view:"pluginCode",title:"Plugin Code - Domma CMS"},{path:"/documentation",view:"documentation",title:"Usage - Domma CMS"},{path:"/tutorials",view:"tutorials",title:"Tutorials - Domma CMS"},{path:"/tutorials/crud",view:"tutorialCrud",title:"Building a CRUD App - Domma CMS"},{path:"/tutorials/plugin",view:"tutorialPlugin",title:"Writing a 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/usage/dashboard",view:"usageDashboard",title:"Dashboard - Usage - Domma CMS"},{path:"/docs/plugins",view:"pluginGuides",title:"Plugin Guides - Domma CMS"},{path:"/docs/plugins/*",view:"pluginGuide",title:"Plugin Guide - Domma CMS"},{path:"/docs/api/authentication",view:"apiRefAuthentication",title:"Authentication - API - Domma CMS"},{path:"/docs/api/pages",view:"apiRefPages",title:"Pages - API - Domma CMS"},{path:"/docs/api/settings",view:"apiRefSettings",title:"Settings - API - Domma CMS"},{path:"/docs/api/layouts",view:"apiRefLayouts",title:"Layouts - API - Domma CMS"},{path:"/docs/api/navigation",view:"apiRefNavigation",title:"Navigation - API - Domma CMS"},{path:"/docs/api/media",view:"apiRefMedia",title:"Media - API - Domma CMS"},{path:"/docs/api/users",view:"apiRefUsers",title:"Users - API - Domma CMS"},{path:"/docs/api/plugins",view:"apiRefPlugins",title:"Plugins - API - Domma CMS"},{path:"/docs/api/collections",view:"apiRefCollections",title:"Collections - API - Domma CMS"},{path:"/docs/api/views",view:"apiRefViews",title:"Views API - Domma CMS"},{path:"/docs/api/actions",view:"apiRefActions",title:"Actions API - Domma CMS"},{path:"/docs/api/forms",view:"apiRefForms",title:"Forms API - Domma CMS"},{path:"/docs/api/scaffold",view:"apiRefScaffold",title:"Scaffold API - Domma CMS"},{path:"/docs/api/external",view:"apiRefExternal",title:"External API & Tokens - Domma CMS"},{path:"/docs/api/content",view:"apiRefContent",title:"Content API - Domma CMS"},{path:"/docs/api/builder",view:"apiRefBuilder",title:"API Builder - Domma CMS"},{path:"/api-reference",view:"apiReference",title:"API Reference - Domma CMS"},{path:"/collections",view:"collections",title:"Collections - Domma CMS"},{path:"/collections/new",view:"collectionEditor",title:"New Collection - Domma CMS"},{path:"/collections/edit/:slug",view:"collectionEditor",title:"Edit Collection - Domma CMS"},{path:"/collections/:slug/entries",view:"collectionEntries",title:"Entries - Domma CMS"},{path:"/forms",view:"forms",title:"Forms - Domma CMS"},{path:"/forms/new",view:"formEditor",title:"New Form - Domma CMS"},{path:"/forms/edit/:slug",view:"formEditor",title:"Edit Form - Domma CMS"},{path:"/forms/:slug/submissions",view:"formSubmissions",title:"Submissions - Domma CMS"},{path:"/views",view:"viewsList",title:"Views - Domma CMS"},{path:"/views/new",view:"viewEditor",title:"New View - Domma CMS"},{path:"/views/edit/:slug",view:"viewEditor",title:"Edit View - Domma CMS"},{path:"/views/:slug/preview",view:"viewPreview",title:"View Preview - Domma CMS"},{path:"/actions",view:"actionsList",title:"Actions - Domma CMS"},{path:"/actions/new",view:"actionEditor",title:"New Action - Domma CMS"},{path:"/actions/edit/:slug",view:"actionEditor",title:"Edit Action - Domma CMS"},{path:"/pro/docs",view:"proDocs",title:"Pro Documentation - Domma CMS"},{path:"/blocks",view:"blocks",title:"Blocks - Domma CMS"},{path:"/blocks/new",view:"blockEditor",title:"New Block - Domma CMS"},{path:"/blocks/edit/:name",view:"blockEditor",title:"Edit Block - Domma CMS"},{path:"/components",view:"components",title:"Components - Domma CMS"},{path:"/components/new",view:"componentEditor",title:"New Component - Domma CMS"},{path:"/components/edit/:name",view:"componentEditor",title:"Edit Component - Domma CMS"},{path:"/my-profile",view:"myProfile",title:"My Profile - Domma CMS"},{path:"/roles",view:"roles",title:"Roles & Permissions - Domma CMS"},{path:"/roles/edit/:id",view:"roleEditor",title:"Edit Role - Domma CMS"},{path:"/effects",view:"effects",title:"Effects - Domma CMS"},{path:"/search",view:"search",title:"Search - Domma CMS"},{path:"/content-api",view:"contentApi",title:"Content API - Domma CMS"},{path:"/notes",view:"notes",title:"Notes - Domma CMS"},{path:"/todo",view:"todo",title:"Todo - Domma CMS"},{path:"/calculator",view:"calculator",title:"Calculator - Domma CMS"},{path:"/analytics",view:"analytics",title:"Analytics - Domma CMS"},{path:"/seo",view:"seo",title:"SEO - Domma CMS"},{path:"/seo/:tab",view:"seo",title:"SEO - Domma CMS"},{path:"/contacts",view:"contacts",title:"Contacts - Domma CMS"},{path:"/calls/new",view:"callNew",title:"Call now - Domma CMS"},{path:"/system/notifications",view:"notifications",title:"Notifications - Domma CMS"},{path:"/api-tokens",view:"apiTokens",title:"API Tokens - Domma CMS"},{path:"/api-endpoints",view:"apiEndpoints",title:"API Builder - Domma CMS"},{path:"/api-endpoints/new",view:"apiEndpointEditor",title:"New API Endpoint - Domma CMS"},{path:"/api-endpoints/edit/:id",view:"apiEndpointEditor",title:"Edit API Endpoint - Domma CMS"},{path:"/projects",view:"projects",title:"Projects - Domma CMS"},{path:"/projects/new",view:"projects",title:"New Project - Domma CMS"},{path:"/projects/edit/:slug",view:"projects",title:"Edit Project - Domma CMS"},{path:"/projects/:slug/settings",view:"projectSettings",title:"Project settings - Domma CMS"},{path:"/projects/:slug/pages",view:"pages",title:"Project pages - Domma CMS"},{path:"/projects/:slug/collections",view:"collections",title:"Project collections - Domma CMS"},{path:"/projects/:slug/forms",view:"forms",title:"Project forms - Domma CMS"},{path:"/projects/:slug/actions",view:"actionsList",title:"Project actions - Domma CMS"},{path:"/projects/:slug/menus",view:"menus",title:"Project menus - Domma CMS"},{path:"/projects/:slug/blocks",view:"blocks",title:"Project blocks - Domma CMS"},{path:"/projects/:slug/components",view:"components",title:"Project components - Domma CMS"},{path:"/projects/:slug/views",view:"viewsList",title:"Project views - Domma CMS"},{path:"/projects/:slug/roles",view:"roles",title:"Project roles - Domma CMS"},{path:"/projects/:slug/users",view:"users",title:"Project users - Domma CMS"},{path:"/projects/:slug/apis",view:"apiEndpoints",title:"Project APIs - Domma CMS"},{path:"/projects/:slug",view:"projectDetail",title:"Project - Domma CMS"},{path:"/login",view:"login",title:"Sign in - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}},{path:"/reset-password",view:"login",title:"Reset Password - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}}];M.subscribe("router:afterChange",async({to:t,from:i})=>{if(!C(t.path)){if($("#admin-sidebar").show(),$("#admin-topbar").show(),C(i?.path)){$("#topbar-user-name").remove(),import("./lib/admin-theme.js").then(a=>a.refreshAdminTheme(c)),await I();const e=await g(),o=b(t.path);if(o&&R.navigate(o),A(e),ot.read().signInToasts)try{const a=await c.system.notifications.list().catch(()=>[]),n=(Array.isArray(a)?a:[]).filter(s=>s.unread&&["warning","critical"].includes(s.data?.severity)).slice(0,3);for(const s of n){const f=d(s.data?.title||""),U=d((s.data?.body||"").slice(0,120));E.toast(`${f} - ${U}`,{type:s.data?.severity==="critical"?"error":"warning",duration:0})}}catch{}}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 i=t?String(t.path).split("?")[0]:"";if(!t||!W(i))return;const o=new URLSearchParams(location.hash.split("?")[1]||"").get("project");if(!o||!/\/new$/.test(i))return;let a=0;const n=60,s=1500,f=()=>{a+=n;for(const U of V){const v=document.querySelector(U);if(!v)continue;if(Array.from(v.options||[]).find(q=>q.value===o)){v.value=o,v.dispatchEvent(new Event("change",{bubbles:!0}));return}}a<s&&setTimeout(f,n)};setTimeout(f,n)});const x="cms_card_states",j=S.get(x)||{};M.subscribe("router:afterChange",()=>{setTimeout(()=>{$("#view-container .card-collapsible").each(function(){const t=$(this).find(".card-header h2, .card-header h3").first().text().trim();t&&j[t]==="collapsed"&&$(this).addClass("card-collapsed")})},200)}),$("#view-container").on("click",".card-collapsible .card-header",function(t){if($(t.target).closest("button, a").length)return;const i=$(this).closest(".card");i.toggleClass("card-collapsed");const e=$(this).find("h2, h3").first().text().trim();e&&(j[e]=i.hasClass("card-collapsed")?"collapsed":"open",S.set(x,j))}),lt(),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash.startsWith("#/login")||window.location.hash.startsWith("#/reset-password"))return;const i=document.querySelector("#view-container .view-header button.btn-primary");i&&(t.preventDefault(),i.click())});const l={...G},H={notes:{displayName:"Notes",icon:"edit-3"},todo:{displayName:"Todo",icon:"check-square"},calculator:{displayName:"Calculator",icon:"calculator"},analytics:{displayName:"Analytics",icon:"chart-bar"},seo:{displayName:"SEO",icon:"search"},contacts:{displayName:"Contacts",icon:"users"},projects:{displayName:"Projects",icon:"folder"},pages:{displayName:"Pages",icon:"file-text"},media:{displayName:"Media",icon:"image"},menus:{displayName:"Menus",icon:"menu"},contextMenus:{displayName:"Context Menus",icon:"menu"},theme:{displayName:"Theme",icon:"droplet"},collections:{displayName:"Collections",icon:"database"},forms:{displayName:"Forms",icon:"layout"},viewsList:{displayName:"Views",icon:"eye"},actionsList:{displayName:"Actions",icon:"zap"},apiEndpoints:{displayName:"API Builder",icon:"code"},blocks:{displayName:"Blocks",icon:"box"},components:{displayName:"Components",icon:"component"},notifications:{displayName:"Notifications",icon:"bell"},roles:{displayName:"Roles",icon:"shield"},users:{displayName:"Users",icon:"users"},settings:{displayName:"Site Settings",icon:"settings"},effects:{displayName:"Effects",icon:"sparkles"},layouts:{displayName:"Layouts",icon:"layout"},plugins:{displayName:"Marketplace",icon:"store"},myProfile:{displayName:"My Profile",icon:"user"},apiTokens:{displayName:"API Tokens",icon:"key"},search:{displayName:"Search",icon:"search"},contentApi:{displayName:"Content API",icon:"file-text"},dashboard:{displayName:"Dashboard",icon:"home"},dashboards:{displayName:"Dashboards",icon:"home"},pageEditor:{displayName:"Pages",section:"Edit",icon:"file-text"},menuEditor:{displayName:"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,i]of Object.entries(H))l[t]&&(l[t]=B(l[t],{plugin:t,licence:"Built in",...i}));l.dashboard=O(l.dashboard);const k=[..._];async function I(){if(m())try{const t=await c.plugins.adminConfig(),i=new Set(k.map(e=>e.path));for(const e of t.routes||[])i.has(e.path)||k.push(e);for(const[e,o]of Object.entries(t.views||{}))try{const a=await import(`/plugins/${o.entry}`);l[e]=B(a[o.exportName],(t.meta||{})[e])}catch{}l.dashboard=O(l.dashboard);for(const[e,{route:o}]of Object.entries(t.takeovers||{}))o&&(D[`/${e}`]=o);for(const[e,{enabled:o,displayName:a}]of Object.entries(t.tools||{}))o===!1&&(y[`/${e}`]=a);for(const{id:e,href:o}of t.css||[])if(!document.getElementById(e)){const a=document.createElement("link");a.id=e,a.rel="stylesheet",a.href=o,document.head.appendChild(a)}}catch{}}function T(){const t=F();if(!t||$("#topbar-user-name").length)return;const e={"super-admin":"Super Admin",admin:"Admin",user:"User"}[t.role]||t.role,o=at(t);$("#topbar-user").html(`
|
|
1
|
+
import{views as G}from"./views/index.js";import{withPluginChrome as B}from"./lib/plugin-chrome.js";import{api as c,getUser as F,isAuthenticated as m,logout as z,sendToLogin as K}from"./api.js";import{isAuthScreen as C,loginHashFor as J}from"./lib/login-arrange.js";import{installHttpInterceptor as Y}from"./http-interceptor.js";import{installResizableSlideovers as Q}from"./lib/slideover-resizable.js";import{installActivityThrobber as X}from"./lib/activity-throbber.js";import{isPathAllowed as Z,redirectFor as tt}from"./lib/admin-scope.js";import{deliverDesktop as et,desktopActive as it,notificationSettings as ot}from"./lib/desktop-notify.js";import{avatarUrl as at}from"./lib/avatar.js";import{installCalculator as st}from"./lib/calculator.js";import{takeOverviewPass as nt,withDashboardStrip as O}from"./lib/dashboards-strip.js";import{installHotkeys as lt}from"./lib/hotkeys-ui.js";$(()=>{Y(),st(),X(),Q(),(async()=>{const{applyAdminTheme:t,refreshAdminTheme:i}=await import("./lib/admin-theme.js");m()?await i(c,{init:!0}):t({dommaTheme:"charcoal-dark"},{init:!0})})();let p=null,r="/",h=!1,w=null;function b(t){if(y[t])return E.toast(`${y[t]} is switched off on this site.`,{type:"info"}),"/";if(t.split("?")[0]==="/my-dashboard")return"/dashboards";const i=D[t]||tt(p,t);return i||((t.split("?")[0]||"/")==="/"&&r&&r!=="/"&&!nt()?D[r]||r:null)}const D={},y={};R.use(async(t,i,e)=>{if(C(t.path))return e();if(!m()){K();return}const o=b(t.path);if(o){R.navigate(o);return}e()});async function g(){try{const t=await c.get("/auth/permissions");p=t.adminScope||null,r=t.adminHome||"/";const i=t.permissions||[];return h=Z(p,"/settings")&&i.some(e=>e==="settings"||e.startsWith("settings.")),w=i.some(e=>e==="notifications"||e.startsWith("notifications.")),i}catch{return p=null,r="/",h=!1,w=null,[]}}document.addEventListener("dm:admin-home-changed",()=>{g()});let P=null,N=!1;document.addEventListener("dm:sidebar-rebuild",()=>{if(P){N=!0;return}P=(async()=>{do{N=!1;try{await A(await g())}catch{}}while(N)})().finally(()=>{P=null})});async function A(t){const{renderAdminSidebar:i}=await import("./lib/sidebar-renderer.js");await i({mount:"#admin-sidebar",permissions:t,adminScope:p})}M.subscribe("router:afterChange",({to:t})=>{C(t.path)&&(u&&(clearInterval(u),u=null),p=null,r="/",h=!1,w=null)});const _=[{path:"/",view:"dashboard",title:"Dashboard - Domma CMS"},{path:"/dashboards",view:"dashboards",title:"Dashboards - Domma CMS"},{path:"/my-dashboard",view:"dashboards",title:"Dashboards - Domma CMS"},{path:"/pages",view:"pages",title:"Pages - Domma CMS"},{path:"/pages/new",view:"pageEditor",title:"New Page - Domma CMS"},{path:"/pages/edit/*",view:"pageEditor",title:"Edit Page - Domma CMS"},{path:"/media",view:"media",title:"Media - Domma CMS"},{path:"/menus",view:"menus",title:"Menus - Domma CMS"},{path:"/context-menus",view:"contextMenus",title:"Context Menus - Domma CMS"},{path:"/context-menus/new",view:"contextMenuEditor",title:"New Context Menu - Domma CMS"},{path:"/context-menus/edit/:slug",view:"contextMenuEditor",title:"Edit Context Menu - Domma CMS"},{path:"/menus/new",view:"menuEditor",title:"New Menu - Domma CMS"},{path:"/menus/edit/:slug",view:"menuEditor",title:"Edit Menu - Domma CMS"},{path:"/menu-locations",view:"menuLocations",title:"Menu Locations - Domma CMS"},{path:"/navigation",view:"menusRedirect",title:"Menus - Domma CMS"},{path:"/layouts",view:"layouts",title:"Layouts - Domma CMS"},{path:"/settings",view:"settings",title:"Settings - Domma CMS"},{path:"/theme",view:"theme",title:"Theme - Domma CMS"},{path:"/users",view:"users",title:"Users - Domma CMS"},{path:"/users/new",view:"userEditor",title:"New User - Domma CMS"},{path:"/users/edit/:id",view:"userEditor",title:"Edit User - Domma CMS"},{path:"/plugins",view:"plugins",title:"Plugins - Domma CMS"},{path:"/plugins/code/:name",view:"pluginCode",title:"Plugin Code - Domma CMS"},{path:"/documentation",view:"documentation",title:"Usage - Domma CMS"},{path:"/tutorials",view:"tutorials",title:"Tutorials - Domma CMS"},{path:"/tutorials/crud",view:"tutorialCrud",title:"Building a CRUD App - Domma CMS"},{path:"/tutorials/plugin",view:"tutorialPlugin",title:"Writing a 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/usage/dashboard",view:"usageDashboard",title:"Dashboard - Usage - Domma CMS"},{path:"/docs/plugins",view:"pluginGuides",title:"Plugin Guides - Domma CMS"},{path:"/docs/plugins/*",view:"pluginGuide",title:"Plugin Guide - Domma CMS"},{path:"/docs/api/authentication",view:"apiRefAuthentication",title:"Authentication - API - Domma CMS"},{path:"/docs/api/pages",view:"apiRefPages",title:"Pages - API - Domma CMS"},{path:"/docs/api/settings",view:"apiRefSettings",title:"Settings - API - Domma CMS"},{path:"/docs/api/layouts",view:"apiRefLayouts",title:"Layouts - API - Domma CMS"},{path:"/docs/api/navigation",view:"apiRefNavigation",title:"Navigation - API - Domma CMS"},{path:"/docs/api/media",view:"apiRefMedia",title:"Media - API - Domma CMS"},{path:"/docs/api/users",view:"apiRefUsers",title:"Users - API - Domma CMS"},{path:"/docs/api/plugins",view:"apiRefPlugins",title:"Plugins - API - Domma CMS"},{path:"/docs/api/collections",view:"apiRefCollections",title:"Collections - API - Domma CMS"},{path:"/docs/api/views",view:"apiRefViews",title:"Views API - Domma CMS"},{path:"/docs/api/actions",view:"apiRefActions",title:"Actions API - Domma CMS"},{path:"/docs/api/forms",view:"apiRefForms",title:"Forms API - Domma CMS"},{path:"/docs/api/scaffold",view:"apiRefScaffold",title:"Scaffold API - Domma CMS"},{path:"/docs/api/external",view:"apiRefExternal",title:"External API & Tokens - Domma CMS"},{path:"/docs/api/content",view:"apiRefContent",title:"Content API - Domma CMS"},{path:"/docs/api/builder",view:"apiRefBuilder",title:"API Builder - Domma CMS"},{path:"/api-reference",view:"apiReference",title:"API Reference - Domma CMS"},{path:"/collections",view:"collections",title:"Collections - Domma CMS"},{path:"/collections/new",view:"collectionEditor",title:"New Collection - Domma CMS"},{path:"/collections/edit/:slug",view:"collectionEditor",title:"Edit Collection - Domma CMS"},{path:"/collections/:slug/entries",view:"collectionEntries",title:"Entries - Domma CMS"},{path:"/forms",view:"forms",title:"Forms - Domma CMS"},{path:"/forms/new",view:"formEditor",title:"New Form - Domma CMS"},{path:"/forms/edit/:slug",view:"formEditor",title:"Edit Form - Domma CMS"},{path:"/forms/:slug/submissions",view:"formSubmissions",title:"Submissions - Domma CMS"},{path:"/views",view:"viewsList",title:"Views - Domma CMS"},{path:"/views/new",view:"viewEditor",title:"New View - Domma CMS"},{path:"/views/edit/:slug",view:"viewEditor",title:"Edit View - Domma CMS"},{path:"/views/:slug/preview",view:"viewPreview",title:"View Preview - Domma CMS"},{path:"/actions",view:"actionsList",title:"Actions - Domma CMS"},{path:"/actions/new",view:"actionEditor",title:"New Action - Domma CMS"},{path:"/actions/edit/:slug",view:"actionEditor",title:"Edit Action - Domma CMS"},{path:"/pro/docs",view:"proDocs",title:"Pro Documentation - Domma CMS"},{path:"/blocks",view:"blocks",title:"Blocks - Domma CMS"},{path:"/blocks/new",view:"blockEditor",title:"New Block - Domma CMS"},{path:"/blocks/edit/:name",view:"blockEditor",title:"Edit Block - Domma CMS"},{path:"/components",view:"components",title:"Components - Domma CMS"},{path:"/components/new",view:"componentEditor",title:"New Component - Domma CMS"},{path:"/components/edit/:name",view:"componentEditor",title:"Edit Component - Domma CMS"},{path:"/my-profile",view:"myProfile",title:"My Profile - Domma CMS"},{path:"/roles",view:"roles",title:"Roles & Permissions - Domma CMS"},{path:"/roles/edit/:id",view:"roleEditor",title:"Edit Role - Domma CMS"},{path:"/effects",view:"effects",title:"Effects - Domma CMS"},{path:"/search",view:"search",title:"Search - Domma CMS"},{path:"/content-api",view:"contentApi",title:"Content API - Domma CMS"},{path:"/notes",view:"notes",title:"Notes - Domma CMS"},{path:"/todo",view:"todo",title:"Todo - Domma CMS"},{path:"/calculator",view:"calculator",title:"Calculator - Domma CMS"},{path:"/analytics",view:"analytics",title:"Analytics - Domma CMS"},{path:"/seo",view:"seo",title:"SEO - Domma CMS"},{path:"/seo/:tab",view:"seo",title:"SEO - Domma CMS"},{path:"/contacts",view:"contacts",title:"Contacts - Domma CMS"},{path:"/calls/new",view:"callNew",title:"Call now - Domma CMS"},{path:"/system/notifications",view:"notifications",title:"Notifications - Domma CMS"},{path:"/api-tokens",view:"apiTokens",title:"API Tokens - Domma CMS"},{path:"/api-endpoints",view:"apiEndpoints",title:"API Builder - Domma CMS"},{path:"/api-endpoints/new",view:"apiEndpointEditor",title:"New API Endpoint - Domma CMS"},{path:"/api-endpoints/edit/:id",view:"apiEndpointEditor",title:"Edit API Endpoint - Domma CMS"},{path:"/projects",view:"projects",title:"Projects - Domma CMS"},{path:"/projects/new",view:"projects",title:"New Project - Domma CMS"},{path:"/projects/edit/:slug",view:"projects",title:"Edit Project - Domma CMS"},{path:"/projects/:slug/settings",view:"projectSettings",title:"Project settings - Domma CMS"},{path:"/projects/:slug/pages",view:"pages",title:"Project pages - Domma CMS"},{path:"/projects/:slug/collections",view:"collections",title:"Project collections - Domma CMS"},{path:"/projects/:slug/forms",view:"forms",title:"Project forms - Domma CMS"},{path:"/projects/:slug/actions",view:"actionsList",title:"Project actions - Domma CMS"},{path:"/projects/:slug/menus",view:"menus",title:"Project menus - Domma CMS"},{path:"/projects/:slug/blocks",view:"blocks",title:"Project blocks - Domma CMS"},{path:"/projects/:slug/components",view:"components",title:"Project components - Domma CMS"},{path:"/projects/:slug/views",view:"viewsList",title:"Project views - Domma CMS"},{path:"/projects/:slug/roles",view:"roles",title:"Project roles - Domma CMS"},{path:"/projects/:slug/users",view:"users",title:"Project users - Domma CMS"},{path:"/projects/:slug/apis",view:"apiEndpoints",title:"Project APIs - Domma CMS"},{path:"/projects/:slug",view:"projectDetail",title:"Project - Domma CMS"},{path:"/login",view:"login",title:"Sign in - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}},{path:"/reset-password",view:"login",title:"Reset Password - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}}];M.subscribe("router:afterChange",async({to:t,from:i})=>{if(!C(t.path)){if($("#admin-sidebar").show(),$("#admin-topbar").show(),C(i?.path)){$("#topbar-user-name").remove(),import("./lib/admin-theme.js").then(a=>a.refreshAdminTheme(c)),await I();const e=await g(),o=b(t.path);if(o&&R.navigate(o),A(e),ot.read().signInToasts)try{const a=await c.system.notifications.list().catch(()=>[]),n=(Array.isArray(a)?a:[]).filter(s=>s.unread&&["warning","critical"].includes(s.data?.severity)).slice(0,3);for(const s of n){const f=d(s.data?.title||""),U=d((s.data?.body||"").slice(0,120));E.toast(`${f} - ${U}`,{type:s.data?.severity==="critical"?"error":"warning",duration:0})}}catch{}}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 i=t?String(t.path).split("?")[0]:"";if(!t||!W(i))return;const o=new URLSearchParams(location.hash.split("?")[1]||"").get("project");if(!o||!/\/new$/.test(i))return;let a=0;const n=60,s=1500,f=()=>{a+=n;for(const U of V){const v=document.querySelector(U);if(!v)continue;if(Array.from(v.options||[]).find(q=>q.value===o)){v.value=o,v.dispatchEvent(new Event("change",{bubbles:!0}));return}}a<s&&setTimeout(f,n)};setTimeout(f,n)});const x="cms_card_states",j=S.get(x)||{};M.subscribe("router:afterChange",()=>{setTimeout(()=>{$("#view-container .card-collapsible").each(function(){const t=$(this).find(".card-header h2, .card-header h3").first().text().trim();t&&j[t]==="collapsed"&&$(this).addClass("card-collapsed")})},200)}),$("#view-container").on("click",".card-collapsible .card-header",function(t){if($(t.target).closest("button, a").length)return;const i=$(this).closest(".card");i.toggleClass("card-collapsed");const e=$(this).find("h2, h3").first().text().trim();e&&(j[e]=i.hasClass("card-collapsed")?"collapsed":"open",S.set(x,j))}),lt(),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash.startsWith("#/login")||window.location.hash.startsWith("#/reset-password"))return;const i=document.querySelector("#view-container .view-header button.btn-primary");i&&(t.preventDefault(),i.click())});const l={...G},H={notes:{displayName:"Notes",icon:"edit-3"},todo:{displayName:"Todo",icon:"check-square"},calculator:{displayName:"Calculator",icon:"calculator"},analytics:{displayName:"Analytics",icon:"chart-bar"},seo:{displayName:"SEO",icon:"search"},contacts:{displayName:"Contacts",icon:"users"},projects:{displayName:"Projects",icon:"folder"},pages:{displayName:"Pages",icon:"file-text"},media:{displayName:"Media",icon:"image"},menus:{displayName:"Site menus",icon:"menu"},contextMenus:{displayName:"Context Menus",icon:"menu"},theme:{displayName:"Theme",icon:"droplet"},collections:{displayName:"Collections",icon:"database"},forms:{displayName:"Forms",icon:"layout"},viewsList:{displayName:"Views",icon:"eye"},actionsList:{displayName:"Actions",icon:"zap"},apiEndpoints:{displayName:"API Builder",icon:"code"},blocks:{displayName:"Blocks",icon:"box"},components:{displayName:"Components",icon:"component"},notifications:{displayName:"Notifications",icon:"bell"},roles:{displayName:"Roles",icon:"shield"},users:{displayName:"Users",icon:"users"},settings:{displayName:"Site Settings",icon:"settings"},effects:{displayName:"Effects",icon:"sparkles"},layouts:{displayName:"Layouts",icon:"layout"},plugins:{displayName:"Marketplace",icon:"store"},myProfile:{displayName:"My Profile",icon:"user"},apiTokens:{displayName:"API Tokens",icon:"key"},search:{displayName:"Search",icon:"search"},contentApi:{displayName:"Content API",icon:"file-text"},dashboard:{displayName:"Dashboard",icon:"home"},dashboards:{displayName:"Dashboards",icon:"home"},pageEditor:{displayName:"Pages",section:"Edit",icon:"file-text"},menuEditor:{displayName:"Site menus",section:"Edit",icon:"menu"},menuLocations:{displayName:"Site menus",section:"Locations",icon:"map-pin"},contextMenuEditor:{displayName:"Context Menus",section:"Edit",icon:"menu"},collectionEditor:{displayName:"Collections",section:"Edit",icon:"database"},collectionEntries:{displayName:"Collections",section:"Entries",icon:"database"},formEditor:{displayName:"Forms",section:"Edit",icon:"layout"},formSubmissions:{displayName:"Forms",section:"Submissions",icon:"inbox"},viewEditor:{displayName:"Views",section:"Edit",icon:"eye"},viewPreview:{displayName:"Views",section:"Preview",icon:"eye"},actionEditor:{displayName:"Actions",section:"Edit",icon:"zap"},apiEndpointEditor:{displayName:"API Builder",section:"Edit",icon:"code"},blockEditor:{displayName:"Blocks",section:"Edit",icon:"box"},componentEditor:{displayName:"Components",section:"Edit",icon:"component"},roleEditor:{displayName:"Roles",section:"Edit",icon:"shield"},userEditor:{displayName:"Users",section:"Edit",icon:"users"},projectDetail:{displayName:"Projects",section:"Overview",icon:"folder"},projectSettings:{displayName:"Projects",section:"Settings",icon:"folder"},pluginCode:{displayName:"Marketplace",section:"Code",icon:"code"}};for(const[t,i]of Object.entries(H))l[t]&&(l[t]=B(l[t],{plugin:t,licence:"Built in",...i}));l.dashboard=O(l.dashboard);const k=[..._];async function I(){if(m())try{const t=await c.plugins.adminConfig(),i=new Set(k.map(e=>e.path));for(const e of t.routes||[])i.has(e.path)||k.push(e);for(const[e,o]of Object.entries(t.views||{}))try{const a=await import(`/plugins/${o.entry}`);l[e]=B(a[o.exportName],(t.meta||{})[e])}catch{}l.dashboard=O(l.dashboard);for(const[e,{route:o}]of Object.entries(t.takeovers||{}))o&&(D[`/${e}`]=o);for(const[e,{enabled:o,displayName:a}]of Object.entries(t.tools||{}))o===!1&&(y[`/${e}`]=a);for(const{id:e,href:o}of t.css||[])if(!document.getElementById(e)){const a=document.createElement("link");a.id=e,a.rel="stylesheet",a.href=o,document.head.appendChild(a)}}catch{}}function T(){const t=F();if(!t||$("#topbar-user-name").length)return;const e={"super-admin":"Super Admin",admin:"Admin",user:"User"}[t.role]||t.role,o=at(t);$("#topbar-user").html(`
|
|
2
2
|
${o?`<img id="topbar-avatar" class="topbar-avatar" src="${d(o)}" alt="">`:""}
|
|
3
3
|
<span id="topbar-user-name" class="topbar-user-name">${d(t.name)}</span>
|
|
4
4
|
<span class="topbar-role-badge topbar-role-badge--${d(t.role)}">${e}</span>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getCharacters as f}from"../../../public/js/celebrations/character-catalog.mjs";import{CELEBRATION_THEMES as i,traitAttribute as p}from"../../../public/js/celebrations/trait-data.mjs";export{i as CELEBRATION_THEMES};export const EFFECTS=[{name:"reveal",label:"Reveal",category:"Entrance",description:"Scroll-triggered entrance animation when the element enters the viewport.",attrs:[{name:"animation",label:"Animation",type:"select",default:"fade",options:[["fade","Fade"],["slide-up","Slide up"],["slide-down","Slide down"],["zoom","Zoom"],["flip","Flip"]]},{name:"duration",label:"Duration (ms)",type:"number",default:600},{name:"delay",label:"Delay (ms)",type:"number",default:0},{name:"threshold",label:"Threshold",type:"number",default:.1,step:.05,min:0,max:1},{name:"once",label:"Animate once",type:"checkbox",default:!0}],hosts:["card","box","hero","banner","col"],previewable:!0},{name:"breathe",label:"Breathe",category:"Animation",description:"Continuous gentle scaling - makes elements feel alive.",attrs:[{name:"amplitude",label:"Amplitude (px)",type:"number",default:8},{name:"duration",label:"Duration (ms)",type:"number",default:3e3}],hosts:["card","box","hero","col","button"],previewable:!0},{name:"pulse",label:"Pulse",category:"Animation",description:"A repeating scale pulse - stronger than breathe.",attrs:[{name:"scale",label:"Scale",type:"number",default:1.05,step:.01},{name:"duration",label:"Duration (ms)",type:"number",default:1e3}],hosts:["card","box","hero","banner","button"],previewable:!0},{name:"shake",label:"Shake",category:"Animation",description:"One-shot horizontal or vertical shake.",attrs:[{name:"intensity",label:"Intensity (px)",type:"number",default:5},{name:"duration",label:"Duration (ms)",type:"number",default:500},{name:"direction",label:"Direction",type:"select",default:"horizontal",options:[["horizontal","Horizontal"],["vertical","Vertical"]]}],hosts:["card","box","banner","button"],previewable:!0},{name:"animate",label:"CSS Animate",category:"Animation",description:"Applies a Domma CSS animation utility class - no JavaScript.",attrs:[{name:"type",label:"Type",type:"select",default:"fade-in-up",options:[["fade-in","fade-in"],["fade-in-up","fade-in-up"],["fade-in-down","fade-in-down"],["fade-in-left","fade-in-left"],["fade-in-right","fade-in-right"],["slide-in-up","slide-in-up"],["slide-in-down","slide-in-down"],["zoom-in","zoom-in"],["zoom-out","zoom-out"],["bounce","bounce"],["pulse","pulse"],["shake","shake"],["spin","spin"],["flip","flip"]]},{name:"duration",label:"Duration",type:"select",default:"normal",options:[["fast","fast"],["normal","normal"],["slow","slow"],["slower","slower"]]},{name:"delay",label:"Delay (ms)",type:"number",default:0},{name:"repeat",label:"Repeat",type:"select",default:"once",options:[["once","Once"],["infinite","Infinite"]]}],hosts:["card","box","hero","banner","col","button"],previewable:!0},{name:"scribe",label:"Scribe (typewriter)",category:"Text",description:"Types text character by character. Simple mode only - for the action-script composer use Insert \u2192 Scribe.",attrs:[{name:"speed",label:"Speed (ms/char)",type:"number",default:50},{name:"cursor",label:"Show cursor",type:"checkbox",default:!0},{name:"loop",label:"Loop",type:"checkbox",default:!1}],hosts:[],previewable:!0},{name:"scramble",label:"Scramble",category:"Text",description:"Reveals text through a character-scramble animation.",attrs:[{name:"speed",label:"Speed (ms/char)",type:"number",default:50},{name:"loop",label:"Loop",type:"checkbox",default:!1}],hosts:[],previewable:!0},{name:"counter",label:"Counter",category:"Text",description:"Animates a number from a starting value to a target. Self-closing shortcode.",attrs:[{name:"to",label:"To",type:"number",default:100},{name:"from",label:"From",type:"number",default:0},{name:"duration",label:"Duration (ms)",type:"number",default:2e3},{name:"prefix",label:"Prefix",type:"text",default:""},{name:"suffix",label:"Suffix",type:"text",default:""},{name:"decimals",label:"Decimal places",type:"number",default:0},{name:"separator",label:"Thousand separator",type:"text",default:""}],hosts:[],previewable:!0,selfClosing:!0},{name:"ripple",label:"Ripple",category:"Visual",description:"Click-triggered ripple effect on the element.",attrs:[{name:"colour",label:"Colour",type:"colour",default:"#7c6af7"},{name:"duration",label:"Duration (ms)",type:"number",default:600},{name:"opacity",label:"Opacity",type:"number",default:.3,step:.05,min:0,max:1}],hosts:["card","box","col","button"],previewable:!0},{name:"twinkle",label:"Twinkle",category:"Visual",description:"Generates animated particle effects inside the element.",attrs:[{name:"count",label:"Particle count",type:"number",default:80},{name:"shape",label:"Shape",type:"select",default:"star",options:[["star","Star"],["circle","Circle"],["square","Square"]]},{name:"colour",label:"Colour",type:"colour",default:"#ffffff"},{name:"min-size",label:"Min size (px)",type:"number",default:2},{name:"max-size",label:"Max size (px)",type:"number",default:6}],hosts:["card","box","hero"],previewable:!0},{name:"ambient",label:"Ambient background",category:"Visual",description:"Applies an animated CSS background class to the element.",attrs:[{name:"type",label:"Type",type:"select",default:"float-blobs",options:[["float-blobs","Float blobs"],["float-orbs","Float orbs"],["float-particles","Float particles"],["rotate-glow","Rotate glow"],["rotate-dual","Rotate dual"],["rotate-spotlight","Rotate spotlight"],["wave","Wave"],["ripple","Ripple"],["aurora","Aurora"]]},{name:"speed",label:"Speed",type:"select",default:"normal",options:[["slow","Slow"],["normal","Normal"],["fast","Fast"]]},{name:"intensity",label:"Intensity",type:"select",default:"subtle",options:[["subtle","Subtle"],["intense","Intense"]]}],hosts:["card","box","hero","banner"],previewable:!0},{name:"firework",label:"Firework",category:"Celebrations",description:"CSS-only firework particle. Self-closing or wrapping form.",attrs:[{name:"type",label:"Type",type:"select",default:"burst",options:[["burst","Burst"],["sparkle","Sparkle"],["trail","Trail"]]},{name:"colour",label:"Colour theme",type:"select",default:"rainbow",options:[["rainbow","Rainbow"],["primary","Primary"],["info","Info"],["success","Success"],["warning","Warning"],["danger","Danger"]]},{name:"size",label:"Size",type:"select",default:"md",options:[["sm","Small"],["md","Medium"],["lg","Large"]]},{name:"continuous",label:"Continuous",type:"checkbox",default:!1},{name:"hover",label:"On hover only",type:"checkbox",default:!1}],hosts:[],previewable:!0},{name:"fireworks",label:"Fireworks show",category:"Celebrations",description:"A container with multiple firework particles inside.",attrs:[],hosts:[],previewable:!1},{name:"celebrate",label:"Celebrate",category:"Celebrations",description:"A full-page seasonal celebration, or one inline character at any time of year.",attrs:[{name:"theme",label:"Theme",type:"select",default:"auto",options:[["auto","Auto (date-detected)"],["christmas","Christmas"],["halloween","Halloween"],["valentines","Valentines"],["guy-fawkes","Guy Fawkes"],["st-patricks","St Patrick's"],["st-davids","St David's"],["st-georges","St George's"],["st-andrews","St Andrew's"]]},{name:"intensity",label:"Intensity",type:"select",default:"medium",options:[["light","Light"],["medium","Medium"],["heavy","Heavy"]]},{name:"snow-only",label:"Snow only (Christmas)",type:"checkbox",default:!1,hint:"Just the falling snow - every Christmas decoration off."},{name:"trees",label:"Christmas: trees",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"wreaths",label:"Christmas: wreaths",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"northstars",label:"Christmas: North Star",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"snowmen",label:"Christmas: snowmen",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"sleigh",label:"Christmas: Santa's sleigh",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"elf",label:"Christmas: elf",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"train",label:"Christmas: train",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"firework",label:"Christmas: fireworks",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"robin",label:"Christmas: robin",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"peeing-elf",label:"Peeing elf (opt-in)",type:"checkbox",default:!1,hint:"A clothed rear-view gag. Off by default; Snow only keeps it off."},{name:"thieving-elf",label:"Thieving elf (opt-in)",type:"checkbox",default:!1,hint:"Runs to a tree, takes a present and escapes. Needs a visible tree."}],hosts:[],previewable:!1,selfClosing:!0},{name:"christmas",label:"Christmas",category:"Celebrations",description:"Christmas on the published site: snow, trees, wreaths, snowmen, Santa's sleigh and more. Snow only for just the snow.",attrs:[{name:"intensity",label:"Intensity",type:"select",default:"medium",options:[["light","Light"],["medium","Medium"],["heavy","Heavy"]]},{name:"snow-only",label:"Snow only",type:"checkbox",default:!1,hint:"Just the falling snow - every decoration off."},{name:"trees",label:"trees",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"wreaths",label:"wreaths",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"northstars",label:"North Star",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"snowmen",label:"snowmen",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"sleigh",label:"Santa's sleigh",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"elf",label:"elf",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"train",label:"train",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"firework",label:"fireworks",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"robin",label:"robin",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"peeing-elf",label:"Peeing elf (opt-in)",type:"checkbox",default:!1,hint:"A clothed rear-view gag. Off by default; Snow only keeps it off."},{name:"thieving-elf",label:"Thieving elf (opt-in)",type:"checkbox",default:!1,hint:"Runs to a tree, takes a present and escapes. Needs a visible tree."}],hosts:[],previewable:!1,selfClosing:!0},{name:"ticker-tape",label:"Ticker tape",category:"Celebrations",description:"Coloured rectangular strips fall from the top, sway, rotate, and fade. Inserted self-closing (full-page); hand-write the wrapping form for container-scoped.",attrs:[{name:"palette",label:"Palette",type:"select",default:"theme",options:[["theme","Theme"],["rainbow","Rainbow"],["gold","Gold"],["silver","Silver"],["festive","Festive"],["pastel","Pastel"],["mono","Mono"]]},{name:"density",label:"Density",type:"number",default:50},{name:"speed",label:"Speed multiplier",type:"number",default:1,step:.1},{name:"sway",label:"Sway (px)",type:"number",default:60},{name:"burst",label:"Burst mode (one-shot)",type:"checkbox",default:!1},{name:"burst-count",label:"Burst count",type:"number",default:150}],hosts:["hero"],previewable:!0,selfClosing:!0},{name:"butterflies",label:"Butterflies",category:"Celebrations",description:"Procedurally drawn butterflies wander and rise with flapping wings. Self-closing for full-page; wrapping form for container-scoped.",attrs:[{name:"palette",label:"Palette",type:"select",default:"meadow",options:[["meadow","Meadow"],["theme","Theme"],["rainbow","Rainbow"],["pastel","Pastel"],["festive","Festive"],["gold","Gold"],["silver","Silver"],["mono","Mono"],["sunset","Sunset"],["ocean","Ocean"],["forest","Forest"],["bridal","Bridal"],["firefly","Firefly"]]},{name:"density",label:"Density",type:"number",default:18},{name:"speed",label:"Speed multiplier",type:"number",default:1,step:.1},{name:"wander",label:"Wander",type:"number",default:1,step:.1,hint:"Steering erraticness"},{name:"rise-speed",label:"Rise speed",type:"number",default:.4,step:.1,hint:"Upward drift bias"},{name:"flap-speed",label:"Flap speed",type:"number",default:1,step:.1},{name:"min-size",label:"Min wingspan (px)",type:"number",default:14},{name:"max-size",label:"Max wingspan (px)",type:"number",default:26},{name:"two-tone",label:"Two-tone wings",type:"checkbox",default:!0},{name:"burst",label:"Burst mode (one-shot)",type:"checkbox",default:!1},{name:"burst-count",label:"Burst count",type:"number",default:40}],hosts:["hero"],previewable:!0},{name:"strobe",label:"Strobe lighting",category:"Celebrations",description:"Light beams sweep from the corners and brighten where they cross - club, concert, or searchlight moods. Self-closing for full-page; wrapping form for container-scoped.",attrs:[{name:"preset",label:"Preset",type:"select",default:"club",options:[["club","Club"],["concert","Concert"],["police","Police"],["searchlight","Searchlight"],["scanner","Scanner"],["mood","Mood"]],hint:"Other fields override the preset when changed"},{name:"motion",label:"Motion",type:"select",default:"sweep",options:[["sweep","Sweep (fan back and forth)"],["rotate","Rotate (full circle)"]]},{name:"speed",label:"Speed multiplier",type:"number",default:1,step:.1},{name:"sweep-arc",label:"Sweep arc (deg)",type:"number",default:100},{name:"beam-width",label:"Beam width (deg)",type:"number",default:16},{name:"intensity",label:"Intensity",type:"number",default:.7,step:.05,min:0,max:1},{name:"flicker",label:"Flicker",type:"checkbox",default:!0},{name:"hz",label:"Flicker rate (Hz)",type:"number",default:10,hint:"Rates above 5 flashes/sec can trigger photosensitive seizures"},{name:"colours",label:"Colours",type:"text",default:"",placeholder:"#ffffff, #38bdf8",hint:"Comma-separated hex colours - overrides the preset palette"},{name:"duration",label:"Auto-stop (ms)",type:"number",default:"",hint:"Stop after this many ms - blank runs forever"}],hosts:["hero"],previewable:!0}];export function getEffect(e){return EFFECTS.find(t=>t.name===e)||null}export function effectsByCategory(){return["Entrance","Animation","Text","Visual","Celebrations"].map(t=>({category:t,effects:EFFECTS.filter(o=>o.category===t)}))}export function effectsInjectableInto(e){return EFFECTS.filter(t=>t.hosts.includes(e))}const n=EFFECTS.find(e=>e.name==="celebrate");for(const e of n.attrs.slice(2))e.themes=["christmas"];n.attrs.push({name:"character",label:"Standalone character",type:"select",default:"",options:[["","Full celebration"],...f().map(e=>[e.name,e.label])]},{name:"size",label:"Character height",type:"text",default:"medium",placeholder:"medium or 200",hint:"small (96px), medium (160px), large (240px), xlarge (360px), or a height in pixels."},{name:"animate",label:"Animate character",type:"checkbox",default:!0},{name:"facing",label:"Character direction",type:"select",default:"right",options:[["right","Right"],["left","Left"]]}),n.attrs.push({name:"falling-only",label:"Falling particles only",type:"checkbox",default:!1}),n.attrs.push({name:"mooning-elf",label:"Mooning elf (opt-in)",type:"checkbox",default:!1,themes:["christmas"]}),EFFECTS.find(e=>e.name==="christmas").attrs.push({name:"mooning-elf",label:"Mooning elf (opt-in)",type:"checkbox",default:!1});const s=new Map(n.attrs.map(e=>[e.name,e]));for(const[e,t]of Object.entries(i))if(e!=="christmas")for(const[o,r]of Object.entries(t.traits)){const a=p(o);let l=s.get(a);l||(l=r.enabled===!1?{name:a,label:`${r.label} (opt-in)`,type:"checkbox",default:!1,themes:[]}:{name:a,label:r.label,type:"select",default:"",options:[["","Shown"],["off","Off"]],themes:[]},s.set(a,l)),l.themes.includes(e)||l.themes.push(e),l.themes.length>1&&(l.label=l.label.replace(/^Christmas: /,""))}for(const e of s.values())n.attrs.includes(e)||n.attrs.push(e);export function getEffectAttrs(e,t={}){if(e.name!=="celebrate")return e.attrs||[];const o=["character","size","animate","facing"];if(t.character)return e.attrs.filter(a=>o.includes(a.name));const r=t.theme||"auto";return e.attrs.filter(a=>(a.name==="character"||!o.includes(a.name))&&(!a.themes||r==="auto"||a.themes.includes(r)))}
|
|
1
|
+
import{SCRIBE_FIELDS as f}from"./scribe-model.js";import{getCharacters as p}from"../../../public/js/celebrations/character-catalog.mjs";import{CELEBRATION_THEMES as i,traitAttribute as m}from"../../../public/js/celebrations/trait-data.mjs";export{i as CELEBRATION_THEMES};export const EFFECTS=[{name:"reveal",label:"Reveal",category:"Entrance",description:"Scroll-triggered entrance animation when the element enters the viewport.",attrs:[{name:"animation",label:"Animation",type:"select",default:"fade",options:[["fade","Fade"],["slide-up","Slide up"],["slide-down","Slide down"],["zoom","Zoom"],["flip","Flip"]]},{name:"duration",label:"Duration (ms)",type:"number",default:600},{name:"delay",label:"Delay (ms)",type:"number",default:0},{name:"threshold",label:"Threshold",type:"number",default:.1,step:.05,min:0,max:1},{name:"once",label:"Animate once",type:"checkbox",default:!0}],hosts:["card","box","hero","banner","col"],previewable:!0},{name:"breathe",label:"Breathe",category:"Animation",description:"Continuous gentle scaling - makes elements feel alive.",attrs:[{name:"amplitude",label:"Amplitude (px)",type:"number",default:8},{name:"duration",label:"Duration (ms)",type:"number",default:3e3}],hosts:["card","box","hero","col","button"],previewable:!0},{name:"pulse",label:"Pulse",category:"Animation",description:"A repeating scale pulse - stronger than breathe.",attrs:[{name:"scale",label:"Scale",type:"number",default:1.05,step:.01},{name:"duration",label:"Duration (ms)",type:"number",default:1e3}],hosts:["card","box","hero","banner","button"],previewable:!0},{name:"shake",label:"Shake",category:"Animation",description:"One-shot horizontal or vertical shake.",attrs:[{name:"intensity",label:"Intensity (px)",type:"number",default:5},{name:"duration",label:"Duration (ms)",type:"number",default:500},{name:"direction",label:"Direction",type:"select",default:"horizontal",options:[["horizontal","Horizontal"],["vertical","Vertical"]]}],hosts:["card","box","banner","button"],previewable:!0},{name:"animate",label:"CSS Animate",category:"Animation",description:"Applies a Domma CSS animation utility class - no JavaScript.",attrs:[{name:"type",label:"Type",type:"select",default:"fade-in-up",options:[["fade-in","fade-in"],["fade-in-up","fade-in-up"],["fade-in-down","fade-in-down"],["fade-in-left","fade-in-left"],["fade-in-right","fade-in-right"],["slide-in-up","slide-in-up"],["slide-in-down","slide-in-down"],["zoom-in","zoom-in"],["zoom-out","zoom-out"],["bounce","bounce"],["pulse","pulse"],["shake","shake"],["spin","spin"],["flip","flip"]]},{name:"duration",label:"Duration",type:"select",default:"normal",options:[["fast","fast"],["normal","normal"],["slow","slow"],["slower","slower"]]},{name:"delay",label:"Delay (ms)",type:"number",default:0},{name:"repeat",label:"Repeat",type:"select",default:"once",options:[["once","Once"],["infinite","Infinite"]]}],hosts:["card","box","hero","banner","col","button"],previewable:!0},{name:"scribe",label:"Scribe (typewriter)",category:"Text",description:"Types characters, words, or sentences with entrance effects and playback options. For action scripts use Insert \u2192 Scribe.",attrs:f,hosts:[],previewable:!0},{name:"scramble",label:"Scramble",category:"Text",description:"Reveals text through a character-scramble animation.",attrs:[{name:"speed",label:"Speed (ms/char)",type:"number",default:50},{name:"loop",label:"Loop",type:"checkbox",default:!1}],hosts:[],previewable:!0},{name:"counter",label:"Counter",category:"Text",description:"Animates a number from a starting value to a target. Self-closing shortcode.",attrs:[{name:"to",label:"To",type:"number",default:100},{name:"from",label:"From",type:"number",default:0},{name:"duration",label:"Duration (ms)",type:"number",default:2e3},{name:"prefix",label:"Prefix",type:"text",default:""},{name:"suffix",label:"Suffix",type:"text",default:""},{name:"decimals",label:"Decimal places",type:"number",default:0},{name:"separator",label:"Thousand separator",type:"text",default:""}],hosts:[],previewable:!0,selfClosing:!0},{name:"ripple",label:"Ripple",category:"Visual",description:"Click-triggered ripple effect on the element.",attrs:[{name:"colour",label:"Colour",type:"colour",default:"#7c6af7"},{name:"duration",label:"Duration (ms)",type:"number",default:600},{name:"opacity",label:"Opacity",type:"number",default:.3,step:.05,min:0,max:1}],hosts:["card","box","col","button"],previewable:!0},{name:"twinkle",label:"Twinkle",category:"Visual",description:"Generates animated particle effects inside the element.",attrs:[{name:"count",label:"Particle count",type:"number",default:80},{name:"shape",label:"Shape",type:"select",default:"star",options:[["star","Star"],["circle","Circle"],["square","Square"]]},{name:"colour",label:"Colour",type:"colour",default:"#ffffff"},{name:"min-size",label:"Min size (px)",type:"number",default:2},{name:"max-size",label:"Max size (px)",type:"number",default:6}],hosts:["card","box","hero"],previewable:!0},{name:"ambient",label:"Ambient background",category:"Visual",description:"Applies an animated CSS background class to the element.",attrs:[{name:"type",label:"Type",type:"select",default:"float-blobs",options:[["float-blobs","Float blobs"],["float-orbs","Float orbs"],["float-particles","Float particles"],["rotate-glow","Rotate glow"],["rotate-dual","Rotate dual"],["rotate-spotlight","Rotate spotlight"],["wave","Wave"],["ripple","Ripple"],["aurora","Aurora"]]},{name:"speed",label:"Speed",type:"select",default:"normal",options:[["slow","Slow"],["normal","Normal"],["fast","Fast"]]},{name:"intensity",label:"Intensity",type:"select",default:"subtle",options:[["subtle","Subtle"],["intense","Intense"]]}],hosts:["card","box","hero","banner"],previewable:!0},{name:"firework",label:"Firework",category:"Celebrations",description:"CSS-only firework particle. Self-closing or wrapping form.",attrs:[{name:"type",label:"Type",type:"select",default:"burst",options:[["burst","Burst"],["sparkle","Sparkle"],["trail","Trail"]]},{name:"colour",label:"Colour theme",type:"select",default:"rainbow",options:[["rainbow","Rainbow"],["primary","Primary"],["info","Info"],["success","Success"],["warning","Warning"],["danger","Danger"]]},{name:"size",label:"Size",type:"select",default:"md",options:[["sm","Small"],["md","Medium"],["lg","Large"]]},{name:"continuous",label:"Continuous",type:"checkbox",default:!1},{name:"hover",label:"On hover only",type:"checkbox",default:!1}],hosts:[],previewable:!0},{name:"fireworks",label:"Fireworks show",category:"Celebrations",description:"A container with multiple firework particles inside.",attrs:[],hosts:[],previewable:!1},{name:"celebrate",label:"Celebrate",category:"Celebrations",description:"A full-page seasonal celebration, or one inline character at any time of year.",attrs:[{name:"theme",label:"Theme",type:"select",default:"auto",options:[["auto","Auto (date-detected)"],["christmas","Christmas"],["halloween","Halloween"],["valentines","Valentines"],["guy-fawkes","Guy Fawkes"],["st-patricks","St Patrick's"],["st-davids","St David's"],["st-georges","St George's"],["st-andrews","St Andrew's"]]},{name:"intensity",label:"Intensity",type:"select",default:"medium",options:[["light","Light"],["medium","Medium"],["heavy","Heavy"]]},{name:"snow-only",label:"Snow only (Christmas)",type:"checkbox",default:!1,hint:"Just the falling snow - every Christmas decoration off."},{name:"trees",label:"Christmas: trees",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"wreaths",label:"Christmas: wreaths",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"northstars",label:"Christmas: North Star",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"snowmen",label:"Christmas: snowmen",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"sleigh",label:"Christmas: Santa's sleigh",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"elf",label:"Christmas: elf",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"train",label:"Christmas: train",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"firework",label:"Christmas: fireworks",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"robin",label:"Christmas: robin",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"peeing-elf",label:"Peeing elf (opt-in)",type:"checkbox",default:!1,hint:"A clothed rear-view gag. Off by default; Snow only keeps it off."},{name:"thieving-elf",label:"Thieving elf (opt-in)",type:"checkbox",default:!1,hint:"Runs to a tree, takes a present and escapes. Needs a visible tree."}],hosts:[],previewable:!1,selfClosing:!0},{name:"christmas",label:"Christmas",category:"Celebrations",description:"Christmas on the published site: snow, trees, wreaths, snowmen, Santa's sleigh and more. Snow only for just the snow.",attrs:[{name:"intensity",label:"Intensity",type:"select",default:"medium",options:[["light","Light"],["medium","Medium"],["heavy","Heavy"]]},{name:"snow-only",label:"Snow only",type:"checkbox",default:!1,hint:"Just the falling snow - every decoration off."},{name:"trees",label:"trees",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"wreaths",label:"wreaths",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"northstars",label:"North Star",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"snowmen",label:"snowmen",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"sleigh",label:"Santa's sleigh",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"elf",label:"elf",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"train",label:"train",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"firework",label:"fireworks",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"robin",label:"robin",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"peeing-elf",label:"Peeing elf (opt-in)",type:"checkbox",default:!1,hint:"A clothed rear-view gag. Off by default; Snow only keeps it off."},{name:"thieving-elf",label:"Thieving elf (opt-in)",type:"checkbox",default:!1,hint:"Runs to a tree, takes a present and escapes. Needs a visible tree."}],hosts:[],previewable:!1,selfClosing:!0},{name:"ticker-tape",label:"Ticker tape",category:"Celebrations",description:"Coloured rectangular strips fall from the top, sway, rotate, and fade. Inserted self-closing (full-page); hand-write the wrapping form for container-scoped.",attrs:[{name:"palette",label:"Palette",type:"select",default:"theme",options:[["theme","Theme"],["rainbow","Rainbow"],["gold","Gold"],["silver","Silver"],["festive","Festive"],["pastel","Pastel"],["mono","Mono"]]},{name:"density",label:"Density",type:"number",default:50},{name:"speed",label:"Speed multiplier",type:"number",default:1,step:.1},{name:"sway",label:"Sway (px)",type:"number",default:60},{name:"burst",label:"Burst mode (one-shot)",type:"checkbox",default:!1},{name:"burst-count",label:"Burst count",type:"number",default:150}],hosts:["hero"],previewable:!0,selfClosing:!0},{name:"butterflies",label:"Butterflies",category:"Celebrations",description:"Procedurally drawn butterflies wander and rise with flapping wings. Self-closing for full-page; wrapping form for container-scoped.",attrs:[{name:"palette",label:"Palette",type:"select",default:"meadow",options:[["meadow","Meadow"],["theme","Theme"],["rainbow","Rainbow"],["pastel","Pastel"],["festive","Festive"],["gold","Gold"],["silver","Silver"],["mono","Mono"],["sunset","Sunset"],["ocean","Ocean"],["forest","Forest"],["bridal","Bridal"],["firefly","Firefly"]]},{name:"density",label:"Density",type:"number",default:18},{name:"speed",label:"Speed multiplier",type:"number",default:1,step:.1},{name:"wander",label:"Wander",type:"number",default:1,step:.1,hint:"Steering erraticness"},{name:"rise-speed",label:"Rise speed",type:"number",default:.4,step:.1,hint:"Upward drift bias"},{name:"flap-speed",label:"Flap speed",type:"number",default:1,step:.1},{name:"min-size",label:"Min wingspan (px)",type:"number",default:14},{name:"max-size",label:"Max wingspan (px)",type:"number",default:26},{name:"two-tone",label:"Two-tone wings",type:"checkbox",default:!0},{name:"burst",label:"Burst mode (one-shot)",type:"checkbox",default:!1},{name:"burst-count",label:"Burst count",type:"number",default:40}],hosts:["hero"],previewable:!0},{name:"strobe",label:"Strobe lighting",category:"Celebrations",description:"Light beams sweep from the corners and brighten where they cross - club, concert, or searchlight moods. Self-closing for full-page; wrapping form for container-scoped.",attrs:[{name:"preset",label:"Preset",type:"select",default:"club",options:[["club","Club"],["concert","Concert"],["police","Police"],["searchlight","Searchlight"],["scanner","Scanner"],["mood","Mood"]],hint:"Other fields override the preset when changed"},{name:"motion",label:"Motion",type:"select",default:"sweep",options:[["sweep","Sweep (fan back and forth)"],["rotate","Rotate (full circle)"]]},{name:"speed",label:"Speed multiplier",type:"number",default:1,step:.1},{name:"sweep-arc",label:"Sweep arc (deg)",type:"number",default:100},{name:"beam-width",label:"Beam width (deg)",type:"number",default:16},{name:"intensity",label:"Intensity",type:"number",default:.7,step:.05,min:0,max:1},{name:"flicker",label:"Flicker",type:"checkbox",default:!0},{name:"hz",label:"Flicker rate (Hz)",type:"number",default:10,hint:"Rates above 5 flashes/sec can trigger photosensitive seizures"},{name:"colours",label:"Colours",type:"text",default:"",placeholder:"#ffffff, #38bdf8",hint:"Comma-separated hex colours - overrides the preset palette"},{name:"duration",label:"Auto-stop (ms)",type:"number",default:"",hint:"Stop after this many ms - blank runs forever"}],hosts:["hero"],previewable:!0}];export function getEffect(e){return EFFECTS.find(t=>t.name===e)||null}export function effectsByCategory(){return["Entrance","Animation","Text","Visual","Celebrations"].map(t=>({category:t,effects:EFFECTS.filter(o=>o.category===t)}))}export function effectsInjectableInto(e){return EFFECTS.filter(t=>t.hosts.includes(e))}const n=EFFECTS.find(e=>e.name==="celebrate");for(const e of n.attrs.slice(2))e.themes=["christmas"];n.attrs.push({name:"character",label:"Standalone character",type:"select",default:"",options:[["","Full celebration"],...p().map(e=>[e.name,e.label])]},{name:"size",label:"Character height",type:"text",default:"medium",placeholder:"medium or 200",hint:"small (96px), medium (160px), large (240px), xlarge (360px), or a height in pixels."},{name:"animate",label:"Animate character",type:"checkbox",default:!0},{name:"facing",label:"Character direction",type:"select",default:"right",options:[["right","Right"],["left","Left"]]}),n.attrs.push({name:"falling-only",label:"Falling particles only",type:"checkbox",default:!1}),n.attrs.push({name:"mooning-elf",label:"Mooning elf (opt-in)",type:"checkbox",default:!1,themes:["christmas"]}),EFFECTS.find(e=>e.name==="christmas").attrs.push({name:"mooning-elf",label:"Mooning elf (opt-in)",type:"checkbox",default:!1});const r=new Map(n.attrs.map(e=>[e.name,e]));for(const[e,t]of Object.entries(i))if(e!=="christmas")for(const[o,s]of Object.entries(t.traits)){const a=m(o);let l=r.get(a);l||(l=s.enabled===!1?{name:a,label:`${s.label} (opt-in)`,type:"checkbox",default:!1,themes:[]}:{name:a,label:s.label,type:"select",default:"",options:[["","Shown"],["off","Off"]],themes:[]},r.set(a,l)),l.themes.includes(e)||l.themes.push(e),l.themes.length>1&&(l.label=l.label.replace(/^Christmas: /,""))}for(const e of r.values())n.attrs.includes(e)||n.attrs.push(e);export function getEffectAttrs(e,t={}){if(e.name!=="celebrate")return e.attrs||[];const o=["character","size","animate","facing"];if(t.character)return e.attrs.filter(a=>o.includes(a.name));const s=t.theme||"auto";return e.attrs.filter(a=>(a.name==="character"||!o.includes(a.name))&&(!a.themes||s==="auto"||a.themes.includes(s)))}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const WIDTHS=["narrow","normal","wide","full"],WIDTH_LABELS={narrow:"Narrow",normal:"Normal",wide:"Wide",full:"Full width"},WIDTH_HINTS={narrow:"768px - a reading column",normal:"1100px",wide:"75% of the window, up to 2000px",full:"edge to edge"},WIDTH_SHARE={narrow:"40%",normal:"62%",wide:"82%",full:"100%"},FILTERS=["all","builtin","custom"];const b=/^[a-zA-Z0-9#(),.\s%/-]+$/;export function layoutKey(e){return String(e??"").toLowerCase().trim().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"")}const g=(e,t,l=`${t}s`)=>`${e} ${e===1?t:l}`;export function chromeText(e){const t=[e.navbar!==!1&&"navbar",e.footer!==!1&&"footer",e.sidebar===!0&&"
|
|
1
|
+
export const WIDTHS=["narrow","normal","wide","full"],WIDTH_LABELS={narrow:"Narrow",normal:"Normal",wide:"Wide",full:"Full width"},WIDTH_HINTS={narrow:"768px - a reading column",normal:"1100px",wide:"75% of the window, up to 2000px",full:"edge to edge"},WIDTH_SHARE={narrow:"40%",normal:"62%",wide:"82%",full:"100%"},FILTERS=["all","builtin","custom"];const b=/^[a-zA-Z0-9#(),.\s%/-]+$/;export function layoutKey(e){return String(e??"").toLowerCase().trim().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"")}const g=(e,t,l=`${t}s`)=>`${e} ${e===1?t:l}`;export function chromeText(e){const t=[e.navbar!==!1&&"navbar",e.footer!==!1&&"footer",e.sidebar===!0&&"page contents"].filter(Boolean);return t.length?t.join(", "):"no navbar or footer"}export function arrangeLayouts(e,t={},{filter:l="all",query:o=""}={}){const i=String(o).trim().toLowerCase(),n=Object.entries(e||{}).map(([r,s])=>({...s,key:r})),a=n.filter(r=>r.builtin),c=n.filter(r=>!r.builtin).sort((r,s)=>String(r.label||r.key).localeCompare(String(s.label||s.key)));return[...a,...c].filter(r=>l==="builtin"?r.builtin:l==="custom"?!r.builtin:!0).filter(r=>!i||[r.key,r.label,r.description,r.class].some(s=>String(s||"").toLowerCase().includes(i))).map(r=>layoutRow(r,(t.byKey||{})[r.key]||[]))}export function layoutRow(e,t=[]){const l=WIDTHS.includes(e.width)?e.width:"normal";return{key:e.key,label:e.label||e.key,meta:[e.key,e.description,chromeText(e)].filter(Boolean).join(" \xB7 "),builtin:e.builtin===!0,widthLabel:WIDTH_LABELS[l],used:t.length,usedText:t.length?g(t.length,"page"):"Unused",usedTone:t.length?"is-info":"is-muted",hasNav:e.navbar!==!1,hasFoot:e.footer!==!1,hasSide:e.sidebar===!0,sideRight:e.sidebar===!0&&e.sidebarSide==="right",share:WIDTH_SHARE[l],bg:e.bgColor&&b.test(e.bgColor)?e.bgColor:""}}export function formFromPreset(e={}){return{label:e.label||"",description:e.description||"",width:WIDTHS.includes(e.width)?e.width:"normal",navbar:e.navbar!==!1,footer:e.footer!==!1,sidebar:e.sidebar===!0,sidebarSide:e.sidebarSide==="right"?"right":"left",useBg:!!e.bgColor,bgColor:e.bgColor&&/^#[0-9a-f]{6}$/i.test(e.bgColor)?e.bgColor:e.bgColor||"#ffffff",bgImage:e.bgImage||"",cssClass:e.class||"",...contentsForm(e.contents)}}export function contentsForm(e={}){return{cWidth:e.width!=null?String(e.width):"",cSticky:e.sticky!==!1,cScrollSpy:e.scrollSpy!==!1,cFloating:e.floating===!0,cFloatAnchor:e.floatAnchor||"TR",cMovable:e.movable===!0,cCollapsible:e.collapsible===!0,cCollapsed:e.collapsed===!0,cAutohide:e.autohide===!0,cHeadings:e.headings||"auto",cNumbered:e.numbered===!0,cTitle:e.title||"",cExclude:e.exclude||"",cHover:e.hover||"",cSelected:e.selected||"",cAccent:e.accent||"",cFont:e.font||"",cFontSize:e.fontSize||"",cFontWeight:e.fontWeight||""}}export function contentsFromForm(e={}){const t={},l=parseInt(e.cWidth,10);Number.isFinite(l)&&(t.width=l),e.cSticky===!1&&(t.sticky=!1),e.cScrollSpy===!1&&(t.scrollSpy=!1),e.cFloating===!0?(t.floating=!0,t.floatAnchor=e.cFloatAnchor||"TR",e.cMovable===!0&&(t.movable=!0)):e.cAutohide===!0?t.autohide=!0:e.cCollapsible===!0&&(t.collapsible=!0,e.cCollapsed===!0&&(t.collapsed=!0)),e.cHeadings&&e.cHeadings!=="auto"&&(t.headings=e.cHeadings),e.cNumbered===!0&&(t.numbered=!0);for(const[o,i]of[["title","cTitle"],["exclude","cExclude"],["hover","cHover"],["selected","cSelected"],["accent","cAccent"],["font","cFont"],["fontSize","cFontSize"],["fontWeight","cFontWeight"]]){const n=String(e[i]??"").trim();n&&(t[o]=n)}return t}export function checkLayout(e,{isNew:t,taken:l=[]}={}){const o=e||{},i={},n=String(o.label??"").trim(),a=t?layoutKey(n):"";n?n.length>60?i.label="Up to 60 characters.":t&&!a?i.label="Needs at least one letter or number.":t&&l.includes(a)&&(i.label=`"${a}" is already a layout - choose another name.`):i.label="A layout needs a name.";const c=o.useBg?String(o.bgColor||"").trim():"";c&&!b.test(c)&&(i.bgColor="Not a colour the server accepts.");const r=String(o.bgImage??"").trim(),s=String(o.cssClass??"").trim();/[^a-zA-Z0-9\s_-]/.test(s)&&(i.cssClass="Letters, numbers, spaces, hyphens and underscores only.");const u=String(o.cWidth??"").trim();u&&!(Number(u)>=160&&Number(u)<=480)&&(i.cWidth="From 160 to 480 px, or blank for the usual 260.");const d=o.sidebar===!0?contentsFromForm(o):{},f={label:n,description:String(o.description??"").trim().slice(0,200),width:WIDTHS.includes(o.width)?o.width:"normal",navbar:o.navbar!==!1,footer:o.footer!==!1,sidebar:o.sidebar===!0,sidebarSide:o.sidebarSide==="right"?"right":"left",bgColor:c,bgImage:r,class:s,...Object.keys(d).length&&{contents:d}};return{errors:i,payload:f,key:a}}export function checkOptions(e){const t={},l=String(e?.spacerSize??"").trim(),o=Number(l);(l===""||!Number.isInteger(o)||o<0||o>500)&&(t.spacerSize="A whole number of pixels, 0 to 500.");const i=String(e?.spacerClass??"").trim();return/[^a-zA-Z0-9\s_-]/.test(i)&&(t.spacerClass="Letters, numbers, spaces, hyphens and underscores only."),{errors:t,payload:{spacerSize:Number.isInteger(o)?o:8,spacerClass:i}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{CORE as m,checkProject as p}from"./projects-arrange.js";export const TYPES=[{key:"pages",label:"Pages",singular:"page",icon:"file-text",tone:"var(--dm-primary)",path:"/pages/new"},{key:"collections",label:"Collections",singular:"collection",icon:"database",tone:"var(--dm-secondary)",path:"/collections/new"},{key:"forms",label:"Forms",singular:"form",icon:"layout",tone:"var(--dm-success)",path:"/forms/new"},{key:"actions",label:"Actions",singular:"action",icon:"zap",tone:"var(--dm-warning, var(--dm-primary))",path:"/actions/new"},{key:"menus",label:"
|
|
1
|
+
import{CORE as m,checkProject as p}from"./projects-arrange.js";export const TYPES=[{key:"pages",label:"Pages",singular:"page",icon:"file-text",tone:"var(--dm-primary)",path:"/pages/new"},{key:"collections",label:"Collections",singular:"collection",icon:"database",tone:"var(--dm-secondary)",path:"/collections/new"},{key:"forms",label:"Forms",singular:"form",icon:"layout",tone:"var(--dm-success)",path:"/forms/new"},{key:"actions",label:"Actions",singular:"action",icon:"zap",tone:"var(--dm-warning, var(--dm-primary))",path:"/actions/new"},{key:"menus",label:"Site menus",singular:"menu",icon:"menu",tone:"var(--dm-info)",path:"/menus/new"},{key:"blocks",label:"Blocks",singular:"block",icon:"box",tone:"var(--dm-primary-hover, var(--dm-primary))",path:"/blocks/new"},{key:"components",label:"Components",singular:"component",icon:"component",tone:"var(--dm-warning-dark, var(--dm-warning, var(--dm-secondary)))",path:"/components/new"},{key:"views",label:"Views",singular:"view",icon:"eye",tone:"var(--dm-info)",path:"/views/new"},{key:"roles",label:"Roles",singular:"role",icon:"shield",tone:"var(--dm-danger)",path:"/roles"},{key:"users",label:"Users",singular:"user",icon:"users",tone:"var(--dm-secondary)",path:"/users/new"},{key:"apis",label:"APIs",singular:"API endpoint",icon:"code",tone:"var(--dm-success)",path:"/api-endpoints/new"}];export function projectFromPath(e,{settings:t=!1}={}){const r=String(e||"").replace(/^#/,"").split("?")[0].replace(/\/+$/,""),n=t?r.match(/^\/projects\/([^/]+)\/settings$/):r.match(/^\/projects\/([^/]+)$/);if(!n||!t&&(n[1]==="new"||n[1]==="edit"))return null;try{return decodeURIComponent(n[1])}catch{return null}}export function countsOf(e){return Object.fromEntries(Object.entries(e||{}).map(([t,r])=>[t,Array.isArray(r)?r.length:Number(r)||0]))}export function tilesFor(e,{slug:t,inline:r=[]}={}){const n=encodeURIComponent(t);let a=0;return{tiles:TYPES.map(o=>{const s=Number(e?.[o.key])||0;a+=s;const c=o.path.includes("?")?"&":"?";return{key:o.key,label:o.label,singular:o.singular,icon:o.icon,tone:o.tone,count:s,has:s>0,cls:s>0?"":"is-empty",inline:r.includes(o.key),listHash:`#/projects/${n}/${o.key}`,newHash:`#${o.path}${c}project=${n}`,countText:String(s)}}),total:a,totalText:`${a} artefact${a===1?"":"s"}`}}export function relTime(e,t=Date.now()){if(!e)return"";const r=new Date(e).getTime();if(Number.isNaN(r))return"";const n=Math.max(0,Math.round((t-r)/1e3)),a=Math.round(n/60),i=Math.round(a/60),o=Math.round(i/24),s=(l,u)=>`${l} ${u}${l===1?"":"s"} ago`;if(n<45)return"just now";if(a<60)return s(a,"min");if(i<24)return s(i,"hour");if(o<30)return s(o,"day");const c=Math.round(o/30);return c<12?s(c,"month"):s(Math.round(o/365),"year")}export function settingsForm(e={}){return{name:String(e.name??""),description:String(e.description??""),icon:String(e.icon||"folder"),rootUrl:String(e.rootUrl??""),sortOrder:String(e.sortOrder??0),enabled:e.enabled!==!1}}export function settingsSnapshot(e={}){return JSON.stringify({name:String(e.name??"").trim(),description:String(e.description??"").trim(),icon:String(e.icon??"").trim()||"folder",rootUrl:String(e.rootUrl??"").trim(),sortOrder:String(Number.parseInt(e.sortOrder,10)||0),enabled:e.enabled!==!1})}export function settingsPayload(e,t={}){return p(e,{isNew:!1,isProtected:t.protected===!0,slug:t.slug||""})}export function settingsRules(e={}){const t=e.slug===m,r=e.protected===!0;return{isCore:t,isProtected:r,canToggle:!t,rootLocked:r,canDelete:!r,canUntag:!r}}export const draftKey=e=>`project-settings.draft.${e}`;export function untagSummary(e){const t=TYPES.map(n=>[n,Number(e?.[n.key])||0]).filter(([,n])=>n>0).map(([n,a])=>`${a} ${a===1?n.singular:n.key==="apis"?"API endpoints":n.label.toLowerCase()}`);return{total:Object.values(e||{}).reduce((n,a)=>n+(Number(a)||0),0),text:t.length?t.join(", "):"nothing"}}
|
|
@@ -1,4 +1 @@
|
|
|
1
|
-
import{insertAtCursor as se}from"./markdown-toolbar.js";const le=20,V={render:{label:"Render",icon:"edit-3",color:"var(--dm-info,#3b82f6)"},wait:{label:"Wait",icon:"clock",color:"var(--dm-warning,#f59e0b)"},undo:{label:"Undo",icon:"corner-up-left",color:"var(--dm-secondary,#a855f7)"},redraw:{label:"Redraw",icon:"refresh-cw",color:"var(--dm-success,#22c55e)"}},T="padding:.4rem .6rem;background:var(--dm-input-bg,#1a1a1a);border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text,#eee);font-size:.9em;box-sizing:border-box;width:100%;",ce="display:block;font-size:.7rem;font-weight:600;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;margin-bottom:.25rem;",ae="display:flex;align-items:center;gap:.5rem;cursor:pointer;font-size:.85em;";export function openScribeComposer(n){const r=de(),e=[];let o=null;const u=E.slideover({title:"Scribe Composer",size:"lg",position:"right"});be();const b=document.createElement("div");b.className="scribe-composer",b.style.cssText="display:flex;flex-direction:column;height:100%;";const y=document.createElement("div");y.style.cssText="padding:1rem 1.25rem;border-bottom:1px solid var(--dm-border,#333);background:var(--dm-background-alt,#0f0f17);";const g=document.createElement("div");g.style.cssText="min-height:3.5rem;padding:.85rem 1rem;background:var(--dm-surface,#1a1a1a);border:1px dashed var(--dm-border,#333);border-radius:6px;font-size:1.25rem;line-height:1.5;overflow-wrap:anywhere;",y.appendChild(g);const w=document.createElement("div");w.style.cssText="display:flex;gap:.5rem;margin-top:.75rem;align-items:center;";const $=ee("rotate-cw","Replay"),B=ee("code","Show code"),k=document.createElement("span");k.style.cssText="font-size:.8em;color:var(--dm-text-muted,#aaa);flex:1;text-align:right;margin-right:.5rem;",w.appendChild($),w.appendChild(k),w.appendChild(B),y.appendChild(w);const p=document.createElement("pre");p.style.cssText="display:none;margin:.75rem 0 0;padding:.6rem .75rem;background:var(--dm-surface,#0a0a0a);border:1px solid var(--dm-border,#333);border-radius:4px;font-family:monospace;font-size:.78em;white-space:pre-wrap;word-break:break-all;color:var(--dm-text-muted,#bbb);max-height:200px;overflow:auto;",y.appendChild(p),B.addEventListener("click",()=>{const t=p.style.display!=="none";p.style.display=t?"none":"block"}),$.addEventListener("click",()=>X()),b.appendChild(y);const R=document.createElement("div");R.style.cssText="display:flex;flex:1;min-height:0;overflow:hidden;";const F=ue(r,()=>{v(),x()});R.appendChild(F.el);const M=document.createElement("main");M.style.cssText="flex:1;overflow-y:auto;padding:1rem 1.25rem;display:flex;flex-direction:column;";const W=document.createElement("h4");W.textContent="Actions",W.style.cssText="margin:0 0 .75rem;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--dm-text-muted,#aaa);",M.appendChild(W);const z=document.createElement("div");z.style.cssText="display:flex;flex-direction:column;gap:.6rem;",M.appendChild(z);const H=document.createElement("div");H.style.cssText="display:flex;gap:.4rem;margin-top:.9rem;flex-wrap:wrap;",Object.entries(V).forEach(([t,s])=>{const l=document.createElement("button");l.type="button",l.style.cssText=`padding:.4rem .8rem;background:transparent;border:1px dashed color-mix(in srgb, ${s.color} 40%, transparent);border-radius:4px;color:${s.color};cursor:pointer;font-size:.85em;display:inline-flex;align-items:center;gap:.35rem;`;const d=document.createElement("span");d.setAttribute("data-icon",s.icon),l.appendChild(d),l.appendChild(document.createTextNode(` + ${s.label}`)),l.addEventListener("click",()=>{v(),r.actions.push(ie(t)),j(),x()}),H.appendChild(l)}),M.appendChild(H),R.appendChild(M),b.appendChild(R);const L=document.createElement("div");L.style.cssText="display:flex;padding:.75rem 1rem;border-top:1px solid var(--dm-border,#333);background:var(--dm-background-alt,#0f0f17);gap:.5rem;align-items:center;";const S=document.createElement("button");S.type="button",S.className="btn btn-ghost btn-sm",S.textContent="Undo (Ctrl+Z)",S.addEventListener("click",()=>J()),L.appendChild(S);const U=document.createElement("span");U.style.cssText="font-size:.75em;color:var(--dm-text-muted,#aaa);",L.appendChild(U);const G=document.createElement("div");G.style.flex="1",L.appendChild(G);const A=document.createElement("button");A.type="button",A.className="btn btn-ghost",A.textContent="Cancel",A.addEventListener("click",()=>u.close()),L.appendChild(A);const D=document.createElement("button");D.type="button",D.className="btn btn-primary",D.textContent="Insert Scribe",D.addEventListener("click",()=>{se(n,Q()),u.close()}),L.appendChild(D),b.appendChild(L),u.setContent(b),b.addEventListener("keydown",t=>{const s=(t.key||"").toLowerCase();(t.ctrlKey||t.metaKey)&&s==="z"&&!t.shiftKey&&e.length>0&&(t.preventDefault(),t.stopPropagation(),J())}),j(),x(),P(),requestAnimationFrame(()=>{Domma.icons.scan()}),u.open();function v(){e.push(JSON.stringify(r)),e.length>le&&e.shift(),P()}function J(){if(e.length===0){E.toast("Nothing to undo",{type:"info",duration:700});return}const t=JSON.parse(e.pop());Object.assign(r.globals,t.globals),r.actions.length=0,t.actions.forEach(s=>r.actions.push(s)),F.sync(),j(),x(),P(),E.toast("Undone",{type:"info",duration:500})}function P(){U.textContent=e.length===0?"":`${e.length} step${e.length===1?"":"s"} stored`,S.disabled=e.length===0,S.style.opacity=e.length===0?".4":"1"}function x(t=150){clearTimeout(o),o=setTimeout(X,t)}function X(){te(g),g.style.color="",g.style.fontStyle="",p.textContent=Q();const t=r.actions.filter(c=>c.type==="render"||c.type==="redraw"?(c.text||"").length>0:!0);if(t.length===0){g.textContent="(add an action to preview)",g.style.color="var(--dm-text-muted,#666)",g.style.fontStyle="italic",k.textContent="";return}const s=document.createElement("span");s.className="dm-fx-scribe",g.appendChild(s);const l=r.globals,d={speed:l.speed,deleteSpeed:l.deleteSpeed,cursor:l.cursor,cursorType:l.cursorType,loop:l.loop,loopDelay:l.loopDelay,pauseOnHover:l.pauseOnHover,actions:pe(t)};l.cursorChar&&(d.cursorChar=l.cursorChar);try{E.scribe(s,d),k.textContent=`${t.length} action${t.length===1?"":"s"}`}catch{k.textContent="Preview error"}}function j(){if(te(z),r.actions.length===0){const t=document.createElement("p");t.textContent="No actions yet - add one from the buttons below.",t.style.cssText="font-size:.85em;color:var(--dm-text-muted,#888);font-style:italic;margin:.5rem 0;",z.appendChild(t);return}r.actions.forEach((t,s)=>{z.appendChild(oe(t,s))}),requestAnimationFrame(()=>Domma.icons.scan())}function oe(t,s){const l=V[t.type],d=document.createElement("div");d.style.cssText=`border:1px solid var(--dm-border,#333);border-left:3px solid ${l.color};border-radius:4px;padding:.6rem .75rem;background:var(--dm-surface,#15151a);`;const c=document.createElement("div");c.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem;";const N=document.createElement("span");N.style.cssText=`display:inline-flex;align-items:center;gap:.3rem;font-size:.72rem;font-weight:700;color:${l.color};text-transform:uppercase;letter-spacing:.06em;`;const a=document.createElement("span");a.setAttribute("data-icon",l.icon),N.appendChild(a),N.appendChild(document.createTextNode(l.label)),c.appendChild(N);const f=document.createElement("span");f.textContent=`#${s+1}`,f.style.cssText="font-size:.7rem;color:var(--dm-text-muted,#888);",c.appendChild(f);const m=document.createElement("div");m.style.flex="1",c.appendChild(m);const i=q("chevron-up","Move up");s===0&&(i.disabled=!0,i.style.opacity=".3"),i.addEventListener("click",()=>Z(s,-1)),c.appendChild(i);const h=q("chevron-down","Move down");s===r.actions.length-1&&(h.disabled=!0,h.style.opacity=".3"),h.addEventListener("click",()=>Z(s,1)),c.appendChild(h);const C=q("x","Remove");return C.addEventListener("click",()=>{v(),r.actions.splice(s,1),j(),x()}),c.appendChild(C),d.appendChild(c),d.appendChild(re(t)),d}function Z(t,s){const l=t+s;if(l<0||l>=r.actions.length)return;v();const[d]=r.actions.splice(t,1);r.actions.splice(l,0,d),j(),x()}function re(t){const s=document.createElement("div");if(s.style.cssText="display:flex;flex-direction:column;gap:.5rem;",t.type==="render"||t.type==="redraw"){const a=document.createElement("textarea");a.value=t.text||"",a.placeholder=t.type==="redraw"?"New text - clears everything first, then types this":"Text to type out",a.rows=2,a.style.cssText=T+"resize:vertical;font-family:inherit;min-height:2.2rem;";let f=a.value;a.addEventListener("input",()=>{t.text=a.value,x()}),a.addEventListener("blur",()=>{a.value!==f&&(v(),f=a.value)}),s.appendChild(a);const m=document.createElement("div");m.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.8em;";const i=document.createElement("label");i.textContent="Effect:",i.style.color="var(--dm-text-muted,#aaa)";const h=document.createElement("select");return h.style.cssText=T+"width:auto;padding:.25rem .4rem;",["none","fade","bounce","glow"].forEach(C=>{const _=document.createElement("option");_.value=C,_.textContent=C.charAt(0).toUpperCase()+C.slice(1),h.appendChild(_)}),h.value=t.effect||"none",h.addEventListener("change",()=>{v(),t.effect=h.value,x()}),m.appendChild(i),m.appendChild(h),s.appendChild(m),s}if(t.type==="wait"){const a=document.createElement("div");a.style.cssText="display:flex;gap:.4rem;align-items:center;";const f=document.createElement("input");f.type="number",f.min="0",f.value=String(t.duration??1e3),f.style.cssText=T+"flex:1;",f.addEventListener("input",()=>{const i=parseInt(f.value,10);!Number.isNaN(i)&&i>=0&&(t.duration=i,x())}),f.addEventListener("blur",()=>v()),a.appendChild(f);const m=document.createElement("select");return m.style.cssText=T+"width:auto;",[["ms","ms"],["s","seconds"]].forEach(([i,h])=>{const C=document.createElement("option");C.value=i,C.textContent=h,m.appendChild(C)}),m.value=t.unit||"ms",m.addEventListener("change",()=>{v(),t.unit=m.value,x()}),a.appendChild(m),s.appendChild(a),s}const l=document.createElement("div");l.style.cssText="display:flex;gap:.75rem;align-items:center;flex-wrap:wrap;font-size:.85em;";const d=document.createElement("input");d.type="number",d.min="1",d.value=String(t.count||5),d.style.cssText=T+"width:5rem;",d.addEventListener("input",()=>{const a=parseInt(d.value,10);!Number.isNaN(a)&&a>0&&(t.count=a,x())}),d.addEventListener("blur",()=>v());const c=`undo-mode-${Math.random().toString(36).slice(2)}`;return[["last","Last render"],["all","All text"],["count","Exactly N chars"]].forEach(([a,f])=>{const m=document.createElement("label");m.style.cssText="display:inline-flex;align-items:center;gap:.3rem;cursor:pointer;";const i=document.createElement("input");i.type="radio",i.name=c,i.value=a,i.checked=(t.mode||"last")===a,i.addEventListener("change",()=>{i.checked&&(v(),t.mode=a,d.style.display=a==="count"?"":"none",x())}),m.appendChild(i),m.appendChild(document.createTextNode(f)),l.appendChild(m),a==="count"&&(l.appendChild(d),d.style.display=(t.mode||"last")==="count"?"":"none")}),s.appendChild(l),s}function Q(){const t=r.globals,s=[];t.speed!==50&&s.push(`speed="${t.speed}"`),t.deleteSpeed!==30&&s.push(`delete-speed="${t.deleteSpeed}"`),t.cursor?(t.cursorType!=="caret"&&s.push(`cursor-type="${t.cursorType}"`),t.cursorChar&&s.push(`cursor-char="${he(t.cursorChar)}"`)):s.push('cursor="false"'),t.loop&&(s.push('loop="true"'),t.loopDelay!==1e3&&s.push(`loop-delay="${t.loopDelay}"`)),t.pauseOnHover&&s.push('pause-on-hover="true"');const l=s.length?" "+s.join(" "):"",d=r.actions.map(c=>c.type==="render"?`[render${c.effect&&c.effect!=="none"?` effect="${c.effect}"`:""}]${c.text||""}[/render]`:c.type==="redraw"?`[redraw${c.effect&&c.effect!=="none"?` effect="${c.effect}"`:""}]${c.text||""}[/redraw]`:c.type==="wait"?`[wait]${c.duration}${c.unit==="s"?"s":""}[/wait]`:c.mode==="all"?'[undo all="true" /]':c.mode==="count"?`[undo count="${c.count}" /]`:"[undo /]");return`[scribe${l}]
|
|
2
|
-
${d.join(`
|
|
3
|
-
`)}
|
|
4
|
-
[/scribe]`}}function de(){return{globals:{speed:50,deleteSpeed:30,cursor:!0,cursorType:"caret",cursorChar:"",loop:!1,loopDelay:1e3,pauseOnHover:!1},actions:[{type:"render",text:"We build things that matter.",effect:"none"},{type:"wait",duration:1500,unit:"ms"},{type:"redraw",text:"We create solutions that last.",effect:"none"},{type:"wait",duration:1500,unit:"ms"}]}}function ie(n){switch(n){case"render":return{type:"render",text:"",effect:"none"};case"wait":return{type:"wait",duration:1e3,unit:"ms"};case"undo":return{type:"undo",mode:"last",count:5};case"redraw":return{type:"redraw",text:"",effect:"none"}}return{type:"render",text:"",effect:"none"}}function pe(n){const r=[];return n.forEach(e=>{if(e.type==="render"){const o={render:e.text||""};e.effect&&e.effect!=="none"&&(o.effect=e.effect),r.push(o)}else if(e.type==="redraw"){r.push({undoRender:"all"});const o={render:e.text||""};e.effect&&e.effect!=="none"&&(o.effect=e.effect),r.push(o)}else e.type==="wait"?r.push({wait:e.unit==="s"?(e.duration||0)*1e3:e.duration||0}):e.type==="undo"&&(e.mode==="all"?r.push({undoRender:"all"}):e.mode==="count"?r.push({undoRender:e.count}):r.push({undoRender:!0}))}),r}function ue(n,r){const e=document.createElement("aside");e.style.cssText="width:240px;flex-shrink:0;padding:1rem 1.25rem;border-right:1px solid var(--dm-border,#333);overflow-y:auto;display:flex;flex-direction:column;gap:.75rem;background:var(--dm-background-alt,#0c0c12);";const o=document.createElement("h4");o.textContent="Global settings",o.style.cssText="margin:0 0 .25rem;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--dm-text-muted,#aaa);",e.appendChild(o);const u=K(n.globals.speed,p=>{n.globals.speed=p,r()}),b=K(n.globals.deleteSpeed,p=>{n.globals.deleteSpeed=p,r()}),y=Y(n.globals.cursor,p=>{n.globals.cursor=p,r()},"Show cursor"),g=fe([["caret","Caret (|)"],["block","Block (\u2588)"],["underline","Underline (_)"]],n.globals.cursorType,p=>{n.globals.cursorType=p,r()}),w=me(n.globals.cursorChar,"\u258C (optional override)",p=>{n.globals.cursorChar=p,r()}),$=Y(n.globals.loop,p=>{n.globals.loop=p,r()},"Loop forever"),B=K(n.globals.loopDelay,p=>{n.globals.loopDelay=p,r()},0),k=Y(n.globals.pauseOnHover,p=>{n.globals.pauseOnHover=p,r()},"Pause on hover");return e.appendChild(O("Speed (ms / char)",u)),e.appendChild(O("Delete speed (ms / char)",b)),e.appendChild(y.wrap),e.appendChild(O("Cursor style",g)),e.appendChild(O("Custom cursor char",w)),e.appendChild($.wrap),e.appendChild(O("Loop delay (ms)",B)),e.appendChild(k.wrap),{el:e,sync(){u.value=String(n.globals.speed),b.value=String(n.globals.deleteSpeed),y.input.checked=n.globals.cursor,g.value=n.globals.cursorType,w.value=n.globals.cursorChar,$.input.checked=n.globals.loop,B.value=String(n.globals.loopDelay),k.input.checked=n.globals.pauseOnHover}}}function O(n,r){const e=document.createElement("div"),o=document.createElement("label");return o.style.cssText=ce,o.textContent=n,e.appendChild(o),e.appendChild(r),e}function K(n,r,e=1){const o=document.createElement("input");return o.type="number",o.min=String(e),o.value=String(n),o.style.cssText=T,o.addEventListener("input",()=>{const u=parseInt(o.value,10);!Number.isNaN(u)&&u>=e&&r(u)}),o}function me(n,r,e){const o=document.createElement("input");return o.type="text",o.value=n,o.placeholder=r,o.style.cssText=T,o.addEventListener("input",()=>e(o.value)),o}function fe(n,r,e){const o=document.createElement("select");return o.style.cssText=T,n.forEach(([u,b])=>{const y=document.createElement("option");y.value=u,y.textContent=b,o.appendChild(y)}),o.value=r,o.addEventListener("change",()=>e(o.value)),o}function Y(n,r,e){const o=document.createElement("label");o.style.cssText=ae;const u=document.createElement("input");return u.type="checkbox",u.checked=n,u.addEventListener("change",()=>r(u.checked)),o.appendChild(u),o.appendChild(document.createTextNode(e)),{wrap:o,input:u}}function ee(n,r){const e=document.createElement("button");e.type="button",e.className="btn btn-ghost btn-sm",e.style.cssText="display:inline-flex;align-items:center;gap:.35rem;";const o=document.createElement("span");return o.setAttribute("data-icon",n),e.appendChild(o),e.appendChild(document.createTextNode(r)),e}function q(n,r){const e=document.createElement("button");e.type="button",e.title=r,e.style.cssText="padding:.25rem .4rem;background:transparent;border:1px solid var(--dm-border,#333);border-radius:3px;color:var(--dm-text-muted,#aaa);cursor:pointer;display:inline-flex;align-items:center;";const o=document.createElement("span");return o.setAttribute("data-icon",n),e.appendChild(o),e}function te(n){for(;n.firstChild;)n.removeChild(n.firstChild)}function he(n){return String(n).replace(/&/g,"&").replace(/"/g,""")}let ne=!1;function be(){ne||(ne=!0,I.register("edit-3",{viewBox:"0 0 24 24",paths:["M12 20h9","M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("clock",{viewBox:"0 0 24 24",paths:["M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z","M12 6v6l4 2"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("corner-up-left",{viewBox:"0 0 24 24",paths:["M9 14 4 9l5-5","M20 20v-7a4 4 0 0 0-4-4H4"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("refresh-cw",{viewBox:"0 0 24 24",paths:["M23 4v6h-6","M1 20v-6h6","M3.51 9a9 9 0 0 1 14.85-3.36L23 10","M20.49 15a9 9 0 0 1-14.85 3.36L1 14"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("rotate-cw",{viewBox:"0 0 24 24",paths:["M23 4v6h-6","M20.49 15a9 9 0 1 1-2.12-9.36L23 10"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("code",{viewBox:"0 0 24 24",paths:["M16 18l6-6-6-6","M8 6l-6 6 6 6"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("chevron-up",{viewBox:"0 0 24 24",path:"M18 15l-6-6-6 6",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("chevron-down",{viewBox:"0 0 24 24",path:"M6 9l6 6 6-6",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("x",{viewBox:"0 0 24 24",paths:["M18 6L6 18","M6 6l12 12"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}))}
|
|
1
|
+
import{insertAtCursor as re}from"./markdown-toolbar.js";import{SCRIBE_FIELDS as ae,scribeDefaults as se,scribeLoop as ce,stateToScribeActions as ie,buildScribeShortcode as le}from"./scribe-model.js";const de=20,Q={render:{label:"Render",icon:"edit-3",color:"var(--dm-info,#3b82f6)"},wait:{label:"Wait",icon:"clock",color:"var(--dm-warning,#f59e0b)"},undo:{label:"Undo",icon:"corner-up-left",color:"var(--dm-secondary,#a855f7)"},redraw:{label:"Redraw",icon:"refresh-cw",color:"var(--dm-success,#22c55e)"}},L="padding:.4rem .6rem;background:var(--dm-input-bg,#1a1a1a);border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text,#eee);font-size:.9em;box-sizing:border-box;width:100%;",pe="display:block;font-size:.7rem;font-weight:600;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;margin-bottom:.25rem;",ue="display:flex;align-items:center;gap:.5rem;cursor:pointer;font-size:.85em;";export function openScribeComposer(s){const r=me(),n=[];let t=null,p=null;function c(){clearTimeout(t),p?.destroy(),p=null}const h=E.slideover({title:"Scribe Composer",size:"lg",position:"right",onClose:c});Ce();const b=document.createElement("div");b.className="scribe-composer",b.style.cssText="display:flex;flex-direction:column;height:100%;";const y=document.createElement("div");y.style.cssText="padding:1rem 1.25rem;border-bottom:1px solid var(--dm-border,#333);background:var(--dm-background-alt,#0f0f17);";const k=document.createElement("div");k.style.cssText="min-height:3.5rem;padding:.85rem 1rem;background:var(--dm-surface,#1a1a1a);border:1px dashed var(--dm-border,#333);border-radius:6px;font-size:1.25rem;line-height:1.5;overflow-wrap:anywhere;",y.appendChild(k);const N=document.createElement("div");N.style.cssText="display:flex;gap:.5rem;margin-top:.75rem;align-items:center;";const Y=V("rotate-cw","Replay"),q=V("code","Show code"),B=document.createElement("span");B.style.cssText="font-size:.8em;color:var(--dm-text-muted,#aaa);flex:1;text-align:right;margin-right:.5rem;",N.appendChild(Y),N.appendChild(B),N.appendChild(q),y.appendChild(N);const M=document.createElement("pre");M.style.cssText="display:none;margin:.75rem 0 0;padding:.6rem .75rem;background:var(--dm-surface,#0a0a0a);border:1px solid var(--dm-border,#333);border-radius:4px;font-family:monospace;font-size:.78em;white-space:pre-wrap;word-break:break-all;color:var(--dm-text-muted,#bbb);max-height:200px;overflow:auto;",y.appendChild(M),q.addEventListener("click",()=>{const e=M.style.display!=="none";M.style.display=e?"none":"block"}),Y.addEventListener("click",()=>{J(),p?.resume()}),b.appendChild(y);const R=document.createElement("div");R.style.cssText="display:flex;flex:1;min-height:0;overflow:hidden;";const F=fe(r,()=>{v(),x()});R.appendChild(F.el);const z=document.createElement("main");z.style.cssText="flex:1;overflow-y:auto;padding:1rem 1.25rem;display:flex;flex-direction:column;";const P=document.createElement("h4");P.textContent="Actions",P.style.cssText="margin:0 0 .75rem;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--dm-text-muted,#aaa);",z.appendChild(P);const A=document.createElement("div");A.style.cssText="display:flex;flex-direction:column;gap:.6rem;",z.appendChild(A);const U=document.createElement("div");U.style.cssText="display:flex;gap:.4rem;margin-top:.9rem;flex-wrap:wrap;",Object.entries(Q).forEach(([e,a])=>{const i=document.createElement("button");i.type="button",i.style.cssText=`padding:.4rem .8rem;background:transparent;border:1px dashed color-mix(in srgb, ${a.color} 40%, transparent);border-radius:4px;color:${a.color};cursor:pointer;font-size:.85em;display:inline-flex;align-items:center;gap:.35rem;`;const l=document.createElement("span");l.setAttribute("data-icon",a.icon),i.appendChild(l),i.appendChild(document.createTextNode(` + ${a.label}`)),i.addEventListener("click",()=>{v(),r.actions.push(he(e)),$(),x()}),U.appendChild(i)}),z.appendChild(U),R.appendChild(z),b.appendChild(R);const T=document.createElement("div");T.style.cssText="display:flex;padding:.75rem 1rem;border-top:1px solid var(--dm-border,#333);background:var(--dm-background-alt,#0f0f17);gap:.5rem;align-items:center;";const S=document.createElement("button");S.type="button",S.className="btn btn-ghost btn-sm",S.textContent="Undo (Ctrl+Z)",S.addEventListener("click",()=>H()),T.appendChild(S);const D=document.createElement("span");D.style.cssText="font-size:.75em;color:var(--dm-text-muted,#aaa);",T.appendChild(D);const G=document.createElement("div");G.style.flex="1",T.appendChild(G);const W=document.createElement("button");W.type="button",W.className="btn btn-ghost",W.textContent="Cancel",W.addEventListener("click",()=>h.close()),T.appendChild(W);const j=document.createElement("button");j.type="button",j.className="btn btn-primary",j.textContent="Insert Scribe",j.addEventListener("click",()=>{re(s,Z()),h.close()}),T.appendChild(j),b.appendChild(T),h.setContent(b),b.addEventListener("keydown",e=>{const a=(e.key||"").toLowerCase();(e.ctrlKey||e.metaKey)&&a==="z"&&!e.shiftKey&&n.length>0&&(e.preventDefault(),e.stopPropagation(),H())}),$(),x(),O(),requestAnimationFrame(()=>{Domma.icons.scan()}),h.open();function v(){n.push(JSON.stringify(r)),n.length>de&&n.shift(),O()}function H(){if(n.length===0){E.toast("Nothing to undo",{type:"info",duration:700});return}const e=JSON.parse(n.pop());Object.assign(r.globals,e.globals),r.actions.length=0,e.actions.forEach(a=>r.actions.push(a)),F.sync(),$(),x(),O(),E.toast("Undone",{type:"info",duration:500})}function O(){D.textContent=n.length===0?"":`${n.length} step${n.length===1?"":"s"} stored`,S.disabled=n.length===0,S.style.opacity=n.length===0?".4":"1"}function x(e=150){clearTimeout(t),t=setTimeout(J,e)}function J(){c(),ee(k),k.style.color="",k.style.fontStyle="",M.textContent=Z();const e=r.actions.filter(o=>o.type==="render"||o.type==="redraw"?(o.text||"").length>0:!0);if(e.length===0){k.textContent="(add an action to preview)",k.style.color="var(--dm-text-muted,#666)",k.style.fontStyle="italic",B.textContent="";return}const a=document.createElement("span");a.className="dm-fx-scribe",k.appendChild(a);const i=r.globals,{effect:l,controls:g,...w}=i;w.actions=ie(e,l);try{p=E.scribe(a,w),B.textContent=i.autoStart?`${e.length} action${e.length===1?"":"s"}`:"Press Replay to start"}catch{B.textContent="Preview error"}}function $(){if(ee(A),r.actions.length===0){const e=document.createElement("p");e.textContent="No actions yet - add one from the buttons below.",e.style.cssText="font-size:.85em;color:var(--dm-text-muted,#888);font-style:italic;margin:.5rem 0;",A.appendChild(e);return}r.actions.forEach((e,a)=>{A.appendChild(ne(e,a))}),requestAnimationFrame(()=>Domma.icons.scan())}function ne(e,a){const i=Q[e.type],l=document.createElement("div");l.style.cssText=`border:1px solid var(--dm-border,#333);border-left:3px solid ${i.color};border-radius:4px;padding:.6rem .75rem;background:var(--dm-surface,#15151a);`;const g=document.createElement("div");g.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem;";const w=document.createElement("span");w.style.cssText=`display:inline-flex;align-items:center;gap:.3rem;font-size:.72rem;font-weight:700;color:${i.color};text-transform:uppercase;letter-spacing:.06em;`;const o=document.createElement("span");o.setAttribute("data-icon",i.icon),w.appendChild(o),w.appendChild(document.createTextNode(i.label)),g.appendChild(w);const m=document.createElement("span");m.textContent=`#${a+1}`,m.style.cssText="font-size:.7rem;color:var(--dm-text-muted,#888);",g.appendChild(m);const u=document.createElement("div");u.style.flex="1",g.appendChild(u);const d=K("chevron-up","Move up");a===0&&(d.disabled=!0,d.style.opacity=".3"),d.addEventListener("click",()=>X(a,-1)),g.appendChild(d);const f=K("chevron-down","Move down");a===r.actions.length-1&&(f.disabled=!0,f.style.opacity=".3"),f.addEventListener("click",()=>X(a,1)),g.appendChild(f);const C=K("x","Remove");return C.addEventListener("click",()=>{v(),r.actions.splice(a,1),$(),x()}),g.appendChild(C),l.appendChild(g),l.appendChild(oe(e)),l}function X(e,a){const i=e+a;if(i<0||i>=r.actions.length)return;v();const[l]=r.actions.splice(e,1);r.actions.splice(i,0,l),$(),x()}function oe(e){const a=document.createElement("div");if(a.style.cssText="display:flex;flex-direction:column;gap:.5rem;",e.type==="render"||e.type==="redraw"){const o=document.createElement("textarea");o.value=e.text||"",o.placeholder=e.type==="redraw"?"New text - clears everything first, then types this":"Text to type out",o.rows=2,o.style.cssText=L+"resize:vertical;font-family:inherit;min-height:2.2rem;";let m=o.value;o.addEventListener("input",()=>{e.text=o.value,x()}),o.addEventListener("blur",()=>{o.value!==m&&(v(),m=o.value)}),a.appendChild(o);const u=document.createElement("div");u.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.8em;";const d=document.createElement("label");d.textContent="Effect:",d.style.color="var(--dm-text-muted,#aaa)";const f=document.createElement("select");return f.style.cssText=L+"width:auto;padding:.25rem .4rem;",["inherit","none","fade","bounce","glow"].forEach(C=>{const _=document.createElement("option");_.value=C,_.textContent=C.charAt(0).toUpperCase()+C.slice(1),f.appendChild(_)}),f.value=e.effect||"none",f.addEventListener("change",()=>{v(),e.effect=f.value,x()}),u.appendChild(d),u.appendChild(f),a.appendChild(u),a}if(e.type==="wait"){const o=document.createElement("div");o.style.cssText="display:flex;gap:.4rem;align-items:center;";const m=document.createElement("input");m.type="number",m.min="0",m.value=String(e.duration??1e3),m.style.cssText=L+"flex:1;",m.addEventListener("input",()=>{const d=parseInt(m.value,10);!Number.isNaN(d)&&d>=0&&(e.duration=d,x())}),m.addEventListener("blur",()=>v()),o.appendChild(m);const u=document.createElement("select");return u.style.cssText=L+"width:auto;",[["ms","ms"],["s","seconds"]].forEach(([d,f])=>{const C=document.createElement("option");C.value=d,C.textContent=f,u.appendChild(C)}),u.value=e.unit||"ms",u.addEventListener("change",()=>{v(),e.unit=u.value,x()}),o.appendChild(u),a.appendChild(o),a}const i=document.createElement("div");i.style.cssText="display:flex;gap:.75rem;align-items:center;flex-wrap:wrap;font-size:.85em;";const l=document.createElement("input");l.type="number",l.min="1",l.value=String(e.count||5),l.style.cssText=L+"width:5rem;",l.addEventListener("input",()=>{const o=parseInt(l.value,10);!Number.isNaN(o)&&o>0&&(e.count=o,x())}),l.addEventListener("blur",()=>v());const g=`undo-mode-${Math.random().toString(36).slice(2)}`;return[["last","Last render"],["all","All text"],["count","Exactly N units"]].forEach(([o,m])=>{const u=document.createElement("label");u.style.cssText="display:inline-flex;align-items:center;gap:.3rem;cursor:pointer;";const d=document.createElement("input");d.type="radio",d.name=g,d.value=o,d.checked=(e.mode||"last")===o,d.addEventListener("change",()=>{d.checked&&(v(),e.mode=o,l.style.display=o==="count"?"":"none",x())}),u.appendChild(d),u.appendChild(document.createTextNode(m)),i.appendChild(u),o==="count"&&(i.appendChild(l),l.style.display=(e.mode||"last")==="count"?"":"none")}),a.appendChild(i),a}function Z(){return le(r.globals,r.actions)}}function me(){return{globals:se(),actions:[{type:"render",text:"We build things that matter.",effect:"inherit"},{type:"wait",duration:1500,unit:"ms"},{type:"redraw",text:"We create solutions that last.",effect:"inherit"},{type:"wait",duration:1500,unit:"ms"}]}}function he(s){switch(s){case"render":return{type:"render",text:"",effect:"inherit"};case"wait":return{type:"wait",duration:1e3,unit:"ms"};case"undo":return{type:"undo",mode:"last",count:5};case"redraw":return{type:"redraw",text:"",effect:"inherit"}}return{type:"render",text:"",effect:"inherit"}}function fe(s,r){const n=document.createElement("aside");n.style.cssText="width:240px;flex-shrink:0;padding:1rem 1.25rem;border-right:1px solid var(--dm-border,#333);overflow-y:auto;display:flex;flex-direction:column;gap:.75rem;background:var(--dm-background-alt,#0c0c12);";const t=document.createElement("h4");t.textContent="Global settings",t.style.cssText="margin:0 0 .25rem;font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--dm-text-muted,#aaa);",n.appendChild(t);const p=ae.map(c=>{const h=y=>{s.globals[c.key]=c.key==="loop"?ce(y):y,r()};let b;if(c.type==="checkbox"){const y=ve(s.globals[c.key],h,c.label);n.appendChild(y.wrap),b=y.input}else b=c.type==="select"?ge(c.options,s.globals[c.key],h):c.type==="number"?xe(s.globals[c.key],h,c.min):ye(s.globals[c.key],"",h),n.appendChild(be(c.label,b));return{field:c,input:b}});return{el:n,sync(){p.forEach(({field:c,input:h})=>{c.type==="checkbox"?h.checked=s.globals[c.key]:h.value=String(s.globals[c.key])})}}}function be(s,r){const n=document.createElement("div"),t=document.createElement("label");return t.style.cssText=pe,t.textContent=s,n.appendChild(t),n.appendChild(r),n}function xe(s,r,n=1){const t=document.createElement("input");return t.type="number",t.min=String(n),t.value=String(s),t.style.cssText=L,t.addEventListener("input",()=>{const p=Number(t.value);!Number.isNaN(p)&&p>=n&&r(p)}),t}function ye(s,r,n){const t=document.createElement("input");return t.type="text",t.value=s,t.placeholder=r,t.style.cssText=L,t.addEventListener("input",()=>n(t.value)),t}function ge(s,r,n){const t=document.createElement("select");return t.style.cssText=L,s.forEach(([p,c])=>{const h=document.createElement("option");h.value=p,h.textContent=c,t.appendChild(h)}),t.value=r,t.addEventListener("change",()=>n(t.value)),t}function ve(s,r,n){const t=document.createElement("label");t.style.cssText=ue;const p=document.createElement("input");return p.type="checkbox",p.checked=s,p.addEventListener("change",()=>r(p.checked)),t.appendChild(p),t.appendChild(document.createTextNode(n)),{wrap:t,input:p}}function V(s,r){const n=document.createElement("button");n.type="button",n.className="btn btn-ghost btn-sm",n.style.cssText="display:inline-flex;align-items:center;gap:.35rem;";const t=document.createElement("span");return t.setAttribute("data-icon",s),n.appendChild(t),n.appendChild(document.createTextNode(r)),n}function K(s,r){const n=document.createElement("button");n.type="button",n.title=r,n.style.cssText="padding:.25rem .4rem;background:transparent;border:1px solid var(--dm-border,#333);border-radius:3px;color:var(--dm-text-muted,#aaa);cursor:pointer;display:inline-flex;align-items:center;";const t=document.createElement("span");return t.setAttribute("data-icon",s),n.appendChild(t),n}function ee(s){for(;s.firstChild;)s.removeChild(s.firstChild)}function we(s){return String(s).replace(/&/g,"&").replace(/"/g,""")}let te=!1;function Ce(){te||(te=!0,I.register("edit-3",{viewBox:"0 0 24 24",paths:["M12 20h9","M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("clock",{viewBox:"0 0 24 24",paths:["M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z","M12 6v6l4 2"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("corner-up-left",{viewBox:"0 0 24 24",paths:["M9 14 4 9l5-5","M20 20v-7a4 4 0 0 0-4-4H4"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("refresh-cw",{viewBox:"0 0 24 24",paths:["M23 4v6h-6","M1 20v-6h6","M3.51 9a9 9 0 0 1 14.85-3.36L23 10","M20.49 15a9 9 0 0 1-14.85 3.36L1 14"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("rotate-cw",{viewBox:"0 0 24 24",paths:["M23 4v6h-6","M20.49 15a9 9 0 1 1-2.12-9.36L23 10"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("code",{viewBox:"0 0 24 24",paths:["M16 18l6-6-6-6","M8 6l-6 6 6 6"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("chevron-up",{viewBox:"0 0 24 24",path:"M18 15l-6-6-6 6",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("chevron-down",{viewBox:"0 0 24 24",path:"M6 9l6 6 6-6",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("x",{viewBox:"0 0 24 24",paths:["M18 6L6 18","M6 6l12 12"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}))}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export const SCRIBE_FIELDS=[{name:"mode",key:"mode",label:"Render mode",type:"select",default:"typewriter",options:[["typewriter","Characters"],["word","Words"],["sentence","Sentences"]]},{name:"effect",key:"effect",label:"Default entrance effect",type:"select",default:"none",options:["none","fade","bounce","glow"].map(r=>[r,r])},{name:"speed",key:"speed",label:"Typing speed (ms/unit)",type:"number",default:50,min:0},{name:"delete-speed",key:"deleteSpeed",label:"Delete speed (ms/unit)",type:"number",default:30,min:0},{name:"cursor",key:"cursor",label:"Show cursor",type:"checkbox",default:!0},{name:"cursor-char",key:"cursorChar",label:"Cursor character",type:"text",default:"|"},{name:"cursor-type",key:"cursorType",label:"Cursor style",type:"select",default:"caret",options:[["caret","Caret"],["block","Block"],["underline","Underline"]]},{name:"cursor-blink",key:"cursorBlink",label:"Blink cursor while idle",type:"checkbox",default:!0},{name:"loop",key:"loop",label:"Loop (false, true, or total plays)",type:"text",default:!1},{name:"loop-delay",key:"loopDelay",label:"Loop delay (ms)",type:"number",default:1e3,min:0},{name:"pause-on-hover",key:"pauseOnHover",label:"Pause on hover",type:"checkbox",default:!1},{name:"auto-start",key:"autoStart",label:"Start automatically",type:"checkbox",default:!0},{name:"respect-motion-preference",key:"respectMotionPreference",label:"Respect reduced motion",type:"checkbox",default:!0},{name:"controls",key:"controls",label:"Show playback controls",type:"checkbox",default:!1}];export function scribeDefaults(){return Object.fromEntries(SCRIBE_FIELDS.map(r=>[r.key,r.default]))}export function scribeLoop(r){if(r===!0||r==="true")return!0;if(r===!1||r==="false"||r==="")return!1;const o=Number(r);return Number.isInteger(o)&&o>=1?o:!1}export function stateToScribeActions(r,o="none"){const n=[];return r.forEach(t=>{t.type==="render"||t.type==="redraw"?(t.type==="redraw"&&n.push({undoRender:"all"}),n.push({render:t.text||"",effect:!t.effect||t.effect==="inherit"?o:t.effect})):t.type==="wait"?n.push({wait:t.unit==="s"?(t.duration||0)*1e3:t.duration||0}):t.type==="undo"&&n.push({undoRender:t.mode==="all"?"all":t.mode==="count"?t.count:!0})}),n}export function buildScribeShortcode(r,o){const n=e=>String(e).replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">"),t=SCRIBE_FIELDS.filter(e=>r[e.key]!==e.default).map(e=>`${e.name}="${n(r[e.key])}"`),l=o.map(e=>{if(e.type==="render"||e.type==="redraw"){const c=e.effect&&e.effect!=="inherit"?` effect="${n(e.effect)}"`:"";return`[${e.type}${c}]${e.text||""}[/${e.type}]`}return e.type==="wait"?`[wait]${e.duration}${e.unit==="s"?"s":""}[/wait]`:e.mode==="all"?'[undo all="true" /]':e.mode==="count"?`[undo count="${e.count}" /]`:"[undo /]"});return`[scribe${t.length?" "+t.join(" "):""}]
|
|
2
|
+
${l.join(`
|
|
3
|
+
`)}
|
|
4
|
+
[/scribe]`}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isPathAllowed as F}from"./admin-scope.js";export const TOOLS_FOLDER_TEXT="Tools",MANAGE_PLUGINS_URL="#/plugins",MANAGE_PLUGINS_ITEM={text:"Marketplace",url:MANAGE_PLUGINS_URL,icon:"store",permission:"plugins"};export function stripItemByUrl(e,t){const n=[];for(const r of e||[]){if(!r||r.url===t)continue;const s={...r};Array.isArray(r.items)&&r.items.length&&(s.items=stripItemByUrl(r.items,t)),n.push(s)}return n}export function managePluginsItem(){return{...MANAGE_PLUGINS_ITEM}}export const TOOLS_FOLDER_KEY="tools";const
|
|
1
|
+
import{isPathAllowed as F}from"./admin-scope.js";export const TOOLS_FOLDER_TEXT="Tools",MANAGE_PLUGINS_URL="#/plugins",MANAGE_PLUGINS_ITEM={text:"Marketplace",url:MANAGE_PLUGINS_URL,icon:"store",permission:"plugins"};export function stripItemByUrl(e,t){const n=[];for(const r of e||[]){if(!r||r.url===t)continue;const s={...r};Array.isArray(r.items)&&r.items.length&&(s.items=stripItemByUrl(r.items,t)),n.push(s)}return n}export function managePluginsItem(){return{...MANAGE_PLUGINS_ITEM}}export const TOOLS_FOLDER_KEY="tools";const E=["countUrl","core","permission"],c=e=>String(e??"").toLowerCase();export function slugify(e){return c(e).trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}export function groupKeyFor(e,t){return`${c(e)}.${slugify(t)}`}export const GROUP_ICON="folder";const P=e=>e&&(e.type==="separator"||e.type==="spacer"||e.type==="theme-switcher"),A=e=>e&&!P(e)&&!e.url;function w(e){return(e||[]).filter(Boolean).map(t=>Array.isArray(t.items)?{...t,items:w(t.items)}:{...t})}function M(e,t,n=[]){for(const r of e||[]){if(!r)continue;if(t(r))return[...n,r];const s=M(r.items,t,[...n,r]);if(s)return s}return null}function I(e,t,n=[],r=[]){for(const s of e||[])s&&(t(s)&&r.push([...n,s]),I(s.items,t,[...n,s],r));return r}const U=e=>!e.some(t=>t.hidden||t.restricted);function $(e,t){for(const n of e||[]){if(t(n))return n;const r=$(n?.items,t);if(r)return r}return null}export function findFolder(e,t){if(!t)return null;const n=c(t);return $(e,r=>A(r)&&c(r.key)===n)||$(e,r=>A(r)&&!r.key&&c(r.text)===n)}function b(e,t){const n={...t,...e};for(const r of E)t[r]!==void 0&&(n[r]=t[r]);return!(Array.isArray(e.items)&&e.items.length)&&Array.isArray(t.items)&&(n.items=t.items),e.text||(n.text=t.text),!e.icon&&t.icon&&(n.icon=t.icon),delete n.ref,delete n.folder,delete n.group,n}function O(e,{persisted:t,orphan:n=!1,node:r=null}){const s=e?.item||{};return{persisted:t,orphan:n,source:s.source||(s.core?"Built in":""),folder:e?.folder||TOOLS_FOLDER_KEY,defaults:n?{text:null,icon:null}:{text:s.text||"",icon:s.icon||""},...n&&r&&{label:r.text||r.url},...(e?.group||e?.item?.group)&&{group:e.group||e.item.group}}}export function placeTools(e,t,{forEditor:n=!1}={}){const r=w(e),s=(t||[]).filter(o=>o&&o.item&&typeof o.item.text=="string"),m=new Map;for(const o of s)o.item.url&&!m.has(o.item.url)&&m.set(o.item.url,o);const y=new Set,d=o=>{const i=[];for(const u of o){const h=u.url?m.get(u.url):null;if(h&&!y.has(u.url)){y.add(u.url);const g=b(u,h.item);Array.isArray(u.items)&&u.items.length?g.items=d(u.items):n&&(g.items=[]),n&&(g.$tool=O(h,{persisted:!0})),i.push(g);continue}if(u.ref){if(!n)continue;const{ref:g,...f}=u;i.push({...f,items:d(u.items||[]),$tool:O(null,{persisted:!0,orphan:!0,node:u})});continue}Array.isArray(u.items)&&(u.items=d(u.items)),i.push(u)}return i},a=d(r),T=new Set,S=o=>{for(const i of o||[])i.hidden&&i.text&&!i.url&&!(Array.isArray(i.items)&&i.items.length)&&T.add(c(i.text)),S(i.items)};S(a);let x=null;const L=()=>{const o=findFolder(a,TOOLS_FOLDER_KEY);if(o)return o;if(!x){x={text:TOOLS_FOLDER_TEXT,key:TOOLS_FOLDER_KEY,icon:"tool",items:[],synthesised:!0};const i=a.findIndex(u=>["system","documentation"].includes(c(u?.key||u?.text)));a.splice(i===-1?a.length:i,0,x)}return x},l=new Set,k=(o,i)=>{const u=groupKeyFor(o.key||slugify(o.text),i),h=$(a,p=>A(p)&&c(p.key)===u)||(o.items||[]).find(p=>A(p)&&(!p.key||c(p.key)===u)&&c(p.text)===c(i));if(h)return h;const g={text:String(i),key:u,icon:GROUP_ICON,items:[],synthesised:!0},f=o.items.findIndex(p=>l.has(p));return o.items.splice(f===-1?o.items.length:f,0,g),g};for(const o of s){const{item:i}=o;if(i.url&&y.has(i.url))continue;i.url&&y.add(i.url);const u=T.has(c(i.text));if(u&&!n)continue;const h=b({},i);n&&(h.items=[],h.$tool=O(o,{persisted:!1}),u&&(h.hidden=!0));let f=(o.parent?findFolder(a,o.parent)||$(a,_=>!P(_)&&c(_.text)===c(o.parent)):null)||findFolder(a,o.folder||i.folder)||L();f.items=Array.isArray(f.items)?f.items:[];const p=typeof(o.group||i.group)=="string"?(o.group||i.group).trim():"";p&&slugify(p)&&(f=k(f,p),f.items=Array.isArray(f.items)?f.items:[]),f.items.push(h),l.add(h)}return a}export const LOCKED_TOOLS=[{folder:"overview",item:{text:"Dashboard",url:"#/",icon:"home"}},{folder:"content",item:{text:"Site menus",url:"#/menus",icon:"menu",permission:"menus"}},{folder:"system",item:{text:"Users",url:"#/users",icon:"users",permission:"users"}},{folder:"system",item:{text:"My Profile",url:"#/my-profile",icon:"user"}}];export function isLockedUrl(e){return LOCKED_TOOLS.some(t=>t.item.url===e)}export function ensureLockedTools(e,{leaveInHiddenFolders:t=!1}={}){const n=w(e);for(const{folder:r,item:s}of LOCKED_TOOLS){const m=I(n,l=>l?.url===s.url),y=m.find(l=>!l.some(k=>k.restricted));if(!y&&m.length)continue;const d=y?.at(-1);if(d){if(d.hidden&&delete d.hidden,!y.slice(0,-1).some(o=>o.hidden)||t)continue;const k=y.at(-2);k.items=k.items.filter(o=>o!==d)}const a=c(r),T=I(n,l=>A(l)&&c(l.key)===a).filter(U),S=I(n,l=>A(l)&&!l.key&&c(l.text)===a).filter(U),x=(T[0]||S[0])?.at(-1)||null,L=d||{...s};x?(x.items=Array.isArray(x.items)?x.items:[],x.items.push(L)):n.push(L)}return n}export function insertFoldersBeforeSystem(e,t){const n=(t||[]).filter(Boolean);if(!n.length)return e;const r=["system","documentation"],s=e.findIndex(m=>r.includes(c(m?.key||m?.text)));return s===-1?[...e,...n]:[...e.slice(0,s),...n,...e.slice(s)]}export function canAny(e,t){return t?!e||!e.length?!1:e.includes(t)?!0:["read","create","update","delete"].some(n=>e.includes(`${t}.${n}`)):!0}export function filterByPermission(e,t,n=null){const r=[];for(const s of e){if(s.hidden||s.permission&&!canAny(t,s.permission))continue;const m=typeof s.url=="string"?s.url:"";if(n&&m.startsWith("#/")&&!F(n,m.slice(1)))continue;const y=Array.isArray(s.items)&&s.items.length>0,d=y?filterByPermission(s.items,t,n):[];y&&!d.length&&!m||r.push({...s,items:d})}return r}export function sidebarStateKey(e,t=null){const n=String(e?.text||"").replace(/\s*\(\d+\)\s*$/,"").toLowerCase().replace(/\s+/g,"-"),r=String(e?.url||"").match(/^#\/projects\/([^/]+)/);let s="";if(r)try{s=`project-${decodeURIComponent(r[1])}.`}catch{s=`project-${r[1]}.`}return t?e?.key?`sidebar.expanded.key.${c(e.key)}`:`${t}/${s}${n}`:`sidebar.expanded.${s}${n}`}export function pruneEmptyToolsFolder(e){return(e||[]).filter(t=>A(t)?!(c(t.key)===TOOLS_FOLDER_KEY||!t.key&&t.text===TOOLS_FOLDER_TEXT)||Array.isArray(t.items)&&t.items.length>0:!0)}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{api as g,getUser as L}from"../api.js";import{isPathAllowed as ue}from"./admin-scope.js";import{colourToCss as V,applyFloat as fe,applyHighlight as me,itemHighlightAttrs as pe}from"/public/js/menu-decor.mjs?v=20261009-hover-darken";import{placeTools as he,ensureLockedTools as ge,managePluginsItem as be,stripItemByUrl as ye,insertFoldersBeforeSystem as ve,pruneEmptyToolsFolder as xe,sidebarStateKey as X,canAny as O,filterByPermission as $e,MANAGE_PLUGINS_URL as Z}from"./sidebar-grouping.js";import{arrangeButtonMarkup as we,canArrange as ke,startArrange as Se}from"./sidebar-arrange.js";import{collapseButtonMarkup as qe,wireSidebarSize as Ce,COLLAPSED_WIDTH as Ae}from"./sidebar-size.js";import{FREQUENT_MAX as Y,addFrequent as Fe,frequentFolder as Le,moveFrequent as J,readFrequent as z,readFrequentHidden as Ee,removeFrequent as Te,saveFrequent as
|
|
1
|
+
import{api as g,getUser as L}from"../api.js";import{isPathAllowed as ue}from"./admin-scope.js";import{colourToCss as V,applyFloat as fe,applyHighlight as me,itemHighlightAttrs as pe}from"/public/js/menu-decor.mjs?v=20261009-hover-darken";import{placeTools as he,ensureLockedTools as ge,managePluginsItem as be,stripItemByUrl as ye,insertFoldersBeforeSystem as ve,pruneEmptyToolsFolder as xe,sidebarStateKey as X,canAny as O,filterByPermission as $e,MANAGE_PLUGINS_URL as Z}from"./sidebar-grouping.js";import{arrangeButtonMarkup as we,canArrange as ke,startArrange as Se}from"./sidebar-arrange.js";import{collapseButtonMarkup as qe,wireSidebarSize as Ce,COLLAPSED_WIDTH as Ae}from"./sidebar-size.js";import{FREQUENT_MAX as Y,addFrequent as Fe,frequentFolder as Le,moveFrequent as J,readFrequent as z,readFrequentHidden as Ee,removeFrequent as Te,saveFrequent as He,syncFrequent as Me,writeFrequent as Ne,writeFrequentHidden as Q}from"./sidebar-frequent.js";import{hotkeyFor as ee,hotkeyTitle as je,setHotkeyTree as Be}from"./hotkeys-ui.js";import{hotkeyLabel as Pe}from"./hotkeys.js";function m(e){return String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}const Re=[{text:"Dashboard",url:"#/",icon:"home"},{text:"Site menus",url:"#/menus",icon:"menu",permission:"menus"}];async function _e(e){if(!O(e,"projects"))return[];let n=[];try{n=await g.projects.listWithCounts()}catch{return[]}if(!Array.isArray(n))return[];const s=[{key:"pages",text:"Pages",icon:"file-text",path:"/pages"},{key:"collections",text:"Collections",icon:"database",path:"/collections"},{key:"forms",text:"Forms",icon:"layout",path:"/forms"},{key:"actions",text:"Actions",icon:"zap",path:"/actions"},{key:"menus",text:"Site menus",icon:"menu",path:"/menus"},{key:"blocks",text:"Blocks",icon:"box",path:"/blocks"},{key:"components",text:"Components",icon:"component",path:"/components"},{key:"views",text:"Views",icon:"eye",path:"/views"},{key:"roles",text:"Roles",icon:"shield",path:"/roles"},{key:"users",text:"Users",icon:"users",path:"/users"},{key:"apis",text:"APIs",icon:"code",path:"/apis"}];return n.map(t=>{const o="#/projects/"+encodeURIComponent(t.slug),a=t.counts||{},d=[{text:"Overview",url:o,icon:t.icon||"folder"}];for(const c of s){const r=Number(a[c.key])||0;r<=0||d.push({text:`${c.text} (${r})`,url:o+c.path,icon:c.icon})}d.push({text:"Settings",url:o+"/settings",icon:"settings"});const u=t.enabled===!1;return{text:t.name||t.slug,key:`project.${t.slug}`,icon:t.icon||"folder",items:d,...u&&{colour:"neutral",badge:{text:"off"}}}})}function Ie(e,n){for(const s of e)if(!s.hidden&&(s.key==="projects"||!s.key&&(s.text||"").toLowerCase()==="projects")){s.items=Array.isArray(s.items)?s.items.slice():[],s.items.push(...n);return}}function te(e,n){const s=se.includes(n.tone)?n.tone:"info",t=n.label||String(n.count);n.details?j.set(e,n.details):j.delete(e);const i=!n.inventory&&A.some(o=>o.url===e&&!o.inventory)?" data-live":"";return`<span class="sidebar-badge sidebar-badge--${s}" data-count-for="${m(e)}"${i} data-badge-label="${m(t)}" aria-label="${m(t)}">${m(String(n.count))}</span>`}function ne(e){for(const n of e.querySelectorAll("details")){const s=n.querySelector(":scope > summary");if(!s)continue;let t=0,i=!1;const o=[];for(const r of n.querySelectorAll(":scope > .sidebar-children .sidebar-badge[data-live]")){const f=Number(r.textContent)||0;f&&(t+=f,i||(i=r.classList.contains("sidebar-badge--danger")),o.push({text:r.closest("a")?.querySelector(".sidebar-text")?.textContent.trim()??"",meta:r.dataset.badgeLabel,tone:r.classList.contains("sidebar-badge--danger")?"danger":void 0}))}const a=`folder:${n.dataset.stateKey||s.textContent.trim()}`;let d=s.querySelector(":scope > .sidebar-badge--sum");if(!t){d?.remove(),j.delete(a);continue}d||(d=document.createElement("span"),s.querySelector(".sidebar-text")?.after(d));const u=s.querySelector(".sidebar-text")?.textContent.trim()||"This folder",c=o.map(r=>`${r.text}: ${r.meta}`).join(" \xB7 ");d.className=`sidebar-badge sidebar-badge--sum sidebar-badge--${i?"danger":"info"}`,d.dataset.countFor=a,d.dataset.badgeLabel=c,d.setAttribute("aria-label",c),d.textContent=String(t),j.set(a,{title:u,items:o})}}const j=new Map;function Oe(e,n){if(!e.url||!n)return"";const s=n[e.url];return!s||!(s.count>0)&&!s.showZero?"":te(e.url,s)}const se=["info","attention","danger","live"],Ue={"#/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 W(e,{fallbackLabel:n="",tone:s="info"}={}){const t=Number(typeof e=="object"&&e!==null?e.count:e);if(!Number.isFinite(t)||t<0)return null;const i=typeof e?.label=="string"&&e.label||(n?`${t} - ${n}`:String(t)),o={count:Math.round(t),label:i,tone:se.includes(e?.tone)?e.tone:s};return e?.details&&typeof e.details=="object"&&(o.details=e.details),e?.inventory===!0&&(o.inventory=!0),e?.showZero===!0&&(o.showZero=!0),o}function oe(e,n,s=null,{inFrequent:t=!1}={}){if(e&&e.type==="separator")return'<hr class="sidebar-divider">';if(e&&e.type==="theme-switcher")return"";if(e&&e.type==="spacer"){const y=["xs","sm","md","lg","xl","flex"].includes(e.size)?e.size:"md";return`<div class="sidebar-spacer sidebar-spacer--${m(y)}" aria-hidden="true"></div>`}const i=String(e.text||"?").trim().charAt(0).toUpperCase()||"?",o=e.icon?`<span data-icon="${m(e.icon)}"></span>`:`<span class="sidebar-initial" aria-hidden="true">${m(i)}</span>`,a=m(e.text||""),d=e.url?m(e.url):"",u=Array.isArray(e.items)&&e.items.length>0,c=e.colour?V(e.colour):"",r=pe(e),f=[r.hover&&` data-menu-item-hover="${m(r.hover)}"`,r.active&&` data-menu-item-active="${m(r.active)}"`].filter(Boolean).join(""),x=[c&&`color:${m(c)}`,r.accent&&`--dm-menu-highlight:${m(r.accent)}`].filter(Boolean).join(";"),w=x?` style="${x}"`:"",k=e.url?je(e.url,e.text||"",{inFrequent:t}):"",q=k?` title="${m(k)}"`:"";let F="";if(e.badge&&e.badge.text!=null&&e.badge.text!==""){const y=e.badge.variant?V(e.badge.variant):"";F=`<span class="dm-menu-badge"${y?` style="background:${m(y)};color:var(--dm-text-inverse,#fff)"`:""}>${m(String(e.badge.text))}</span>`}if(e.frequent&&e.empty){const y=X(e,s),C=S.get(y)!==!1;return`<details data-state-key="${m(y)}"${C?" open":""} data-frequent data-frequent-empty>
|
|
2
2
|
<summary${w}>${o} <span class="sidebar-text">${a}</span></summary>
|
|
3
3
|
<div class="sidebar-children"><p class="sidebar-frequent-empty">Right-click any Tool and choose <strong>Add to Frequent</strong> to keep it here.</p></div>
|
|
4
4
|
</details>`}if(u){const y=X(e,s),C=S.get(y)!==!1,B=e.items.map(T=>oe(T,n,y,{inFrequent:!!e.frequent})).join(""),P=e.frequent?' data-frequent title="Your Frequent Tools - right-click any Tool to add or remove it"':"";return`<details data-state-key="${m(y)}"${C?" open":""}${P}>
|
|
5
5
|
<summary${w}${q}>${o} <span class="sidebar-text">${a}</span>${F}</summary>
|
|
6
6
|
<div class="sidebar-children">${B}</div>
|
|
7
|
-
</details>`}return`<a href="${d}" class="sidebar-link" data-url="${d}"${w}${f}${q}>${o} <span class="sidebar-text">${a}</span>${Oe(e,n)}${F}</a>`}async function ze(e=[],n=[],s=null){const t=p=>p.then(b=>Array.isArray(b)?b.length:Array.isArray(b?.entries)?b.entries.length:0).catch(()=>0);A=Ge(e,s);const i=new Set(A.map(p=>p.url)),o={pages:"#/pages",media:"#/media",menus:"#/menus","context-menus":"#/context-menus",theme:"#/theme",collections:"#/collections",forms:"#/forms",views:"#/views",actions:"#/actions",blocks:"#/blocks",components:"#/components",users:"#/users",plugins:"#/plugins",notifications:"#/system/notifications"},a=(p,b)=>O(n,p)&&!i.has(o[p])?b():Promise.resolve(0),[d,u,c,r,f,x,w,k,q,F,y,C,B,P]=await Promise.all([a("pages",()=>t(g.pages.list())),a("media",()=>t(g.media.list())),a("menus",()=>t(g.menus.list())),a("context-menus",()=>t(g.contextMenus.list())),a("theme",()=>g.theme.get().then(p=>(p?.overrides||[]).length).catch(()=>0)),a("collections",()=>t(g.collections.list())),a("forms",()=>t(g.forms.list())),a("views",()=>t(g.views.list())),a("actions",()=>t(g.actions.list())),a("blocks",()=>t(g.blocks.list())),a("components",()=>t(g.components.list())),a("users",()=>t(g.users.list())),a("plugins",()=>t(g.plugins.list())),a("notifications",()=>g.system?.notifications?.unreadCount?.().then(p=>p?.count??0).catch(()=>0)??Promise.resolve(0))]),T={};await Promise.all(A.map(async p=>{const b=await ie(p);b&&(T[p.url]=b)}));const U={"#/pages":d,"#/media":u,"#/menus":c,"#/context-menus":r,"#/theme":f,"#/collections":x,"#/forms":w,"#/views":k,"#/actions":q,"#/blocks":F,"#/components":y,"#/users":C,"#/plugins":B,"#/system/notifications":P},R={};for(const[p,b]of Object.entries(U)){if(i.has(p))continue;const[I,l]=Ue[p]??["item","items"],v=W(b);v&&(R[p]={...v,label:`${v.count} ${v.count===1?I:l}`})}return{...R,...T}}let A=[];const We="dm:sidebar-count",De=6e4;function Ge(e=[],n=null){const s=[],t=i=>{if(i&&!(n&&typeof i.url=="string"&&i.url.startsWith("#/")&&!ue(n,i.url.slice(1)))){i.url&&typeof i.countUrl=="string"&&i.countUrl&&s.push({url:i.url,countUrl:i.countUrl,text:i.text||"",countLabel:i.countLabel||"",inventory:i.inventory===!0});for(const o of[...i.children||[],...i.items||[]])t(o)}};for(const i of e??[])t(i?.item??i);return s}async function ie(e){try{const n=new Date,s=`${n.getFullYear()}-${String(n.getMonth()+1).padStart(2,"0")}-${String(n.getDate()).padStart(2,"0")}`,t=e.countUrl+(e.countUrl.includes("?")?"&":"?")+`today=${s}`;return W(await H.get(t),{fallbackLabel:e.countLabel||e.text})}catch{return null}}function ae(e,n,s){const t=[...e.querySelectorAll("a.sidebar-link")].filter(i=>i.dataset.url===n);if(t.length){for(const i of t){const o=i.querySelector(".sidebar-badge[data-count-for]");if(!s||!(s.count>0)&&!s.showZero)o?.remove();else{const a=document.createRange();a.selectNodeContents(i);const d=a.createContextualFragment(te(n,s)).firstElementChild;o?o.replaceWith(d):i.querySelector(".sidebar-text")?.after(d)}}D.set(n,s),ne(e)}}const D=new Map;async function G(e,n=null){const s=n?A.filter(t=>t.url===n):A;await Promise.all(s.map(async t=>{const i=await ie(t),o=D.get(t.url);JSON.stringify(o??null)!==JSON.stringify(i)&&ae(e,t.url,i)}))}function Ke(e){e.dataset.liveCounts||(e.dataset.liveCounts="1",window.addEventListener(We,n=>{const s=n.detail||{};if(s.url)if(s.count!==void 0){const t=A.find(i=>i.url===s.url);ae(e,s.url,W(s,{fallbackLabel:t?.countLabel||t?.text||""}))}else G(e,s.url)}),setInterval(()=>{!document.hidden&&document.body.contains(e)&&G(e)},De),document.addEventListener("visibilitychange",()=>{document.hidden||G(e)}),Ve(e))}function re(e,n,s){const t=(a,d,u)=>{const c=document.createElement(a);return d&&(c.className=d),u!=null&&(c.textContent=String(u)),c};if(!s){e.textContent=n;return}e.appendChild(t("strong","dm-badge-pop-title",s.title||n)),(s.rows?.length||!s.items?.length)&&e.appendChild(t("p","dm-badge-pop-sub",n));const i=Array.isArray(s.rows)?s.rows.slice(0,6):[];if(i.length){const a=t("dl","dm-badge-pop-rows");for(const d of i)a.appendChild(t("dt",null,d.label)),a.appendChild(t("dd",d.tone==="danger"?"is-danger":null,d.value));e.appendChild(a)}const o=Array.isArray(s.items)?s.items.slice(0,6):[];if(o.length){const a=t("ul","dm-badge-pop-items");for(const d of o){const u=t("li",d.tone==="danger"?"is-danger":null);u.appendChild(t("span","dm-badge-pop-text",d.text)),d.meta&&u.appendChild(t("span","dm-badge-pop-meta",d.meta)),a.appendChild(u)}e.appendChild(a)}else s.empty&&e.appendChild(t("p","dm-badge-pop-empty",s.empty))}function Ve(e){let n=null,s=null;const t=()=>e.classList.contains("is-collapsed"),i=()=>{clearTimeout(s),n?.remove(),n=null},o=(c,r)=>{i(),s=setTimeout(()=>{n=document.createElement("div"),n.className="dm-help-pop dm-badge-pop",n.setAttribute("role","tooltip"),r(n),document.body.appendChild(n);const f=c.getBoundingClientRect(),x=n.getBoundingClientRect(),w=Math.min(f.right+10,innerWidth-x.width-8);n.style.left=`${Math.max(8,w)}px`,n.style.top=`${Math.max(8,Math.min(f.top+f.height/2-x.height/2,innerHeight-x.height-8))}px`},150)},a=c=>{const r=c?.dataset.badgeLabel;if(!r)return i();o(c,f=>re(f,r,j.get(c.dataset.countFor)))},d=c=>{const r=c.querySelector(":scope > .sidebar-text")?.textContent.trim()||"",f=c.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");o(c,x=>{const w=f&&j.get(f.dataset.countFor);if(w)return re(x,f.dataset.badgeLabel,{...w,title:w.title||r});if(!f){x.textContent=r;return}const k=document.createElement("strong");k.className="dm-badge-pop-title",k.textContent=r;const q=document.createElement("p");q.className="dm-badge-pop-sub",q.textContent=f.dataset.badgeLabel,x.append(k,q)})},u=c=>{const r=c.closest?.("a.sidebar-link, summary");return r&&e.contains(r)?r:null};e.addEventListener("mouseover",c=>{if(t()){const f=u(c.target);f&&!f.contains(c.relatedTarget)&&d(f);return}const r=c.target.closest?.(".sidebar-badge[data-badge-label]");r&&a(r)}),e.addEventListener("mouseout",c=>{if(t()){const r=u(c.target);r&&!r.contains(c.relatedTarget)&&i();return}c.target.closest?.(".sidebar-badge[data-badge-label]")&&i()}),e.addEventListener("focusin",c=>{const r=c.target.closest?.("a, summary");if(t()&&r&&e.contains(r)&&r.querySelector(":scope > .sidebar-text"))return d(r);const f=r?.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");f?a(f):i()}),e.addEventListener("focusout",i),e.addEventListener("click",i),window.addEventListener("scroll",i,!0),window.addEventListener("hashchange",i)}async function Xe(e=[]){if(!O(e,"settings"))return"Admin";try{const n=await g.settings.get();return n?.adminBrand?.title||n?.title||"Admin"}catch{return"Admin"}}export async function renderAdminSidebar({mount:e,permissions:n,adminScope:s=null}){const t=$(e).get(0);if(!t)return;const i=He(g,L()).catch(()=>{});let o,a=null,d=null,u=null,c=null,r=null;try{const l=await g.get("/sidebar/menu");o=Array.isArray(l?.items)?l.items:null,a=l?.variant||null,d=l?.position||null,u=l?.style||null,c=l?.float||null,r=l?.highlight||null}catch{o=null}(!o||!o.some(l=>l&&(!l.hidden||l.restricted)))&&(console.warn("[admin-sidebar] No admin-sidebar menu found; using fallback tree"),o=Re.slice());let f=[];try{f=await H.get("/api/sidebar/registered-items")||[]}catch{}o=ye(o,Z),o=he(o,f.filter(l=>(l?.item??l)?.url!==Z)),o=ge(o),o=ve(o,[be()]);const x=await _e(n);x.length&&Ie(o,x),o=$e(o,n,s),o=xe(o),await i;const w=Le(o,z(L()),{hidden:Ee(L())});w&&(o=[w,...o]),Be(o);const[k,q]=await Promise.all([ze(f.filter(l=>O(n,l?.item?.permission)),n,s).catch(()=>({})),Xe(n)]),F=a?` dm-admin-sidebar--${m(a)}`:"";function y(l,v="px"){if(l==null)return l;const h=String(l).trim();return/^\d+(\.\d+)?$/.test(h)?`${h}${v}`:h}let C="";if(u){const l=[];u.fontFamily&&l.push(`font-family: ${u.fontFamily}, sans-serif`),u.fontSize&&l.push(`font-size: ${y(u.fontSize)}`),u.fontWeight&&l.push(`font-weight: ${u.fontWeight}`),u.letterSpacing&&l.push(`letter-spacing: ${y(u.letterSpacing,"em")}`);const v=[];if(l.length&&v.push(`#admin-sidebar .dm-admin-sidebar, #admin-sidebar .dm-admin-sidebar .sidebar-link, #admin-sidebar .dm-admin-sidebar summary, #admin-sidebar .dm-admin-sidebar .sidebar-text { ${l.join("; ")} }`),u.iconSize){const h=y(u.iconSize);v.push(`#admin-sidebar .dm-admin-sidebar [data-icon], #admin-sidebar .dm-admin-sidebar [data-icon] svg { width: ${h} !important; height: ${h} !important; }`)}v.length&&(C=`<style data-admin-sidebar-style>${v.join(" ")}</style>`)}const B=ke(n)?we():"",P=`<div class="dm-admin-sidebar-header"><span data-icon="layout"></span><span class="dm-admin-sidebar-title">${m(q)}</span>${B}${qe()}</div>`,T=o.some(l=>l&&l.type==="spacer"&&l.size==="flex")?" dm-admin-sidebar--has-flex-spacer":"",U=`${C}<nav class="dm-admin-sidebar${F}${T}">${P}${o.map(l=>oe(l,k)).join("")}</nav>`,R=document.createRange();R.selectNodeContents(t);const p=R.createContextualFragment(U);t.replaceChildren(p),Domma.icons.scan(t),t.querySelector(".dm-admin-sidebar-arrange")?.addEventListener("click",()=>Se(t));for(const l of A)D.set(l.url,k[l.url]??null);ne(t),Ke(t),t.removeAttribute("data-menu-hover"),t.removeAttribute("data-menu-active"),t.style.removeProperty("--dm-menu-highlight"),me(t,{highlight:r});const b=document.querySelector(".dashboard-wrapper");["position","top","bottom","left","right","zIndex","maxHeight","transform"].forEach(l=>{t.style[l]=""}),t.classList.remove("dm-menu-floating"),b&&(b.style.paddingLeft="",b.style.paddingRight=""),t.__dmGutter=()=>{if(d!=="floating"||!b)return;const l=c&&c.anchor||"TL",v=Number.isFinite(Number(c?.offsetX))?Number(c.offsetX):16,h=t.classList.contains("is-collapsed")?Ae:parseInt(t.style.width,10)||t.getBoundingClientRect().width||260,N=Math.round(h+v*2)+"px";l==="R"||l==="TR"||l==="BR"?b.style.paddingRight=N:b.style.paddingLeft=N},d==="floating"&&fe(t,{position:"floating",float:c||{}}),t.querySelectorAll("details").forEach(l=>{l.addEventListener("toggle",function(){const v=this.getAttribute("data-state-key");v&&S.set(v,this.open);const h=this.querySelector(":scope > .sidebar-children");if(h)if(this.open){const N=h.scrollHeight;h.style.maxHeight="0",requestAnimationFrame(()=>{h.style.maxHeight=N+"px",h.addEventListener("transitionend",function de(){h.style.maxHeight="none",h.removeEventListener("transitionend",de)})})}else{const N=h.scrollHeight;h.style.maxHeight=N+"px",requestAnimationFrame(()=>{h.style.maxHeight="0"})}})}),Ce(t),Qe(t),t.dataset.routeWired||(t.dataset.routeWired="1",M.subscribe("router:afterChange",()=>t.__dmSetActive?.()));function I(){const l=/^#\/(dashboards|my-dashboard)\b/.test(location.hash)?"#/":location.hash||"#/";$(t).find(".sidebar-link").removeClass("active"),$(t).find(`.sidebar-link[data-url="${l}"]`).addClass("active")}I(),t.__dmSetActive=I}function le(){try{document.dispatchEvent(new CustomEvent("dm:sidebar-rebuild"))}catch{}}function K(e){return`${location.origin}${location.pathname}${e}`}function _(e,n){const s=L(),t=e(z(s));if(!Ne(s,t)){E.toast("Frequent could not be saved - this browser is blocking storage.",{type:"error"});return}t.length&&Q(s,!1),ce(s),n&&E.toast(n,{type:"success",duration:2500}),le()}function ce(e){Me(g,e).catch(()=>E.toast("Frequent is saved in this browser only - the server could not keep it.",{type:"warning"}))}function Ze(e){if(!Q(L(),e)){E.toast("Frequent could not be saved - this browser is blocking storage.",{type:"error"});return}ce(L()),E.toast(e?"Frequent hidden. Add a Tool to it from its right-click menu to bring it back.":"Frequent shown.",{type:"success",duration:3500}),le()}function Ye(e){const n=e.dataset.url;if(!n)return[];const s=e.querySelector(".sidebar-text")?.textContent.trim()||n,t=z(L()),i=t.includes(n),o=!!e.closest("details[data-frequent]"),a=t.indexOf(n),d=!i&&t.length>=Y;return[{header:s},{label:"Open",icon:"arrow-right",...ee(n)&&{shortcut:Pe(ee(n))},action:()=>{location.hash=n}},{label:"Open in a new tab",icon:"external-link",action:()=>window.open(K(n),"_blank","noopener")},{type:"divider"},i?{label:"Remove from Frequent",icon:"star",action:()=>_(u=>Te(u,n),`${s} removed from Frequent.`)}:{label:d?`Frequent is full (${Y})`:"Add to Frequent",icon:"star",disabled:d,action:()=>_(u=>Fe(u,n),`${s} added to Frequent.`)},...o?[{label:"Move up",icon:"arrow-up",disabled:a<=0,action:()=>_(u=>J(u,n,-1))},{label:"Move down",icon:"arrow-down",disabled:a<0||a>=t.length-1,action:()=>_(u=>J(u,n,1))}]:[],{type:"divider"},{label:"Copy link",icon:"link",action:async()=>{try{await navigator.clipboard.writeText(K(n)),E.toast("Link copied.",{type:"success",duration:2e3})}catch{E.toast(K(n),{type:"info",duration:8e3})}}}]}function Je(e,n){const s=e.closest("details"),t=e.querySelector(".sidebar-text")?.textContent.trim()||"Folder",i=o=>n.querySelectorAll("details").forEach(a=>{a.open!==o&&(a.open=o)});return[{header:t},{label:s?.open?"Close":"Open",icon:s?.open?"chevron-up":"chevron-down",action:()=>{s&&(s.open=!s.open)}},{label:"Open every folder",icon:"chevrons-down",action:()=>i(!0)},{label:"Close every folder",icon:"chevrons-up",action:()=>i(!1)},...s?.hasAttribute("data-frequent-empty")?[{type:"divider"},{label:"Hide Frequent until I add a Tool",icon:"eye-off",action:()=>Ze(!0)}]:s?.hasAttribute("data-frequent")?[{type:"divider"},{label:"Clear Frequent",icon:"x",action:()=>_(()=>[],"Frequent cleared.")}]:[]]}function Qe(e){e.dataset.ctxWired||typeof E>"u"||!E.contextMenu||(e.dataset.ctxWired="1",E.contextMenu(e,{match:"a.sidebar-link, details > summary",inherit:!1,density:"compact",items:n=>n.matches("summary")?Je(n,e):Ye(n)}))}
|
|
7
|
+
</details>`}return`<a href="${d}" class="sidebar-link" data-url="${d}"${w}${f}${q}>${o} <span class="sidebar-text">${a}</span>${Oe(e,n)}${F}</a>`}async function ze(e=[],n=[],s=null){const t=p=>p.then(b=>Array.isArray(b)?b.length:Array.isArray(b?.entries)?b.entries.length:0).catch(()=>0);A=Ge(e,s);const i=new Set(A.map(p=>p.url)),o={pages:"#/pages",media:"#/media",menus:"#/menus","context-menus":"#/context-menus",theme:"#/theme",collections:"#/collections",forms:"#/forms",views:"#/views",actions:"#/actions",blocks:"#/blocks",components:"#/components",users:"#/users",plugins:"#/plugins",notifications:"#/system/notifications"},a=(p,b)=>O(n,p)&&!i.has(o[p])?b():Promise.resolve(0),[d,u,c,r,f,x,w,k,q,F,y,C,B,P]=await Promise.all([a("pages",()=>t(g.pages.list())),a("media",()=>t(g.media.list())),a("menus",()=>t(g.menus.list())),a("context-menus",()=>t(g.contextMenus.list())),a("theme",()=>g.theme.get().then(p=>(p?.overrides||[]).length).catch(()=>0)),a("collections",()=>t(g.collections.list())),a("forms",()=>t(g.forms.list())),a("views",()=>t(g.views.list())),a("actions",()=>t(g.actions.list())),a("blocks",()=>t(g.blocks.list())),a("components",()=>t(g.components.list())),a("users",()=>t(g.users.list())),a("plugins",()=>t(g.plugins.list())),a("notifications",()=>g.system?.notifications?.unreadCount?.().then(p=>p?.count??0).catch(()=>0)??Promise.resolve(0))]),T={};await Promise.all(A.map(async p=>{const b=await ie(p);b&&(T[p.url]=b)}));const U={"#/pages":d,"#/media":u,"#/menus":c,"#/context-menus":r,"#/theme":f,"#/collections":x,"#/forms":w,"#/views":k,"#/actions":q,"#/blocks":F,"#/components":y,"#/users":C,"#/plugins":B,"#/system/notifications":P},R={};for(const[p,b]of Object.entries(U)){if(i.has(p))continue;const[I,l]=Ue[p]??["item","items"],v=W(b);v&&(R[p]={...v,label:`${v.count} ${v.count===1?I:l}`})}return{...R,...T}}let A=[];const We="dm:sidebar-count",De=6e4;function Ge(e=[],n=null){const s=[],t=i=>{if(i&&!(n&&typeof i.url=="string"&&i.url.startsWith("#/")&&!ue(n,i.url.slice(1)))){i.url&&typeof i.countUrl=="string"&&i.countUrl&&s.push({url:i.url,countUrl:i.countUrl,text:i.text||"",countLabel:i.countLabel||"",inventory:i.inventory===!0});for(const o of[...i.children||[],...i.items||[]])t(o)}};for(const i of e??[])t(i?.item??i);return s}async function ie(e){try{const n=new Date,s=`${n.getFullYear()}-${String(n.getMonth()+1).padStart(2,"0")}-${String(n.getDate()).padStart(2,"0")}`,t=e.countUrl+(e.countUrl.includes("?")?"&":"?")+`today=${s}`;return W(await H.get(t),{fallbackLabel:e.countLabel||e.text})}catch{return null}}function ae(e,n,s){const t=[...e.querySelectorAll("a.sidebar-link")].filter(i=>i.dataset.url===n);if(t.length){for(const i of t){const o=i.querySelector(".sidebar-badge[data-count-for]");if(!s||!(s.count>0)&&!s.showZero)o?.remove();else{const a=document.createRange();a.selectNodeContents(i);const d=a.createContextualFragment(te(n,s)).firstElementChild;o?o.replaceWith(d):i.querySelector(".sidebar-text")?.after(d)}}D.set(n,s),ne(e)}}const D=new Map;async function G(e,n=null){const s=n?A.filter(t=>t.url===n):A;await Promise.all(s.map(async t=>{const i=await ie(t),o=D.get(t.url);JSON.stringify(o??null)!==JSON.stringify(i)&&ae(e,t.url,i)}))}function Ke(e){e.dataset.liveCounts||(e.dataset.liveCounts="1",window.addEventListener(We,n=>{const s=n.detail||{};if(s.url)if(s.count!==void 0){const t=A.find(i=>i.url===s.url);ae(e,s.url,W(s,{fallbackLabel:t?.countLabel||t?.text||""}))}else G(e,s.url)}),setInterval(()=>{!document.hidden&&document.body.contains(e)&&G(e)},De),document.addEventListener("visibilitychange",()=>{document.hidden||G(e)}),Ve(e))}function re(e,n,s){const t=(a,d,u)=>{const c=document.createElement(a);return d&&(c.className=d),u!=null&&(c.textContent=String(u)),c};if(!s){e.textContent=n;return}e.appendChild(t("strong","dm-badge-pop-title",s.title||n)),(s.rows?.length||!s.items?.length)&&e.appendChild(t("p","dm-badge-pop-sub",n));const i=Array.isArray(s.rows)?s.rows.slice(0,6):[];if(i.length){const a=t("dl","dm-badge-pop-rows");for(const d of i)a.appendChild(t("dt",null,d.label)),a.appendChild(t("dd",d.tone==="danger"?"is-danger":null,d.value));e.appendChild(a)}const o=Array.isArray(s.items)?s.items.slice(0,6):[];if(o.length){const a=t("ul","dm-badge-pop-items");for(const d of o){const u=t("li",d.tone==="danger"?"is-danger":null);u.appendChild(t("span","dm-badge-pop-text",d.text)),d.meta&&u.appendChild(t("span","dm-badge-pop-meta",d.meta)),a.appendChild(u)}e.appendChild(a)}else s.empty&&e.appendChild(t("p","dm-badge-pop-empty",s.empty))}function Ve(e){let n=null,s=null;const t=()=>e.classList.contains("is-collapsed"),i=()=>{clearTimeout(s),n?.remove(),n=null},o=(c,r)=>{i(),s=setTimeout(()=>{n=document.createElement("div"),n.className="dm-help-pop dm-badge-pop",n.setAttribute("role","tooltip"),r(n),document.body.appendChild(n);const f=c.getBoundingClientRect(),x=n.getBoundingClientRect(),w=Math.min(f.right+10,innerWidth-x.width-8);n.style.left=`${Math.max(8,w)}px`,n.style.top=`${Math.max(8,Math.min(f.top+f.height/2-x.height/2,innerHeight-x.height-8))}px`},150)},a=c=>{const r=c?.dataset.badgeLabel;if(!r)return i();o(c,f=>re(f,r,j.get(c.dataset.countFor)))},d=c=>{const r=c.querySelector(":scope > .sidebar-text")?.textContent.trim()||"",f=c.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");o(c,x=>{const w=f&&j.get(f.dataset.countFor);if(w)return re(x,f.dataset.badgeLabel,{...w,title:w.title||r});if(!f){x.textContent=r;return}const k=document.createElement("strong");k.className="dm-badge-pop-title",k.textContent=r;const q=document.createElement("p");q.className="dm-badge-pop-sub",q.textContent=f.dataset.badgeLabel,x.append(k,q)})},u=c=>{const r=c.closest?.("a.sidebar-link, summary");return r&&e.contains(r)?r:null};e.addEventListener("mouseover",c=>{if(t()){const f=u(c.target);f&&!f.contains(c.relatedTarget)&&d(f);return}const r=c.target.closest?.(".sidebar-badge[data-badge-label]");r&&a(r)}),e.addEventListener("mouseout",c=>{if(t()){const r=u(c.target);r&&!r.contains(c.relatedTarget)&&i();return}c.target.closest?.(".sidebar-badge[data-badge-label]")&&i()}),e.addEventListener("focusin",c=>{const r=c.target.closest?.("a, summary");if(t()&&r&&e.contains(r)&&r.querySelector(":scope > .sidebar-text"))return d(r);const f=r?.querySelector(":scope > .sidebar-badge[data-badge-label]:not([hidden])");f?a(f):i()}),e.addEventListener("focusout",i),e.addEventListener("click",i),window.addEventListener("scroll",i,!0),window.addEventListener("hashchange",i)}async function Xe(e=[]){if(!O(e,"settings"))return"Admin";try{const n=await g.settings.get();return n?.adminBrand?.title||n?.title||"Admin"}catch{return"Admin"}}export async function renderAdminSidebar({mount:e,permissions:n,adminScope:s=null}){const t=$(e).get(0);if(!t)return;const i=Me(g,L()).catch(()=>{});let o,a=null,d=null,u=null,c=null,r=null;try{const l=await g.get("/sidebar/menu");o=Array.isArray(l?.items)?l.items:null,a=l?.variant||null,d=l?.position||null,u=l?.style||null,c=l?.float||null,r=l?.highlight||null}catch{o=null}(!o||!o.some(l=>l&&(!l.hidden||l.restricted)))&&(console.warn("[admin-sidebar] No admin-sidebar menu found; using fallback tree"),o=Re.slice());let f=[];try{f=await H.get("/api/sidebar/registered-items")||[]}catch{}o=ye(o,Z),o=he(o,f.filter(l=>(l?.item??l)?.url!==Z)),o=ge(o),o=ve(o,[be()]);const x=await _e(n);x.length&&Ie(o,x),o=$e(o,n,s),o=xe(o),await i;const w=Le(o,z(L()),{hidden:Ee(L())});w&&(o=[w,...o]),Be(o);const[k,q]=await Promise.all([ze(f.filter(l=>O(n,l?.item?.permission)),n,s).catch(()=>({})),Xe(n)]),F=a?` dm-admin-sidebar--${m(a)}`:"";function y(l,v="px"){if(l==null)return l;const h=String(l).trim();return/^\d+(\.\d+)?$/.test(h)?`${h}${v}`:h}let C="";if(u){const l=[];u.fontFamily&&l.push(`font-family: ${u.fontFamily}, sans-serif`),u.fontSize&&l.push(`font-size: ${y(u.fontSize)}`),u.fontWeight&&l.push(`font-weight: ${u.fontWeight}`),u.letterSpacing&&l.push(`letter-spacing: ${y(u.letterSpacing,"em")}`);const v=[];if(l.length&&v.push(`#admin-sidebar .dm-admin-sidebar, #admin-sidebar .dm-admin-sidebar .sidebar-link, #admin-sidebar .dm-admin-sidebar summary, #admin-sidebar .dm-admin-sidebar .sidebar-text { ${l.join("; ")} }`),u.iconSize){const h=y(u.iconSize);v.push(`#admin-sidebar .dm-admin-sidebar [data-icon], #admin-sidebar .dm-admin-sidebar [data-icon] svg { width: ${h} !important; height: ${h} !important; }`)}v.length&&(C=`<style data-admin-sidebar-style>${v.join(" ")}</style>`)}const B=ke(n)?we():"",P=`<div class="dm-admin-sidebar-header"><span data-icon="layout"></span><span class="dm-admin-sidebar-title">${m(q)}</span>${B}${qe()}</div>`,T=o.some(l=>l&&l.type==="spacer"&&l.size==="flex")?" dm-admin-sidebar--has-flex-spacer":"",U=`${C}<nav class="dm-admin-sidebar${F}${T}">${P}${o.map(l=>oe(l,k)).join("")}</nav>`,R=document.createRange();R.selectNodeContents(t);const p=R.createContextualFragment(U);t.replaceChildren(p),Domma.icons.scan(t),t.querySelector(".dm-admin-sidebar-arrange")?.addEventListener("click",()=>Se(t));for(const l of A)D.set(l.url,k[l.url]??null);ne(t),Ke(t),t.removeAttribute("data-menu-hover"),t.removeAttribute("data-menu-active"),t.style.removeProperty("--dm-menu-highlight"),me(t,{highlight:r});const b=document.querySelector(".dashboard-wrapper");["position","top","bottom","left","right","zIndex","maxHeight","transform"].forEach(l=>{t.style[l]=""}),t.classList.remove("dm-menu-floating"),b&&(b.style.paddingLeft="",b.style.paddingRight=""),t.__dmGutter=()=>{if(d!=="floating"||!b)return;const l=c&&c.anchor||"TL",v=Number.isFinite(Number(c?.offsetX))?Number(c.offsetX):16,h=t.classList.contains("is-collapsed")?Ae:parseInt(t.style.width,10)||t.getBoundingClientRect().width||260,N=Math.round(h+v*2)+"px";l==="R"||l==="TR"||l==="BR"?b.style.paddingRight=N:b.style.paddingLeft=N},d==="floating"&&fe(t,{position:"floating",float:c||{}}),t.querySelectorAll("details").forEach(l=>{l.addEventListener("toggle",function(){const v=this.getAttribute("data-state-key");v&&S.set(v,this.open);const h=this.querySelector(":scope > .sidebar-children");if(h)if(this.open){const N=h.scrollHeight;h.style.maxHeight="0",requestAnimationFrame(()=>{h.style.maxHeight=N+"px",h.addEventListener("transitionend",function de(){h.style.maxHeight="none",h.removeEventListener("transitionend",de)})})}else{const N=h.scrollHeight;h.style.maxHeight=N+"px",requestAnimationFrame(()=>{h.style.maxHeight="0"})}})}),Ce(t),Qe(t),t.dataset.routeWired||(t.dataset.routeWired="1",M.subscribe("router:afterChange",()=>t.__dmSetActive?.()));function I(){const l=/^#\/(dashboards|my-dashboard)\b/.test(location.hash)?"#/":location.hash||"#/";$(t).find(".sidebar-link").removeClass("active"),$(t).find(`.sidebar-link[data-url="${l}"]`).addClass("active")}I(),t.__dmSetActive=I}function le(){try{document.dispatchEvent(new CustomEvent("dm:sidebar-rebuild"))}catch{}}function K(e){return`${location.origin}${location.pathname}${e}`}function _(e,n){const s=L(),t=e(z(s));if(!Ne(s,t)){E.toast("Frequent could not be saved - this browser is blocking storage.",{type:"error"});return}t.length&&Q(s,!1),ce(s),n&&E.toast(n,{type:"success",duration:2500}),le()}function ce(e){He(g,e).catch(()=>E.toast("Frequent is saved in this browser only - the server could not keep it.",{type:"warning"}))}function Ze(e){if(!Q(L(),e)){E.toast("Frequent could not be saved - this browser is blocking storage.",{type:"error"});return}ce(L()),E.toast(e?"Frequent hidden. Add a Tool to it from its right-click menu to bring it back.":"Frequent shown.",{type:"success",duration:3500}),le()}function Ye(e){const n=e.dataset.url;if(!n)return[];const s=e.querySelector(".sidebar-text")?.textContent.trim()||n,t=z(L()),i=t.includes(n),o=!!e.closest("details[data-frequent]"),a=t.indexOf(n),d=!i&&t.length>=Y;return[{header:s},{label:"Open",icon:"arrow-right",...ee(n)&&{shortcut:Pe(ee(n))},action:()=>{location.hash=n}},{label:"Open in a new tab",icon:"external-link",action:()=>window.open(K(n),"_blank","noopener")},{type:"divider"},i?{label:"Remove from Frequent",icon:"star",action:()=>_(u=>Te(u,n),`${s} removed from Frequent.`)}:{label:d?`Frequent is full (${Y})`:"Add to Frequent",icon:"star",disabled:d,action:()=>_(u=>Fe(u,n),`${s} added to Frequent.`)},...o?[{label:"Move up",icon:"arrow-up",disabled:a<=0,action:()=>_(u=>J(u,n,-1))},{label:"Move down",icon:"arrow-down",disabled:a<0||a>=t.length-1,action:()=>_(u=>J(u,n,1))}]:[],{type:"divider"},{label:"Copy link",icon:"link",action:async()=>{try{await navigator.clipboard.writeText(K(n)),E.toast("Link copied.",{type:"success",duration:2e3})}catch{E.toast(K(n),{type:"info",duration:8e3})}}}]}function Je(e,n){const s=e.closest("details"),t=e.querySelector(".sidebar-text")?.textContent.trim()||"Folder",i=o=>n.querySelectorAll("details").forEach(a=>{a.open!==o&&(a.open=o)});return[{header:t},{label:s?.open?"Close":"Open",icon:s?.open?"chevron-up":"chevron-down",action:()=>{s&&(s.open=!s.open)}},{label:"Open every folder",icon:"chevrons-down",action:()=>i(!0)},{label:"Close every folder",icon:"chevrons-up",action:()=>i(!1)},...s?.hasAttribute("data-frequent-empty")?[{type:"divider"},{label:"Hide Frequent until I add a Tool",icon:"eye-off",action:()=>Ze(!0)}]:s?.hasAttribute("data-frequent")?[{type:"divider"},{label:"Clear Frequent",icon:"x",action:()=>_(()=>[],"Frequent cleared.")}]:[]]}function Qe(e){e.dataset.ctxWired||typeof E>"u"||!E.contextMenu||(e.dataset.ctxWired="1",E.contextMenu(e,{match:"a.sidebar-link, details > summary",inherit:!1,density:"compact",items:n=>n.matches("summary")?Je(n,e):Ye(n)}))}
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
<div class="col-12">
|
|
8
8
|
<div class="docs-body">
|
|
9
9
|
|
|
10
|
-
<p>The site's navigation is made of <strong>menus</strong>, edited at <strong>Content >
|
|
11
|
-
|
|
10
|
+
<p>The site's navigation is made of <strong>site menus</strong>, edited at <strong>Content >
|
|
11
|
+
Site menus</strong>. (Not to be confused with <strong>page contents</strong> - the list a layout
|
|
12
|
+
with a sidebar builds from a page's own headings, set up in <strong>Layouts</strong> and per page
|
|
13
|
+
with <code>[contents]</code>.) A menu is a list of links; a <strong>location</strong> (a slot) is where one is
|
|
12
14
|
shown. Each menu is a file in <code>config/menus/</code>, and the slot map is
|
|
13
15
|
<code>config/menu-locations.json</code>. Pages have no "show in navigation" switch - a page
|
|
14
16
|
appears in the navbar when a menu links to it.</p>
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
<!-- Layouts -->
|
|
12
12
|
<div class="tab-panel active">
|
|
13
|
-
<p class="lyx-note">A layout sets a page's width, background, and whether it has the navbar, footer and
|
|
13
|
+
<p class="lyx-note">A layout sets a page's width, background, and whether it has the navbar, footer and page contents (the list of the page's sections).
|
|
14
14
|
Pages choose one in the page editor; a page that names none uses Default.
|
|
15
15
|
<span data-help-title="Layouts" data-help="The built-in layouts can be edited but not deleted. A layout you make gets a key from its name (Dark Landing becomes dark-landing) - that key is what a page's layout: line names, and it does not change if you rename the layout later. Deleting a layout that pages use leaves those pages on Default."></span></p>
|
|
16
16
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{api as O,apiRequest as Qe}from"../api.js";import{HIGHLIGHT_HOVER as Xe,HIGHLIGHT_ACTIVE as Ze,HIGHLIGHT_HOVER_LABELS as et,HIGHLIGHT_ACTIVE_LABELS as tt}from"/public/js/menu-decor.mjs?v=20261009-hover-darken";import{addBannerAction as at}from"../lib/plugin-chrome.js";import{helpPopovers as Me}from"../lib/help-popover.js";import{onceClosed as Te,openMenuAt as He,sidebarCount as lt}from"../lib/tool-kit.js";import{FILTERS as st,WIDTHS as ot,WIDTH_HINTS as nt,WIDTH_LABELS as it,WIDTH_SHARE as $e,arrangeLayouts as ct,checkLayout as rt,checkOptions as dt,formFromPreset as ut}from"../lib/layouts-arrange.js";const ae=/^\/layouts\/?(\?.*)?$/,Le="#/layouts",le="layouts.draft",
|
|
1
|
+
import{api as O,apiRequest as Qe}from"../api.js";import{HIGHLIGHT_HOVER as Xe,HIGHLIGHT_ACTIVE as Ze,HIGHLIGHT_HOVER_LABELS as et,HIGHLIGHT_ACTIVE_LABELS as tt}from"/public/js/menu-decor.mjs?v=20261009-hover-darken";import{addBannerAction as at}from"../lib/plugin-chrome.js";import{helpPopovers as Me}from"../lib/help-popover.js";import{onceClosed as Te,openMenuAt as He,sidebarCount as lt}from"../lib/tool-kit.js";import{FILTERS as st,WIDTHS as ot,WIDTH_HINTS as nt,WIDTH_LABELS as it,WIDTH_SHARE as $e,arrangeLayouts as ct,checkLayout as rt,checkOptions as dt,formFromPreset as ut}from"../lib/layouts-arrange.js";const ae=/^\/layouts\/?(\?.*)?$/,Le="#/layouts",le="layouts.draft",z="layouts.options.draft",se="layouts.filter",Oe=(l="")=>`
|
|
2
2
|
<span class="lyx-shape" aria-hidden="true" data-bind-style-background-color="bg${l}">
|
|
3
3
|
<span class="lyx-shape-nav" data-bind-hidden="!hasNav${l}"></span>
|
|
4
4
|
<span class="lyx-shape-body"><span class="lyx-shape-col" data-bind-style-width="share${l}"></span><span class="lyx-shape-side" data-bind-hidden="!hasSide${l}" data-bind-class="sideRight${l} && 'is-right'"></span></span>
|
|
@@ -13,11 +13,11 @@ import{api as O,apiRequest as Qe}from"../api.js";import{HIGHLIGHT_HOVER as Xe,HI
|
|
|
13
13
|
<span class="lyx-chip" data-bind-class="usedTone" title="Pages that use this layout">{{usedText}}</span>
|
|
14
14
|
</span>
|
|
15
15
|
<button type="button" class="lyx-kebab" aria-label="Actions for {{label}}" title="Actions">\u22EE</button>
|
|
16
|
-
</div>`,G=l=>String(l??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");function Be(l){const c=document.createElement("template");return c.innerHTML=l.trim(),c.content.firstElementChild}const De=(l,c,i=`${c}s`)=>`${l} ${l===1?c:i}`,bt=l=>`#/pages/edit${l}`;let m=null,Ae=!1;function ht(){Ae||!window.R?.use||(Ae=!0,R.use((l,c,i)=>!(ae.test(c?.path||"")&&!ae.test(l?.path||""))||!m?.dirty()?i():E.confirm("You have unsaved layout changes. Leave anyway? They are kept as a draft for your next visit.").then(u=>{u?i():history.replaceState(null,"",Le)})),M.subscribe("router:afterChange",({to:l}={})=>{if(m&&!ae.test(l?.path||"")){const c=m;m=null,c.dispose()}}))}export const layoutsView={templateUrl:"/admin/js/templates/layouts.html",async onMount(l){if(ht(),m){const e=m;m=null,e.dispose()}const c=[],i=e=>(c.push(e),e);let V=!1;const u=l.get(0).querySelector(".lyx")||l.get(0),p=()=>!V&&u.isConnected,b=M.observable({}),
|
|
16
|
+
</div>`,G=l=>String(l??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");function Be(l){const c=document.createElement("template");return c.innerHTML=l.trim(),c.content.firstElementChild}const De=(l,c,i=`${c}s`)=>`${l} ${l===1?c:i}`,bt=l=>`#/pages/edit${l}`;let m=null,Ae=!1;function ht(){Ae||!window.R?.use||(Ae=!0,R.use((l,c,i)=>!(ae.test(c?.path||"")&&!ae.test(l?.path||""))||!m?.dirty()?i():E.confirm("You have unsaved layout changes. Leave anyway? They are kept as a draft for your next visit.").then(u=>{u?i():history.replaceState(null,"",Le)})),M.subscribe("router:afterChange",({to:l}={})=>{if(m&&!ae.test(l?.path||"")){const c=m;m=null,c.dispose()}}))}export const layoutsView={templateUrl:"/admin/js/templates/layouts.html",async onMount(l){if(ht(),m){const e=m;m=null,e.dispose()}const c=[],i=e=>(c.push(e),e);let V=!1;const u=l.get(0).querySelector(".lyx")||l.get(0),p=()=>!V&&u.isConnected,b=M.observable({}),F=M.observable({byKey:{},missing:[]}),oe=M.observable(!0),Y=M.observable("");let ne="all";try{ne=st.includes(S.get(se))?S.get(se):"all"}catch{}const k=M.observable(ne),ie=M.observable("");i(M.effect(()=>{const e=k.value;try{S.set(se,e)}catch{}}));const ce=M.observable([]);i(M.effect(()=>{ce.value=ct(b.value,F.value,{filter:k.value,query:ie.value})}));const Ie=M.computed(()=>{const e=Object.values(b.value),t=e.filter(o=>o.builtin).length;return{all:e.length,builtin:t,custom:e.length-t}}),U=M.computed(()=>F.value.missing||[]),Pe=M.computed(()=>{const e=U.value.length;return`${De(e,"page")} name${e===1?"s":""} a layout that no longer exists and show${e===1?"s":""} as Default.`}),j=M.observable({spacerSize:"8",spacerClass:""}),f=M.observable("8"),g=M.observable(""),J=M.observable(!1),re=()=>({spacerSize:String(f.value??""),spacerClass:String(g.value??"")}),N=M.computed(()=>dt(re())),x=M.computed(()=>{const e=j.value;return String(f.value??"")!==e.spacerSize||String(g.value??"")!==e.spacerClass}),de=M.computed(()=>J.value||!x.value||Object.keys(N.value.errors).length>0),ze=M.computed(()=>x.value?"Unsaved changes - Ctrl+S saves.":"Saved."),B=e=>({...b.peek()[e]||{},key:e}),Q=e=>(F.peek().byKey||{})[e]||[];async function X(){try{const[e,t]=await Promise.all([O.layouts.get(),Qe("/layouts/_usage",{method:"GET"}).catch(()=>({byKey:{},missing:[]}))]);if(!p())return;b.value=e&&typeof e=="object"?e:{},F.value=t&&typeof t=="object"?t:{byKey:{},missing:[]},Y.value=""}catch(e){Y.value=`Could not load the layouts: ${e.message||e}`}finally{oe.value=!1}}async function Fe(){const e=await O.layouts.getOptions().catch(()=>({spacerSize:8,spacerClass:""}));if(!p())return;const t={spacerSize:String(e?.spacerSize??8),spacerClass:String(e?.spacerClass??"")};j.value=t,f.value=t.spacerSize,g.value=t.spacerClass}async function ue(){if(de.peek())return;J.value=!0;const{payload:e}=N.peek();try{await O.layouts.saveOptions(e),j.value={spacerSize:String(e.spacerSize),spacerClass:e.spacerClass},f.value=String(e.spacerSize),g.value=e.spacerClass;try{S.remove?S.remove(z):S.set(z,null)}catch{}E.toast("Layout options saved.",{type:"success"})}catch(t){E.toast(`Not saved: ${t.message||t}`,{type:"error"})}finally{J.value=!1}}function Re(){const e=j.peek();f.value=e.spacerSize,g.value=e.spacerClass}const pe=()=>lt(Le);async function je(e){const t=B(e);if(t.builtin)return;const o=Q(e).length,r=o?` ${De(o,"page")} use${o===1?"s":""} it and will show as Default.`:" No page uses it.";if(await E.confirm(`Delete the layout "${G(t.label||e)}"?${r}`))try{await O.layouts.remove(e),E.toast(`Deleted "${t.label||e}".`,{type:"success"}),pe(),p()&&await X()}catch(n){E.toast(`Not deleted: ${n.message||n}`,{type:"error"})}}async function Ne(e){const t=`layout: ${e}`;try{await navigator.clipboard.writeText(t),E.toast(`Copied "${t}" - paste it into a page's frontmatter.`,{type:"success"})}catch{E.toast(`Copy this into a page's frontmatter: ${t}`,{type:"info",duration:7e3})}}const C=new Set;let h=null,be=!1;function he(e){const t=e===null,o=t?U.peek():Q(e),r=t?null:B(e),n=E.slideover({title:t?"Pages on a missing layout":`Pages using ${G(r.label||e)}`,size:"sm",position:"right"}),D=Be(`
|
|
17
17
|
<div class="lyx-form">
|
|
18
18
|
<p class="lyx-note"></p>
|
|
19
19
|
<div class="lyx-pages"></div>
|
|
20
|
-
</div>`);D.querySelector(".lyx-note").textContent=t?"These pages name a layout that no longer exists, so they show as Default. Open one and choose a layout for it.":o.length?"Click a page to open it in the editor.":"No page uses this layout.";const W=D.querySelector(".lyx-pages");for(const s of o){const w=document.createElement("a");w.href=bt(s.urlPath);const A=document.createElement("span");A.textContent=s.title||s.urlPath;const _=document.createElement("small");_.textContent=[s.urlPath,t?`"${s.layout}"`:"",s.status==="draft"?"draft":""].filter(Boolean).join(" \xB7 "),w.append(A,_),w.addEventListener("click",()=>n.close()),W.appendChild(w)}n.element.appendChild(D),C.add(n),Te(n,()=>C.delete(n)),n.open()}function T(e=null,{from:t=null,draft:o=null}={}){const r=e?B(e):null;if(e&&!b.peek()[e]){E.toast(`There is no layout "${e}".`,{type:"warning"});return}const n=!r,D=r||(t?{...B(t),label:`${B(t).label||t} copy`,builtin:!1}:{}),W={...ut(D),...o||{}},s=Object.fromEntries(Object.entries(W).map(([a,y])=>[a,M.observable(y)])),w=()=>Object.fromEntries(Object.keys(s).map(a=>[a,s[a].peek()])),A=a=>JSON.stringify(Object.fromEntries(Object.entries(a).map(([y,te])=>[y,typeof te=="boolean"?te:String(te??"")]))),_=A(W),Se=()=>A(w())!==_||!!o||!!(t&&n),K=M.observable(!1),
|
|
20
|
+
</div>`);D.querySelector(".lyx-note").textContent=t?"These pages name a layout that no longer exists, so they show as Default. Open one and choose a layout for it.":o.length?"Click a page to open it in the editor.":"No page uses this layout.";const W=D.querySelector(".lyx-pages");for(const s of o){const w=document.createElement("a");w.href=bt(s.urlPath);const A=document.createElement("span");A.textContent=s.title||s.urlPath;const _=document.createElement("small");_.textContent=[s.urlPath,t?`"${s.layout}"`:"",s.status==="draft"?"draft":""].filter(Boolean).join(" \xB7 "),w.append(A,_),w.addEventListener("click",()=>n.close()),W.appendChild(w)}n.element.appendChild(D),C.add(n),Te(n,()=>C.delete(n)),n.open()}function T(e=null,{from:t=null,draft:o=null}={}){const r=e?B(e):null;if(e&&!b.peek()[e]){E.toast(`There is no layout "${e}".`,{type:"warning"});return}const n=!r,D=r||(t?{...B(t),label:`${B(t).label||t} copy`,builtin:!1}:{}),W={...ut(D),...o||{}},s=Object.fromEntries(Object.entries(W).map(([a,y])=>[a,M.observable(y)])),w=()=>Object.fromEntries(Object.keys(s).map(a=>[a,s[a].peek()])),A=a=>JSON.stringify(Object.fromEntries(Object.entries(a).map(([y,te])=>[y,typeof te=="boolean"?te:String(te??"")]))),_=A(W),Se=()=>A(w())!==_||!!o||!!(t&&n),K=M.observable(!1),P=M.computed(()=>rt(Object.fromEntries(Object.keys(s).map(a=>[a,s[a].value])),{isNew:n,taken:Object.keys(b.value)})),q=a=>M.computed(()=>P.value.errors[a]||""),qe={hasNav:M.computed(()=>s.navbar.value!==!1),hasFoot:M.computed(()=>s.footer.value!==!1),hasSide:M.computed(()=>s.sidebar.value===!0),sideRight:M.computed(()=>s.sidebar.value===!0&&s.sidebarSide.value==="right"),share:M.computed(()=>$e[s.width.value]||$e.normal),bg:M.computed(()=>s.useBg.value?s.bgColor.value:"")},Ee={...s,...qe,saving:K,labelErr:q("label"),bgErr:q("bgColor"),classErr:q("cssClass"),cWidthErr:q("cWidth"),hoverOptions:[{value:"",label:"The theme's"},...Xe.map(a=>({value:a,label:et[a]||a}))],selectedOptions:[{value:"",label:"The theme's"},...Ze.map(a=>({value:a,label:tt[a]||a}))],keyHint:M.computed(()=>n?P.value.key?`Pages will name it "layout: ${P.value.key}".`:"":`Pages name it "layout: ${r.key}" - renaming does not change that.`),widthHint:M.computed(()=>nt[s.width.value]||""),builtinNote:!n&&r.builtin===!0,noBg:M.computed(()=>!s.useBg.value),cannot:M.computed(()=>K.value||Object.keys(P.value.errors).length>0),saveText:n?"Create layout":"Save"},Ge=n?t?"Duplicate layout":"New layout":`Edit ${G(r.label||r.key)}`,d=E.slideover({title:Ge,size:"md",position:"right"}),L=Be(`
|
|
21
21
|
<div class="lyx-form">
|
|
22
22
|
<div class="lyx-preview">${Oe(".value")}
|
|
23
23
|
<small class="lyx-hint" data-bind-text="keyHint.value"></small></div>
|
|
@@ -42,18 +42,18 @@ import{api as O,apiRequest as Qe}from"../api.js";import{HIGHLIGHT_HOVER as Xe,HI
|
|
|
42
42
|
</div>
|
|
43
43
|
<div class="lyx-wide">
|
|
44
44
|
<span class="form-label">Around the content
|
|
45
|
-
<span data-help="Whether pages on this layout show the site's navbar and footer, and the
|
|
45
|
+
<span data-help="Whether pages on this layout show the site's navbar and footer, and the page contents - a list of the page's own sections, built from its headings."></span></span>
|
|
46
46
|
<div class="lyx-switches">
|
|
47
47
|
<label><input type="checkbox" class="form-switch" data-model="navbar.value"> Navbar</label>
|
|
48
48
|
<label><input type="checkbox" class="form-switch" data-model="footer.value"> Footer</label>
|
|
49
|
-
<label><input type="checkbox" class="form-switch" data-model="sidebar.value">
|
|
49
|
+
<label><input type="checkbox" class="form-switch" data-model="sidebar.value"> Page contents</label>
|
|
50
50
|
</div>
|
|
51
51
|
<div class="lyx-switches" data-if="sidebar.value">
|
|
52
|
-
<label><input type="radio" name="lyx-side" value="left" data-model="sidebarSide.value">
|
|
52
|
+
<label><input type="radio" name="lyx-side" value="left" data-model="sidebarSide.value"> Page contents on the left</label>
|
|
53
53
|
<label><input type="radio" name="lyx-side" value="right" data-model="sidebarSide.value"> on the right</label>
|
|
54
54
|
</div>
|
|
55
55
|
<details class="lyx-contents" data-if="sidebar.value">
|
|
56
|
-
<summary>Page contents settings <span data-help="
|
|
56
|
+
<summary>Page contents settings <span data-help="Page contents lists the page's own headings. These are the defaults for every page on the layout; a page can change any of them for itself with the [contents] tag."></span></summary>
|
|
57
57
|
<div class="lyx-contents-grid">
|
|
58
58
|
<label>Width (px) <input type="number" class="form-input" min="160" max="480" step="10" placeholder="260" data-model="cWidth.value"></label>
|
|
59
59
|
<label>Headings
|
|
@@ -124,4 +124,4 @@ import{api as O,apiRequest as Qe}from"../api.js";import{HIGHLIGHT_HOVER as Xe,HI
|
|
|
124
124
|
<button type="button" class="btn btn-sm btn-primary" data-on-click="save" data-bind-disabled="cannot.value">
|
|
125
125
|
<span data-icon="check" data-icon-size="13"></span> <span data-bind-text="saveText"></span></button>
|
|
126
126
|
</div>
|
|
127
|
-
</div>`);async function ke(){if(Ee.cannot.peek())return;K.value=!0;const{payload:a}=
|
|
127
|
+
</div>`);async function ke(){if(Ee.cannot.peek())return;K.value=!0;const{payload:a}=P.peek();try{n?await O.layouts.create(a):await O.layouts.update(r.key,a),E.toast(n?`Created "${a.label}".`:"Saved.",{type:"success"}),Z=!0,d.close(),pe(),p()&&await X()}catch(y){E.toast(`Not saved: ${y.message||y}`,{type:"error"})}finally{K.value=!1}}d.element.appendChild(L);const Ve=Me(L),Ye=M.applyBindings(Ee,L,{methods:{save:ke,cancel:()=>d.close()}}),Ue=a=>{(a.ctrlKey||a.metaKey)&&a.key==="Enter"&&(a.preventDefault(),ke())};L.addEventListener("keydown",Ue);const Ce={key:r?.key??null,dirty:Se,values:w};h=Ce,C.add(d),Te(d,()=>{Ye.dispose(),Ve(),C.delete(d),h===Ce&&(h=null)});const Je=d.close.bind(d);let Z=!1,ee=!1;d.close=async a=>{if(!Z&&!be&&a!==!0&&Se()){if(ee)return;ee=!0;const y=await E.confirm("Discard the changes you have not saved?");if(ee=!1,!y)return}return Z=!0,Je()},I.scan(L),d.open(),L.querySelector("#lyx-f-label")?.focus()}function We(e){const t=b.peek()[e];if(!t)return[];const o=Q(e).length;return[{header:t.label||e},{label:"Edit\u2026",icon:"edit",action:()=>T(e)},{label:o?`Pages using it (${o})\u2026`:"Pages using it",icon:"file-text",disabled:!o,action:()=>he(e)},{label:"Duplicate\u2026",icon:"copy",action:()=>T(null,{from:e})},{label:"Copy frontmatter line",icon:"clipboard",action:()=>Ne(e)},...t.builtin?[]:[{type:"divider"},{label:"Delete\u2026",icon:"trash",danger:!0,action:()=>je(e)}]]}const H=u.querySelector(".lyx-list");H.innerHTML=pt;const ve=E.tabs(u.querySelector(".lyx-tabs"),{tabSelector:".tab-item",panelSelector:".tab-panel"}),_e=M.applyBindings({loading:oe,loadError:Y,filter:k,query:ie,rows:ce,counts:Ie,missing:U,missingText:Pe,spacerSize:f,spacerClass:g,optDirty:x,optCannot:de,optStatus:ze,optErrSize:M.computed(()=>N.value.errors.spacerSize||""),optErrClass:M.computed(()=>N.value.errors.spacerClass||"")},u,{methods:{showAll:()=>{k.value="all"},showBuiltin:()=>{k.value="builtin"},showCustom:()=>{k.value="custom"},showMissing:()=>he(null),saveOptions:()=>ue(),resetOptions:()=>Re()}});i(()=>_e.dispose()),i(Me(u,{position:"bottom"}));const Ke=E.contextMenu(H,{cursor:"glyph",match:".lyx-row",inherit:!1,density:"compact",items:e=>We(e.dataset.key)});i(()=>Ke?.destroy?.());const ye=e=>{const t=e.target.closest(".lyx-row");if(!t)return;const o=e.target.closest(".lyx-kebab");if(o){He(o);return}T(t.dataset.key)},me=e=>{const t=e.target.closest(".lyx-row");t&&(e.key==="F10"&&e.shiftKey?(e.preventDefault(),He(t)):e.key==="Enter"&&e.target===t&&T(t.dataset.key))};H.addEventListener("click",ye),H.addEventListener("keydown",me),i(()=>{H.removeEventListener("click",ye),H.removeEventListener("keydown",me)});const fe=e=>{!(e.ctrlKey||e.metaKey)||e.key!=="s"||!p()||C.size||(e.preventDefault(),x.peek()&&ue())};document.addEventListener("keydown",fe,!0),i(()=>document.removeEventListener("keydown",fe,!0)),at(l,{id:"layouts-new",icon:"plus",title:"New layout",onClick:()=>{ve.activate?.(0),T()}});const ge=e=>{(x.peek()||h?.dirty())&&(xe(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",ge),i(()=>window.removeEventListener("beforeunload",ge));function xe(){try{h?.dirty()&&S.set(le,{key:h.key,values:h.values(),at:new Date().toISOString()}),x.peek()&&S.set(z,{...re(),at:new Date().toISOString()})}catch{}}if(m={dirty:()=>x.peek()||!!h?.dirty(),dispose(){V=!0,xe(),be=!0,[...C].forEach(e=>{try{e.close(!0)}catch{}}),c.splice(0).forEach(e=>{try{e()}catch{}})}},I.scan(u),await Promise.all([X(),Fe()]),!p())return;I.scan(u);let $=null,v=null;try{$=S.get(z),v=S.get(le)}catch{}const we=e=>{try{S.remove?S.remove(e):S.set(e,null)}catch{}};if($&&typeof $=="object"&&"spacerSize"in $&&(we(z),await E.confirm("You left unsaved spacer options. Put them back?")&&p()&&(f.value=String($.spacerSize??""),g.value=String($.spacerClass??""),ve.activate?.(1))),v?.values&&p()){we(le);const e=v.key&&b.peek()[v.key],t=e?`your changes to "${G(b.peek()[v.key].label||v.key)}"`:"the new layout you started";await E.confirm(`You left ${t} unsaved. Pick up where you left off?`)&&p()&&T(e?v.key:null,{draft:v.values})}}};
|
package/package.json
CHANGED
|
@@ -1841,8 +1841,13 @@ function processEffectsBlocks(markdown) {
|
|
|
1841
1841
|
apply('scribe', (attrStr, body) => {
|
|
1842
1842
|
if (body === null) return '';
|
|
1843
1843
|
const attrs = parseShortcodeAttrs(attrStr);
|
|
1844
|
+
// Composer attributes use HTML entities for literal quotes and ampersands.
|
|
1845
|
+
const entities = {amp: '&', quot: '"', apos: "'", lt: '<', gt: '>'};
|
|
1846
|
+
for (const key of Object.keys(attrs)) {
|
|
1847
|
+
if (typeof attrs[key] === 'string') attrs[key] = attrs[key].replace(/&(amp|quot|apos|lt|gt);/g, (_, name) => entities[name]);
|
|
1848
|
+
}
|
|
1844
1849
|
const dataAttrs = Object.entries(attrs).map(([k, v]) => ` data-fx-${k}="${escapeAttr(v)}"`).join('');
|
|
1845
|
-
const hasActions = /\[\s*(render|wait|undo|redraw)\b
|
|
1850
|
+
const hasActions = /\[\s*(render|wait|undo|redraw)\b/i.test(body);
|
|
1846
1851
|
if (!hasActions) {
|
|
1847
1852
|
// Restore code placeholders before the nested parse (tabs-handler
|
|
1848
1853
|
// pattern) so code spans/fences render as <code>, not raw backticks.
|
|
@@ -1852,15 +1857,16 @@ function processEffectsBlocks(markdown) {
|
|
|
1852
1857
|
const actions = [];
|
|
1853
1858
|
const actionRe = /\[render([^\]]*)\]([\s\S]*?)\[\/render\]|\[wait\]([\s\S]*?)\[\/wait\]|\[undo([^\]]*?)\/\]|\[redraw([^\]]*)\]([\s\S]*?)\[\/redraw\]/gi;
|
|
1854
1859
|
for (const match of body.matchAll(actionRe)) {
|
|
1855
|
-
|
|
1860
|
+
const tag = match[0].toLowerCase();
|
|
1861
|
+
if (tag.startsWith('[render')) {
|
|
1856
1862
|
const rAttrs = parseShortcodeAttrs(match[1] || '');
|
|
1857
1863
|
const action = {render: match[2].trim()};
|
|
1858
1864
|
if (rAttrs.effect) action.effect = rAttrs.effect;
|
|
1859
1865
|
actions.push(action);
|
|
1860
|
-
} else if (
|
|
1866
|
+
} else if (tag.startsWith('[wait')) {
|
|
1861
1867
|
const val = match[3].trim();
|
|
1862
|
-
actions.push({wait: isNaN(val) ? val :
|
|
1863
|
-
} else if (
|
|
1868
|
+
actions.push({wait: isNaN(val) ? val : Number(val)});
|
|
1869
|
+
} else if (tag.startsWith('[undo')) {
|
|
1864
1870
|
const uAttrs = parseShortcodeAttrs(match[4] || '');
|
|
1865
1871
|
if (uAttrs.all === 'true') actions.push({undoRender: 'all'});
|
|
1866
1872
|
else if (uAttrs.count) actions.push({undoRender: parseInt(uAttrs.count, 10)});
|
|
@@ -37,7 +37,7 @@ const SEED_ITEMS = [
|
|
|
37
37
|
items: [
|
|
38
38
|
{text: 'Pages', url: '#/pages', icon: 'file-text', permission: 'pages'},
|
|
39
39
|
{text: 'Media', url: '#/media', icon: 'image', permission: 'media'},
|
|
40
|
-
{text: '
|
|
40
|
+
{text: 'Site menus', url: '#/menus', icon: 'menu', permission: 'menus'}
|
|
41
41
|
]
|
|
42
42
|
},
|
|
43
43
|
{
|
|
@@ -372,6 +372,19 @@ export async function ensureFolderKeys(opts = {}) {
|
|
|
372
372
|
changed = true;
|
|
373
373
|
}
|
|
374
374
|
|
|
375
|
+
// The Menus screen became "Site menus" in 1.6.16 (beside "Page contents", the list a
|
|
376
|
+
// page builds from its headings). Renamed once (`meta.siteMenusName`) and only where
|
|
377
|
+
// the item still says Menus, so a name an admin gave it stays.
|
|
378
|
+
if (!menu.meta?.siteMenusName && items.length) {
|
|
379
|
+
const rename = (list) => (list || []).forEach(n => {
|
|
380
|
+
if (n && n.url === '#/menus' && String(n.text || '').trim() === 'Menus') n.text = 'Site menus';
|
|
381
|
+
if (Array.isArray(n?.items)) rename(n.items);
|
|
382
|
+
});
|
|
383
|
+
rename(items);
|
|
384
|
+
menu.meta = {...(menu.meta || {}), siteMenusName: true};
|
|
385
|
+
changed = true;
|
|
386
|
+
}
|
|
387
|
+
|
|
375
388
|
// An empty menu is left empty: the renderer's minimal fallback (Dashboard,
|
|
376
389
|
// Menus) is what an admin needs to repair it, and a lone Tools folder would
|
|
377
390
|
// stop that fallback from applying.
|