domma-cms 0.6.1 → 0.6.4

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,4 +1,4 @@
1
- import{api as U}from"../api.js";import{createToolbar as ye,insertAtCursor as he}from"../lib/markdown-toolbar.js";function xe(){const o=E.slideover({title:"Editor Reference",size:"md",position:"right"}),be="background:var(--dm-surface-subtle,#1a1a2e);padding:.2rem .4rem;border-radius:3px;font-size:.85em;font-family:monospace;",ge="margin:1rem 0 .5rem;font-size:1rem;";function T(i){const d=document.createElement("code");return d.style.cssText=be,d.textContent=i,d}function C(i){const d=document.createElement("h3");return d.style.cssText=ge,d.textContent=i,d}const se=document.createElement("div");se.style.cssText="padding:1rem;";const Z=document.createElement("div");Z.className="tabs";const ce=document.createElement("div");ce.className="tab-list",["Basics","Layout","Components","Data","Advanced"].forEach((i,d)=>{const t=document.createElement("button");t.className="tab-item"+(d===0?" active":""),t.textContent=i,ce.appendChild(t)});const J=document.createElement("div");J.className="tab-content";const v="display:flex;flex-direction:column;gap:1rem;",ne=document.createElement("div");ne.className="tab-panel active",ne.style.cssText=v;const Q=document.createElement("div");Q.className="tab-panel",Q.style.cssText=v;const O=document.createElement("div");O.className="tab-panel",O.style.cssText=v;const G=document.createElement("div");G.className="tab-panel",G.style.cssText=v;const ae=document.createElement("div");ae.className="tab-panel",ae.style.cssText=v,J.appendChild(ne),J.appendChild(Q),J.appendChild(O),J.appendChild(G),J.appendChild(ae),Z.appendChild(ce),Z.appendChild(J),se.appendChild(Z);const q=document.createElement("div");q.appendChild(C("Markdown Basics"));const ee=document.createElement("table");ee.style.cssText="width:100%;border-collapse:collapse;font-size:.9em;";const j=document.createElement("thead"),te=document.createElement("tr");["Syntax","Result"].forEach(i=>{const d=document.createElement("th");d.style.cssText="text-align:left;padding:.4rem .5rem;border-bottom:1px solid var(--dm-border,#333);",d.textContent=i,te.appendChild(d)}),j.appendChild(te),ee.appendChild(j);const pe=document.createElement("tbody");[["**bold**","bold (rendered bold)"],["_italic_","italic (rendered italic)"],["## Heading","Heading (h2)"],["[text](url)","Link"],["![alt](url)","Image"],["- item","Bullet list"],["`code`","Inline code"],["---","Divider"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.35rem .5rem;vertical-align:top;",n.appendChild(T(i));const a=document.createElement("td");a.style.cssText="padding:.35rem .5rem;vertical-align:top;color:var(--dm-text-muted,#aaa);font-size:.85em;",a.textContent=d,t.appendChild(n),t.appendChild(a),pe.appendChild(t)}),ee.appendChild(pe),q.appendChild(ee),ne.appendChild(q);const _=document.createElement("div");_.appendChild(C("Grid & Columns"));function k(i){const d=document.createElement("p");return d.style.cssText="margin:.3rem 0 .6rem;font-size:.82em;color:var(--dm-text-muted,#aaa);",d.textContent=i,d}function e(i,d,t){const n=document.createElement("p");n.style.cssText="margin:.75rem 0 .25rem;font-size:.85em;font-weight:600;",n.textContent=i;const a=document.createElement("pre");a.style.cssText=be+"display:block;padding:.5rem .75rem;white-space:pre;overflow-x:auto;margin:0;",a.textContent=d;const re=document.createDocumentFragment();return re.appendChild(n),re.appendChild(a),t&&re.appendChild(k(t)),re}const f=document.createElement("table");f.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['cols="N"',"[grid] only","Number of columns (1\u201312)"],['gap="N"',"[grid], [row]","Gap between columns/rows (1\u20136)"],['span="N"',"[col] only","How many columns this cell spans"],['fullwidth="true"',"[grid] only","Break out of page container to span full viewport"],['class="x"',"all","Add extra CSS classes"]].forEach(([i,d,t])=>{const n=document.createElement("tr");[i,d,t].forEach((a,re)=>{const ue=document.createElement("td");ue.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",re===0?ue.appendChild(T(i)):(ue.style.color="var(--dm-text-muted,#aaa)",ue.textContent=a),n.appendChild(ue)}),f.appendChild(n)}),_.appendChild(f),_.appendChild(e("Equal columns",`[grid cols="3" gap="4"]
1
+ import{api as U}from"../api.js";import{createToolbar as ye,insertAtCursor as he}from"../lib/markdown-toolbar.js";function xe(){const o=E.slideover({title:"Editor Reference",size:"md",position:"right"}),be="background:var(--dm-surface-subtle,#1a1a2e);padding:.2rem .4rem;border-radius:3px;font-size:.85em;font-family:monospace;",ge="margin:1rem 0 .5rem;font-size:1rem;";function S(i){const d=document.createElement("code");return d.style.cssText=be,d.textContent=i,d}function y(i){const d=document.createElement("h3");return d.style.cssText=ge,d.textContent=i,d}const re=document.createElement("div");re.style.cssText="padding:1rem;";const Z=document.createElement("div");Z.className="tabs";const ce=document.createElement("div");ce.className="tab-list",["Basics","Layout","Components","Data","Advanced"].forEach((i,d)=>{const t=document.createElement("button");t.className="tab-item"+(d===0?" active":""),t.textContent=i,ce.appendChild(t)});const J=document.createElement("div");J.className="tab-content";const x="display:flex;flex-direction:column;gap:1rem;",ne=document.createElement("div");ne.className="tab-panel active",ne.style.cssText=x;const Q=document.createElement("div");Q.className="tab-panel",Q.style.cssText=x;const M=document.createElement("div");M.className="tab-panel",M.style.cssText=x;const V=document.createElement("div");V.className="tab-panel",V.style.cssText=x;const ae=document.createElement("div");ae.className="tab-panel",ae.style.cssText=x,J.appendChild(ne),J.appendChild(Q),J.appendChild(M),J.appendChild(V),J.appendChild(ae),Z.appendChild(ce),Z.appendChild(J),re.appendChild(Z);const O=document.createElement("div");O.appendChild(y("Markdown Basics"));const ee=document.createElement("table");ee.style.cssText="width:100%;border-collapse:collapse;font-size:.9em;";const j=document.createElement("thead"),te=document.createElement("tr");["Syntax","Result"].forEach(i=>{const d=document.createElement("th");d.style.cssText="text-align:left;padding:.4rem .5rem;border-bottom:1px solid var(--dm-border,#333);",d.textContent=i,te.appendChild(d)}),j.appendChild(te),ee.appendChild(j);const pe=document.createElement("tbody");[["**bold**","bold (rendered bold)"],["_italic_","italic (rendered italic)"],["## Heading","Heading (h2)"],["[text](url)","Link"],["![alt](url)","Image"],["- item","Bullet list"],["`code`","Inline code"],["---","Divider"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.35rem .5rem;vertical-align:top;",n.appendChild(S(i));const a=document.createElement("td");a.style.cssText="padding:.35rem .5rem;vertical-align:top;color:var(--dm-text-muted,#aaa);font-size:.85em;",a.textContent=d,t.appendChild(n),t.appendChild(a),pe.appendChild(t)}),ee.appendChild(pe),O.appendChild(ee),ne.appendChild(O);const _=document.createElement("div");_.appendChild(y("Grid & Columns"));function z(i){const d=document.createElement("p");return d.style.cssText="margin:.3rem 0 .6rem;font-size:.82em;color:var(--dm-text-muted,#aaa);",d.textContent=i,d}function e(i,d,t){const n=document.createElement("p");n.style.cssText="margin:.75rem 0 .25rem;font-size:.85em;font-weight:600;",n.textContent=i;const a=document.createElement("pre");a.style.cssText=be+"display:block;padding:.5rem .75rem;white-space:pre;overflow-x:auto;margin:0;",a.textContent=d;const se=document.createDocumentFragment();return se.appendChild(n),se.appendChild(a),t&&se.appendChild(z(t)),se}const f=document.createElement("table");f.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['cols="N"',"[grid] only","Number of columns (1\u201312)"],['gap="N"',"[grid], [row]","Gap between columns/rows (1\u20136)"],['span="N"',"[col] only","How many columns this cell spans"],['fullwidth="true"',"[grid] only","Break out of page container to span full viewport"],['class="x"',"all","Add extra CSS classes"]].forEach(([i,d,t])=>{const n=document.createElement("tr");[i,d,t].forEach((a,se)=>{const ue=document.createElement("td");ue.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",se===0?ue.appendChild(S(i)):(ue.style.color="var(--dm-text-muted,#aaa)",ue.textContent=a),n.appendChild(ue)}),f.appendChild(n)}),_.appendChild(f),_.appendChild(e("Equal columns",`[grid cols="3" gap="4"]
2
2
  [col]One[/col]
3
3
  [col]Two[/col]
4
4
  [col]Three[/col]
@@ -18,7 +18,7 @@ import{api as U}from"../api.js";import{createToolbar as ye,insertAtCursor as he}
18
18
  [col]
19
19
  [card title="Three"]Content[/card]
20
20
  [/col]
21
- [/grid]`,null)),Q.appendChild(_);const l=document.createElement("div");l.appendChild(C("Cards")),l.appendChild(e("Basic card",`[card title="Optional Title"]
21
+ [/grid]`,null)),Q.appendChild(_);const l=document.createElement("div");l.appendChild(y("Cards")),l.appendChild(e("Basic card",`[card title="Optional Title"]
22
22
  Markdown **works** here.
23
23
  [/card]`,"Omit title for a card with no header.")),l.appendChild(e("Icon \u2014 inline (default)",`[card title="Feature" icon="star"]
24
24
  Icon sits left of the title in a flex row.
@@ -32,41 +32,41 @@ Hidden by default.
32
32
  variant="primary" hover footer="Footer text"
33
33
  collapsible="true" class="extra" id="my-card"]
34
34
  Body content.
35
- [/card]`,'All attributes are optional. variant accepts "primary". icon-layout accepts "inline" (default) or "stacked".')),O.appendChild(l);const c=document.createElement("div");c.appendChild(C("Badge")),c.appendChild(k("Inline badge/label elements. Self-closing renders an empty coloured dot."));const h=document.createElement("table");h.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['variant="..."',"primary (default), secondary, success, danger, warning, info, light, dark"],["pill","Flag: rounded pill shape (.badge-pill)"],["outline","Flag: outlined style (.badge-outline)"],['size="small|large"',"Reduced or enlarged badge"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(T(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),h.appendChild(t)}),c.appendChild(h),c.appendChild(e("Basic badge",'[badge variant="success"]New[/badge]',null)),c.appendChild(e("Pill outline badge",'[badge variant="danger" outline pill]Deprecated[/badge]',null)),O.appendChild(c);const r=document.createElement("div");r.appendChild(C("Spacer")),r.appendChild(k("Self-closing shortcode that inserts a fixed-height blank gap."));const b=document.createElement("table");b.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['size="N"',"Height in px (default: 8)"],['class="..."',"Extra CSS class on the spacer div"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(T(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),b.appendChild(t)}),r.appendChild(b),r.appendChild(e("32px gap",'[spacer size="32" /]',null)),Q.appendChild(r);const p=document.createElement("div");p.appendChild(C("Icon")),p.appendChild(k("Self-closing shortcode that renders any Domma icon inline."));const m=document.createElement("table");m.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['name="..."',"Domma icon name (required). Also accepted as src=."],['size="N"',"Width and height in px"],['color="..."',"CSS colour applied via style"],['class="..."',"Extra CSS classes"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(T(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),m.appendChild(t)}),p.appendChild(m),p.appendChild(e("Coloured icon",'[icon name="star" size="24" color="#f5a623" /]',null)),O.appendChild(p);const y=document.createElement("div");y.appendChild(C("Center")),y.appendChild(k("Wraps content in a centred div. Works with any content including cards, grids, and text.")),y.appendChild(e("Example","[center]Centred content here[/center]",'Accepts an optional class="..." attribute for extra styling.')),Q.appendChild(y);const s=document.createElement("div");s.appendChild(C("Hero")),s.appendChild(k("Full-width hero sections \u2014 no plugin required. Uses Domma's built-in Hero CSS component."));const g=document.createElement("table");g.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['title="..."',"Heading text"],['tagline="..."',"Subtitle text"],['size="sm|lg|full"',"Height variant (default: normal)"],['variant="dark|primary|gradient-blue|gradient-purple|gradient-sunset|gradient-ocean"',"Colour / gradient preset"],['image="url"',"Background image URL (adds cover mode)"],['overlay="light|dark|darker|gradient|gradient-reverse"',"Image overlay style"],['align="center|left"',"Content alignment (default: center)"],['fullwidth="true"',"Break out of the page container to span full viewport width"],['bg="..."',"Background colour CSS value"],["twinkle","Flag: adds particle overlay (requires Effects plugin)"],['twinkle-count="N"',"Number of particles"],['twinkle-colour="..."',"Particle colour CSS value"],["blobs","Flag: adds ambient blob background"],['blobs-type="..."',"Blob animation type (default: float-blobs)"],['class="..."',"Extra CSS classes"],['id="..."',"Element id"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(T(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),g.appendChild(t)}),s.appendChild(g),s.appendChild(e("Basic hero",'[hero title="Welcome" tagline="Build something great"][/hero]',null)),s.appendChild(e("Gradient with body content",`[hero title="Get Started" tagline="Everything you need." size="lg" variant="gradient-blue" align="center"]
35
+ [/card]`,'All attributes are optional. variant accepts "primary". icon-layout accepts "inline" (default) or "stacked".')),M.appendChild(l);const p=document.createElement("div");p.appendChild(y("Badge")),p.appendChild(z("Inline badge/label elements. Self-closing renders an empty coloured dot."));const h=document.createElement("table");h.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['variant="..."',"primary (default), secondary, success, danger, warning, info, light, dark"],["pill","Flag: rounded pill shape (.badge-pill)"],["outline","Flag: outlined style (.badge-outline)"],['size="small|large"',"Reduced or enlarged badge"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(S(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),h.appendChild(t)}),p.appendChild(h),p.appendChild(e("Basic badge",'[badge variant="success"]New[/badge]',null)),p.appendChild(e("Pill outline badge",'[badge variant="danger" outline pill]Deprecated[/badge]',null)),M.appendChild(p);const s=document.createElement("div");s.appendChild(y("Spacer")),s.appendChild(z("Self-closing shortcode that inserts a fixed-height blank gap."));const b=document.createElement("table");b.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['size="N"',"Height in px (default: 8)"],['class="..."',"Extra CSS class on the spacer div"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(S(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),b.appendChild(t)}),s.appendChild(b),s.appendChild(e("32px gap",'[spacer size="32" /]',null)),Q.appendChild(s);const m=document.createElement("div");m.appendChild(y("Icon")),m.appendChild(z("Self-closing shortcode that renders any Domma icon inline."));const u=document.createElement("table");u.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['name="..."',"Domma icon name (required). Also accepted as src=."],['size="N"',"Width and height in px"],['color="..."',"CSS colour applied via style"],['class="..."',"Extra CSS classes"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(S(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),u.appendChild(t)}),m.appendChild(u),m.appendChild(e("Coloured icon",'[icon name="star" size="24" color="#f5a623" /]',null)),M.appendChild(m);const w=document.createElement("div");w.appendChild(y("Center")),w.appendChild(z("Wraps content in a centred div. Works with any content including cards, grids, and text.")),w.appendChild(e("Example","[center]Centred content here[/center]",'Accepts an optional class="..." attribute for extra styling.')),Q.appendChild(w);const c=document.createElement("div");c.appendChild(y("Hero")),c.appendChild(z("Full-width hero sections \u2014 no plugin required. Uses Domma's built-in Hero CSS component."));const v=document.createElement("table");v.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['title="..."',"Heading text"],['tagline="..."',"Subtitle text"],['size="sm|lg|full"',"Height variant (default: normal)"],['variant="dark|primary|gradient-blue|gradient-purple|gradient-sunset|gradient-ocean"',"Colour / gradient preset"],['image="url"',"Background image URL (adds cover mode)"],['overlay="light|dark|darker|gradient|gradient-reverse"',"Image overlay style"],['align="center|left"',"Content alignment (default: center)"],['fullwidth="true"',"Break out of the page container to span full viewport width"],['bg="..."',"Background colour CSS value"],["twinkle","Flag: adds particle overlay (requires Effects plugin)"],['twinkle-count="N"',"Number of particles"],['twinkle-colour="..."',"Particle colour CSS value"],["blobs","Flag: adds ambient blob background"],['blobs-type="..."',"Blob animation type (default: float-blobs)"],['class="..."',"Extra CSS classes"],['id="..."',"Element id"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(S(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),v.appendChild(t)}),c.appendChild(v),c.appendChild(e("Basic hero",'[hero title="Welcome" tagline="Build something great"][/hero]',null)),c.appendChild(e("Gradient with body content",`[hero title="Get Started" tagline="Everything you need." size="lg" variant="gradient-blue" align="center"]
36
36
  Some introductory **Markdown** content here.
37
- [/hero]`,null)),s.appendChild(e("Background image with overlay",'[hero title="Our Story" image="/media/hero.jpg" overlay="dark" size="full"][/hero]',"Combine image + overlay for text legibility over photos.")),Q.appendChild(s);const S=document.createElement("div");S.appendChild(C("Interactive Components")),S.appendChild(k("Tabs, accordion, carousel, and countdown \u2014 no plugin required.")),S.appendChild(e("Tabs",`[tabs]
37
+ [/hero]`,null)),c.appendChild(e("Background image with overlay",'[hero title="Our Story" image="/media/hero.jpg" overlay="dark" size="full"][/hero]',"Combine image + overlay for text legibility over photos.")),Q.appendChild(c);const T=document.createElement("div");T.appendChild(y("Interactive Components")),T.appendChild(z("Tabs, accordion, carousel, and countdown \u2014 no plugin required.")),T.appendChild(e("Tabs",`[tabs]
38
38
  [tab title="First"]Content **A**[/tab]
39
39
  [tab title="Second"]Content **B**[/tab]
40
- [/tabs]`,'Add style="pills" for pill-style navigation.')),S.appendChild(e("Accordion",`[accordion]
40
+ [/tabs]`,'Add style="pills" for pill-style navigation.')),T.appendChild(e("Accordion",`[accordion]
41
41
  [item title="Question 1"]Answer here.[/item]
42
42
  [item title="Question 2"]Answer here.[/item]
43
- [/accordion]`,'Add multiple="true" to allow several panels open at once.')),S.appendChild(e("Carousel",`[carousel autoplay="true" interval="5000"]
43
+ [/accordion]`,'Add multiple="true" to allow several panels open at once.')),T.appendChild(e("Carousel",`[carousel autoplay="true" interval="5000"]
44
44
  [slide title="Slide 1"]Description[/slide]
45
45
  [slide image="/media/photo.jpg" title="Slide 2"]Caption[/slide]
46
- [/carousel]`,'Omit autoplay for a manual carousel. loop="false" disables wrapping.')),S.appendChild(e("Countdown (to date)",'[countdown to="2026-12-31" format="DD:HH:mm:ss" /]',null)),S.appendChild(e("Countdown (duration)",'[countdown duration="300" format="mm:ss" /]',"duration is in seconds. format options: mm:ss \xB7 HH:mm:ss \xB7 DD:HH:mm:ss")),O.appendChild(S);const u=document.createElement("div");u.appendChild(C("Timeline")),u.appendChild(k("Renders a Domma Progression component with event items."));const w=document.createElement("table");w.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['layout="vertical|centred|horizontal"',"[timeline] \u2014 layout orientation (default: vertical)"],['theme="minimal|corporate|modern"',"[timeline] \u2014 visual theme (default: minimal)"],['mode="timeline|roadmap"',"[timeline] \u2014 display mode (default: timeline)"],['class="..."',"[timeline] \u2014 extra CSS classes"],['id="..."',"[timeline] \u2014 element id"],['title="..."',"[event] \u2014 event heading"],['date="..."',"[event] \u2014 display date string"],['status="planned|in-progress|completed|blocked"',"[event] \u2014 progress status"],['icon="..."',"[event] \u2014 Domma icon name"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(T(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),w.appendChild(t)}),u.appendChild(w),u.appendChild(e("Vertical timeline",`[timeline layout="vertical" theme="modern"]
46
+ [/carousel]`,'Omit autoplay for a manual carousel. loop="false" disables wrapping.')),T.appendChild(e("Countdown (to date)",'[countdown to="2026-12-31" format="DD:HH:mm:ss" /]',null)),T.appendChild(e("Countdown (duration)",'[countdown duration="300" format="mm:ss" /]',"duration is in seconds. format options: mm:ss \xB7 HH:mm:ss \xB7 DD:HH:mm:ss")),M.appendChild(T);const g=document.createElement("div");g.appendChild(y("Timeline")),g.appendChild(z("Renders a Domma Progression component with event items."));const C=document.createElement("table");C.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['layout="vertical|centred|horizontal"',"[timeline] \u2014 layout orientation (default: vertical)"],['theme="minimal|corporate|modern"',"[timeline] \u2014 visual theme (default: minimal)"],['mode="timeline|roadmap"',"[timeline] \u2014 display mode (default: timeline)"],['class="..."',"[timeline] \u2014 extra CSS classes"],['id="..."',"[timeline] \u2014 element id"],['title="..."',"[event] \u2014 event heading"],['date="..."',"[event] \u2014 display date string"],['status="planned|in-progress|completed|blocked"',"[event] \u2014 progress status"],['icon="..."',"[event] \u2014 Domma icon name"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(S(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),C.appendChild(t)}),g.appendChild(C),g.appendChild(e("Vertical timeline",`[timeline layout="vertical" theme="modern"]
47
47
  [event title="Kickoff" date="Jan 2025" status="completed" icon="flag"]
48
48
  Project launched.
49
49
  [/event]
50
50
  [event title="Beta" date="Jun 2025" status="in-progress" icon="rocket"]
51
51
  In active development.
52
52
  [/event]
53
- [/timeline]`,null)),O.appendChild(u);const B=document.createElement("div");B.appendChild(C("Embedding a Form"));const x=document.createElement("table");x.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['name="..."',"Form slug (required). Also accepted as slug=."],['class="..."',"Extra CSS classes on the wrapper div"],['id="..."',"id attribute on the wrapper div"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(T(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),x.appendChild(t)}),B.appendChild(x),B.appendChild(e("Basic",'[form name="contact" /]',"Forms are managed under Forms in the sidebar.")),B.appendChild(e("With wrapper styling",'[form name="newsletter" class="mt-4" id="newsletter-form" /]',null)),G.appendChild(B);const z=document.createElement("div");z.appendChild(C("Displaying a Collection"));const L=document.createElement("pre");L.style.cssText=be+"display:block;padding:.5rem .75rem;white-space:pre;overflow-x:auto;",L.textContent='[collection slug="enquiries" display="table" /]',z.appendChild(L);const A=[["slug","Required. The collection slug."],["display","table (default), cards, or list."],["fields","Comma-separated field keys to show. Defaults to all."],["limit","Maximum number of entries to display."],["sort","Field key to sort by."],["order","asc or desc (default asc)."],["title-field","Field used as the card/list title (cards + list)."],["columns","Grid columns for cards display (2\u20134, default 3)."],["search","true/false \u2014 enable search in table mode (default true)."],["sortable","true/false \u2014 enable column sorting in table mode (default true)."],["exportable","true/false \u2014 show CSV export button in table mode (default false)."],["page-size","Rows per page in table mode (default 25)."],["empty","Text shown when there are no entries."],["cta","Action slug: enables per-entry CTA buttons."],["cta-label",'CTA button label (default: "Run").'],["cta-icon","CTA button icon name."],["cta-style","CTA button variant (default: primary)."],["cta-confirm","CTA confirmation prompt text."]],H=document.createElement("table");H.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",A.forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;width:35%;",n.appendChild(T(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),H.appendChild(t)}),z.appendChild(H),z.appendChild(k("Table display uses Domma Table \u2014 search, sort, pagination, column selector, and CSV export are all built in. Cards and lists are static.")),G.appendChild(z);const M=document.createElement("div");M.appendChild(C("View \u2014 Pro")),M.appendChild(k("Renders a Collection with full filtering, pagination, and display options. Requires a Pro MongoDB collection."));const V=document.createElement("table");V.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[["slug","Required. The collection slug."],["display","table (default), cards, or list."],["limit","Maximum number of entries to display."],["fields","Comma-separated field keys to show."],["title-field","Field used as the card/list title."],["columns","Grid columns for cards display (2\u20134, default 3)."],["empty","Text shown when there are no entries."]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;width:35%;",n.appendChild(T(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),V.appendChild(t)}),M.appendChild(V),M.appendChild(e("Table display",'[view slug="products" display="table" limit="50" /]',null)),M.appendChild(e("Cards display",'[view slug="team" display="cards" columns="3" title-field="name" /]',null)),G.appendChild(M);const P=document.createElement("div");P.appendChild(C("CTA \u2014 Pro")),P.appendChild(k("Renders an action button that triggers a Pro Action against an entry. Requires the Pro Actions feature."));const K=document.createElement("table");K.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[["action","Required. The action slug."],["entry","Required. The entry id to act on."],["style","Button variant (default: primary)."],["icon","Domma icon name for the button."],["size","Button size (sm, lg)."],["confirm","Confirmation prompt text before running."],["label","Self-closing only: button label text."]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;width:35%;",n.appendChild(T(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),K.appendChild(t)}),P.appendChild(K),P.appendChild(e("Wrapping form",'[cta action="approve" entry="abc123" style="success" confirm="Approve this entry?"]Approve[/cta]',null)),P.appendChild(e("Self-closing",'[cta action="archive" entry="abc123" icon="archive" label="Archive" /]',null)),G.appendChild(P);const F=document.createElement("div");F.appendChild(C("Effects")),F.appendChild(k("Shortcodes for scroll-triggered animations, counters, typewriter text, and more. Requires the Domma Effects plugin."));const me=[['[reveal animation="fade"]...[/reveal]',"Fade/slide/zoom on scroll"],["[breathe]...[/breathe]","Continuous gentle scale loop"],["[pulse]...[/pulse]","Repeating scale pulse"],["[shake]...[/shake]","One-shot shake"],['[scribe speed="50"]...[/scribe]',"Typewriter \u2014 simple mode (text typed letter by letter)"],['[scribe loop="true"][render]Text[/render][wait]1500[/wait][undo /][/scribe]',"Typewriter \u2014 script mode (sequenced render/wait/undo actions)"],["[scramble]...[/scramble]","Character-scramble reveal"],['[counter to="100" /]',"Animated number counter (self-closing)"],["[ripple]...[/ripple]","Click-triggered ripple"],['[twinkle count="50"]...[/twinkle]',"Particle/star overlay"],['[animate type="fade-in-up"]...[/animate]',"CSS-only animation (no plugin needed)"],['[ambient type="aurora"]...[/ambient]',"CSS-only animated background"],['[firework type="burst" colour="rainbow" /]',"CSS firework (self-closing)"],["[fireworks]...[/fireworks]","Fireworks display container"],['[celebrate theme="auto" /]',"Seasonal canvas celebration"]],le=document.createElement("table");le.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",me.forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(T(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),le.appendChild(t)}),F.appendChild(le),F.appendChild(e("Scribe script \u2014 looping multi-phrase typewriter",`[scribe loop="true" loop-delay="2000" delete-speed="20"]
53
+ [/timeline]`,null)),M.appendChild(g);const B=document.createElement("div");B.appendChild(y("Embedding a Form"));const r=document.createElement("table");r.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['name="..."',"Form slug (required). Also accepted as slug=."],['class="..."',"Extra CSS classes on the wrapper div"],['id="..."',"id attribute on the wrapper div"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(S(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),r.appendChild(t)}),B.appendChild(r),B.appendChild(e("Basic",'[form name="contact" /]',"Forms are managed under Forms in the sidebar.")),B.appendChild(e("With wrapper styling",'[form name="newsletter" class="mt-4" id="newsletter-form" /]',null)),V.appendChild(B);const k=document.createElement("div");k.appendChild(y("Displaying a Collection"));const D=document.createElement("pre");D.style.cssText=be+"display:block;padding:.5rem .75rem;white-space:pre;overflow-x:auto;",D.textContent='[collection slug="enquiries" display="table" /]',k.appendChild(D);const N=[["slug","Required. The collection slug."],["display","table (default), cards, or list."],["fields","Comma-separated field keys to show. Defaults to all."],["limit","Maximum number of entries to display."],["sort","Field key to sort by."],["order","asc or desc (default asc)."],["title-field","Field used as the card/list title (cards + list)."],["columns","Grid columns for cards display (2\u20134, default 3)."],["search","true/false \u2014 enable search in table mode (default true)."],["sortable","true/false \u2014 enable column sorting in table mode (default true)."],["exportable","true/false \u2014 show CSV export button in table mode (default false)."],["page-size","Rows per page in table mode (default 25)."],["empty","Text shown when there are no entries."],["cta","Action slug: enables per-entry CTA buttons."],["cta-label",'CTA button label (default: "Run").'],["cta-icon","CTA button icon name."],["cta-style","CTA button variant (default: primary)."],["cta-confirm","CTA confirmation prompt text."]],P=document.createElement("table");P.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",N.forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;width:35%;",n.appendChild(S(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),P.appendChild(t)}),k.appendChild(P),k.appendChild(z("Table display uses Domma Table \u2014 search, sort, pagination, column selector, and CSV export are all built in. Cards and lists are static.")),V.appendChild(k);const H=document.createElement("div");H.appendChild(y("View \u2014 Pro")),H.appendChild(z("Renders a Collection with full filtering, pagination, and display options. Requires a Pro MongoDB collection."));const W=document.createElement("table");W.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[["slug","Required. The collection slug."],["display","table (default), cards, or list."],["limit","Maximum number of entries to display."],["fields","Comma-separated field keys to show."],["title-field","Field used as the card/list title."],["columns","Grid columns for cards display (2\u20134, default 3)."],["empty","Text shown when there are no entries."]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;width:35%;",n.appendChild(S(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),W.appendChild(t)}),H.appendChild(W),H.appendChild(e("Table display",'[view slug="products" display="table" limit="50" /]',null)),H.appendChild(e("Cards display",'[view slug="team" display="cards" columns="3" title-field="name" /]',null)),V.appendChild(H);const F=document.createElement("div");F.appendChild(y("CTA \u2014 Pro")),F.appendChild(z("Renders an action button that triggers a Pro Action against an entry. Requires the Pro Actions feature."));const K=document.createElement("table");K.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[["action","Required. The action slug."],["entry","Required. The entry id to act on."],["style","Button variant (default: primary)."],["icon","Domma icon name for the button."],["size","Button size (sm, lg)."],["confirm","Confirmation prompt text before running."],["label","Self-closing only: button label text."]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;width:35%;",n.appendChild(S(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),K.appendChild(t)}),F.appendChild(K),F.appendChild(e("Wrapping form",'[cta action="approve" entry="abc123" style="success" confirm="Approve this entry?"]Approve[/cta]',null)),F.appendChild(e("Self-closing",'[cta action="archive" entry="abc123" icon="archive" label="Archive" /]',null)),V.appendChild(F);const q=document.createElement("div");q.appendChild(y("Effects")),q.appendChild(z("Shortcodes for scroll-triggered animations, counters, typewriter text, and more. Requires the Domma Effects plugin."));const me=[['[reveal animation="fade"]...[/reveal]',"Fade/slide/zoom on scroll"],["[breathe]...[/breathe]","Continuous gentle scale loop"],["[pulse]...[/pulse]","Repeating scale pulse"],["[shake]...[/shake]","One-shot shake"],['[scribe speed="50"]...[/scribe]',"Typewriter \u2014 simple mode (text typed letter by letter)"],['[scribe loop="true"][render]Text[/render][wait]1500[/wait][undo /][/scribe]',"Typewriter \u2014 script mode (sequenced render/wait/undo actions)"],["[scramble]...[/scramble]","Character-scramble reveal"],['[counter to="100" /]',"Animated number counter (self-closing)"],["[ripple]...[/ripple]","Click-triggered ripple"],['[twinkle count="50"]...[/twinkle]',"Particle/star overlay"],['[animate type="fade-in-up"]...[/animate]',"CSS-only animation (no plugin needed)"],['[ambient type="aurora"]...[/ambient]',"CSS-only animated background"],['[firework type="burst" colour="rainbow" /]',"CSS firework (self-closing)"],["[fireworks]...[/fireworks]","Fireworks display container"],['[celebrate theme="auto" /]',"Seasonal canvas celebration"]],le=document.createElement("table");le.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",me.forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(S(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),le.appendChild(t)}),q.appendChild(le),q.appendChild(e("Scribe script \u2014 looping multi-phrase typewriter",`[scribe loop="true" loop-delay="2000" delete-speed="20"]
54
54
  [render]We build things that matter.[/render]
55
55
  [wait]1200[/wait]
56
56
  [undo all="true" /]
57
57
  [render]We design for humans.[/render]
58
58
  [wait]1200[/wait]
59
59
  [undo all="true" /]
60
- [/scribe]`,'Use [render]...[/render] to type text, [wait]Ms[/wait] to pause, and [undo /] to delete. loop="true" replays the sequence. Only [render], [wait], and [undo] shortcodes are parsed \u2014 plain text between actions is ignored.')),F.appendChild(k("Enable the Domma Effects plugin to activate these shortcodes on your site.")),ae.appendChild(F);const W=document.createElement("div");W.appendChild(C("Tables")),W.appendChild(k("Wrap a standard Markdown (GFM) table with Domma CSS classes and responsive horizontal scrolling."));const Y=document.createElement("table");Y.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['striped="true"',"Alternate row background (.table-striped)"],['bordered="true"',"Borders on all cells (.table-bordered)"],['compact="true"',"Reduced cell padding (.table-compact)"],['caption="..."',"Caption text above the table"],['class="..."',"Extra CSS classes appended to .table"],['id="..."',"id attribute on the <table> element"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(T(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),Y.appendChild(t)}),W.appendChild(Y),W.appendChild(e("Striped + bordered table",`[table striped="true" bordered="true" caption="Product Pricing"]
60
+ [/scribe]`,'Use [render]...[/render] to type text, [wait]Ms[/wait] to pause, and [undo /] to delete. loop="true" replays the sequence. Only [render], [wait], and [undo] shortcodes are parsed \u2014 plain text between actions is ignored.')),q.appendChild(z("Enable the Domma Effects plugin to activate these shortcodes on your site.")),ae.appendChild(q);const G=document.createElement("div");G.appendChild(y("Tables")),G.appendChild(z("Wrap a standard Markdown (GFM) table with Domma CSS classes and responsive horizontal scrolling."));const Y=document.createElement("table");Y.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['striped="true"',"Alternate row background (.table-striped)"],['bordered="true"',"Borders on all cells (.table-bordered)"],['compact="true"',"Reduced cell padding (.table-compact)"],['caption="..."',"Caption text above the table"],['class="..."',"Extra CSS classes appended to .table"],['id="..."',"id attribute on the <table> element"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(S(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),Y.appendChild(t)}),G.appendChild(Y),G.appendChild(e("Striped + bordered table",`[table striped="true" bordered="true" caption="Product Pricing"]
61
61
  | Product | Price |
62
62
  | ------- | ----: |
63
63
  | Widget | $9.99 |
64
64
  | Gadget | $14.99 |
65
- [/table]`,"The inner content must be a valid GFM Markdown table. The shortcode adds Domma CSS classes and wraps in a responsive scroll container.")),O.appendChild(W);const oe=document.createElement("div");oe.appendChild(C("Slideover")),oe.appendChild(k("A trigger button that opens a slide-in panel with Markdown content. No plugin required."));const ie=document.createElement("table");ie.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['title="..."',"Panel header text"],['trigger="..."','Button label (default: "Open")'],['size="sm|md|lg"',"Panel width (default: md)"],['position="right|left"',"Slide direction (default: right)"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(T(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),ie.appendChild(t)}),oe.appendChild(ie),oe.appendChild(e("Example",`[slideover title="More Info" trigger="Read more" size="md"]
65
+ [/table]`,"The inner content must be a valid GFM Markdown table. The shortcode adds Domma CSS classes and wraps in a responsive scroll container.")),M.appendChild(G);const oe=document.createElement("div");oe.appendChild(y("Slideover")),oe.appendChild(z("A trigger button that opens a slide-in panel with Markdown content. No plugin required."));const ie=document.createElement("table");ie.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['title="..."',"Panel header text"],['trigger="..."','Button label (default: "Open")'],['size="sm|md|lg"',"Panel width (default: md)"],['position="right|left"',"Slide direction (default: right)"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(S(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),ie.appendChild(t)}),oe.appendChild(ie),oe.appendChild(e("Example",`[slideover title="More Info" trigger="Read more" size="md"]
66
66
  ## Details
67
67
  Markdown content here.
68
- [/slideover]`,"Nested [card] and [grid] shortcodes work inside the slideover body.")),O.appendChild(oe);const X=document.createElement("div");X.appendChild(C("DConfig \u2014 Declarative Behaviour")),X.appendChild(k("Define click handlers and class toggles without writing JavaScript. Use the DConfig section above or embed inline with [dconfig]...[/dconfig] in the content body. Inline shortcodes win on selector conflict.")),X.appendChild(e("Toggle a class on click",'{ "#my-btn": { "events": { "click": { "target": "#panel", "toggleClass": "hidden" } } } }','Selector keys use standard CSS selectors. "target" is optional \u2014 defaults to the element itself.')),X.appendChild(e("Inline shortcode syntax",`[dconfig]
68
+ [/slideover]`,"Nested [card] and [grid] shortcodes work inside the slideover body.")),M.appendChild(oe);const X=document.createElement("div");X.appendChild(y("DConfig \u2014 Declarative Behaviour")),X.appendChild(z("Define click handlers and class toggles without writing JavaScript. Use the DConfig section above or embed inline with [dconfig]...[/dconfig] in the content body. Inline shortcodes win on selector conflict.")),X.appendChild(e("Toggle a class on click",'{ "#my-btn": { "events": { "click": { "target": "#panel", "toggleClass": "hidden" } } } }','Selector keys use standard CSS selectors. "target" is optional \u2014 defaults to the element itself.')),X.appendChild(e("Inline shortcode syntax",`[dconfig]
69
69
  { "#my-btn": { "events": { "click": { "target": "#panel", "toggleClass": "hidden" } } } }
70
- [/dconfig]`,null)),ae.appendChild(X);const D=document.createElement("div");D.appendChild(C("Tips"));const N=document.createElement("table");N.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['class="text-center"',"Centre text and inline elements on any shortcode"],['class="mx-auto"',"Centre a block element horizontally"],["[center]...[/center]","Shorthand wrapper for centred content"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(T(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),N.appendChild(t)}),D.appendChild(N),D.appendChild(k("Most shortcodes accept a class attribute for custom styling.")),ne.appendChild(D),o.setContent(se),o.open(),E.tabs(Z)}let de=!1,fe=null,ve=!1;export const pageEditorView={templateUrl:"/admin/js/templates/page-editor.html",async onMount(o){const ge=window.location.hash.match(/#\/pages\/edit(\/.*)/),T=ge?ge[1]:null,C=!!T,se=E.loader(o.get(0),{type:"dots"}),Z=[U.layouts.get().catch(()=>({})),U.settings.get().catch(()=>({}))];C&&Z.push(U.pages.get(T).catch(()=>null));const[ce,J,v]=await Promise.all(Z);se.destroy();const ne=J?.layoutOptions?.spacerSize??40;if(C&&!v){E.toast("Page not found.",{type:"error"}),R.navigate("/pages");return}o.find("#editor-title").text(C?`Edit Page \u2014 ${v.title||T}`:"New Page"),C&&o.find("#page-url-path").val(T),v&&(o.find("#field-title").val(v.title||""),o.find("#field-description").val(v.description||""),o.find("#field-status").val(v.status||"draft"),o.find("#field-sort-order").val(v.sortOrder??99),o.find("#field-show-in-nav").prop("checked",!!v.showInNav),o.find("#field-sidebar").prop("checked",!!v.sidebar),o.find("#field-show-breadcrumbs").prop("checked",v.breadcrumbs!==!1),o.find("#field-category").val(v.category||""),o.find("#field-visibility").val(v.visibility||"public"),o.find("#field-theme").val(v.theme||""),o.find("#field-seo-title").val(v.seo?.title||""),o.find("#field-seo-desc").val(v.seo?.description||""),v.dconfig&&o.find("#field-dconfig").val(JSON.stringify(v.dconfig,null,2)));const Q=await U.pages.tags().catch(()=>[]),O=o.find("#field-tags").get(0),G=O?E.pillbox(O,{data:Q,value:v?.tags||[],creatable:!0,searchable:!0,placeholder:"Add tag\u2026"}):null,ae=o.find("#field-layout").empty();if(Object.entries(ce).forEach(([e,f])=>{const l=(v?.layout||"default")===e?"selected":"";ae.append(`<option value="${e}" ${l}>${f.label||e}</option>`)}),C){const e=o.find("#view-page-btn").get(0);e.href=T,e.style.display="";const f=o.find("#live-preview-tab").get(0);f.style.display=""}if(E.tabs(o.find("#editor-meta-tabs").get(0)),C){const e=o.find("#live-preview-tab").get(0),f=o.find("#live-preview-frame").get(0);let l=!1;e.addEventListener("click",function(){l||(f.src=T,l=!0)})}const q=o.find("#markdown-editor"),ee=o.find("#markdown-preview");v&&q.val(v.content||"");const j=q.get(0),te=document.createElement("div");te.className="editor-line-numbers",j.parentElement.insertBefore(te,j);const pe=()=>{const e=j.value.split(`
70
+ [/dconfig]`,null)),ae.appendChild(X);const A=document.createElement("div");A.appendChild(y("Tips"));const L=document.createElement("table");L.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['class="text-center"',"Centre text and inline elements on any shortcode"],['class="mx-auto"',"Centre a block element horizontally"],["[center]...[/center]","Shorthand wrapper for centred content"]].forEach(([i,d])=>{const t=document.createElement("tr"),n=document.createElement("td");n.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",n.appendChild(S(i));const a=document.createElement("td");a.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",a.textContent=d,t.appendChild(n),t.appendChild(a),L.appendChild(t)}),A.appendChild(L),A.appendChild(z("Most shortcodes accept a class attribute for custom styling.")),ne.appendChild(A),o.setContent(re),o.open(),E.tabs(Z)}let de=!1,fe=null,ve=!1;export const pageEditorView={templateUrl:"/admin/js/templates/page-editor.html",async onMount(o){const ge=window.location.hash.match(/#\/pages\/edit(\/.*)/),S=ge?ge[1]:null,y=!!S,re=E.loader(o.get(0),{type:"dots"}),Z=[U.layouts.get().catch(()=>({})),U.settings.get().catch(()=>({}))];y&&Z.push(U.pages.get(S).catch(()=>null));const[ce,J,x]=await Promise.all(Z);re.destroy();const ne=J?.layoutOptions?.spacerSize??40;if(y&&!x){E.toast("Page not found.",{type:"error"}),R.navigate("/pages");return}o.find("#editor-title").text(y?`Edit Page \u2014 ${x.title||S}`:"New Page"),y&&o.find("#page-url-path").val(S),x&&(o.find("#field-title").val(x.title||""),o.find("#field-description").val(x.description||""),o.find("#field-status").val(x.status||"draft"),o.find("#field-sort-order").val(x.sortOrder??99),o.find("#field-show-in-nav").prop("checked",!!x.showInNav),o.find("#field-sidebar").prop("checked",!!x.sidebar),o.find("#field-show-breadcrumbs").prop("checked",x.breadcrumbs!==!1),o.find("#field-category").val(x.category||""),o.find("#field-visibility").val(x.visibility||"public"),o.find("#field-theme").val(x.theme||""),o.find("#field-seo-title").val(x.seo?.title||""),o.find("#field-seo-desc").val(x.seo?.description||""),x.dconfig&&o.find("#field-dconfig").val(JSON.stringify(x.dconfig,null,2)));const Q=await U.pages.tags().catch(()=>[]),M=o.find("#field-tags").get(0),V=M?E.pillbox(M,{data:Q,value:x?.tags||[],creatable:!0,searchable:!0,placeholder:"Add tag\u2026"}):null,ae=o.find("#field-layout").empty();if(Object.entries(ce).forEach(([e,f])=>{const l=(x?.layout||"default")===e?"selected":"";ae.append(`<option value="${e}" ${l}>${f.label||e}</option>`)}),y){const e=o.find("#view-page-btn").get(0);e.href=S,e.style.display="";const f=o.find("#live-preview-tab").get(0);f.style.display=""}if(E.tabs(o.find("#editor-meta-tabs").get(0)),y){const e=o.find("#live-preview-tab").get(0),f=o.find("#live-preview-frame").get(0);let l=!1;e.addEventListener("click",function(){l||(f.src=S,l=!0)})}const O=o.find("#markdown-editor"),ee=o.find("#markdown-preview");x&&O.val(x.content||"");const j=O.get(0),te=document.createElement("div");te.className="editor-line-numbers",j.parentElement.insertBefore(te,j);const pe=()=>{const e=j.value.split(`
71
71
  `).length;te.textContent=Array.from({length:e},(f,l)=>l+1).join(`
72
- `),te.scrollTop=j.scrollTop};pe(),j.addEventListener("input",pe),j.addEventListener("scroll",()=>{te.scrollTop=j.scrollTop});let Ce=null;const _=()=>{clearTimeout(Ce),Ce=setTimeout(async()=>{try{const{html:e}=await U.pages.preview(q.val());ee.html(e,{safe:!1}),I.scan(ee.get(0))}catch{window.marked&&ee.html(marked.parse(q.val()))}},400)},k=ye(q,o.find("#editor-toolbar"),{spacerDefault:ne});k.onLink(async e=>{const l=(await U.pages.list().catch(()=>[])).map(g=>({label:g.title||g.urlPath,value:g.urlPath})),c=document.createElement("div");c.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const h=document.createElement("label");h.className="form-label",h.textContent="URL";const r=document.createElement("input");r.type="text",r.className="form-input",r.placeholder="/about or https://example.com";const b=e.value.substring(e.selectionStart,e.selectionEnd);b&&b.startsWith("/")&&(r.value=b),c.appendChild(h),c.appendChild(r);const p=document.createElement("label");p.className="form-label",p.textContent="Link text";const m=document.createElement("input");m.type="text",m.className="form-input",m.placeholder="Display text",b&&!b.startsWith("/")&&(m.value=b),c.appendChild(p),c.appendChild(m);const y=document.createElement("button");y.type="button",y.className="btn btn-primary",y.textContent="Insert Link",c.appendChild(y);const s=E.modal({title:"Insert Link",size:"sm"});s.element.appendChild(c),s.open(),requestAnimationFrame(()=>{E.autocomplete(r,{data:l,minChars:1,onSelect:g=>{r.value=g.value,m.value||(m.value=g.label)}}),r.focus()}),y.addEventListener("click",()=>{const g=r.value.trim(),S=m.value.trim();if(!g)return;s.close();const u=`[${S||g}](${g})`,w=e.selectionStart,B=e.selectionEnd;e.value=e.value.substring(0,w)+u+e.value.substring(B),e.selectionStart=e.selectionEnd=w+u.length,e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),k.onImage(async e=>{const l=(await U.media.list().catch(()=>[])).filter(r=>/\.(png|jpe?g|gif|webp|svg)$/i.test(r.name)),c=document.createElement("div");if(c.className="media-picker-grid",l.length)l.forEach(r=>{const b=document.createElement("div");b.className="media-picker-item",b.dataset.url=r.url;const p=document.createElement("img");p.src=r.url,p.alt=r.name;const m=document.createElement("span");m.textContent=r.name,b.appendChild(p),b.appendChild(m),c.appendChild(b)});else{const r=document.createElement("p");r.className="text-muted p-3",r.textContent="No images uploaded yet.",c.appendChild(r)}const h=E.modal({title:"Insert Image",size:"lg"});h.element.appendChild(c),$(h.element).on("click",".media-picker-item",function(){const r=$(this).data("url"),b=$(this).find("span").text();he(e,`![${b}](${r})`),h.close(),q.get(0).dispatchEvent(new Event("input",{bubbles:!0}))}),h.open()}),k.onCollection(async e=>{const f=await U.collections.list().catch(()=>[]),l=document.createElement("div");l.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const c="display:block;font-size:.85em;font-weight:600;margin-bottom:.25rem;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",h="width:100%;padding:.4rem .6rem;background:var(--dm-input-bg,#1a1a1a);border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text,#eee);font-size:.9em;",r=document.createElement("div"),b=document.createElement("label");b.style.cssText=c,b.textContent="Collection";const p=document.createElement("select");if(p.style.cssText=h,f.length)f.forEach(D=>{const N=document.createElement("option");N.value=D.slug,N.textContent=D.title||D.slug,p.appendChild(N)});else{const D=document.createElement("option");D.value="",D.textContent="No collections found",p.appendChild(D)}r.appendChild(b),r.appendChild(p),l.appendChild(r);const m=document.createElement("div"),y=document.createElement("label");y.style.cssText=c,y.textContent="Display";const s=document.createElement("select");s.style.cssText=h,["table","cards","list"].forEach(D=>{const N=document.createElement("option");N.value=D,N.textContent=D.charAt(0).toUpperCase()+D.slice(1),s.appendChild(N)}),m.appendChild(y),m.appendChild(s),l.appendChild(m);const g=document.createElement("div");g.style.display="none";const S=document.createElement("label");S.style.cssText=c,S.textContent="Columns (2\u20134)";const u=document.createElement("input");u.type="number",u.min="2",u.max="4",u.value="3",u.style.cssText=h,g.appendChild(S),g.appendChild(u),l.appendChild(g);const w=document.createElement("div"),B=document.createElement("label");B.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.9em;cursor:pointer;";const x=document.createElement("input");x.type="checkbox",x.checked=!0,B.appendChild(x),B.appendChild(document.createTextNode("Enable search")),w.appendChild(B),l.appendChild(w);const z=document.createElement("div"),L=document.createElement("label");L.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.9em;cursor:pointer;";const A=document.createElement("input");A.type="checkbox",A.checked=!0,L.appendChild(A),L.appendChild(document.createTextNode("Sortable columns")),z.appendChild(L),l.appendChild(z);const H=document.createElement("div"),M=document.createElement("label");M.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.9em;cursor:pointer;";const V=document.createElement("input");V.type="checkbox",V.checked=!1,M.appendChild(V),M.appendChild(document.createTextNode("CSV export")),H.appendChild(M),l.appendChild(H);const P=document.createElement("div"),K=document.createElement("label");K.style.cssText=c,K.textContent="Rows per page";const F=document.createElement("input");F.type="number",F.min="5",F.max="100",F.value="25",F.style.cssText=h,P.appendChild(K),P.appendChild(F),l.appendChild(P);const me=document.createElement("div"),le=document.createElement("label");le.style.cssText=c,le.textContent="Limit (optional)";const W=document.createElement("input");W.type="number",W.placeholder="All",W.style.cssText=h,me.appendChild(le),me.appendChild(W),l.appendChild(me);const Y=document.createElement("button");Y.type="button",Y.className="btn btn-primary",Y.textContent="Insert",l.appendChild(Y);const oe=[w,z,H,P],ie=()=>{const D=s.value==="table";oe.forEach(N=>{N.style.display=D?"":"none"}),g.style.display=s.value==="cards"?"":"none"};s.addEventListener("change",ie),ie();const X=E.modal({title:"Insert Collection",size:"sm"});X.element.appendChild(l),X.open(),Y.addEventListener("click",()=>{const D=p.value;if(!D)return;const N=s.value;let i=`[collection slug="${D}" display="${N}"`;N==="cards"&&(i+=` columns="${u.value}"`),N==="table"&&!x.checked&&(i+=' search="false"'),N==="table"&&!A.checked&&(i+=' sortable="false"'),N==="table"&&V.checked&&(i+=' exportable="true"'),N==="table"&&F.value!=="25"&&(i+=` page-size="${F.value}"`);const d=W.value.trim();d&&(i+=` limit="${d}"`),i+=" /]",X.close(),he(e,i),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),k.onForm(async e=>{const f=await U.forms.list().catch(()=>[]),l=document.createElement("div");l.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const c="display:block;font-size:.85em;font-weight:600;margin-bottom:.25rem;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",h="width:100%;padding:.4rem .6rem;background:var(--dm-input-bg,#1a1a1a);border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text,#eee);font-size:.9em;",r=document.createElement("div"),b=document.createElement("label");b.style.cssText=c,b.textContent="Form";const p=document.createElement("select");if(p.style.cssText=h,f.length)f.forEach(s=>{const g=document.createElement("option");g.value=s.slug,g.textContent=s.title||s.slug,p.appendChild(g)});else{const s=document.createElement("option");s.value="",s.textContent="No forms found",p.appendChild(s)}r.appendChild(b),r.appendChild(p),l.appendChild(r);const m=document.createElement("button");m.className="btn btn-primary btn-sm",m.style.cssText="align-self:flex-end;margin-top:.5rem;",m.textContent="Insert Form",l.appendChild(m);const y=E.modal({title:"Insert Form",size:"sm"});y.element.appendChild(l),y.open(),m.addEventListener("click",()=>{const s=p.value;s&&(he(e,`[form slug="${s}" /]`),y.close(),q.get(0).dispatchEvent(new Event("input",{bubbles:!0})))})}),k.onView(async e=>{const f=await U.views.list().catch(()=>[]),l=document.createElement("div");l.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const c="display:block;font-size:.85em;font-weight:600;margin-bottom:.25rem;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",h="width:100%;padding:.4rem .6rem;background:var(--dm-input-bg,#1a1a1a);border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text,#eee);font-size:.9em;",r=document.createElement("div"),b=document.createElement("label");b.style.cssText=c,b.textContent="View";const p=document.createElement("select");if(p.style.cssText=h,f.length)f.forEach(u=>{const w=document.createElement("option");w.value=u.slug,w.textContent=u.title||u.slug,p.appendChild(w)});else{const u=document.createElement("option");u.value="",u.textContent="No Views configured yet \u2014 create one under Data \u2192 Views",p.appendChild(u)}r.appendChild(b),r.appendChild(p),l.appendChild(r);const m=document.createElement("div"),y=document.createElement("label");y.style.cssText=c,y.textContent="Display";const s=document.createElement("select");s.style.cssText=h,["table","cards","list"].forEach(u=>{const w=document.createElement("option");w.value=u,w.textContent=u.charAt(0).toUpperCase()+u.slice(1),s.appendChild(w)}),m.appendChild(y),m.appendChild(s),l.appendChild(m);const g=document.createElement("button");g.type="button",g.className="btn btn-primary",g.textContent="Insert",l.appendChild(g);const S=E.modal({title:"Insert View",size:"sm"});S.element.appendChild(l),S.open(),g.addEventListener("click",()=>{const u=p.value;if(!u)return;const w=`[view slug="${u}" display="${s.value}" /]`;S.close(),he(e,w),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),k.onCta(async e=>{const f=await U.actions.list().catch(()=>[]),l=document.createElement("div");l.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const c="display:block;font-size:.85em;font-weight:600;margin-bottom:.25rem;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",h="width:100%;padding:.4rem .6rem;background:var(--dm-input-bg,#1a1a1a);border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text,#eee);font-size:.9em;";function r(x,z){const L=document.createElement("div"),A=document.createElement("label");return A.style.cssText=c,A.textContent=x,L.appendChild(A),L.appendChild(z),L}function b(x){const z=document.createElement("select");return z.style.cssText=h,x.forEach(([L,A])=>{const H=document.createElement("option");H.value=L,H.textContent=A,z.appendChild(H)}),z}function p(x,z,L){const A=document.createElement("input");return A.type=x,A.placeholder=z||"",A.value=L||"",A.style.cssText=h,A}const m=b(f.length?f.map(x=>[x.slug,x.title||x.slug]):[["","No Actions configured yet \u2014 create one under Data \u2192 Actions"]]);l.appendChild(r("Action",m));const y=p("text","Button label","Run");if(l.appendChild(r("Label",y)),m.addEventListener("change",()=>{const x=f.find(z=>z.slug===m.value);x?.trigger?.label&&(y.value=x.trigger.label)}),f.length){const x=f[0];x?.trigger?.label&&(y.value=x.trigger.label)}const s=p("text","Paste entry UUID\u2026","");l.appendChild(r("Entry ID",s));const g=b([["primary","Primary"],["secondary","Secondary"],["ghost","Ghost"],["danger","Danger"]]);l.appendChild(r("Style",g));const S=p("text","e.g. check, zap, send (optional)","");l.appendChild(r("Icon",S));const u=p("text","Confirmation message (optional)","");l.appendChild(r("Confirm prompt",u));const w=document.createElement("button");w.type="button",w.className="btn btn-primary",w.textContent="Insert",l.appendChild(w);const B=E.modal({title:"Insert CTA Button",size:"sm"});B.element.appendChild(l),B.open(),w.addEventListener("click",()=>{const x=m.value;if(!x)return;const z=s.value.trim(),L=(y.value.trim()||"Run").replace(/\[\/cta\]/gi,""),A=g.value,H=S.value.trim().replace(/"/g,""),M=u.value.trim().replace(/"/g,""),V=z.replace(/"/g,"");let P=`action="${x}" style="${A}"`;V&&(P+=` entry="${V}"`),H&&(P+=` icon="${H}"`),M&&(P+=` confirm="${M}"`);const K=`[cta ${P}]${L}[/cta]`;B.close(),he(e,K),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),k.onHelp(()=>{xe()}),o.find(".editor-view-btn").on("click",function(){const e=$(this).data("mode");o.find(".editor-view-btn").removeClass("active"),$(this).addClass("active"),o.find("#editor-body").removeClass("editor-mode-split editor-mode-write editor-mode-preview").addClass(`editor-mode-${e}`)}),o.find("#fullscreen-btn").on("click",function(){o.find(".editor-card").toggleClass("editor-fullscreen")}),de=!1,fe&&window.removeEventListener("beforeunload",fe),fe=e=>{de&&e.preventDefault()},window.addEventListener("beforeunload",fe),ve||(ve=!0,R.use((e,f,l)=>{const c=window.location.hash.startsWith("#/pages/edit")||window.location.hash==="#/pages/new";if(!de||!c)return l();E.confirm("You have unsaved changes. Leave this page?").then(h=>{h&&(de=!1,l())})})),q.on("input",()=>{de=!0,_()}),_(),o.find("#save-btn").on("click",async()=>{const e=o.find("#page-url-path").val().trim();if(!e){E.toast("URL path is required.",{type:"warning"});return}const f=o.find("#field-dconfig").val().trim();let l=null;if(f)try{l=JSON.parse(f)}catch{E.toast("DConfig JSON is invalid. Please check the format before saving.",{type:"warning"});return}const c={title:o.find("#field-title").val().trim()||"Untitled",description:o.find("#field-description").val().trim(),layout:o.find("#field-layout").val(),status:o.find("#field-status").val(),sortOrder:parseInt(o.find("#field-sort-order").val(),10)||99,showInNav:o.find("#field-show-in-nav").is(":checked"),sidebar:o.find("#field-sidebar").is(":checked"),...o.find("#field-show-breadcrumbs").is(":checked")?{}:{breadcrumbs:!1},tags:G?G.getValue():[],category:o.find("#field-category").val().trim()||null,visibility:o.find("#field-visibility").val()||"public",seo:{title:o.find("#field-seo-title").val().trim(),description:o.find("#field-seo-desc").val().trim()},dconfig:l,...o.find("#field-theme").val()?{theme:o.find("#field-theme").val()}:{}};try{if(o.find("#save-btn").prop("disabled",!0).text("Saving\u2026"),C){const h={frontmatter:c,body:q.val()};e!==T&&(h.newUrlPath=e),await U.pages.update(T,h),E.toast("Page saved successfully.",{type:"success"}),de=!1,e!==T&&R.navigate(`/pages/edit${e}`)}else await U.pages.create({urlPath:e,frontmatter:c,body:q.val()}),E.toast("Page saved successfully.",{type:"success"}),de=!1,R.navigate("/pages")}catch(h){E.toast(`Save failed: ${h.message||"Unknown error"}`,{type:"error"})}finally{o.find("#save-btn").prop("disabled",!1).text("Save")}}),o.find("#cancel-btn").on("click",()=>R.navigate("/pages")),Domma.icons.scan()}};
72
+ `),te.scrollTop=j.scrollTop};pe(),j.addEventListener("input",pe),j.addEventListener("scroll",()=>{te.scrollTop=j.scrollTop});let Ce=null;const _=()=>{clearTimeout(Ce),Ce=setTimeout(async()=>{try{const{html:e}=await U.pages.preview(O.val());ee.html(e,{safe:!1}),I.scan(ee.get(0))}catch{window.marked&&ee.html(marked.parse(O.val()))}},400)},z=ye(O,o.find("#editor-toolbar"),{spacerDefault:ne});z.onLink(async e=>{const l=(await U.pages.list().catch(()=>[])).map(r=>({label:r.title||r.urlPath,value:r.urlPath})),p=document.createElement("div");p.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const h=document.createElement("label");h.className="form-label",h.textContent="URL";const s=document.createElement("input");s.type="text",s.className="form-input",s.placeholder="/about or https://example.com";const b=e.value.substring(e.selectionStart,e.selectionEnd);b&&b.startsWith("/")&&(s.value=b),p.appendChild(h),p.appendChild(s);const m=document.createElement("label");m.className="form-label",m.textContent="Link text";const u=document.createElement("input");u.type="text",u.className="form-input",u.placeholder="Display text",b&&!b.startsWith("/")&&(u.value=b),p.appendChild(m),p.appendChild(u);const w=document.createElement("label");w.className="form-label",w.style.cssText="display:flex;align-items:center;gap:.5rem;cursor:pointer;";const c=document.createElement("input");c.type="checkbox",c.style.cssText="width:1rem;height:1rem;cursor:pointer;",w.appendChild(c),w.appendChild(document.createTextNode("Display as button")),p.appendChild(w);const v=document.createElement("label");v.className="form-label",v.textContent="Button colour",v.style.display="none";const T=document.createElement("select");T.className="form-select",T.style.display="none",[["primary","Primary"],["secondary","Secondary"],["success","Success (Green)"],["danger","Danger (Red)"],["warning","Warning (Orange)"],["info","Info (Blue)"],["outline","Outline"],["outline-success","Outline Success"],["outline-danger","Outline Danger"],["ghost","Ghost"]].forEach(([r,k])=>{const D=document.createElement("option");D.value=r,D.textContent=k,T.appendChild(D)}),p.appendChild(v),p.appendChild(T),c.addEventListener("change",()=>{const r=c.checked;v.style.display=r?"":"none",T.style.display=r?"":"none"});const C=document.createElement("button");C.type="button",C.className="btn btn-primary",C.textContent="Insert Link",p.appendChild(C);const B=E.modal({title:"Insert Link",size:"sm"});B.element.appendChild(p),B.open(),requestAnimationFrame(()=>{E.autocomplete(s,{data:l,minChars:1,onSelect:r=>{s.value=r.value,u.value||(u.value=r.label)}}),s.focus()}),C.addEventListener("click",()=>{const r=s.value.trim(),k=u.value.trim();if(!r)return;B.close();const D=c.checked?`<a href="${r}" class="btn btn-${T.value}">${k||r}</a>`:`[${k||r}](${r})`,N=e.selectionStart,P=e.selectionEnd;e.value=e.value.substring(0,N)+D+e.value.substring(P),e.selectionStart=e.selectionEnd=N+D.length,e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),z.onImage(async e=>{const l=(await U.media.list().catch(()=>[])).filter(s=>/\.(png|jpe?g|gif|webp|svg)$/i.test(s.name)),p=document.createElement("div");if(p.className="media-picker-grid",l.length)l.forEach(s=>{const b=document.createElement("div");b.className="media-picker-item",b.dataset.url=s.url;const m=document.createElement("img");m.src=s.url,m.alt=s.name;const u=document.createElement("span");u.textContent=s.name,b.appendChild(m),b.appendChild(u),p.appendChild(b)});else{const s=document.createElement("p");s.className="text-muted p-3",s.textContent="No images uploaded yet.",p.appendChild(s)}const h=E.modal({title:"Insert Image",size:"lg"});h.element.appendChild(p),$(h.element).on("click",".media-picker-item",function(){const s=$(this).data("url"),b=$(this).find("span").text();he(e,`![${b}](${s})`),h.close(),O.get(0).dispatchEvent(new Event("input",{bubbles:!0}))}),h.open()}),z.onCollection(async e=>{const f=await U.collections.list().catch(()=>[]),l=document.createElement("div");l.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const p="display:block;font-size:.85em;font-weight:600;margin-bottom:.25rem;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",h="width:100%;padding:.4rem .6rem;background:var(--dm-input-bg,#1a1a1a);border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text,#eee);font-size:.9em;",s=document.createElement("div"),b=document.createElement("label");b.style.cssText=p,b.textContent="Collection";const m=document.createElement("select");if(m.style.cssText=h,f.length)f.forEach(A=>{const L=document.createElement("option");L.value=A.slug,L.textContent=A.title||A.slug,m.appendChild(L)});else{const A=document.createElement("option");A.value="",A.textContent="No collections found",m.appendChild(A)}s.appendChild(b),s.appendChild(m),l.appendChild(s);const u=document.createElement("div"),w=document.createElement("label");w.style.cssText=p,w.textContent="Display";const c=document.createElement("select");c.style.cssText=h,["table","cards","list"].forEach(A=>{const L=document.createElement("option");L.value=A,L.textContent=A.charAt(0).toUpperCase()+A.slice(1),c.appendChild(L)}),u.appendChild(w),u.appendChild(c),l.appendChild(u);const v=document.createElement("div");v.style.display="none";const T=document.createElement("label");T.style.cssText=p,T.textContent="Columns (2\u20134)";const g=document.createElement("input");g.type="number",g.min="2",g.max="4",g.value="3",g.style.cssText=h,v.appendChild(T),v.appendChild(g),l.appendChild(v);const C=document.createElement("div"),B=document.createElement("label");B.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.9em;cursor:pointer;";const r=document.createElement("input");r.type="checkbox",r.checked=!0,B.appendChild(r),B.appendChild(document.createTextNode("Enable search")),C.appendChild(B),l.appendChild(C);const k=document.createElement("div"),D=document.createElement("label");D.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.9em;cursor:pointer;";const N=document.createElement("input");N.type="checkbox",N.checked=!0,D.appendChild(N),D.appendChild(document.createTextNode("Sortable columns")),k.appendChild(D),l.appendChild(k);const P=document.createElement("div"),H=document.createElement("label");H.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.9em;cursor:pointer;";const W=document.createElement("input");W.type="checkbox",W.checked=!1,H.appendChild(W),H.appendChild(document.createTextNode("CSV export")),P.appendChild(H),l.appendChild(P);const F=document.createElement("div"),K=document.createElement("label");K.style.cssText=p,K.textContent="Rows per page";const q=document.createElement("input");q.type="number",q.min="5",q.max="100",q.value="25",q.style.cssText=h,F.appendChild(K),F.appendChild(q),l.appendChild(F);const me=document.createElement("div"),le=document.createElement("label");le.style.cssText=p,le.textContent="Limit (optional)";const G=document.createElement("input");G.type="number",G.placeholder="All",G.style.cssText=h,me.appendChild(le),me.appendChild(G),l.appendChild(me);const Y=document.createElement("button");Y.type="button",Y.className="btn btn-primary",Y.textContent="Insert",l.appendChild(Y);const oe=[C,k,P,F],ie=()=>{const A=c.value==="table";oe.forEach(L=>{L.style.display=A?"":"none"}),v.style.display=c.value==="cards"?"":"none"};c.addEventListener("change",ie),ie();const X=E.modal({title:"Insert Collection",size:"sm"});X.element.appendChild(l),X.open(),Y.addEventListener("click",()=>{const A=m.value;if(!A)return;const L=c.value;let i=`[collection slug="${A}" display="${L}"`;L==="cards"&&(i+=` columns="${g.value}"`),L==="table"&&!r.checked&&(i+=' search="false"'),L==="table"&&!N.checked&&(i+=' sortable="false"'),L==="table"&&W.checked&&(i+=' exportable="true"'),L==="table"&&q.value!=="25"&&(i+=` page-size="${q.value}"`);const d=G.value.trim();d&&(i+=` limit="${d}"`),i+=" /]",X.close(),he(e,i),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),z.onForm(async e=>{const f=await U.forms.list().catch(()=>[]),l=document.createElement("div");l.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const p="display:block;font-size:.85em;font-weight:600;margin-bottom:.25rem;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",h="width:100%;padding:.4rem .6rem;background:var(--dm-input-bg,#1a1a1a);border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text,#eee);font-size:.9em;",s=document.createElement("div"),b=document.createElement("label");b.style.cssText=p,b.textContent="Form";const m=document.createElement("select");if(m.style.cssText=h,f.length)f.forEach(c=>{const v=document.createElement("option");v.value=c.slug,v.textContent=c.title||c.slug,m.appendChild(v)});else{const c=document.createElement("option");c.value="",c.textContent="No forms found",m.appendChild(c)}s.appendChild(b),s.appendChild(m),l.appendChild(s);const u=document.createElement("button");u.className="btn btn-primary btn-sm",u.style.cssText="align-self:flex-end;margin-top:.5rem;",u.textContent="Insert Form",l.appendChild(u);const w=E.modal({title:"Insert Form",size:"sm"});w.element.appendChild(l),w.open(),u.addEventListener("click",()=>{const c=m.value;c&&(he(e,`[form slug="${c}" /]`),w.close(),O.get(0).dispatchEvent(new Event("input",{bubbles:!0})))})}),z.onView(async e=>{const f=await U.views.list().catch(()=>[]),l=document.createElement("div");l.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const p="display:block;font-size:.85em;font-weight:600;margin-bottom:.25rem;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",h="width:100%;padding:.4rem .6rem;background:var(--dm-input-bg,#1a1a1a);border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text,#eee);font-size:.9em;",s=document.createElement("div"),b=document.createElement("label");b.style.cssText=p,b.textContent="View";const m=document.createElement("select");if(m.style.cssText=h,f.length)f.forEach(g=>{const C=document.createElement("option");C.value=g.slug,C.textContent=g.title||g.slug,m.appendChild(C)});else{const g=document.createElement("option");g.value="",g.textContent="No Views configured yet \u2014 create one under Data \u2192 Views",m.appendChild(g)}s.appendChild(b),s.appendChild(m),l.appendChild(s);const u=document.createElement("div"),w=document.createElement("label");w.style.cssText=p,w.textContent="Display";const c=document.createElement("select");c.style.cssText=h,["table","cards","list"].forEach(g=>{const C=document.createElement("option");C.value=g,C.textContent=g.charAt(0).toUpperCase()+g.slice(1),c.appendChild(C)}),u.appendChild(w),u.appendChild(c),l.appendChild(u);const v=document.createElement("button");v.type="button",v.className="btn btn-primary",v.textContent="Insert",l.appendChild(v);const T=E.modal({title:"Insert View",size:"sm"});T.element.appendChild(l),T.open(),v.addEventListener("click",()=>{const g=m.value;if(!g)return;const C=`[view slug="${g}" display="${c.value}" /]`;T.close(),he(e,C),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),z.onCta(async e=>{const f=await U.actions.list().catch(()=>[]),l=document.createElement("div");l.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const p="display:block;font-size:.85em;font-weight:600;margin-bottom:.25rem;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",h="width:100%;padding:.4rem .6rem;background:var(--dm-input-bg,#1a1a1a);border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text,#eee);font-size:.9em;";function s(r,k){const D=document.createElement("div"),N=document.createElement("label");return N.style.cssText=p,N.textContent=r,D.appendChild(N),D.appendChild(k),D}function b(r){const k=document.createElement("select");return k.style.cssText=h,r.forEach(([D,N])=>{const P=document.createElement("option");P.value=D,P.textContent=N,k.appendChild(P)}),k}function m(r,k,D){const N=document.createElement("input");return N.type=r,N.placeholder=k||"",N.value=D||"",N.style.cssText=h,N}const u=b(f.length?f.map(r=>[r.slug,r.title||r.slug]):[["","No Actions configured yet \u2014 create one under Data \u2192 Actions"]]);l.appendChild(s("Action",u));const w=m("text","Button label","Run");if(l.appendChild(s("Label",w)),u.addEventListener("change",()=>{const r=f.find(k=>k.slug===u.value);r?.trigger?.label&&(w.value=r.trigger.label)}),f.length){const r=f[0];r?.trigger?.label&&(w.value=r.trigger.label)}const c=m("text","Paste entry UUID\u2026","");l.appendChild(s("Entry ID",c));const v=b([["primary","Primary"],["secondary","Secondary"],["ghost","Ghost"],["danger","Danger"]]);l.appendChild(s("Style",v));const T=m("text","e.g. check, zap, send (optional)","");l.appendChild(s("Icon",T));const g=m("text","Confirmation message (optional)","");l.appendChild(s("Confirm prompt",g));const C=document.createElement("button");C.type="button",C.className="btn btn-primary",C.textContent="Insert",l.appendChild(C);const B=E.modal({title:"Insert CTA Button",size:"sm"});B.element.appendChild(l),B.open(),C.addEventListener("click",()=>{const r=u.value;if(!r)return;const k=c.value.trim(),D=(w.value.trim()||"Run").replace(/\[\/cta\]/gi,""),N=v.value,P=T.value.trim().replace(/"/g,""),H=g.value.trim().replace(/"/g,""),W=k.replace(/"/g,"");let F=`action="${r}" style="${N}"`;W&&(F+=` entry="${W}"`),P&&(F+=` icon="${P}"`),H&&(F+=` confirm="${H}"`);const K=`[cta ${F}]${D}[/cta]`;B.close(),he(e,K),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),z.onHelp(()=>{xe()}),o.find(".editor-view-btn").on("click",function(){const e=$(this).data("mode");o.find(".editor-view-btn").removeClass("active"),$(this).addClass("active"),o.find("#editor-body").removeClass("editor-mode-split editor-mode-write editor-mode-preview").addClass(`editor-mode-${e}`)}),o.find("#fullscreen-btn").on("click",function(){o.find(".editor-card").toggleClass("editor-fullscreen")}),de=!1,fe&&window.removeEventListener("beforeunload",fe),fe=e=>{de&&e.preventDefault()},window.addEventListener("beforeunload",fe),ve||(ve=!0,R.use((e,f,l)=>{const p=window.location.hash.startsWith("#/pages/edit")||window.location.hash==="#/pages/new";if(!de||!p)return l();E.confirm("You have unsaved changes. Leave this page?").then(h=>{h&&(de=!1,l())})})),O.on("input",()=>{de=!0,_()}),_(),o.find("#save-btn").on("click",async()=>{const e=o.find("#page-url-path").val().trim();if(!e){E.toast("URL path is required.",{type:"warning"});return}const f=o.find("#field-dconfig").val().trim();let l=null;if(f)try{l=JSON.parse(f)}catch{E.toast("DConfig JSON is invalid. Please check the format before saving.",{type:"warning"});return}const p={title:o.find("#field-title").val().trim()||"Untitled",description:o.find("#field-description").val().trim(),layout:o.find("#field-layout").val(),status:o.find("#field-status").val(),sortOrder:parseInt(o.find("#field-sort-order").val(),10)||99,showInNav:o.find("#field-show-in-nav").is(":checked"),sidebar:o.find("#field-sidebar").is(":checked"),...o.find("#field-show-breadcrumbs").is(":checked")?{}:{breadcrumbs:!1},tags:V?V.getValue():[],category:o.find("#field-category").val().trim()||null,visibility:o.find("#field-visibility").val()||"public",seo:{title:o.find("#field-seo-title").val().trim(),description:o.find("#field-seo-desc").val().trim()},dconfig:l,...o.find("#field-theme").val()?{theme:o.find("#field-theme").val()}:{}};try{if(o.find("#save-btn").prop("disabled",!0).text("Saving\u2026"),y){const h={frontmatter:p,body:O.val()};e!==S&&(h.newUrlPath=e),await U.pages.update(S,h),E.toast("Page saved successfully.",{type:"success"}),de=!1,e!==S&&R.navigate(`/pages/edit${e}`)}else await U.pages.create({urlPath:e,frontmatter:p,body:O.val()}),E.toast("Page saved successfully.",{type:"success"}),de=!1,R.navigate("/pages")}catch(h){E.toast(`Save failed: ${h.message||"Unknown error"}`,{type:"error"})}finally{o.find("#save-btn").prop("disabled",!1).text("Save")}}),o.find("#cancel-btn").on("click",()=>R.navigate("/pages")),Domma.icons.scan()}};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-cms",
3
- "version": "0.6.1",
3
+ "version": "0.6.4",
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",
@@ -68,7 +68,7 @@
68
68
  "@fastify/rate-limit": "^10.3.0",
69
69
  "@fastify/static": "^8.1.0",
70
70
  "bcryptjs": "^3.0.3",
71
- "domma-js": "^0.19.5",
71
+ "domma-js": "^0.20.2",
72
72
  "dotenv": "^17.2.3",
73
73
  "fastify": "5.8.1",
74
74
  "gray-matter": "^4.0.3",
@@ -1,24 +1,24 @@
1
1
  {
2
- "/": 136,
2
+ "/": 138,
3
3
  "/about": 71,
4
4
  "/blog": 30,
5
5
  "/contact": 30,
6
6
  "/resources/typography": 4,
7
7
  "/resources": 13,
8
8
  "/resources/shortcodes": 14,
9
- "/resources/cards": 10,
9
+ "/resources/cards": 14,
10
10
  "/resources/interactive": 13,
11
- "/resources/grid": 5,
11
+ "/resources/grid": 6,
12
12
  "/forms": 14,
13
13
  "/resources/effects": 6,
14
14
  "/blog/hello-world": 20,
15
- "/feedback": 37,
15
+ "/feedback": 38,
16
16
  "/resources/dependencies": 2,
17
17
  "/resources/components": 6,
18
18
  "/gdpr": 3,
19
19
  "/scratch": 51,
20
20
  "/getting-started": 3,
21
21
  "/resources/pro": 1,
22
- "/todo": 21,
22
+ "/todo": 23,
23
23
  "/thank-you": 1
24
24
  }
@@ -61,6 +61,6 @@
61
61
  "slug": "contacts"
62
62
  }
63
63
  },
64
- "createdAt": "2026-03-13T14:07:25.959Z",
65
- "updatedAt": "2026-03-13T14:07:25.959Z"
64
+ "createdAt": "2026-03-17T12:35:44.569Z",
65
+ "updatedAt": "2026-03-17T12:35:44.569Z"
66
66
  }
@@ -98,6 +98,6 @@
98
98
  "slug": "enquiries"
99
99
  }
100
100
  },
101
- "createdAt": "2026-03-13T14:07:25.959Z",
102
- "updatedAt": "2026-03-13T14:07:25.959Z"
101
+ "createdAt": "2026-03-17T12:35:44.569Z",
102
+ "updatedAt": "2026-03-17T12:35:44.569Z"
103
103
  }
@@ -126,6 +126,6 @@
126
126
  "slug": "feedback"
127
127
  }
128
128
  },
129
- "createdAt": "2026-03-13T14:07:25.959Z",
130
- "updatedAt": "2026-03-13T14:07:25.959Z"
129
+ "createdAt": "2026-03-17T12:35:44.569Z",
130
+ "updatedAt": "2026-03-17T12:35:44.569Z"
131
131
  }
@@ -74,6 +74,6 @@
74
74
  "slug": "notes"
75
75
  }
76
76
  },
