domma-cms 0.31.0 → 0.32.2

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 THEMES=[{id:"charcoal-dark",label:"Charcoal",dark:!0,primary:"#5b8cff",bg:"#1a1d23"},{id:"charcoal-light",label:"Charcoal",dark:!1,primary:"#4a7aff",bg:"#f4f5f7"},{id:"ocean-dark",label:"Ocean",dark:!0,primary:"#00b4d8",bg:"#0d1b2a"},{id:"ocean-light",label:"Ocean",dark:!1,primary:"#0096c7",bg:"#e8f4f8"},{id:"forest-dark",label:"Forest",dark:!0,primary:"#52b788",bg:"#1a231e"},{id:"forest-light",label:"Forest",dark:!1,primary:"#40916c",bg:"#f0f7f4"},{id:"sunset-dark",label:"Sunset",dark:!0,primary:"#ff6b6b",bg:"#1f1a1a"},{id:"sunset-light",label:"Sunset",dark:!1,primary:"#e05555",bg:"#fff0f0"},{id:"royal-dark",label:"Royal",dark:!0,primary:"#9b59b6",bg:"#1a1525"},{id:"royal-light",label:"Royal",dark:!1,primary:"#8e44ad",bg:"#f5f0fa"},{id:"lemon-dark",label:"Lemon",dark:!0,primary:"#f1c40f",bg:"#1a1a10"},{id:"lemon-light",label:"Lemon",dark:!1,primary:"#d4ac0d",bg:"#fffef0"},{id:"silver-dark",label:"Silver",dark:!0,primary:"#95a5a6",bg:"#1c1e20"},{id:"silver-light",label:"Silver",dark:!1,primary:"#7f8c8d",bg:"#f2f3f4"},{id:"grayve-dark",label:"Grayve",dark:!0,primary:"#00bcd4",bg:"#1a1e21"},{id:"grayve-light",label:"Grayve",dark:!1,primary:"#00838f",bg:"#ffffff"},{id:"mint-dark",label:"Mint",dark:!0,primary:"#00c896",bg:"#0f1f1a"},{id:"mint-light",label:"Mint",dark:!1,primary:"#00a878",bg:"#f0faf6"},{id:"unicorn-dark",label:"Unicorn",dark:!0,primary:"#e040fb",bg:"#1a0a1e"},{id:"unicorn-light",label:"Unicorn",dark:!1,primary:"#ba68c8",bg:"#fdf5ff"},{id:"dreamy-dark",label:"Dreamy",dark:!0,primary:"#7986cb",bg:"#15152a"},{id:"dreamy-light",label:"Dreamy",dark:!1,primary:"#5c6bc0",bg:"#f3f4fd"},{id:"christmas-dark",label:"Christmas",dark:!0,primary:"#e53935",bg:"#0a1a0a"},{id:"christmas-light",label:"Christmas",dark:!1,primary:"#c62828",bg:"#f5fff5"},{id:"wedding-dark",label:"Wedding",dark:!0,primary:"#ec407a",bg:"#1a0e14"},{id:"wedding-light",label:"Wedding",dark:!1,primary:"#e91e63",bg:"#fff5f8"}];export function populateThemeSelect(r,{includeDefault:f=!1,defaultLabel:t="\u2014 Use site default \u2014"}={}){for(;r.firstChild;)r.removeChild(r.firstChild);if(f){const a=document.createElement("option");a.value="",a.textContent=t,r.appendChild(a)}const i=new Set;for(const a of THEMES)if(!i.has(a.label)){i.add(a.label);const l=document.createElement("optgroup");l.label=a.label;for(const d of THEMES.filter(e=>e.label===a.label)){const e=document.createElement("option");e.value=d.id,e.textContent=`${d.label} ${d.dark?"Dark":"Light"}`,l.appendChild(e)}r.appendChild(l)}}
1
+ export const THEMES=[{id:"charcoal-dark",label:"Charcoal",dark:!0,primary:"#5b8cff",bg:"#1a1d23"},{id:"charcoal-light",label:"Charcoal",dark:!1,primary:"#4a7aff",bg:"#f4f5f7"},{id:"ocean-dark",label:"Ocean",dark:!0,primary:"#00b4d8",bg:"#0d1b2a"},{id:"ocean-light",label:"Ocean",dark:!1,primary:"#0096c7",bg:"#e8f4f8"},{id:"forest-dark",label:"Forest",dark:!0,primary:"#52b788",bg:"#1a231e"},{id:"forest-light",label:"Forest",dark:!1,primary:"#40916c",bg:"#f0f7f4"},{id:"sunset-dark",label:"Sunset",dark:!0,primary:"#ff6b6b",bg:"#1f1a1a"},{id:"sunset-light",label:"Sunset",dark:!1,primary:"#e05555",bg:"#fff0f0"},{id:"royal-dark",label:"Royal",dark:!0,primary:"#9b59b6",bg:"#1a1525"},{id:"royal-light",label:"Royal",dark:!1,primary:"#8e44ad",bg:"#f5f0fa"},{id:"lemon-dark",label:"Lemon",dark:!0,primary:"#f1c40f",bg:"#1a1a10"},{id:"lemon-light",label:"Lemon",dark:!1,primary:"#d4ac0d",bg:"#fffef0"},{id:"silver-dark",label:"Silver",dark:!0,primary:"#95a5a6",bg:"#1c1e20"},{id:"silver-light",label:"Silver",dark:!1,primary:"#7f8c8d",bg:"#f2f3f4"},{id:"grayve-dark",label:"Grayve",dark:!0,primary:"#00bcd4",bg:"#1a1e21"},{id:"grayve-light",label:"Grayve",dark:!1,primary:"#00838f",bg:"#ffffff"},{id:"mint-dark",label:"Mint",dark:!0,primary:"#00c896",bg:"#0f1f1a"},{id:"mint-light",label:"Mint",dark:!1,primary:"#00a878",bg:"#f0faf6"},{id:"unicorn-dark",label:"Unicorn",dark:!0,primary:"#e040fb",bg:"#1a0a1e"},{id:"unicorn-light",label:"Unicorn",dark:!1,primary:"#ba68c8",bg:"#fdf5ff"},{id:"dreamy-dark",label:"Dreamy",dark:!0,primary:"#7986cb",bg:"#15152a"},{id:"dreamy-light",label:"Dreamy",dark:!1,primary:"#5c6bc0",bg:"#f3f4fd"},{id:"christmas-dark",label:"Christmas",dark:!0,primary:"#e53935",bg:"#0a1a0a"},{id:"christmas-light",label:"Christmas",dark:!1,primary:"#c62828",bg:"#f5fff5"},{id:"wedding-dark",label:"Wedding",dark:!0,primary:"#ec407a",bg:"#1a0e14"},{id:"wedding-light",label:"Wedding",dark:!1,primary:"#e91e63",bg:"#fff5f8"},{id:"admin-sharp-indigo",label:"Admin \xB7 Sharp",name:"Indigo",admin:!0,dark:!1,primary:"#5b63a8",bg:"#eef1f6"},{id:"admin-sharp-steel",label:"Admin \xB7 Sharp",name:"Steel",admin:!0,dark:!1,primary:"#3b76bc",bg:"#eef1f6"},{id:"admin-sharp-teal",label:"Admin \xB7 Sharp",name:"Teal",admin:!0,dark:!1,primary:"#2a8178",bg:"#eef1f6"},{id:"admin-smooth-indigo",label:"Admin \xB7 Smooth",name:"Indigo",admin:!0,dark:!0,primary:"#5b63a8",bg:"#586170"},{id:"admin-smooth-steel",label:"Admin \xB7 Smooth",name:"Steel",admin:!0,dark:!0,primary:"#3b76bc",bg:"#586170"},{id:"admin-smooth-teal",label:"Admin \xB7 Smooth",name:"Teal",admin:!0,dark:!0,primary:"#2a8178",bg:"#586170"}];export function populateThemeSelect(e,{includeDefault:f=!1,defaultLabel:b="\u2014 Use site default \u2014",includeAdmin:m=!1}={}){for(;e.firstChild;)e.removeChild(e.firstChild);if(f){const a=document.createElement("option");a.value="",a.textContent=b,e.appendChild(a)}const d=THEMES.filter(a=>m||!a.admin),t=new Set;for(const a of d)if(!t.has(a.label)){t.add(a.label);const l=document.createElement("optgroup");l.label=a.label;for(const i of d.filter(r=>r.label===a.label)){const r=document.createElement("option");r.value=i.id,r.textContent=i.name||`${i.label} ${i.dark?"Dark":"Light"}`,l.appendChild(r)}e.appendChild(l)}}
@@ -1,3 +1,3 @@
1
- import{api as C,apiRequest as v}from"../api.js";import{populateThemeSelect as w}from"../lib/themes.js";export const settingsView={templateUrl:"/admin/js/templates/settings.html",async onMount(e){E.tabs(e.find("#settings-tabs").get(0));const k=E.loader(e.get(0),{type:"dots"}),l=await C.settings.get().catch(()=>({}));if(k.destroy(),e.find("#field-site-title").val(l.title||""),e.find("#field-tagline").val(l.tagline||""),e.find("#field-font-family").val(l.fontFamily||"Roboto"),e.find("#field-font-size").val(l.fontSize||16),w(e.find("#field-theme").get(0)),w(e.find("#field-admin-theme").get(0)),e.find("#field-theme").val(l.baseTheme||l.theme||"charcoal-dark"),l.baseTheme){e.find("#field-theme").prop("disabled",!0);const t=document.createElement("p");t.className="form-hint",t.style.cssText="margin-top:.4rem;font-size:.8rem;color:var(--dm-info);";const s=document.createElement("a");s.href="#/plugins/theme-roller",s.textContent="Theme Roller",t.appendChild(document.createTextNode(`Custom theme \u201C${l.theme}\u201D is active (based on ${l.baseTheme}). Manage via `)),t.appendChild(s),t.appendChild(document.createTextNode(".")),e.find("#field-theme").get(0).closest(".col-6").appendChild(t)}e.find("#field-admin-theme").val(l.adminTheme||"charcoal-dark");const p=l.autoTheme||{},h=!!p.enabled,b=e.find("#field-auto-theme-enabled"),y=e.find("#auto-theme-fields"),u=e.find("#field-theme"),f=u.get(0).innerHTML;e.find("#field-day-theme").html(f),e.find("#field-night-theme").html(f),l.baseTheme?(b.prop("disabled",!0),e.find("#auto-theme-roller-hint").show()):(b.prop("checked",h),h&&(y.show(),u.prop("disabled",!0)),b.on("change",function(){const t=this.checked;y.toggle(t),u.prop("disabled",t)})),e.find("#field-day-theme").val(p.dayTheme||"charcoal-light"),e.find("#field-night-theme").val(p.nightTheme||"charcoal-dark"),e.find("#field-day-start").val(p.dayStart||"07:00"),e.find("#field-night-start").val(p.nightStart||"19:00"),e.find("#field-spacer-size").val(l.layoutOptions?.spacerSize??40),e.find("#field-seo-title").val(l.seo?.defaultTitle||""),e.find("#field-seo-separator").val(l.seo?.titleSeparator||" | "),e.find("#field-seo-desc").val(l.seo?.defaultDescription||""),e.find("#field-footer-copy").val(l.footer?.copyright||""),e.find("#field-social-twitter").val(l.social?.twitter||""),e.find("#field-social-facebook").val(l.social?.facebook||""),e.find("#field-social-instagram").val(l.social?.instagram||""),e.find("#field-social-linkedin").val(l.social?.linkedin||""),e.find("#field-social-github").val(l.social?.github||""),e.find("#field-social-youtube").val(l.social?.youtube||""),e.find("#field-smtp-host").val(l.smtp?.host||""),e.find("#field-smtp-port").val(l.smtp?.port||587),e.find("#field-smtp-user").val(l.smtp?.user||""),e.find("#field-smtp-pass").val(l.smtp?.pass||""),e.find("#field-smtp-secure").prop("checked",l.smtp?.secure||!1),e.find("#field-smtp-from-address").val(l.smtp?.fromAddress||""),e.find("#field-smtp-from-name").val(l.smtp?.fromName||"");const i=l.backToTop||{};e.find("#field-btt-enabled").prop("checked",i.enabled!==!1),e.find("#field-btt-threshold").val(i.scrollThreshold??300),e.find("#field-btt-position").val(i.position||"bottom-right"),e.find("#field-btt-offset").val(i.offset??32),e.find("#field-btt-bottom-offset").val(i.bottomOffset??i.offset??32),e.find("#field-btt-label").val(i.label||""),e.find("#field-btt-smooth").prop("checked",i.smooth!==!1);const d=l.cookieConsent||{};e.find("#field-cc-enabled").prop("checked",d.enabled!==!1),e.find("#field-cc-message").val(d.message||""),e.find("#field-cc-accept-all").val(d.acceptAllText||"Accept All"),e.find("#field-cc-reject-all").val(d.rejectAllText||"Reject All"),e.find("#field-cc-customize").val(d.customizeText||"Customize"),e.find("#field-cc-save-prefs").val(d.savePreferencesText||"Save Preferences"),e.find("#field-cc-privacy-text").val(d.privacyPolicyText||"Privacy Policy"),e.find("#field-cc-privacy-url").val(d.privacyPolicyUrl||""),e.find("#field-cc-cookie-text").val(d.cookiePolicyText||"Cookie Policy"),e.find("#field-cc-cookie-url").val(d.cookiePolicyUrl||""),e.find("#field-cc-position").val(d.position||"bottom"),e.find("#field-cc-layout").val(d.layout||"bar"),e.find("#field-cc-theme").val(d.theme||"dark"),e.find("#field-cc-show-functional").prop("checked",d.showFunctional!==!1),e.find("#field-cc-show-analytics").prop("checked",d.showAnalytics!==!1),e.find("#field-cc-show-marketing").prop("checked",d.showMarketing!==!1),e.find("#field-cc-version").val(d.consentVersion||"1.0");const m=l.breadcrumbs||{};e.find("#field-breadcrumbs-enabled").prop("checked",m.enabled===!0),e.find("#field-breadcrumbs-home-label").val(m.homeLabel||"Home"),e.find("#field-breadcrumbs-position").val(m.position||"TL"),e.find("#field-bc-offset-x").val(m.offsetX??8),e.find("#field-bc-offset-y").val(m.offsetY??8);function c(t){e.find(".bc-pos-btn").each(function(){const s=this.dataset.pos===t;this.style.background=s?"var(--primary, #5b8cff)":"",this.style.color=s?"#fff":""})}c(m.position||"TL"),e.find(".bc-pos-btn").on("click",function(){const t=this.dataset.pos;e.find("#field-breadcrumbs-position").val(t),c(t)});let a=null;try{const{css:t}=await v("/settings/custom-css");e.find("#field-custom-css").val(t||""),E.editor&&(a=E.editor(e.find("#field-custom-css").get(0),{mode:"code",language:"css",lineNumbers:!0,showToolbar:!1,minHeight:420,placeholder:"/* Add your custom CSS here */",characterCount:!0}))}catch{}a&&a._editorEl&&a._editorEl.addEventListener("keydown",t=>{if(!t.ctrlKey&&!t.metaKey)return;if(t.key==="s"){t.preventDefault(),e.find("#save-css-btn").get(0)?.click();return}const s=a._editorEl,n=s.selectionStart!==s.selectionEnd;if((t.key==="c"||t.key==="x")&&!n){t.preventDefault();const o=s.value,g=s.selectionStart,T=o.lastIndexOf(`
1
+ import{api as C,apiRequest as v}from"../api.js";import{populateThemeSelect as w}from"../lib/themes.js";export const settingsView={templateUrl:"/admin/js/templates/settings.html",async onMount(e){E.tabs(e.find("#settings-tabs").get(0));const k=E.loader(e.get(0),{type:"dots"}),l=await C.settings.get().catch(()=>({}));if(k.destroy(),e.find("#field-site-title").val(l.title||""),e.find("#field-tagline").val(l.tagline||""),e.find("#field-font-family").val(l.fontFamily||"Roboto"),e.find("#field-font-size").val(l.fontSize||16),w(e.find("#field-theme").get(0)),w(e.find("#field-admin-theme").get(0),{includeAdmin:!0}),e.find("#field-theme").val(l.baseTheme||l.theme||"charcoal-dark"),l.baseTheme){e.find("#field-theme").prop("disabled",!0);const t=document.createElement("p");t.className="form-hint",t.style.cssText="margin-top:.4rem;font-size:.8rem;color:var(--dm-info);";const s=document.createElement("a");s.href="#/plugins/theme-roller",s.textContent="Theme Roller",t.appendChild(document.createTextNode(`Custom theme \u201C${l.theme}\u201D is active (based on ${l.baseTheme}). Manage via `)),t.appendChild(s),t.appendChild(document.createTextNode(".")),e.find("#field-theme").get(0).closest(".col-6").appendChild(t)}e.find("#field-admin-theme").val(l.adminTheme||"charcoal-dark");const p=l.autoTheme||{},h=!!p.enabled,b=e.find("#field-auto-theme-enabled"),y=e.find("#auto-theme-fields"),u=e.find("#field-theme"),f=u.get(0).innerHTML;e.find("#field-day-theme").html(f),e.find("#field-night-theme").html(f),l.baseTheme?(b.prop("disabled",!0),e.find("#auto-theme-roller-hint").show()):(b.prop("checked",h),h&&(y.show(),u.prop("disabled",!0)),b.on("change",function(){const t=this.checked;y.toggle(t),u.prop("disabled",t)})),e.find("#field-day-theme").val(p.dayTheme||"charcoal-light"),e.find("#field-night-theme").val(p.nightTheme||"charcoal-dark"),e.find("#field-day-start").val(p.dayStart||"07:00"),e.find("#field-night-start").val(p.nightStart||"19:00"),e.find("#field-spacer-size").val(l.layoutOptions?.spacerSize??40),e.find("#field-seo-title").val(l.seo?.defaultTitle||""),e.find("#field-seo-separator").val(l.seo?.titleSeparator||" | "),e.find("#field-seo-desc").val(l.seo?.defaultDescription||""),e.find("#field-footer-copy").val(l.footer?.copyright||""),e.find("#field-social-twitter").val(l.social?.twitter||""),e.find("#field-social-facebook").val(l.social?.facebook||""),e.find("#field-social-instagram").val(l.social?.instagram||""),e.find("#field-social-linkedin").val(l.social?.linkedin||""),e.find("#field-social-github").val(l.social?.github||""),e.find("#field-social-youtube").val(l.social?.youtube||""),e.find("#field-smtp-host").val(l.smtp?.host||""),e.find("#field-smtp-port").val(l.smtp?.port||587),e.find("#field-smtp-user").val(l.smtp?.user||""),e.find("#field-smtp-pass").val(l.smtp?.pass||""),e.find("#field-smtp-secure").prop("checked",l.smtp?.secure||!1),e.find("#field-smtp-from-address").val(l.smtp?.fromAddress||""),e.find("#field-smtp-from-name").val(l.smtp?.fromName||"");const i=l.backToTop||{};e.find("#field-btt-enabled").prop("checked",i.enabled!==!1),e.find("#field-btt-threshold").val(i.scrollThreshold??300),e.find("#field-btt-position").val(i.position||"bottom-right"),e.find("#field-btt-offset").val(i.offset??32),e.find("#field-btt-bottom-offset").val(i.bottomOffset??i.offset??32),e.find("#field-btt-label").val(i.label||""),e.find("#field-btt-smooth").prop("checked",i.smooth!==!1);const d=l.cookieConsent||{};e.find("#field-cc-enabled").prop("checked",d.enabled!==!1),e.find("#field-cc-message").val(d.message||""),e.find("#field-cc-accept-all").val(d.acceptAllText||"Accept All"),e.find("#field-cc-reject-all").val(d.rejectAllText||"Reject All"),e.find("#field-cc-customize").val(d.customizeText||"Customize"),e.find("#field-cc-save-prefs").val(d.savePreferencesText||"Save Preferences"),e.find("#field-cc-privacy-text").val(d.privacyPolicyText||"Privacy Policy"),e.find("#field-cc-privacy-url").val(d.privacyPolicyUrl||""),e.find("#field-cc-cookie-text").val(d.cookiePolicyText||"Cookie Policy"),e.find("#field-cc-cookie-url").val(d.cookiePolicyUrl||""),e.find("#field-cc-position").val(d.position||"bottom"),e.find("#field-cc-layout").val(d.layout||"bar"),e.find("#field-cc-theme").val(d.theme||"dark"),e.find("#field-cc-show-functional").prop("checked",d.showFunctional!==!1),e.find("#field-cc-show-analytics").prop("checked",d.showAnalytics!==!1),e.find("#field-cc-show-marketing").prop("checked",d.showMarketing!==!1),e.find("#field-cc-version").val(d.consentVersion||"1.0");const m=l.breadcrumbs||{};e.find("#field-breadcrumbs-enabled").prop("checked",m.enabled===!0),e.find("#field-breadcrumbs-home-label").val(m.homeLabel||"Home"),e.find("#field-breadcrumbs-position").val(m.position||"TL"),e.find("#field-bc-offset-x").val(m.offsetX??8),e.find("#field-bc-offset-y").val(m.offsetY??8);function c(t){e.find(".bc-pos-btn").each(function(){const s=this.dataset.pos===t;this.style.background=s?"var(--primary, #5b8cff)":"",this.style.color=s?"#fff":""})}c(m.position||"TL"),e.find(".bc-pos-btn").on("click",function(){const t=this.dataset.pos;e.find("#field-breadcrumbs-position").val(t),c(t)});let a=null;try{const{css:t}=await v("/settings/custom-css");e.find("#field-custom-css").val(t||""),E.editor&&(a=E.editor(e.find("#field-custom-css").get(0),{mode:"code",language:"css",lineNumbers:!0,showToolbar:!1,minHeight:420,placeholder:"/* Add your custom CSS here */",characterCount:!0}))}catch{}a&&a._editorEl&&a._editorEl.addEventListener("keydown",t=>{if(!t.ctrlKey&&!t.metaKey)return;if(t.key==="s"){t.preventDefault(),e.find("#save-css-btn").get(0)?.click();return}const s=a._editorEl,n=s.selectionStart!==s.selectionEnd;if((t.key==="c"||t.key==="x")&&!n){t.preventDefault();const o=s.value,g=s.selectionStart,T=o.lastIndexOf(`
2
2
  `,g-1)+1,x=o.indexOf(`
3
3
  `,g),S=x===-1?o.slice(T):o.slice(T,x+1);if(navigator.clipboard.writeText(S),t.key==="x"){const z=o.slice(0,T),P=x===-1?"":o.slice(x+1);s.value=z+P,s.selectionStart=s.selectionEnd=T,s.dispatchEvent(new Event("input",{bubbles:!0})),E.toast("Line cut",{type:"info",duration:1500})}else E.toast("Line copied",{type:"info",duration:1500})}}),e.find("#save-settings-btn").on("click",async()=>{const t=e.find("#field-admin-theme").val(),s=!l.baseTheme&&e.find("#field-auto-theme-enabled").prop("checked"),n={enabled:s,dayTheme:e.find("#field-day-theme").val()||"charcoal-light",nightTheme:e.find("#field-night-theme").val()||"charcoal-dark",dayStart:e.find("#field-day-start").val()||"07:00",nightStart:e.find("#field-night-start").val()||"19:00"},o={title:e.find("#field-site-title").val().trim(),tagline:e.find("#field-tagline").val().trim(),fontFamily:e.find("#field-font-family").val()||"Roboto",fontSize:parseInt(e.find("#field-font-size").val(),10)||16,theme:l.baseTheme?l.theme:s?n.dayTheme:e.find("#field-theme").val(),...l.baseTheme?{baseTheme:l.baseTheme}:{},autoTheme:n,adminTheme:t,layoutOptions:{spacerSize:parseInt(e.find("#field-spacer-size").val(),10)||40},seo:{defaultTitle:e.find("#field-seo-title").val().trim(),titleSeparator:e.find("#field-seo-separator").val()||" | ",defaultDescription:e.find("#field-seo-desc").val().trim()},footer:{copyright:e.find("#field-footer-copy").val().trim(),links:l.footer?.links||[]},social:{twitter:e.find("#field-social-twitter").val().trim(),facebook:e.find("#field-social-facebook").val().trim(),instagram:e.find("#field-social-instagram").val().trim(),linkedin:e.find("#field-social-linkedin").val().trim(),github:e.find("#field-social-github").val().trim(),youtube:e.find("#field-social-youtube").val().trim()},smtp:{host:e.find("#field-smtp-host").val().trim(),port:parseInt(e.find("#field-smtp-port").val(),10)||587,user:e.find("#field-smtp-user").val().trim(),pass:e.find("#field-smtp-pass").val(),secure:e.find("#field-smtp-secure").prop("checked"),fromAddress:e.find("#field-smtp-from-address").val().trim(),fromName:e.find("#field-smtp-from-name").val().trim()},backToTop:{enabled:e.find("#field-btt-enabled").prop("checked"),scrollThreshold:parseInt(e.find("#field-btt-threshold").val(),10)||300,position:e.find("#field-btt-position").val()||"bottom-right",offset:parseInt(e.find("#field-btt-offset").val(),10)||32,bottomOffset:parseInt(e.find("#field-btt-bottom-offset").val(),10)||32,label:e.find("#field-btt-label").val().trim(),smooth:e.find("#field-btt-smooth").prop("checked")},cookieConsent:{enabled:e.find("#field-cc-enabled").prop("checked"),message:e.find("#field-cc-message").val().trim(),acceptAllText:e.find("#field-cc-accept-all").val().trim(),rejectAllText:e.find("#field-cc-reject-all").val().trim(),customizeText:e.find("#field-cc-customize").val().trim(),savePreferencesText:e.find("#field-cc-save-prefs").val().trim(),privacyPolicyText:e.find("#field-cc-privacy-text").val().trim(),privacyPolicyUrl:e.find("#field-cc-privacy-url").val().trim(),cookiePolicyText:e.find("#field-cc-cookie-text").val().trim(),cookiePolicyUrl:e.find("#field-cc-cookie-url").val().trim(),position:e.find("#field-cc-position").val(),layout:e.find("#field-cc-layout").val(),theme:e.find("#field-cc-theme").val(),showFunctional:e.find("#field-cc-show-functional").prop("checked"),showAnalytics:e.find("#field-cc-show-analytics").prop("checked"),showMarketing:e.find("#field-cc-show-marketing").prop("checked"),consentVersion:e.find("#field-cc-version").val().trim()||"1.0"}},g=e.find("#save-settings-btn");g.prop("disabled",!0);try{await C.settings.save(o),Domma.theme.set(t),E.toast("Settings saved.",{type:"success"})}catch{E.toast("Failed to save settings.",{type:"error"})}finally{g.prop("disabled",!1)}}),e.find("#send-test-email-btn").on("click",async()=>{const t=e.find("#field-test-email-to").val().trim(),s=e.find("#test-email-result").get(0),n=e.find("#send-test-email-btn");n.prop("disabled",!0),s&&(s.textContent="Sending\u2026",s.style.color="");try{const o=await v("/settings/test-email",{method:"POST",body:JSON.stringify({to:t||void 0})});s&&(s.textContent=o.message||"Test email sent.",s.style.color="var(--success,#4ade80)")}catch(o){s&&(s.textContent=o.message||"Failed to send test email.",s.style.color="var(--danger,#f87171)")}finally{n.prop("disabled",!1)}}),e.find("#save-breadcrumbs-btn").on("click",async()=>{const t=e.find("#save-breadcrumbs-btn");t.prop("disabled",!0);try{const s={enabled:e.find("#field-breadcrumbs-enabled").prop("checked"),homeLabel:e.find("#field-breadcrumbs-home-label").val().trim()||"Home",position:e.find("#field-breadcrumbs-position").val()||"TL",offsetX:parseInt(e.find("#field-bc-offset-x").val(),10)||8,offsetY:parseInt(e.find("#field-bc-offset-y").val(),10)||8};await C.settings.save({...l,breadcrumbs:s}),l.breadcrumbs=s,E.toast("Breadcrumbs settings saved.",{type:"success"})}catch{E.toast("Failed to save breadcrumbs settings.",{type:"error"})}finally{t.prop("disabled",!1)}}),e.find("#save-css-btn").on("click",async()=>{const t=a?a.getValue():e.find("#field-custom-css").val(),s=e.find("#save-css-btn");s.prop("disabled",!0);try{await v("/settings/custom-css",{method:"PUT",body:JSON.stringify({css:t})}),E.toast("Custom CSS saved.",{type:"success"})}catch(n){E.toast(n.message||"Failed to save CSS.",{type:"error"})}finally{s.prop("disabled",!1)}});async function r(){try{const t=await v("/cache/status"),s=t.enabled?`enabled (${t.driver})`:"disabled",n=t.maxItems?` \xB7 ${t.size} / ${t.maxItems} entries`:"";e.find("#cache-status").text(s+n)}catch{e.find("#cache-status").text("unknown")}try{const{entries:t}=await v("/cache/keys");A(e.find("#cache-keys-table"),t),e.find("#cache-key-count").text(`(${t.length})`)}catch(t){e.find("#cache-keys-table").get(0).textContent="Failed to load keys: "+t.message}}await r(),e.find("#btn-clear-cache").on("click",async()=>{if(!await E.confirm("Clear the entire response cache?"))return;const t=e.find("#btn-clear-cache");t.prop("disabled",!0);try{await v("/cache/clear",{method:"POST"}),E.toast("Cache cleared.",{type:"success"}),await r()}catch(s){E.toast(s.message||"Failed to clear cache.",{type:"error"})}finally{t.prop("disabled",!1)}}),e.find("#btn-refresh-cache-keys").on("click",r)}};function A(e,k){const l=e.get(0);if(!l)return;for(;l.firstChild;)l.removeChild(l.firstChild);if(!k.length){const f=document.createElement("p");f.className="text-muted",f.style.fontSize=".875rem",f.textContent="Cache is empty.",l.appendChild(f);return}const p=[...k].reverse(),h=document.createElement("table");h.className="table table-compact";const b=document.createElement("thead"),y=document.createElement("tr");for(const f of["Key","Tags","Expires"]){const i=document.createElement("th");i.textContent=f,y.appendChild(i)}b.appendChild(y),h.appendChild(b);const u=document.createElement("tbody");for(const f of p){const i=document.createElement("tr"),d=document.createElement("td");d.style.fontFamily="monospace",d.style.fontSize="12px",d.textContent=f.key,i.appendChild(d);const m=document.createElement("td");m.style.fontSize="12px";for(const a of f.tags){const r=document.createElement("span");r.className="badge",r.style.marginRight="4px",r.style.fontSize="11px",r.textContent=a,m.appendChild(r)}i.appendChild(m);const c=document.createElement("td");if(c.style.fontSize="12px",c.style.color="var(--dm-muted, #6b7280)",f.expiresAt===null)c.textContent="no expiry";else{const a=f.expiresAt-Date.now();a<=0?c.textContent="expired":a<6e4?c.textContent=`${Math.round(a/1e3)}s`:a<36e5?c.textContent=`${Math.round(a/6e4)}m`:c.textContent=`${Math.round(a/36e5)}h`}i.appendChild(c),u.appendChild(i)}h.appendChild(u),l.appendChild(h)}
package/config/site.json CHANGED
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "title": "My CMS Site",
3
+ "url": "http://domma-cms.local:4096",
3
4
  "tagline": "This is the Domma CMS",
4
5
  "theme": "silver-dark",
5
6
  "seo": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-cms",
3
- "version": "0.31.0",
3
+ "version": "0.32.2",
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",
@@ -81,7 +81,7 @@
81
81
  "@fastify/rate-limit": "^10.3.0",
82
82
  "@fastify/static": "9.1.1",
83
83
  "bcryptjs": "^3.0.3",
84
- "domma-js": "^0.27.1",
84
+ "domma-js": "0.29.1",
85
85
  "dotenv": "^17.2.3",
86
86
  "fastify": "5.8.5",
87
87
  "gray-matter": "^4.0.3",
@@ -0,0 +1,47 @@
1
+ <div class="survey-audience-plugin">
2
+
3
+ <div class="toolbar mb-3" style="display:flex;align-items:center;gap:0.5rem;flex-wrap:wrap;">
4
+ <h2 style="margin:0;flex:1;">Survey Audience</h2>
5
+ <button id="back-surveys" class="btn btn-outline">
6
+ <span data-icon="arrow-left"></span>
7
+ Surveys
8
+ </button>
9
+ <button id="import-contacts" class="btn btn-outline">
10
+ <span data-icon="download"></span>
11
+ Import from Contacts
12
+ </button>
13
+ <button id="add-contact" class="btn btn-primary">
14
+ <span data-icon="plus"></span>
15
+ Add contact
16
+ </button>
17
+ </div>
18
+
19
+ <div class="grid grid-cols-3" style="gap:1rem;align-items:start;">
20
+
21
+ <!-- Contacts table -->
22
+ <div style="grid-column:span 2;">
23
+ <div id="audience-table"></div>
24
+ <div id="audience-empty" style="display:none;text-align:center;padding:2rem 1rem;color:var(--dm-text-muted);">
25
+ <p>No audience contacts yet. Add one or import from the Contacts plugin.</p>
26
+ </div>
27
+ </div>
28
+
29
+ <!-- Groups panel -->
30
+ <div class="card">
31
+ <div class="card-header"><strong>Groups</strong></div>
32
+ <div class="card-body">
33
+ <div style="display:flex;gap:0.5rem;margin-bottom:1rem;">
34
+ <input id="new-group-name" type="text" class="form-input" placeholder="New group name…" style="flex:1;">
35
+ <button id="add-group" class="btn btn-primary">
36
+ <span data-icon="plus"></span>
37
+ </button>
38
+ </div>
39
+ <div id="groups-list">
40
+ <p class="text-muted">No groups yet.</p>
41
+ </div>
42
+ </div>
43
+ </div>
44
+
45
+ </div>
46
+
47
+ </div>
@@ -0,0 +1,58 @@
1
+ <div class="survey-results-plugin">
2
+
3
+ <style>
4
+ /* Layout only + real Domma theme tokens — surfaces use .card so the
5
+ active theme is always respected; no hardcoded colours. */
6
+ .survey-results-plugin .sr-head { display:flex; align-items:flex-start; gap:1rem; flex-wrap:wrap; margin-bottom:1.25rem; }
7
+ .survey-results-plugin .sr-head h2 { margin:0 0 0.25rem; }
8
+ .survey-results-plugin .sr-sub { color:var(--dm-text-muted); margin:0; }
9
+ .survey-results-plugin .sr-head-actions { margin-left:auto; display:flex; gap:0.5rem; flex-wrap:wrap; }
10
+
11
+ .survey-results-plugin .sr-summary { display:grid; grid-template-columns:1.4fr 1fr 1fr 1fr; gap:1rem; margin-bottom:1.5rem; }
12
+ @media (max-width:760px){ .survey-results-plugin .sr-summary { grid-template-columns:1fr 1fr; } }
13
+ .survey-results-plugin .sr-rate-num { font-size:2.4rem; font-weight:800; line-height:1; color:var(--dm-primary); }
14
+ .survey-results-plugin .sr-kpi-num { font-size:2rem; font-weight:800; line-height:1; }
15
+ .survey-results-plugin .sr-kpi-lbl { color:var(--dm-text-muted); font-size:0.85rem; margin-top:0.35rem; }
16
+ .survey-results-plugin .sr-rate-bar { height:8px; border-radius:999px; background:var(--dm-border); margin-top:0.75rem; overflow:hidden; }
17
+ .survey-results-plugin .sr-rate-fill { height:100%; background:var(--dm-primary); border-radius:999px; }
18
+
19
+ .survey-results-plugin .sr-section-title { font-size:1.05rem; font-weight:700; margin:0 0 0.75rem; }
20
+ .survey-results-plugin .sr-q-head { display:flex; align-items:baseline; gap:0.6rem; margin-bottom:0.75rem; }
21
+ .survey-results-plugin .sr-q-num { display:inline-flex; align-items:center; justify-content:center; min-width:1.6rem; height:1.6rem; border-radius:50%; background:var(--dm-primary); color:var(--dm-text-inverse); font-size:0.8rem; font-weight:700; }
22
+ .survey-results-plugin .sr-q-label { font-weight:700; }
23
+ .survey-results-plugin .sr-q-meta { margin-left:auto; color:var(--dm-text-muted); font-size:0.8rem; text-transform:uppercase; letter-spacing:0.03em; }
24
+ .survey-results-plugin .sr-avg { font-size:1.1rem; margin:0 0 0.6rem; }
25
+ .survey-results-plugin .sr-avg strong { font-size:1.5rem; color:var(--dm-primary); }
26
+
27
+ /* Distribution bars (survey.css isn't loaded in admin, so style here). */
28
+ .survey-results-plugin .survey-dist { display:flex; flex-direction:column; gap:0.5rem; }
29
+ .survey-results-plugin .survey-dist-row { display:grid; grid-template-columns:minmax(80px,160px) 1fr auto; align-items:center; gap:0.75rem; }
30
+ .survey-results-plugin .survey-dist-label { font-size:0.9rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
31
+ .survey-results-plugin .survey-dist-bar { height:14px; background:var(--dm-border); border-radius:999px; overflow:hidden; }
32
+ .survey-results-plugin .survey-dist-fill { height:100%; background:var(--dm-primary); border-radius:999px; min-width:2px; }
33
+ .survey-results-plugin .survey-dist-val { font-size:0.85rem; color:var(--dm-text-muted); white-space:nowrap; }
34
+
35
+ /* Free-text answers. */
36
+ .survey-results-plugin .sr-quotes { max-height:260px; overflow:auto; display:flex; flex-direction:column; gap:0.5rem; }
37
+ .survey-results-plugin .sr-quote { padding:0.5rem 0.75rem; background:var(--dm-surface-raised); border-left:3px solid var(--dm-border); border-radius:0 6px 6px 0; font-size:0.92rem; }
38
+ .survey-results-plugin .sr-empty { color:var(--dm-text-muted); font-style:italic; }
39
+ </style>
40
+
41
+ <div class="sr-head">
42
+ <div>
43
+ <h2 id="results-title">Survey Results</h2>
44
+ <p class="sr-sub" id="results-sub">How your audience responded.</p>
45
+ </div>
46
+ <div class="sr-head-actions">
47
+ <button id="export-csv" class="btn btn-outline"><span data-icon="download"></span> CSV</button>
48
+ <button id="export-json" class="btn btn-outline"><span data-icon="download"></span> JSON</button>
49
+ <button id="back-surveys" class="btn btn-outline"><span data-icon="arrow-left"></span> Surveys</button>
50
+ </div>
51
+ </div>
52
+
53
+ <div id="summary" class="sr-summary"></div>
54
+
55
+ <h3 class="sr-section-title">Question breakdown</h3>
56
+ <div id="questions"></div>
57
+
58
+ </div>
@@ -0,0 +1,141 @@
1
+ <div class="survey-editor-plugin">
2
+
3
+ <style>
4
+ /* Layout only — colours/borders come from Domma's themed .card and
5
+ .form-input, so the active theme is always respected. */
6
+ .survey-editor-plugin .se-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem 1.25rem; }
7
+ .survey-editor-plugin .se-span-2 { grid-column: 1 / -1; }
8
+ .survey-editor-plugin .se-field { display: flex; flex-direction: column; gap: 0.35rem; }
9
+ .survey-editor-plugin .se-field label { font-weight: 600; font-size: 0.85rem; }
10
+ .survey-editor-plugin .se-field .se-hint { font-size: 0.78rem; color: var(--dm-text-muted); }
11
+ .survey-editor-plugin .se-field .form-input { width: 100%; }
12
+ .survey-editor-plugin .se-field textarea.form-input { min-height: 90px; resize: vertical; }
13
+ .survey-editor-plugin .se-field select[multiple].form-input { min-height: 120px; }
14
+ @media (max-width: 640px) { .survey-editor-plugin .se-grid { grid-template-columns: 1fr; } }
15
+ </style>
16
+
17
+ <div class="toolbar mb-3" style="display:flex;align-items:center;gap:0.5rem;">
18
+ <h2 id="editor-title" style="margin:0;flex:1;">New Survey</h2>
19
+ <button id="back-btn" class="btn btn-outline"><span data-icon="arrow-left"></span> Back</button>
20
+ </div>
21
+
22
+ <div class="card">
23
+ <div class="card-header"><strong>Basics</strong></div>
24
+ <div class="card-body">
25
+ <div class="se-grid">
26
+ <div class="se-field se-span-2">
27
+ <label for="f-title">Title</label>
28
+ <input id="f-title" name="title" type="text" class="form-input" placeholder="Customer satisfaction Q3">
29
+ </div>
30
+ <div class="se-field">
31
+ <label for="f-slug">Slug</label>
32
+ <input id="f-slug" name="slug" type="text" class="form-input" placeholder="customer-sat-q3">
33
+ <span class="se-hint" id="slug-hint">URL-safe identifier.</span>
34
+ </div>
35
+ <div class="se-field">
36
+ <label for="f-formSlug">Form</label>
37
+ <select id="f-formSlug" name="formSlug" class="form-input"><option value="">Select a form…</option></select>
38
+ <span class="se-hint">The Forms definition respondents complete.</span>
39
+ </div>
40
+ <div class="se-field se-span-2">
41
+ <label for="f-description">Description</label>
42
+ <textarea id="f-description" name="description" class="form-input" placeholder="Optional internal description…"></textarea>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </div>
47
+
48
+ <div class="card mt-3">
49
+ <div class="card-header"><strong>Setup</strong></div>
50
+ <div class="card-body">
51
+ <div class="se-grid">
52
+ <div class="se-field">
53
+ <label for="f-status">Status</label>
54
+ <select id="f-status" name="status" class="form-input">
55
+ <option value="draft">Draft</option>
56
+ <option value="open">Open</option>
57
+ <option value="closed">Closed</option>
58
+ </select>
59
+ </div>
60
+ <div class="se-field">
61
+ <label for="f-mode">Mode</label>
62
+ <select id="f-mode" name="mode" class="form-input">
63
+ <option value="identified">Identified</option>
64
+ <option value="anonymous">Anonymous</option>
65
+ </select>
66
+ <span class="se-hint">Anonymous de-links responses from contacts.</span>
67
+ </div>
68
+ <div class="se-field se-span-2">
69
+ <label for="f-groupIds">Audience groups</label>
70
+ <select id="f-groupIds" name="groupIds" class="form-input" multiple></select>
71
+ <span class="se-hint">Hold Ctrl/Cmd to select multiple groups.</span>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ </div>
76
+
77
+ <div class="card mt-3">
78
+ <div class="card-header"><strong>Schedule</strong></div>
79
+ <div class="card-body">
80
+ <div class="se-grid">
81
+ <div class="se-field"><label for="f-opensAt">Opens at</label><input id="f-opensAt" name="opensAt" type="date" class="form-input"></div>
82
+ <div class="se-field"><label for="f-closesAt">Closes at</label><input id="f-closesAt" name="closesAt" type="date" class="form-input"></div>
83
+ <div class="se-field"><label for="f-reminderAfterDays">Reminder after (days)</label><input id="f-reminderAfterDays" name="reminderAfterDays" type="number" min="0" value="7" class="form-input"></div>
84
+ </div>
85
+ </div>
86
+ </div>
87
+
88
+ <div class="card mt-3">
89
+ <div class="card-header"><strong>Survey page content</strong></div>
90
+ <div class="card-body">
91
+ <div class="se-grid">
92
+ <div class="se-field se-span-2">
93
+ <label for="f-intro">Intro (Markdown — shown above the form)</label>
94
+ <textarea id="f-intro" name="intro" class="form-input" placeholder="## Welcome&#10;Thanks for taking a moment to help us improve."></textarea>
95
+ </div>
96
+ <div class="se-field se-span-2">
97
+ <label for="f-footer">Footer (Markdown — shown below the form)</label>
98
+ <textarea id="f-footer" name="footer" class="form-input" placeholder="Questions? Contact us at hello@example.com"></textarea>
99
+ </div>
100
+ <div class="se-field se-span-2">
101
+ <label for="f-thankYou">Thank-you message (shown after submitting)</label>
102
+ <textarea id="f-thankYou" name="thankYou" class="form-input" placeholder="Thank you — your response has been recorded."></textarea>
103
+ </div>
104
+ </div>
105
+ </div>
106
+ </div>
107
+
108
+ <div class="card mt-3">
109
+ <div class="card-header"><strong>Invitation email</strong></div>
110
+ <div class="card-body">
111
+ <div class="se-grid">
112
+ <div class="se-field se-span-2">
113
+ <label for="f-inviteSubject">Subject</label>
114
+ <input id="f-inviteSubject" name="inviteSubject" type="text" class="form-input" placeholder="We would love your feedback">
115
+ </div>
116
+ <div class="se-field se-span-2">
117
+ <label for="f-inviteBody">Body</label>
118
+ <textarea id="f-inviteBody" name="inviteBody" class="form-input" placeholder="Hi {{name}}, please complete our survey: {{link}}"></textarea>
119
+ <span class="se-hint">{{name}} and {{link}} are substituted per recipient; {{link}} becomes the button.</span>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ </div>
124
+
125
+ <div class="card mt-3">
126
+ <div class="card-body" style="display:flex;gap:0.5rem;flex-wrap:wrap;">
127
+ <button id="save-btn" class="btn btn-primary">Save Survey</button>
128
+ <button id="cancel-btn" class="btn btn-outline">Cancel</button>
129
+ </div>
130
+ </div>
131
+
132
+ <div id="send-panel" class="card mt-3" style="display:none;">
133
+ <div class="card-header"><strong>Invitations</strong></div>
134
+ <div class="card-body" style="display:flex;gap:0.5rem;flex-wrap:wrap;align-items:center;">
135
+ <button id="send-all-btn" class="btn btn-primary"><span data-icon="send"></span> Send invitations</button>
136
+ <button id="send-reminder-btn" class="btn btn-outline"><span data-icon="bell"></span> Send reminder to non-responders</button>
137
+ <span id="send-hint" class="text-muted" style="font-size:0.85rem;"></span>
138
+ </div>
139
+ </div>
140
+
141
+ </div>
@@ -0,0 +1,25 @@
1
+ <div class="surveys-plugin">
2
+
3
+ <!-- Toolbar -->
4
+ <div class="toolbar mb-3" style="display:flex;align-items:center;gap:0.5rem;flex-wrap:wrap;">
5
+ <h2 style="margin:0;flex:1;">Surveys</h2>
6
+ <button id="manage-audience" class="btn btn-outline">
7
+ <span data-icon="users"></span>
8
+ Audience
9
+ </button>
10
+ <button id="new-survey" class="btn btn-primary">
11
+ <span data-icon="plus"></span>
12
+ New Survey
13
+ </button>
14
+ </div>
15
+
16
+ <!-- Surveys table -->
17
+ <div id="surveys-table"></div>
18
+
19
+ <!-- Empty state -->
20
+ <div id="surveys-empty" style="display:none;text-align:center;padding:3rem 1rem;color:var(--dm-text-muted);">
21
+ <span data-icon="clipboard-list" style="font-size:3rem;display:block;margin-bottom:1rem;"></span>
22
+ <p>No surveys yet. Click <strong>New Survey</strong> to create your first campaign.</p>
23
+ </div>
24
+
25
+ </div>