domma-cms 1.6.13 → 1.6.15

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.
@@ -1 +1 @@
1
- export const WIDTHS=["narrow","normal","wide","full"],WIDTH_LABELS={narrow:"Narrow",normal:"Normal",wide:"Wide",full:"Full width"},WIDTH_HINTS={narrow:"768px - a reading column",normal:"1100px",wide:"75% of the window, up to 2000px",full:"edge to edge"},WIDTH_SHARE={narrow:"40%",normal:"62%",wide:"82%",full:"100%"},FILTERS=["all","builtin","custom"];const b=/^[a-zA-Z0-9#(),.\s%/-]+$/;export function layoutKey(e){return String(e??"").toLowerCase().trim().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"")}const d=(e,r,a=`${r}s`)=>`${e} ${e===1?r:a}`;export function chromeText(e){const r=[e.navbar!==!1&&"navbar",e.footer!==!1&&"footer",e.sidebar===!0&&"sidebar"].filter(Boolean);return r.length?r.join(", "):"no navbar or footer"}export function arrangeLayouts(e,r={},{filter:a="all",query:o=""}={}){const s=String(o).trim().toLowerCase(),n=Object.entries(e||{}).map(([t,i])=>({...i,key:t})),l=n.filter(t=>t.builtin),c=n.filter(t=>!t.builtin).sort((t,i)=>String(t.label||t.key).localeCompare(String(i.label||i.key)));return[...l,...c].filter(t=>a==="builtin"?t.builtin:a==="custom"?!t.builtin:!0).filter(t=>!s||[t.key,t.label,t.description,t.class].some(i=>String(i||"").toLowerCase().includes(s))).map(t=>layoutRow(t,(r.byKey||{})[t.key]||[]))}export function layoutRow(e,r=[]){const a=WIDTHS.includes(e.width)?e.width:"normal";return{key:e.key,label:e.label||e.key,meta:[e.key,e.description,chromeText(e)].filter(Boolean).join(" \xB7 "),builtin:e.builtin===!0,widthLabel:WIDTH_LABELS[a],used:r.length,usedText:r.length?d(r.length,"page"):"Unused",usedTone:r.length?"is-info":"is-muted",hasNav:e.navbar!==!1,hasFoot:e.footer!==!1,hasSide:e.sidebar===!0,sideRight:e.sidebar===!0&&e.sidebarSide==="right",share:WIDTH_SHARE[a],bg:e.bgColor&&b.test(e.bgColor)?e.bgColor:""}}export function formFromPreset(e={}){return{label:e.label||"",description:e.description||"",width:WIDTHS.includes(e.width)?e.width:"normal",navbar:e.navbar!==!1,footer:e.footer!==!1,sidebar:e.sidebar===!0,sidebarSide:e.sidebarSide==="right"?"right":"left",useBg:!!e.bgColor,bgColor:e.bgColor&&/^#[0-9a-f]{6}$/i.test(e.bgColor)?e.bgColor:e.bgColor||"#ffffff",bgImage:e.bgImage||"",cssClass:e.class||""}}export function checkLayout(e,{isNew:r,taken:a=[]}={}){const o=e||{},s={},n=String(o.label??"").trim(),l=r?layoutKey(n):"";n?n.length>60?s.label="Up to 60 characters.":r&&!l?s.label="Needs at least one letter or number.":r&&a.includes(l)&&(s.label=`"${l}" is already a layout - choose another name.`):s.label="A layout needs a name.";const c=o.useBg?String(o.bgColor||"").trim():"";c&&!b.test(c)&&(s.bgColor="Not a colour the server accepts.");const t=String(o.bgImage??"").trim(),i=String(o.cssClass??"").trim();/[^a-zA-Z0-9\s_-]/.test(i)&&(s.cssClass="Letters, numbers, spaces, hyphens and underscores only.");const u={label:n,description:String(o.description??"").trim().slice(0,200),width:WIDTHS.includes(o.width)?o.width:"normal",navbar:o.navbar!==!1,footer:o.footer!==!1,sidebar:o.sidebar===!0,sidebarSide:o.sidebarSide==="right"?"right":"left",bgColor:c,bgImage:t,class:i};return{errors:s,payload:u,key:l}}export function checkOptions(e){const r={},a=String(e?.spacerSize??"").trim(),o=Number(a);(a===""||!Number.isInteger(o)||o<0||o>500)&&(r.spacerSize="A whole number of pixels, 0 to 500.");const s=String(e?.spacerClass??"").trim();return/[^a-zA-Z0-9\s_-]/.test(s)&&(r.spacerClass="Letters, numbers, spaces, hyphens and underscores only."),{errors:r,payload:{spacerSize:Number.isInteger(o)?o:8,spacerClass:s}}}
1
+ export const WIDTHS=["narrow","normal","wide","full"],WIDTH_LABELS={narrow:"Narrow",normal:"Normal",wide:"Wide",full:"Full width"},WIDTH_HINTS={narrow:"768px - a reading column",normal:"1100px",wide:"75% of the window, up to 2000px",full:"edge to edge"},WIDTH_SHARE={narrow:"40%",normal:"62%",wide:"82%",full:"100%"},FILTERS=["all","builtin","custom"];const b=/^[a-zA-Z0-9#(),.\s%/-]+$/;export function layoutKey(e){return String(e??"").toLowerCase().trim().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"")}const g=(e,t,l=`${t}s`)=>`${e} ${e===1?t:l}`;export function chromeText(e){const t=[e.navbar!==!1&&"navbar",e.footer!==!1&&"footer",e.sidebar===!0&&"sidebar"].filter(Boolean);return t.length?t.join(", "):"no navbar or footer"}export function arrangeLayouts(e,t={},{filter:l="all",query:r=""}={}){const i=String(r).trim().toLowerCase(),s=Object.entries(e||{}).map(([o,n])=>({...n,key:o})),a=s.filter(o=>o.builtin),c=s.filter(o=>!o.builtin).sort((o,n)=>String(o.label||o.key).localeCompare(String(n.label||n.key)));return[...a,...c].filter(o=>l==="builtin"?o.builtin:l==="custom"?!o.builtin:!0).filter(o=>!i||[o.key,o.label,o.description,o.class].some(n=>String(n||"").toLowerCase().includes(i))).map(o=>layoutRow(o,(t.byKey||{})[o.key]||[]))}export function layoutRow(e,t=[]){const l=WIDTHS.includes(e.width)?e.width:"normal";return{key:e.key,label:e.label||e.key,meta:[e.key,e.description,chromeText(e)].filter(Boolean).join(" \xB7 "),builtin:e.builtin===!0,widthLabel:WIDTH_LABELS[l],used:t.length,usedText:t.length?g(t.length,"page"):"Unused",usedTone:t.length?"is-info":"is-muted",hasNav:e.navbar!==!1,hasFoot:e.footer!==!1,hasSide:e.sidebar===!0,sideRight:e.sidebar===!0&&e.sidebarSide==="right",share:WIDTH_SHARE[l],bg:e.bgColor&&b.test(e.bgColor)?e.bgColor:""}}export function formFromPreset(e={}){return{label:e.label||"",description:e.description||"",width:WIDTHS.includes(e.width)?e.width:"normal",navbar:e.navbar!==!1,footer:e.footer!==!1,sidebar:e.sidebar===!0,sidebarSide:e.sidebarSide==="right"?"right":"left",useBg:!!e.bgColor,bgColor:e.bgColor&&/^#[0-9a-f]{6}$/i.test(e.bgColor)?e.bgColor:e.bgColor||"#ffffff",bgImage:e.bgImage||"",cssClass:e.class||"",...contentsForm(e.contents)}}export function contentsForm(e={}){return{cWidth:e.width!=null?String(e.width):"",cSticky:e.sticky!==!1,cScrollSpy:e.scrollSpy!==!1,cFloating:e.floating===!0,cFloatAnchor:e.floatAnchor||"TR",cMovable:e.movable===!0,cCollapsible:e.collapsible===!0,cCollapsed:e.collapsed===!0,cAutohide:e.autohide===!0,cHeadings:e.headings||"auto",cNumbered:e.numbered===!0,cTitle:e.title||"",cExclude:e.exclude||"",cHover:e.hover||"",cSelected:e.selected||"",cAccent:e.accent||"",cFont:e.font||"",cFontSize:e.fontSize||"",cFontWeight:e.fontWeight||""}}export function contentsFromForm(e={}){const t={},l=parseInt(e.cWidth,10);Number.isFinite(l)&&(t.width=l),e.cSticky===!1&&(t.sticky=!1),e.cScrollSpy===!1&&(t.scrollSpy=!1),e.cFloating===!0?(t.floating=!0,t.floatAnchor=e.cFloatAnchor||"TR",e.cMovable===!0&&(t.movable=!0)):e.cAutohide===!0?t.autohide=!0:e.cCollapsible===!0&&(t.collapsible=!0,e.cCollapsed===!0&&(t.collapsed=!0)),e.cHeadings&&e.cHeadings!=="auto"&&(t.headings=e.cHeadings),e.cNumbered===!0&&(t.numbered=!0);for(const[r,i]of[["title","cTitle"],["exclude","cExclude"],["hover","cHover"],["selected","cSelected"],["accent","cAccent"],["font","cFont"],["fontSize","cFontSize"],["fontWeight","cFontWeight"]]){const s=String(e[i]??"").trim();s&&(t[r]=s)}return t}export function checkLayout(e,{isNew:t,taken:l=[]}={}){const r=e||{},i={},s=String(r.label??"").trim(),a=t?layoutKey(s):"";s?s.length>60?i.label="Up to 60 characters.":t&&!a?i.label="Needs at least one letter or number.":t&&l.includes(a)&&(i.label=`"${a}" is already a layout - choose another name.`):i.label="A layout needs a name.";const c=r.useBg?String(r.bgColor||"").trim():"";c&&!b.test(c)&&(i.bgColor="Not a colour the server accepts.");const o=String(r.bgImage??"").trim(),n=String(r.cssClass??"").trim();/[^a-zA-Z0-9\s_-]/.test(n)&&(i.cssClass="Letters, numbers, spaces, hyphens and underscores only.");const u=String(r.cWidth??"").trim();u&&!(Number(u)>=160&&Number(u)<=480)&&(i.cWidth="From 160 to 480 px, or blank for the usual 260.");const d=r.sidebar===!0?contentsFromForm(r):{},f={label:s,description:String(r.description??"").trim().slice(0,200),width:WIDTHS.includes(r.width)?r.width:"normal",navbar:r.navbar!==!1,footer:r.footer!==!1,sidebar:r.sidebar===!0,sidebarSide:r.sidebarSide==="right"?"right":"left",bgColor:c,bgImage:o,class:n,...Object.keys(d).length&&{contents:d}};return{errors:i,payload:f,key:a}}export function checkOptions(e){const t={},l=String(e?.spacerSize??"").trim(),r=Number(l);(l===""||!Number.isInteger(r)||r<0||r>500)&&(t.spacerSize="A whole number of pixels, 0 to 500.");const i=String(e?.spacerClass??"").trim();return/[^a-zA-Z0-9\s_-]/.test(i)&&(t.spacerClass="Letters, numbers, spaces, hyphens and underscores only."),{errors:t,payload:{spacerSize:Number.isInteger(r)?r:8,spacerClass:i}}}
@@ -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:"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"],["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"}];
@@ -1 +1 @@
1
- export const TAB_FIELDS={general:["title","tagline","siteUrl","adminHome","spacerSize","bodyClass"],seo:["seoTitle","seoSeparator","seoDescription"],footer:["footerCopyright","footerMotionToggle","footerClass","footerSocialClass","socialTwitter","socialFacebook","socialInstagram","socialLinkedin","socialGithub","socialYoutube"],email:["smtpHost","smtpPort","smtpUser","smtpPass","smtpSecure","smtpFromAddress","smtpFromName"],backToTop:["bttEnabled","bttSmooth","bttLabel","bttThreshold","bttPosition","bttOffset","bttBottomOffset"],cookies:["ccEnabled","ccMessage","ccAcceptAll","ccRejectAll","ccCustomize","ccSavePrefs","ccPrivacyText","ccPrivacyUrl","ccCookieText","ccCookieUrl","ccPosition","ccLayout","ccTheme","ccShowFunctional","ccShowAnalytics","ccShowMarketing","ccVersion"],breadcrumbs:["bcEnabled","bcHomeLabel","bcPosition","bcOffsetX","bcOffsetY"],css:["css"]},SECRET_FIELDS=["smtpPass"],BREADCRUMB_POSITIONS=["TL","TR","BL","BR"];const e=(t,s="")=>t==null?s:String(t),i=(t,s)=>{const c=Number.parseInt(t,10);return Number.isFinite(c)?c:s};export function homeValue(t){return/^\/[A-Za-z0-9\-_/]*$/.test(t||"")&&String(t).replace(/\/+$/,"")||"/"}export function formFromSettings(t={},s=""){const c=t.backToTop||{},o=t.cookieConsent||{},r=t.breadcrumbs||{};return{title:e(t.title),tagline:e(t.tagline),siteUrl:e(t.baseUrl),adminHome:homeValue(t.adminHome),spacerSize:e(t.layoutOptions?.spacerSize??40),bodyClass:e(t.bodyClass),seoTitle:e(t.seo?.defaultTitle),seoSeparator:e(t.seo?.titleSeparator," | "),seoDescription:e(t.seo?.defaultDescription),footerCopyright:e(t.footer?.copyright),footerMotionToggle:t.footer?.showMotionToggle!==!1,footerClass:e(t.footer?.class),footerSocialClass:e(t.footer?.socialClass),socialTwitter:e(t.social?.twitter),socialFacebook:e(t.social?.facebook),socialInstagram:e(t.social?.instagram),socialLinkedin:e(t.social?.linkedin),socialGithub:e(t.social?.github),socialYoutube:e(t.social?.youtube),smtpHost:e(t.smtp?.host),smtpPort:e(t.smtp?.port||587),smtpUser:e(t.smtp?.user),smtpPass:"",smtpSecure:t.smtp?.secure===!0,smtpFromAddress:e(t.smtp?.fromAddress),smtpFromName:e(t.smtp?.fromName),bttEnabled:c.enabled!==!1,bttSmooth:c.smooth!==!1,bttLabel:e(c.label),bttThreshold:e(c.scrollThreshold??300),bttPosition:e(c.position,"bottom-right"),bttOffset:e(c.offset??32),bttBottomOffset:e(c.bottomOffset??c.offset??32),ccEnabled:o.enabled!==!1,ccMessage:e(o.message),ccAcceptAll:e(o.acceptAllText,"Accept All"),ccRejectAll:e(o.rejectAllText,"Reject All"),ccCustomize:e(o.customizeText,"Customize"),ccSavePrefs:e(o.savePreferencesText,"Save Preferences"),ccPrivacyText:e(o.privacyPolicyText,"Privacy Policy"),ccPrivacyUrl:e(o.privacyPolicyUrl),ccCookieText:e(o.cookiePolicyText,"Cookie Policy"),ccCookieUrl:e(o.cookiePolicyUrl),ccPosition:e(o.position,"bottom"),ccLayout:e(o.layout,"bar"),ccTheme:e(o.theme,"dark"),ccShowFunctional:o.showFunctional!==!1,ccShowAnalytics:o.showAnalytics!==!1,ccShowMarketing:o.showMarketing!==!1,ccVersion:e(o.consentVersion,"1.0"),bcEnabled:r.enabled===!0,bcHomeLabel:e(r.homeLabel,"Home"),bcPosition:BREADCRUMB_POSITIONS.includes(r.position)?r.position:"TL",bcOffsetX:e(r.offsetX??8),bcOffsetY:e(r.offsetY??8),css:e(s)}}export function payloadFromForm(t,s={},{adminHomeReady:c=!0}={}){const o=r=>e(r).trim();return{title:o(t.title),tagline:o(t.tagline),baseUrl:o(t.siteUrl).replace(/\/+$/,""),...c&&{adminHome:o(t.adminHome)||"/"},layoutOptions:{spacerSize:i(t.spacerSize,40)||40},bodyClass:o(t.bodyClass),seo:{defaultTitle:o(t.seoTitle),titleSeparator:e(t.seoSeparator)||" | ",defaultDescription:o(t.seoDescription)},footer:{copyright:o(t.footerCopyright),showMotionToggle:t.footerMotionToggle===!0,links:s.footer?.links||[],class:o(t.footerClass),socialClass:o(t.footerSocialClass)},social:{twitter:o(t.socialTwitter),facebook:o(t.socialFacebook),instagram:o(t.socialInstagram),linkedin:o(t.socialLinkedin),github:o(t.socialGithub),youtube:o(t.socialYoutube)},smtp:{host:o(t.smtpHost),port:i(t.smtpPort,587)||587,user:o(t.smtpUser),pass:e(t.smtpPass),secure:t.smtpSecure===!0,fromAddress:o(t.smtpFromAddress),fromName:o(t.smtpFromName)},backToTop:{enabled:t.bttEnabled===!0,scrollThreshold:i(t.bttThreshold,300),position:e(t.bttPosition)||"bottom-right",offset:i(t.bttOffset,32),bottomOffset:i(t.bttBottomOffset,32),label:o(t.bttLabel),smooth:t.bttSmooth===!0},cookieConsent:{enabled:t.ccEnabled===!0,message:o(t.ccMessage),acceptAllText:o(t.ccAcceptAll),rejectAllText:o(t.ccRejectAll),customizeText:o(t.ccCustomize),savePreferencesText:o(t.ccSavePrefs),privacyPolicyText:o(t.ccPrivacyText),privacyPolicyUrl:o(t.ccPrivacyUrl),cookiePolicyText:o(t.ccCookieText),cookiePolicyUrl:o(t.ccCookieUrl),position:e(t.ccPosition)||"bottom",layout:e(t.ccLayout)||"bar",theme:e(t.ccTheme)||"dark",showFunctional:t.ccShowFunctional===!0,showAnalytics:t.ccShowAnalytics===!0,showMarketing:t.ccShowMarketing===!0,consentVersion:o(t.ccVersion)||"1.0"},breadcrumbs:{enabled:t.bcEnabled===!0,homeLabel:o(t.bcHomeLabel)||"Home",position:BREADCRUMB_POSITIONS.includes(t.bcPosition)?t.bcPosition:"TL",offsetX:i(t.bcOffsetX,8),offsetY:i(t.bcOffsetY,8)}}}export function siteUrlError(t){const s=e(t).trim().replace(/\/+$/,"");return s?/^https?:\/\//i.test(s)?/^https?:\/\/([a-z0-9.\-]+|\[[0-9a-f:.]+\])(:\d{1,5})?$/i.test(s)?"":"Just the address, like https://example.com - no page path after it.":"Start with https:// (or http://).":""}const a=t=>typeof t=="boolean"?t:e(t);export function snapshot(t){return JSON.stringify(Object.keys(t).sort().map(s=>[s,a(t[s])]))}export function changedTabs(t,s){const c=[];for(const[o,r]of Object.entries(TAB_FIELDS))r.some(n=>a(t[n])!==a(s[n]))&&c.push(o);return c}export function draftOf(t){return Object.fromEntries(Object.entries(t).filter(([s])=>!SECRET_FIELDS.includes(s)))}export function settingsProblems(t){const s=[];return e(t.smtpHost).trim()?e(t.smtpFromAddress).trim()||s.push({tone:"warn",tab:"email",text:"No From address - mail has no sender and many servers refuse it."}):s.push({tone:"danger",tab:"email",text:"No SMTP host - mail goes to a throwaway test inbox and reaches no one (password resets, form notifications)."}),t.ccEnabled===!0&&!e(t.ccPrivacyUrl).trim()&&s.push({tone:"warn",tab:"cookies",text:"The cookie banner is on but links to no privacy policy."}),s}export function describeCacheKey(t){const s=/^page:(.*?):m\d+:role([^:]*):/.exec(String(t||""));return s?{path:s[1]||"/",role:s[2]||"anon",page:!0}:{path:String(t||""),role:"",page:!1}}export function expiresIn(t,s=Date.now()){if(t==null)return"no expiry";const c=t-s;return c<=0?"expired":c<6e4?`${Math.round(c/1e3)}s`:c<36e5?`${Math.round(c/6e4)}m`:`${Math.round(c/36e5)}h`}
1
+ export const TAB_FIELDS={general:["title","tagline","siteUrl","adminHome","spacerSize","bodyClass"],seo:["seoTitle","seoSeparator","seoDescription"],footer:["footerCopyright","footerMotionToggle","footerMotionDefault","footerMotionControl","footerClass","footerSocialClass","socialTwitter","socialFacebook","socialInstagram","socialLinkedin","socialGithub","socialYoutube"],email:["smtpHost","smtpPort","smtpUser","smtpPass","smtpSecure","smtpFromAddress","smtpFromName"],backToTop:["bttEnabled","bttSmooth","bttLabel","bttThreshold","bttPosition","bttOffset","bttBottomOffset"],cookies:["ccEnabled","ccMessage","ccAcceptAll","ccRejectAll","ccCustomize","ccSavePrefs","ccPrivacyText","ccPrivacyUrl","ccCookieText","ccCookieUrl","ccPosition","ccLayout","ccTheme","ccShowFunctional","ccShowAnalytics","ccShowMarketing","ccVersion"],breadcrumbs:["bcEnabled","bcHomeLabel","bcPosition","bcOffsetX","bcOffsetY"],css:["css"]},SECRET_FIELDS=["smtpPass"],BREADCRUMB_POSITIONS=["TL","TR","BL","BR"];const o=(t,s="")=>t==null?s:String(t),i=(t,s)=>{const c=Number.parseInt(t,10);return Number.isFinite(c)?c:s};export function homeValue(t){return/^\/[A-Za-z0-9\-_/]*$/.test(t||"")&&String(t).replace(/\/+$/,"")||"/"}export function formFromSettings(t={},s=""){const c=t.backToTop||{},e=t.cookieConsent||{},r=t.breadcrumbs||{};return{title:o(t.title),tagline:o(t.tagline),siteUrl:o(t.baseUrl),adminHome:homeValue(t.adminHome),spacerSize:o(t.layoutOptions?.spacerSize??40),bodyClass:o(t.bodyClass),seoTitle:o(t.seo?.defaultTitle),seoSeparator:o(t.seo?.titleSeparator," | "),seoDescription:o(t.seo?.defaultDescription),footerCopyright:o(t.footer?.copyright),footerMotionToggle:t.footer?.showMotionToggle!==!1,footerMotionDefault:["off","on"].includes(t.footer?.motionDefault)?t.footer.motionDefault:"system",footerMotionControl:t.footer?.motionControl==="checkbox"?"checkbox":"switch",footerClass:o(t.footer?.class),footerSocialClass:o(t.footer?.socialClass),socialTwitter:o(t.social?.twitter),socialFacebook:o(t.social?.facebook),socialInstagram:o(t.social?.instagram),socialLinkedin:o(t.social?.linkedin),socialGithub:o(t.social?.github),socialYoutube:o(t.social?.youtube),smtpHost:o(t.smtp?.host),smtpPort:o(t.smtp?.port||587),smtpUser:o(t.smtp?.user),smtpPass:"",smtpSecure:t.smtp?.secure===!0,smtpFromAddress:o(t.smtp?.fromAddress),smtpFromName:o(t.smtp?.fromName),bttEnabled:c.enabled!==!1,bttSmooth:c.smooth!==!1,bttLabel:o(c.label),bttThreshold:o(c.scrollThreshold??300),bttPosition:o(c.position,"bottom-right"),bttOffset:o(c.offset??32),bttBottomOffset:o(c.bottomOffset??c.offset??32),ccEnabled:e.enabled!==!1,ccMessage:o(e.message),ccAcceptAll:o(e.acceptAllText,"Accept All"),ccRejectAll:o(e.rejectAllText,"Reject All"),ccCustomize:o(e.customizeText,"Customize"),ccSavePrefs:o(e.savePreferencesText,"Save Preferences"),ccPrivacyText:o(e.privacyPolicyText,"Privacy Policy"),ccPrivacyUrl:o(e.privacyPolicyUrl),ccCookieText:o(e.cookiePolicyText,"Cookie Policy"),ccCookieUrl:o(e.cookiePolicyUrl),ccPosition:o(e.position,"bottom"),ccLayout:o(e.layout,"bar"),ccTheme:o(e.theme,"dark"),ccShowFunctional:e.showFunctional!==!1,ccShowAnalytics:e.showAnalytics!==!1,ccShowMarketing:e.showMarketing!==!1,ccVersion:o(e.consentVersion,"1.0"),bcEnabled:r.enabled===!0,bcHomeLabel:o(r.homeLabel,"Home"),bcPosition:BREADCRUMB_POSITIONS.includes(r.position)?r.position:"TL",bcOffsetX:o(r.offsetX??8),bcOffsetY:o(r.offsetY??8),css:o(s)}}export function payloadFromForm(t,s={},{adminHomeReady:c=!0}={}){const e=r=>o(r).trim();return{title:e(t.title),tagline:e(t.tagline),baseUrl:e(t.siteUrl).replace(/\/+$/,""),...c&&{adminHome:e(t.adminHome)||"/"},layoutOptions:{spacerSize:i(t.spacerSize,40)||40},bodyClass:e(t.bodyClass),seo:{defaultTitle:e(t.seoTitle),titleSeparator:o(t.seoSeparator)||" | ",defaultDescription:e(t.seoDescription)},footer:{copyright:e(t.footerCopyright),showMotionToggle:t.footerMotionToggle===!0,links:s.footer?.links||[],motionDefault:["off","on"].includes(t.footerMotionDefault)?t.footerMotionDefault:"system",motionControl:t.footerMotionControl==="checkbox"?"checkbox":"switch",class:e(t.footerClass),socialClass:e(t.footerSocialClass)},social:{twitter:e(t.socialTwitter),facebook:e(t.socialFacebook),instagram:e(t.socialInstagram),linkedin:e(t.socialLinkedin),github:e(t.socialGithub),youtube:e(t.socialYoutube)},smtp:{host:e(t.smtpHost),port:i(t.smtpPort,587)||587,user:e(t.smtpUser),pass:o(t.smtpPass),secure:t.smtpSecure===!0,fromAddress:e(t.smtpFromAddress),fromName:e(t.smtpFromName)},backToTop:{enabled:t.bttEnabled===!0,scrollThreshold:i(t.bttThreshold,300),position:o(t.bttPosition)||"bottom-right",offset:i(t.bttOffset,32),bottomOffset:i(t.bttBottomOffset,32),label:e(t.bttLabel),smooth:t.bttSmooth===!0},cookieConsent:{enabled:t.ccEnabled===!0,message:e(t.ccMessage),acceptAllText:e(t.ccAcceptAll),rejectAllText:e(t.ccRejectAll),customizeText:e(t.ccCustomize),savePreferencesText:e(t.ccSavePrefs),privacyPolicyText:e(t.ccPrivacyText),privacyPolicyUrl:e(t.ccPrivacyUrl),cookiePolicyText:e(t.ccCookieText),cookiePolicyUrl:e(t.ccCookieUrl),position:o(t.ccPosition)||"bottom",layout:o(t.ccLayout)||"bar",theme:o(t.ccTheme)||"dark",showFunctional:t.ccShowFunctional===!0,showAnalytics:t.ccShowAnalytics===!0,showMarketing:t.ccShowMarketing===!0,consentVersion:e(t.ccVersion)||"1.0"},breadcrumbs:{enabled:t.bcEnabled===!0,homeLabel:e(t.bcHomeLabel)||"Home",position:BREADCRUMB_POSITIONS.includes(t.bcPosition)?t.bcPosition:"TL",offsetX:i(t.bcOffsetX,8),offsetY:i(t.bcOffsetY,8)}}}export function siteUrlError(t){const s=o(t).trim().replace(/\/+$/,"");return s?/^https?:\/\//i.test(s)?/^https?:\/\/([a-z0-9.\-]+|\[[0-9a-f:.]+\])(:\d{1,5})?$/i.test(s)?"":"Just the address, like https://example.com - no page path after it.":"Start with https:// (or http://).":""}const a=t=>typeof t=="boolean"?t:o(t);export function snapshot(t){return JSON.stringify(Object.keys(t).sort().map(s=>[s,a(t[s])]))}export function changedTabs(t,s){const c=[];for(const[e,r]of Object.entries(TAB_FIELDS))r.some(n=>a(t[n])!==a(s[n]))&&c.push(e);return c}export function draftOf(t){return Object.fromEntries(Object.entries(t).filter(([s])=>!SECRET_FIELDS.includes(s)))}export function settingsProblems(t){const s=[];return o(t.smtpHost).trim()?o(t.smtpFromAddress).trim()||s.push({tone:"warn",tab:"email",text:"No From address - mail has no sender and many servers refuse it."}):s.push({tone:"danger",tab:"email",text:"No SMTP host - mail goes to a throwaway test inbox and reaches no one (password resets, form notifications)."}),t.ccEnabled===!0&&!o(t.ccPrivacyUrl).trim()&&s.push({tone:"warn",tab:"cookies",text:"The cookie banner is on but links to no privacy policy."}),s}export function describeCacheKey(t){const s=/^page:(.*?):m\d+:role([^:]*):/.exec(String(t||""));return s?{path:s[1]||"/",role:s[2]||"anon",page:!0}:{path:String(t||""),role:"",page:!1}}export function expiresIn(t,s=Date.now()){if(t==null)return"no expiry";const c=t-s;return c<=0?"expired":c<6e4?`${Math.round(c/1e3)}s`:c<36e5?`${Math.round(c/6e4)}m`:`${Math.round(c/36e5)}h`}
@@ -119,6 +119,12 @@
119
119
  .lyx-form { display: flex; flex-direction: column; gap: .85rem; }
120
120
  .lyx-form .form-label { display: flex; align-items: center; gap: .3rem; }
121
121
  .lyx-switches { display: flex; flex-wrap: wrap; gap: 1.25rem; }
122
+ .lyx-contents { margin-top: 0.75rem; padding: 0.6rem 0.8rem; border: 1px solid var(--dm-border); border-radius: 8px; }
123
+ .lyx-contents > summary { cursor: pointer; font-weight: 600; }
124
+ .lyx-contents[open] > summary { margin-bottom: 0.75rem; }
125
+ .lyx-contents > .lyx-switches { margin-top: 0.75rem; }
126
+ .lyx-contents-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 0.6rem 1rem; }
127
+ .lyx-contents-grid > label { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.85rem; }
122
128
  .lyx-switches label { display: inline-flex; align-items: center; gap: .4rem; cursor: pointer; }
123
129
  .lyx-colour { display: flex; align-items: center; gap: .5rem; }
124
130
  .lyx-colour input[type="color"] { width: 2.4rem; height: 2rem; padding: 2px; border: 1px solid var(--dm-border); border-radius: var(--dm-radius-sm); background: transparent; cursor: pointer; }
@@ -121,6 +121,23 @@
121
121
  <span data-help="An accessibility switch in the footer that lets visitors turn animations off. Turning this off only hides the switch - visitors who already chose it, and anyone whose browser asks for reduced motion, still get a calmer site."></span></label>
122
122
  <input id="stx-motion" type="checkbox" class="form-switch" data-model="footerMotionToggle.value">
123
123
  </div>
124
+ <div class="stx-setting">
125
+ <label class="form-label" for="stx-motion-default">Reduced motion for new visitors
126
+ <span data-help="What a visitor who has not used the switch gets. Follow their device honours the system setting (the accessible default). Off shows every animation until they ask for less; On starts calm. A visitor's own choice always wins."></span></label>
127
+ <select id="stx-motion-default" class="form-input" data-model="footerMotionDefault.value">
128
+ <option value="system">Follow their device</option>
129
+ <option value="off">Off - full motion</option>
130
+ <option value="on">On - calm</option>
131
+ </select>
132
+ </div>
133
+ <div class="stx-setting">
134
+ <label class="form-label" for="stx-motion-control">The control
135
+ <span data-help="How the Reduce motion control looks in the footer."></span></label>
136
+ <select id="stx-motion-control" class="form-input" data-model="footerMotionControl.value">
137
+ <option value="switch">A switch</option>
138
+ <option value="checkbox">A checkbox</option>
139
+ </select>
140
+ </div>
124
141
  <div>
125
142
  <label class="form-label" for="stx-footer-class">Footer CSS classes
126
143
  <span data-help-title="Footer CSS classes" data-help="Your own class names, separated by spaces, added to the site footer. Style them in Custom CSS. The footer's menus take classes of their own in Menus."></span></label>
@@ -1,11 +1,11 @@
1
- import{api as L,apiRequest as Qe}from"../api.js";import{addBannerAction as Xe}from"../lib/plugin-chrome.js";import{helpPopovers as Me}from"../lib/help-popover.js";import{onceClosed as $e,openMenuAt as Oe,sidebarCount as Ze}from"../lib/tool-kit.js";import{FILTERS as et,WIDTHS as tt,WIDTH_HINTS as at,WIDTH_LABELS as st,WIDTH_SHARE as De,arrangeLayouts as lt,checkLayout as nt,checkOptions as ot,formFromPreset as it}from"../lib/layouts-arrange.js";const ae=/^\/layouts\/?(\?.*)?$/,Te="#/layouts",se="layouts.draft",N="layouts.options.draft",le="layouts.filter",Le=(s="")=>`
2
- <span class="lyx-shape" aria-hidden="true" data-bind-style-background-color="bg${s}">
3
- <span class="lyx-shape-nav" data-bind-hidden="!hasNav${s}"></span>
4
- <span class="lyx-shape-body"><span class="lyx-shape-col" data-bind-style-width="share${s}"></span><span class="lyx-shape-side" data-bind-hidden="!hasSide${s}" data-bind-class="sideRight${s} && 'is-right'"></span></span>
5
- <span class="lyx-shape-foot" data-bind-hidden="!hasFoot${s}"></span>
6
- </span>`,rt=`
1
+ import{api as O,apiRequest as Qe}from"../api.js";import{HIGHLIGHT_HOVER as Xe,HIGHLIGHT_ACTIVE as Ze,HIGHLIGHT_HOVER_LABELS as et,HIGHLIGHT_ACTIVE_LABELS as tt}from"/public/js/menu-decor.mjs?v=20261009-hover-darken";import{addBannerAction as at}from"../lib/plugin-chrome.js";import{helpPopovers as Me}from"../lib/help-popover.js";import{onceClosed as Te,openMenuAt as He,sidebarCount as lt}from"../lib/tool-kit.js";import{FILTERS as st,WIDTHS as ot,WIDTH_HINTS as nt,WIDTH_LABELS as it,WIDTH_SHARE as $e,arrangeLayouts as ct,checkLayout as rt,checkOptions as dt,formFromPreset as ut}from"../lib/layouts-arrange.js";const ae=/^\/layouts\/?(\?.*)?$/,Le="#/layouts",le="layouts.draft",F="layouts.options.draft",se="layouts.filter",Oe=(l="")=>`
2
+ <span class="lyx-shape" aria-hidden="true" data-bind-style-background-color="bg${l}">
3
+ <span class="lyx-shape-nav" data-bind-hidden="!hasNav${l}"></span>
4
+ <span class="lyx-shape-body"><span class="lyx-shape-col" data-bind-style-width="share${l}"></span><span class="lyx-shape-side" data-bind-hidden="!hasSide${l}" data-bind-class="sideRight${l} && 'is-right'"></span></span>
5
+ <span class="lyx-shape-foot" data-bind-hidden="!hasFoot${l}"></span>
6
+ </span>`,pt=`
7
7
  <div class="lyx-row" data-key="{{key}}" tabindex="0">
8
- ${Le()}
8
+ ${Oe()}
9
9
  <span class="lyx-main"><strong>{{label}}</strong><small>{{meta}}</small></span>
10
10
  <span class="lyx-chips">
11
11
  <span class="lyx-chip" data-bind-hidden="!builtin" title="Built in - can be edited, not deleted">Built in</span>
@@ -13,13 +13,13 @@ import{api as L,apiRequest as Qe}from"../api.js";import{addBannerAction as Xe}fr
13
13
  <span class="lyx-chip" data-bind-class="usedTone" title="Pages that use this layout">{{usedText}}</span>
14
14
  </span>
15
15
  <button type="button" class="lyx-kebab" aria-label="Actions for {{label}}" title="Actions">\u22EE</button>
16
- </div>`,W=s=>String(s??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;");function Be(s){const r=document.createElement("template");return r.innerHTML=s.trim(),r.content.firstElementChild}const je=(s,r,i=`${r}s`)=>`${s} ${s===1?r:i}`,ct=s=>`#/pages/edit${s}`;let f=null,ze=!1;function dt(){ze||!window.R?.use||(ze=!0,R.use((s,r,i)=>!(ae.test(r?.path||"")&&!ae.test(s?.path||""))||!f?.dirty()?i():E.confirm("You have unsaved layout changes. Leave anyway? They are kept as a draft for your next visit.").then(u=>{u?i():history.replaceState(null,"",Te)})),M.subscribe("router:afterChange",({to:s}={})=>{if(f&&!ae.test(s?.path||"")){const r=f;f=null,r.dispose()}}))}export const layoutsView={templateUrl:"/admin/js/templates/layouts.html",async onMount(s){if(dt(),f){const e=f;f=null,e.dispose()}const r=[],i=e=>(r.push(e),e);let Y=!1;const u=s.get(0).querySelector(".lyx")||s.get(0),p=()=>!Y&&u.isConnected,y=M.observable({}),A=M.observable({byKey:{},missing:[]}),ne=M.observable(!0),U=M.observable("");let oe="all";try{oe=et.includes(S.get(le))?S.get(le):"all"}catch{}const k=M.observable(oe),ie=M.observable("");i(M.effect(()=>{const e=k.value;try{S.set(le,e)}catch{}}));const re=M.observable([]);i(M.effect(()=>{re.value=lt(y.value,A.value,{filter:k.value,query:ie.value})}));const Pe=M.computed(()=>{const e=Object.values(y.value),t=e.filter(n=>n.builtin).length;return{all:e.length,builtin:t,custom:e.length-t}}),V=M.computed(()=>A.value.missing||[]),Ne=M.computed(()=>{const e=V.value.length;return`${je(e,"page")} name${e===1?"s":""} a layout that no longer exists and show${e===1?"s":""} as Default.`}),H=M.observable({spacerSize:"8",spacerClass:""}),m=M.observable("8"),g=M.observable(""),G=M.observable(!1),ce=()=>({spacerSize:String(m.value??""),spacerClass:String(g.value??"")}),K=M.computed(()=>ot(ce())),x=M.computed(()=>{const e=H.value;return String(m.value??"")!==e.spacerSize||String(g.value??"")!==e.spacerClass}),de=M.computed(()=>G.value||!x.value||Object.keys(K.value.errors).length>0),Ae=M.computed(()=>x.value?"Unsaved changes - Ctrl+S saves.":"Saved."),B=e=>({...y.peek()[e]||{},key:e}),J=e=>(A.peek().byKey||{})[e]||[];async function Q(){try{const[e,t]=await Promise.all([L.layouts.get(),Qe("/layouts/_usage",{method:"GET"}).catch(()=>({byKey:{},missing:[]}))]);if(!p())return;y.value=e&&typeof e=="object"?e:{},A.value=t&&typeof t=="object"?t:{byKey:{},missing:[]},U.value=""}catch(e){U.value=`Could not load the layouts: ${e.message||e}`}finally{ne.value=!1}}async function He(){const e=await L.layouts.getOptions().catch(()=>({spacerSize:8,spacerClass:""}));if(!p())return;const t={spacerSize:String(e?.spacerSize??8),spacerClass:String(e?.spacerClass??"")};H.value=t,m.value=t.spacerSize,g.value=t.spacerClass}async function ue(){if(de.peek())return;G.value=!0;const{payload:e}=K.peek();try{await L.layouts.saveOptions(e),H.value={spacerSize:String(e.spacerSize),spacerClass:e.spacerClass},m.value=String(e.spacerSize),g.value=e.spacerClass;try{S.remove?S.remove(N):S.set(N,null)}catch{}E.toast("Layout options saved.",{type:"success"})}catch(t){E.toast(`Not saved: ${t.message||t}`,{type:"error"})}finally{G.value=!1}}function Ie(){const e=H.peek();m.value=e.spacerSize,g.value=e.spacerClass}const pe=()=>Ze(Te);async function Ke(e){const t=B(e);if(t.builtin)return;const n=J(e).length,c=n?` ${je(n,"page")} use${n===1?"s":""} it and will show as Default.`:" No page uses it.";if(await E.confirm(`Delete the layout "${W(t.label||e)}"?${c}`))try{await L.layouts.remove(e),E.toast(`Deleted "${t.label||e}".`,{type:"success"}),pe(),p()&&await Q()}catch(o){E.toast(`Not deleted: ${o.message||o}`,{type:"error"})}}async function Re(e){const t=`layout: ${e}`;try{await navigator.clipboard.writeText(t),E.toast(`Copied "${t}" - paste it into a page's frontmatter.`,{type:"success"})}catch{E.toast(`Copy this into a page's frontmatter: ${t}`,{type:"info",duration:7e3})}}const C=new Set;let b=null,ye=!1;function be(e){const t=e===null,n=t?V.peek():J(e),c=t?null:B(e),o=E.slideover({title:t?"Pages on a missing layout":`Pages using ${W(c.label||e)}`,size:"sm",position:"right"}),j=Be(`
16
+ </div>`,G=l=>String(l??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;");function Be(l){const c=document.createElement("template");return c.innerHTML=l.trim(),c.content.firstElementChild}const De=(l,c,i=`${c}s`)=>`${l} ${l===1?c:i}`,bt=l=>`#/pages/edit${l}`;let m=null,Ae=!1;function ht(){Ae||!window.R?.use||(Ae=!0,R.use((l,c,i)=>!(ae.test(c?.path||"")&&!ae.test(l?.path||""))||!m?.dirty()?i():E.confirm("You have unsaved layout changes. Leave anyway? They are kept as a draft for your next visit.").then(u=>{u?i():history.replaceState(null,"",Le)})),M.subscribe("router:afterChange",({to:l}={})=>{if(m&&!ae.test(l?.path||"")){const c=m;m=null,c.dispose()}}))}export const layoutsView={templateUrl:"/admin/js/templates/layouts.html",async onMount(l){if(ht(),m){const e=m;m=null,e.dispose()}const c=[],i=e=>(c.push(e),e);let V=!1;const u=l.get(0).querySelector(".lyx")||l.get(0),p=()=>!V&&u.isConnected,b=M.observable({}),j=M.observable({byKey:{},missing:[]}),oe=M.observable(!0),Y=M.observable("");let ne="all";try{ne=st.includes(S.get(se))?S.get(se):"all"}catch{}const k=M.observable(ne),ie=M.observable("");i(M.effect(()=>{const e=k.value;try{S.set(se,e)}catch{}}));const ce=M.observable([]);i(M.effect(()=>{ce.value=ct(b.value,j.value,{filter:k.value,query:ie.value})}));const Ie=M.computed(()=>{const e=Object.values(b.value),t=e.filter(o=>o.builtin).length;return{all:e.length,builtin:t,custom:e.length-t}}),U=M.computed(()=>j.value.missing||[]),ze=M.computed(()=>{const e=U.value.length;return`${De(e,"page")} name${e===1?"s":""} a layout that no longer exists and show${e===1?"s":""} as Default.`}),N=M.observable({spacerSize:"8",spacerClass:""}),f=M.observable("8"),g=M.observable(""),J=M.observable(!1),re=()=>({spacerSize:String(f.value??""),spacerClass:String(g.value??"")}),P=M.computed(()=>dt(re())),x=M.computed(()=>{const e=N.value;return String(f.value??"")!==e.spacerSize||String(g.value??"")!==e.spacerClass}),de=M.computed(()=>J.value||!x.value||Object.keys(P.value.errors).length>0),Fe=M.computed(()=>x.value?"Unsaved changes - Ctrl+S saves.":"Saved."),B=e=>({...b.peek()[e]||{},key:e}),Q=e=>(j.peek().byKey||{})[e]||[];async function X(){try{const[e,t]=await Promise.all([O.layouts.get(),Qe("/layouts/_usage",{method:"GET"}).catch(()=>({byKey:{},missing:[]}))]);if(!p())return;b.value=e&&typeof e=="object"?e:{},j.value=t&&typeof t=="object"?t:{byKey:{},missing:[]},Y.value=""}catch(e){Y.value=`Could not load the layouts: ${e.message||e}`}finally{oe.value=!1}}async function Re(){const e=await O.layouts.getOptions().catch(()=>({spacerSize:8,spacerClass:""}));if(!p())return;const t={spacerSize:String(e?.spacerSize??8),spacerClass:String(e?.spacerClass??"")};N.value=t,f.value=t.spacerSize,g.value=t.spacerClass}async function ue(){if(de.peek())return;J.value=!0;const{payload:e}=P.peek();try{await O.layouts.saveOptions(e),N.value={spacerSize:String(e.spacerSize),spacerClass:e.spacerClass},f.value=String(e.spacerSize),g.value=e.spacerClass;try{S.remove?S.remove(F):S.set(F,null)}catch{}E.toast("Layout options saved.",{type:"success"})}catch(t){E.toast(`Not saved: ${t.message||t}`,{type:"error"})}finally{J.value=!1}}function je(){const e=N.peek();f.value=e.spacerSize,g.value=e.spacerClass}const pe=()=>lt(Le);async function Ne(e){const t=B(e);if(t.builtin)return;const o=Q(e).length,r=o?` ${De(o,"page")} use${o===1?"s":""} it and will show as Default.`:" No page uses it.";if(await E.confirm(`Delete the layout "${G(t.label||e)}"?${r}`))try{await O.layouts.remove(e),E.toast(`Deleted "${t.label||e}".`,{type:"success"}),pe(),p()&&await X()}catch(n){E.toast(`Not deleted: ${n.message||n}`,{type:"error"})}}async function Pe(e){const t=`layout: ${e}`;try{await navigator.clipboard.writeText(t),E.toast(`Copied "${t}" - paste it into a page's frontmatter.`,{type:"success"})}catch{E.toast(`Copy this into a page's frontmatter: ${t}`,{type:"info",duration:7e3})}}const C=new Set;let h=null,be=!1;function he(e){const t=e===null,o=t?U.peek():Q(e),r=t?null:B(e),n=E.slideover({title:t?"Pages on a missing layout":`Pages using ${G(r.label||e)}`,size:"sm",position:"right"}),D=Be(`
17
17
  <div class="lyx-form">
18
18
  <p class="lyx-note"></p>
19
19
  <div class="lyx-pages"></div>
20
- </div>`);j.querySelector(".lyx-note").textContent=t?"These pages name a layout that no longer exists, so they show as Default. Open one and choose a layout for it.":n.length?"Click a page to open it in the editor.":"No page uses this layout.";const F=j.querySelector(".lyx-pages");for(const l of n){const w=document.createElement("a");w.href=ct(l.urlPath);const z=document.createElement("span");z.textContent=l.title||l.urlPath;const q=document.createElement("small");q.textContent=[l.urlPath,t?`"${l.layout}"`:"",l.status==="draft"?"draft":""].filter(Boolean).join(" \xB7 "),w.append(z,q),w.addEventListener("click",()=>o.close()),F.appendChild(w)}o.element.appendChild(j),C.add(o),$e(o,()=>C.delete(o)),o.open()}function $(e=null,{from:t=null,draft:n=null}={}){const c=e?B(e):null;if(e&&!y.peek()[e]){E.toast(`There is no layout "${e}".`,{type:"warning"});return}const o=!c,j=c||(t?{...B(t),label:`${B(t).label||t} copy`,builtin:!1}:{}),F={...it(j),...n||{}},l=Object.fromEntries(Object.entries(F).map(([a,h])=>[a,M.observable(h)])),w=()=>Object.fromEntries(Object.keys(l).map(a=>[a,l[a].peek()])),z=a=>JSON.stringify(Object.fromEntries(Object.entries(a).map(([h,te])=>[h,typeof te=="boolean"?te:String(te??"")]))),q=z(F),Se=()=>z(w())!==q||!!n||!!(t&&o),_=M.observable(!1),P=M.computed(()=>nt(Object.fromEntries(Object.keys(l).map(a=>[a,l[a].value])),{isNew:o,taken:Object.keys(y.value)})),X=a=>M.computed(()=>P.value.errors[a]||""),We={hasNav:M.computed(()=>l.navbar.value!==!1),hasFoot:M.computed(()=>l.footer.value!==!1),hasSide:M.computed(()=>l.sidebar.value===!0),sideRight:M.computed(()=>l.sidebar.value===!0&&l.sidebarSide.value==="right"),share:M.computed(()=>De[l.width.value]||De.normal),bg:M.computed(()=>l.useBg.value?l.bgColor.value:"")},Ee={...l,...We,saving:_,labelErr:X("label"),bgErr:X("bgColor"),classErr:X("cssClass"),keyHint:M.computed(()=>o?P.value.key?`Pages will name it "layout: ${P.value.key}".`:"":`Pages name it "layout: ${c.key}" - renaming does not change that.`),widthHint:M.computed(()=>at[l.width.value]||""),builtinNote:!o&&c.builtin===!0,noBg:M.computed(()=>!l.useBg.value),cannot:M.computed(()=>_.value||Object.keys(P.value.errors).length>0),saveText:o?"Create layout":"Save"},Ye=o?t?"Duplicate layout":"New layout":`Edit ${W(c.label||c.key)}`,d=E.slideover({title:Ye,size:"md",position:"right"}),T=Be(`
20
+ </div>`);D.querySelector(".lyx-note").textContent=t?"These pages name a layout that no longer exists, so they show as Default. Open one and choose a layout for it.":o.length?"Click a page to open it in the editor.":"No page uses this layout.";const W=D.querySelector(".lyx-pages");for(const s of o){const w=document.createElement("a");w.href=bt(s.urlPath);const A=document.createElement("span");A.textContent=s.title||s.urlPath;const _=document.createElement("small");_.textContent=[s.urlPath,t?`"${s.layout}"`:"",s.status==="draft"?"draft":""].filter(Boolean).join(" \xB7 "),w.append(A,_),w.addEventListener("click",()=>n.close()),W.appendChild(w)}n.element.appendChild(D),C.add(n),Te(n,()=>C.delete(n)),n.open()}function T(e=null,{from:t=null,draft:o=null}={}){const r=e?B(e):null;if(e&&!b.peek()[e]){E.toast(`There is no layout "${e}".`,{type:"warning"});return}const n=!r,D=r||(t?{...B(t),label:`${B(t).label||t} copy`,builtin:!1}:{}),W={...ut(D),...o||{}},s=Object.fromEntries(Object.entries(W).map(([a,y])=>[a,M.observable(y)])),w=()=>Object.fromEntries(Object.keys(s).map(a=>[a,s[a].peek()])),A=a=>JSON.stringify(Object.fromEntries(Object.entries(a).map(([y,te])=>[y,typeof te=="boolean"?te:String(te??"")]))),_=A(W),Se=()=>A(w())!==_||!!o||!!(t&&n),K=M.observable(!1),z=M.computed(()=>rt(Object.fromEntries(Object.keys(s).map(a=>[a,s[a].value])),{isNew:n,taken:Object.keys(b.value)})),q=a=>M.computed(()=>z.value.errors[a]||""),qe={hasNav:M.computed(()=>s.navbar.value!==!1),hasFoot:M.computed(()=>s.footer.value!==!1),hasSide:M.computed(()=>s.sidebar.value===!0),sideRight:M.computed(()=>s.sidebar.value===!0&&s.sidebarSide.value==="right"),share:M.computed(()=>$e[s.width.value]||$e.normal),bg:M.computed(()=>s.useBg.value?s.bgColor.value:"")},Ee={...s,...qe,saving:K,labelErr:q("label"),bgErr:q("bgColor"),classErr:q("cssClass"),cWidthErr:q("cWidth"),hoverOptions:[{value:"",label:"The theme's"},...Xe.map(a=>({value:a,label:et[a]||a}))],selectedOptions:[{value:"",label:"The theme's"},...Ze.map(a=>({value:a,label:tt[a]||a}))],keyHint:M.computed(()=>n?z.value.key?`Pages will name it "layout: ${z.value.key}".`:"":`Pages name it "layout: ${r.key}" - renaming does not change that.`),widthHint:M.computed(()=>nt[s.width.value]||""),builtinNote:!n&&r.builtin===!0,noBg:M.computed(()=>!s.useBg.value),cannot:M.computed(()=>K.value||Object.keys(z.value.errors).length>0),saveText:n?"Create layout":"Save"},Ge=n?t?"Duplicate layout":"New layout":`Edit ${G(r.label||r.key)}`,d=E.slideover({title:Ge,size:"md",position:"right"}),L=Be(`
21
21
  <div class="lyx-form">
22
- <div class="lyx-preview">${Le(".value")}
22
+ <div class="lyx-preview">${Oe(".value")}
23
23
  <small class="lyx-hint" data-bind-text="keyHint.value"></small></div>
24
24
  <p class="lyx-note" data-bind-hidden="!builtinNote">A built-in layout: you can change it, but not delete it.</p>
25
25
  <div class="lyx-grid">
@@ -32,7 +32,7 @@ import{api as L,apiRequest as Qe}from"../api.js";import{addBannerAction as Xe}fr
32
32
  <label class="form-label" for="lyx-f-width">Width
33
33
  <span data-help="How wide the page's content column is. The navbar and footer are not affected."></span></label>
34
34
  <select id="lyx-f-width" class="form-input" data-model="width.value">
35
- ${tt.map(a=>`<option value="${a}">${st[a]}</option>`).join("")}
35
+ ${ot.map(a=>`<option value="${a}">${it[a]}</option>`).join("")}
36
36
  </select>
37
37
  <small class="lyx-hint" data-bind-text="widthHint.value"></small>
38
38
  </div>
@@ -52,6 +52,48 @@ import{api as L,apiRequest as Qe}from"../api.js";import{addBannerAction as Xe}fr
52
52
  <label><input type="radio" name="lyx-side" value="left" data-model="sidebarSide.value"> Sidebar on the left</label>
53
53
  <label><input type="radio" name="lyx-side" value="right" data-model="sidebarSide.value"> on the right</label>
54
54
  </div>
55
+ <details class="lyx-contents" data-if="sidebar.value">
56
+ <summary>Page contents settings <span data-help="The sidebar on this layout lists the page's own headings. These are the defaults for every page on the layout; a page can change any of them for itself with the [contents] tag."></span></summary>
57
+ <div class="lyx-contents-grid">
58
+ <label>Width (px) <input type="number" class="form-input" min="160" max="480" step="10" placeholder="260" data-model="cWidth.value"></label>
59
+ <label>Headings
60
+ <select class="form-input" data-model="cHeadings.value">
61
+ <option value="auto">Sections (H2s, else H3s)</option><option value="h2">H2 only</option>
62
+ <option value="h3">H3 only</option><option value="h2h3">H2 and H3</option>
63
+ </select></label>
64
+ <label>Title <input type="text" class="form-input" maxlength="80" placeholder="On this page" data-model="cTitle.value"></label>
65
+ <label>Leave out <input type="text" class="form-input" placeholder="Headings, comma separated" data-model="cExclude.value"></label>
66
+ <label>Hover effect
67
+ <select class="form-input" data-options="hoverOptions" data-options-text="label" data-options-value="value" data-model="cHover.value"></select></label>
68
+ <label>When selected
69
+ <select class="form-input" data-options="selectedOptions" data-options-text="label" data-options-value="value" data-model="cSelected.value"></select></label>
70
+ <label>Accent <input type="text" class="form-input" placeholder="primary or #rrggbb" data-model="cAccent.value"></label>
71
+ <label>Font <input type="text" class="form-input" placeholder="Theme font" data-model="cFont.value"></label>
72
+ <label>Font size <input type="text" class="form-input" placeholder="0.9rem" data-model="cFontSize.value"></label>
73
+ <label>Font weight <input type="text" class="form-input" placeholder="400" data-model="cFontWeight.value"></label>
74
+ </div>
75
+ <div class="lyx-switches">
76
+ <label><input type="checkbox" class="form-switch" data-model="cSticky.value"> Stays in view</label>
77
+ <label><input type="checkbox" class="form-switch" data-model="cScrollSpy.value"> Marks the section being read</label>
78
+ <label><input type="checkbox" class="form-switch" data-model="cNumbered.value"> Numbered</label>
79
+ </div>
80
+ <div class="lyx-switches">
81
+ <label><input type="checkbox" class="form-switch" data-model="cCollapsible.value"> Collapsible to a button</label>
82
+ <label data-if="cCollapsible.value"><input type="checkbox" class="form-switch" data-model="cCollapsed.value"> Start collapsed</label>
83
+ <label><input type="checkbox" class="form-switch" data-model="cAutohide.value"> Autohide off its edge</label>
84
+ <label><input type="checkbox" class="form-switch" data-model="cFloating.value"> Floating panel</label>
85
+ </div>
86
+ <div class="lyx-switches" data-if="cFloating.value">
87
+ <label>Anchor
88
+ <select class="form-input" data-model="cFloatAnchor.value">
89
+ <option value="TL">Top left</option><option value="TR">Top right</option><option value="BL">Bottom left</option>
90
+ <option value="BR">Bottom right</option><option value="L">Left, centred</option><option value="R">Right, centred</option>
91
+ </select></label>
92
+ <label><input type="checkbox" class="form-switch" data-model="cMovable.value"> Visitors can move it</label>
93
+ </div>
94
+ <small class="lyx-hint">Floating wins over autohide, and both over collapse - the list leaves its column for them.</small>
95
+ <small class="lyx-err" data-bind-hidden="!cWidthErr.value" data-bind-text="cWidthErr.value"></small>
96
+ </details>
55
97
  </div>
56
98
  <div>
57
99
  <span class="form-label">Background colour
@@ -82,4 +124,4 @@ import{api as L,apiRequest as Qe}from"../api.js";import{addBannerAction as Xe}fr
82
124
  <button type="button" class="btn btn-sm btn-primary" data-on-click="save" data-bind-disabled="cannot.value">
83
125
  <span data-icon="check" data-icon-size="13"></span> <span data-bind-text="saveText"></span></button>
84
126
  </div>
85
- </div>`);async function ke(){if(Ee.cannot.peek())return;_.value=!0;const{payload:a}=P.peek();try{o?await L.layouts.create(a):await L.layouts.update(c.key,a),E.toast(o?`Created "${a.label}".`:"Saved.",{type:"success"}),Z=!0,d.close(),pe(),p()&&await Q()}catch(h){E.toast(`Not saved: ${h.message||h}`,{type:"error"})}finally{_.value=!1}}d.element.appendChild(T);const Ue=Me(T),Ve=M.applyBindings(Ee,T,{methods:{save:ke,cancel:()=>d.close()}}),Ge=a=>{(a.ctrlKey||a.metaKey)&&a.key==="Enter"&&(a.preventDefault(),ke())};T.addEventListener("keydown",Ge);const Ce={key:c?.key??null,dirty:Se,values:w};b=Ce,C.add(d),$e(d,()=>{Ve.dispose(),Ue(),C.delete(d),b===Ce&&(b=null)});const Je=d.close.bind(d);let Z=!1,ee=!1;d.close=async a=>{if(!Z&&!ye&&a!==!0&&Se()){if(ee)return;ee=!0;const h=await E.confirm("Discard the changes you have not saved?");if(ee=!1,!h)return}return Z=!0,Je()},I.scan(T),d.open(),T.querySelector("#lyx-f-label")?.focus()}function Fe(e){const t=y.peek()[e];if(!t)return[];const n=J(e).length;return[{header:t.label||e},{label:"Edit\u2026",icon:"edit",action:()=>$(e)},{label:n?`Pages using it (${n})\u2026`:"Pages using it",icon:"file-text",disabled:!n,action:()=>be(e)},{label:"Duplicate\u2026",icon:"copy",action:()=>$(null,{from:e})},{label:"Copy frontmatter line",icon:"clipboard",action:()=>Re(e)},...t.builtin?[]:[{type:"divider"},{label:"Delete\u2026",icon:"trash",danger:!0,action:()=>Ke(e)}]]}const O=u.querySelector(".lyx-list");O.innerHTML=rt;const ve=E.tabs(u.querySelector(".lyx-tabs"),{tabSelector:".tab-item",panelSelector:".tab-panel"}),qe=M.applyBindings({loading:ne,loadError:U,filter:k,query:ie,rows:re,counts:Pe,missing:V,missingText:Ne,spacerSize:m,spacerClass:g,optDirty:x,optCannot:de,optStatus:Ae,optErrSize:M.computed(()=>K.value.errors.spacerSize||""),optErrClass:M.computed(()=>K.value.errors.spacerClass||"")},u,{methods:{showAll:()=>{k.value="all"},showBuiltin:()=>{k.value="builtin"},showCustom:()=>{k.value="custom"},showMissing:()=>be(null),saveOptions:()=>ue(),resetOptions:()=>Ie()}});i(()=>qe.dispose()),i(Me(u,{position:"bottom"}));const _e=E.contextMenu(O,{cursor:"glyph",match:".lyx-row",inherit:!1,density:"compact",items:e=>Fe(e.dataset.key)});i(()=>_e?.destroy?.());const he=e=>{const t=e.target.closest(".lyx-row");if(!t)return;const n=e.target.closest(".lyx-kebab");if(n){Oe(n);return}$(t.dataset.key)},fe=e=>{const t=e.target.closest(".lyx-row");t&&(e.key==="F10"&&e.shiftKey?(e.preventDefault(),Oe(t)):e.key==="Enter"&&e.target===t&&$(t.dataset.key))};O.addEventListener("click",he),O.addEventListener("keydown",fe),i(()=>{O.removeEventListener("click",he),O.removeEventListener("keydown",fe)});const me=e=>{!(e.ctrlKey||e.metaKey)||e.key!=="s"||!p()||C.size||(e.preventDefault(),x.peek()&&ue())};document.addEventListener("keydown",me,!0),i(()=>document.removeEventListener("keydown",me,!0)),Xe(s,{id:"layouts-new",icon:"plus",title:"New layout",onClick:()=>{ve.activate?.(0),$()}});const ge=e=>{(x.peek()||b?.dirty())&&(xe(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",ge),i(()=>window.removeEventListener("beforeunload",ge));function xe(){try{b?.dirty()&&S.set(se,{key:b.key,values:b.values(),at:new Date().toISOString()}),x.peek()&&S.set(N,{...ce(),at:new Date().toISOString()})}catch{}}if(f={dirty:()=>x.peek()||!!b?.dirty(),dispose(){Y=!0,xe(),ye=!0,[...C].forEach(e=>{try{e.close(!0)}catch{}}),r.splice(0).forEach(e=>{try{e()}catch{}})}},I.scan(u),await Promise.all([Q(),He()]),!p())return;I.scan(u);let D=null,v=null;try{D=S.get(N),v=S.get(se)}catch{}const we=e=>{try{S.remove?S.remove(e):S.set(e,null)}catch{}};if(D&&typeof D=="object"&&"spacerSize"in D&&(we(N),await E.confirm("You left unsaved spacer options. Put them back?")&&p()&&(m.value=String(D.spacerSize??""),g.value=String(D.spacerClass??""),ve.activate?.(1))),v?.values&&p()){we(se);const e=v.key&&y.peek()[v.key],t=e?`your changes to "${W(y.peek()[v.key].label||v.key)}"`:"the new layout you started";await E.confirm(`You left ${t} unsaved. Pick up where you left off?`)&&p()&&$(e?v.key:null,{draft:v.values})}}};
127
+ </div>`);async function ke(){if(Ee.cannot.peek())return;K.value=!0;const{payload:a}=z.peek();try{n?await O.layouts.create(a):await O.layouts.update(r.key,a),E.toast(n?`Created "${a.label}".`:"Saved.",{type:"success"}),Z=!0,d.close(),pe(),p()&&await X()}catch(y){E.toast(`Not saved: ${y.message||y}`,{type:"error"})}finally{K.value=!1}}d.element.appendChild(L);const Ve=Me(L),Ye=M.applyBindings(Ee,L,{methods:{save:ke,cancel:()=>d.close()}}),Ue=a=>{(a.ctrlKey||a.metaKey)&&a.key==="Enter"&&(a.preventDefault(),ke())};L.addEventListener("keydown",Ue);const Ce={key:r?.key??null,dirty:Se,values:w};h=Ce,C.add(d),Te(d,()=>{Ye.dispose(),Ve(),C.delete(d),h===Ce&&(h=null)});const Je=d.close.bind(d);let Z=!1,ee=!1;d.close=async a=>{if(!Z&&!be&&a!==!0&&Se()){if(ee)return;ee=!0;const y=await E.confirm("Discard the changes you have not saved?");if(ee=!1,!y)return}return Z=!0,Je()},I.scan(L),d.open(),L.querySelector("#lyx-f-label")?.focus()}function We(e){const t=b.peek()[e];if(!t)return[];const o=Q(e).length;return[{header:t.label||e},{label:"Edit\u2026",icon:"edit",action:()=>T(e)},{label:o?`Pages using it (${o})\u2026`:"Pages using it",icon:"file-text",disabled:!o,action:()=>he(e)},{label:"Duplicate\u2026",icon:"copy",action:()=>T(null,{from:e})},{label:"Copy frontmatter line",icon:"clipboard",action:()=>Pe(e)},...t.builtin?[]:[{type:"divider"},{label:"Delete\u2026",icon:"trash",danger:!0,action:()=>Ne(e)}]]}const H=u.querySelector(".lyx-list");H.innerHTML=pt;const ve=E.tabs(u.querySelector(".lyx-tabs"),{tabSelector:".tab-item",panelSelector:".tab-panel"}),_e=M.applyBindings({loading:oe,loadError:Y,filter:k,query:ie,rows:ce,counts:Ie,missing:U,missingText:ze,spacerSize:f,spacerClass:g,optDirty:x,optCannot:de,optStatus:Fe,optErrSize:M.computed(()=>P.value.errors.spacerSize||""),optErrClass:M.computed(()=>P.value.errors.spacerClass||"")},u,{methods:{showAll:()=>{k.value="all"},showBuiltin:()=>{k.value="builtin"},showCustom:()=>{k.value="custom"},showMissing:()=>he(null),saveOptions:()=>ue(),resetOptions:()=>je()}});i(()=>_e.dispose()),i(Me(u,{position:"bottom"}));const Ke=E.contextMenu(H,{cursor:"glyph",match:".lyx-row",inherit:!1,density:"compact",items:e=>We(e.dataset.key)});i(()=>Ke?.destroy?.());const ye=e=>{const t=e.target.closest(".lyx-row");if(!t)return;const o=e.target.closest(".lyx-kebab");if(o){He(o);return}T(t.dataset.key)},me=e=>{const t=e.target.closest(".lyx-row");t&&(e.key==="F10"&&e.shiftKey?(e.preventDefault(),He(t)):e.key==="Enter"&&e.target===t&&T(t.dataset.key))};H.addEventListener("click",ye),H.addEventListener("keydown",me),i(()=>{H.removeEventListener("click",ye),H.removeEventListener("keydown",me)});const fe=e=>{!(e.ctrlKey||e.metaKey)||e.key!=="s"||!p()||C.size||(e.preventDefault(),x.peek()&&ue())};document.addEventListener("keydown",fe,!0),i(()=>document.removeEventListener("keydown",fe,!0)),at(l,{id:"layouts-new",icon:"plus",title:"New layout",onClick:()=>{ve.activate?.(0),T()}});const ge=e=>{(x.peek()||h?.dirty())&&(xe(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",ge),i(()=>window.removeEventListener("beforeunload",ge));function xe(){try{h?.dirty()&&S.set(le,{key:h.key,values:h.values(),at:new Date().toISOString()}),x.peek()&&S.set(F,{...re(),at:new Date().toISOString()})}catch{}}if(m={dirty:()=>x.peek()||!!h?.dirty(),dispose(){V=!0,xe(),be=!0,[...C].forEach(e=>{try{e.close(!0)}catch{}}),c.splice(0).forEach(e=>{try{e()}catch{}})}},I.scan(u),await Promise.all([X(),Re()]),!p())return;I.scan(u);let $=null,v=null;try{$=S.get(F),v=S.get(le)}catch{}const we=e=>{try{S.remove?S.remove(e):S.set(e,null)}catch{}};if($&&typeof $=="object"&&"spacerSize"in $&&(we(F),await E.confirm("You left unsaved spacer options. Put them back?")&&p()&&(f.value=String($.spacerSize??""),g.value=String($.spacerClass??""),ve.activate?.(1))),v?.values&&p()){we(le);const e=v.key&&b.peek()[v.key],t=e?`your changes to "${G(b.peek()[v.key].label||v.key)}"`:"the new layout you started";await E.confirm(`You left ${t} unsaved. Pick up where you left off?`)&&p()&&T(e?v.key:null,{draft:v.values})}}};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-cms",
3
- "version": "1.6.13",
3
+ "version": "1.6.15",
4
4
  "description": "File-based CMS powered by Domma and Fastify. Run npx domma-cms my-site to create a new project.",
5
5
  "type": "module",
6
6
  "main": "server/server.js",