77
- "createdAt": "2026-03-13T14:07:25.959Z",
78
- "updatedAt": "2026-03-13T14:07:25.959Z"
77
+ "createdAt": "2026-03-17T12:35:44.569Z",
78
+ "updatedAt": "2026-03-17T12:35:44.569Z"
79
79
  }
@@ -95,6 +95,6 @@
95
95
  "slug": "to-do"
96
96
  }
97
97
  },
98
- "createdAt": "2026-03-13T14:07:25.959Z",
99
- "updatedAt": "2026-03-13T14:07:25.959Z"
98
+ "createdAt": "2026-03-17T12:35:44.569Z",
99
+ "updatedAt": "2026-03-17T12:35:44.569Z"
100
100
  }
package/public/js/site.js CHANGED
@@ -1 +1 @@
1
- $(()=>{const y=window.__CMS_NAV__||{},r=window.__CMS_SITE__||{};if(r.autoTheme?.enabled){let e=function(n){const u=(n||"07:00").split(":");return+u[0]*60+(+u[1]||0)},o=function(){const n=new Date,u=n.getHours()*60+n.getMinutes();return u>=e(t.dayStart)&&u<e(t.nightStart)?t.dayTheme:t.nightTheme};var s=e,l=o;const t=r.autoTheme;Domma.theme.set(o()),setInterval(()=>Domma.theme.set(o()),6e4)}if($("#site-navbar").length&&y.brand){const t={...y.brand};if(t.icon){let e=`<span data-icon="${t.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`;t.text&&(e+=`<span class="navbar-brand-text">${t.text}</span>`),t.html=e}Domma.elements.navbar("#site-navbar",{brand:t,items:y.items||[],variant:y.variant||"dark",position:y.position||"sticky",collapsible:!0}),Domma.icons.scan("#site-navbar")}const v=$("#site-footer");if(v.length){const t=r.social||{},e={twitter:{label:"X / Twitter",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.742l7.73-8.835L1.254 2.25H8.08l4.259 5.629L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'},facebook:{label:"Facebook",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'},instagram:{label:"Instagram",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>'},linkedin:{label:"LinkedIn",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'},github:{label:"GitHub",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'},youtube:{label:"YouTube",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z"/></svg>'}};let o='<div class="footer-inner container">';if(r.footer){const h=r.footer;o+=`<p>${h.copyright||""}</p>`,h.links?.length&&(o+='<nav class="footer-links">',h.links.forEach(i=>{o+=`<a href="${i.url}">${i.text}</a>`}),o+="</nav>");const f=Object.keys(e).filter(i=>t[i]);f.length&&(o+='<div class="footer-social">',f.forEach(i=>{const{label:d,svg:a}=e[i];o+=`<a href="${t[i]}" target="_blank" rel="noopener noreferrer" aria-label="${d}" class="footer-social-link">${a}</a>`}),o+="</div>")}const n=S.get("reduced_motion"),u=n!==null?!!n:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches);o+=`<label class="form-switch footer-motion-switch" title="Reduce motion"><input type="checkbox" class="form-switch-input" id="dm-motion-switch"${u?" checked":""}><span class="form-switch-label">Reduce motion</span></label>`,o+="</div>",v.html(o,{safe:!1}),document.getElementById("dm-motion-switch").addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()})}$("#site-sidebar").length&&Domma.elements.sidebar("#site-sidebar",{autoGenerate:!0,selector:"h2, h3",collapsible:!1,push:!0,contentSelector:".site-content"}),Domma.icons.scan();const c=$(".page-body");if(c.length&&(c.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),c.find(".tabs").each(function(){Domma.elements.tabs(this)}),c.find(".carousel").each(function(){Domma.elements.carousel(this,{autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"})}),c.find(".dm-countdown").each(function(){const t={autoStart:!0};this.dataset.to&&(t.targetDate=new Date(this.dataset.to)),this.dataset.duration&&(t.duration=parseInt(this.dataset.duration,10)),this.dataset.format&&(t.format=this.dataset.format),Domma.elements.timer(this,t)}),c.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),c.find(".dm-progression").each(function(){Domma.elements.progression(this,{layout:this.dataset.layout||"vertical",theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0})}),c.find(".card[data-collapsible]").each(function(){const t=this.querySelector(".card-header");t&&t.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),c.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const t=this.dataset.soTarget,e=document.getElementById(t);if(!e)return;const o=E.slideover({title:e.dataset.soTitle||"",size:e.dataset.soSize||"md",position:e.dataset.soPosition||"right"});e.style.display="",o.setContent(e),o.open()})})),typeof $.setup=="function"){const t=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(e=>{try{const o=atob(e.dataset.config),n=JSON.parse(o);Object.assign(t,n)}catch{}}),Object.keys(t).length>0){const e={};for(const[o,n]of Object.entries(t)){const u=n?.events?.click,{confirm:h,toast:f,alert:i,prompt:d,...a}=u||{};h||f||i||d?($(o).on("click",async function(p){if(p.preventDefault(),h&&!await E.confirm(h))return;let C=null;if(!(d&&(C=await E.prompt(d,{inputPlaceholder:a.promptPlaceholder||"",inputValue:a.promptDefault||""}),C===null))){if(a.target){const w=$(a.target);a.toggleClass&&w.toggleClass(a.toggleClass),a.addClass&&w.addClass(a.addClass),a.removeClass&&w.removeClass(a.removeClass),C!==null&&(a.setText&&w.text(C),a.setVal&&w.val(C),a.setAttr&&w.attr(a.setAttr,C))}a.href&&(window.location.href=a.href),f&&E.toast(f,{type:a.toastType||"success"}),i&&E.alert(i)}}),Object.keys(a).length&&(e[o]={...n,events:{...n.events,click:a}})):e[o]=n}$.setup(e)}}c.length&&wireCTAButtons(c.get(0))});function wireCTAButtons(y){y.querySelectorAll(".dm-cta-trigger").forEach(r=>{r.addEventListener("click",async()=>{const b=r.dataset.action,v=r.dataset.entry,g=r.dataset.confirm;let c=S.get("auth_token");if(!c){E.toast("Please log in to perform this action.",{type:"warning"});return}if(g&&!await E.confirm(g))return;const s=Array.from(r.childNodes).map(t=>t.cloneNode(!0));r.disabled=!0,r.textContent="Running\u2026";const l=t=>fetch(`/api/actions/${b}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify({entryId:v})});try{let t=await l(c);if(t.status===401){const o=S.get("auth_refresh_token");if(o){const n=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:o})});if(n.ok){const{token:u}=await n.json();S.set("auth_token",u),c=u,t=await l(c)}}}const e=await t.json().catch(()=>({}));if(!t.ok)throw new Error(e.error||e.message||`Error ${t.status}`);E.toast(e.message||"Action completed.",{type:"success"})}catch(t){E.toast(t.message||"Action failed.",{type:"error"})}finally{r.disabled=!1,r.textContent="",s.forEach(t=>r.appendChild(t)),Domma.icons.scan(r)}})})}(function(){const r=document.querySelectorAll("[data-collection-table]");!r.length||typeof T>"u"||!T.create||r.forEach(b=>{let v;try{v=JSON.parse(atob(b.dataset.payload))}catch{return}const{columns:g,rows:c,search:s,sortable:l,exportable:t,pageSize:e,empty:o,ctaConfig:n}=v;if(!g?.length)return;if(n){const f=g.findIndex(i=>i.key==="_cta");f!==-1&&(g[f]={key:"_cta",title:"",render:(i,d)=>{const a=document.createElement("button");if(a.className=`btn btn-${n.style||"primary"} dm-cta-trigger`,a.dataset.action=n.action||"",a.dataset.entry=d._entryId||"",n.confirm&&(a.dataset.confirm=n.confirm),n.icon){const m=document.createElement("span");m.dataset.icon=n.icon,a.appendChild(m),a.appendChild(document.createTextNode(" "))}return a.appendChild(document.createTextNode(n.label||"Run")),a}})}const u="col-table-"+Math.random().toString(36).slice(2,7),h=document.createElement("div");h.id=u,b.replaceChildren(h),T.create("#"+u,{data:c,columns:g,search:s,sortable:l,exportable:t,pageSize:e,emptyMessage:o}),n&&wireCTAButtons(h)})})(),(function(){const r=document.querySelectorAll("[data-form-inline]");if(!r.length||typeof F>"u")return;function b(s,l){const t={};return(s||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const o=e.type==="checkbox"?"boolean":e.type==="date"?"string":e.type,n={...e.formConfig||{}};n.span==="full"&&l&&(n.span=l),t[e.name]={type:o,label:e.label,required:e.required,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{hint:e.helper},...n}}}),t}function v(s){const l={};return(s||[]).forEach(t=>{if(!(!t.name||t.type==="page-break"||t.type==="spacer")&&(t.type==="select"||t.type==="multiselect")&&t.required){const e=(t.options||[])[0];e&&(l[t.name]=typeof e=="object"?e.value:e)}}),l}function g(s,l){(l||[]).forEach(t=>{if(t.type!=="date"||!t.name)return;const e=s.querySelector(`[name="${t.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function c(s,l,t){let e=s.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",s.appendChild(e)),e.textContent=l,e.style.cssText=t?"color:var(--danger,#f87171);margin-top:.75rem;":"color:var(--success,#4ade80);margin-top:.75rem;"}r.forEach(s=>{let l;try{l=JSON.parse(atob(s.dataset.formInline))}catch{return}const t=l.fields||[],e=l.settings||{},o=e.columns||1,n=e.layout||"stacked",u=t.some(i=>i.type==="page-break"),h=async i=>{try{const d=s.querySelector('[name="website"]'),a=s.querySelector('[name="_t"]'),m=Object.assign({},i);d!==null&&(m._hp=d.value),a!==null&&(m._t=a.value);const p=await H.post(`/api/forms/submit/${l.slug}`,m);if(p?.redirect){window.location.href=p.redirect;return}for(;s.firstChild;)s.removeChild(s.firstChild);c(s,p?.message||e.successMessage||"Thank you for your submission.",!1)}catch(d){throw c(s,d.message||"Submission failed. Please try again.",!0),d}};function f(i){const d=i.querySelector("form");if(!d)return;const a=document.createElement("div");a.className="fb-form-honeypot",a.setAttribute("aria-hidden","true");const m=document.createElement("input");m.name="website",m.type="text",m.tabIndex=-1,m.autocomplete="url",m.placeholder="https://",a.appendChild(m);const p=document.createElement("input");p.name="_t",p.type="hidden",p.value=Date.now(),a.appendChild(p),d.appendChild(a)}if(u&&F.wizard){const i=[];let d=[],a=l.title||"Step 1",m="";t.forEach(p=>{p.type==="page-break"?(i.push({title:a,description:m,fields:b(d,o)}),d=[],a=p.label||`Step ${i.length+1}`,m=p.description||""):p.type!=="spacer"&&d.push(p)}),i.push({title:a,description:m,fields:b(d,o)}),F.wizard(s,{schema:{steps:i},onSubmit:h}),g(s,t),e.honeypot!==!1&&f(s)}else if(F.render){if(F.render(s,b(t,o),v(t),{submitText:e.submitText||"Submit",layout:n,columns:o,onSubmit:h}),n==="grid"&&e.submitSpan==="full"){const i=s.querySelector(".form-buttons");i&&i.classList.add("col-span-full")}g(s,t),e.honeypot!==!1&&f(s)}})})();
1
+ $(()=>{const y=window.__CMS_NAV__||{},r=window.__CMS_SITE__||{};if(r.autoTheme?.enabled){let e=function(n){const u=(n||"07:00").split(":");return+u[0]*60+(+u[1]||0)},o=function(){const n=new Date,u=n.getHours()*60+n.getMinutes();return u>=e(t.dayStart)&&u<e(t.nightStart)?t.dayTheme:t.nightTheme};var s=e,l=o;const t=r.autoTheme;Domma.theme.set(o()),setInterval(()=>Domma.theme.set(o()),6e4)}if($("#site-navbar").length&&y.brand){const t={...y.brand};if(t.icon){let e=`<span data-icon="${t.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`;t.text&&(e+=`<span class="navbar-brand-text">${t.text}</span>`),t.html=e}Domma.elements.navbar("#site-navbar",{brand:t,items:y.items||[],variant:y.variant||"dark",position:y.position||"sticky",collapsible:!0}),Domma.icons.scan("#site-navbar")}const v=$("#site-footer");if(v.length){const t=r.social||{},e={twitter:{label:"X / Twitter",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.742l7.73-8.835L1.254 2.25H8.08l4.259 5.629L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'},facebook:{label:"Facebook",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'},instagram:{label:"Instagram",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>'},linkedin:{label:"LinkedIn",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'},github:{label:"GitHub",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'},youtube:{label:"YouTube",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z"/></svg>'}};let o='<div class="footer-inner container">';if(r.footer){const h=r.footer;o+=`<p>${h.copyright||""}</p>`,h.links?.length&&(o+='<nav class="footer-links">',h.links.forEach(i=>{o+=`<a href="${i.url}">${i.text}</a>`}),o+="</nav>");const f=Object.keys(e).filter(i=>t[i]);f.length&&(o+='<div class="footer-social">',f.forEach(i=>{const{label:d,svg:a}=e[i];o+=`<a href="${t[i]}" target="_blank" rel="noopener noreferrer" aria-label="${d}" class="footer-social-link">${a}</a>`}),o+="</div>")}const n=S.get("reduced_motion"),u=n!==null?!!n:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches);o+=`<label class="form-switch footer-motion-switch" title="Reduce motion"><input type="checkbox" class="form-switch-input" id="dm-motion-switch"${u?" checked":""}><span class="form-switch-label">Reduce motion</span></label>`,o+="</div>",v.html(o,{safe:!1}),document.getElementById("dm-motion-switch").addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()})}$("#site-sidebar").length&&Domma.elements.sidebar("#site-sidebar",{autoGenerate:!0,selector:"h2, h3",collapsible:!1,push:!0,contentSelector:".site-content"}),Domma.icons.scan();const c=$(".page-body");if(c.length&&(c.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),c.find(".tabs").each(function(){Domma.elements.tabs(this)}),c.find(".carousel").each(function(){Domma.elements.carousel(this,{autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"})}),c.find(".dm-countdown").each(function(){const t={autoStart:!0};this.dataset.to&&(t.targetDate=new Date(this.dataset.to)),this.dataset.duration&&(t.duration=parseInt(this.dataset.duration,10)),this.dataset.format&&(t.format=this.dataset.format),Domma.elements.timer(this,t)}),c.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),c.find(".dm-progression").each(function(){Domma.elements.progression(this,{layout:this.dataset.layout||"vertical",theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0})}),Domma.effects.reveal(".page-body .hero",{animation:"slide-up",duration:480,threshold:.06,stagger:60,once:!1}),c.find(".card[data-collapsible]").each(function(){const t=this.querySelector(".card-header");t&&t.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),c.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const t=this.dataset.soTarget,e=document.getElementById(t);if(!e)return;const o=E.slideover({title:e.dataset.soTitle||"",size:e.dataset.soSize||"md",position:e.dataset.soPosition||"right"});e.style.display="",o.setContent(e),o.open()})})),typeof $.setup=="function"){const t=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(e=>{try{const o=atob(e.dataset.config),n=JSON.parse(o);Object.assign(t,n)}catch{}}),Object.keys(t).length>0){const e={};for(const[o,n]of Object.entries(t)){const u=n?.events?.click,{confirm:h,toast:f,alert:i,prompt:d,...a}=u||{};h||f||i||d?($(o).on("click",async function(p){if(p.preventDefault(),h&&!await E.confirm(h))return;let C=null;if(!(d&&(C=await E.prompt(d,{inputPlaceholder:a.promptPlaceholder||"",inputValue:a.promptDefault||""}),C===null))){if(a.target){const w=$(a.target);a.toggleClass&&w.toggleClass(a.toggleClass),a.addClass&&w.addClass(a.addClass),a.removeClass&&w.removeClass(a.removeClass),C!==null&&(a.setText&&w.text(C),a.setVal&&w.val(C),a.setAttr&&w.attr(a.setAttr,C))}a.href&&(window.location.href=a.href),f&&E.toast(f,{type:a.toastType||"success"}),i&&E.alert(i)}}),Object.keys(a).length&&(e[o]={...n,events:{...n.events,click:a}})):e[o]=n}$.setup(e)}}c.length&&wireCTAButtons(c.get(0))});function wireCTAButtons(y){y.querySelectorAll(".dm-cta-trigger").forEach(r=>{r.addEventListener("click",async()=>{const b=r.dataset.action,v=r.dataset.entry,g=r.dataset.confirm;let c=S.get("auth_token");if(!c){E.toast("Please log in to perform this action.",{type:"warning"});return}if(g&&!await E.confirm(g))return;const s=Array.from(r.childNodes).map(t=>t.cloneNode(!0));r.disabled=!0,r.textContent="Running\u2026";const l=t=>fetch(`/api/actions/${b}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify({entryId:v})});try{let t=await l(c);if(t.status===401){const o=S.get("auth_refresh_token");if(o){const n=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:o})});if(n.ok){const{token:u}=await n.json();S.set("auth_token",u),c=u,t=await l(c)}}}const e=await t.json().catch(()=>({}));if(!t.ok)throw new Error(e.error||e.message||`Error ${t.status}`);E.toast(e.message||"Action completed.",{type:"success"})}catch(t){E.toast(t.message||"Action failed.",{type:"error"})}finally{r.disabled=!1,r.textContent="",s.forEach(t=>r.appendChild(t)),Domma.icons.scan(r)}})})}(function(){const r=document.querySelectorAll("[data-collection-table]");!r.length||typeof T>"u"||!T.create||r.forEach(b=>{let v;try{v=JSON.parse(atob(b.dataset.payload))}catch{return}const{columns:g,rows:c,search:s,sortable:l,exportable:t,pageSize:e,empty:o,ctaConfig:n}=v;if(!g?.length)return;if(n){const f=g.findIndex(i=>i.key==="_cta");f!==-1&&(g[f]={key:"_cta",title:"",render:(i,d)=>{const a=document.createElement("button");if(a.className=`btn btn-${n.style||"primary"} dm-cta-trigger`,a.dataset.action=n.action||"",a.dataset.entry=d._entryId||"",n.confirm&&(a.dataset.confirm=n.confirm),n.icon){const m=document.createElement("span");m.dataset.icon=n.icon,a.appendChild(m),a.appendChild(document.createTextNode(" "))}return a.appendChild(document.createTextNode(n.label||"Run")),a}})}const u="col-table-"+Math.random().toString(36).slice(2,7),h=document.createElement("div");h.id=u,b.replaceChildren(h),T.create("#"+u,{data:c,columns:g,search:s,sortable:l,exportable:t,pageSize:e,emptyMessage:o}),n&&wireCTAButtons(h)})})(),(function(){const r=document.querySelectorAll("[data-form-inline]");if(!r.length||typeof F>"u")return;function b(s,l){const t={};return(s||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const o=e.type==="checkbox"?"boolean":e.type==="date"?"string":e.type,n={...e.formConfig||{}};n.span==="full"&&l&&(n.span=l),t[e.name]={type:o,label:e.label,required:e.required,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{hint:e.helper},...n}}}),t}function v(s){const l={};return(s||[]).forEach(t=>{if(!(!t.name||t.type==="page-break"||t.type==="spacer")&&(t.type==="select"||t.type==="multiselect")&&t.required){const e=(t.options||[])[0];e&&(l[t.name]=typeof e=="object"?e.value:e)}}),l}function g(s,l){(l||[]).forEach(t=>{if(t.type!=="date"||!t.name)return;const e=s.querySelector(`[name="${t.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function c(s,l,t){let e=s.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",s.appendChild(e)),e.textContent=l,e.style.cssText=t?"color:var(--danger,#f87171);margin-top:.75rem;":"color:var(--success,#4ade80);margin-top:.75rem;"}r.forEach(s=>{let l;try{l=JSON.parse(atob(s.dataset.formInline))}catch{return}const t=l.fields||[],e=l.settings||{},o=e.columns||1,n=e.layout||"stacked",u=t.some(i=>i.type==="page-break"),h=async i=>{try{const d=s.querySelector('[name="website"]'),a=s.querySelector('[name="_t"]'),m=Object.assign({},i);d!==null&&(m._hp=d.value),a!==null&&(m._t=a.value);const p=await H.post(`/api/forms/submit/${l.slug}`,m);if(p?.redirect){window.location.href=p.redirect;return}for(;s.firstChild;)s.removeChild(s.firstChild);c(s,p?.message||e.successMessage||"Thank you for your submission.",!1)}catch(d){throw c(s,d.message||"Submission failed. Please try again.",!0),d}};function f(i){const d=i.querySelector("form");if(!d)return;const a=document.createElement("div");a.className="fb-form-honeypot",a.setAttribute("aria-hidden","true");const m=document.createElement("input");m.name="website",m.type="text",m.tabIndex=-1,m.autocomplete="url",m.placeholder="https://",a.appendChild(m);const p=document.createElement("input");p.name="_t",p.type="hidden",p.value=Date.now(),a.appendChild(p),d.appendChild(a)}if(u&&F.wizard){const i=[];let d=[],a=l.title||"Step 1",m="";t.forEach(p=>{p.type==="page-break"?(i.push({title:a,description:m,fields:b(d,o)}),d=[],a=p.label||`Step ${i.length+1}`,m=p.description||""):p.type!=="spacer"&&d.push(p)}),i.push({title:a,description:m,fields:b(d,o)}),F.wizard(s,{schema:{steps:i},onSubmit:h}),g(s,t),e.honeypot!==!1&&f(s)}else if(F.render){if(F.render(s,b(t,o),v(t),{submitText:e.submitText||"Submit",layout:n,columns:o,onSubmit:h}),n==="grid"&&e.submitSpan==="full"){const i=s.querySelector(".form-buttons");i&&i.classList.add("col-span-full")}g(s,t),e.honeypot!==!1&&f(s)}})})();
package/scripts/seed.js CHANGED
@@ -1197,6 +1197,277 @@ See the [Shortcode Reference](/resources/shortcodes) for full documentation.
1197
1197
  ---
1198
1198
 
1199
1199
  ← [Back to Resources](/resources)
1200
+ `
1201
+ },
1202
+ {
1203
+ file: 'privacy.md',
1204
+ fm: {
1205
+ title: 'Privacy Policy', slug: 'privacy',
1206
+ description: 'How we collect, use, and protect your personal data',
1207
+ layout: 'default', status: 'published',
1208
+ sortOrder: 90, showInNav: false, sidebar: false,
1209
+ seo: {title: 'Privacy Policy', description: 'How we collect, use, and protect your personal data'},
1210
+ createdAt: now, updatedAt: now, visibility: 'public'
1211
+ },
1212
+ body: `[hero variant="dark" size="sm" fullwidth="true" twinkle twinkle-count="20" blobs]
1213
+
1214
+ # Privacy Policy
1215
+
1216
+ How [Your Organisation] collects, uses, and protects your personal data.
1217
+
1218
+ [/hero]
1219
+
1220
+ **Last updated: ${new Date().toLocaleDateString('en-GB', {day: 'numeric', month: 'long', year: 'numeric'})}**
1221
+
1222
+ [Your Organisation] ("we", "us", "our") is committed to protecting and respecting your privacy. This policy explains
1223
+ what personal data we collect, how we use it, and your rights under applicable data protection law, including the UK
1224
+ GDPR.
1225
+
1226
+ ---
1227
+
1228
+ ## 1. Who We Are
1229
+
1230
+ **Data Controller:** [Your Organisation]
1231
+
1232
+ If you have any questions about this policy or how we handle your data, please [contact us](/contact).
1233
+
1234
+ ---
1235
+
1236
+ ## 2. What Data We Collect
1237
+
1238
+ We may collect and process the following categories of personal data:
1239
+
1240
+ **Information you provide directly:**
1241
+
1242
+ - Name and contact details (e.g. email address) when you submit a contact or feedback form
1243
+ - Any other information you voluntarily provide through the site
1244
+
1245
+ **Information collected automatically:**
1246
+
1247
+ - Server access logs (IP address, browser type, pages visited, timestamps) for security and performance monitoring
1248
+ - Session data stored in your browser (e.g. theme preferences)
1249
+
1250
+ We do not use third-party analytics services or advertising trackers on this site.
1251
+
1252
+ ---
1253
+
1254
+ ## 3. How We Use Your Data
1255
+
1256
+ We use the personal data we collect for the following purposes:
1257
+
1258
+ | Purpose | Legal Basis |
1259
+ |----------------------------------------------------------|----------------------|
1260
+ | Responding to enquiries submitted via our contact form | Legitimate interests |
1261
+ | Monitoring site security and diagnosing technical issues | Legitimate interests |
1262
+ | Complying with legal obligations | Legal obligation |
1263
+
1264
+ We will never sell, rent, or share your personal data with third parties for marketing purposes.
1265
+
1266
+ ---
1267
+
1268
+ ## 4. How Long We Keep Your Data
1269
+
1270
+ We retain personal data only for as long as necessary for the purposes described above:
1271
+
1272
+ - **Contact form submissions:** Up to 12 months from the date of receipt, unless an ongoing relationship requires longer
1273
+ retention
1274
+ - **Server access logs:** Up to 30 days, then automatically purged
1275
+
1276
+ ---
1277
+
1278
+ ## 5. Your Rights
1279
+
1280
+ Under the UK GDPR, you have the following rights regarding your personal data:
1281
+
1282
+ - **Right of access** — request a copy of the data we hold about you
1283
+ - **Right to rectification** — ask us to correct inaccurate data
1284
+ - **Right to erasure** — ask us to delete your data where we have no lawful basis to retain it
1285
+ - **Right to restrict processing** — ask us to pause processing while a dispute is resolved
1286
+ - **Right to data portability** — receive your data in a structured, machine-readable format
1287
+ - **Right to object** — object to processing based on legitimate interests
1288
+
1289
+ To exercise any of these rights, please [contact us](/contact). We will respond within one calendar month.
1290
+
1291
+ If you are not satisfied with our response, you have the right to lodge a complaint with the **Information
1292
+ Commissioner's Office (ICO)**: [ico.org.uk](https://ico.org.uk) | 0303 123 1113.
1293
+
1294
+ ---
1295
+
1296
+ ## 6. Cookies
1297
+
1298
+ This site uses only essential cookies necessary for its operation (e.g. storing your preferred theme). No tracking or
1299
+ advertising cookies are used. See our [Cookie Policy](/cookies) for full details.
1300
+
1301
+ ---
1302
+
1303
+ ## 7. Security
1304
+
1305
+ We take appropriate technical and organisational measures to protect your personal data against unauthorised access,
1306
+ disclosure, alteration, or destruction.
1307
+
1308
+ ---
1309
+
1310
+ ## 8. Changes to This Policy
1311
+
1312
+ We may update this policy from time to time. The "Last updated" date at the top of this page will always reflect the
1313
+ most recent revision.
1314
+
1315
+ ---
1316
+
1317
+ ## 9. Contact Us
1318
+
1319
+ For any privacy-related enquiries, please use our [contact page](/contact).
1320
+ `
1321
+ },
1322
+ {
1323
+ file: 'cookies.md',
1324
+ fm: {
1325
+ title: 'Cookie Policy', slug: 'cookies',
1326
+ description: 'Our approach to cookies, tracking, and your rights under UK GDPR',
1327
+ layout: 'default', status: 'published',
1328
+ sortOrder: 91, showInNav: false, sidebar: false,
1329
+ seo: {
1330
+ title: 'Cookie Policy',
1331
+ description: 'Our approach to cookies, tracking, and your rights under UK GDPR'
1332
+ },
1333
+ createdAt: now, updatedAt: now, visibility: 'public'
1334
+ },
1335
+ body: `[hero variant="dark" size="sm" fullwidth="true" twinkle twinkle-count="20" blobs]
1336
+
1337
+ # Cookie Policy
1338
+
1339
+ Your rights and our obligations under UK GDPR & PECR.
1340
+
1341
+ [/hero]
1342
+
1343
+ **Last updated: ${new Date().toLocaleDateString('en-GB', {day: 'numeric', month: 'long', year: 'numeric'})}**
1344
+
1345
+ This page explains how [Your Organisation] handles cookies and your rights under the UK General Data Protection
1346
+ Regulation (UK GDPR) and the Privacy and Electronic Communications Regulations (PECR).
1347
+
1348
+ ---
1349
+
1350
+ ## 1. What Are Cookies?
1351
+
1352
+ Cookies are small text files placed on your device by a website. They allow the site to remember your preferences and
1353
+ settings between visits.
1354
+
1355
+ ---
1356
+
1357
+ ## 2. Cookies We Use
1358
+
1359
+ This site uses a minimal, privacy-first approach to cookies. We do **not** use advertising, tracking, or profiling
1360
+ cookies of any kind.
1361
+
1362
+ | Cookie | Type | Purpose | Duration |
1363
+ |-------------|------------|---------------------------------------|------------------------|
1364
+ | \`dm_theme\` | Functional | Stores your preferred colour theme | Session / localStorage |
1365
+ | \`dm_motion\` | Functional | Stores your reduced-motion preference | Session / localStorage |
1366
+
1367
+ **Note:** Theme and motion preferences are stored in your browser's \`localStorage\`, not as HTTP cookies. They never
1368
+ leave your device and are not transmitted to our servers.
1369
+
1370
+ We do not embed third-party content (e.g. social media widgets, YouTube iframes) that would set third-party cookies
1371
+ without your consent.
1372
+
1373
+ ---
1374
+
1375
+ ## 3. Your Cookie Choices
1376
+
1377
+ Because we only use functional, non-tracking storage, we do not require a cookie consent banner under PECR.
1378
+
1379
+ You can clear locally stored preferences at any time through your browser settings:
1380
+
1381
+ - **Chrome / Edge:** Settings → Privacy and security → Clear browsing data → Cookies and other site data
1382
+ - **Firefox:** Settings → Privacy & Security → Cookies and Site Data → Clear Data
1383
+ - **Safari:** Settings → Safari → Clear History and Website Data
1384
+
1385
+ Clearing these will reset your theme and motion preferences to the site defaults.
1386
+
1387
+ ---
1388
+
1389
+ ## 4. Your Rights Under UK GDPR
1390
+
1391
+ As a UK resident (or EU resident accessing our site), you have rights under UK GDPR:
1392
+
1393
+ ### Right to Be Informed
1394
+
1395
+ We tell you how we use your data through this policy and our [Privacy Policy](/privacy).
1396
+
1397
+ ### Right of Access
1398
+
1399
+ You can request a copy of any personal data we hold about you by [contacting us](/contact).
1400
+
1401
+ ### Right to Erasure ("Right to be Forgotten")
1402
+
1403
+ Where we hold personal data about you and have no overriding legal reason to retain it, you can ask us to delete it.
1404
+
1405
+ ### Right to Restrict Processing
1406
+
1407
+ If you dispute the accuracy of your data or our right to process it, you can ask us to pause processing while we
1408
+ investigate.
1409
+
1410
+ ### Right to Object
1411
+
1412
+ You can object to processing of your personal data where we rely on legitimate interests as the legal basis.
1413
+
1414
+ ### Right to Data Portability
1415
+
1416
+ Where processing is based on your consent or a contract, you can receive your data in a portable format.
1417
+
1418
+ ### How to Exercise Your Rights
1419
+
1420
+ Submit a request via our [contact page](/contact). We will acknowledge your request within 72 hours and respond fully
1421
+ within one calendar month.
1422
+
1423
+ ---
1424
+
1425
+ ## 5. Lawful Bases for Processing
1426
+
1427
+ We only process personal data where we have a valid lawful basis. The primary bases we rely on are:
1428
+
1429
+ - **Legitimate interests** — operating a secure, functional website and responding to enquiries
1430
+ - **Legal obligation** — where required by UK law
1431
+
1432
+ We do not rely on consent for any processing on this site, meaning you do not need to opt in or out for the site to
1433
+ function normally.
1434
+
1435
+ ---
1436
+
1437
+ ## 6. International Transfers
1438
+
1439
+ All data processed by this site is stored and processed within the UK and/or EEA. We do not transfer personal data to
1440
+ countries outside these territories.
1441
+
1442
+ ---
1443
+
1444
+ ## 7. Data Retention
1445
+
1446
+ We retain personal data only as long as necessary. See our [Privacy Policy](/privacy) for specific retention periods.
1447
+
1448
+ ---
1449
+
1450
+ ## 8. The ICO
1451
+
1452
+ The UK supervisory authority for data protection is the **Information Commissioner's Office (ICO)**:
1453
+
1454
+ - Website: [ico.org.uk](https://ico.org.uk)
1455
+ - Phone: 0303 123 1113
1456
+
1457
+ You have the right to lodge a complaint with the ICO at any time if you believe we have not handled your data lawfully.
1458
+
1459
+ ---
1460
+
1461
+ ## 9. Changes to This Policy
1462
+
1463
+ We will update this page whenever our practices change. The "Last updated" date at the top reflects the current
1464
+ revision.
1465
+
1466
+ ---
1467
+
1468
+ ## 10. Contact
1469
+
1470
+ Privacy queries: use our [contact page](/contact).
1200
1471
  `
1201
1472
  }
1202
1473
  ];
@@ -550,9 +550,30 @@ function processCardBlocks(markdown) {
550
550
 
551
551
  const iconLayout = (attrs['icon-layout'] || 'inline').trim(); // 'inline' | 'stacked'
552
552
 
553
- // Header only rendered when at least title or icon is set
553
+ // Extract [header]...[/header] and [footer]...[/footer] sub-tags (Pattern B)
554
+ let headerContent = null;
555
+ let footerContent = null;
556
+ let remaining = body;
557
+ remaining = remaining.replace(
558
+ /\[header\]([\s\S]*?)\[\/header\]/i,
559
+ (_, inner) => {
560
+ headerContent = inner.trim();
561
+ return '';
562
+ }
563
+ );
564
+ remaining = remaining.replace(
565
+ /\[footer\]([\s\S]*?)\[\/footer\]/i,
566
+ (_, inner) => {
567
+ footerContent = inner.trim();
568
+ return '';
569
+ }
570
+ );
571
+
572
+ // Header — sub-tag wins over attributes; attributes only used when no sub-tag
554
573
  let headerHtml = '';
555
- if (title || icon) {
574
+ if (headerContent !== null) {
575
+ headerHtml = `<div class="card-header">${marked.parse(headerContent)}</div>`;
576
+ } else if (title || icon) {
556
577
  let inner = '';
557
578
  if (icon && iconLayout === 'stacked') {
558
579
  // Stacked: icon centred above title, all centred
@@ -574,8 +595,10 @@ function processCardBlocks(markdown) {
574
595
  }
575
596
  }
576
597
 
577
- const bodyHtml = `<div class="card-body">${marked.parse(body.trim())}</div>`;
578
- const footerHtml = footer ? `<div class="card-footer">${footer}</div>` : '';
598
+ const bodyHtml = `<div class="card-body">${marked.parse(remaining.trim())}</div>`;
599
+ const footerHtml = footerContent !== null
600
+ ? `<div class="card-footer">${marked.parse(footerContent)}</div>`
601
+ : footer ? `<div class="card-footer">${footer}</div>` : '';
579
602
 
580
603
  return `<div class="${classes.join(' ')}"${coll}${id}>${headerHtml}${bodyHtml}${footerHtml}</div>\n`;
581
604
  }