domma-cms 1.6.60 → 1.7.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/CHANGELOG.md +4349 -0
- package/README.md +1 -1
- package/admin/dist/domma/domma-tools.css +3 -3
- package/admin/dist/domma/domma-tools.min.js +3 -3
- package/admin/js/lib/effect-defs.js +1 -1
- package/admin/js/lib/effects-builder.js +2 -2
- package/admin/js/lib/mail-compose.js +1 -0
- package/admin/js/lib/page-editor-reference.js +5 -5
- package/admin/js/lib/shortcode-tags.js +1 -1
- package/admin/js/templates/call-new.html +1 -1
- package/admin/js/templates/docs/usage-shortcodes.html +39 -0
- package/admin/js/views/call-new.js +1 -1
- package/admin/js/views/contacts.js +15 -15
- package/admin/js/views/form-submissions.js +2 -2
- package/docs/DommaFeatures.md +3005 -0
- package/docs/MultiTenant.md +176 -0
- package/docs/api-reference.md +1667 -0
- package/docs/cache.md +96 -0
- package/docs/configuration.md +1811 -0
- package/docs/editions-and-licences.md +190 -0
- package/docs/getting-started.md +201 -0
- package/docs/guides/form-triggers.md +260 -0
- package/docs/guides/mongodb-storage-adapter.md +282 -0
- package/docs/guides/multi-page-forms.md +121 -0
- package/docs/images/scribe-composer.png +0 -0
- package/docs/markdown-shortcodes.md +1738 -0
- package/docs/pentest-report-2026-04-03.md +181 -0
- package/docs/plans/2026-02-28-admin-topbar-login-forms.md +349 -0
- package/docs/plans/2026-02-28-domma-ui-polish.md +286 -0
- package/docs/plans/2026-02-28-reset-seed-fresh-commands.md +520 -0
- package/docs/plans/2026-03-02-cookie-consent-design.md +86 -0
- package/docs/plans/2026-03-02-cookie-consent-plugin.md +453 -0
- package/docs/plans/2026-03-02-tooltip-sweep-and-back-to-top-plugin-design.md +126 -0
- package/docs/plans/2026-03-02-tooltip-sweep-back-to-top.md +427 -0
- package/docs/plans/2026-03-05-cookie-consent-btt-core.md +115 -0
- package/docs/plans/2026-03-05-custom-css-migrate-to-core-design.md +35 -0
- package/docs/plans/2026-03-05-custom-css-migrate-to-core.md +352 -0
- package/docs/plans/2026-03-05-scribe-script-shortcode.md +373 -0
- package/docs/plans/2026-03-05-user-types-design.md +139 -0
- package/docs/plans/2026-03-05-user-types-implementation.md +1035 -0
- package/docs/plans/2026-03-09-page-editor-view-cta-inserts-design.md +78 -0
- package/docs/plans/2026-03-09-page-editor-view-cta-inserts.md +388 -0
- package/docs/plans/2026-03-09-pro-documentation-design.md +80 -0
- package/docs/plans/2026-03-09-pro-documentation.md +1017 -0
- package/docs/plans/2026-06-10-core-project-design.md +240 -0
- package/docs/plugin-development.md +827 -0
- package/docs/recruitment-recipe.md +501 -0
- package/docs/scaffolding.md +312 -0
- package/docs/screenshots/calculator-floating.png +0 -0
- package/docs/screenshots/calculator-standard.png +0 -0
- package/docs/screenshots/email-settings.png +0 -0
- package/docs/screenshots/messages-count.png +0 -0
- package/docs/structure/user-types.md +107 -0
- package/docs/superpowers/plans/2026-05-25-admin-sidebar-via-menus.md +1112 -0
- package/docs/superpowers/plans/2026-06-08-navbar-mobile-drawer-and-menu-enrichment.md +1161 -0
- package/docs/superpowers/plans/2026-06-25-domma-docs-project.md +695 -0
- package/docs/superpowers/plans/2026-06-26-surveys-HANDOFF.md +68 -0
- package/docs/superpowers/plans/2026-06-26-surveys-pro-plugin.md +1902 -0
- package/docs/superpowers/plans/2026-08-19-collection-context-sort-export.md +1577 -0
- package/docs/superpowers/plans/2026-10-09-content-api.md +1192 -0
- package/docs/superpowers/specs/2026-03-12-form-followup-actions-design.md +152 -0
- package/docs/superpowers/specs/2026-03-12-page-editor-form-insert-and-per-page-theme-design.md +115 -0
- package/docs/superpowers/specs/2026-03-17-nav-hidden-items-design.md +85 -0
- package/docs/superpowers/specs/2026-03-19-accordion-insert-design.md +175 -0
- package/docs/superpowers/specs/2026-03-25-static-block-shortcode-design.md +153 -0
- package/docs/superpowers/specs/2026-03-25-text-shortcode-design.md +168 -0
- package/docs/superpowers/specs/2026-04-05-card-builder-design.md +315 -0
- package/docs/superpowers/specs/2026-04-06-insert-builders-design.md +278 -0
- package/docs/superpowers/specs/2026-04-06-layouts-design.md +226 -0
- package/docs/superpowers/specs/2026-04-07-blog-plugin-design.md +300 -0
- package/docs/superpowers/specs/2026-04-07-calendar-plugin-design.md +538 -0
- package/docs/superpowers/specs/2026-04-17-components-area-design.md +368 -0
- package/docs/superpowers/specs/2026-05-02-effects-slideover-overhaul-design.md +193 -0
- package/docs/superpowers/specs/2026-05-06-dashboard-rebuild-design.md +257 -0
- package/docs/superpowers/specs/2026-05-24-menus-design.md +466 -0
- package/docs/superpowers/specs/2026-05-25-admin-sidebar-via-menus-design.md +358 -0
- package/docs/superpowers/specs/2026-05-25-projects-design.md +380 -0
- package/docs/superpowers/specs/2026-06-08-navbar-mobile-drawer-and-menu-enrichment-design.md +292 -0
- package/docs/superpowers/specs/2026-06-08-sidebar-tools-plugins-folders-design.md +59 -0
- package/docs/superpowers/specs/2026-06-25-domma-docs-project-design.md +192 -0
- package/docs/superpowers/specs/2026-06-26-domma-charts-upstream-proposal.md +44 -0
- package/docs/superpowers/specs/2026-06-26-manager-scheduler-upstream-proposal.md +46 -0
- package/docs/superpowers/specs/2026-06-26-surveys-pro-plugin-design.md +206 -0
- package/docs/superpowers/specs/2026-07-02-shortcode-context-menu-design.md +96 -0
- package/docs/superpowers/specs/2026-08-19-collection-context-sort-export-design.md +330 -0
- package/docs/superpowers/specs/2026-09-22-dcms-marketplace-design.md +1150 -0
- package/docs/superpowers/specs/2026-10-09-content-api-design.md +325 -0
- package/docs/superpowers/specs/plans/2026-03-12-form-followup-actions.md +624 -0
- package/docs/superpowers/specs/plans/2026-03-12-page-editor-form-insert-and-per-page-theme.md +125 -0
- package/docs/superpowers/specs/plans/2026-03-17-nav-hidden-items.md +578 -0
- package/docs/superpowers/specs/plans/2026-03-19-accordion-insert.md +635 -0
- package/docs/superpowers/specs/plans/2026-03-25-text-shortcode.md +545 -0
- package/docs/superpowers/specs/plans/2026-04-05-card-builder.md +1970 -0
- package/docs/superpowers/specs/plans/2026-04-06-insert-builders.md +1049 -0
- package/docs/superpowers/specs/plans/2026-04-06-layouts.md +1015 -0
- package/docs/superpowers/specs/plans/2026-04-07-calendar-plugin.md +1526 -0
- package/docs/superpowers/specs/plans/2026-04-08-plugin-monetisation-split.md +242 -0
- package/docs/superpowers/specs/plans/2026-04-17-components-area.md +2038 -0
- package/docs/superpowers/specs/plans/2026-05-02-effects-slideover-overhaul.md +2335 -0
- package/docs/superpowers/specs/plans/2026-05-06-dashboard-rebuild.md +1999 -0
- package/docs/superpowers/specs/plans/2026-05-17-cache-system.md +1436 -0
- package/docs/superpowers/specs/plans/2026-05-24-menus.md +3096 -0
- package/docs/superpowers/specs/plans/2026-05-25-projects.md +2869 -0
- package/docs/superpowers/specs/plans/2026-09-22-marketplace-step2-consolidation.md +505 -0
- package/docs/superpowers/specs/plans/2026-09-22-marketplace-step3-catalogue-and-key.md +154 -0
- package/docs/superpowers/specs/plans/2026-09-22-tools-promotion.md +260 -0
- package/docs/theming.md +320 -0
- package/package.json +2 -2
- package/plugins/_lib/admin/mail/compose-window.js +3 -1
- package/plugins/_lib/admin/mail/identity.js +18 -3
- package/plugins/_lib/admin/mail/reader-view.js +32 -3
- package/plugins/blog/CLAUDE.md +4 -0
- package/plugins/blog/admin/lib/email.js +191 -0
- package/plugins/blog/admin/views/comments.js +3 -1
- package/plugins/blog/plugin.json +2 -2
- package/plugins/blog/plugin.public.js +1 -1
- package/plugins/blog/tests/email.test.js +69 -0
- package/plugins/booking/CLAUDE.md +4 -0
- package/plugins/booking/admin/lib/email.js +191 -0
- package/plugins/booking/admin/views/panels.js +7 -3
- package/plugins/booking/plugin.json +1 -1
- package/plugins/booking/tests/email.test.js +69 -0
- package/plugins/free-tier.lock.json +27 -19
- package/plugins/shopping-cart/CLAUDE.md +4 -0
- package/plugins/shopping-cart/admin/lib/email.js +191 -0
- package/plugins/shopping-cart/admin/views/orders.js +11 -3
- package/plugins/shopping-cart/plugin.json +2 -2
- package/plugins/shopping-cart/tests/email.test.js +69 -0
- package/plugins/support/CLAUDE.md +4 -0
- package/plugins/support/admin/lib/email.js +191 -0
- package/plugins/support/admin/views/desk.js +4 -1
- package/plugins/support/admin/views/inbox.js +6 -2
- package/plugins/support/plugin.json +2 -2
- package/plugins/support/tests/email.test.js +69 -0
- package/public/js/celebrations/core/canvas.js +1 -1
- package/public/js/celebrations/index.js +1 -1
- package/public/js/celebrations/licenses/ABeeZee-Regular-OFL.txt +93 -0
- package/public/js/celebrations/licenses/Allura-Regular-OFL.txt +93 -0
- package/public/js/celebrations/message-attrs.mjs +1 -0
- package/public/js/celebrations/themes/anniversary.js +1 -0
- package/public/js/celebrations/themes/autumn.js +1 -0
- package/public/js/celebrations/themes/birthday.js +1 -0
- package/public/js/celebrations/themes/butterfly-flight.js +1 -0
- package/public/js/celebrations/themes/calendar.js +1 -0
- package/public/js/celebrations/themes/chinese-new-year-dates.js +1 -0
- package/public/js/celebrations/themes/chinese-new-year.js +1 -0
- package/public/js/celebrations/themes/christmas.js +1 -1
- package/public/js/celebrations/themes/easter.js +1 -0
- package/public/js/celebrations/themes/garden-drawing.js +1 -0
- package/public/js/celebrations/themes/garden.js +1 -0
- package/public/js/celebrations/themes/graduation.js +1 -0
- package/public/js/celebrations/themes/greeting.js +1 -0
- package/public/js/celebrations/themes/make-it-rain.js +1 -0
- package/public/js/celebrations/themes/money-rain.js +1 -0
- package/public/js/celebrations/themes/new-year.js +1 -0
- package/public/js/celebrations/themes/occasion-drawing.js +1 -0
- package/public/js/celebrations/themes/occasions.js +1 -0
- package/public/js/celebrations/themes/rain-control.js +1 -0
- package/public/js/celebrations/themes/sparkler-font-data.js +1 -0
- package/public/js/celebrations/themes/sparkler-font.js +1 -0
- package/public/js/celebrations/themes/sparkler-writing.js +1 -0
- package/public/js/celebrations/themes/summer-garden.js +1 -0
- package/public/js/celebrations/themes/winter.js +1 -0
- package/public/js/celebrations/trait-data.mjs +1 -1
- package/public/js/effects.js +1 -1
- package/scripts/build.js +4 -0
- package/server/services/markdown.js +5 -2
- package/server/templates/page.html +1 -1
package/README.md
CHANGED
|
@@ -281,7 +281,7 @@ These features are part of the CMS core - no plugins required. They are configur
|
|
|
281
281
|
|--------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
282
282
|
| **Search** | Full-text search over the site's pages - navbar icon or floating button, Cmd+K shortcut, weighted results (`config/search.json`, admin: **System > Search**) |
|
|
283
283
|
| **Effects** | Scroll-reveal animations on page elements (`[data-fx]`) and row shortcodes (`[data-reveal]`) via IntersectionObserver |
|
|
284
|
-
| **Celebrations** | Seasonal particle effects that activate automatically by date - Christmas, Halloween, Valentine's, Guy Fawkes, St Patrick's, St Andrew's, St David's, St George's |
|
|
284
|
+
| **Celebrations** | Seasonal particle effects that activate automatically by date - Christmas, Halloween, Valentine's, Guy Fawkes, St Patrick's, St Andrew's, St David's, St George's, New Year, Chinese New Year, Easter, Summer Garden, Winter, Autumn; weather rain, Money Rain, Graduation, Birthday, Anniversary and custom sparkler messages |
|
|
285
285
|
| **Back to Top** | Configurable scroll-to-top button with position, offset, and label options |
|
|
286
286
|
| **Cookie Consent** | GDPR cookie consent banner with per-category toggles (necessary, functional, analytics, marketing) |
|
|
287
287
|
| **Auto Day/Night** | Automatic theme switching between light and dark variants based on time of day |
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Domma Tools CSS v0.52.
|
|
2
|
+
* Domma Tools CSS v0.52.4
|
|
3
3
|
* Dynamic Object Manipulation & Modeling API
|
|
4
4
|
* (c) 2026 Darryl Waterhouse & DCBW-IT
|
|
5
|
-
* Built: 2026-10-
|
|
6
|
-
* Commit:
|
|
5
|
+
* Built: 2026-10-15T17:20:44.118Z
|
|
6
|
+
* Commit: a5dee96
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
/*!
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Domma Tools v0.52.
|
|
2
|
+
* Domma Tools v0.52.4
|
|
3
3
|
* Developer tools: Theme Roller & Page Roller
|
|
4
4
|
* (c) 2026 Darryl Waterhouse & DCBW-IT
|
|
5
|
-
* Built: 2026-10-
|
|
6
|
-
* Commit:
|
|
5
|
+
* Built: 2026-10-15T17:20:31.284Z
|
|
6
|
+
* Commit: a5dee96
|
|
7
7
|
*
|
|
8
8
|
* Requires: domma.min.js
|
|
9
9
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SCRIBE_FIELDS as p}from"./scribe-model.js";import{getCharacters as m}from"../../../public/js/celebrations/character-catalog.mjs";import{CELEBRATION_THEMES as f,traitAttribute as u}from"../../../public/js/celebrations/trait-data.mjs";import{SHORTCODE_EXPIRY_FIELDS as s}from"../../../public/js/shortcode-expiry.mjs";export{f as CELEBRATION_THEMES};export const EFFECTS=[{name:"reveal",label:"Reveal",category:"Entrance",description:"Scroll-triggered entrance animation when the element enters the viewport.",attrs:[{name:"animation",label:"Animation",type:"select",default:"fade",options:[["fade","Fade"],["slide-up","Slide up"],["slide-down","Slide down"],["zoom","Zoom"],["flip","Flip"]]},{name:"duration",label:"Duration (ms)",type:"number",default:600},{name:"delay",label:"Delay (ms)",type:"number",default:0},{name:"threshold",label:"Threshold",type:"number",default:.1,step:.05,min:0,max:1},{name:"once",label:"Animate once",type:"checkbox",default:!0}],hosts:["card","box","hero","banner","col"],previewable:!0},{name:"breathe",label:"Breathe",category:"Animation",description:"Continuous gentle scaling - makes elements feel alive.",attrs:[{name:"amplitude",label:"Amplitude (px)",type:"number",default:8},{name:"duration",label:"Duration (ms)",type:"number",default:3e3}],hosts:["card","box","hero","col","button"],previewable:!0},{name:"pulse",label:"Pulse",category:"Animation",description:"A repeating scale pulse - stronger than breathe.",attrs:[{name:"scale",label:"Scale",type:"number",default:1.05,step:.01},{name:"duration",label:"Duration (ms)",type:"number",default:1e3}],hosts:["card","box","hero","banner","button"],previewable:!0},{name:"shake",label:"Shake",category:"Animation",description:"One-shot horizontal or vertical shake.",attrs:[{name:"intensity",label:"Intensity (px)",type:"number",default:5},{name:"duration",label:"Duration (ms)",type:"number",default:500},{name:"direction",label:"Direction",type:"select",default:"horizontal",options:[["horizontal","Horizontal"],["vertical","Vertical"]]}],hosts:["card","box","banner","button"],previewable:!0},{name:"animate",label:"CSS Animate",category:"Animation",description:"Applies a Domma CSS animation utility class - no JavaScript.",attrs:[{name:"type",label:"Type",type:"select",default:"fade-in-up",options:[["fade-in","fade-in"],["fade-in-up","fade-in-up"],["fade-in-down","fade-in-down"],["fade-in-left","fade-in-left"],["fade-in-right","fade-in-right"],["slide-in-up","slide-in-up"],["slide-in-down","slide-in-down"],["zoom-in","zoom-in"],["zoom-out","zoom-out"],["bounce","bounce"],["pulse","pulse"],["shake","shake"],["spin","spin"],["flip","flip"]]},{name:"duration",label:"Duration",type:"select",default:"normal",options:[["fast","fast"],["normal","normal"],["slow","slow"],["slower","slower"]]},{name:"delay",label:"Delay (ms)",type:"number",default:0},{name:"repeat",label:"Repeat",type:"select",default:"once",options:[["once","Once"],["infinite","Infinite"]]}],hosts:["card","box","hero","banner","col","button"],previewable:!0},{name:"scribe",preview:"scribe",label:"Scribe (typewriter)",category:"Text",description:"Types characters, words, or sentences with entrance effects and playback options. For action scripts use Insert \u2192 Scribe.",attrs:p,hosts:[],previewable:!0},{name:"scramble",label:"Scramble",category:"Text",description:"Reveals text through a character-scramble animation.",attrs:[{name:"speed",label:"Speed (ms/char)",type:"number",default:50},{name:"loop",label:"Loop",type:"checkbox",default:!1}],hosts:[],previewable:!0},{name:"counter",label:"Counter",category:"Text",description:"Animates a number from a starting value to a target. Self-closing shortcode.",attrs:[{name:"to",label:"To",type:"number",default:100},{name:"from",label:"From",type:"number",default:0},{name:"duration",label:"Duration (ms)",type:"number",default:2e3},{name:"prefix",label:"Prefix",type:"text",default:""},{name:"suffix",label:"Suffix",type:"text",default:""},{name:"decimals",label:"Decimal places",type:"number",default:0},{name:"separator",label:"Thousand separator",type:"text",default:""}],hosts:[],previewable:!0,selfClosing:!0},{name:"ripple",label:"Ripple",category:"Visual",description:"Click-triggered ripple effect on the element.",attrs:[{name:"colour",label:"Colour",type:"colour",default:"#7c6af7"},{name:"duration",label:"Duration (ms)",type:"number",default:600},{name:"opacity",label:"Opacity",type:"number",default:.3,step:.05,min:0,max:1}],hosts:["card","box","col","button"],previewable:!0},{name:"twinkle",label:"Twinkle",category:"Visual",description:"Generates animated particle effects inside the element.",attrs:[{name:"count",label:"Particle count",type:"number",default:80},{name:"shape",label:"Shape",type:"select",default:"star",options:[["star","Star"],["circle","Circle"],["square","Square"]]},{name:"colour",label:"Colour",type:"colour",default:"#ffffff"},{name:"min-size",label:"Min size (px)",type:"number",default:2},{name:"max-size",label:"Max size (px)",type:"number",default:6}],hosts:["card","box","hero"],previewable:!0},{name:"ambient",label:"Ambient background",category:"Visual",description:"Applies an animated CSS background class to the element.",attrs:[{name:"type",label:"Type",type:"select",default:"float-blobs",options:[["float-blobs","Float blobs"],["float-orbs","Float orbs"],["float-particles","Float particles"],["rotate-glow","Rotate glow"],["rotate-dual","Rotate dual"],["rotate-spotlight","Rotate spotlight"],["wave","Wave"],["ripple","Ripple"],["aurora","Aurora"]]},{name:"speed",label:"Speed",type:"select",default:"normal",options:[["slow","Slow"],["normal","Normal"],["fast","Fast"]]},{name:"intensity",label:"Intensity",type:"select",default:"subtle",options:[["subtle","Subtle"],["intense","Intense"]]}],hosts:["card","box","hero","banner"],previewable:!0},{name:"firework",label:"Firework",category:"Celebrations",description:"CSS-only firework particle. Self-closing or wrapping form.",attrs:[{name:"type",label:"Type",type:"select",default:"burst",options:[["burst","Burst"],["sparkle","Sparkle"],["trail","Trail"]]},{name:"colour",label:"Colour theme",type:"select",default:"rainbow",options:[["rainbow","Rainbow"],["primary","Primary"],["info","Info"],["success","Success"],["warning","Warning"],["danger","Danger"]]},{name:"size",label:"Size",type:"select",default:"md",options:[["sm","Small"],["md","Medium"],["lg","Large"]]},{name:"continuous",label:"Continuous",type:"checkbox",default:!1},{name:"hover",label:"On hover only",type:"checkbox",default:!1}],hosts:[],previewable:!0},{name:"fireworks",label:"Fireworks show",category:"Celebrations",description:"A container with multiple firework particles inside.",attrs:[],hosts:[],previewable:!1},{name:"celebrate",label:"Celebrate",category:"Celebrations",description:"A full-page seasonal celebration, or one inline character at any time of year.",attrs:[{name:"theme",label:"Theme",type:"select",default:"auto",options:[["auto","Auto (date-detected)"],["christmas","Christmas"],["halloween","Halloween"],["valentines","Valentines"],["guy-fawkes","Guy Fawkes"],["st-patricks","St Patrick's"],["st-davids","St David's"],["st-georges","St George's"],["st-andrews","St Andrew's"]]},{name:"intensity",label:"Intensity",type:"select",default:"medium",options:[["light","Light"],["medium","Medium"],["heavy","Heavy"]]},{name:"snow-only",label:"Snow only (Christmas)",type:"checkbox",default:!1,hint:"Just the falling snow - every Christmas decoration off."},{name:"trees",label:"Christmas: trees",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"wreaths",label:"Christmas: wreaths",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"northstars",label:"Christmas: North Star",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"snowmen",label:"Christmas: snowmen",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"sleigh",label:"Christmas: Santa's sleigh",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"elf",label:"Christmas: elf",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"train",label:"Christmas: train",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"firework",label:"Christmas: fireworks",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"robin",label:"Christmas: robin",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"peeing-elf",label:"Peeing elf (opt-in)",type:"checkbox",default:!1,hint:"A clothed rear-view gag. Off by default; Snow only keeps it off."},{name:"thieving-elf",label:"Thieving elf (opt-in)",type:"checkbox",default:!1,hint:"Runs to a tree, takes a present and escapes. Needs a visible tree."}],hosts:[],previewable:!1,selfClosing:!0},{name:"christmas",label:"Christmas",category:"Celebrations",description:"Christmas on the published site: snow, trees, wreaths, snowmen, Santa's sleigh and more. Snow only for just the snow.",attrs:[{name:"intensity",label:"Intensity",type:"select",default:"medium",options:[["light","Light"],["medium","Medium"],["heavy","Heavy"]]},{name:"snow-only",label:"Snow only",type:"checkbox",default:!1,hint:"Just the falling snow - every decoration off."},{name:"trees",label:"trees",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"wreaths",label:"wreaths",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"northstars",label:"North Star",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"snowmen",label:"snowmen",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"sleigh",label:"Santa's sleigh",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"elf",label:"elf",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"train",label:"train",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"firework",label:"fireworks",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"robin",label:"robin",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"peeing-elf",label:"Peeing elf (opt-in)",type:"checkbox",default:!1,hint:"A clothed rear-view gag. Off by default; Snow only keeps it off."},{name:"thieving-elf",label:"Thieving elf (opt-in)",type:"checkbox",default:!1,hint:"Runs to a tree, takes a present and escapes. Needs a visible tree."}],hosts:[],previewable:!1,selfClosing:!0},{name:"ticker-tape",label:"Ticker tape",category:"Celebrations",description:"Coloured rectangular strips fall from the top, sway, rotate, and fade. Inserted self-closing (full-page); hand-write the wrapping form for container-scoped.",attrs:[{name:"palette",label:"Palette",type:"select",default:"theme",options:[["theme","Theme"],["rainbow","Rainbow"],["gold","Gold"],["silver","Silver"],["festive","Festive"],["pastel","Pastel"],["mono","Mono"]]},{name:"density",label:"Density",type:"number",default:50},{name:"speed",label:"Speed multiplier",type:"number",default:1,step:.1},{name:"sway",label:"Sway (px)",type:"number",default:60},{name:"burst",label:"Burst mode (one-shot)",type:"checkbox",default:!1},{name:"burst-count",label:"Burst count",type:"number",default:150}],hosts:["hero"],previewable:!0,selfClosing:!0},{name:"butterflies",label:"Butterflies",category:"Celebrations",description:"Procedurally drawn butterflies wander and rise with flapping wings. Self-closing for full-page; wrapping form for container-scoped.",attrs:[{name:"palette",label:"Palette",type:"select",default:"meadow",options:[["meadow","Meadow"],["theme","Theme"],["rainbow","Rainbow"],["pastel","Pastel"],["festive","Festive"],["gold","Gold"],["silver","Silver"],["mono","Mono"],["sunset","Sunset"],["ocean","Ocean"],["forest","Forest"],["bridal","Bridal"],["firefly","Firefly"]]},{name:"density",label:"Density",type:"number",default:18},{name:"speed",label:"Speed multiplier",type:"number",default:1,step:.1},{name:"wander",label:"Wander",type:"number",default:1,step:.1,hint:"Steering erraticness"},{name:"rise-speed",label:"Rise speed",type:"number",default:.4,step:.1,hint:"Upward drift bias"},{name:"flap-speed",label:"Flap speed",type:"number",default:1,step:.1},{name:"min-size",label:"Min wingspan (px)",type:"number",default:14},{name:"max-size",label:"Max wingspan (px)",type:"number",default:26},{name:"two-tone",label:"Two-tone wings",type:"checkbox",default:!0},{name:"burst",label:"Burst mode (one-shot)",type:"checkbox",default:!1},{name:"burst-count",label:"Burst count",type:"number",default:40}],hosts:["hero"],previewable:!0},{name:"strobe",label:"Strobe lighting",category:"Celebrations",description:"Light beams sweep from the corners and brighten where they cross - club, concert, or searchlight moods. Self-closing for full-page; wrapping form for container-scoped.",attrs:[{name:"preset",label:"Preset",type:"select",default:"club",options:[["club","Club"],["concert","Concert"],["police","Police"],["searchlight","Searchlight"],["scanner","Scanner"],["mood","Mood"]],hint:"Other fields override the preset when changed"},{name:"motion",label:"Motion",type:"select",default:"sweep",options:[["sweep","Sweep (fan back and forth)"],["rotate","Rotate (full circle)"]]},{name:"speed",label:"Speed multiplier",type:"number",default:1,step:.1},{name:"sweep-arc",label:"Sweep arc (deg)",type:"number",default:100},{name:"beam-width",label:"Beam width (deg)",type:"number",default:16},{name:"intensity",label:"Intensity",type:"number",default:.7,step:.05,min:0,max:1},{name:"flicker",label:"Flicker",type:"checkbox",default:!0},{name:"hz",label:"Flicker rate (Hz)",type:"number",default:10,hint:"Rates above 5 flashes/sec can trigger photosensitive seizures"},{name:"colours",label:"Colours",type:"text",default:"",placeholder:"#ffffff, #38bdf8",hint:"Comma-separated hex colours - overrides the preset palette"},{name:"duration",label:"Auto-stop (ms)",type:"number",default:"",hint:"Stop after this many ms - blank runs forever"}],hosts:["hero"],previewable:!0}];export function getEffect(e){return EFFECTS.find(t=>t.name===e)||null}export function effectsByCategory(){return["Entrance","Animation","Text","Visual","Celebrations"].map(t=>({category:t,effects:EFFECTS.filter(o=>o.category===t)}))}export function effectsInjectableInto(e){return EFFECTS.filter(t=>t.hosts.includes(e))}const n=EFFECTS.find(e=>e.name==="celebrate");for(const e of n.attrs.slice(2))e.themes=["christmas"];n.attrs.push({name:"character",label:"Standalone character",type:"select",default:"",options:[["","Full celebration"],...m().map(e=>[e.name,e.label])]},{name:"size",label:"Character height",type:"text",default:"medium",placeholder:"medium or 200",hint:"small (96px), medium (160px), large (240px), xlarge (360px), or a height in pixels."},{name:"animate",label:"Animate character",type:"checkbox",default:!0},{name:"facing",label:"Character direction",type:"select",default:"right",options:[["right","Right"],["left","Left"]]}),n.attrs.push({name:"falling-only",label:"Falling particles only",type:"checkbox",default:!1}),n.attrs.push({name:"mooning-elf",label:"Mooning elf (opt-in)",type:"checkbox",default:!1,themes:["christmas"]}),EFFECTS.find(e=>e.name==="christmas").attrs.push({name:"mooning-elf",label:"Mooning elf (opt-in)",type:"checkbox",default:!1});const i=new Map(n.attrs.map(e=>[e.name,e]));for(const[e,t]of Object.entries(f))if(e!=="christmas")for(const[o,r]of Object.entries(t.traits)){const a=u(o);let l=i.get(a);l||(l=r.enabled===!1?{name:a,label:`${r.label} (opt-in)`,type:"checkbox",default:!1,themes:[]}:{name:a,label:r.label,type:"select",default:"",options:[["","Shown"],["off","Off"]],themes:[]},i.set(a,l)),l.themes.includes(e)||l.themes.push(e),l.themes.length>1&&(l.label=l.label.replace(/^Christmas: /,""))}for(const e of i.values())n.attrs.includes(e)||n.attrs.push(e);export function getEffectAttrs(e,t={}){if(e.name!=="celebrate")return[...e.attrs||[],...s];const o=["character","size","animate","facing"];if(t.character)return[...e.attrs.filter(a=>o.includes(a.name)),...s];const r=t.theme||"auto";return[...e.attrs.filter(a=>(a.name==="character"||!o.includes(a.name))&&(!a.themes||r==="auto"||a.themes.includes(r))),...s]}
|
|
1
|
+
import{SCRIBE_FIELDS as m}from"./scribe-model.js";import{getCharacters as u}from"../../../public/js/celebrations/character-catalog.mjs";import{CELEBRATION_THEMES as s,traitAttribute as c}from"../../../public/js/celebrations/trait-data.mjs";import{SHORTCODE_EXPIRY_FIELDS as i}from"../../../public/js/shortcode-expiry.mjs";export{s as CELEBRATION_THEMES};export const EFFECTS=[{name:"reveal",label:"Reveal",category:"Entrance",description:"Scroll-triggered entrance animation when the element enters the viewport.",attrs:[{name:"animation",label:"Animation",type:"select",default:"fade",options:[["fade","Fade"],["slide-up","Slide up"],["slide-down","Slide down"],["zoom","Zoom"],["flip","Flip"]]},{name:"duration",label:"Duration (ms)",type:"number",default:600},{name:"delay",label:"Delay (ms)",type:"number",default:0},{name:"threshold",label:"Threshold",type:"number",default:.1,step:.05,min:0,max:1},{name:"once",label:"Animate once",type:"checkbox",default:!0}],hosts:["card","box","hero","banner","col"],previewable:!0},{name:"breathe",label:"Breathe",category:"Animation",description:"Continuous gentle scaling - makes elements feel alive.",attrs:[{name:"amplitude",label:"Amplitude (px)",type:"number",default:8},{name:"duration",label:"Duration (ms)",type:"number",default:3e3}],hosts:["card","box","hero","col","button"],previewable:!0},{name:"pulse",label:"Pulse",category:"Animation",description:"A repeating scale pulse - stronger than breathe.",attrs:[{name:"scale",label:"Scale",type:"number",default:1.05,step:.01},{name:"duration",label:"Duration (ms)",type:"number",default:1e3}],hosts:["card","box","hero","banner","button"],previewable:!0},{name:"shake",label:"Shake",category:"Animation",description:"One-shot horizontal or vertical shake.",attrs:[{name:"intensity",label:"Intensity (px)",type:"number",default:5},{name:"duration",label:"Duration (ms)",type:"number",default:500},{name:"direction",label:"Direction",type:"select",default:"horizontal",options:[["horizontal","Horizontal"],["vertical","Vertical"]]}],hosts:["card","box","banner","button"],previewable:!0},{name:"animate",label:"CSS Animate",category:"Animation",description:"Applies a Domma CSS animation utility class - no JavaScript.",attrs:[{name:"type",label:"Type",type:"select",default:"fade-in-up",options:[["fade-in","fade-in"],["fade-in-up","fade-in-up"],["fade-in-down","fade-in-down"],["fade-in-left","fade-in-left"],["fade-in-right","fade-in-right"],["slide-in-up","slide-in-up"],["slide-in-down","slide-in-down"],["zoom-in","zoom-in"],["zoom-out","zoom-out"],["bounce","bounce"],["pulse","pulse"],["shake","shake"],["spin","spin"],["flip","flip"]]},{name:"duration",label:"Duration",type:"select",default:"normal",options:[["fast","fast"],["normal","normal"],["slow","slow"],["slower","slower"]]},{name:"delay",label:"Delay (ms)",type:"number",default:0},{name:"repeat",label:"Repeat",type:"select",default:"once",options:[["once","Once"],["infinite","Infinite"]]}],hosts:["card","box","hero","banner","col","button"],previewable:!0},{name:"scribe",preview:"scribe",label:"Scribe (typewriter)",category:"Text",description:"Types characters, words, or sentences with entrance effects and playback options. For action scripts use Insert \u2192 Scribe.",attrs:m,hosts:[],previewable:!0},{name:"scramble",label:"Scramble",category:"Text",description:"Reveals text through a character-scramble animation.",attrs:[{name:"speed",label:"Speed (ms/char)",type:"number",default:50},{name:"loop",label:"Loop",type:"checkbox",default:!1}],hosts:[],previewable:!0},{name:"counter",label:"Counter",category:"Text",description:"Animates a number from a starting value to a target. Self-closing shortcode.",attrs:[{name:"to",label:"To",type:"number",default:100},{name:"from",label:"From",type:"number",default:0},{name:"duration",label:"Duration (ms)",type:"number",default:2e3},{name:"prefix",label:"Prefix",type:"text",default:""},{name:"suffix",label:"Suffix",type:"text",default:""},{name:"decimals",label:"Decimal places",type:"number",default:0},{name:"separator",label:"Thousand separator",type:"text",default:""}],hosts:[],previewable:!0,selfClosing:!0},{name:"ripple",label:"Ripple",category:"Visual",description:"Click-triggered ripple effect on the element.",attrs:[{name:"colour",label:"Colour",type:"colour",default:"#7c6af7"},{name:"duration",label:"Duration (ms)",type:"number",default:600},{name:"opacity",label:"Opacity",type:"number",default:.3,step:.05,min:0,max:1}],hosts:["card","box","col","button"],previewable:!0},{name:"twinkle",label:"Twinkle",category:"Visual",description:"Generates animated particle effects inside the element.",attrs:[{name:"count",label:"Particle count",type:"number",default:80},{name:"shape",label:"Shape",type:"select",default:"star",options:[["star","Star"],["circle","Circle"],["square","Square"]]},{name:"colour",label:"Colour",type:"colour",default:"#ffffff"},{name:"min-size",label:"Min size (px)",type:"number",default:2},{name:"max-size",label:"Max size (px)",type:"number",default:6}],hosts:["card","box","hero"],previewable:!0},{name:"ambient",label:"Ambient background",category:"Visual",description:"Applies an animated CSS background class to the element.",attrs:[{name:"type",label:"Type",type:"select",default:"float-blobs",options:[["float-blobs","Float blobs"],["float-orbs","Float orbs"],["float-particles","Float particles"],["rotate-glow","Rotate glow"],["rotate-dual","Rotate dual"],["rotate-spotlight","Rotate spotlight"],["wave","Wave"],["ripple","Ripple"],["aurora","Aurora"]]},{name:"speed",label:"Speed",type:"select",default:"normal",options:[["slow","Slow"],["normal","Normal"],["fast","Fast"]]},{name:"intensity",label:"Intensity",type:"select",default:"subtle",options:[["subtle","Subtle"],["intense","Intense"]]}],hosts:["card","box","hero","banner"],previewable:!0},{name:"firework",label:"Firework",category:"Celebrations",description:"CSS-only firework particle. Self-closing or wrapping form.",attrs:[{name:"type",label:"Type",type:"select",default:"burst",options:[["burst","Burst"],["sparkle","Sparkle"],["trail","Trail"]]},{name:"colour",label:"Colour theme",type:"select",default:"rainbow",options:[["rainbow","Rainbow"],["primary","Primary"],["info","Info"],["success","Success"],["warning","Warning"],["danger","Danger"]]},{name:"size",label:"Size",type:"select",default:"md",options:[["sm","Small"],["md","Medium"],["lg","Large"]]},{name:"continuous",label:"Continuous",type:"checkbox",default:!1},{name:"hover",label:"On hover only",type:"checkbox",default:!1}],hosts:[],previewable:!0},{name:"fireworks",label:"Fireworks show",category:"Celebrations",description:"A container with multiple firework particles inside.",attrs:[],hosts:[],previewable:!1},{name:"celebrate",label:"Celebrate",category:"Celebrations",description:"A full-page seasonal celebration, or one inline character at any time of year.",attrs:[{name:"theme",label:"Theme",type:"select",default:"auto",options:[["auto","Auto (date-detected)"],...Object.entries(s).map(([e,t])=>[e,t.label])]},{name:"intensity",label:"Intensity",type:"select",default:"medium",options:[["light","Light"],["medium","Medium"],["heavy","Heavy"]]},{name:"snow-only",label:"Snow only (Christmas)",type:"checkbox",default:!1,hint:"Just the falling snow - every Christmas decoration off."},{name:"trees",label:"Christmas: trees",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"wreaths",label:"Christmas: wreaths",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"northstars",label:"Christmas: North Star",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"snowmen",label:"Christmas: snowmen",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"sleigh",label:"Christmas: Santa's sleigh",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"elf",label:"Christmas: elf",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"train",label:"Christmas: train",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"firework",label:"Christmas: fireworks",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"robin",label:"Christmas: robin",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"peeing-elf",label:"Peeing elf (opt-in)",type:"checkbox",default:!1,hint:"A clothed rear-view gag. Off by default; Snow only keeps it off."},{name:"thieving-elf",label:"Thieving elf (opt-in)",type:"checkbox",default:!1,hint:"Runs to a tree, takes a present and escapes. Needs a visible tree."}],hosts:[],previewable:!1,selfClosing:!0},{name:"christmas",label:"Christmas",category:"Celebrations",description:"Christmas on the published site: snow, trees, wreaths, snowmen, Santa's sleigh and more. Snow only for just the snow.",attrs:[{name:"intensity",label:"Intensity",type:"select",default:"medium",options:[["light","Light"],["medium","Medium"],["heavy","Heavy"]]},{name:"snow-only",label:"Snow only",type:"checkbox",default:!1,hint:"Just the falling snow - every decoration off."},{name:"trees",label:"trees",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"wreaths",label:"wreaths",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"northstars",label:"North Star",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"snowmen",label:"snowmen",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"sleigh",label:"Santa's sleigh",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"elf",label:"elf",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"train",label:"train",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"firework",label:"fireworks",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"robin",label:"robin",type:"select",default:"",options:[["","Shown"],["off","Off"]]},{name:"peeing-elf",label:"Peeing elf (opt-in)",type:"checkbox",default:!1,hint:"A clothed rear-view gag. Off by default; Snow only keeps it off."},{name:"thieving-elf",label:"Thieving elf (opt-in)",type:"checkbox",default:!1,hint:"Runs to a tree, takes a present and escapes. Needs a visible tree."}],hosts:[],previewable:!1,selfClosing:!0},{name:"ticker-tape",label:"Ticker tape",category:"Celebrations",description:"Coloured rectangular strips fall from the top, sway, rotate, and fade. Inserted self-closing (full-page); hand-write the wrapping form for container-scoped.",attrs:[{name:"palette",label:"Palette",type:"select",default:"theme",options:[["theme","Theme"],["rainbow","Rainbow"],["gold","Gold"],["silver","Silver"],["festive","Festive"],["pastel","Pastel"],["mono","Mono"]]},{name:"density",label:"Density",type:"number",default:50},{name:"speed",label:"Speed multiplier",type:"number",default:1,step:.1},{name:"sway",label:"Sway (px)",type:"number",default:60},{name:"burst",label:"Burst mode (one-shot)",type:"checkbox",default:!1},{name:"burst-count",label:"Burst count",type:"number",default:150}],hosts:["hero"],previewable:!0,selfClosing:!0},{name:"butterflies",label:"Butterflies",category:"Celebrations",description:"Procedurally drawn butterflies wander and rise with flapping wings. Self-closing for full-page; wrapping form for container-scoped.",attrs:[{name:"palette",label:"Palette",type:"select",default:"meadow",options:[["meadow","Meadow"],["theme","Theme"],["rainbow","Rainbow"],["pastel","Pastel"],["festive","Festive"],["gold","Gold"],["silver","Silver"],["mono","Mono"],["sunset","Sunset"],["ocean","Ocean"],["forest","Forest"],["bridal","Bridal"],["firefly","Firefly"]]},{name:"density",label:"Density",type:"number",default:18},{name:"speed",label:"Speed multiplier",type:"number",default:1,step:.1},{name:"wander",label:"Wander",type:"number",default:1,step:.1,hint:"Steering erraticness"},{name:"rise-speed",label:"Rise speed",type:"number",default:.4,step:.1,hint:"Upward drift bias"},{name:"flap-speed",label:"Flap speed",type:"number",default:1,step:.1},{name:"min-size",label:"Min wingspan (px)",type:"number",default:14},{name:"max-size",label:"Max wingspan (px)",type:"number",default:26},{name:"two-tone",label:"Two-tone wings",type:"checkbox",default:!0},{name:"burst",label:"Burst mode (one-shot)",type:"checkbox",default:!1},{name:"burst-count",label:"Burst count",type:"number",default:40}],hosts:["hero"],previewable:!0},{name:"strobe",label:"Strobe lighting",category:"Celebrations",description:"Light beams sweep from the corners and brighten where they cross - club, concert, or searchlight moods. Self-closing for full-page; wrapping form for container-scoped.",attrs:[{name:"preset",label:"Preset",type:"select",default:"club",options:[["club","Club"],["concert","Concert"],["police","Police"],["searchlight","Searchlight"],["scanner","Scanner"],["mood","Mood"]],hint:"Other fields override the preset when changed"},{name:"motion",label:"Motion",type:"select",default:"sweep",options:[["sweep","Sweep (fan back and forth)"],["rotate","Rotate (full circle)"]]},{name:"speed",label:"Speed multiplier",type:"number",default:1,step:.1},{name:"sweep-arc",label:"Sweep arc (deg)",type:"number",default:100},{name:"beam-width",label:"Beam width (deg)",type:"number",default:16},{name:"intensity",label:"Intensity",type:"number",default:.7,step:.05,min:0,max:1},{name:"flicker",label:"Flicker",type:"checkbox",default:!0},{name:"hz",label:"Flicker rate (Hz)",type:"number",default:10,hint:"Rates above 5 flashes/sec can trigger photosensitive seizures"},{name:"colours",label:"Colours",type:"text",default:"",placeholder:"#ffffff, #38bdf8",hint:"Comma-separated hex colours - overrides the preset palette"},{name:"duration",label:"Auto-stop (ms)",type:"number",default:"",hint:"Stop after this many ms - blank runs forever"}],hosts:["hero"],previewable:!0}];export function getEffect(e){return EFFECTS.find(t=>t.name===e)||null}export function effectsByCategory(){return["Entrance","Animation","Text","Visual","Celebrations"].map(t=>({category:t,effects:EFFECTS.filter(r=>r.category===t)}))}export function effectsInjectableInto(e){return EFFECTS.filter(t=>t.hosts.includes(e))}const l=EFFECTS.find(e=>e.name==="celebrate");for(const e of l.attrs.slice(2))e.themes=["christmas"];l.attrs.push({name:"character",label:"Standalone character",type:"select",default:"",options:[["","Full celebration"],...u().map(e=>[e.name,e.label])]},{name:"size",label:"Character height",type:"text",default:"medium",placeholder:"medium or 200",hint:"small (96px), medium (160px), large (240px), xlarge (360px), or a height in pixels."},{name:"animate",label:"Animate character",type:"checkbox",default:!0},{name:"facing",label:"Character direction",type:"select",default:"right",options:[["right","Right"],["left","Left"]]}),l.attrs.push({name:"falling-only",label:"Falling particles only",type:"checkbox",default:!1}),l.attrs.push({name:"mooning-elf",label:"Mooning elf (opt-in)",type:"checkbox",default:!1,themes:["christmas"]}),EFFECTS.find(e=>e.name==="christmas").attrs.push({name:"mooning-elf",label:"Mooning elf (opt-in)",type:"checkbox",default:!1}),l.attrs.push({name:"sparkler-message",label:"Sparkler greeting",type:"select",default:"",options:[["","Shown"],["off","Off"]],themes:["christmas"]}),EFFECTS.find(e=>e.name==="christmas").attrs.push({name:"sparkler-message",label:"Sparkler greeting",type:"select",default:"",options:[["","Shown"],["off","Off"]]});const b=["christmas","new-year","chinese-new-year","graduation","birthday","anniversary","greeting"],p=[{name:"message",label:"Custom message",type:"text",default:"",hint:"Optional wording or a name. Use {year} to insert the greeting year. Maximum 120 characters."},{name:"sparkler-style",label:"Sparkler lettering",type:"select",default:"stroke",options:[["stroke","Stroke"],["cursive","Cursive"]]},{name:"sparkler-colour",label:"Sparkler colour",type:"select",default:"gold",options:[["gold","Gold"],["silver","Silver"]]},{name:"sparkler-greeting",label:"Sparkler greeting text",type:"select",default:"default",options:[["default","Seasonal greeting"],["best-wishes","Best wishes for the year"]]},{name:"sparkler-year",label:"Greeting year",type:"number",default:"",min:1,max:9999,hint:"For Best wishes and {year} in your message. Leave blank for the current year."}];l.attrs.push(...p.map(e=>({...e,themes:b}))),EFFECTS.find(e=>e.name==="christmas").attrs.push(...p),l.attrs.push({name:"duration",label:"Rain duration (ms)",type:"number",default:8e3,min:0,max:6e5,hint:"0 keeps raining until stopped.",themes:["make-it-rain","money-rain"]});const f=new Map(l.attrs.map(e=>[e.name,e]));for(const[e,t]of Object.entries(s))if(e!=="christmas")for(const[r,o]of Object.entries(t.traits)){const a=c(r);let n=f.get(a);n||(n=o.enabled===!1?{name:a,label:`${o.label} (opt-in)`,type:"checkbox",default:!1,themes:[]}:{name:a,label:o.label,type:"select",default:"",options:[["","Shown"],["off","Off"]],themes:[]},f.set(a,n)),n.themes.includes(e)||n.themes.push(e),n.themes.length>1&&(n.label=n.label.replace(/^Christmas: /,""))}for(const e of f.values())l.attrs.includes(e)||l.attrs.push(e);for(const e of["new-year","chinese-new-year","easter","summer-garden","winter","make-it-rain","money-rain","graduation","birthday","anniversary","greeting","autumn"])EFFECTS.push({name:e,label:s[e].label,category:"Celebrations",description:`The ${s[e].label} celebration at any time of year.`,attrs:l.attrs.filter(t=>!["theme","character","size","animate","facing"].includes(t.name)&&(!t.themes||t.themes.includes(e))),hosts:[],selfClosing:!0,previewable:!1});export function getEffectAttrs(e,t={}){if(e.name!=="celebrate")return[...e.attrs||[],...i];const r=["character","size","animate","facing"];if(t.character)return[...e.attrs.filter(a=>r.includes(a.name)),...i];const o=t.theme||"auto";return[...e.attrs.filter(a=>(a.name==="character"||!r.includes(a.name))&&(!a.themes||o==="auto"||a.themes.includes(o))),...i]}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{EFFECTS as
|
|
1
|
+
import{findEnclosingShortcode as j}from"./shortcode-tags.js";import{encodeMessageAttribute as x,decodeMessageAttribute as O}from"../../../public/js/celebrations/message-attrs.mjs";import{EFFECTS as k,effectsByCategory as N,effectsInjectableInto as B,getEffect as u,getEffectAttrs as g}from"./effect-defs.js";import{scribeLoop as q}from"./scribe-model.js";import{buildScribeStyle as P}from"../../../public/js/scribe-style.mjs?v=20261013-cms-fonts";import{insertAtCursor as M,replaceRange as T}from"./markdown-toolbar.js";let h=null,b=null;function F(e,t,o=14){e.textContent="";const n=document.createElement("span");return n.setAttribute("data-icon",t),n.setAttribute("data-icon-size",String(o)),n.style.cssText="display:inline-flex;align-items:center;vertical-align:middle;",e.appendChild(n),I.scan(e),e}function D(e,t,o,n=14){return F(e,t,n),e.appendChild(document.createTextNode(" "+o)),e}export const EffectsBuilder={open(e){if(h)return;const t={textarea:e,activeEffect:S.get("effects-builder-last")||"reveal",scope:"standalone",host:null,editing:null,fieldValues:{}};if(t.host=G(e),t.host&&B(t.host.name).map(r=>r.name).includes(t.activeEffect)&&!(t.activeEffect in t.host.attrs)&&(t.scope="inject"),t.editing=W(e),t.editing){t.activeEffect=t.editing.name,t.scope="standalone";const c=u(t.editing.name);for(const r of g(c,t.fieldValues))if(r.name in t.editing.attrs){const l=t.editing.attrs[r.name];r.type==="number"?t.fieldValues[r.name]=Number(l):r.type==="checkbox"?t.fieldValues[r.name]=l==="true":t.fieldValues[r.name]=l}}const o=E.slideover({title:"Effects",size:"lg",position:"right",onClose:()=>{b?.destroy(),b=null,h=null}}),n=H(t,o);o.element.appendChild(n),o.open(),I.scan(n),h={state:t,slideover:o}},close(){h&&(h.slideover.close(),h=null)}};function H(e,t){const o=document.createElement("div");o.style.cssText="display:grid;grid-template-columns:170px 1fr;height:100%;min-height:520px;";const n=document.createElement("div");n.className="effects-rail",n.style.cssText="background:var(--dm-background-alt,#15152a);border-right:1px solid var(--dm-border,#2a2a40);padding:.5rem .35rem;font-size:.8rem;overflow-y:auto;";const c=document.createElement("div");c.className="effects-pane",c.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;overflow-y:auto;";function r(){Q(c,e,t)}return $(n,e,r),r(),o.appendChild(n),o.appendChild(c),o}function $(e,t,o){e.replaceChildren();for(const n of N()){const c=document.createElement("div");c.style.cssText="color:var(--dm-text-muted,#888);text-transform:uppercase;font-size:.65rem;letter-spacing:.05em;margin:.4rem .35rem .25rem;",c.textContent=n.category,e.appendChild(c);for(const r of n.effects){const l=document.createElement("button");l.type="button",l.dataset.effect=r.name,l.style.cssText="display:block;width:100%;text-align:left;padding:.3rem .5rem;border:none;background:transparent;color:var(--dm-text,#ddd);border-radius:3px;font-size:.8rem;cursor:pointer;margin-bottom:.1rem;",l.textContent=r.label,r.name===t.activeEffect&&(l.style.background="var(--dm-primary,#4338ca)",l.style.color="var(--dm-primary-text,#fff)"),l.addEventListener("click",()=>{t.activeEffect=r.name,S.set("effects-builder-last",r.name),t.fieldValues={},$(e,t,o),o()}),e.appendChild(l)}}}function K(e,t){if(e.type==="select"){const n=document.createElement("select");n.style.cssText="width:100%;padding:.35rem .5rem;background:var(--dm-input-bg,#1a1a1a);border:1px solid var(--dm-border,#333);border-radius:3px;color:var(--dm-text,#eee);font-size:.85rem;";for(const[c,r]of e.options){const l=document.createElement("option");l.value=c,l.textContent=r,n.appendChild(l)}return n.value=t??e.default??"",n}if(e.type==="checkbox"){const n=document.createElement("input");return n.type="checkbox",n.checked=t??e.default??!1,n}const o=document.createElement("input");return o.type=e.type==="number"?"number":e.type==="colour"?"color":"text",o.value=t??e.default??"",e.placeholder&&(o.placeholder=e.placeholder),e.type==="number"&&(e.min!=null&&(o.min=e.min),e.max!=null&&(o.max=e.max),e.step!=null&&(o.step=e.step)),o.style.cssText="width:100%;padding:.35rem .5rem;background:var(--dm-input-bg,#1a1a1a);border:1px solid var(--dm-border,#333);border-radius:3px;color:var(--dm-text,#eee);font-size:.85rem;",o}function w(e,t){return t.type==="checkbox"?e.checked:t.type==="number"?e.value===""?"":Number(e.value):e.value}function y(e,t,o){const n=u(t.activeEffect);if(e.replaceChildren(),n.attrs.length===0){const r=document.createElement("div");r.style.cssText="color:var(--dm-text-muted,#888);font-size:.8rem;font-style:italic;",r.textContent="This effect takes no parameters.",e.appendChild(r);return}const c=document.createElement("div");c.style.cssText="display:grid;grid-template-columns:1fr 1fr;gap:.6rem .7rem;";for(const r of g(n,t.fieldValues)){const l=document.createElement("div"),d=document.createElement("label");d.style.cssText="display:block;color:var(--dm-text-muted,#888);font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;margin-bottom:.2rem;",d.textContent=r.label,l.appendChild(d);const i=K(r,t.fieldValues[r.name]);if(i.addEventListener("input",()=>{t.fieldValues[r.name]=w(i,r),o(),r.name==="theme"&&y(e,t,o)}),i.addEventListener("change",()=>{t.fieldValues[r.name]=w(i,r),o(),r.name==="theme"&&y(e,t,o)}),l.appendChild(i),r.hint){const s=document.createElement("div");s.style.cssText="color:var(--dm-text-muted,#666);font-size:.7rem;margin-top:.15rem;",s.textContent=r.hint,l.appendChild(s)}c.appendChild(l)}e.appendChild(c)}function _(e){const t={},o=/(\w[\w-]*)(?:\s*=\s*"([^"]*)"|\s*=\s*'([^']*)'|\s*=\s*(\S+))?/g;for(const n of e.matchAll(o)){const c=n[1],r=n[2]??n[3]??n[4]??"";t[c]=c==="message"?O(r):r}return t}const U=k.filter(e=>!e.selfClosing).map(e=>e.name);function W(e){const t=j(e,k.filter(c=>c.selfClosing));if(t?.selfClosing)return{name:t.name,blockStart:t.openTagStart,blockEnd:t.openTagEnd,openTagEnd:t.openTagEnd,attrs:t.attrs};const o=e.value,n=e.selectionStart;for(const c of U){const r=new RegExp(`\\[${c}([^\\]]*)\\]`,"g"),l=new RegExp(`\\[/${c}\\]`,"g"),d=[];for(const a of o.matchAll(r)){if(a.index>=n)break;d.push({type:"open",start:a.index,end:a.index+a[0].length,attrStr:a[1]})}for(const a of o.matchAll(l)){if(a.index>=n)break;d.push({type:"close",start:a.index,end:a.index+a[0].length})}d.sort((a,m)=>a.start-m.start);let i=0,s=null;for(const a of d)a.type==="open"?(i++,s=a):(i--,i<0&&(i=0,s=null));if(i>0&&s){const a=new RegExp(`\\[/${c}\\]`,"g"),m=o.slice(n),f=m.match(a);if(f){const p=n+m.indexOf(f[0]);return{name:c,blockStart:s.start,blockEnd:p+f[0].length,openTagEnd:s.end,attrs:_(s.attrStr)}}}}return null}const X=["card","box","hero","banner","col","button"];function G(e){const t=e.value,o=e.selectionStart;for(const n of X){const c=new RegExp(`\\[${n}([^\\]]*)\\]`,"g"),r=new RegExp(`\\[/${n}\\]`,"g"),l=[];for(const s of t.matchAll(c)){if(s.index>=o)break;l.push({type:"open",start:s.index,end:s.index+s[0].length,attrStr:s[1]})}for(const s of t.matchAll(r)){if(s.index>=o)break;l.push({type:"close",start:s.index,end:s.index+s[0].length})}l.sort((s,a)=>s.start-a.start);let d=0,i=null;for(const s of l)s.type==="open"?(d++,i=s):(d--,d<0&&(d=0,i=null));if(d>0&&i){const s=new RegExp(`\\[/${n}\\]`,"g"),a=t.slice(o),m=a.match(s);if(m){const f=o+a.indexOf(m[0]);return{name:n,openTagStart:i.start,openTagEnd:i.end,closeTagStart:f,closeTagEnd:f+m[0].length,attrs:_(i.attrStr)}}}}return null}function z(e,t){const o=[];for(const n of g(t,e.fieldValues)){const c=e.fieldValues[n.name];c===void 0||c===""||c===null||c===n.default&&!n.always||(n.type==="checkbox"?o.push(`${n.name}="${c?"true":"false"}"`):o.push(`${n.name}="${n.name==="message"?x(c):String(c).replace(/"/g,""")}"`))}return o.join(" ")}function A(e){const t=u(e.activeEffect),o=z(e,t),n=o?" ":"";if(e.scope==="inject"&&e.host){const c=e.host.attrs,r=t.name,l=[];for(const d of Object.keys(c)){if(d===r||d.startsWith(r+"-"))continue;const i=c[d];l.push(i===""?d:`${d}="${attr.name==="message"?x(i):String(i).replace(/"/g,""")}"`)}l.push(r);for(const d of g(t,e.fieldValues)){const i=e.fieldValues[d.name];if(i===void 0||i===""||i===null||i===d.default)continue;const s=d.type==="checkbox"?i?"true":"false":d.name==="message"?x(i):String(i).replace(/"/g,""");l.push(`${r}-${d.name}="${s}"`)}return`[${e.host.name} ${l.join(" ")}]\u2026[/${e.host.name}]`}return t.selfClosing?`[${t.name}${n}${o} /]`:`[${t.name}${n}${o}]
|
|
2
2
|
Content
|
|
3
|
-
[/${t.name}]`}function
|
|
3
|
+
[/${t.name}]`}function V(e,t){e.replaceChildren();const o=document.createElement("div");o.style.cssText="color:var(--dm-text-muted,#888);font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;margin-bottom:.25rem;",o.textContent="Shortcode (live)",e.appendChild(o);const n=document.createElement("pre");n.style.cssText="background:var(--dm-code-bg,#000);border:1px solid var(--dm-border,#333);padding:.5rem .65rem;border-radius:3px;font-family:var(--dm-font-mono,monospace);font-size:.75rem;color:var(--dm-code-text,#7c6af7);line-height:1.5;white-space:pre-wrap;word-break:break-all;margin:0;",n.textContent=A(t),e.appendChild(n)}function J(e,t,o){if(e.replaceChildren(),t.editing)return;const n=u(t.activeEffect),c=t.host&&n.hosts.includes(t.host.name),r=document.createElement("div");r.style.cssText="background:var(--dm-background-alt,#15152a);border:1px solid var(--dm-border,#2a2a40);border-radius:4px;padding:.5rem .6rem;";const l=document.createElement("div");if(l.style.cssText="color:var(--dm-text-muted,#888);text-transform:uppercase;font-size:.65rem;letter-spacing:.05em;margin-bottom:.3rem;",l.textContent="Apply as",r.appendChild(l),!c){const s=document.createElement("div");s.style.cssText="color:var(--dm-text-muted,#888);font-size:.75rem;font-style:italic;",t.host?s.textContent=`Inserted as a standalone block (${n.label} cannot be injected into [${t.host.name}]).`:s.textContent="Inserted as a standalone block.",r.appendChild(s),e.appendChild(r),t.scope="standalone";return}const d=document.createElement("div");d.style.cssText="display:flex;gap:.3rem;";function i(s,a){const m=document.createElement("button");return m.type="button",m.style.cssText="flex:1;padding:.35rem;border:1px solid var(--dm-border,#333);border-radius:3px;font-size:.75rem;font-weight:600;cursor:pointer;",t.scope===s?(m.style.background="var(--dm-primary,#4338ca)",m.style.color="var(--dm-primary-text,#fff)",m.style.borderColor="var(--dm-primary,#4338ca)"):(m.style.background="transparent",m.style.color="var(--dm-text-muted,#bbb)"),m.textContent=a,m.addEventListener("click",()=>{t.scope=s,o()}),m}d.appendChild(i("standalone","Standalone block")),d.appendChild(i("inject",`Inject into [${t.host.name}]`)),r.appendChild(d),e.appendChild(r)}let C=0;function R(e){return e.replace(/-([a-z])/g,(t,o)=>o.toUpperCase())}function L(e,t){e.replaceChildren();const o=u(t.activeEffect),n=document.createElement("div");n.style.cssText="display:flex;justify-content:space-between;align-items:center;margin-bottom:.25rem;";const c=document.createElement("div");if(c.style.cssText="color:var(--dm-text-muted,#888);text-transform:uppercase;font-size:.65rem;letter-spacing:.05em;",c.textContent="Live preview",n.appendChild(c),!o.previewable){const i=document.createElement("div");i.style.cssText="background:var(--dm-background-alt,#15152a);border:1px solid var(--dm-border,#2a2a40);border-radius:4px;padding:.6rem;color:var(--dm-text-muted,#888);font-size:.8rem;font-style:italic;text-align:center;",i.textContent="Runs full-page on the published site - no inline preview.",e.appendChild(i);return}const r=document.createElement("button");r.type="button",r.style.cssText="background:transparent;border:1px solid var(--dm-border,#333);color:var(--dm-text-muted,#888);font-size:.65rem;padding:.15rem .4rem;border-radius:2px;cursor:pointer;",D(r,"refresh-cw","Replay",12),n.appendChild(r),e.appendChild(n);const l=document.createElement("div");l.style.cssText="background:var(--dm-background-alt,#1a1a2e);border:1px solid var(--dm-border,#2a2a40);border-radius:4px;padding:1rem;text-align:center;min-height:80px;position:relative;overflow:hidden;",e.appendChild(l);function d(i=!1){b?.destroy(),b=null,C++;const s=C;l.replaceChildren();const a=document.createElement("div");a.style.cssText="background:linear-gradient(135deg,#6366f1,#a855f7);color:#fff;padding:.6rem;border-radius:4px;font-size:.8rem;font-weight:600;",a.textContent="Sample content",l.appendChild(a);const m=window.Domma&&window.Domma.effects;if(!m)return;const f={};for(const p of g(o,t.fieldValues)){const v=t.fieldValues[p.name];v===void 0||v===""||v===null||(f[R(p.name)]=v)}o.name==="scribe"&&(a.style.cssText+=";"+P(t.fieldValues),f.loop=q(f.loop??!1),f.actions=[{render:a.textContent,effect:f.effect||"none"}],a.textContent="");try{const p=m[R(o.name)];if(typeof p=="function"){if(s!==C)return;b=p(a,f),i&&b?.resume()}}catch(p){console.warn("Effects preview failed:",p)}}r.addEventListener("click",()=>d(!0)),d()}function Q(e,t,o){const n=u(t.activeEffect);e.replaceChildren();const c=document.createElement("h3");c.style.cssText="margin:0;font-size:1rem;color:var(--dm-text,#eee);",c.textContent=n.label,e.appendChild(c);const r=document.createElement("p");r.style.cssText="margin:0;color:var(--dm-text-muted,#999);font-size:.8rem;line-height:1.4;",r.textContent=n.description,e.appendChild(r);const l=document.createElement("div");e.appendChild(l);const d=document.createElement("div");e.appendChild(d);const i=document.createElement("div");e.appendChild(i);const s=document.createElement("div");e.appendChild(s);function a(){J(l,t,a),y(d,t,()=>{V(i,t),L(s,t)}),V(i,t),L(s,t)}a();const m=document.createElement("div");m.style.cssText="display:flex;gap:.5rem;margin-top:auto;padding-top:.75rem;border-top:1px solid var(--dm-border,#2a2a40);";const f=document.createElement("button");f.type="button",f.className="btn btn-ghost",f.textContent="Cancel",f.addEventListener("click",()=>o.close()),m.appendChild(f);const p=document.createElement("button");p.type="button",p.className="btn btn-primary",p.style.flex="2",p.textContent=t.editing?"Update":"Insert effect",p.addEventListener("click",()=>Y(t,o)),m.appendChild(p),e.appendChild(m)}function Y(e,t){const o=e.textarea,n=A(e);if(e.editing){const c=u(e.editing.name),r=o.value,l=`[/${c.name}]`.length,d=e.editing.openTagEnd,i=e.editing.blockEnd-l,s=r.substring(d,i),a=z(e,c),m=a?" ":"",f=c.selfClosing?`[${c.name}${m}${a} /]`:`[${c.name}${m}${a}]${s}[/${c.name}]`;T(o,e.editing.blockStart,e.editing.blockEnd,f);const p=c.selfClosing?e.editing.blockStart+f.length:e.editing.blockStart+f.indexOf("]")+1;o.setSelectionRange(p,p),t.close();return}if(e.scope==="inject"&&e.host){const c=u(e.activeEffect),r=e.host.attrs,l=c.name,d=[];for(const s of Object.keys(r)){if(s===l||s.startsWith(l+"-"))continue;const a=r[s];d.push(a===""?s:`${s}="${attr.name==="message"?x(a):String(a).replace(/"/g,""")}"`)}d.push(l);for(const s of g(c,e.fieldValues)){const a=e.fieldValues[s.name];if(a===void 0||a===""||a===null||a===s.default)continue;const m=s.type==="checkbox"?a?"true":"false":s.name==="message"?x(a):String(a).replace(/"/g,""");d.push(`${l}-${s.name}="${m}"`)}const i=`[${e.host.name} ${d.join(" ")}]`;T(o,e.host.openTagStart,e.host.openTagEnd,i),t.close();return}M(o,n),t.close()}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isPathAllowed as m}from"./admin-scope.js";export const MAIL_PLUGIN="email-pro",MAIL_ROUTE=`#/plugins/${MAIL_PLUGIN}`;const d=2e3;export const MAILTO_LIMIT=1800;export function looksLikeAddress(t){return typeof t=="string"&&/^[^\s@<>,;"]+@[^\s@<>,;"]+$/.test(t)}export function recipientList(t){const e=new Set,n=[],o=(Array.isArray(t)?t:t==null?[]:[t]).flatMap(c=>typeof c=="string"?c.split(/[,;]/).map(f):[c]);for(const c of o){const s=String(typeof c=="string"?c:c?.address??c?.email??"").trim();if(!looksLikeAddress(s))continue;const r=s.toLowerCase();if(e.has(r))continue;e.add(r);const a=typeof c=="object"&&c?String(c.name??"").replace(/[\r\n<>",;]+/g," ").replace(/\s+/g," ").trim():"";n.push({name:a,address:s})}return n}function f(t){const e=String(t).trim().match(/^(.*?)\s*<([^<>]+)>$/);return e?{name:e[1].replace(/^["']|["']$/g,""),address:e[2].trim()}:String(t).trim()}function h(t){return t.map(e=>e.name?`${e.name} <${e.address}>`:e.address).join(", ")}function l(t){return recipientList(String(t??""))}function u(t,{oneLine:e=!1}={}){let n=String(t??"");return e&&(n=n.replace(/[\r\n]+/g," ")),n.slice(0,d).trim()}export function composeRequest({to:t,cc:e,bcc:n,subject:o,body:c}={}){return{to:recipientList(t),cc:recipientList(e),bcc:recipientList(n),subject:u(o,{oneLine:!0}),body:u(c)}}export function composeHash(t){const e=composeRequest(t),n=new URLSearchParams({compose:"1"});for(const o of["to","cc","bcc"])e[o].length&&n.set(o,h(e[o]));return e.subject&&n.set("subject",e.subject),e.body&&n.set("body",e.body),`${MAIL_ROUTE}?${n}`}export function readComposeRequest(t){const e=String(t??"").match(/^#\/plugins\/[\w-]+\?(.*)$/);if(!e)return null;const n=new URLSearchParams(e[1]);return n.get("compose")!=="1"?null:composeRequest({to:l(n.get("to")),cc:l(n.get("cc")),bcc:l(n.get("bcc")),subject:n.get("subject")??"",body:n.get("body")??""})}export function mailtoHref(t){const e=composeRequest(t),n=c=>c.map(s=>encodeURIComponent(s.address).replace(/%40/g,"@")).join(","),o=[];return e.cc.length&&o.push(`cc=${n(e.cc)}`),e.bcc.length&&o.push(`bcc=${n(e.bcc)}`),e.subject&&o.push(`subject=${encodeURIComponent(e.subject)}`),e.body&&o.push(`body=${encodeURIComponent(e.body)}`),`mailto:${n(e.to)}${o.length?`?${o.join("&")}`:""}`}export function composeAvailable({routes:t,accounts:e,scope:n=null}={}){const o=`/plugins/${MAIL_PLUGIN}`;return Array.isArray(t)&&t.some(s=>s?.path===o)&&m(n,o)&&Array.isArray(e)&&e.length>0}export function allAddresses(t){const e=composeRequest(t);return[...e.to,...e.cc,...e.bcc].map(n=>n.address)}export function copyNotice(t){const e=composeRequest(t),n=allAddresses(e).length,o=e.bcc.length?"Bcc":"To",c=n===1?"the address":`${n} addresses`,s=[e.subject&&`the subject "${e.subject.length>60?`${e.subject.slice(0,57)}...`:e.subject}"`,e.body&&"the message"].filter(Boolean),r=s.length?` ${s.join(" and ").replace(/^./,a=>a.toUpperCase())} ${s.length>1?"weren't":"wasn't"} included.`:"";return`Copied ${c} - paste ${n===1?"it":"them"} into ${o}.${r}`}const g=1e4;let i=null;export function emailReady(){if(i&&Date.now()-i.at<g)return i.answer;const t={at:Date.now(),answer:null};return t.answer=(async()=>{const e=await b();return!e&&i===t&&(i=null),e})(),i=t,t.answer}async function b(){try{const{apiRequest:t}=await import("/admin/js/api.js"),[e,n]=await Promise.all([t("/plugins/admin-config",{method:"GET"}),t("/auth/permissions",{method:"GET"}).catch(()=>null)]),o=n?.adminScope||null;if(!composeAvailable({routes:e?.routes,accounts:[1],scope:o}))return!1;const c=await t(`/plugins/${MAIL_PLUGIN}/accounts`,{method:"GET"});return composeAvailable({routes:e.routes,accounts:c?.accounts,scope:o})}catch{return!1}}function p(t,e="info"){try{globalThis.E?.toast?.(t,{type:e})}catch{}}async function y(t){try{if(!navigator.clipboard?.writeText)throw new Error("no async clipboard");return await navigator.clipboard.writeText(t),!0}catch{try{const e=document.createElement("textarea");e.value=t,e.style.cssText="position:fixed;opacity:0",document.body.appendChild(e),e.select();const n=document.execCommand("copy");return e.remove(),n}catch{return!1}}}function w(t){const e=allAddresses(t),n=E.modal({title:`${e.length} ${e.length===1?"address":"addresses"}`,size:"md"}),o=document.createElement("div");o.className="flex flex-col gap-2";const c=document.createElement("p");c.className="text-sm",c.textContent=`Too many for your mail program to take from a link, and the browser would not copy them. Select them, copy, and paste into ${t.bcc.length?"Bcc":"To"}.`;const s=document.createElement("textarea");if(s.className="form-textarea mail-compose-addresses",s.readOnly=!0,s.rows=8,s.value=e.join(", "),o.append(c,s),t.subject){const r=document.createElement("label");r.className="form-label",r.textContent="Subject";const a=document.createElement("input");a.className="form-input",a.readOnly=!0,a.value=t.subject,o.append(r,a)}n.element.appendChild(o),n.open(),setTimeout(()=>{s.focus(),s.select()},60)}export async function openEmail(t){const e=composeRequest(t);if(!e.to.length&&!e.cc.length&&!e.bcc.length)return p("There is no valid email address to write to.","warning"),"none";if(await emailReady())return location.hash=composeHash(e),"compose";const n=mailtoHref(e);return n.length>MAILTO_LIMIT?await y(allAddresses(e).join(", "))?(p(copyNotice(e),"info"),"copied"):(w(e),"shown"):(location.href=n,"mailto")}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{onceClosed as p}from"./tool-kit.js";const u=([o,a,
|
|
1
|
+
import{onceClosed as p}from"./tool-kit.js";const u=([o,a,r])=>r===void 0?{code:o,where:"",text:a}:{code:o,where:a,text:r},e=(o,a,r="")=>({title:o,code:a,note:r||""}),t=(o,{intro:a="",head:r=null,rows:s=[],block:l="",examples:n=[],outro:i=""}={})=>({title:o,intro:a,head:(r||[]).map((d,c)=>({i:c,text:d})),rows:s.map(u),block:l,examples:n,outro:i}),m=[t("Markdown Basics",{head:["Syntax","Result"],rows:[["**bold**","bold (rendered bold)"],["_italic_","italic (rendered italic)"],["## Heading","Heading (h2)"],["[text](url)","Link"],["","Image"],["- item","Bullet list"],["`code`","Inline code"],["---","Divider"]]}),t("Keyboard Shortcuts",{rows:[["Ctrl + /","Toggle a comment around selected text or the current paragraph"],["Ctrl + B","Bold selected text"],["Ctrl + I","Italic selected text"],["Ctrl + K","Insert link dialog"],["Tab","Indent 2 spaces at cursor"],["Shift + Tab","Dedent 2 spaces on current line"],["Ctrl + X","Cut current line (no selection) or cut selection"],["Ctrl + C","Copy current line (no selection) or copy selection"],["Ctrl + V","Paste from clipboard"],["Ctrl + Z","Undo"],["Ctrl + Y","Redo"]]}),t("Tips",{rows:[['class="text-center"',"Centre text and inline elements on any shortcode"],['class="mx-auto"',"Centre a block element horizontally"],["[center]...[/center]","Shorthand wrapper for centred content"]],outro:"Most shortcodes accept a class attribute for custom styling."})],g=[t("Grid & Columns",{rows:[['cols="N"',"[grid] only","Number of columns (1-12)"],['gap="N"',"[grid], [row]","Gap between columns/rows (1-6)"],['span="N"',"[col] only","How many columns this cell spans"],['fullwidth="true"',"[grid] only","Break out of page container to span full viewport"],['class="x"',"all","Add extra CSS classes"]],examples:[e("Equal columns",`[grid cols="3" gap="4"]
|
|
2
2
|
[col]One[/col]
|
|
3
3
|
[col]Two[/col]
|
|
4
4
|
[col]Three[/col]
|
|
@@ -30,7 +30,7 @@ Any content - text, buttons, a grid or a card.
|
|
|
30
30
|
Lifted off the page.
|
|
31
31
|
[/box]`,"Title, icon and image are ignored - a box never has a header. A [card] can go inside a [box], and a [box] inside a [card].")]}),t("Spacer",{intro:"Self-closing shortcode that inserts a fixed-height blank gap.",rows:[['size="N"',"Height in px (default: 8)"],['class="..."',"Extra CSS class on the spacer div"]],examples:[e("32px gap",'[spacer size="32" /]')]}),t("Center",{intro:"Wraps content in a centred div. Works with any content including cards, grids, and text.",examples:[e("Example","[center]Centred content here[/center]",'Accepts an optional class="..." attribute for extra styling.')]}),t("Hero",{intro:"Full-width hero sections - no plugin required. Uses Domma's built-in Hero CSS component.",rows:[['title="..."',"Heading text"],['tagline="..."',"Subtitle text"],['size="sm|lg|full"',"Height variant (default: normal)"],['variant="dark|primary|gradient-blue|gradient-purple|gradient-sunset|gradient-ocean"',"Colour / gradient preset"],['image="url"',"Background image URL (adds cover mode)"],['overlay="light|dark|darker|gradient|gradient-reverse"',"Image overlay style"],['align="center|left"',"Content alignment (default: center)"],['fullwidth="true"',"Break out of the page container to span full viewport width"],['bg="..."',"Background colour CSS value"],["twinkle","Flag: adds particle overlay (requires Effects plugin)"],['twinkle-count="N"',"Number of particles"],['twinkle-colour="..."',"Particle colour CSS value"],["blobs","Flag: adds ambient blob background"],['blobs-type="..."',"Blob animation type (default: float-blobs)"],['class="..."',"Extra CSS classes"],['id="..."',"Element id"]],examples:[e("Basic hero",'[hero title="Welcome" tagline="Build something great"][/hero]'),e("Gradient with body content",`[hero title="Get Started" tagline="Everything you need." size="lg" variant="gradient-blue" align="center"]
|
|
32
32
|
Some introductory **Markdown** content here.
|
|
33
|
-
[/hero]`),e("Background image with overlay",'[hero title="Our Story" image="/media/hero.jpg" overlay="dark" size="full"][/hero]',"Combine image + overlay for text legibility over photos.")]})],
|
|
33
|
+
[/hero]`),e("Background image with overlay",'[hero title="Our Story" image="/media/hero.jpg" overlay="dark" size="full"][/hero]',"Combine image + overlay for text legibility over photos.")]})],h=[t("Cards",{examples:[e("Basic card",`[card title="Optional Title"]
|
|
34
34
|
Markdown **works** here.
|
|
35
35
|
[/card]`,"Omit title for a card with no header."),e("Icon - inline (default)",`[card title="Feature" icon="star"]
|
|
36
36
|
Icon sits left of the title in a flex row.
|
|
@@ -91,7 +91,7 @@ In active development.
|
|
|
91
91
|
[/table]`,"The inner content must be a valid GFM Markdown table. The shortcode adds Domma CSS classes and wraps in a responsive scroll container.")]}),t("Slideover",{intro:"A trigger button that opens a slide-in panel with Markdown content. No plugin required.",rows:[['title="..."',"Panel header text"],['trigger="..."','Button label (default: "Open")'],['size="sm|md|lg"',"Panel width (default: md)"],['position="right|left"',"Slide direction (default: right)"]],examples:[e("Example",`[slideover title="More Info" trigger="Read more" size="md"]
|
|
92
92
|
## Details
|
|
93
93
|
Markdown content here.
|
|
94
|
-
[/slideover]`,"Nested [card] and [grid] shortcodes work inside the slideover body.")]})],b=[t("Embedding a Form",{rows:[['name="..."',"Form slug (required). Also accepted as slug=."],['class="..."',"Extra CSS classes on the wrapper div"],['id="..."',"id attribute on the wrapper div"]],examples:[e("Basic",'[form name="contact" /]',"Forms are managed under Forms in the sidebar."),e("With wrapper styling",'[form name="newsletter" class="mt-4" id="newsletter-form" /]')]}),t("Displaying a Collection",{block:'[collection slug="enquiries" display="table" /]',rows:[["slug","Required. The collection slug."],["display","table (default), cards, or list."],["fields","Comma-separated field keys to show. Defaults to all."],["limit","Maximum number of entries to display."],["sort","Field key to sort by."],["order","asc or desc (default asc)."],["title-field","Field used as the card/list title (cards + list)."],["columns","Grid columns for cards display (2-4, default 3)."],["search","true/false - enable search in table mode (default true)."],["sortable","true/false - enable column sorting in table mode (default true)."],["exportable","true/false - show CSV export button in table mode (default false)."],["page-size","Rows per page in table mode (default 25)."],["empty","Text shown when there are no entries."],["cta","Action slug: enables per-entry CTA buttons."],["cta-label",'CTA button label (default: "Run").'],["cta-icon","CTA button icon name."],["cta-style","CTA button variant (default: primary)."],["cta-confirm","CTA confirmation prompt text."],["class","Extra CSS classes on the wrapper div."],["id","id attribute on the wrapper div."]],outro:"Table display uses Domma Table - search, sort, pagination, column selector, and CSV export are all built in. Cards and lists are static."}),t("View - Pro",{intro:"Renders a Collection with full filtering, pagination, and display options. Requires a Pro MongoDB collection.",rows:[["slug","Required. The collection slug."],["display","table (default), cards, or list."],["limit","Maximum number of entries to display."],["fields","Comma-separated field keys to show."],["title-field","Field used as the card/list title."],["columns","Grid columns for cards display (2-4, default 3)."],["empty","Text shown when there are no entries."],["class","Extra CSS classes on the wrapper div."],["id","id attribute on the wrapper div."]],examples:[e("Table display",'[view slug="products" display="table" limit="50" /]'),e("Cards display",'[view slug="team" display="cards" columns="3" title-field="name" /]')]}),t("CTA - Pro",{intro:"Renders an action button that triggers a Pro Action against an entry. Requires the Pro Actions feature.",rows:[["action","Required. The action slug."],["entry","Required. The entry id to act on."],["style","Button variant (default: primary)."],["icon","Domma icon name for the button."],["size","Button size (sm, lg)."],["confirm","Confirmation prompt text before running."],["label","Self-closing only: button label text."]],examples:[e("Wrapping form",'[cta action="approve" entry="abc123" style="success" confirm="Approve this entry?"]Approve[/cta]'),e("Self-closing",'[cta action="archive" entry="abc123" icon="archive" label="Archive" /]')]})],f=[t("Effects",{intro:"Shortcodes for scroll-triggered animations, counters, typewriter text, and more.
|
|
94
|
+
[/slideover]`,"Nested [card] and [grid] shortcodes work inside the slideover body.")]})],b=[t("Embedding a Form",{rows:[['name="..."',"Form slug (required). Also accepted as slug=."],['class="..."',"Extra CSS classes on the wrapper div"],['id="..."',"id attribute on the wrapper div"]],examples:[e("Basic",'[form name="contact" /]',"Forms are managed under Forms in the sidebar."),e("With wrapper styling",'[form name="newsletter" class="mt-4" id="newsletter-form" /]')]}),t("Displaying a Collection",{block:'[collection slug="enquiries" display="table" /]',rows:[["slug","Required. The collection slug."],["display","table (default), cards, or list."],["fields","Comma-separated field keys to show. Defaults to all."],["limit","Maximum number of entries to display."],["sort","Field key to sort by."],["order","asc or desc (default asc)."],["title-field","Field used as the card/list title (cards + list)."],["columns","Grid columns for cards display (2-4, default 3)."],["search","true/false - enable search in table mode (default true)."],["sortable","true/false - enable column sorting in table mode (default true)."],["exportable","true/false - show CSV export button in table mode (default false)."],["page-size","Rows per page in table mode (default 25)."],["empty","Text shown when there are no entries."],["cta","Action slug: enables per-entry CTA buttons."],["cta-label",'CTA button label (default: "Run").'],["cta-icon","CTA button icon name."],["cta-style","CTA button variant (default: primary)."],["cta-confirm","CTA confirmation prompt text."],["class","Extra CSS classes on the wrapper div."],["id","id attribute on the wrapper div."]],outro:"Table display uses Domma Table - search, sort, pagination, column selector, and CSV export are all built in. Cards and lists are static."}),t("View - Pro",{intro:"Renders a Collection with full filtering, pagination, and display options. Requires a Pro MongoDB collection.",rows:[["slug","Required. The collection slug."],["display","table (default), cards, or list."],["limit","Maximum number of entries to display."],["fields","Comma-separated field keys to show."],["title-field","Field used as the card/list title."],["columns","Grid columns for cards display (2-4, default 3)."],["empty","Text shown when there are no entries."],["class","Extra CSS classes on the wrapper div."],["id","id attribute on the wrapper div."]],examples:[e("Table display",'[view slug="products" display="table" limit="50" /]'),e("Cards display",'[view slug="team" display="cards" columns="3" title-field="name" /]')]}),t("CTA - Pro",{intro:"Renders an action button that triggers a Pro Action against an entry. Requires the Pro Actions feature.",rows:[["action","Required. The action slug."],["entry","Required. The entry id to act on."],["style","Button variant (default: primary)."],["icon","Domma icon name for the button."],["size","Button size (sm, lg)."],["confirm","Confirmation prompt text before running."],["label","Self-closing only: button label text."]],examples:[e("Wrapping form",'[cta action="approve" entry="abc123" style="success" confirm="Approve this entry?"]Approve[/cta]'),e("Self-closing",'[cta action="archive" entry="abc123" icon="archive" label="Archive" /]')]})],f=[t("Effects",{intro:"Shortcodes for scroll-triggered animations, counters, typewriter text, and more. Built into Domma CMS; configure site-wide defaults in System > Effects.",rows:[['[reveal animation="fade"]...[/reveal]',"Fade/slide/zoom on scroll"],["[breathe]...[/breathe]","Continuous gentle scale loop"],["[pulse]...[/pulse]","Repeating scale pulse"],["[shake]...[/shake]","One-shot shake"],['[scribe mode="word" effect="bounce" speed="140" loop="3" cursor-blink="false" controls="true"]...[/scribe]',"Scribe - characters, words, or sentences; effects, counted loops, cursor and playback settings"],['[scribe loop="true"][render]Text[/render][wait]1500[/wait][undo /][/scribe]',"Typewriter - script mode (sequenced render/wait/undo actions)"],["[scramble]...[/scramble]","Character-scramble reveal"],['[counter to="100" /]',"Animated number counter (self-closing)"],["[ripple]...[/ripple]","Click-triggered ripple"],['[twinkle count="50"]...[/twinkle]',"Particle/star overlay"],['[animate type="fade-in-up"]...[/animate]',"CSS-only animation (no plugin needed)"],['[ambient type="aurora"]...[/ambient]',"CSS-only animated background"],['[firework type="burst" colour="rainbow" /]',"CSS firework (self-closing)"],["[fireworks]...[/fireworks]","Fireworks display container"],['[celebrate theme="auto" /]',"Seasonal canvas celebration (auto-detects date)"],["[christmas /]","Christmas - snowflakes, trees, snowmen, sleigh"],["[new-year /]","Sparkler greeting, confetti, streamers, fireworks, balloons and a midnight clock"],["[chinese-new-year /]","Chinese dragon, red lanterns, fireworks, confetti and a sparkler greeting"],['sparkler-message="off"',"Hide the lettering on any scene with a sparkler greeting"],['sparkler-style="cursive" sparkler-colour="silver"',"Cursive Latin lettering with silver sparks (defaults: stroke and gold)"],['sparkler-greeting="best-wishes" sparkler-year="2027"',"Write Best wishes for 2027! in either style and colour"],["[easter /]","Blossom, decorated eggs, a bunny, flowers and butterflies"],["[summer-garden /]","Petals, flowers, butterflies, bumblebees and ladybirds"],["[winter /]","Snow, frosted pines, snowdrifts, icicles and winter stars"],["[autumn /]","Falling leaves, acorns and mushrooms"],['[graduation message="Congratulations, Chlo\xE9!" /]',"Tossed mortarboards, diplomas, confetti and your greeting"],['[birthday message="Happy Birthday, Sarah!" /]',"Cake, balloons, confetti and your greeting"],['[anniversary message="Happy Anniversary!" /]',"Rings, hearts, ribbons and your greeting"],['[celebrate message="Best wishes for {year}!" sparkler-year="2027" /]',"Standalone sparkler lettering; custom messages allow 120 characters"],['[money-rain duration="8000" /]',"Illustrated banknotes, separate from actual weather rain"],['[make-it-rain intensity="heavy" duration="8000" /]',"Weather rain and ground splashes; duration 0 keeps raining"],["[halloween /]","Halloween - pumpkins, ghosts, bats, spiders"],["[st-patricks /]","St Patrick's Day - shamrocks, coins, rainbows, leprechaun"],["[valentines /]","Valentine's Day - hearts, roses"],["[guy-fawkes /]","Guy Fawkes - fireworks, sparkles"],["[st-andrews /]","St Andrew's Day - heather, thistles"],["[st-davids /]","St David's Day - daffodils, dragons"],["[st-georges /]","St George's Day - roses, flags"]],examples:[e("Scribe script - looping multi-phrase typewriter",`[scribe loop="true" loop-delay="2000" delete-speed="20"]
|
|
95
95
|
[render]We build things that matter.[/render]
|
|
96
96
|
[wait]1200[/wait]
|
|
97
97
|
[undo all="true" /]
|
|
@@ -100,7 +100,7 @@ Markdown content here.
|
|
|
100
100
|
[undo all="true" /]
|
|
101
101
|
[/scribe]`,'Use [render]...[/render] to type text, [wait]Ms[/wait] to pause, and [undo /] to delete. loop="true" replays the sequence. Only [render], [wait], and [undo] shortcodes are parsed - plain text between actions is ignored.'),e("Christmas",'[christmas intensity="medium" snowmen="off" trees="off" wreaths="off" northStars="off" sleigh="off" train="off" elf="off" robin="off" firework="off" /]',"intensity: light | medium | heavy. Count-based (off disables entirely): snowmen, trees, wreaths, northStars. Probability-based specials (off prevents spawning): sleigh, train, elf, robin, firework. Snowflakes always fall."),e("Halloween",'[halloween intensity="medium" gravestones="off" jackOLanterns="off" spiders="off" scarecrows="off" twinklingStars="off" /]',"intensity: light | medium | heavy. Decoration toggles (off disables): gravestones, jackOLanterns, spiders, scarecrows, twinklingStars. Particles: bats, ghosts, pumpkins (always on). Fixed decorations: haunted-house, cauldron (always present)."),e("St Patrick's Day",'[st-patricks intensity="medium" pots="off" twinklingStars="off" /]',"intensity: light | medium | heavy. Decoration toggles (off disables): pots (pots of gold), twinklingStars. Particles: shamrocks, clover petals, gold coins, sparkles (always on). Fixed decorations: rainbow, harp, moon. Leprechaun appears randomly - more likely at higher intensity."),e("Valentine's Day",'[valentines intensity="medium" garlands="off" butterflies="off" /]',"intensity: light | medium | heavy. Decoration toggles (off disables): garlands (heart garlands), butterflies. Particles: hearts, rose petals, sparkles, lips (always on). Fixed decorations: envelopes (always present). Cupid appears randomly - more likely at higher intensity."),e("Guy Fawkes Night",'[guy-fawkes intensity="medium" bonfires="off" /]',"intensity: light | medium | heavy. Decoration toggles (off disables): bonfires. Particles: embers, fireworks, sparks, rockets, bursts, trails (always on). Fixed decorations: guy-effigy, catherine-wheel, roman-candle, sparkler-bundle, moon. Starts at 25% particle density and builds up gradually."),e("St Andrew's Day",'[st-andrews intensity="medium" thistles="off" twinklingStars="off" /]',"intensity: light | medium | heavy. Decoration toggles (off disables): thistles, twinklingStars. Particles: heather petals, saltire sparkles (always on). Fixed decorations: bagpiper, saltire-flag, tartan-pattern, highland-scene (always present). Bagpiper probability increases with intensity."),e("St David's Day",'[st-davids intensity="medium" daffodilFields="off" twinklingStars="off" /]',"intensity: light | medium | heavy. Decoration toggles (off disables): daffodilFields, twinklingStars. Particles: daffodil petals, spring sparkles (always on). Fixed decorations: welsh-dragon, harp, leek-bundle, flag (always present). Flying dragon appears randomly - more likely at higher intensity."),e("St George's Day",'[st-georges intensity="medium" roses="off" twinklingStars="off" /]',"intensity: light | medium | heavy. Decoration toggles (off disables): roses (English roses), twinklingStars. Particles: rose petals, Tudor roses, oak leaves, sparkles (always on). Fixed decorations: st-georges-cross, dragon, shield, castle (always present). Knight appears randomly - more likely at higher intensity.")],outro:"Enable the Domma Effects plugin to activate these shortcodes on your site."}),t("DConfig - Declarative Behaviour",{intro:"Define click handlers and class toggles without writing JavaScript. Use the DConfig section above or embed inline with [dconfig]...[/dconfig] in the content body. Inline shortcodes win on selector conflict.",examples:[e("Toggle a class on click",'{ "#my-btn": { "events": { "click": { "target": "#panel", "toggleClass": "hidden" } } } }','Selector keys use standard CSS selectors. "target" is optional - defaults to the element itself.'),e("Inline shortcode syntax",`[dconfig]
|
|
102
102
|
{ "#my-btn": { "events": { "click": { "target": "#panel", "toggleClass": "hidden" } } } }
|
|
103
|
-
[/dconfig]`)]}),t("CSS Editor",{intro:"The toolbar's </> button (left of the Split View icon) swaps the left pane between Markdown and Custom CSS. Edits are saved together with the page - one Save, one toast.",rows:[["</> button","Toggle between Markdown editor and Custom CSS editor"],["Split / Write / Preview","Standard view mode buttons to the right of </>"],["Save","Saves both page content and CSS in a single operation"]]}),t("Colour Picker",{intro:"Insert \u2192 Colour Picker opens a centred colour picker. Pick a colour, then use Copy to copy the hex code to the clipboard or Insert to drop it at the cursor. Click outside the panel to dismiss."})];export const REFERENCE=[{id:"basics",label:"Basics",sections:m},{id:"layout",label:"Layout",sections:
|
|
103
|
+
[/dconfig]`)]}),t("CSS Editor",{intro:"The toolbar's </> button (left of the Split View icon) swaps the left pane between Markdown and Custom CSS. Edits are saved together with the page - one Save, one toast.",rows:[["</> button","Toggle between Markdown editor and Custom CSS editor"],["Split / Write / Preview","Standard view mode buttons to the right of </>"],["Save","Saves both page content and CSS in a single operation"]]}),t("Colour Picker",{intro:"Insert \u2192 Colour Picker opens a centred colour picker. Pick a colour, then use Copy to copy the hex code to the clipboard or Insert to drop it at the cursor. Click outside the panel to dismiss."})];export const REFERENCE=[{id:"basics",label:"Basics",sections:m},{id:"layout",label:"Layout",sections:g},{id:"components",label:"Components",sections:h},{id:"data",label:"Data",sections:b},{id:"advanced",label:"Advanced",sections:f}];const y=`
|
|
104
104
|
<div class="per">
|
|
105
105
|
<div class="per-tabs" role="tablist" data-each="tabs key=id">
|
|
106
106
|
<button type="button" class="per-tab" role="tab" data-on-click="$root.show(id)"
|
|
@@ -166,4 +166,4 @@ pre.per-block { padding-right: .75rem; margin-bottom: .5rem; }
|
|
|
166
166
|
.per-copy { position: absolute; top: .3rem; right: .3rem; background: none; border: none; cursor: pointer; opacity: .45; padding: .15rem;
|
|
167
167
|
line-height: 1; color: inherit; }
|
|
168
168
|
.per-copy:hover, .per-copy:focus-visible { opacity: 1; }
|
|
169
|
-
</style>`;async function
|
|
169
|
+
</style>`;async function w(o){try{return await navigator.clipboard.writeText(o),!0}catch{const a=document.createElement("textarea");a.value=o,a.style.cssText="position:fixed;opacity:0;",document.body.appendChild(a),a.select();let r=!1;try{r=document.execCommand("copy")}catch{}return a.remove(),r}}export function openEditorReference(){const o=E.slideover({title:"Editor Reference",size:"md",position:"right"}),a=document.createElement("div");a.innerHTML=y;const r=M.observable("basics"),s=M.applyBindings({tabs:REFERENCE,tab:r,show:n=>{r.value=n},copy:async n=>{const i=await w(n);E.toast(i?"Copied!":"Could not copy - select the code and copy it.",{type:i?"success":"warning",duration:1200})}},a);o.element.appendChild(a);const l=M.effect(()=>{r.value,requestAnimationFrame(()=>I.scan(a))});p(o,()=>{try{l()}catch{}try{s.dispose()}catch{}}),o.open()}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{encodeMessageAttribute as p,decodeMessageAttribute as g}from"../../../public/js/celebrations/message-attrs.mjs";import{replaceRange as d}from"./markdown-toolbar.js";export function parseAttrs(n){const t=String(n??""),r={};for(const[,f,o,e]of t.matchAll(/([\w-]+)=(?:"([^"]*)"|'([^']*)')/g))r[f]=f==="message"?g(o??e??""):o??e??"";const c=t.replace(/([\w-]+)=(?:"[^"]*"|'[^']*')/g,f=>" ".repeat(f.length));for(const[,f]of c.matchAll(/\b([\w-]+)\b/g))f in r||(r[f]=!0);return r}export function serialiseTag(n,t,r){const c=[];for(const[o,e]of Object.entries(t))if(e===!0)c.push(o);else{if(e===!1||e===null||e===void 0||e==="")continue;c.push(`${o}="${o==="message"?p(e):String(e).replace(/"/g,""")}"`)}const f=c.length?` ${c.join(" ")}`:"";return r?`[${n}${f} /]`:`[${n}${f}]`}function a(n,t,r,c){const f=n.slice(t,r),o=/\/\s*\]$/.test(f),e=f.match(new RegExp(`^\\[${c}([\\s\\S]*?)\\s*/?\\]$`)),s=e?e[1]:"";return{name:c,openTagStart:t,openTagEnd:r,attrStr:s,attrs:parseAttrs(s),selfClosing:o}}function u(n,t,r){const c=new RegExp(`\\[${r}(?=[\\s\\]/])([^\\]]*)\\]`,"g"),f=new RegExp(`\\[/${r}\\]`,"g"),o=[];for(const l of n.matchAll(c)){if(l.index>=t)break;o.push({type:"open",start:l.index,end:l.index+l[0].length})}for(const l of n.matchAll(f)){if(l.index>=t)break;o.push({type:"close",start:l.index})}o.sort((l,i)=>l.start-i.start);let e=0,s=null;for(const l of o)l.type==="open"?(e++,s=l):(e--,e<0&&(e=0,s=null));return e>0&&s&&new RegExp(`\\[/${r}\\]`).test(n.slice(t))?{start:s.start,end:s.end}:null}export function findEnclosingShortcode(n,t){const r=n.value,c=n.selectionStart,f=new Map(t.map(s=>[s.name,s]));let o=null;for(const s of r.matchAll(/\[(\/)?([a-zA-Z][\w-]*)((?:[^\]]*?))\]/g)){const l=s.index,i=l+s[0].length;c<l||c>i||f.has(s[2])&&(o={name:s[2],start:l,end:i,close:!!s[1]})}if(o)if(o.close){const s=u(r,o.start,o.name);if(s)return a(r,s.start,s.end,o.name)}else return a(r,o.start,o.end,o.name);let e=null;for(const s of t){if(s.selfClosing)continue;const l=u(r,c,s.name);l&&(!e||l.start>e.start)&&(e={...l,name:s.name})}return e?a(r,e.start,e.end,e.name):null}export function setAttr(n,t,r,c){return setAttrs(n,t,{[r]:c})}export function setAttrs(n,t,r){const c={...t.attrs};for(const[s,l]of Object.entries(r))l===!1||l===null||l===void 0||l===""?delete c[s]:c[s]=l;const f=serialiseTag(t.name,c,t.selfClosing),o=typeof document<"u"?document.activeElement:null;d(n,t.openTagStart,t.openTagEnd,f),o&&o!==n&&typeof o.focus=="function"&&o.focus();const e=t.openTagStart+f.length;return a(n.value,t.openTagStart,e,t.name)}export function effectAttrKeys(n,t){const r=`${t}-`;return Object.keys(n).filter(c=>c===t||c.startsWith(r))}export function boolState(n,t){return n==null?!!t:!(n===!1||n==="false"||n==="0")}export function boolValue(n,t){return t===!!n.default?null:t?"true":"false"}
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
<input id="cnx-link" class="form-input cnx-link" readonly data-bind-value="joinUrl.value" data-on-focus="selectLink">
|
|
52
52
|
<div class="cnx-actions is-start">
|
|
53
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>
|
|
54
|
+
<a class="btn btn-sm btn-ghost" data-bind-href="mailto.value" data-on-click="emailIt"><span data-icon="mail" data-icon-size="13"></span> Email it…</a>
|
|
55
55
|
<span class="cnx-spacer"></span>
|
|
56
56
|
<button type="button" class="btn btn-sm btn-ghost" data-on-click="close">Done</button>
|
|
57
57
|
</div>
|
|
@@ -950,6 +950,45 @@ Body content in **Markdown** is supported.
|
|
|
950
950
|
<code>[christmas /]</code>; <code>[christmas snow-only="true" /]</code> is just the falling snow). Many can also be added to a card, box, hero, banner, column or
|
|
951
951
|
button as attributes: <code>[card reveal reveal-animation="zoom"]</code>. Settings are at
|
|
952
952
|
<strong>System > Effects</strong>.</p>
|
|
953
|
+
<h3>Celebrations, seasons and occasions</h3>
|
|
954
|
+
<p>Every site includes 20 celebration scenes. In the page editor, use
|
|
955
|
+
<strong>Insert > Effects > Celebrate</strong> or right-click and open
|
|
956
|
+
<strong>Celebrations</strong>. Select a theme, intensity and the decorations you want.
|
|
957
|
+
Right-click an existing shortcode to change its settings. Use <code>falling-only="true"</code>
|
|
958
|
+
for particles without the fixed decorations.</p>
|
|
959
|
+
<p><code>theme="auto"</code> follows the visitor's local calendar. Seasonal themes are
|
|
960
|
+
<code>christmas</code>, <code>new-year</code>, <code>chinese-new-year</code>,
|
|
961
|
+
<code>easter</code>, <code>summer-garden</code>, <code>autumn</code>, <code>winter</code>,
|
|
962
|
+
<code>halloween</code>, <code>valentines</code>, <code>guy-fawkes</code>,
|
|
963
|
+
<code>st-patricks</code>, <code>st-andrews</code>, <code>st-davids</code> and <code>st-georges</code>.
|
|
964
|
+
Specific holidays take priority over Autumn and Winter. Easter and Chinese New Year move
|
|
965
|
+
with their calendars. Any theme can be selected manually throughout the year.</p>
|
|
966
|
+
<p>The manual scenes are <code>graduation</code> (tossed caps and diplomas),
|
|
967
|
+
<code>birthday</code> (cake and balloons), <code>anniversary</code> (rings and hearts),
|
|
968
|
+
<code>greeting</code> (lettering alone), <code>make-it-rain</code> (actual water and splashes)
|
|
969
|
+
and <code>money-rain</code> (illustrated banknotes). Summer Garden reuses the illustrated
|
|
970
|
+
butterflies and has bees visiting flowers; Easter's bunny hops between rests.</p>
|
|
971
|
+
<pre><code>[graduation message="Congratulations, Chloé!" sparkler-style="cursive" sparkler-colour="silver" /]
|
|
972
|
+
[birthday message="Happy Birthday, Sarah!" /]
|
|
973
|
+
[anniversary message="Happy Anniversary, Mum & Dad!" /]
|
|
974
|
+
[celebrate message="Best wishes for {year}!" sparkler-year="2027" /]
|
|
975
|
+
[make-it-rain intensity="heavy" duration="8000" /]
|
|
976
|
+
[money-rain duration="4000" /]</code></pre>
|
|
977
|
+
<p><strong>Personalise the sky writing:</strong> choose <code>stroke</code> or <code>cursive</code>
|
|
978
|
+
lettering and <code>gold</code> or <code>silver</code> sparks. <code>message</code> accepts
|
|
979
|
+
up to 120 characters, including names, punctuation and Latin accents. It replaces the
|
|
980
|
+
default greeting on Christmas, New Year, Chinese New Year and the occasion scenes.
|
|
981
|
+
Without a theme it selects standalone lettering; in the editor choose <strong>Sparkler greeting</strong>.
|
|
982
|
+
<code>{year}</code> inserts <code>sparkler-year</code>, which defaults to the current year.</p>
|
|
983
|
+
<p>For a preset, use <code>sparkler-greeting="best-wishes" sparkler-year="2027"</code>.
|
|
984
|
+
Omit it for Merry Christmas, Happy New Year, <span lang="zh">新年快乐</span>, or the
|
|
985
|
+
occasion's default message. <code>sparkler-message="off"</code> hides the lettering.
|
|
986
|
+
In hand-written messages, encode quotes as <code>&quot;</code> and square brackets as
|
|
987
|
+
<code>&#91;</code> and <code>&#93;</code>; the editor handles this for you.</p>
|
|
988
|
+
<p>Both rain effects accept <code>duration</code> in milliseconds: default 8000, maximum 600000,
|
|
989
|
+
or 0 to keep running until the effect is destroyed. Effects respect the site's reduced-motion
|
|
990
|
+
setting, stay transparent to clicks and remove their canvases when stopped.</p>
|
|
991
|
+
|
|
953
992
|
<p><code>[celebrate character="robin" /]</code> puts one character on a page at any time of year,
|
|
954
993
|
whatever the theme or date: <code>elf</code>, <code>leprechaun</code>, <code>bagpiper</code>,
|
|
955
994
|
<code>knight</code>, <code>dragon</code>, <code>welsh-dragon</code>, <code>cupid</code>,
|
|
@@ -1 +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
|
|
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";import{mailtoHref as z,openEmail as F}from"../lib/mail-compose.js";const K=/^#\/calls\/new\/?(\?.*)?$/,H=/^[^\s@<>"]+@[^\s@<>"]+\.[^\s@<>"]+$/,y=Y(K);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,l){if(!t||t.closed)return!1;try{t.opener=null}catch{}return t.location.href=l,!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){y.reset();const l=t.get(0).querySelector(".cnx")||t.get(0),g=new URLSearchParams(location.hash.split("?")[1]||""),w=M.observable(!0),L=M.observable(""),s=M.observable(String(g.get("name")||"").slice(0,120)),i=M.observable(String(g.get("email")||"").slice(0,200)),r=M.observable(""),u=M.observable(""),c=M.observable(!0),m=M.observable(!1),k=M.observable(""),d=M.observable([]),S=M.observable(!1),h=M.observable(!1),v=M.observable(!1),b=M.observable(""),j=M.computed(()=>H.test(String(i.value||"").trim())),T=M.observable(!1),I=M.observable(!1),O=M.observable(""),x=M.observable(""),P=M.observable(""),N=M.observable("");let f=null,A=0,C=0;const q=M.effect(()=>{const e=String(k.value||"").trim();clearTimeout(C);const U=++A;if(e.length<2){d.value=[],S.value=!1;return}C=setTimeout(async()=>{try{const o=await p(`/calls/contacts?q=${encodeURIComponent(e)}`,{method:"GET"});if(U!==A)return;const a=(Array.isArray(o)?o:[]).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,S.value=a.length===0}catch(o){(o?.status===403||o?.status===404)&&(h.value=!1),d.value=[]}},200)});y.add(()=>{clearTimeout(C);try{typeof q=="function"?q():q?.dispose?.()}catch{}});const B={loading:w,unavailable:L,name:s,email:i,contactId:r,contactName:u,mailReady:c,notify:m,q:k,matches:d,noMatch:S,contactsOn:h,busy:v,err:b,hasEmail:j,started:T,blocked:I,hostUrl:O,joinUrl:x,emailLine:P,mailto:N,pick(e){r.value=e.id,u.value=e.name,s.value=e.name==="(no name)"?"":e.name,i.value=e.email,m.value=!!e.email&&c.peek(),k.value=""},clearContact(){r.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(x.peek()),E.toast("Their link is copied",{type:"success",duration:1500})}catch{l.querySelector(".cnx-link")?.select?.(),E.toast("Copy it from the box (Ctrl+C)",{type:"info"})}},emailIt(e){f?.to.length&&(e?.preventDefault?.(),F(f))},selectLink:e=>{try{e?.target?.select?.()}catch{}},save(){if(v.peek()||T.peek())return;const e={name:String(s.peek()||"").trim(),email:String(i.peek()||"").trim()};if(e.email&&!H.test(e.email)){b.value="That email address does not look right.";return}const U=m.peek()===!0&&!!e.email&&c.peek();b.value="";const o=openHostTab();v.value=!0,p("/calls",{method:"POST",body:JSON.stringify({...e,contactId:r.peek(),notify:U})}).then(a=>{const n=new URL(a.hostUrl,location.origin).href,D=aimTab(o,n);O.value=n,x.value=a.joinUrl,I.value=!D;const $=e.name||e.email||"them";P.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.",f={to:e.email?[{name:e.name,address:e.email}]:[],subject:"Join the call",body:`Join the call: ${a.joinUrl}`},N.value=z(f),T.value=!0,setTimeout(()=>l.querySelector(".cnx-link")?.focus(),60)}).catch(a=>{try{o?.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})}};y.add(V(l)),window.I?.scan?.(l);const J=Object.fromEntries(Object.entries(B).filter(([,e])=>typeof e=="function"&&!M.isObservable?.(e))),_=M.applyBindings(B,l,{methods:J});y.add(()=>{try{_?.dispose?.()}catch{}});try{const e=await p("/calls/status",{method:"GET"});c.value=e?.mailReady!==!1,h.value=e?.contacts===!0}catch(e){w.value=!1,L.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 G=g.get("contact");if(G)try{const e=await p(`/calls/contact/${encodeURIComponent(G)}`,{method:"GET"});r.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=j.peek()&&c.peek(),w.value=!1,setTimeout(()=>l.querySelector(r.peek()||s.peek()?"#cnx-email":h.peek()?"#cnx-q":"#cnx-name")?.focus(),60)}};
|