domma-cms 1.6.33 → 1.6.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/js/api.js +1 -1
- package/admin/js/lib/effect-defs.js +1 -1
- package/admin/js/lib/effects-builder.js +1 -1
- package/admin/js/lib/pages-arrange.js +1 -1
- package/admin/js/lib/scribe-composer.js +1 -1
- package/admin/js/lib/scribe-model.js +1 -1
- package/admin/js/lib/shortcode-preview.js +1 -1
- package/admin/js/lib/shortcode-registry.js +3 -3
- package/admin/js/templates/docs/usage-site-settings.html +2 -1
- package/admin/js/templates/page-editor.html +2 -1
- package/admin/js/templates/pages.html +1 -1
- package/admin/js/templates/theme.html +1 -1
- package/admin/js/views/pages.js +3 -3
- package/admin/js/views/theme.js +5 -5
- package/package.json +1 -1
- package/plugins/_lib/outreach/audience.js +76 -0
- package/plugins/_lib/outreach/collections/outreach-contacts.schema.json +12 -0
- package/plugins/_lib/outreach/collections/outreach-groups.schema.json +10 -0
- package/plugins/_lib/outreach/collections/outreach-unsubscribes.schema.json +11 -0
- package/plugins/_lib/outreach/expiry.js +46 -0
- package/plugins/_lib/outreach/index.js +15 -0
- package/plugins/_lib/outreach/ledger.js +127 -0
- package/plugins/_lib/outreach/sending.js +121 -0
- package/plugins/_lib/outreach/suppression.js +62 -0
- package/plugins/_lib/outreach/tokens.js +35 -0
- package/plugins/_lib/outreach/unsubscribe.js +46 -0
- package/plugins/free-tier.lock.json +35 -24
- package/plugins/shopping-cart/CLAUDE.md +64 -0
- package/plugins/shopping-cart/admin/css/index.css +16 -0
- package/plugins/shopping-cart/admin/lib/chart.js +64 -0
- package/plugins/shopping-cart/admin/lib/kit.js +15 -0
- package/plugins/shopping-cart/admin/views/orders.js +3 -2
- package/plugins/shopping-cart/admin/views/overview.js +4 -53
- package/plugins/shopping-cart/admin/views/sales.js +133 -0
- package/plugins/shopping-cart/admin/views/settings.js +3 -1
- package/plugins/shopping-cart/admin/views/shop.js +2 -1
- package/plugins/shopping-cart/config.js +1 -0
- package/plugins/shopping-cart/docs/guide.md +25 -0
- package/plugins/shopping-cart/inject/body.html +2 -2
- package/plugins/shopping-cart/lib/accounts.js +180 -0
- package/plugins/shopping-cart/lib/commission.js +90 -0
- package/plugins/shopping-cart/lib/extensions.js +12 -4
- package/plugins/shopping-cart/lib/pricing.js +65 -13
- package/plugins/shopping-cart/lib/render.js +17 -4
- package/plugins/shopping-cart/lib/sales.js +193 -0
- package/plugins/shopping-cart/lib/shop.js +24 -3
- package/plugins/shopping-cart/lib/summary.js +4 -2
- package/plugins/shopping-cart/plugin.js +156 -22
- package/plugins/shopping-cart/plugin.json +2 -2
- package/plugins/shopping-cart/plugin.public.js +17 -1
- package/plugins/shopping-cart/public/account.js +277 -0
- package/plugins/shopping-cart/public/cart.js +16 -0
- package/plugins/shopping-cart/public/lib/session.js +82 -0
- package/plugins/shopping-cart/public/shop.css +27 -0
- package/plugins/shopping-cart/roles/shop-customer.json +8 -0
- package/plugins/shopping-cart/tests/accounts.test.js +127 -0
- package/plugins/shopping-cart/tests/api.test.js +63 -0
- package/plugins/shopping-cart/tests/commission.test.js +90 -0
- package/plugins/shopping-cart/tests/pure.test.js +30 -1
- package/plugins/shopping-cart/tests/sales.test.js +73 -0
- package/public/css/site.css +1 -1
- package/public/js/effects.js +1 -1
- package/public/js/scribe-style.mjs +1 -1
- package/public/js/shortcode-expiry.mjs +1 -0
- package/public/js/site.js +1 -1
- package/public/js/text-style.mjs +1 -1
- package/server/routes/api/theme.js +12 -2
- package/server/routes/outreach-public.js +68 -0
- package/server/routes/public.js +24 -1
- package/server/server.js +5 -0
- package/server/services/content.js +7 -7
- package/server/services/markdown.js +15 -5
- package/server/services/outreachUnsubscribe.js +54 -0
- package/server/services/shortcodeExpiry.js +146 -0
- package/server/templates/page.html +3 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const SHORTCODES=[{name:"card",label:"Card",icon:"card",category:"Layout",selfClosing:!1,container:!0,wrappable:!0,template:`[card title="Title"]
|
|
1
|
+
import{SHORTCODE_EXPIRY_FIELDS as l}from"../../../public/js/shortcode-expiry.mjs";export const SHORTCODES=[{name:"card",label:"Card",icon:"card",category:"Layout",selfClosing:!1,container:!0,wrappable:!0,template:`[card title="Title"]
|
|
2
2
|
Content
|
|
3
3
|
[/card]`,custom:"card",attrs:[{name:"title",type:"text",label:"Title"},{name:"subtitle",type:"text",label:"Subtitle"},{name:"icon",type:"icon",label:"Icon"},{name:"span",type:"select",label:"Span (in a grid)",options:[["","1 column"],["2","2 columns"],["3","3 columns"],["4","4 columns"],["5","5 columns"],["6","6 columns"],["full","Full row"]]},{name:"icon-layout",type:"select",label:"Icon layout",options:[["inline","Inline"],["stacked","Stacked"]]},{name:"icon-size",type:"text",label:"Icon size (xs, sm, md, lg, xl, 2xl or 48px / 3rem)"},{name:"icon-align",type:"select",label:"Icon alignment",options:[["","Default"],["left","Left"],["center","Centre"],["right","Right"]]},{name:"footer",type:"text",label:"Footer"},{name:"variant",type:"select",label:"Variant",options:[["","Default"],["primary","Primary"]]},{name:"collapsible",type:"checkbox",label:"Collapsible"},{name:"hover",type:"checkbox",label:"Hover highlight"},{name:"class",type:"text",label:"CSS class"},{name:"id",type:"text",label:"ID"}]},{name:"box",label:"Box",icon:"square",category:"Layout",selfClosing:!1,container:!0,wrappable:!0,template:`[box]
|
|
4
4
|
Content
|
|
@@ -33,7 +33,7 @@ Banner content
|
|
|
33
33
|
[/accordion]`,children:{name:"item",min:2,max:12},attrs:[{name:"multiple",type:"checkbox",label:"Allow multiple open"},{name:"id",type:"text",label:"ID"}]},{name:"item",label:"Item",icon:"list",category:"Components",child:!0,selfClosing:!1,container:!0,wrappable:!1,template:'[item title="Item title"]Content[/item]',inline:!0,attrs:[{name:"title",type:"text",label:"Title"}]},{name:"carousel",label:"Carousel",icon:"carousel-insert",category:"Components",selfClosing:!1,container:!0,wrappable:!1,template:`[carousel autoplay="true" interval="5000"]
|
|
34
34
|
[slide title="Slide 1" image="/media/photo.jpg"]Caption.[/slide]
|
|
35
35
|
[slide title="Slide 2"]Text-only slide.[/slide]
|
|
36
|
-
[/carousel]`,children:{name:"slide",min:2,max:12},attrs:[{name:"autoplay",type:"checkbox",label:"Autoplay"},{name:"interval",type:"number",label:"Interval (ms)"},{name:"loop",type:"checkbox",label:"Loop"},{name:"animation",type:"select",label:"Animation",options:[["slide","Slide"],["fade","Fade"],["crossfade","Crossfade"]]},{name:"animation-duration",type:"number",label:"Animation duration (ms)"},{name:"animation-easing",type:"text",label:"Animation easing"},{name:"id",type:"text",label:"ID"}]},{name:"slide",label:"Slide",icon:"image",category:"Components",child:!0,selfClosing:!1,container:!0,wrappable:!1,template:'[slide title="Slide title" image="/media/photo.jpg"]Caption[/slide]',inline:!0,attrs:[{name:"title",type:"text",label:"Title"},{name:"image",type:"image",label:"Image"}]},{name:"countdown",label:"Countdown",icon:"stopwatch",category:"Components",selfClosing:!0,container:!1,wrappable:!1,template:'[countdown to="2026-12-31" format="DD:HH:mm:ss" /]',attrs:[{name:"to",type:"text",label:"Target date (ISO)"},{name:"duration",type:"number",label:"Duration (seconds)"},{name:"format",type:"select",label:"Format",options:[["mm:ss","mm:ss"],["HH:mm:ss","HH:mm:ss"],["DD:HH:mm:ss","DD:HH:mm:ss"]]},{name:"id",type:"text",label:"ID"}]},{name:"timeline",label:"Timeline",icon:"activity",category:"Components",selfClosing:!1,container:!0,wrappable:!1,template:`[timeline layout="vertical" theme="modern"]
|
|
36
|
+
[/carousel]`,children:{name:"slide",min:2,max:12},quickAttrs:["arrow-position","background","arrow-background","arrow-color"],attrs:[{name:"autoplay",type:"checkbox",label:"Autoplay"},{name:"interval",type:"number",label:"Interval (ms)"},{name:"loop",type:"checkbox",label:"Loop"},{name:"animation",type:"select",label:"Animation",options:[["slide","Slide"],["fade","Fade"],["crossfade","Crossfade"]]},{name:"animation-duration",type:"number",label:"Animation duration (ms)"},{name:"animation-easing",type:"text",label:"Animation easing"},{name:"arrow-position",type:"select",label:"Arrow position",options:[["middle","Middle"],["top","Top"],["bottom","Bottom"]]},{name:"background",type:"colour",label:"Background colour"},{name:"arrow-background",type:"colour",label:"Arrow background"},{name:"arrow-color",type:"colour",label:"Arrow colour"},{name:"id",type:"text",label:"ID"}]},{name:"slide",label:"Slide",icon:"image",category:"Components",child:!0,selfClosing:!1,container:!0,wrappable:!1,template:'[slide title="Slide title" image="/media/photo.jpg"]Caption[/slide]',inline:!0,attrs:[{name:"title",type:"text",label:"Title"},{name:"image",type:"image",label:"Image"}]},{name:"countdown",label:"Countdown",icon:"stopwatch",category:"Components",selfClosing:!0,container:!1,wrappable:!1,template:'[countdown to="2026-12-31" format="DD:HH:mm:ss" /]',attrs:[{name:"to",type:"text",label:"Target date (ISO)"},{name:"duration",type:"number",label:"Duration (seconds)"},{name:"format",type:"select",label:"Format",options:[["mm:ss","mm:ss"],["HH:mm:ss","HH:mm:ss"],["DD:HH:mm:ss","DD:HH:mm:ss"]]},{name:"id",type:"text",label:"ID"}]},{name:"timeline",label:"Timeline",icon:"activity",category:"Components",selfClosing:!1,container:!0,wrappable:!1,template:`[timeline layout="vertical" theme="modern"]
|
|
37
37
|
[event title="Launch" date="2025-01-15" status="completed"]Launched.[/event]
|
|
38
38
|
[/timeline]`,children:{name:"event",min:1,max:20},custom:"timeline",attrs:[{name:"layout",type:"select",label:"Layout",options:[["vertical","Vertical"],["centred","Centred"],["horizontal","Horizontal"]]},{name:"theme",type:"select",label:"Theme",options:[["minimal","Minimal"],["corporate","Corporate"],["modern","Modern"]]},{name:"mode",type:"select",label:"Mode",options:[["timeline","Timeline"],["roadmap","Roadmap"]]},{name:"class",type:"text",label:"CSS class"},{name:"id",type:"text",label:"ID"}]},{name:"event",label:"Event",icon:"dot",category:"Components",child:!0,selfClosing:!1,container:!0,wrappable:!1,template:'[event title="Milestone" date="2025-01-15" status="completed"]Description[/event]',inline:!0,attrs:[{name:"title",type:"text",label:"Title"},{name:"date",type:"text",label:"Date"},{name:"status",type:"select",label:"Status",options:[["planned","Planned"],["in-progress","In progress"],["completed","Completed"],["blocked","Blocked"]]},{name:"icon",type:"icon",label:"Icon"}]},{name:"hero",label:"Hero",icon:"hero",category:"Content",selfClosing:!1,container:!0,wrappable:!0,template:`[hero title="Welcome" tagline="Build something great" size="lg" variant="gradient-blue"]
|
|
39
39
|
Optional body content.
|
|
@@ -44,4 +44,4 @@ Optional body content.
|
|
|
44
44
|
[/table]`,attrs:[{name:"striped",type:"checkbox",label:"Striped"},{name:"bordered",type:"checkbox",label:"Bordered"},{name:"compact",type:"checkbox",label:"Compact"},{name:"caption",type:"text",label:"Caption"},{name:"class",type:"text",label:"CSS class"},{name:"id",type:"text",label:"ID"}]},{name:"badge",label:"Badge",icon:"badge",category:"Content",selfClosing:!1,container:!1,wrappable:!1,template:'[badge variant="success"]New[/badge]',inline:!0,attrs:[{name:"variant",type:"select",label:"Variant",options:[["primary","Primary"],["secondary","Secondary"],["success","Success"],["danger","Danger"],["warning","Warning"],["info","Info"],["light","Light"],["dark","Dark"]]},{name:"pill",type:"checkbox",label:"Pill"},{name:"outline",type:"checkbox",label:"Outline"},{name:"size",type:"select",label:"Size",options:[["","Default"],["small","Small"],["large","Large"]]}]},{name:"text",label:"Text",icon:"text-style",preview:"text",category:"Content",selfClosing:!1,container:!0,wrappable:!0,template:"[text]Styled text[/text]",inline:!0,attrs:[{name:"size",type:"select",label:"Size",options:[["xs","XS"],["sm","Small"],["base","Base"],["lg","Large"],["xl","XL"],["2xl","2XL"],["3xl","3XL"],["4xl","4XL"]]},{name:"weight",type:"select",label:"Weight",options:[["thin","Thin"],["light","Light"],["normal","Normal"],["medium","Medium"],["semibold","Semibold"],["bold","Bold"],["extrabold","Extra bold"],["black","Black"]]},{name:"point-size",type:"number",label:"Point size"},{name:"bold",type:"checkbox",label:"Bold"},{name:"italic",type:"checkbox",label:"Italic"},{name:"color",type:"colour",label:"Colour"},{name:"font",type:"select",label:"Font",options:[["Georgia","Georgia"],["Arial","Arial"],["Verdana","Verdana"],["Courier New","Courier New"],["Times New Roman","Times New Roman"],["Trebuchet MS","Trebuchet MS"]]},{name:"transform",type:"select",label:"Transform",options:[["upper","Uppercase"],["lower","Lowercase"],["capitalize","Capitalise"],["none","None"]]},{name:"decoration",type:"select",label:"Decoration",options:[["underline","Underline"],["line-through","Line through"],["none","None"]]},{name:"spacing",type:"select",label:"Letter spacing",options:[["tight","Tight"],["normal","Normal"],["wide","Wide"],["wider","Wider"]]},{name:"align",type:"select",label:"Align",options:[["","None"],["left","Left"],["center","Centre"],["right","Right"]]},{name:"style",type:"text",label:"Inline CSS"},{name:"class",type:"text",label:"CSS class"},{name:"id",type:"text",label:"ID"}]},{name:"button",label:"Button",icon:"btn-insert",category:"Content",selfClosing:!0,container:!1,wrappable:!1,template:'[button href="/contact" variant="primary" label="Get in touch" /]',attrs:[{name:"href",type:"url",label:"Link"},{name:"label",type:"text",label:"Label"},{name:"variant",type:"select",label:"Variant",options:[["primary","Primary"],["secondary","Secondary"],["success","Success"],["danger","Danger"],["warning","Warning"],["info","Info"],["outline","Outline"],["ghost","Ghost"],["link","Link"],["outline-success","Outline success"],["outline-danger","Outline danger"],["outline-warning","Outline warning"],["outline-info","Outline info"]]},{name:"size",type:"select",label:"Size",options:[["","Default"],["sm","Small"],["lg","Large"]]},{name:"icon",type:"icon",label:"Icon (before)"},{name:"icon-after",type:"icon",label:"Icon (after)"},{name:"target",type:"text",label:"Target"},{name:"class",type:"text",label:"CSS class"},{name:"id",type:"text",label:"ID"}]},{name:"link",label:"Link",icon:"link-shortcode",category:"Content",selfClosing:!0,container:!1,wrappable:!1,template:'[link href="/about" label="About us" /]',formats:[{value:"markdown",label:"Markdown link",emit:e=>Object.keys(e).some(t=>t!=="href"&&t!=="label"&&e[t])||!e.href?null:`[${e.label||e.href}](${e.href})`},{value:"shortcode",label:"Link shortcode"}],attrs:[{name:"href",type:"url",label:"Link"},{name:"label",type:"text",label:"Label"},{name:"target",type:"text",label:"Target"},{name:"class",type:"text",label:"CSS class"},{name:"icon",type:"icon",label:"Icon (before)"},{name:"icon-after",type:"icon",label:"Icon (after)"},{name:"id",type:"text",label:"ID"}]},{name:"listgroup",label:"List group",icon:"list",category:"Content",selfClosing:!1,container:!0,wrappable:!1,template:`[listgroup]
|
|
45
45
|
[item]First item[/item]
|
|
46
46
|
[item]Second item[/item]
|
|
47
|
-
[/listgroup]`,children:{name:"item",min:1,max:20},attrs:[{name:"variant",type:"select",label:"Variant",options:[["","Default"],["flush","Flush"],["numbered","Numbered"],["horizontal","Horizontal"]]},{name:"size",type:"select",label:"Size",options:[["","Default"],["sm","Small"],["lg","Large"]]},{name:"itemvariant",type:"select",label:"Item variant",options:[["","Default"],["primary","Primary"],["secondary","Secondary"],["success","Success"],["danger","Danger"],["warning","Warning"],["info","Info"]]},{name:"action",type:"checkbox",label:"Actionable items"}]},{name:"collection",label:"Collection",icon:"database",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[collection slug="enquiries" display="table" /]',attrs:[{name:"slug",type:"lookup",source:"collections",label:"Collection"},{name:"display",type:"select",label:"Display",options:[["table","Table"],["cards","Cards"],["list","List"],["accordion","Accordion"],["timeline","Timeline"],["block","Block"]]},{name:"fields",type:"text",label:"Fields (comma-separated)"},{name:"limit",type:"number",label:"Limit"},{name:"sort",type:"lookup",source:"collection-fields",label:"Sort field"},{name:"order",type:"select",label:"Order",options:[["desc","Descending"],["asc","Ascending"]]},{name:"title-field",type:"lookup",source:"collection-fields",label:"Title field"},{name:"columns",type:"number",label:"Card columns"},{name:"search",type:"checkbox",label:"Search (table)",default:!0},{name:"sortable",type:"checkbox",label:"Sortable (table)",default:!0},{name:"exportable",type:"checkbox",label:"CSV export"},{name:"page-size",type:"number",label:"Page size"},{name:"empty",type:"text",label:"Empty message"},{name:"scope",type:"select",label:"Scope",options:[["","All"],["mine","Current user"]]},{name:"cta",type:"lookup",source:"actions",label:"CTA action"},{name:"cta-label",type:"text",label:"CTA label"},{name:"cta-icon",type:"icon",label:"CTA icon"},{name:"cta-style",type:"select",label:"CTA style",options:[["primary","Primary"],["secondary","Secondary"],["success","Success"],["danger","Danger"],["warning","Warning"],["info","Info"]]},{name:"cta-confirm",type:"text",label:"CTA confirm text"},{name:"class",type:"text",label:"CSS class"},{name:"id",type:"text",label:"ID"}]},{name:"view",label:"View",icon:"eye",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[view slug="products" display="table" limit="50" /]',attrs:[{name:"slug",type:"lookup",source:"views",label:"View"},{name:"display",type:"select",label:"Display",options:[["table","Table"],["cards","Cards"],["list","List"],["accordion","Accordion"],["timeline","Timeline"],["block","Block"]]},{name:"limit",type:"number",label:"Limit"},{name:"fields",type:"text",label:"Fields (comma-separated)"},{name:"title-field",type:"text",label:"Title field"},{name:"columns",type:"number",label:"Card columns"},{name:"empty",type:"text",label:"Empty message"}]},{name:"block",label:"Block",icon:"layout",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[block template="post-card" title="My Post" /]',attrs:[{name:"template",type:"lookup",source:"blocks",label:"Template"}]},{name:"form",label:"Form",icon:"file-text",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[form name="contact" /]',attrs:[{name:"name",type:"lookup",source:"forms",label:"Form"},{name:"class",type:"text",label:"CSS class"},{name:"id",type:"text",label:"ID"}]},{name:"cta",label:"CTA",icon:"mouse-pointer",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[cta action="send-welcome" entry="abc123" style="primary" label="Send" /]',attrs:[{name:"action",type:"lookup",source:"actions",label:"Action"},{name:"entry",type:"text",label:"Entry ID"},{name:"style",type:"select",label:"Style",options:[["primary","Primary"],["secondary","Secondary"],["success","Success"],["danger","Danger"],["warning","Warning"],["info","Info"]]},{name:"icon",type:"icon",label:"Icon"},{name:"size",type:"select",label:"Size",options:[["","Default"],["sm","Small"],["lg","Large"]]},{name:"confirm",type:"text",label:"Confirm text"},{name:"label",type:"text",label:"Label"}]},{name:"menu",label:"Menu",icon:"menu",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[menu slug="legal" /]',attrs:[{name:"slug",type:"lookup",source:"menus",label:"Menu"},{name:"location",type:"lookup",source:"menu-locations",label:"Location slot"},{name:"variant",type:"text",label:"Variant"},{name:"class",type:"text",label:"CSS class"},{name:"depth",type:"number",label:"Max depth"},{name:"orientation",type:"select",label:"Orientation",options:[["","Menu default"],["horizontal","Horizontal"],["vertical","Vertical"]]},{name:"float",type:"select",label:"Floating",options:[["","Menu default"],["no","Render inline here"]]}]},{name:"contents",label:"Page contents (this page)",icon:"list",category:"Layout",selfClosing:!0,container:!1,wrappable:!1,template:'[contents numbered="true" /]',actions:[{label:"Number the contents",icon:"hash",set:{numbered:"true"}},{label:"List H2 and H3 headings",icon:"list",set:{headings:"h2h3"}},{label:"Contents on the right",icon:"sidebar-right",set:{side:"right"}},{label:"Collapse the contents to a button",icon:"minimize",set:{collapsible:"true",collapsed:"true"}},{label:"Float the contents",icon:"move",set:{floating:"true","float-anchor":"TR",movable:"true"}},{label:"No contents on this page",icon:"eye-off",set:{hidden:"true"}},{label:"Remove this override",icon:"trash",remove:!0}],quickAttrs:["side","headings","numbered","title","collapsible","floating","hidden"],attrs:[{name:"side",type:"select",label:"Side",options:[["","Layout default"],["left","Left"],["right","Right"]]},{name:"width",type:"number",label:"Width (px, 160-480)"},{name:"headings",type:"select",label:"Headings",options:[["","Layout default"],["auto","Sections (H2s, else H3s)"],["h2","H2 only"],["h3","H3 only"],["h2h3","H2 and H3"]]},{name:"numbered",type:"select",label:"Numbered",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"title",type:"text",label:"Title above the list"},{name:"exclude",type:"text",label:"Leave out (headings, comma separated)"},{name:"sticky",type:"select",label:"Stays in view",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"scroll-spy",type:"select",label:"Marks the section being read",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"collapsible",type:"select",label:"Collapsible to a button",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"collapsed",type:"select",label:"Start collapsed",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"autohide",type:"select",label:"Autohide off its edge",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"floating",type:"select",label:"Floating panel",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"float-anchor",type:"select",label:"Float anchor",options:[["","Layout default"],["TL","Top left"],["TR","Top right"],["BL","Bottom left"],["BR","Bottom right"],["L","Left"],["R","Right"]]},{name:"movable",type:"select",label:"Visitors can move it",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"hidden",type:"select",label:"No contents on this page",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"hover",type:"text",label:"Hover effect"},{name:"selected",type:"text",label:"When selected"},{name:"accent",type:"colour",label:"Accent"},{name:"font",type:"text",label:"Font family"},{name:"font-size",type:"text",label:"Font size"},{name:"font-weight",type:"text",label:"Font weight"}]},{name:"navbar",label:"Navbar settings (this page)",icon:"sliders",category:"Layout",selfClosing:!0,container:!1,wrappable:!1,template:'[navbar collapsed="true" /]',actions:[{label:"Collapse the rail on this page",icon:"chevrons-right",set:{collapsible:"true",collapsed:"true",autohide:"false"}},{label:"Autohide the rail on this page",icon:"eye-off",set:{autohide:"true",collapsed:null}},{label:"Keep the rail open on this page",icon:"eye",set:{autohide:"false",collapsed:"false"}},{label:"Hide the bar on scroll on this page",icon:"arrow-up",set:{"hide-on-scroll":"true"}},{label:"Remove this override",icon:"trash",remove:!0}],quickAttrs:["position","width","collapsible","collapsed","autohide","hide-on-scroll"],attrs:[{name:"position",type:"select",label:"Position",options:[["","Menu default"],["sticky","Sticky"],["fixed","Fixed"],["static","Static"],["floating","Floating"]]},{name:"orientation",type:"select",label:"Orientation",options:[["","Menu default"],["horizontal","Horizontal"],["vertical","Vertical"]]},{name:"side",type:"select",label:"Rail side",options:[["","Menu default"],["left","Left"],["right","Right"]]},{name:"align",type:"select",label:"Alignment",options:[["","Menu default"],["left","Left"],["centre","Centre"],["right","Right"],["spread","Spread"]]},{name:"width",type:"number",label:"Rail width (px, 160-480)"},{name:"collapsible",type:"select",label:"Collapsible to icons",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"collapsed",type:"select",label:"Start collapsed",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"autohide",type:"select",label:"Autohide off-screen",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"hide-on-scroll",type:"select",label:"Hide on scroll (bar)",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"hover-open",type:"select",label:"Open dropdowns on hover",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"hover",type:"text",label:"Hover effect"},{name:"selected",type:"text",label:"When selected"},{name:"accent",type:"colour",label:"Highlight colour"},{name:"variant",type:"text",label:"Variant"},{name:"font",type:"text",label:"Font family"},{name:"font-size",type:"text",label:"Font size"},{name:"font-weight",type:"text",label:"Font weight"},{name:"letter-spacing",type:"text",label:"Letter spacing"},{name:"icon-size",type:"text",label:"Icon size"},{name:"float-anchor",type:"select",label:"Float anchor",options:[["","Menu default"],["TL","Top left"],["TR","Top right"],["BL","Bottom left"],["BR","Bottom right"],["L","Left"],["R","Right"]]},{name:"float-x",type:"number",label:"Float offset X (px)"},{name:"float-y",type:"number",label:"Float offset Y (px)"},{name:"float-width",type:"number",label:"Float width (px)"},{name:"float-collapsible",type:"select",label:"Float: collapsible",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"float-movable",type:"select",label:"Float: movable",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"class",type:"text",label:"CSS classes"}]},{name:"theme-switcher",label:"Theme switcher",icon:"palette",category:"Components",selfClosing:!0,container:!1,wrappable:!1,template:"[theme-switcher /]",attrs:[{name:"position",type:"select",label:"Position",options:[["","Inline - where the tag is"],["top-left","Top left"],["top-right","Top right"],["left-center","Left, centred"],["right-center","Right, centred"],["bottom-left","Bottom left"],["bottom-right","Bottom right"]]},{name:"offset-x",type:"number",label:"Offset from the side (px, pinned only)"},{name:"offset-y",type:"number",label:"Offset from the top/bottom (px, pinned only)"},{name:"themes",type:"text",label:"Themes (comma list - blank for the site's)"}]},{name:"icon",label:"Icon",icon:"icon-pick",category:"Media",selfClosing:!0,container:!1,wrappable:!1,template:'[icon name="arrow-right" /]',attrs:[{name:"name",type:"icon",label:"Icon name"},{name:"size",type:"number",label:"Size (px)"},{name:"color",type:"colour",label:"Colour"},{name:"class",type:"text",label:"CSS class"}]},{name:"embed",label:"Embed",icon:"video",category:"Media",selfClosing:!0,container:!1,wrappable:!1,template:'[embed url="https://youtu.be/dQw4w9WgXcQ" /]',custom:"embed",attrs:[{name:"url",type:"url",label:"URL"},{name:"ratio",type:"select",label:"Aspect ratio",options:[["16:9","16:9"],["4:3","4:3"],["1:1","1:1"],["21:9","21:9"],["9:16","9:16"],["3:4","3:4"]]},{name:"title",type:"text",label:"Title / caption"},{name:"width",type:"text",label:"Width"},{name:"align",type:"select",label:"Alignment",options:[["left","Left"],["center","Centre"],["right","Right"]]},{name:"autoplay",type:"checkbox",label:"Autoplay"},{name:"loop",type:"checkbox",label:"Loop"},{name:"muted",type:"checkbox",label:"Muted"}]}],EDITOR_TOOLS=[{name:"image",label:"Image",icon:"image-add",category:"Media",custom:"image"},{name:"colourpick",label:"Colour picker",icon:"colour-picker",category:"Media",custom:"colourpick"},{name:"component",label:"Component",icon:"component",category:"Components",custom:"component"},{name:"scribe",label:"Scribe",icon:"scribe-insert",category:"Content",custom:"scribe"}];
|
|
47
|
+
[/listgroup]`,children:{name:"item",min:1,max:20},attrs:[{name:"variant",type:"select",label:"Variant",options:[["","Default"],["flush","Flush"],["numbered","Numbered"],["horizontal","Horizontal"]]},{name:"size",type:"select",label:"Size",options:[["","Default"],["sm","Small"],["lg","Large"]]},{name:"itemvariant",type:"select",label:"Item variant",options:[["","Default"],["primary","Primary"],["secondary","Secondary"],["success","Success"],["danger","Danger"],["warning","Warning"],["info","Info"]]},{name:"action",type:"checkbox",label:"Actionable items"}]},{name:"collection",label:"Collection",icon:"database",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[collection slug="enquiries" display="table" /]',attrs:[{name:"slug",type:"lookup",source:"collections",label:"Collection"},{name:"display",type:"select",label:"Display",options:[["table","Table"],["cards","Cards"],["list","List"],["accordion","Accordion"],["timeline","Timeline"],["carousel","Carousel"],["block","Block"]]},{name:"fields",type:"text",label:"Fields (comma-separated)"},{name:"limit",type:"number",label:"Limit"},{name:"sort",type:"lookup",source:"collection-fields",label:"Sort field"},{name:"order",type:"select",label:"Order",options:[["desc","Descending"],["asc","Ascending"]]},{name:"title-field",type:"lookup",source:"collection-fields",label:"Title field"},{name:"image-field",type:"lookup",source:"collection-fields",label:"Image field"},{name:"body-field",type:"lookup",source:"collection-fields",label:"Body field"},{name:"arrow-position",type:"select",label:"Arrow position",options:[["middle","Middle"],["top","Top"],["bottom","Bottom"]]},{name:"background",type:"colour",label:"Carousel background"},{name:"arrow-background",type:"colour",label:"Arrow background"},{name:"arrow-color",type:"colour",label:"Arrow colour"},{name:"autoplay",type:"checkbox",label:"Autoplay"},{name:"interval",type:"number",label:"Interval (ms)"},{name:"loop",type:"checkbox",label:"Loop"},{name:"animation",type:"select",label:"Animation",options:[["slide","Slide"],["fade","Fade"],["crossfade","Crossfade"]]},{name:"columns",type:"number",label:"Card columns"},{name:"search",type:"checkbox",label:"Search (table)",default:!0},{name:"sortable",type:"checkbox",label:"Sortable (table)",default:!0},{name:"exportable",type:"checkbox",label:"CSV export"},{name:"page-size",type:"number",label:"Page size"},{name:"empty",type:"text",label:"Empty message"},{name:"scope",type:"select",label:"Scope",options:[["","All"],["mine","Current user"]]},{name:"cta",type:"lookup",source:"actions",label:"CTA action"},{name:"cta-label",type:"text",label:"CTA label"},{name:"cta-icon",type:"icon",label:"CTA icon"},{name:"cta-style",type:"select",label:"CTA style",options:[["primary","Primary"],["secondary","Secondary"],["success","Success"],["danger","Danger"],["warning","Warning"],["info","Info"]]},{name:"cta-confirm",type:"text",label:"CTA confirm text"},{name:"class",type:"text",label:"CSS class"},{name:"id",type:"text",label:"ID"}]},{name:"view",label:"View",icon:"eye",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[view slug="products" display="table" limit="50" /]',attrs:[{name:"slug",type:"lookup",source:"views",label:"View"},{name:"display",type:"select",label:"Display",options:[["table","Table"],["cards","Cards"],["list","List"],["accordion","Accordion"],["timeline","Timeline"],["carousel","Carousel"],["block","Block"]]},{name:"limit",type:"number",label:"Limit"},{name:"fields",type:"text",label:"Fields (comma-separated)"},{name:"title-field",type:"text",label:"Title field"},{name:"image-field",type:"text",label:"Image field"},{name:"body-field",type:"text",label:"Body field"},{name:"arrow-position",type:"select",label:"Arrow position",options:[["middle","Middle"],["top","Top"],["bottom","Bottom"]]},{name:"background",type:"colour",label:"Carousel background"},{name:"arrow-background",type:"colour",label:"Arrow background"},{name:"arrow-color",type:"colour",label:"Arrow colour"},{name:"autoplay",type:"checkbox",label:"Autoplay"},{name:"interval",type:"number",label:"Interval (ms)"},{name:"loop",type:"checkbox",label:"Loop"},{name:"animation",type:"select",label:"Animation",options:[["slide","Slide"],["fade","Fade"],["crossfade","Crossfade"]]},{name:"columns",type:"number",label:"Card columns"},{name:"empty",type:"text",label:"Empty message"}]},{name:"block",label:"Block",icon:"layout",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[block template="post-card" title="My Post" /]',attrs:[{name:"template",type:"lookup",source:"blocks",label:"Template"}]},{name:"form",label:"Form",icon:"file-text",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[form name="contact" /]',attrs:[{name:"name",type:"lookup",source:"forms",label:"Form"},{name:"class",type:"text",label:"CSS class"},{name:"id",type:"text",label:"ID"}]},{name:"cta",label:"CTA",icon:"mouse-pointer",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[cta action="send-welcome" entry="abc123" style="primary" label="Send" /]',attrs:[{name:"action",type:"lookup",source:"actions",label:"Action"},{name:"entry",type:"text",label:"Entry ID"},{name:"style",type:"select",label:"Style",options:[["primary","Primary"],["secondary","Secondary"],["success","Success"],["danger","Danger"],["warning","Warning"],["info","Info"]]},{name:"icon",type:"icon",label:"Icon"},{name:"size",type:"select",label:"Size",options:[["","Default"],["sm","Small"],["lg","Large"]]},{name:"confirm",type:"text",label:"Confirm text"},{name:"label",type:"text",label:"Label"}]},{name:"menu",label:"Menu",icon:"menu",category:"Data",selfClosing:!0,container:!1,wrappable:!1,template:'[menu slug="legal" /]',attrs:[{name:"slug",type:"lookup",source:"menus",label:"Menu"},{name:"location",type:"lookup",source:"menu-locations",label:"Location slot"},{name:"variant",type:"text",label:"Variant"},{name:"class",type:"text",label:"CSS class"},{name:"depth",type:"number",label:"Max depth"},{name:"orientation",type:"select",label:"Orientation",options:[["","Menu default"],["horizontal","Horizontal"],["vertical","Vertical"]]},{name:"float",type:"select",label:"Floating",options:[["","Menu default"],["no","Render inline here"]]}]},{name:"contents",label:"Page contents (this page)",icon:"list",category:"Layout",selfClosing:!0,container:!1,wrappable:!1,template:'[contents numbered="true" /]',actions:[{label:"Number the contents",icon:"hash",set:{numbered:"true"}},{label:"List H2 and H3 headings",icon:"list",set:{headings:"h2h3"}},{label:"Contents on the right",icon:"sidebar-right",set:{side:"right"}},{label:"Collapse the contents to a button",icon:"minimize",set:{collapsible:"true",collapsed:"true"}},{label:"Float the contents",icon:"move",set:{floating:"true","float-anchor":"TR",movable:"true"}},{label:"No contents on this page",icon:"eye-off",set:{hidden:"true"}},{label:"Remove this override",icon:"trash",remove:!0}],quickAttrs:["side","headings","numbered","title","collapsible","floating","hidden"],attrs:[{name:"side",type:"select",label:"Side",options:[["","Layout default"],["left","Left"],["right","Right"]]},{name:"width",type:"number",label:"Width (px, 160-480)"},{name:"headings",type:"select",label:"Headings",options:[["","Layout default"],["auto","Sections (H2s, else H3s)"],["h2","H2 only"],["h3","H3 only"],["h2h3","H2 and H3"]]},{name:"numbered",type:"select",label:"Numbered",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"title",type:"text",label:"Title above the list"},{name:"exclude",type:"text",label:"Leave out (headings, comma separated)"},{name:"sticky",type:"select",label:"Stays in view",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"scroll-spy",type:"select",label:"Marks the section being read",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"collapsible",type:"select",label:"Collapsible to a button",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"collapsed",type:"select",label:"Start collapsed",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"autohide",type:"select",label:"Autohide off its edge",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"floating",type:"select",label:"Floating panel",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"float-anchor",type:"select",label:"Float anchor",options:[["","Layout default"],["TL","Top left"],["TR","Top right"],["BL","Bottom left"],["BR","Bottom right"],["L","Left"],["R","Right"]]},{name:"movable",type:"select",label:"Visitors can move it",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"hidden",type:"select",label:"No contents on this page",options:[["","Layout default"],["true","On"],["false","Off"]]},{name:"hover",type:"text",label:"Hover effect"},{name:"selected",type:"text",label:"When selected"},{name:"accent",type:"colour",label:"Accent"},{name:"font",type:"text",label:"Font family"},{name:"font-size",type:"text",label:"Font size"},{name:"font-weight",type:"text",label:"Font weight"}]},{name:"navbar",label:"Navbar settings (this page)",icon:"sliders",category:"Layout",selfClosing:!0,container:!1,wrappable:!1,template:'[navbar collapsed="true" /]',actions:[{label:"Collapse the rail on this page",icon:"chevrons-right",set:{collapsible:"true",collapsed:"true",autohide:"false"}},{label:"Autohide the rail on this page",icon:"eye-off",set:{autohide:"true",collapsed:null}},{label:"Keep the rail open on this page",icon:"eye",set:{autohide:"false",collapsed:"false"}},{label:"Hide the bar on scroll on this page",icon:"arrow-up",set:{"hide-on-scroll":"true"}},{label:"Remove this override",icon:"trash",remove:!0}],quickAttrs:["position","width","collapsible","collapsed","autohide","hide-on-scroll"],attrs:[{name:"position",type:"select",label:"Position",options:[["","Menu default"],["sticky","Sticky"],["fixed","Fixed"],["static","Static"],["floating","Floating"]]},{name:"orientation",type:"select",label:"Orientation",options:[["","Menu default"],["horizontal","Horizontal"],["vertical","Vertical"]]},{name:"side",type:"select",label:"Rail side",options:[["","Menu default"],["left","Left"],["right","Right"]]},{name:"align",type:"select",label:"Alignment",options:[["","Menu default"],["left","Left"],["centre","Centre"],["right","Right"],["spread","Spread"]]},{name:"width",type:"number",label:"Rail width (px, 160-480)"},{name:"collapsible",type:"select",label:"Collapsible to icons",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"collapsed",type:"select",label:"Start collapsed",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"autohide",type:"select",label:"Autohide off-screen",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"hide-on-scroll",type:"select",label:"Hide on scroll (bar)",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"hover-open",type:"select",label:"Open dropdowns on hover",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"hover",type:"text",label:"Hover effect"},{name:"selected",type:"text",label:"When selected"},{name:"accent",type:"colour",label:"Highlight colour"},{name:"variant",type:"text",label:"Variant"},{name:"font",type:"text",label:"Font family"},{name:"font-size",type:"text",label:"Font size"},{name:"font-weight",type:"text",label:"Font weight"},{name:"letter-spacing",type:"text",label:"Letter spacing"},{name:"icon-size",type:"text",label:"Icon size"},{name:"float-anchor",type:"select",label:"Float anchor",options:[["","Menu default"],["TL","Top left"],["TR","Top right"],["BL","Bottom left"],["BR","Bottom right"],["L","Left"],["R","Right"]]},{name:"float-x",type:"number",label:"Float offset X (px)"},{name:"float-y",type:"number",label:"Float offset Y (px)"},{name:"float-width",type:"number",label:"Float width (px)"},{name:"float-collapsible",type:"select",label:"Float: collapsible",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"float-movable",type:"select",label:"Float: movable",options:[["","Menu default"],["true","On"],["false","Off"]]},{name:"class",type:"text",label:"CSS classes"}]},{name:"theme-switcher",label:"Theme switcher",icon:"palette",category:"Components",selfClosing:!0,container:!1,wrappable:!1,template:"[theme-switcher /]",attrs:[{name:"position",type:"select",label:"Position",options:[["","Inline - where the tag is"],["top-left","Top left"],["top-right","Top right"],["left-center","Left, centred"],["right-center","Right, centred"],["bottom-left","Bottom left"],["bottom-right","Bottom right"]]},{name:"offset-x",type:"number",label:"Offset from the side (px, pinned only)"},{name:"offset-y",type:"number",label:"Offset from the top/bottom (px, pinned only)"},{name:"themes",type:"text",label:"Themes (comma list - blank for the site's)"}]},{name:"icon",label:"Icon",icon:"icon-pick",category:"Media",selfClosing:!0,container:!1,wrappable:!1,template:'[icon name="arrow-right" /]',attrs:[{name:"name",type:"icon",label:"Icon name"},{name:"size",type:"number",label:"Size (px)"},{name:"color",type:"colour",label:"Colour"},{name:"class",type:"text",label:"CSS class"}]},{name:"embed",label:"Embed",icon:"video",category:"Media",selfClosing:!0,container:!1,wrappable:!1,template:'[embed url="https://youtu.be/dQw4w9WgXcQ" /]',custom:"embed",attrs:[{name:"url",type:"url",label:"URL"},{name:"ratio",type:"select",label:"Aspect ratio",options:[["16:9","16:9"],["4:3","4:3"],["1:1","1:1"],["21:9","21:9"],["9:16","9:16"],["3:4","3:4"]]},{name:"title",type:"text",label:"Title / caption"},{name:"width",type:"text",label:"Width"},{name:"align",type:"select",label:"Alignment",options:[["left","Left"],["center","Centre"],["right","Right"]]},{name:"autoplay",type:"checkbox",label:"Autoplay"},{name:"loop",type:"checkbox",label:"Loop"},{name:"muted",type:"checkbox",label:"Muted"}]}];for(const e of SHORTCODES){e.attrs||(e.attrs=[]);for(const a of l)e.attrs.some(t=>t.name===a.name)||e.attrs.push(a)}export const EDITOR_TOOLS=[{name:"image",label:"Image",icon:"image-add",category:"Media",custom:"image"},{name:"colourpick",label:"Colour picker",icon:"colour-picker",category:"Media",custom:"colourpick"},{name:"component",label:"Component",icon:"component",category:"Components",custom:"component"},{name:"scribe",label:"Scribe",icon:"scribe-insert",category:"Content",custom:"scribe"}];
|
|
@@ -62,7 +62,8 @@
|
|
|
62
62
|
<p><strong>Content > Theme</strong> (stored in <code>config/theme.json</code>) sets the public
|
|
63
63
|
theme, the admin theme, fonts, the visitor theme switcher, automatic day and night themes (at
|
|
64
64
|
the clock times you choose) and per-element overrides. A page can override the theme in its
|
|
65
|
-
Page Details
|
|
65
|
+
Page Details. Picking an admin theme shows it at once so you can try them on - Save makes it
|
|
66
|
+
everyone's, and leaving without saving puts the saved theme back.</p>
|
|
66
67
|
|
|
67
68
|
<h3>Dates and times</h3>
|
|
68
69
|
<p>The admin shows dates as dd/mm/yyyy and times on a 24-hour clock (dd/mm/yyyy HH:mm), whatever
|
|
@@ -82,10 +82,11 @@
|
|
|
82
82
|
<select id="field-layout" class="form-select" data-model="layout.value"></select>
|
|
83
83
|
</div>
|
|
84
84
|
<div class="col-3">
|
|
85
|
-
<label class="form-label">Status <span data-help-title="
|
|
85
|
+
<label class="form-label">Status <span data-help-title="Page status" data-help="Draft and Review pages are hidden from visitors (404). Anyone who can read pages can preview them, and Live Preview and share links show them before publication."></span></label>
|
|
86
86
|
<select id="field-status" class="form-select" data-model="status.value">
|
|
87
87
|
<option value="draft">Draft</option>
|
|
88
88
|
<option value="published">Published</option>
|
|
89
|
+
<option value="review">Review</option>
|
|
89
90
|
</select>
|
|
90
91
|
</div>
|
|
91
92
|
</div>
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
<button type="button" data-on-click="showList" data-bind-class="mode.value === 'list' && 'is-on'" title="List">List</button>
|
|
26
26
|
<button type="button" data-on-click="showTree" data-bind-class="mode.value === 'tree' && 'is-on'" title="Tree - pages under the page whose URL they sit beneath">Tree</button>
|
|
27
27
|
</div>
|
|
28
|
-
<span data-help-title="Pages" data-help="Click a page to edit it. Right-click it (or ⋮, or Shift+F10) to view it on the site, copy its address, trim its revision history or delete it. Drafts are not public: visitors get a 404, while anyone who can edit pages sees them with a Draft banner. Tree shows each page under the page whose address it sits beneath. Your filter, sort and view are remembered."></span>
|
|
28
|
+
<span data-help-title="Pages" data-help="Click a page to edit it. Right-click it (or ⋮, or Shift+F10) to view it on the site, copy its address, transition between Draft, Published and Review, trim its revision history or delete it. Drafts are not public: visitors get a 404, while anyone who can edit pages sees them with a Draft banner. Tree shows each page under the page whose address it sits beneath. Your filter, sort and view are remembered."></span>
|
|
29
29
|
</div>
|
|
30
30
|
<div class="pgx-note dm-loading-slot" data-bind-hidden="!loading.value">Loading…</div>
|
|
31
31
|
<p class="pgx-note is-bad" data-bind-hidden="!loadError.value" data-bind-text="loadError.value"></p>
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
</div>
|
|
46
46
|
<div>
|
|
47
47
|
<label class="form-label" for="thx-admin">Admin theme
|
|
48
|
-
<span data-help="This admin panel, for everyone who signs in.
|
|
48
|
+
<span data-help="This admin panel, for everyone who signs in. A pick shows here at once so you can try them on; Save makes it everyone's, leaving without saving puts the saved one back. Admin-only palettes (Sharp, Smooth) are offered here and nowhere else."></span></label>
|
|
49
49
|
<select id="thx-admin" class="form-select" data-model="adminTheme.value"></select>
|
|
50
50
|
</div>
|
|
51
51
|
</div>
|
package/admin/js/views/pages.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{api as
|
|
1
|
+
import{api as y}from"../api.js";import{SHAPES as ke,whileLoading as Pe}from"../lib/loading.js";import{addBannerAction as Me}from"../lib/plugin-chrome.js";import{helpPopovers as Z}from"../lib/help-popover.js";import{onceClosed as Ee,openMenuAt as ee,settingsStore as xe,sidebarCount as te,viewLifetime as Se}from"../lib/tool-kit.js";import{filterByProject as Ce,getProjectFromHash as $e}from"../lib/project-context.js";import{arrangePages as Te,checkPrune as Le,normaliseSettings as ae,pageRow as De,statusCounts as Ae,treeRows as Re}from"../lib/pages-arrange.js";const se=/^#\/(pages|projects\/[^/?#]+\/pages)\/?(\?.*)?$/,ne="#/pages",C="pages.prune.draft",je=`
|
|
2
2
|
<div class="pgx-row" data-path="{{urlPath}}" tabindex="0" data-bind-class="rowCls">
|
|
3
3
|
<span class="pgx-main"><strong>{{title}}</strong><small>{{meta}} <span class="pgx-tags">{{tagsText}}</span></small></span>
|
|
4
4
|
<span class="pgx-chips">
|
|
@@ -9,7 +9,7 @@ import{api as K}from"../api.js";import{SHAPES as be,whileLoading as ye}from"../l
|
|
|
9
9
|
<span class="pgx-chip" data-bind-class="statusCls">{{statusText}}</span>
|
|
10
10
|
</span>
|
|
11
11
|
<button type="button" class="pgx-kebab" aria-label="Actions for {{title}}" title="Actions">\u22EE</button>
|
|
12
|
-
</div>`,
|
|
12
|
+
</div>`,w=l=>String(l??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");function Be(l){const r=document.createElement("template");return r.innerHTML=l.trim(),r.content.firstElementChild}const oe=xe("pages.settings",ae),o=Se(se),Ke=l=>`#/pages/edit${l}`;export const pagesView={templateUrl:"/admin/js/templates/pages.html",async onMount(l){o.reset();const r=l.get(0).querySelector(".pgx")||l.get(0),$=()=>se.test(location.hash)&&r.isConnected,u=$e(),T=oe.read(),n=M.observable([]),H=M.observable(0),N=M.observable(!0),L=M.observable(""),p=M.observable(T.status),D=M.observable(T.sort),m=M.observable(T.mode),U=M.observable("");o.add(M.effect(()=>oe.write(ae({status:p.value,sort:D.value,mode:m.value}))));const A=M.observable([]);o.add(M.effect(()=>{A.value=Te(n.value,{status:p.value,query:U.value,sort:D.value})}));const _=M.observable([]);o.add(M.effect(()=>{_.value=A.value.map(e=>De(e))}));const re=M.computed(()=>Ae(n.value)),ie=M.computed(()=>n.value.length?"No pages match.":"No pages yet - New page in the banner makes one."),le=M.computed(()=>{const e=H.value;return!u||!e?"":`Showing only pages in ${u}. ${e} other page${e===1?" is":"s are"} filed elsewhere.`}),F=e=>n.peek().find(t=>t.urlPath===e);async function ce(){try{const e=await Pe(r.querySelector(".dm-loading-slot"),y.pages.list(),ke.rows);if(!$())return;const t=Array.isArray(e)?e:[],a=u?Ce(t,u):t;H.value=t.length-a.length,n.value=a,L.value=""}catch(e){L.value=`Could not load the pages: ${e.message||e}`}finally{N.value=!1}}const k=e=>{location.hash=Ke(e)},de=()=>{location.hash=u?`#/pages/new?project=${encodeURIComponent(u)}`:"#/pages/new"};async function ue(e){const t=e.plugin?`"${w(e.title||e.urlPath)}" is managed by the <strong>${w(e.plugin)}</strong> plugin - deleting it may stop that plugin working. Delete it anyway?`:`Delete the page at <strong>${w(e.urlPath)}</strong>? This cannot be undone.`;if(await E.confirm(t))try{await y.pages.delete(e.urlPath),E.toast("Page deleted.",{type:"success"}),n.value=n.peek().filter(a=>a.urlPath!==e.urlPath),te(ne)}catch(a){E.toast(`Not deleted: ${a.message||a}`,{type:"error"})}}async function pe(e){const t=new URL(e.urlPath,location.origin).href;try{await navigator.clipboard.writeText(t),E.toast("Address copied.",{type:"success",duration:2e3})}catch{E.toast(t,{type:"info",duration:8e3})}}async function R(e,t){if(e.status!==t)try{const a=await y.pages.get(e.urlPath);await y.pages.update(e.urlPath,{frontmatter:{status:t},body:a.content||""}),n.value=n.peek().map(c=>c.urlPath===e.urlPath?{...c,status:t}:c),E.toast(t==="published"?"Page published.":t==="review"?"Page moved to review.":"Page moved to drafts.",{type:"success"}),te(ne)}catch(a){E.toast(`Could not change page status: ${a.message||a}`,{type:"error"})}}let d=null;const j=new Set;let V=!1;function O(e,t=null){const a=e.versionCount||0,c=M.observable(String(t?.keep??Math.min(5,a))),x=M.observable(!1),f=M.computed(()=>Le(c.value,a)),G={keep:c,saving:x,err:M.computed(()=>f.value.error),summary:M.computed(()=>{const s=f.value;return s.error?"":s.deletes?`Deletes the ${s.deletes} oldest, keeps the newest ${Math.min(s.keep,a)}.`:"Nothing to delete - that keeps them all."}),cannot:M.computed(()=>x.value||!!f.value.error||f.value.deletes===0),intro:`${e.title||e.urlPath} has ${a} saved revision${a===1?"":"s"} - earlier versions you can compare with and restore from the editor's History.`},ve=String(c.peek()),J=()=>String(c.peek())!==ve||!!t,i=E.slideover({title:`Trim revisions - ${w(e.title||e.urlPath)}`,size:"sm",position:"right"}),g=Be(`
|
|
13
13
|
<div class="pgx-form">
|
|
14
14
|
<p class="pgx-note" data-bind-text="intro"></p>
|
|
15
15
|
<div>
|
|
@@ -25,4 +25,4 @@ import{api as K}from"../api.js";import{SHAPES as be,whileLoading as ye}from"../l
|
|
|
25
25
|
<button type="button" class="btn btn-sm btn-danger" data-on-click="trim" data-bind-disabled="cannot.value">
|
|
26
26
|
<span data-icon="archive" data-icon-size="13"></span> Trim</button>
|
|
27
27
|
</div>
|
|
28
|
-
</div>`);async function
|
|
28
|
+
</div>`);async function Q(){if(!G.cannot.peek()){x.value=!0;try{const s=await y.versions.prune(e.urlPath,f.peek().keep);E.toast(`Removed ${s.deleted} revision${s.deleted===1?"":"s"}; kept ${s.kept}.`,{type:"success"}),n.value=n.peek().map(b=>b.urlPath===e.urlPath?{...b,versionCount:s.kept}:b),K=!0,i.close()}catch(s){E.toast(s.message||"Could not trim the revisions.",{type:"error"})}finally{x.value=!1}}}i.element.appendChild(g);const fe=Z(g),be=M.applyBindings(G,g,{methods:{trim:Q,cancel:()=>i.close()}}),ye=s=>{s.key==="Enter"&&s.target.id==="pgx-f-keep"&&(s.preventDefault(),Q())};g.addEventListener("keydown",ye);const X={path:e.urlPath,dirty:J,values:()=>({keep:c.peek()})};d=X,j.add(i),Ee(i,()=>{be.dispose(),fe(),j.delete(i),d===X&&(d=null)});const we=i.close.bind(i);let K=!1,q=!1;i.close=async s=>{if(!K&&s!==!0&&!V&&J()){if(q)return;q=!0;const b=await E.confirm("Discard the change you have not applied?");if(q=!1,!b)return}return K=!0,we()},I.scan(g),i.open(),g.querySelector("#pgx-f-keep")?.select()}function he(e){const t=F(e);if(!t)return[];const a=t.versionCount||0;return[{header:t.title||t.urlPath},{label:"Edit",icon:"edit",action:()=>k(t.urlPath)},{label:t.status==="published"?"View on the site":"Preview (draft)",icon:"external-link",action:()=>window.open(t.urlPath,"_blank","noopener")},{label:"Copy address",icon:"copy",action:()=>pe(t)},{label:"Transition",icon:"circle",submenu:[{label:"Published",disabled:t.status==="published",action:()=>R(t,"published")},{label:"Draft",disabled:t.status==="draft",action:()=>R(t,"draft")},{label:"Review",disabled:t.status==="review",action:()=>R(t,"review")}]},{type:"divider"},{label:a?`Trim revisions (${a})\u2026`:"No revisions to trim",icon:"archive",disabled:!a,action:()=>O(t)},{type:"divider"},{label:"Delete\u2026",icon:"trash",danger:!0,action:()=>ue(t)}]}const h=r.querySelector(".pgx-list");h.innerHTML=je;const ge=M.applyBindings({pages:n,loading:N,loadError:L,status:p,sort:D,mode:m,query:U,rows:_,counts:re,emptyText:ie,projectNote:le},r,{methods:{showAll:()=>{p.value="all"},showPublished:()=>{p.value="published"},showDrafts:()=>{p.value="draft"},showList:()=>{m.value="list"},showTree:()=>{m.value="tree"}}});o.add(()=>ge.dispose()),o.add(Z(r,{position:"bottom"}));const B=r.querySelector(".pgx-tree");let P=null;o.add(M.effect(()=>{const e=m.value==="tree",t=e?Re(A.value):null;try{P?.destroy?.()}catch{}P=null,B.replaceChildren(),!(!e||!t.length)&&(P=E.treeView(B,{data:t,idKey:"id",parentKey:"parent_id",labelKey:"name",iconKey:"icon",expandedByDefault:!0,onSelect:a=>k(a)}),I.scan(B))})),o.add(()=>{try{P?.destroy?.()}catch{}});const me=E.contextMenu(h,{cursor:"glyph",match:".pgx-row",inherit:!1,density:"compact",items:e=>he(e.dataset.path)});o.add(()=>me?.destroy?.());const z=e=>{const t=e.target.closest(".pgx-row");if(!t)return;const a=e.target.closest(".pgx-kebab");if(a){ee(a);return}k(t.dataset.path)},W=e=>{const t=e.target.closest(".pgx-row");t&&(e.key==="F10"&&e.shiftKey?(e.preventDefault(),ee(t)):e.key==="Enter"&&e.target===t&&k(t.dataset.path))};h.addEventListener("click",z),h.addEventListener("keydown",W),o.add(()=>{h.removeEventListener("click",z),h.removeEventListener("keydown",W)}),Me(l,{id:"pages-new",icon:"plus",title:"New page",onClick:de});const Y=e=>{d?.dirty()&&(e.preventDefault(),e.returnValue="")};if(window.addEventListener("beforeunload",Y),o.add(()=>window.removeEventListener("beforeunload",Y)),o.add(()=>{if(d?.dirty())try{S.set(C,{path:d.path,values:d.values(),at:new Date().toISOString()})}catch{}V=!0,[...j].forEach(e=>{try{e.close(!0)}catch{}})}),I.scan(r),await ce(),!$())return;let v=null;try{v=S.get(C)}catch{}if(v?.path&&v.values){try{S.remove?S.remove(C):S.set(C,null)}catch{}const e=F(v.path);e&&(e.versionCount||0)>0&&await E.confirm(`You left trimming the revisions of "${w(e.title||e.urlPath)}" unfinished. Pick up where you left off?`)&&$()&&O(e,v.values)}}};
|
package/admin/js/views/theme.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import{api as
|
|
1
|
+
import{api as x}from"../api.js";import{applyAdminTheme as dt,refreshAdminTheme as Pe}from"../lib/admin-theme.js";import{addBannerAction as Ke}from"../lib/plugin-chrome.js";import{helpPopovers as Fe}from"../lib/help-popover.js";import{onceClosed as mt,openMenuAt as ze,sidebarCount as ht}from"../lib/tool-kit.js";import{populateThemeSelect as Be}from"../lib/themes.js";import{cloneOverride as G,dayOrNight as ut,formFromConfig as Q,knownThemeIds as pt,moveItem as _e,overrideRow as ft,payloadFromForm as X,snapshot as pe,swatchColour as He,themeDiff as vt,themeLabel as bt,themeProblems as yt}from"../lib/theme-settings-arrange.js";import{fmtDateTime as kt}from"../lib/dates.js";const fe=/^\/theme(\?.*)?$/,gt="#/theme",D="theme.draft",wt=[{value:"#site-navbar",label:"Navigation bar"},{value:"#site-footer",label:"Footer"},{value:".site-content",label:"Page content area"},{value:".page-body",label:"Page body (inside the container)"},{value:".site-sidebar",label:"Sidebar"},{value:".card",label:"Every card"},{value:".cms-form-embed",label:"Embedded forms"},{value:".cms-collection",label:"Collection displays"}],xt=`
|
|
2
2
|
<div class="thx-row" data-id="{{id}}" tabindex="0" data-bind-class="off && 'is-off'">
|
|
3
3
|
<span class="thx-main"><strong>{{name}}</strong><small>{{meta}}</small></span>
|
|
4
4
|
<span class="thx-chip is-danger" data-bind-hidden="!missing" title="Its theme no longer exists">Theme gone</span>
|
|
5
5
|
<span class="thx-chip is-warn" data-bind-hidden="!empty" title="No theme, token or CSS - it changes nothing">Empty</span>
|
|
6
6
|
<span class="thx-chip" data-bind-hidden="!off">Off</span>
|
|
7
7
|
<button type="button" class="thx-kebab" aria-label="Actions for {{name}}" title="Actions">\u22EE</button>
|
|
8
|
-
</div>`,
|
|
8
|
+
</div>`,ve=h=>String(h??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");function Et(h){const y=document.createElement("template");return y.innerHTML=h.trim(),y.content.firstElementChild}let T=null,Ue=!1;function St(){Ue||!window.R?.use||(Ue=!0,R.use((h,y,m)=>!(fe.test(y?.path||"")&&!fe.test(h?.path||""))||!T?.dirty()?m():E.confirm("You have unsaved theme changes. Leave anyway? They are kept as a draft for your next visit.").then(f=>{f?m():history.replaceState(null,"","#/theme")})),M.subscribe("router:afterChange",({to:h}={})=>{if(T&&!fe.test(h?.path||"")){const y=T;T=null,y.dispose()}}))}export const themeView={templateUrl:"/admin/js/templates/theme.html",async onMount(h){if(St(),T){const e=T;T=null,e.dispose()}const y=[],m=e=>(y.push(e),e);let Z=!1;const f=h.get(0).querySelector(".thx")||h.get(0),L=()=>!Z&&f.isConnected,be=E.loader(h.get(0),{type:"dots"});let j,q;try{[j,q]=await Promise.all([x.theme.get(),x.theme.catalog().catch(()=>({themes:[],tokens:[],custom:[]}))])}catch(e){be.destroy(),E.toast(`Could not load the theme settings: ${e.message||e}`,{type:"error"});return}if(be.destroy(),!f.isConnected)return;const ee=q.tokens||[],P=Array.isArray(q.custom)?q.custom:[],ye=pt(q),p=M.observable(j),O=Q(j),n={};for(const[e,t]of Object.entries(O))n[e]=M.observable(t);const ke=()=>Object.fromEntries(Object.keys(n).map(e=>[e,n[e].value])),te=()=>Object.fromEntries(Object.keys(n).map(e=>[e,n[e].peek()])),K=M.observable(pe(O,j)),F=M.observable(!1),z=M.observable([]),oe=M.observable("(nothing yet)"),ne=M.observable(!1),Ve=M.computed(()=>pe(ke(),p.value)),B=M.computed(()=>Ve.value!==K.value),ge=M.computed(()=>yt(ke(),ye)),we=M.observable([]);m(M.effect(()=>{we.value=n.overrides.value.map((e,t)=>ft(e,t,{custom:P,known:ye}))}));const _=e=>bt(e,P),Ye={...n,saving:F,rows:we,cssText:oe,cssError:ne,dirty:B,errorsText:M.computed(()=>z.value.join(" \xB7 ")),problemsText:M.computed(()=>ge.value.length?`${ge.value.map(e=>`${e.where}: "${e.id}"`).join(", ")} - no longer available, so its base theme shows. Pick another.`:""),liveSite:M.computed(()=>{const e=p.value;if(e.autoTheme?.enabled){const t=ut(e.autoTheme)==="day"?"day":"night";return`Site: ${_(e.autoTheme.dayTheme)} by day, ${_(e.autoTheme.nightTheme)} by night (${t} now)`}return`Site: ${_(e.theme||"charcoal-dark")}`}),liveAdmin:M.computed(()=>`Admin: ${_(p.value.adminTheme||"charcoal-dark")}`),liveOverrides:M.computed(()=>{const e=p.value.overrides||[],t=e.filter(o=>o.enabled!==!1).length;return e.length?`${t} override${t===1?"":"s"} on`:""}),liveSwitcher:M.computed(()=>p.value.switcher?.enabled?"Theme switcher on":"")};function xe(e){if(!P.length)return;const t=document.createElement("optgroup");t.label="Custom themes";for(const o of P){const s=document.createElement("option");s.value=o.id,s.textContent=`${o.label} (on ${o.baseTheme})`,t.appendChild(s)}e.appendChild(t)}function Ee(e,t){if(t&&![...e.options].some(o=>o.value===t)){const o=document.createElement("option");o.value=t,o.textContent=`${t} (not available)`,e.appendChild(o)}}const H=(e,t,o)=>{const s=f.querySelector(e);return Be(s,o),xe(s),Ee(s,t),s};H("#thx-site",O.theme),H("#thx-admin",O.adminTheme,{includeAdmin:!0}),H("#thx-day",O.dayTheme),H("#thx-night",O.nightTheme);let ae=0;async function Je(e){const t=++ae;try{const o=await x.theme.admin(e);if(t!==ae||!L())return;dt(o)}catch{}}let Se=!0;m(M.effect(()=>{const e=n.adminTheme.value||"charcoal-dark";if(Se){Se=!1;return}Je(e)})),m(()=>{ae++,(n.adminTheme.peek()||"charcoal-dark")!==(p.peek().adminTheme||"charcoal-dark")&&Pe(x)});let se=null,re=0;async function U(){const e=++re;try{const t=await x.theme.compile(X(te(),p.peek()));if(e!==re||!L())return;oe.value=t.css?.trim()||"(no overrides - nothing is injected)",ne.value=!1}catch(t){if(e!==re||!L())return;oe.value=t.message||String(t),ne.value=!0}}m(M.effect(()=>{n.overrides.value,clearTimeout(se),se=setTimeout(U,400)})),m(()=>clearTimeout(se));async function le(){if(!F.peek()){F.value=!0,z.value=[];try{const e=await x.theme.save(X(te(),p.peek()));if(!L())return;p.value=e;const t=Q(e);for(const[o,s]of Object.entries(t))n[o].value=s;K.value=pe(t,e);try{S.remove?S.remove(D):S.set(D,null)}catch{}await Pe(x),ht(gt),E.toast("Theme saved.",{type:"success"}),U()}catch(e){z.value=e.validation||[e.message||"Could not save the theme."],E.toast("Theme not saved - see the message at the top.",{type:"error"})}finally{F.value=!1}}}async function We(){if(!await E.confirm("Throw away your unsaved theme changes?"))return;const e=Q(p.peek());for(const[t,o]of Object.entries(e))n[t].value=o;z.value=[]}const Te=e=>n.overrides.peek().find(t=>t.id===e),C=e=>{n.overrides.value=e},ie=(e,t)=>C(n.overrides.peek().map(o=>o.id===e?t:o)),Ge=e=>n.overrides.peek().findIndex(t=>t.id===e),ce=new Map;async function Ce(e){if(!e)return null;if(ce.has(e))return ce.get(e);const o=(await x.theme.themeTokens(e).catch(()=>null))?.tokens||null;return ce.set(e,o),o}const Qe=()=>(n.autoEnabled.peek()?n.dayTheme.peek():n.theme.peek())||"charcoal-dark",V=new Set;function Y(e){const t=e?Te(e):G({});if(!t)return;const o=!e,s=G(t),i={label:M.observable(s.label),scope:M.observable(s.scope),enabled:M.observable(s.enabled),theme:M.observable(s.theme),css:M.observable(s.css),tokenErr:M.observable("")},c=E.slideover({title:o?"New override":`Override: ${ve(t.label||t.scope||"untitled")}`,size:"md",position:"right"}),v=Et(`
|
|
9
9
|
<div class="thx-form">
|
|
10
10
|
<div class="thx-grid">
|
|
11
11
|
<div>
|
|
@@ -22,7 +22,7 @@ import{api as N}from"../api.js";import{addBannerAction as je}from"../lib/plugin-
|
|
|
22
22
|
<label class="form-label" for="thx-o-scope">Target selector
|
|
23
23
|
<span data-help-title="Target selector" data-help="The region to re-theme - a CSS selector. Everything inside it follows. Pick a common region from the list or type your own (#site-navbar, .card, .page-body \u2026)."></span></label>
|
|
24
24
|
<input id="thx-o-scope" type="text" class="form-input font-mono" list="thx-scope-options" placeholder="#site-navbar" data-model="scope.value">
|
|
25
|
-
<datalist id="thx-scope-options">${
|
|
25
|
+
<datalist id="thx-scope-options">${wt.map(a=>`<option value="${a.value}" label="${a.label}"></option>`).join("")}</datalist>
|
|
26
26
|
</div>
|
|
27
27
|
<div>
|
|
28
28
|
<label class="form-label" for="thx-o-theme">Adopt a theme here
|
|
@@ -39,7 +39,7 @@ import{api as N}from"../api.js";import{addBannerAction as je}from"../lib/plugin-
|
|
|
39
39
|
<span data-icon="plus" data-icon-size="13"></span> Add token</button>
|
|
40
40
|
<small class="thx-err" data-bind-hidden="!tokenErr.value" data-bind-text="tokenErr.value"></small>
|
|
41
41
|
</div>
|
|
42
|
-
<details class="thx-o-css"${
|
|
42
|
+
<details class="thx-o-css"${s.css?" open":""}>
|
|
43
43
|
<summary>Custom CSS for this override</summary>
|
|
44
44
|
<textarea class="form-input font-mono" rows="4" style="margin-top:.5rem" placeholder="#site-navbar .brand { letter-spacing: .04em; }" data-model="css.value"></textarea>
|
|
45
45
|
<small class="thx-hint">Appended verbatim after the generated rule - you own these selectors. Prefer tokens: they survive a Domma upgrade; hand-written rules fight the cascade.</small>
|
|
@@ -52,4 +52,4 @@ import{api as N}from"../api.js";import{addBannerAction as je}from"../lib/plugin-
|
|
|
52
52
|
<button type="button" class="btn btn-sm btn-primary" data-on-click="done">
|
|
53
53
|
<span data-icon="check" data-icon-size="13"></span> ${o?"Add":"Done"}</button>
|
|
54
54
|
</div>
|
|
55
|
-
</div>`),
|
|
55
|
+
</div>`),de=v.querySelector("#thx-o-theme");Be(de,{includeDefault:!0,defaultLabel:"Keep the site theme"}),xe(de),Ee(de,s.theme);const tt=v.querySelector("#thx-token-names");for(const a of ee){const l=document.createElement("option");l.value=a,tt.appendChild(l)}const me=v.querySelector(".thx-toks");function De(a="",l=""){const b=document.createElement("div");b.className="thx-tok";const r=document.createElement("input");r.type="text",r.className="form-input font-mono thx-tok-name",r.placeholder="--dm-primary",r.setAttribute("list","thx-token-names"),r.value=a,r.setAttribute("aria-label","Token");const u=document.createElement("input");u.type="text",u.className="form-input thx-tok-value",u.placeholder="#0096c7",u.value=l,u.setAttribute("aria-label","Value");const k=document.createElement("input");k.type="color",k.title="Pick a colour";const g=He(l);/^#[0-9a-f]{6}$/i.test(g)&&(k.value=g),k.addEventListener("input",()=>{u.value=k.value});const w=document.createElement("button");return w.type="button",w.className="btn btn-sm btn-ghost",w.textContent="\xD7",w.title="Remove this token",w.addEventListener("click",()=>b.remove()),b.append(r,u,k,w),b}for(const[a,l]of Object.entries(s.tokens))me.appendChild(De(a,l));function Le(){const a={},l=[];return me.querySelectorAll(".thx-tok").forEach(b=>{const r=b.querySelector(".thx-tok-name").value.trim(),u=b.querySelector(".thx-tok-value").value.trim();!r||!u||(ee.length&&!ee.includes(r)&&l.push(r),a[r]=u)}),{tokens:a,bad:l}}const Re=()=>({...s,label:i.label.peek(),scope:i.scope.peek(),enabled:i.enabled.peek(),theme:i.theme.peek(),css:i.css.peek(),tokens:Le().tokens}),ot=JSON.stringify(X({overrides:[s]}).overrides),nt=()=>JSON.stringify(X({overrides:[Re()]}).overrides)!==ot,he=v.querySelector(".thx-diff-host");let qe=0;async function at(){const a=++qe;he.replaceChildren();const l=i.theme.peek();if(!l)return;const b=Qe(),r=document.createElement("div");if(r.className="thx-diff",r.style.marginTop=".5rem",l===b){const d=document.createElement("p");d.textContent="That is already the site theme, so this changes nothing on its own. Set a token below instead.",r.appendChild(d),he.appendChild(r);return}const[u,k]=await Promise.all([Ce(b),Ce(l)]);if(a!==qe||!u||!k)return;const g=vt(u,k),w=document.createElement("p");if(w.textContent=`Changes ${g.changed} of ${g.total} tokens here.`,r.appendChild(w),g.keepsFill){const d=document.createElement("p");d.className="is-warn",d.textContent="Both themes use the same surface colour, so this region keeps the fill it has now. Set --dm-card-bg (a card) or --dm-surface (most other regions) below to change it.",r.appendChild(d)}const W=document.createElement("div");W.className="thx-diff-list";const Ie=d=>{const N=document.createElement("span");return N.className="thx-sw",N.style.background=He(d)||"transparent",N};for(const d of g.shown){const N=document.createElement("span");N.append(Ie(d.from),document.createTextNode(`${d.name} `),Ie(d.to)),W.appendChild(N)}if(g.more){const d=document.createElement("span");d.textContent=`+${g.more} more`,W.appendChild(d)}r.appendChild(W),he.appendChild(r)}const st=M.effect(()=>{i.theme.value,at()});let J=!1,ue=!1;const je={addToken:()=>{const a=De();me.appendChild(a),a.querySelector("input").focus()},cancel:()=>c.close(),async remove(){await E.confirm(`Remove the override "${ve(t.label||t.scope||"untitled")}"? It goes when you save.`)&&(C(n.overrides.peek().filter(a=>a.id!==t.id)),J=!0,c.close())},done(){const{bad:a}=Le();if(a.length){i.tokenErr.value=`Not a token this Domma defines: ${a.join(", ")}. Pick one from the list.`;return}const l=Re();o?C([...n.overrides.peek(),l]):ie(t.id,l),J=!0,c.close()}};c.element.appendChild(v);const rt=Fe(v),lt=M.applyBindings(i,v,{methods:je}),it=a=>{(a.ctrlKey||a.metaKey)&&a.key==="Enter"&&(a.preventDefault(),je.done())};v.addEventListener("keydown",it),V.add(c),mt(c,()=>{st(),lt.dispose(),rt(),V.delete(c)});const ct=c.close.bind(c);c.close=async a=>{if(!J&&a!==!0&&nt()){if(ue)return;ue=!0;const l=await E.confirm("Discard your changes to this override?");if(ue=!1,!l)return}return J=!0,ct()},I.scan(v),c.open(),v.querySelector(o?"#thx-o-scope":"#thx-o-label")?.focus()}function Xe(e){const t=Te(e);if(!t)return[];const o=Ge(e),s=n.overrides.peek().length;return[{header:t.label||t.scope||`Override ${o+1}`},{label:"Edit\u2026",icon:"edit",action:()=>Y(e)},t.enabled?{label:"Switch off",icon:"pause",action:()=>ie(e,{...t,enabled:!1})}:{label:"Switch on",icon:"play",action:()=>ie(e,{...t,enabled:!0})},{label:"Duplicate",icon:"copy",action:()=>{const i=G({...t,id:"",label:t.label?`${t.label} copy`:""}),c=[...n.overrides.peek()];c.splice(o+1,0,i),C(c)}},{type:"divider"},{label:"Move up",icon:"arrow-up",disabled:o<=0,action:()=>C(_e(n.overrides.peek(),o,-1))},{label:"Move down",icon:"arrow-down",disabled:o>=s-1,action:()=>C(_e(n.overrides.peek(),o,1))},{type:"divider"},{label:"Remove\u2026",icon:"trash",danger:!0,action:async()=>{await E.confirm(`Remove the override "${ve(t.label||t.scope||"untitled")}"? It goes when you save.`)&&C(n.overrides.peek().filter(i=>i.id!==e))}}]}const A=f.querySelector(".thx-list");A.innerHTML=xt,E.tabs(f.querySelector(".thx-tabs"),{tabSelector:".tab-item",panelSelector:".tab-panel"});const Ze=M.applyBindings(Ye,f,{methods:{save:le,discard:We,refreshCss:U,addOverride:()=>Y(null)}});m(()=>Ze.dispose()),m(Fe(f,{position:"bottom"}));const et=E.contextMenu(A,{cursor:"glyph",match:".thx-row",inherit:!1,density:"compact",items:e=>Xe(e.dataset.id)});m(()=>et?.destroy?.());const Me=e=>{const t=e.target.closest(".thx-row");if(t){if(e.target.closest(".thx-kebab")){ze(e.target.closest(".thx-kebab"));return}Y(t.dataset.id)}},$e=e=>{const t=e.target.closest(".thx-row");t&&(e.key==="F10"&&e.shiftKey?(e.preventDefault(),ze(t)):e.key==="Enter"&&e.target===t&&Y(t.dataset.id))};A.addEventListener("click",Me),A.addEventListener("keydown",$e),m(()=>{A.removeEventListener("click",Me),A.removeEventListener("keydown",$e)}),Ke(h,{id:"theme-view-site",icon:"external-link",title:"View site",onClick:()=>window.open("/","_blank","noopener")}),Ke(h,{id:"theme-save",icon:"save",title:"Save (Ctrl+S)",onClick:()=>le()});const Oe=e=>{!(e.ctrlKey||e.metaKey)||e.key!=="s"||V.size||(e.preventDefault(),e.stopImmediatePropagation(),le())};document.addEventListener("keydown",Oe,!0),m(()=>document.removeEventListener("keydown",Oe,!0));const Ae=()=>{if(B.peek())try{S.set(D,{form:te(),base:K.peek(),at:new Date().toISOString()})}catch{}},Ne=e=>{B.peek()&&(Ae(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",Ne),m(()=>window.removeEventListener("beforeunload",Ne)),T={dirty:()=>B.peek(),dispose(){Z=!0,Ae(),[...V].forEach(e=>{try{e.close(!0)}catch{}}),y.splice(0).reverse().forEach(e=>{try{e()}catch{}})}},I.scan(f),U();let $=null;try{$=S.get(D)}catch{}if($?.form){try{S.remove?S.remove(D):S.set(D,null)}catch{}const e=$.base&&$.base!==K.peek(),t=$.at?` (${kt($.at)})`:"",o=e?`You left unsaved theme changes${t}, but the saved settings have changed since. Restore your version anyway?`:`You left unsaved theme changes${t}. Restore them?`;if(await E.confirm(o)&&L()){const s={...Q(p.peek()),...$.form};s.overrides=(s.overrides||[]).map(i=>G(i));for(const[i,c]of Object.entries(s))n[i]&&(n[i].value=c)}}}};
|
package/package.json
CHANGED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared outreach audience: one store of contacts and named groups,
|
|
3
|
+
* imported from the Contacts Tool once and targeted by every outreach Tool
|
|
4
|
+
* (Surveys, Campaigns, Onboard...). Collections default to the shared
|
|
5
|
+
* `outreach-contacts` / `outreach-groups` (schemas in ./collections - each
|
|
6
|
+
* consuming plugin ships a copy; setupPlugin skips a slug that already
|
|
7
|
+
* exists, so the first Tool on the site creates them and the rest share).
|
|
8
|
+
*
|
|
9
|
+
* Lifted from Surveys 1.4 (lib/audience.js), generalised. A contact's
|
|
10
|
+
* `groups` holds group NAMES, not ids - a group row is a label with a
|
|
11
|
+
* description, membership lives on the contact.
|
|
12
|
+
*
|
|
13
|
+
* @module _lib/outreach/audience
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
function parseGroups(raw) {
|
|
17
|
+
if (Array.isArray(raw)) return raw;
|
|
18
|
+
if (typeof raw === 'string') { try { return JSON.parse(raw); } catch { return raw ? [raw] : []; } }
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {{createEntry,listEntries,getEntry,deleteEntry?}} col collections service
|
|
24
|
+
* @param {{contactsSlug?: string, groupsSlug?: string}} [opts]
|
|
25
|
+
*/
|
|
26
|
+
export function createAudience(col, {contactsSlug = 'outreach-contacts', groupsSlug = 'outreach-groups'} = {}) {
|
|
27
|
+
async function listContacts() {
|
|
28
|
+
const {entries} = await col.listEntries(contactsSlug, {limit: 100000});
|
|
29
|
+
return entries.map(e => ({id: e.id, name: e.data.name ?? '', email: e.data.email ?? '', groups: parseGroups(e.data.groups)}));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function contactIdsForGroups(groupNames) {
|
|
33
|
+
const want = new Set(groupNames);
|
|
34
|
+
const contacts = await listContacts();
|
|
35
|
+
return contacts.filter(c => c.groups.some(g => want.has(g))).map(c => c.id);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function importFromContacts(sourceContacts) {
|
|
39
|
+
let imported = 0;
|
|
40
|
+
for (const c of sourceContacts) {
|
|
41
|
+
const email = (c.email ?? '').trim();
|
|
42
|
+
if (!email) continue;
|
|
43
|
+
await col.createEntry(contactsSlug, {
|
|
44
|
+
name: (c.name ?? '').trim(),
|
|
45
|
+
email,
|
|
46
|
+
groups: parseGroups(c.groups)
|
|
47
|
+
}, {source: 'import-contacts'});
|
|
48
|
+
imported++;
|
|
49
|
+
}
|
|
50
|
+
return {imported};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Add one contact by hand. Email required by the schema. */
|
|
54
|
+
async function addContact({name, email, groups} = {}, source = 'admin') {
|
|
55
|
+
return col.createEntry(contactsSlug, {
|
|
56
|
+
name: name ?? '', email, groups: Array.isArray(groups) ? groups : []
|
|
57
|
+
}, {source});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function removeContact(id) {
|
|
61
|
+
if (typeof col.deleteEntry !== 'function') return false;
|
|
62
|
+
await col.deleteEntry(contactsSlug, id);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function listGroups() {
|
|
67
|
+
const {entries} = await col.listEntries(groupsSlug, {limit: 10000});
|
|
68
|
+
return entries.map(e => ({id: e.id, name: e.data.name, description: e.data.description ?? ''}));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function addGroup({name, description} = {}, source = 'admin') {
|
|
72
|
+
return col.createEntry(groupsSlug, {name, description: description ?? ''}, {source});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {listContacts, contactIdsForGroups, importFromContacts, addContact, removeContact, listGroups, addGroup, contactsSlug, groupsSlug};
|
|
76
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"slug": "outreach-contacts",
|
|
3
|
+
"title": "Outreach Contacts",
|
|
4
|
+
"description": "The shared outreach audience - contacts targeted by Surveys, Campaigns and other outreach Tools.",
|
|
5
|
+
"storage": {"adapter": "mongodb", "connection": "default"},
|
|
6
|
+
"fields": [
|
|
7
|
+
{"name": "name", "label": "Name", "type": "text", "required": false},
|
|
8
|
+
{"name": "email", "label": "Email", "type": "text", "required": true},
|
|
9
|
+
{"name": "groups","label": "Groups","type": "text", "required": false},
|
|
10
|
+
{"name": "meta", "label": "Meta", "type": "textarea", "required": false}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"slug": "outreach-groups",
|
|
3
|
+
"title": "Outreach Groups",
|
|
4
|
+
"description": "Named audience groups for the shared outreach contacts.",
|
|
5
|
+
"storage": {"adapter": "mongodb", "connection": "default"},
|
|
6
|
+
"fields": [
|
|
7
|
+
{"name": "name", "label": "Name", "type": "text", "required": true},
|
|
8
|
+
{"name": "description", "label": "Description", "type": "textarea", "required": false}
|
|
9
|
+
]
|
|
10
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"slug": "outreach-unsubscribes",
|
|
3
|
+
"title": "Outreach Unsubscribes",
|
|
4
|
+
"description": "The shared suppression list - addresses no outreach Tool may email.",
|
|
5
|
+
"storage": {"adapter": "mongodb", "connection": "default"},
|
|
6
|
+
"fields": [
|
|
7
|
+
{"name": "email", "label": "Email", "type": "text", "required": true},
|
|
8
|
+
{"name": "at", "label": "At", "type": "text", "required": false},
|
|
9
|
+
{"name": "source", "label": "Source", "type": "text", "required": false}
|
|
10
|
+
]
|
|
11
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared expiry state: a thing that closes by a DATE or by a USAGE TALLY,
|
|
3
|
+
* whichever is reached first. The vocabulary is deliberately the same as
|
|
4
|
+
* Cart Pro's discount codes (shopping-cart-pro/lib/codes.js):
|
|
5
|
+
*
|
|
6
|
+
* {active, startsAt, endsAt, limit, uses}
|
|
7
|
+
*
|
|
8
|
+
* Dates are 'YYYY-MM-DD', inclusive, '' for open; `limit` is null for
|
|
9
|
+
* unlimited; `uses` is the server's counter. The two modules must never
|
|
10
|
+
* disagree about what "expired" means - change one, change both.
|
|
11
|
+
*
|
|
12
|
+
* One deliberate difference: an entry that never carried `active` (a
|
|
13
|
+
* campaign offer, say) is treated as active; Cart Pro's cleanCode always
|
|
14
|
+
* writes the field, so its rows never hit that branch.
|
|
15
|
+
*
|
|
16
|
+
* @module _lib/outreach/expiry
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** 'YYYY-MM-DD' for a time, in the server's zone. */
|
|
20
|
+
export const dayOf = (t) => {
|
|
21
|
+
const d = new Date(t);
|
|
22
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Live, scheduled, ended, used up or paused - for state chips and refusals.
|
|
27
|
+
*
|
|
28
|
+
* @param {{active?: boolean, startsAt?: string, endsAt?: string, limit?: number|null, uses?: number}} c
|
|
29
|
+
* @param {number} [now]
|
|
30
|
+
* @returns {'paused'|'scheduled'|'ended'|'used'|'live'}
|
|
31
|
+
*/
|
|
32
|
+
export function codeState(c, now = Date.now()) {
|
|
33
|
+
const today = dayOf(now);
|
|
34
|
+
const active = c.active === undefined ? true : Boolean(c.active);
|
|
35
|
+
if (!active) return 'paused';
|
|
36
|
+
if (c.startsAt && today < c.startsAt) return 'scheduled';
|
|
37
|
+
if (c.endsAt && today > c.endsAt) return 'ended';
|
|
38
|
+
if (c.limit !== null && c.limit !== undefined && (Number(c.uses) || 0) >= c.limit) return 'used';
|
|
39
|
+
return 'live';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Why a thing is not live now, or null when it is. */
|
|
43
|
+
export function closedReason(c, now = Date.now()) {
|
|
44
|
+
const state = codeState(c, now);
|
|
45
|
+
return state === 'live' ? null : state;
|
|
46
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The outreach library: the audience, token, ledger, sending, suppression
|
|
3
|
+
* and expiry plumbing shared by every Tool that emails people and follows
|
|
4
|
+
* up (Surveys, Campaigns, Onboard). See each module's header; collection
|
|
5
|
+
* schemas the consuming plugins copy are in ./collections.
|
|
6
|
+
*
|
|
7
|
+
* @module _lib/outreach
|
|
8
|
+
*/
|
|
9
|
+
export {generateToken, hashToken, formatCode, normaliseCode} from './tokens.js';
|
|
10
|
+
export {createAudience} from './audience.js';
|
|
11
|
+
export {createLedger} from './ledger.js';
|
|
12
|
+
export {createSuppression, normaliseEmail} from './suppression.js';
|
|
13
|
+
export {escapeHtml, escapeAttr, outreachBaseUrl, renderInvite, sendBatch} from './sending.js';
|
|
14
|
+
export {dayOf, codeState, closedReason} from './expiry.js';
|
|
15
|
+
export {UNSUBSCRIBE_PATH, mintSecret, signEmail, unsubscribeUrl, verifyUnsubscribe} from './unsubscribe.js';
|