domma-cms 0.104.2 → 0.105.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +9 -0
- package/admin/dist/domma/domma-tools.css +3 -3
- package/admin/dist/domma/domma-tools.min.js +3 -3
- package/admin/js/app.js +1 -1
- package/admin/js/lib/effect-defs.js +1 -1
- package/admin/js/templates/call-new.html +88 -0
- package/admin/js/templates/docs/usage-tools.html +15 -0
- package/admin/js/views/call-new.js +1 -0
- package/admin/js/views/index.js +1 -1
- package/package.json +2 -2
- package/plugins/_lib/public/call/call.js +7 -0
- package/plugins/booking/CLAUDE.md +4 -0
- package/plugins/booking/plugin.json +1 -1
- package/plugins/booking/plugin.public.js +6 -1
- package/plugins/free-tier.lock.json +5 -5
- package/public/js/celebrations/index.js +1 -1
- package/public/js/celebrations/themes/christmas-antics.js +1 -0
- package/public/js/celebrations/themes/christmas-drawing.js +1 -0
- package/public/js/celebrations/themes/christmas-festive.js +1 -0
- package/public/js/celebrations/themes/christmas-vehicles.js +1 -0
- package/public/js/celebrations/themes/christmas.js +1 -1
- package/public/js/effects.js +1 -1
- package/server/routes/api/calls.js +134 -0
- package/server/routes/call-public.js +104 -0
- package/server/server.js +9 -0
- package/server/services/hooks.js +66 -3
- package/server/services/instantCalls.js +362 -0
- package/server/services/permissionRegistry.js +13 -0
- package/server/services/plugins.js +21 -2
- package/server/services/renderer.js +10 -5
- package/server/templates/call.html +3 -0
- package/server/templates/page.html +1 -1
package/CLAUDE.md
CHANGED
|
@@ -234,6 +234,15 @@ renderer appends `?today=YYYY-MM-DD` (the BROWSER's day). A view whose number ch
|
|
|
234
234
|
route; counts also refresh every minute. Plugin sub-items (`children`) are NOT rendered in the
|
|
235
235
|
sidebar - put a plugin's `countUrl` on its top-level entry. See "Live counts" in sidebar-renderer.js.
|
|
236
236
|
|
|
237
|
+
**Instant calls ("Call Sam now…") are core code that only a Pro Tool switches on** - `services/instantCalls.js`,
|
|
238
|
+
`routes/api/calls.js`, `routes/call-public.js`, admin `#/calls/new`. `hooks.enableInstantCalls()` (plugins.js binds it
|
|
239
|
+
to the manifest) is accepted only from a `tier: "paid"` + `closedSource: true` plugin; with none, `/api/calls` is 404,
|
|
240
|
+
`/_call/*` falls through to the pages and no related action exists; `runLifecycleHook(onDisable)` takes a plugin's hold
|
|
241
|
+
off at once. The admin grant rides the awaited access step after `registerPluginAccess` (roles.js writes unlocked).
|
|
242
|
+
While an OLDER Booking Pro 0.3.x registers its own related provider `booking-pro-call`, core's provider stands aside
|
|
243
|
+
(`when`, checked per request) so "Call … now" never shows twice. `/_call` renders with `renderBlogPage(..., {injection:
|
|
244
|
+
false})` - no injected snippets or beacon near a URL that carries a host key.
|
|
245
|
+
|
|
237
246
|
## Config Reference
|
|
238
247
|
|
|
239
248
|
| File | Owner | Purpose |
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Domma Tools CSS v0.51.
|
|
2
|
+
* Domma Tools CSS v0.51.3
|
|
3
3
|
* Dynamic Object Manipulation & Modeling API
|
|
4
4
|
* (c) 2026 Darryl Waterhouse & DCBW-IT
|
|
5
|
-
* Built: 2026-09-
|
|
6
|
-
* Commit:
|
|
5
|
+
* Built: 2026-09-30T14:40:55.590Z
|
|
6
|
+
* Commit: 2256764
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
/*!
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Domma Tools v0.51.
|
|
2
|
+
* Domma Tools v0.51.3
|
|
3
3
|
* Developer tools: Theme Roller & Page Roller
|
|
4
4
|
* (c) 2026 Darryl Waterhouse & DCBW-IT
|
|
5
|
-
* Built: 2026-09-
|
|
6
|
-
* Commit:
|
|
5
|
+
* Built: 2026-09-30T14:40:43.146Z
|
|
6
|
+
* Commit: 2256764
|
|
7
7
|
*
|
|
8
8
|
* Requires: domma.min.js
|
|
9
9
|
*/
|
package/admin/js/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{views as q}from"./views/index.js";import{withPluginChrome as B}from"./lib/plugin-chrome.js";import{api as r,getUser as O,isAuthenticated as u,logout as G,sendToLogin as z}from"./api.js";import{isAuthScreen as C,loginHashFor as K}from"./lib/login-arrange.js";import{installHttpInterceptor as J}from"./http-interceptor.js";import{installResizableSlideovers as Y}from"./lib/slideover-resizable.js";import{installActivityThrobber as Q}from"./lib/activity-throbber.js";import{isPathAllowed as X,redirectFor as Z}from"./lib/admin-scope.js";import{deliverDesktop as tt,desktopActive as et,notificationSettings as it}from"./lib/desktop-notify.js";import{avatarUrl as ot}from"./lib/avatar.js";$(()=>{J(),Q(),Y(),(async()=>{const{applyAdminTheme:t,refreshAdminTheme:e}=await import("./lib/admin-theme.js");u()?await e(r,{init:!0}):t({dommaTheme:"charcoal-dark"},{init:!0})})();let c=null,l="/",h=!1,w=null;function D(t){if(y[t])return E.toast(`${y[t]} is switched off on this site.`,{type:"info"}),"/";const e=b[t]||Z(c,t);return e||((t.split("?")[0]||"/")==="/"&&l&&l!=="/"?b[l]||l:null)}const b={},y={};R.use(async(t,e,i)=>{if(C(t.path))return i();if(!u()){z();return}const o=D(t.path);if(o){R.navigate(o);return}i()});async function g(){try{const t=await r.get("/auth/permissions");c=t.adminScope||null,l=t.adminHome||"/";const e=t.permissions||[];return h=X(c,"/settings")&&e.some(i=>i==="settings"||i.startsWith("settings.")),w=e.some(i=>i==="notifications"||i.startsWith("notifications.")),e}catch{return c=null,l="/",h=!1,w=null,[]}}document.addEventListener("dm:admin-home-changed",()=>{g()});let P=null,N=!1;document.addEventListener("dm:sidebar-rebuild",()=>{if(P){N=!0;return}P=(async()=>{do{N=!1;try{await j(await g())}catch{}}while(N)})().finally(()=>{P=null})});async function j(t){const{renderAdminSidebar:e}=await import("./lib/sidebar-renderer.js");await e({mount:"#admin-sidebar",permissions:t,adminScope:c})}M.subscribe("router:afterChange",({to:t})=>{C(t.path)&&(d&&(clearInterval(d),d=null),c=null,l="/",h=!1,w=null)});const F=[{path:"/",view:"dashboard",title:"Dashboard - Domma CMS"},{path:"/pages",view:"pages",title:"Pages - Domma CMS"},{path:"/pages/new",view:"pageEditor",title:"New Page - Domma CMS"},{path:"/pages/edit/*",view:"pageEditor",title:"Edit Page - Domma CMS"},{path:"/media",view:"media",title:"Media - Domma CMS"},{path:"/menus",view:"menus",title:"Menus - Domma CMS"},{path:"/context-menus",view:"contextMenus",title:"Context Menus - Domma CMS"},{path:"/context-menus/new",view:"contextMenuEditor",title:"New Context Menu - Domma CMS"},{path:"/context-menus/edit/:slug",view:"contextMenuEditor",title:"Edit Context Menu - Domma CMS"},{path:"/menus/new",view:"menuEditor",title:"New Menu - Domma CMS"},{path:"/menus/edit/:slug",view:"menuEditor",title:"Edit Menu - Domma CMS"},{path:"/menu-locations",view:"menuLocations",title:"Menu Locations - Domma CMS"},{path:"/navigation",view:"menusRedirect",title:"Menus - Domma CMS"},{path:"/layouts",view:"layouts",title:"Layouts - Domma CMS"},{path:"/settings",view:"settings",title:"Settings - Domma CMS"},{path:"/theme",view:"theme",title:"Theme - Domma CMS"},{path:"/users",view:"users",title:"Users - Domma CMS"},{path:"/users/new",view:"userEditor",title:"New User - Domma CMS"},{path:"/users/edit/:id",view:"userEditor",title:"Edit User - Domma CMS"},{path:"/plugins",view:"plugins",title:"Plugins - Domma CMS"},{path:"/plugins/code/:name",view:"pluginCode",title:"Plugin Code - Domma CMS"},{path:"/documentation",view:"documentation",title:"Usage - Domma CMS"},{path:"/tutorials",view:"tutorials",title:"Tutorials - Domma CMS"},{path:"/tutorials/crud",view:"tutorialCrud",title:"Building a CRUD App - Domma CMS"},{path:"/tutorials/plugin",view:"tutorialPlugin",title:"Writing a Plugin - Domma CMS"},{path:"/tutorials/forms",view:"tutorialForms",title:"Form Follow-Up - Domma CMS"},{path:"/docs/components",view:"componentsReference",title:"Components Reference - Domma CMS"},{path:"/docs/components-howto",view:"componentsHowto",title:"Components How-To - Domma CMS"},{path:"/docs/components-walkthrough",view:"componentsWalkthrough",title:"Components Walkthrough - Domma CMS"},{path:"/docs/components-rules",view:"componentsRules",title:"Components Rules - Domma CMS"},{path:"/docs/usage/pages",view:"usagePages",title:"Pages - Usage - Domma CMS"},{path:"/docs/usage/media",view:"usageMedia",title:"Media - Usage - Domma CMS"},{path:"/docs/usage/navigation",view:"usageNavigation",title:"Navigation - Usage - Domma CMS"},{path:"/docs/usage/dconfig",view:"usageDconfig",title:"DConfig - Usage - Domma CMS"},{path:"/docs/usage/shortcodes",view:"usageShortcodes",title:"Shortcodes - Usage - Domma CMS"},{path:"/docs/usage/site-settings",view:"usageSiteSettings",title:"Site Settings - Usage - Domma CMS"},{path:"/docs/usage/plugins",view:"usagePlugins",title:"Plugins - Usage - Domma CMS"},{path:"/docs/usage/users-roles",view:"usageUsersRoles",title:"Users & Roles - Usage - Domma CMS"},{path:"/docs/usage/views",view:"usageViews",title:"Views - Usage - Domma CMS"},{path:"/docs/usage/actions",view:"usageActions",title:"Actions - Usage - Domma CMS"},{path:"/docs/usage/cta-shortcode",view:"usageCtaShortcode",title:"CTA Shortcode - Usage - Domma CMS"},{path:"/docs/usage/editions",view:"usageEditions",title:"Editions & Licences - Usage - Domma CMS"},{path:"/docs/usage/collections",view:"usageCollections",title:"Collections & Forms - Usage - Domma CMS"},{path:"/docs/usage/tools",view:"usageTools",title:"Built-in Tools - Usage - Domma CMS"},{path:"/docs/plugins",view:"pluginGuides",title:"Plugin Guides - Domma CMS"},{path:"/docs/plugins/*",view:"pluginGuide",title:"Plugin Guide - Domma CMS"},{path:"/docs/api/authentication",view:"apiRefAuthentication",title:"Authentication - API - Domma CMS"},{path:"/docs/api/pages",view:"apiRefPages",title:"Pages - API - Domma CMS"},{path:"/docs/api/settings",view:"apiRefSettings",title:"Settings - API - Domma CMS"},{path:"/docs/api/layouts",view:"apiRefLayouts",title:"Layouts - API - Domma CMS"},{path:"/docs/api/navigation",view:"apiRefNavigation",title:"Navigation - API - Domma CMS"},{path:"/docs/api/media",view:"apiRefMedia",title:"Media - API - Domma CMS"},{path:"/docs/api/users",view:"apiRefUsers",title:"Users - API - Domma CMS"},{path:"/docs/api/plugins",view:"apiRefPlugins",title:"Plugins - API - Domma CMS"},{path:"/docs/api/collections",view:"apiRefCollections",title:"Collections - API - Domma CMS"},{path:"/docs/api/views",view:"apiRefViews",title:"Views API - Domma CMS"},{path:"/docs/api/actions",view:"apiRefActions",title:"Actions API - Domma CMS"},{path:"/docs/api/forms",view:"apiRefForms",title:"Forms API - Domma CMS"},{path:"/docs/api/scaffold",view:"apiRefScaffold",title:"Scaffold API - Domma CMS"},{path:"/docs/api/external",view:"apiRefExternal",title:"External API & Tokens - Domma CMS"},{path:"/docs/api/builder",view:"apiRefBuilder",title:"API Builder - Domma CMS"},{path:"/api-reference",view:"apiReference",title:"API Reference - Domma CMS"},{path:"/collections",view:"collections",title:"Collections - Domma CMS"},{path:"/collections/new",view:"collectionEditor",title:"New Collection - Domma CMS"},{path:"/collections/edit/:slug",view:"collectionEditor",title:"Edit Collection - Domma CMS"},{path:"/collections/:slug/entries",view:"collectionEntries",title:"Entries - Domma CMS"},{path:"/forms",view:"forms",title:"Forms - Domma CMS"},{path:"/forms/new",view:"formEditor",title:"New Form - Domma CMS"},{path:"/forms/edit/:slug",view:"formEditor",title:"Edit Form - Domma CMS"},{path:"/forms/:slug/submissions",view:"formSubmissions",title:"Submissions - Domma CMS"},{path:"/views",view:"viewsList",title:"Views - Domma CMS"},{path:"/views/new",view:"viewEditor",title:"New View - Domma CMS"},{path:"/views/edit/:slug",view:"viewEditor",title:"Edit View - Domma CMS"},{path:"/views/:slug/preview",view:"viewPreview",title:"View Preview - Domma CMS"},{path:"/actions",view:"actionsList",title:"Actions - Domma CMS"},{path:"/actions/new",view:"actionEditor",title:"New Action - Domma CMS"},{path:"/actions/edit/:slug",view:"actionEditor",title:"Edit Action - Domma CMS"},{path:"/pro/docs",view:"proDocs",title:"Pro Documentation - Domma CMS"},{path:"/blocks",view:"blocks",title:"Blocks - Domma CMS"},{path:"/blocks/new",view:"blockEditor",title:"New Block - Domma CMS"},{path:"/blocks/edit/:name",view:"blockEditor",title:"Edit Block - Domma CMS"},{path:"/components",view:"components",title:"Components - Domma CMS"},{path:"/components/new",view:"componentEditor",title:"New Component - Domma CMS"},{path:"/components/edit/:name",view:"componentEditor",title:"Edit Component - Domma CMS"},{path:"/my-profile",view:"myProfile",title:"My Profile - Domma CMS"},{path:"/roles",view:"roles",title:"Roles & Permissions - Domma CMS"},{path:"/roles/edit/:id",view:"roleEditor",title:"Edit Role - Domma CMS"},{path:"/effects",view:"effects",title:"Effects - Domma CMS"},{path:"/search",view:"search",title:"Search - Domma CMS"},{path:"/notes",view:"notes",title:"Notes - Domma CMS"},{path:"/todo",view:"todo",title:"Todo - Domma CMS"},{path:"/analytics",view:"analytics",title:"Analytics - Domma CMS"},{path:"/seo",view:"seo",title:"SEO - Domma CMS"},{path:"/seo/:tab",view:"seo",title:"SEO - Domma CMS"},{path:"/contacts",view:"contacts",title:"Contacts - Domma CMS"},{path:"/system/notifications",view:"notifications",title:"Notifications - Domma CMS"},{path:"/api-tokens",view:"apiTokens",title:"API Tokens - Domma CMS"},{path:"/api-endpoints",view:"apiEndpoints",title:"API Builder - Domma CMS"},{path:"/api-endpoints/new",view:"apiEndpointEditor",title:"New API Endpoint - Domma CMS"},{path:"/api-endpoints/edit/:id",view:"apiEndpointEditor",title:"Edit API Endpoint - Domma CMS"},{path:"/projects",view:"projects",title:"Projects - Domma CMS"},{path:"/projects/new",view:"projects",title:"New Project - Domma CMS"},{path:"/projects/edit/:slug",view:"projects",title:"Edit Project - Domma CMS"},{path:"/projects/:slug/settings",view:"projectSettings",title:"Project settings - Domma CMS"},{path:"/projects/:slug/pages",view:"pages",title:"Project pages - Domma CMS"},{path:"/projects/:slug/collections",view:"collections",title:"Project collections - Domma CMS"},{path:"/projects/:slug/forms",view:"forms",title:"Project forms - Domma CMS"},{path:"/projects/:slug/actions",view:"actionsList",title:"Project actions - Domma CMS"},{path:"/projects/:slug/menus",view:"menus",title:"Project menus - Domma CMS"},{path:"/projects/:slug/blocks",view:"blocks",title:"Project blocks - Domma CMS"},{path:"/projects/:slug/components",view:"components",title:"Project components - Domma CMS"},{path:"/projects/:slug/views",view:"viewsList",title:"Project views - Domma CMS"},{path:"/projects/:slug/roles",view:"roles",title:"Project roles - Domma CMS"},{path:"/projects/:slug/users",view:"users",title:"Project users - Domma CMS"},{path:"/projects/:slug/apis",view:"apiEndpoints",title:"Project APIs - Domma CMS"},{path:"/projects/:slug",view:"projectDetail",title:"Project - Domma CMS"},{path:"/login",view:"login",title:"Sign in - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}},{path:"/reset-password",view:"login",title:"Reset Password - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}}];M.subscribe("router:afterChange",async({to:t,from:e})=>{if(!C(t.path)){if($("#admin-sidebar").show(),$("#admin-topbar").show(),C(e?.path)){$("#topbar-user-name").remove(),import("./lib/admin-theme.js").then(a=>a.refreshAdminTheme(r)),await x();const i=await g(),o=D(t.path);if(o&&R.navigate(o),j(i),it.read().signInToasts)try{const a=await r.system.notifications.list().catch(()=>[]),n=(Array.isArray(a)?a:[]).filter(s=>s.unread&&["warning","critical"].includes(s.data?.severity)).slice(0,3);for(const s of n){const f=p(s.data?.title||""),k=p((s.data?.body||"").slice(0,120));E.toast(`${f} - ${k}`,{type:s.data?.severity==="critical"?"error":"warning",duration:0})}}catch{}}T()}}),M.subscribe("router:afterChange",()=>{setTimeout(()=>{$(".btn-primary, .btn-danger").length&&Domma.effects.reveal(".btn-primary, .btn-danger",{animation:"fade",stagger:40,duration:300})},50)});const V=["#field-project","#menu-project","#collection-project","#endpoint-project","#action-project","#view-project","#block-project","#component-project","#role-project",'[name="project"]','[name="ownedByProject"]'];function W(t){return/^\/(pages|menus|context-menus|collections|forms|actions|views|blocks|components|users|roles|api-endpoints)\/(new|edit\/)/.test(t)}M.subscribe("router:afterChange",({to:t})=>{const e=t?String(t.path).split("?")[0]:"";if(!t||!W(e))return;const o=new URLSearchParams(location.hash.split("?")[1]||"").get("project");if(!o||!/\/new$/.test(e))return;let a=0;const n=60,s=1500,f=()=>{a+=n;for(const k of V){const v=document.querySelector(k);if(!v)continue;if(Array.from(v.options||[]).find(H=>H.value===o)){v.value=o,v.dispatchEvent(new Event("change",{bubbles:!0}));return}}a<s&&setTimeout(f,n)};setTimeout(f,n)});const I="cms_card_states",A=S.get(I)||{};M.subscribe("router:afterChange",()=>{setTimeout(()=>{$("#view-container .card-collapsible").each(function(){const t=$(this).find(".card-header h2, .card-header h3").first().text().trim();t&&A[t]==="collapsed"&&$(this).addClass("card-collapsed")})},200)}),$("#view-container").on("click",".card-collapsible .card-header",function(t){if($(t.target).closest("button, a").length)return;const e=$(this).closest(".card");e.toggleClass("card-collapsed");const i=$(this).find("h2, h3").first().text().trim();i&&(A[i]=e.hasClass("card-collapsed")?"collapsed":"open",S.set(I,A))}),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash.startsWith("#/login")||window.location.hash.startsWith("#/reset-password"))return;const e=document.querySelector("#view-container .view-header button.btn-primary");e&&(t.preventDefault(),e.click())});const m={...q},_={notes:{displayName:"Notes",icon:"edit-3"},todo:{displayName:"Todo",icon:"check-square"},analytics:{displayName:"Analytics",icon:"chart-bar"},seo:{displayName:"SEO",icon:"search"},contacts:{displayName:"Contacts",icon:"users"},projects:{displayName:"Projects",icon:"folder"},pages:{displayName:"Pages",icon:"file-text"},media:{displayName:"Media",icon:"image"},menus:{displayName:"Menus",icon:"menu"},contextMenus:{displayName:"Context Menus",icon:"menu"},theme:{displayName:"Theme",icon:"droplet"},collections:{displayName:"Collections",icon:"database"},forms:{displayName:"Forms",icon:"layout"},viewsList:{displayName:"Views",icon:"eye"},actionsList:{displayName:"Actions",icon:"zap"},apiEndpoints:{displayName:"API Builder",icon:"code"},blocks:{displayName:"Blocks",icon:"box"},components:{displayName:"Components",icon:"component"},notifications:{displayName:"Notifications",icon:"bell"},roles:{displayName:"Roles",icon:"shield"},users:{displayName:"Users",icon:"users"},settings:{displayName:"Site Settings",icon:"settings"},effects:{displayName:"Effects",icon:"sparkles"},layouts:{displayName:"Layouts",icon:"layout"},plugins:{displayName:"Marketplace",icon:"store"},myProfile:{displayName:"My Profile",icon:"user"},apiTokens:{displayName:"API Tokens",icon:"key"},search:{displayName:"Search",icon:"search"},dashboard:{displayName:"Dashboard",icon:"home"},pageEditor:{displayName:"Pages",section:"Edit",icon:"file-text"},menuEditor:{displayName:"Menus",section:"Edit",icon:"menu"},menuLocations:{displayName:"Menus",section:"Locations",icon:"map-pin"},contextMenuEditor:{displayName:"Context Menus",section:"Edit",icon:"menu"},collectionEditor:{displayName:"Collections",section:"Edit",icon:"database"},collectionEntries:{displayName:"Collections",section:"Entries",icon:"database"},formEditor:{displayName:"Forms",section:"Edit",icon:"layout"},formSubmissions:{displayName:"Forms",section:"Submissions",icon:"inbox"},viewEditor:{displayName:"Views",section:"Edit",icon:"eye"},viewPreview:{displayName:"Views",section:"Preview",icon:"eye"},actionEditor:{displayName:"Actions",section:"Edit",icon:"zap"},apiEndpointEditor:{displayName:"API Builder",section:"Edit",icon:"code"},blockEditor:{displayName:"Blocks",section:"Edit",icon:"box"},componentEditor:{displayName:"Components",section:"Edit",icon:"component"},roleEditor:{displayName:"Roles",section:"Edit",icon:"shield"},userEditor:{displayName:"Users",section:"Edit",icon:"users"},projectDetail:{displayName:"Projects",section:"Overview",icon:"folder"},projectSettings:{displayName:"Projects",section:"Settings",icon:"folder"},pluginCode:{displayName:"Marketplace",section:"Code",icon:"code"}};for(const[t,e]of Object.entries(_))m[t]&&(m[t]=B(m[t],{plugin:t,licence:"Built in",...e}));const U=[...F];async function x(){if(u())try{const t=await r.plugins.adminConfig();t.routes?.length&&U.push(...t.routes);for(const[e,i]of Object.entries(t.views||{}))try{const o=await import(`/plugins/${i.entry}`);m[e]=B(o[i.exportName],(t.meta||{})[e])}catch{}for(const[e,{route:i}]of Object.entries(t.takeovers||{}))i&&(b[`/${e}`]=i);for(const[e,{enabled:i,displayName:o}]of Object.entries(t.tools||{}))i===!1&&(y[`/${e}`]=o);for(const{id:e,href:i}of t.css||[])if(!document.getElementById(e)){const o=document.createElement("link");o.id=e,o.rel="stylesheet",o.href=i,document.head.appendChild(o)}}catch{}}function T(){const t=O();if(!t||$("#topbar-user-name").length)return;const i={"super-admin":"Super Admin",admin:"Admin",user:"User"}[t.role]||t.role,o=ot(t);$("#topbar-user").html(`
|
|
1
|
+
import{views as q}from"./views/index.js";import{withPluginChrome as B}from"./lib/plugin-chrome.js";import{api as r,getUser as O,isAuthenticated as u,logout as G,sendToLogin as z}from"./api.js";import{isAuthScreen as C,loginHashFor as K}from"./lib/login-arrange.js";import{installHttpInterceptor as J}from"./http-interceptor.js";import{installResizableSlideovers as Y}from"./lib/slideover-resizable.js";import{installActivityThrobber as Q}from"./lib/activity-throbber.js";import{isPathAllowed as X,redirectFor as Z}from"./lib/admin-scope.js";import{deliverDesktop as tt,desktopActive as et,notificationSettings as it}from"./lib/desktop-notify.js";import{avatarUrl as ot}from"./lib/avatar.js";$(()=>{J(),Q(),Y(),(async()=>{const{applyAdminTheme:t,refreshAdminTheme:e}=await import("./lib/admin-theme.js");u()?await e(r,{init:!0}):t({dommaTheme:"charcoal-dark"},{init:!0})})();let c=null,l="/",h=!1,w=null;function D(t){if(y[t])return E.toast(`${y[t]} is switched off on this site.`,{type:"info"}),"/";const e=b[t]||Z(c,t);return e||((t.split("?")[0]||"/")==="/"&&l&&l!=="/"?b[l]||l:null)}const b={},y={};R.use(async(t,e,i)=>{if(C(t.path))return i();if(!u()){z();return}const o=D(t.path);if(o){R.navigate(o);return}i()});async function g(){try{const t=await r.get("/auth/permissions");c=t.adminScope||null,l=t.adminHome||"/";const e=t.permissions||[];return h=X(c,"/settings")&&e.some(i=>i==="settings"||i.startsWith("settings.")),w=e.some(i=>i==="notifications"||i.startsWith("notifications.")),e}catch{return c=null,l="/",h=!1,w=null,[]}}document.addEventListener("dm:admin-home-changed",()=>{g()});let P=null,N=!1;document.addEventListener("dm:sidebar-rebuild",()=>{if(P){N=!0;return}P=(async()=>{do{N=!1;try{await j(await g())}catch{}}while(N)})().finally(()=>{P=null})});async function j(t){const{renderAdminSidebar:e}=await import("./lib/sidebar-renderer.js");await e({mount:"#admin-sidebar",permissions:t,adminScope:c})}M.subscribe("router:afterChange",({to:t})=>{C(t.path)&&(d&&(clearInterval(d),d=null),c=null,l="/",h=!1,w=null)});const F=[{path:"/",view:"dashboard",title:"Dashboard - Domma CMS"},{path:"/pages",view:"pages",title:"Pages - Domma CMS"},{path:"/pages/new",view:"pageEditor",title:"New Page - Domma CMS"},{path:"/pages/edit/*",view:"pageEditor",title:"Edit Page - Domma CMS"},{path:"/media",view:"media",title:"Media - Domma CMS"},{path:"/menus",view:"menus",title:"Menus - Domma CMS"},{path:"/context-menus",view:"contextMenus",title:"Context Menus - Domma CMS"},{path:"/context-menus/new",view:"contextMenuEditor",title:"New Context Menu - Domma CMS"},{path:"/context-menus/edit/:slug",view:"contextMenuEditor",title:"Edit Context Menu - Domma CMS"},{path:"/menus/new",view:"menuEditor",title:"New Menu - Domma CMS"},{path:"/menus/edit/:slug",view:"menuEditor",title:"Edit Menu - Domma CMS"},{path:"/menu-locations",view:"menuLocations",title:"Menu Locations - Domma CMS"},{path:"/navigation",view:"menusRedirect",title:"Menus - Domma CMS"},{path:"/layouts",view:"layouts",title:"Layouts - Domma CMS"},{path:"/settings",view:"settings",title:"Settings - Domma CMS"},{path:"/theme",view:"theme",title:"Theme - Domma CMS"},{path:"/users",view:"users",title:"Users - Domma CMS"},{path:"/users/new",view:"userEditor",title:"New User - Domma CMS"},{path:"/users/edit/:id",view:"userEditor",title:"Edit User - Domma CMS"},{path:"/plugins",view:"plugins",title:"Plugins - Domma CMS"},{path:"/plugins/code/:name",view:"pluginCode",title:"Plugin Code - Domma CMS"},{path:"/documentation",view:"documentation",title:"Usage - Domma CMS"},{path:"/tutorials",view:"tutorials",title:"Tutorials - Domma CMS"},{path:"/tutorials/crud",view:"tutorialCrud",title:"Building a CRUD App - Domma CMS"},{path:"/tutorials/plugin",view:"tutorialPlugin",title:"Writing a Plugin - Domma CMS"},{path:"/tutorials/forms",view:"tutorialForms",title:"Form Follow-Up - Domma CMS"},{path:"/docs/components",view:"componentsReference",title:"Components Reference - Domma CMS"},{path:"/docs/components-howto",view:"componentsHowto",title:"Components How-To - Domma CMS"},{path:"/docs/components-walkthrough",view:"componentsWalkthrough",title:"Components Walkthrough - Domma CMS"},{path:"/docs/components-rules",view:"componentsRules",title:"Components Rules - Domma CMS"},{path:"/docs/usage/pages",view:"usagePages",title:"Pages - Usage - Domma CMS"},{path:"/docs/usage/media",view:"usageMedia",title:"Media - Usage - Domma CMS"},{path:"/docs/usage/navigation",view:"usageNavigation",title:"Navigation - Usage - Domma CMS"},{path:"/docs/usage/dconfig",view:"usageDconfig",title:"DConfig - Usage - Domma CMS"},{path:"/docs/usage/shortcodes",view:"usageShortcodes",title:"Shortcodes - Usage - Domma CMS"},{path:"/docs/usage/site-settings",view:"usageSiteSettings",title:"Site Settings - Usage - Domma CMS"},{path:"/docs/usage/plugins",view:"usagePlugins",title:"Plugins - Usage - Domma CMS"},{path:"/docs/usage/users-roles",view:"usageUsersRoles",title:"Users & Roles - Usage - Domma CMS"},{path:"/docs/usage/views",view:"usageViews",title:"Views - Usage - Domma CMS"},{path:"/docs/usage/actions",view:"usageActions",title:"Actions - Usage - Domma CMS"},{path:"/docs/usage/cta-shortcode",view:"usageCtaShortcode",title:"CTA Shortcode - Usage - Domma CMS"},{path:"/docs/usage/editions",view:"usageEditions",title:"Editions & Licences - Usage - Domma CMS"},{path:"/docs/usage/collections",view:"usageCollections",title:"Collections & Forms - Usage - Domma CMS"},{path:"/docs/usage/tools",view:"usageTools",title:"Built-in Tools - Usage - Domma CMS"},{path:"/docs/plugins",view:"pluginGuides",title:"Plugin Guides - Domma CMS"},{path:"/docs/plugins/*",view:"pluginGuide",title:"Plugin Guide - Domma CMS"},{path:"/docs/api/authentication",view:"apiRefAuthentication",title:"Authentication - API - Domma CMS"},{path:"/docs/api/pages",view:"apiRefPages",title:"Pages - API - Domma CMS"},{path:"/docs/api/settings",view:"apiRefSettings",title:"Settings - API - Domma CMS"},{path:"/docs/api/layouts",view:"apiRefLayouts",title:"Layouts - API - Domma CMS"},{path:"/docs/api/navigation",view:"apiRefNavigation",title:"Navigation - API - Domma CMS"},{path:"/docs/api/media",view:"apiRefMedia",title:"Media - API - Domma CMS"},{path:"/docs/api/users",view:"apiRefUsers",title:"Users - API - Domma CMS"},{path:"/docs/api/plugins",view:"apiRefPlugins",title:"Plugins - API - Domma CMS"},{path:"/docs/api/collections",view:"apiRefCollections",title:"Collections - API - Domma CMS"},{path:"/docs/api/views",view:"apiRefViews",title:"Views API - Domma CMS"},{path:"/docs/api/actions",view:"apiRefActions",title:"Actions API - Domma CMS"},{path:"/docs/api/forms",view:"apiRefForms",title:"Forms API - Domma CMS"},{path:"/docs/api/scaffold",view:"apiRefScaffold",title:"Scaffold API - Domma CMS"},{path:"/docs/api/external",view:"apiRefExternal",title:"External API & Tokens - Domma CMS"},{path:"/docs/api/builder",view:"apiRefBuilder",title:"API Builder - Domma CMS"},{path:"/api-reference",view:"apiReference",title:"API Reference - Domma CMS"},{path:"/collections",view:"collections",title:"Collections - Domma CMS"},{path:"/collections/new",view:"collectionEditor",title:"New Collection - Domma CMS"},{path:"/collections/edit/:slug",view:"collectionEditor",title:"Edit Collection - Domma CMS"},{path:"/collections/:slug/entries",view:"collectionEntries",title:"Entries - Domma CMS"},{path:"/forms",view:"forms",title:"Forms - Domma CMS"},{path:"/forms/new",view:"formEditor",title:"New Form - Domma CMS"},{path:"/forms/edit/:slug",view:"formEditor",title:"Edit Form - Domma CMS"},{path:"/forms/:slug/submissions",view:"formSubmissions",title:"Submissions - Domma CMS"},{path:"/views",view:"viewsList",title:"Views - Domma CMS"},{path:"/views/new",view:"viewEditor",title:"New View - Domma CMS"},{path:"/views/edit/:slug",view:"viewEditor",title:"Edit View - Domma CMS"},{path:"/views/:slug/preview",view:"viewPreview",title:"View Preview - Domma CMS"},{path:"/actions",view:"actionsList",title:"Actions - Domma CMS"},{path:"/actions/new",view:"actionEditor",title:"New Action - Domma CMS"},{path:"/actions/edit/:slug",view:"actionEditor",title:"Edit Action - Domma CMS"},{path:"/pro/docs",view:"proDocs",title:"Pro Documentation - Domma CMS"},{path:"/blocks",view:"blocks",title:"Blocks - Domma CMS"},{path:"/blocks/new",view:"blockEditor",title:"New Block - Domma CMS"},{path:"/blocks/edit/:name",view:"blockEditor",title:"Edit Block - Domma CMS"},{path:"/components",view:"components",title:"Components - Domma CMS"},{path:"/components/new",view:"componentEditor",title:"New Component - Domma CMS"},{path:"/components/edit/:name",view:"componentEditor",title:"Edit Component - Domma CMS"},{path:"/my-profile",view:"myProfile",title:"My Profile - Domma CMS"},{path:"/roles",view:"roles",title:"Roles & Permissions - Domma CMS"},{path:"/roles/edit/:id",view:"roleEditor",title:"Edit Role - Domma CMS"},{path:"/effects",view:"effects",title:"Effects - Domma CMS"},{path:"/search",view:"search",title:"Search - Domma CMS"},{path:"/notes",view:"notes",title:"Notes - Domma CMS"},{path:"/todo",view:"todo",title:"Todo - Domma CMS"},{path:"/analytics",view:"analytics",title:"Analytics - Domma CMS"},{path:"/seo",view:"seo",title:"SEO - Domma CMS"},{path:"/seo/:tab",view:"seo",title:"SEO - Domma CMS"},{path:"/contacts",view:"contacts",title:"Contacts - Domma CMS"},{path:"/calls/new",view:"callNew",title:"Call now - Domma CMS"},{path:"/system/notifications",view:"notifications",title:"Notifications - Domma CMS"},{path:"/api-tokens",view:"apiTokens",title:"API Tokens - Domma CMS"},{path:"/api-endpoints",view:"apiEndpoints",title:"API Builder - Domma CMS"},{path:"/api-endpoints/new",view:"apiEndpointEditor",title:"New API Endpoint - Domma CMS"},{path:"/api-endpoints/edit/:id",view:"apiEndpointEditor",title:"Edit API Endpoint - Domma CMS"},{path:"/projects",view:"projects",title:"Projects - Domma CMS"},{path:"/projects/new",view:"projects",title:"New Project - Domma CMS"},{path:"/projects/edit/:slug",view:"projects",title:"Edit Project - Domma CMS"},{path:"/projects/:slug/settings",view:"projectSettings",title:"Project settings - Domma CMS"},{path:"/projects/:slug/pages",view:"pages",title:"Project pages - Domma CMS"},{path:"/projects/:slug/collections",view:"collections",title:"Project collections - Domma CMS"},{path:"/projects/:slug/forms",view:"forms",title:"Project forms - Domma CMS"},{path:"/projects/:slug/actions",view:"actionsList",title:"Project actions - Domma CMS"},{path:"/projects/:slug/menus",view:"menus",title:"Project menus - Domma CMS"},{path:"/projects/:slug/blocks",view:"blocks",title:"Project blocks - Domma CMS"},{path:"/projects/:slug/components",view:"components",title:"Project components - Domma CMS"},{path:"/projects/:slug/views",view:"viewsList",title:"Project views - Domma CMS"},{path:"/projects/:slug/roles",view:"roles",title:"Project roles - Domma CMS"},{path:"/projects/:slug/users",view:"users",title:"Project users - Domma CMS"},{path:"/projects/:slug/apis",view:"apiEndpoints",title:"Project APIs - Domma CMS"},{path:"/projects/:slug",view:"projectDetail",title:"Project - Domma CMS"},{path:"/login",view:"login",title:"Sign in - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}},{path:"/reset-password",view:"login",title:"Reset Password - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}}];M.subscribe("router:afterChange",async({to:t,from:e})=>{if(!C(t.path)){if($("#admin-sidebar").show(),$("#admin-topbar").show(),C(e?.path)){$("#topbar-user-name").remove(),import("./lib/admin-theme.js").then(a=>a.refreshAdminTheme(r)),await x();const i=await g(),o=D(t.path);if(o&&R.navigate(o),j(i),it.read().signInToasts)try{const a=await r.system.notifications.list().catch(()=>[]),n=(Array.isArray(a)?a:[]).filter(s=>s.unread&&["warning","critical"].includes(s.data?.severity)).slice(0,3);for(const s of n){const f=p(s.data?.title||""),k=p((s.data?.body||"").slice(0,120));E.toast(`${f} - ${k}`,{type:s.data?.severity==="critical"?"error":"warning",duration:0})}}catch{}}T()}}),M.subscribe("router:afterChange",()=>{setTimeout(()=>{$(".btn-primary, .btn-danger").length&&Domma.effects.reveal(".btn-primary, .btn-danger",{animation:"fade",stagger:40,duration:300})},50)});const V=["#field-project","#menu-project","#collection-project","#endpoint-project","#action-project","#view-project","#block-project","#component-project","#role-project",'[name="project"]','[name="ownedByProject"]'];function W(t){return/^\/(pages|menus|context-menus|collections|forms|actions|views|blocks|components|users|roles|api-endpoints)\/(new|edit\/)/.test(t)}M.subscribe("router:afterChange",({to:t})=>{const e=t?String(t.path).split("?")[0]:"";if(!t||!W(e))return;const o=new URLSearchParams(location.hash.split("?")[1]||"").get("project");if(!o||!/\/new$/.test(e))return;let a=0;const n=60,s=1500,f=()=>{a+=n;for(const k of V){const v=document.querySelector(k);if(!v)continue;if(Array.from(v.options||[]).find(H=>H.value===o)){v.value=o,v.dispatchEvent(new Event("change",{bubbles:!0}));return}}a<s&&setTimeout(f,n)};setTimeout(f,n)});const I="cms_card_states",A=S.get(I)||{};M.subscribe("router:afterChange",()=>{setTimeout(()=>{$("#view-container .card-collapsible").each(function(){const t=$(this).find(".card-header h2, .card-header h3").first().text().trim();t&&A[t]==="collapsed"&&$(this).addClass("card-collapsed")})},200)}),$("#view-container").on("click",".card-collapsible .card-header",function(t){if($(t.target).closest("button, a").length)return;const e=$(this).closest(".card");e.toggleClass("card-collapsed");const i=$(this).find("h2, h3").first().text().trim();i&&(A[i]=e.hasClass("card-collapsed")?"collapsed":"open",S.set(I,A))}),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash.startsWith("#/login")||window.location.hash.startsWith("#/reset-password"))return;const e=document.querySelector("#view-container .view-header button.btn-primary");e&&(t.preventDefault(),e.click())});const m={...q},_={notes:{displayName:"Notes",icon:"edit-3"},todo:{displayName:"Todo",icon:"check-square"},analytics:{displayName:"Analytics",icon:"chart-bar"},seo:{displayName:"SEO",icon:"search"},contacts:{displayName:"Contacts",icon:"users"},projects:{displayName:"Projects",icon:"folder"},pages:{displayName:"Pages",icon:"file-text"},media:{displayName:"Media",icon:"image"},menus:{displayName:"Menus",icon:"menu"},contextMenus:{displayName:"Context Menus",icon:"menu"},theme:{displayName:"Theme",icon:"droplet"},collections:{displayName:"Collections",icon:"database"},forms:{displayName:"Forms",icon:"layout"},viewsList:{displayName:"Views",icon:"eye"},actionsList:{displayName:"Actions",icon:"zap"},apiEndpoints:{displayName:"API Builder",icon:"code"},blocks:{displayName:"Blocks",icon:"box"},components:{displayName:"Components",icon:"component"},notifications:{displayName:"Notifications",icon:"bell"},roles:{displayName:"Roles",icon:"shield"},users:{displayName:"Users",icon:"users"},settings:{displayName:"Site Settings",icon:"settings"},effects:{displayName:"Effects",icon:"sparkles"},layouts:{displayName:"Layouts",icon:"layout"},plugins:{displayName:"Marketplace",icon:"store"},myProfile:{displayName:"My Profile",icon:"user"},apiTokens:{displayName:"API Tokens",icon:"key"},search:{displayName:"Search",icon:"search"},dashboard:{displayName:"Dashboard",icon:"home"},pageEditor:{displayName:"Pages",section:"Edit",icon:"file-text"},menuEditor:{displayName:"Menus",section:"Edit",icon:"menu"},menuLocations:{displayName:"Menus",section:"Locations",icon:"map-pin"},contextMenuEditor:{displayName:"Context Menus",section:"Edit",icon:"menu"},collectionEditor:{displayName:"Collections",section:"Edit",icon:"database"},collectionEntries:{displayName:"Collections",section:"Entries",icon:"database"},formEditor:{displayName:"Forms",section:"Edit",icon:"layout"},formSubmissions:{displayName:"Forms",section:"Submissions",icon:"inbox"},viewEditor:{displayName:"Views",section:"Edit",icon:"eye"},viewPreview:{displayName:"Views",section:"Preview",icon:"eye"},actionEditor:{displayName:"Actions",section:"Edit",icon:"zap"},apiEndpointEditor:{displayName:"API Builder",section:"Edit",icon:"code"},blockEditor:{displayName:"Blocks",section:"Edit",icon:"box"},componentEditor:{displayName:"Components",section:"Edit",icon:"component"},roleEditor:{displayName:"Roles",section:"Edit",icon:"shield"},userEditor:{displayName:"Users",section:"Edit",icon:"users"},projectDetail:{displayName:"Projects",section:"Overview",icon:"folder"},projectSettings:{displayName:"Projects",section:"Settings",icon:"folder"},pluginCode:{displayName:"Marketplace",section:"Code",icon:"code"}};for(const[t,e]of Object.entries(_))m[t]&&(m[t]=B(m[t],{plugin:t,licence:"Built in",...e}));const U=[...F];async function x(){if(u())try{const t=await r.plugins.adminConfig();t.routes?.length&&U.push(...t.routes);for(const[e,i]of Object.entries(t.views||{}))try{const o=await import(`/plugins/${i.entry}`);m[e]=B(o[i.exportName],(t.meta||{})[e])}catch{}for(const[e,{route:i}]of Object.entries(t.takeovers||{}))i&&(b[`/${e}`]=i);for(const[e,{enabled:i,displayName:o}]of Object.entries(t.tools||{}))i===!1&&(y[`/${e}`]=o);for(const{id:e,href:i}of t.css||[])if(!document.getElementById(e)){const o=document.createElement("link");o.id=e,o.rel="stylesheet",o.href=i,document.head.appendChild(o)}}catch{}}function T(){const t=O();if(!t||$("#topbar-user-name").length)return;const i={"super-admin":"Super Admin",admin:"Admin",user:"User"}[t.role]||t.role,o=ot(t);$("#topbar-user").html(`
|
|
2
2
|
${o?`<img id="topbar-avatar" class="topbar-avatar" src="${p(o)}" alt="">`:""}
|
|
3
3
|
<span id="topbar-user-name" class="topbar-user-name">${p(t.name)}</span>
|
|
4
4
|
<span class="topbar-role-badge topbar-role-badge--${p(t.role)}">${i}</span>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
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:"Canvas-based seasonal particle system. Runs full-page on the published site.",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"]]}],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"]]}],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(t){return EFFECTS.find(e=>e.name===t)||null}export function effectsByCategory(){return["Entrance","Animation","Text","Visual","Celebrations"].map(e=>({category:e,effects:EFFECTS.filter(a=>a.category===e)}))}export function effectsInjectableInto(t){return EFFECTS.filter(e=>e.hosts.includes(t))}
|
|
1
|
+
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:"Canvas-based seasonal particle system. Runs full-page on the published site.",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(t){return EFFECTS.find(e=>e.name===t)||null}export function effectsByCategory(){return["Entrance","Animation","Text","Visual","Celebrations"].map(e=>({category:e,effects:EFFECTS.filter(a=>a.category===e)}))}export function effectsInjectableInto(t){return EFFECTS.filter(e=>e.hosts.includes(t))}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<!-- Call now (instant calls). Bound with M.applyBindings - see admin/js/views/call-new.js. Not in the sidebar:
|
|
2
|
+
it is opened by "Call Sam now…" on a contact or an email, and by Booking's Call now. No user data in here. -->
|
|
3
|
+
<div class="cnx">
|
|
4
|
+
<div class="cnx-card">
|
|
5
|
+
<h1 class="cnx-title"><span data-icon="video" data-icon-size="18"></span> Call now</h1>
|
|
6
|
+
|
|
7
|
+
<p class="cnx-note" data-bind-hidden="!loading.value">Loading…</p>
|
|
8
|
+
<p class="cnx-note is-warn" data-bind-hidden="!unavailable.value" data-bind-text="unavailable.value"></p>
|
|
9
|
+
|
|
10
|
+
<div class="cnx-ask" hidden data-bind-hidden="loading.value || unavailable.value || started.value">
|
|
11
|
+
<p class="cnx-note">A video call on your site, starting now - not a booking, so nothing goes in a diary. They get a link; you let them in. The link works for 3 hours.
|
|
12
|
+
<span data-help-title="Call now" data-help="For a quick call with anyone: a customer, a supplier, a colleague. They need no account or app - the call runs in the browser. To call someone about a booking they have, open that booking instead: an online booking has Call now of its own, using the link they already have."></span></p>
|
|
13
|
+
|
|
14
|
+
<div class="cnx-from" data-bind-hidden="!contactId.value">
|
|
15
|
+
<span data-icon="user" data-icon-size="13"></span> From your contact <strong data-bind-text="contactName.value"></strong>
|
|
16
|
+
<button type="button" class="btn btn-xs btn-ghost" data-on-click="clearContact">Someone else</button>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<div class="cnx-find" data-bind-hidden="!(contactsOn.value && !contactId.value)">
|
|
20
|
+
<label class="form-label" for="cnx-q">Find a contact <span data-help-title="Find a contact" data-help="Your Contacts, by name, email or phone. Or leave this and type their name and email below."></span></label>
|
|
21
|
+
<input id="cnx-q" class="form-input" type="search" autocomplete="off" placeholder="Type a name or email" data-model="q.value">
|
|
22
|
+
<div class="cnx-matches" data-each="matches.value key=id"><button type="button" class="cnx-match" data-on-click="$root.pick($data)"><strong data-bind-text="name"></strong><span class="cnx-muted" data-bind-text="email"></span><span class="cnx-shared" data-bind-text="note"></span></button></div>
|
|
23
|
+
<p class="cnx-note" data-bind-hidden="!noMatch.value">No contact matches. Type their name and email below.</p>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div class="cnx-grid">
|
|
27
|
+
<div><label class="form-label" for="cnx-name">Name</label>
|
|
28
|
+
<input id="cnx-name" class="form-input" maxlength="120" data-model="name.value"></div>
|
|
29
|
+
<div><label class="form-label" for="cnx-email">Email <span data-help-title="Email" data-help="Where their link is sent. Leave it empty to give them the link yourself - copy it, or read it out."></span></label>
|
|
30
|
+
<input id="cnx-email" class="form-input" type="email" maxlength="200" data-model="email.value"></div>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<label class="cnx-check"><input type="checkbox" data-model="notify.value" data-bind-disabled="!hasEmail.value"> Email them the link
|
|
34
|
+
<span data-help-title="Email them the link" data-help="A short email: you would like to talk now, with a Join the call button. Off: nothing is sent - the link is shown here for you to pass on."></span></label>
|
|
35
|
+
<p class="cnx-note is-warn" data-bind-hidden="mailReady.value">This site has no outgoing mail server set up, so no email can be sent. Give them the link yourself.</p>
|
|
36
|
+
<small class="cnx-err" data-bind-text="err.value" data-bind-hidden="!err.value"></small>
|
|
37
|
+
|
|
38
|
+
<div class="cnx-actions">
|
|
39
|
+
<button type="button" class="btn btn-sm btn-ghost" data-on-click="close">Cancel</button>
|
|
40
|
+
<button type="button" class="btn btn-sm btn-primary cnx-start" data-on-click="save" data-bind-disabled="busy.value"><span data-icon="video" data-icon-size="13"></span> Start the call</button>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="cnx-done" hidden data-bind-hidden="!started.value">
|
|
45
|
+
<p data-bind-hidden="blocked.value"><span data-icon="video" data-icon-size="14"></span> The call is open in a new tab, with you as the host. Let them in when they knock.</p>
|
|
46
|
+
<p class="cnx-note is-warn" data-bind-hidden="!blocked.value">Your browser stopped the call opening in a new tab. Start it here:</p>
|
|
47
|
+
<div class="cnx-actions is-start" data-bind-hidden="!blocked.value">
|
|
48
|
+
<a class="btn btn-sm btn-primary" data-bind-href="hostUrl.value" target="_blank" rel="noopener">Start the call</a></div>
|
|
49
|
+
<p class="cnx-emailed" data-bind-text="emailLine.value"></p>
|
|
50
|
+
<label class="form-label" for="cnx-link">Their link <span data-help-title="Their link" data-help="The address they join the call with. Anyone who has it can knock - you choose who comes in. Only send it to them."></span></label>
|
|
51
|
+
<input id="cnx-link" class="form-input cnx-link" readonly data-bind-value="joinUrl.value" data-on-focus="selectLink">
|
|
52
|
+
<div class="cnx-actions is-start">
|
|
53
|
+
<button type="button" class="btn btn-sm btn-outline" data-on-click="copyLink"><span data-icon="copy" data-icon-size="13"></span> Copy</button>
|
|
54
|
+
<a class="btn btn-sm btn-ghost" data-bind-href="mailto.value"><span data-icon="mail" data-icon-size="13"></span> Email it…</a>
|
|
55
|
+
<span class="cnx-spacer"></span>
|
|
56
|
+
<button type="button" class="btn btn-sm btn-ghost" data-on-click="close">Done</button>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<style>
|
|
63
|
+
.cnx { max-width: 640px; margin: 1.5rem auto; }
|
|
64
|
+
.cnx [hidden] { display: none !important; }
|
|
65
|
+
.cnx-card { border: 1px solid var(--dm-border, rgba(128,128,128,.3)); border-radius: 10px; padding: 1.25rem 1.4rem; background: var(--dm-surface, transparent); }
|
|
66
|
+
.cnx-title { display: flex; align-items: center; gap: .5rem; font-size: 1.25rem; margin: 0 0 .75rem; }
|
|
67
|
+
.cnx-note { color: var(--dm-text-muted, inherit); font-size: .9rem; margin: 0 0 .9rem; }
|
|
68
|
+
.cnx-note.is-warn { color: var(--dm-warning, #b45309); }
|
|
69
|
+
.cnx-from { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; margin-bottom: .9rem; }
|
|
70
|
+
.cnx-find { margin-bottom: .9rem; }
|
|
71
|
+
.cnx-matches { display: flex; flex-direction: column; gap: 2px; margin-top: .35rem; }
|
|
72
|
+
.cnx-match { display: flex; gap: .6rem; align-items: baseline; text-align: left; background: none; border: 1px solid transparent; border-radius: 6px; padding: .35rem .5rem; color: inherit; cursor: pointer; }
|
|
73
|
+
.cnx-match:hover, .cnx-match:focus-visible { border-color: var(--dm-border, rgba(128,128,128,.4)); background: var(--dm-surface-hover, rgba(128,128,128,.08)); }
|
|
74
|
+
.cnx-muted { color: var(--dm-text-muted, inherit); font-size: .85rem; }
|
|
75
|
+
.cnx-shared { margin-left: auto; color: var(--dm-text-muted, inherit); font-size: .75rem; font-style: italic; }
|
|
76
|
+
.cnx-shared:empty { display: none; }
|
|
77
|
+
.cnx-grid { display: grid; grid-template-columns: 1fr 1fr; gap: .75rem; margin-bottom: .75rem; }
|
|
78
|
+
@media (max-width: 560px) { .cnx-grid { grid-template-columns: 1fr; } }
|
|
79
|
+
.cnx-check { display: flex; align-items: center; gap: .45rem; margin: .25rem 0 .75rem; }
|
|
80
|
+
.cnx-err { display: block; color: var(--dm-danger, #b91c1c); margin-bottom: .5rem; }
|
|
81
|
+
.cnx-actions { display: flex; gap: .5rem; justify-content: flex-end; align-items: center; margin-top: .75rem; flex-wrap: wrap; }
|
|
82
|
+
.cnx-actions.is-start { justify-content: flex-start; }
|
|
83
|
+
.cnx-spacer { flex: 1; }
|
|
84
|
+
.cnx .btn { display: inline-flex; align-items: center; gap: .35rem; }
|
|
85
|
+
.cnx-matches:empty { display: none; }
|
|
86
|
+
.cnx-emailed { margin: .5rem 0 .9rem; }
|
|
87
|
+
.cnx-link { font-family: var(--dm-font-mono, monospace); font-size: .85rem; }
|
|
88
|
+
</style>
|
|
@@ -26,6 +26,21 @@
|
|
|
26
26
|
holders of the permission can change them. In Manage groups, Rename and Delete are greyed out
|
|
27
27
|
where you may not, with a note saying why.</p>
|
|
28
28
|
|
|
29
|
+
<h3>Call someone now (Pro)</h3>
|
|
30
|
+
<p>With a Pro Tool that has calls - <strong>Booking Pro</strong>, <strong>Contacts Pro</strong> or
|
|
31
|
+
the <strong>Calendar</strong> - a contact's menu and an email's menu offer <strong>Call Sam
|
|
32
|
+
now…</strong>: a video call on your own site with that one person, starting now. It is not a
|
|
33
|
+
booking or an event, so nothing goes in a diary. The <strong>Call now</strong> screen opens
|
|
34
|
+
with them filled in (or pick a contact, or type a name and email). Tick <strong>Email them
|
|
35
|
+
the link</strong> to send them a <strong>Join the call</strong> button, then click
|
|
36
|
+
<strong>Start the call</strong>: the call opens in a new tab with you as the host, and the
|
|
37
|
+
screen shows their link to copy or send. They need no account or app, and wait until you let
|
|
38
|
+
them in. The link works for 3 hours. The contact's <strong>Calls</strong> list shows your
|
|
39
|
+
recent calls with them.</p>
|
|
40
|
+
<p>Who may start a call is the <strong>Calls</strong> permission under <strong>System >
|
|
41
|
+
Roles</strong>; Admin is given it once, the first time a Tool with calls runs. A site without
|
|
42
|
+
any of those Tools has no instant calls at all.</p>
|
|
43
|
+
|
|
29
44
|
<h3>Notes</h3>
|
|
30
45
|
<p>Private notes for each user, with categories and search.</p>
|
|
31
46
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{apiRequest as p}from"../api.js";import{helpPopovers as V}from"../lib/help-popover.js";import{viewLifetime as Y}from"../lib/tool-kit.js";const z=/^#\/calls\/new\/?(\?.*)?$/,G=/^[^\s@<>"]+@[^\s@<>"]+\.[^\s@<>"]+$/,f=Y(z);export function openHostTab(){let t=null;try{t=window.open("","_blank")}catch{t=null}if(t)try{t.document.title="Starting the call\u2026",t.document.body.style.font="16px system-ui, sans-serif",t.document.body.textContent="Starting the call\u2026"}catch{}return t}export function aimTab(t,o){if(!t||t.closed)return!1;try{t.opener=null}catch{}return t.location.href=o,!0}const R=t=>(t?.message||String(t||"Something went wrong")).replace(/^HTTP \d+$/,"Something went wrong");export const callNewView={templateUrl:"/admin/js/templates/call-new.html",async onMount(t){f.reset();const o=t.get(0).querySelector(".cnx")||t.get(0),y=new URLSearchParams(location.hash.split("?")[1]||""),g=M.observable(!0),q=M.observable(""),s=M.observable(String(y.get("name")||"").slice(0,120)),i=M.observable(String(y.get("email")||"").slice(0,200)),c=M.observable(""),u=M.observable(""),r=M.observable(!0),m=M.observable(!1),w=M.observable(""),d=M.observable([]),k=M.observable(!1),h=M.observable(!1),v=M.observable(!1),b=M.observable(""),I=M.computed(()=>G.test(String(i.value||"").trim())),S=M.observable(!1),L=M.observable(!1),j=M.observable(""),T=M.observable(""),O=M.observable(""),P=M.observable("");let N=0,C=0;const x=M.effect(()=>{const e=String(w.value||"").trim();clearTimeout(C);const U=++N;if(e.length<2){d.value=[],k.value=!1;return}C=setTimeout(async()=>{try{const l=await p(`/calls/contacts?q=${encodeURIComponent(e)}`,{method:"GET"});if(U!==N)return;const a=(Array.isArray(l)?l:[]).slice(0,8).map(n=>({id:String(n.id),name:n.name||n.email||"(no name)",email:n.email||"",note:n.mine===!1?"shared with you":""}));d.value=a,k.value=a.length===0}catch(l){(l?.status===403||l?.status===404)&&(h.value=!1),d.value=[]}},200)});f.add(()=>{clearTimeout(C);try{typeof x=="function"?x():x?.dispose?.()}catch{}});const A={loading:g,unavailable:q,name:s,email:i,contactId:c,contactName:u,mailReady:r,notify:m,q:w,matches:d,noMatch:k,contactsOn:h,busy:v,err:b,hasEmail:I,started:S,blocked:L,hostUrl:j,joinUrl:T,emailLine:O,mailto:P,pick(e){c.value=e.id,u.value=e.name,s.value=e.name==="(no name)"?"":e.name,i.value=e.email,m.value=!!e.email&&r.peek(),w.value=""},clearContact(){c.value="",u.value="",s.value="",i.value="",m.value=!1},close:()=>{history.length>1?history.back():location.hash="#/"},async copyLink(){try{await navigator.clipboard.writeText(T.peek()),E.toast("Their link is copied",{type:"success",duration:1500})}catch{o.querySelector(".cnx-link")?.select?.(),E.toast("Copy it from the box (Ctrl+C)",{type:"info"})}},selectLink:e=>{try{e?.target?.select?.()}catch{}},save(){if(v.peek()||S.peek())return;const e={name:String(s.peek()||"").trim(),email:String(i.peek()||"").trim()};if(e.email&&!G.test(e.email)){b.value="That email address does not look right.";return}const U=m.peek()===!0&&!!e.email&&r.peek();b.value="";const l=openHostTab();v.value=!0,p("/calls",{method:"POST",body:JSON.stringify({...e,contactId:c.peek(),notify:U})}).then(a=>{const n=new URL(a.hostUrl,location.origin).href,_=aimTab(l,n);j.value=n,T.value=a.joinUrl,L.value=!_;const $=e.name||e.email||"them";O.value=a.emailed===!0?`Emailed ${$} their link.`:a.emailLimited===!0?`Not emailed: ${$} has had several call emails in the last hour. Send them the link below, or read it out.`:e.email?`${$} has not been emailed - send them the link below, or read it out.`:"Send them the link below, or read it out.",P.value=`mailto:${encodeURIComponent(e.email)}?subject=${encodeURIComponent("Join the call")}&body=${encodeURIComponent(`Join the call: ${a.joinUrl}`)}`,S.value=!0,setTimeout(()=>o.querySelector(".cnx-link")?.focus(),60)}).catch(a=>{try{l?.close()}catch{}b.value=a?.status===404?"Calls are not available on this site.":a?.status===429?"That is a lot of calls in a minute - wait a moment and try again.":`No call: ${R(a)}`}).finally(()=>{v.value=!1})}};f.add(V(o)),window.I?.scan?.(o);const J=Object.fromEntries(Object.entries(A).filter(([,e])=>typeof e=="function"&&!M.isObservable?.(e))),H=M.applyBindings(A,o,{methods:J});f.add(()=>{try{H?.dispose?.()}catch{}});try{const e=await p("/calls/status",{method:"GET"});r.value=e?.mailReady!==!1,h.value=e?.contacts===!0}catch(e){g.value=!1,q.value=e?.status===404?"Calls need a Pro Tool with calls - Booking Pro, Contacts Pro or Calendar.":e?.status===403?"Your role cannot start calls. Ask an administrator for the Calls permission.":`Calls could not load: ${R(e)}`;return}const B=y.get("contact");if(B)try{const e=await p(`/calls/contact/${encodeURIComponent(B)}`,{method:"GET"});c.value=e.id,u.value=e.name||e.email||"(no name)",s.value=e.name||"",i.value=e.email||""}catch(e){E.toast(`That contact could not be read: ${R(e)}`,{type:"warning"})}m.value=I.peek()&&r.peek(),g.value=!1,setTimeout(()=>o.querySelector(c.peek()||s.peek()?"#cnx-email":h.peek()?"#cnx-q":"#cnx-name")?.focus(),60)}};
|
package/admin/js/views/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dashboardView as o}from"./dashboard.js";import{pagesView as i}from"./pages.js";import{pageEditorView as r}from"./page-editor.js";import{settingsView as
|
|
1
|
+
import{dashboardView as o}from"./dashboard.js";import{pagesView as i}from"./pages.js";import{pageEditorView as r}from"./page-editor.js";import{settingsView as e}from"./settings.js";import{themeView as t}from"./theme.js";import{notificationsView as m}from"./notifications.js";import{layoutsView as p}from"./layouts.js";import{mediaView as n}from"./media.js";import{loginView as s}from"./login.js";import{usersView as f}from"./users.js";import{userEditorView as w}from"./user-editor.js";import{pluginsView as c}from"./plugins.js";import{pluginCodeView as V}from"./plugin-code.js";import{documentationView as a}from"./documentation.js";import{tutorialsView as d}from"./tutorials.js";import{docViews as l}from"./doc-pages.js";import{pluginGuidesView as u,pluginGuideView as E}from"./plugin-guides.js";import{apiReferenceView as g}from"./api-reference.js";import{collectionsView as v}from"./collections.js";import{collectionEditorView as b}from"./collection-editor.js";import{collectionEntriesView as h}from"./collection-entries.js";import{formsView as y}from"./forms.js";import{formEditorView as j}from"./form-editor.js";import{formSubmissionsView as k}from"./form-submissions.js";import{viewsListView as L}from"./views-list.js";import{viewEditorView as x}from"./view-editor.js";import{viewPreviewView as M}from"./view-preview.js";import{actionsListView as D}from"./actions-list.js";import{actionEditorView as G}from"./action-editor.js";import{proDocsView as P}from"./pro-docs.js";import{blocksView as R}from"./blocks.js";import{blockEditorView as S}from"./block-editor.js";import{componentsView as C}from"./components.js";import{componentEditorView as N}from"./component-editor.js";import{myProfileView as T}from"./my-profile.js";import{rolesView as U}from"./roles.js";import{roleEditorView as q}from"./role-editor.js";import{effectsView as z}from"./effects.js";import{searchView as A}from"./search.js";import{notesView as B}from"./notes.js";import{todoView as F}from"./todo.js";import{analyticsView as H}from"./analytics.js";import{seoView as I}from"./seo.js";import{contactsView as J}from"./contacts.js";import{callNewView as K}from"./call-new.js";import{menusView as O}from"./menus.js";import{contextMenusView as Q}from"./context-menus.js";import{contextMenuEditorView as W}from"./context-menu-editor.js";import{menuEditorView as X}from"./menu-editor.js";import{menuLocationsView as Y}from"./menu-locations.js";import{projectsView as Z}from"./projects.js";import{projectDetailView as _}from"./project-detail.js";import{projectSettingsView as $}from"./project-settings.js";import{apiTokensView as oo}from"./api-tokens.js";import{apiEndpointsView as io}from"./api-endpoints.js";import{apiEndpointEditorView as ro}from"./api-endpoint-editor.js";const eo={templateUrl:"",async onMount(){location.hash="#/menus"}};export const views={projects:Z,projectDetail:_,projectSettings:$,apiTokens:oo,apiEndpoints:io,apiEndpointEditor:ro,dashboard:o,pages:i,pageEditor:r,settings:e,theme:t,layouts:p,media:n,login:s,users:f,userEditor:w,plugins:c,pluginCode:V,documentation:a,tutorials:d,apiReference:g,collections:v,collectionEditor:b,collectionEntries:h,forms:y,formEditor:j,formSubmissions:k,viewsList:L,viewEditor:x,viewPreview:M,actionsList:D,actionEditor:G,proDocs:P,blocks:R,blockEditor:S,components:C,componentEditor:N,myProfile:T,roles:U,roleEditor:q,effects:z,search:A,notes:B,todo:F,analytics:H,seo:I,contacts:J,callNew:K,notifications:m,menus:O,contextMenus:Q,contextMenuEditor:W,menuEditor:X,menuLocations:Y,menusRedirect:eo,pluginGuides:u,pluginGuide:E,...l};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "domma-cms",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.105.1",
|
|
4
4
|
"description": "File-based CMS powered by Domma and Fastify. Run npx domma-cms my-site to create a new project.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server/server.js",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"@fastify/rate-limit": "^10.3.0",
|
|
85
85
|
"@fastify/static": "9.1.1",
|
|
86
86
|
"bcryptjs": "^3.0.3",
|
|
87
|
-
"domma-js": "0.51.
|
|
87
|
+
"domma-js": "0.51.3",
|
|
88
88
|
"dotenv": "^17.2.3",
|
|
89
89
|
"fastify": "5.8.5",
|
|
90
90
|
"gray-matter": "^4.0.3",
|
|
@@ -107,6 +107,13 @@ function mount(el) {
|
|
|
107
107
|
let cfg;
|
|
108
108
|
try { cfg = JSON.parse(el.getAttribute('data-dm-call') || '{}'); } catch { cfg = {}; }
|
|
109
109
|
if (!M || !cfg.api || !cfg.room) { el.textContent = 'This call page could not start.'; return; }
|
|
110
|
+
// The link's credentials (?t= / ?h= / ?key=) are in the config now: take them out of the address bar, so
|
|
111
|
+
// a script that reads location later, a screenshot or a copied address never carries them. This tab keeps
|
|
112
|
+
// them (sessionStorage), so a reload of the bare address can come back (core's /_call "not valid" page does).
|
|
113
|
+
if (location.search) {
|
|
114
|
+
try { sessionStorage.setItem(`dm-call:${location.pathname}`, location.search); } catch { /* storage blocked */ }
|
|
115
|
+
try { history.replaceState(history.state, '', location.pathname + location.hash); } catch { /* never mind */ }
|
|
116
|
+
}
|
|
110
117
|
|
|
111
118
|
// ---- State the screen shows -------------------------------------------------------
|
|
112
119
|
const stage = M.observable('check'); // check | waiting | in | ended | away | error
|
|
@@ -32,6 +32,10 @@ min, callOpenedAt + 2 h), so the customer's EXISTING link works at once - Bookin
|
|
|
32
32
|
not-online | cancelled), `callLinks(b, request?)` (absolute join/host URLs from a RAW booking), `adminBooking`,
|
|
33
33
|
`ownContact(id, userId)`, `siteBase(request?)`; `book()` takes `contactId` + `userId` (checked with `ownContact`) and
|
|
34
34
|
`request` (for the email links). An older Pro registers no actions; an older Booking ignores them.
|
|
35
|
+
Instant calls ("Call Sam now…", not a booking) were Booking Pro 0.3's and MOVED INTO THE CMS in Booking Pro 0.4.0
|
|
36
|
+
(core `services/instantCalls.js`, admin `#/calls/new`): Pro's banner action `call-now` now only sends the browser to
|
|
37
|
+
`#/calls/new[?contact=|?email=&name=]`, so the deep link `#/plugins/booking/call-now?...` above ends there too. Nothing in
|
|
38
|
+
Booking changed for it; `ownContact`/`siteBase` stay for Pro's booking-panel Call now.
|
|
35
39
|
|
|
36
40
|
1.3.0 (2026-09-30): online bookings (plan: `dcms-marketplace/docs/online-meetings-plan.md`). An extension's
|
|
37
41
|
`rules()` may say `online` (+ `recordingAllowed`); `placeBooking` then stores `online: true`, `hostKey`,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "booking",
|
|
3
3
|
"displayName": "Booking",
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.1",
|
|
5
5
|
"tier": "free",
|
|
6
6
|
"description": "Let people book appointments with you or your team, and rooms or equipment, from a booking page on your site. Opening hours, days off and buffers; confirmation and reminder emails with a link to change or cancel; a diary in the admin. Book straight from a contact or an email.",
|
|
7
7
|
"author": "Domma CMS",
|
|
@@ -35,6 +35,9 @@ export function matchBookingPath(pathname, base) {
|
|
|
35
35
|
return null;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/** On a bare call address: return to the link this tab kept (the call screen moves it out of the address bar). */
|
|
39
|
+
const RECOVER_CALL = '<script>(function(){try{if(location.search)return;var s=sessionStorage.getItem("dm-call:"+location.pathname);if(s&&s.charAt(0)==="?")location.replace(location.pathname+s);}catch(e){}})();</script>';
|
|
40
|
+
|
|
38
41
|
export const esc = (s) => String(s ?? '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
39
42
|
|
|
40
43
|
/** Plain text with blank lines between paragraphs, as HTML. */
|
|
@@ -131,7 +134,9 @@ export default async function bookingPublic(fastify) {
|
|
|
131
134
|
const head = `<header class="bk-hero"><p class="bk-crumbs"><a href="${esc(s.bookingPath)}">${esc(s.pageTitle || 'Book')}</a></p>`;
|
|
132
135
|
if (!who) {
|
|
133
136
|
return send(reply, request, s, {page: 'call', title: `Call - ${s.pageTitle || 'Book'}`, urlPath: `${s.bookingPath}/call/${room}`, noindex: true,
|
|
134
|
-
body: `${head}<h1>This call link is not valid</h1></header><p class="bk-note">The booking may have been cancelled or moved. Check the latest email we sent you.</p>`
|
|
137
|
+
body: `${head}<h1>This call link is not valid</h1></header><p class="bk-note">The booking may have been cancelled or moved. Check the latest email we sent you.</p>`
|
|
138
|
+
// A reload of the bare address (the call screen took the link out of the address bar): back to it.
|
|
139
|
+
+ (cred.t || cred.h ? '' : RECOVER_CALL)});
|
|
135
140
|
}
|
|
136
141
|
const {closesAt} = callWindow(b);
|
|
137
142
|
const cfg = {api: '/api/plugins/booking/call', room, cred, title: `${b.serviceName}${who.host ? `: ${b.name}` : b.bookableName ? ` with ${b.bookableName}` : ''}`,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"note": "Written by `make sync-free-tier` in dcms-marketplace. Do not edit these plugins here - edit them there and sync.",
|
|
3
|
-
"source": "dcms-marketplace@
|
|
3
|
+
"source": "dcms-marketplace@25153e0",
|
|
4
4
|
"plugins": {
|
|
5
5
|
"blog": {
|
|
6
6
|
"version": "1.9.4",
|
|
@@ -73,9 +73,9 @@
|
|
|
73
73
|
}
|
|
74
74
|
},
|
|
75
75
|
"booking": {
|
|
76
|
-
"version": "1.4.
|
|
76
|
+
"version": "1.4.1",
|
|
77
77
|
"files": {
|
|
78
|
-
"CLAUDE.md": "
|
|
78
|
+
"CLAUDE.md": "c4401810011ed98fe488e89e03d32ee601025818d1f354ec07fcd9f9add1115a",
|
|
79
79
|
"admin/css/index.css": "4f7731921c0c5038cb1566a6ab2d43ffc35bf9b161e58f87a0cff423e81b47fa",
|
|
80
80
|
"admin/lib/details.js": "8b37536bfa9ccfcc5265cc957ba8266d3f5f777133e7c0946ae49540f1c80e3c",
|
|
81
81
|
"admin/lib/diary-layout.js": "d818c79001fb9a9ecc15c96239ba961b0a1d98b9e8fce903eefb57d85adf2a73",
|
|
@@ -100,8 +100,8 @@
|
|
|
100
100
|
"lib/mail.js": "cad8f409df62d6e42f0f4024ec364bbe018a62b842fa3f7c38786fd4e7f92408",
|
|
101
101
|
"lib/store.js": "270161f907c68de39d32758931e86c290582524af4650e722c2c3be209acc875",
|
|
102
102
|
"plugin.js": "963e8190dbcaec71915d73402648899bb38135b25ec869a659d0938d03868682",
|
|
103
|
-
"plugin.json": "
|
|
104
|
-
"plugin.public.js": "
|
|
103
|
+
"plugin.json": "704402863b0aec563046b347d4341680e2dc9739db967bec0a933c2bf0e4e5c9",
|
|
104
|
+
"plugin.public.js": "5f2dc4f01202b16ae804364a33c547509d1fa4026eadebac440730234aa266b5",
|
|
105
105
|
"public/booking.css": "8b88989fdd04e50efc1688a2f32fe2e422299d7d13b81bb8fce0cc162fa99df6",
|
|
106
106
|
"public/booking.js": "7dfc3d205696967013886567f67a867443c2e370bac29019d69911d695fdbade",
|
|
107
107
|
"public/lib/clean.js": "d591be9ea8462705b7bc8806670027fdd4d6d4764777da08e91a010d27ec1651",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var v=Object.defineProperty;var w=(f,e,i)=>e in f?v(f,e,{enumerable:!0,configurable:!0,writable:!0,value:i}):f[e]=i;var p=(f,e,i)=>w(f,typeof e!="symbol"?e+"":e,i);import{CanvasManager as M}from"./core/canvas.js";import{PhysicsEngine as P,updateMovingParticle as b,updateParticlePhysics as C}from"./core/physics.js";import{createParticle as z}from"./core/particles.js";const h=class h{static isDateInRange(e,i,s,o){const r=new Date,n=r.getMonth()+1,c=r.getDate();return s<e?n===e&&c>=i||n>e||n<e&&n<=s||n===s&&c<=o:(n>e||n===e&&c>=i)&&(n<s||n===s&&c<=o)}static getCurrentTheme(){for(const[e,i]of Object.entries(h.themes)){const[[s,o],[r,n]]=i.dates;if(h.isDateInRange(s,o,r,n))return e}return null}static isCelebrationSeason(){return h.getCurrentTheme()!==null}static getThemes(){return h.themes}constructor(e={}){this.options={theme:e.theme||"auto",intensity:e.intensity||"medium",enabled:e.enabled!==void 0?e.enabled:!0,zIndex:e.zIndex||999,overrides:e.overrides||{}},this.canvasManager=null,this.physicsEngine=null,this.themeModule=null,this.currentTheme=null,this.particles=[],this.specialParticles=[],this.animationFrame=null,this.lastTime=0,this.running=!1,this.initialized=!1}async init(){if(this.initialized){console.warn("[Celebrations] Already initialized");return}console.log("[Celebrations] Initializing...");let e=this.options.theme;if(e==="auto"&&(e=h.getCurrentTheme(),!e)){console.log("[Celebrations] No active celebration detected");return}if(!h.themes[e]){console.error(`[Celebrations] Unknown theme: ${e}`);return}this.currentTheme=e,console.log(`[Celebrations] Loading theme: ${e}`);try{const s=await import(h.themes[e].module);this.themeModule=s.default,console.log(`[Celebrations] Theme loaded: ${this.themeModule.displayName}`)}catch(i){console.error(`[Celebrations] Failed to load theme module: ${i.message}`);return}this.canvasManager=new M({canvasId:"celebrations-canvas",zIndex:this.options.zIndex}),this.canvasManager.create(),this.physicsEngine=new P({gustInterval:[5e3,15e3],gustStrength:[-2,2]}),window.addEventListener("resize",()=>{this.canvasManager.resize(),this.resetParticles()}),this.resetParticles(),this.initialized=!0,console.log("[Celebrations] Initialized successfully"),this.options.enabled&&this.start()}resetParticles(){const e=Object.assign({},this.themeModule.intensityConfig[this.options.intensity]),i=a=>String(a).toLowerCase().replace(/[^a-z0-9]/g,""),s=[...Object.keys(e),...this.themeModule.decorationKeys||[]],o=a=>s.find(l=>i(l)===i(a))||a,r=a=>["true","1","yes","on"].includes(String(a).toLowerCase()),n=this.themeModule.optInDecorations||{},c=Object.entries(this.options.overrides).some(([a,l])=>["snowonly","fallingonly"].includes(i(a))&&r(l));for(const a of Object.values(n))e[a.chance]=0;for(const[a,l]of Object.entries(this.options.overrides)){if((i(a)==="snowonly"||i(a)==="fallingonly")&&r(l)){for(const g of this.themeModule.decorationKeys||[])e[g]=0;continue}const y=Object.keys(n).find(g=>i(g)===i(a));if(y&&!c&&r(l)){const g=n[y];e[g.chance]=g.value}l==="off"&&(e[o(a)]=0)}this.config=e;const t=this.canvasManager.canvas.width,d=this.canvasManager.canvas.height;this.particles=[],this.specialParticles=[];const m=e.initialParticleRatio!==void 0?e.initialParticleRatio:1,u=Math.floor(e.count*m);if(this.themeModule.createFallingParticle)for(let a=0;a<u;a++){const l=this.themeModule.createFallingParticle(t,d,e);l.y=Math.random()*d,this.particles.push(l)}else for(let a=0;a<u;a++){const l=z(e,t,d);l.y=Math.random()*d,this.particles.push(l)}if(this.themeModule.createInitialDecorations){const a=this.themeModule.createInitialDecorations(t,d,e);this.specialParticles.push(...a),console.log(`[Celebrations] Created ${a.length} decorations`)}console.log(`[Celebrations] Created ${u}/${e.count} particles (${this.options.intensity}, ${Math.round(m*100)}% initial)`)}start(){if(!this.initialized){console.warn("[Celebrations] Cannot start - not initialized");return}if(this.running){console.warn("[Celebrations] Already running");return}console.log("[Celebrations] Starting animation"),this.running=!0,this.lastTime=performance.now(),this.animate()}pause(){this.running&&(console.log("[Celebrations] Pausing animation"),this.running=!1,this.animationFrame&&(cancelAnimationFrame(this.animationFrame),this.animationFrame=null))}resume(){this.running||this.start()}destroy(){console.log("[Celebrations] Destroying effect"),this.pause(),this.canvasManager&&this.canvasManager.canvas&&this.canvasManager.canvas.remove(),this.particles=[],this.specialParticles=[],this.canvasManager=null,this.physicsEngine=null,this.themeModule=null,this.initialized=!1}setIntensity(e){if(!["light","medium","heavy"].includes(e)){console.error(`[Celebrations] Invalid intensity: ${e}`);return}console.log(`[Celebrations] Changing intensity to: ${e}`),this.options.intensity=e,this.resetParticles()}async setTheme(e){console.log(`[Celebrations] Changing theme to: ${e}`);const i=this.running;this.pause(),await new Promise(s=>setTimeout(s,20)),this.initialized=!1,this.canvasManager&&this.canvasManager.clear(),this.particles=[],this.specialParticles=[],this.canvasManager&&this.canvasManager.clear(),this.options.theme=e,await this.init(),i&&this.start()}animate(){if(!this.running)return;const e=performance.now(),i=e-this.lastTime;this.lastTime=e,this.canvasManager.clear();const s=this.canvasManager.ctx,o=this.canvasManager.canvas.width,r=this.canvasManager.canvas.height,n=this.config;this.physicsEngine.updateWind(e);const c=this.physicsEngine.getWindForce();if(this.particles.length<n.count&&Math.random()<.05&&this.themeModule.createFallingParticle){const t=this.themeModule.createFallingParticle(o,r,n);(t.y===void 0||t.y<0)&&(t.y=-20),this.particles.push(t)}if(this.particles.forEach((t,d)=>{if(C(t,i,c),t.y>r+50&&(t.y=-20,t.x=Math.random()*o),t.x<-50&&(t.x=o+50),t.x>o+50&&(t.x=-50),this.themeModule.drawSnowflake&&t.type==="snowflake")this.themeModule.drawSnowflake(s,t);else if(this.themeModule["draw"+this.capitalizeFirst(t.type)]){const m="draw"+this.capitalizeFirst(t.type);this.themeModule[m](s,t,e)}else s.save(),s.globalAlpha=t.opacity,s.fillStyle=this.themeModule.colors?.primary||"#ffffff",s.beginPath(),s.arc(t.x,t.y,t.size,0,Math.PI*2),s.fill(),s.restore()}),this.themeModule.updateSpecialParticles&&this.themeModule.updateSpecialParticles(this.specialParticles,i,o,r),this.specialParticles=this.specialParticles.filter(t=>{if(!t.active)return!1;if(!t.static){b(t,i,e);const u=t.type==="train"?600:200;if(t.x<-u||t.x>o+u)return t.active=!1,!1}if(!isFinite(t.x)||!isFinite(t.y)||t.size&&!isFinite(t.size))return console.warn("[Celebrations] Invalid particle values, removing:",t.type),t.active=!1,!1;const m="draw"+this.capitalizeFirst(t.type);return this.themeModule[m]&&this.themeModule[m](s,t,e),!0}),this.themeModule.spawnSpecialParticle){const t=this.themeModule.spawnSpecialParticle(this.specialParticles,o,r,n);t&&this.specialParticles.push(t)}this.themeModule.drawGlobalEffects&&this.themeModule.drawGlobalEffects(s,e,o,r),this.animationFrame=requestAnimationFrame(()=>this.animate())}capitalizeFirst(e){if(!e)return"";const i=e.replace(/-([a-z])/g,(s,o)=>o.toUpperCase());return i.charAt(0).toUpperCase()+i.slice(1)}};p(h,"themes",{christmas:{module:"./themes/christmas.js?v=20260930-festive",dates:[[12,1],[1,1]],displayName:"Christmas",emoji:"\u{1F384}"},halloween:{module:"./themes/halloween.js?v=20260930-snow",dates:[[10,26],[10,31]],displayName:"Halloween",emoji:"\u{1F383}"},valentines:{module:"./themes/valentines.js?v=20260930-snow",dates:[[2,9],[2,14]],displayName:"Valentine's Day",emoji:"\u{1F495}"},"guy-fawkes":{module:"./themes/guy-fawkes.js?v=20260930-snow",dates:[[11,1],[11,5]],displayName:"Guy Fawkes Night",emoji:"\u{1F386}"},"st-patricks":{module:"./themes/st-patricks.js?v=20260930-snow",dates:[[3,12],[3,17]],displayName:"St Patrick's Day",emoji:"\u2618\uFE0F"},"st-andrews":{module:"./themes/st-andrews.js?v=20260930-snow",dates:[[11,25],[11,30]],displayName:"St Andrew's Day",emoji:"\u{1F3F4}\u{E0067}\u{E0062}\u{E0073}\u{E0063}\u{E0074}\u{E007F}"},"st-davids":{module:"./themes/st-davids.js?v=20260930-snow",dates:[[2,24],[3,1]],displayName:"St David's Day",emoji:"\u{1F3F4}\u{E0067}\u{E0062}\u{E0077}\u{E006C}\u{E0073}\u{E007F}"},"st-georges":{module:"./themes/st-georges.js?v=20260930-snow",dates:[[4,18],[4,23]],displayName:"St George's Day",emoji:"\u{1F3F4}\u{E0067}\u{E0062}\u{E0065}\u{E006E}\u{E0067}\u{E007F}"}});export let CelebrationsEffect=h;export default CelebrationsEffect;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function spawnElfAntic(t,o,r,s={}){for(const[n,g]of[["peeing-elf","peeingElfChance"],["thieving-elf","thievingElfChance"]]){if(!(s[g]>0)||t.some(a=>a.active&&a.type===n)||Math.random()>=s[g])continue;const c=t.filter(a=>a.type==="tree"&&a.active&&a.gifts?.some(x=>!x.stolen&&!x.reserved));if(n==="thieving-elf"&&!c.length)continue;const e=c[Math.floor(Math.random()*c.length)],v=Math.random()<.5,h=13+Math.random()*3,i={type:n,x:v?-40:o+40,y:r-h*1.02-8,size:h,vx:v?1:-1,vy:0,opacity:.95,rotation:0,time:0,state:"approaching",stateTime:0,active:!0,static:!0,targetX:e?e.x+e.size*.7:o*(v?.72:.28),targetTree:e||null};return n==="thieving-elf"&&(i.gift=e.gifts.find(a=>!a.stolen&&!a.reserved),i.gift.reserved=!0,i.targetX=e.x+(i.gift.x+i.gift.width/2)*e.size,i.y=e.y+(i.gift.y+i.gift.height)*e.size-h*1.02),i}return null}export function updateElfAntic(t,o,r){const s=Math.max(0,Math.min(o,50));if(t.type==="thieving-elf"&&!t.targetTree?.active){t.gift&&(t.gift.reserved=!1),t.active=!1;return}if(t.stateTime+=s,t.state==="approaching"){const n=t.targetX-t.x,g=(t.type==="thieving-elf"?145:75)*s/1e3;t.vx=n>=0?1:-1,Math.abs(n)<=g?(t.x=t.targetX,t.state=t.type==="thieving-elf"?"grabbing":"peeing",t.stateTime=0):t.x+=Math.sign(n)*g}else t.state==="grabbing"&&t.stateTime>=450?(t.gift.stolen=!0,t.gift.reserved=!1,t.state="escaping",t.stateTime=0,t.vx=t.x<r/2?-1:1):t.state==="peeing"&&t.stateTime>=4200?(t.state="escaping",t.stateTime=0,t.vx=t.x<r/2?-1:1):t.state==="escaping"&&(t.x+=t.vx*(t.type==="thieving-elf"?230:90)*s/1e3,(t.x<-60||t.x>r+60)&&(t.active=!1))}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function path(e,o,n,i,l=1){e.beginPath();for(const[t,...r]of o)e[t](...r);n&&(e.fillStyle=n,e.fill()),i&&(e.strokeStyle=i,e.lineWidth=l,e.stroke())}export function ellipse(e,o,n,i,l,t){e.beginPath(),e.ellipse(o,n,i,l,0,0,Math.PI*2),e.fillStyle=t,e.fill()}export function gradient(e,o,n,i){const l=e.createLinearGradient(0,o,0,n);return i.forEach((t,r)=>l.addColorStop(r/(i.length-1),t)),l}export function rect(e,o,n,i,l,t,r,f,a=.6){e.beginPath(),e.roundRect(o,n,i,l,t),e.fillStyle=r,e.fill(),f&&(e.strokeStyle=f,e.lineWidth=a,e.stroke())}
|