domma-cms 0.14.10 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/js/lib/effect-defs.js +1 -0
- package/admin/js/lib/effects-builder.js +3 -0
- package/admin/js/lib/markdown-toolbar.js +17 -46
- package/admin/js/templates/effects.html +83 -0
- package/admin/js/views/page-editor.js +12 -12
- package/package.json +2 -2
- package/public/js/effects.js +1 -1
- package/server/services/markdown.js +90 -21
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{api as _}from"../api.js";import{CardBuilder as Ke}from"../lib/card-builder.js?v=4";import{TimelineBuilder as Ze}from"../lib/timeline-builder.js?v=1";import{attachEditorKeybindings as Xe,createToolbar as et,insertAtCursor as xe}from"../lib/markdown-toolbar.js?v=
|
|
1
|
+
import{api as _}from"../api.js";import{CardBuilder as Ke}from"../lib/card-builder.js?v=4";import{TimelineBuilder as Ze}from"../lib/timeline-builder.js?v=1";import{attachEditorKeybindings as Xe,createToolbar as et,insertAtCursor as xe}from"../lib/markdown-toolbar.js?v=13";import{EffectsBuilder as tt}from"../lib/effects-builder.js?v=1";import{populateThemeSelect as nt}from"../lib/themes.js";import{createFoldingManager as at}from"../lib/editor-folding.js";import{makeCheckbox as He,makeField as W,makeIconInput as Oe,makeLivePreview as Ue,makeSelect as Be,makeTextInput as Ce}from"../lib/shortcode-modal.js";import{openScribeComposer as ot}from"../lib/scribe-composer.js?v=2";function lt(){const o=E.slideover({title:"Editor Reference",size:"md",position:"right"}),re="background:var(--dm-surface-subtle,#1a1a2e);padding:.2rem .4rem;border-radius:3px;font-size:.85em;font-family:monospace;",de="margin:1rem 0 .5rem;font-size:1rem;";function G(u){const g=document.createElement("code");return g.style.cssText=re,g.textContent=u,g}function K(u){const g=document.createElement("h3");return g.style.cssText=de,g.textContent=u,g}const Ie=document.createElement("div");Ie.style.cssText="padding:1rem;";const De=document.createElement("div");De.className="tabs";const Me=document.createElement("div");Me.className="tab-list",["Basics","Layout","Components","Data","Advanced"].forEach((u,g)=>{const l=document.createElement("button");l.className="tab-item"+(g===0?" active":""),l.textContent=u,Me.appendChild(l)});const Y=document.createElement("div");Y.className="tab-content";const Le="display:flex;flex-direction:column;gap:1rem;",$e=document.createElement("div");$e.className="tab-panel active",$e.style.cssText=Le;const fe=document.createElement("div");fe.className="tab-panel",fe.style.cssText=Le;const se=document.createElement("div");se.className="tab-panel",se.style.cssText=Le;const ve=document.createElement("div");ve.className="tab-panel",ve.style.cssText=Le;const we=document.createElement("div");we.className="tab-panel",we.style.cssText=Le,Y.appendChild($e),Y.appendChild(fe),Y.appendChild(se),Y.appendChild(ve),Y.appendChild(we),De.appendChild(Me),De.appendChild(Y),Ie.appendChild(De);const qe=document.createElement("div");qe.appendChild(K("Markdown Basics"));const me=document.createElement("table");me.style.cssText="width:100%;border-collapse:collapse;font-size:.9em;";const X=document.createElement("thead"),oe=document.createElement("tr");["Syntax","Result"].forEach(u=>{const g=document.createElement("th");g.style.cssText="text-align:left;padding:.4rem .5rem;border-bottom:1px solid var(--dm-border,#333);",g.textContent=u,oe.appendChild(g)}),X.appendChild(oe),me.appendChild(X);const he=document.createElement("tbody");[["**bold**","bold (rendered bold)"],["_italic_","italic (rendered italic)"],["## Heading","Heading (h2)"],["[text](url)","Link"],["","Image"],["- item","Bullet list"],["`code`","Inline code"],["---","Divider"]].forEach(([u,g])=>{const l=document.createElement("tr"),i=document.createElement("td");i.style.cssText="padding:.35rem .5rem;vertical-align:top;",i.appendChild(G(u));const d=document.createElement("td");d.style.cssText="padding:.35rem .5rem;vertical-align:top;color:var(--dm-text-muted,#aaa);font-size:.85em;",d.textContent=g,l.appendChild(i),l.appendChild(d),he.appendChild(l)}),me.appendChild(he),qe.appendChild(me),$e.appendChild(qe);const ne=document.createElement("div");ne.appendChild(K("Grid & Columns"));function U(u){const g=document.createElement("p");return g.style.cssText="margin:.3rem 0 .6rem;font-size:.82em;color:var(--dm-text-muted,#aaa);",g.textContent=u,g}function L(u,g,l){const i=document.createElement("p");i.style.cssText="margin:.75rem 0 .25rem;font-size:.85em;font-weight:600;",i.textContent=u;const d=document.createElement("div");d.style.cssText="position:relative;";const j=document.createElement("pre");j.style.cssText=re+"display:block;padding:.5rem 2rem .5rem .75rem;white-space:pre;overflow-x:auto;margin:0;",j.textContent=g;const Z=document.createElement("button");Z.type="button",Z.title="Copy code",Z.style.cssText="position:absolute;top:.3rem;right:.3rem;background:none;border:none;cursor:pointer;opacity:.45;padding:.15rem;line-height:1;color:inherit;",Z.addEventListener("mouseenter",()=>{Z.style.opacity="1"}),Z.addEventListener("mouseleave",()=>{Z.style.opacity=".45"});const J=document.createElement("span");J.setAttribute("data-icon","copy"),Z.appendChild(J),Z.addEventListener("click",()=>{const P=document.createElement("textarea");P.value=g,P.style.cssText="position:fixed;opacity:0;",document.body.appendChild(P),P.select(),document.execCommand("copy"),P.remove(),E.toast("Copied!",{type:"success",duration:1200})}),d.appendChild(j),d.appendChild(Z);const ee=document.createDocumentFragment();return ee.appendChild(i),ee.appendChild(d),l&&ee.appendChild(U(l)),ee}const ae=document.createElement("table");ae.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(([u,g,l])=>{const i=document.createElement("tr");[u,g,l].forEach((d,j)=>{const Z=document.createElement("td");Z.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",j===0?Z.appendChild(G(u)):(Z.style.color="var(--dm-text-muted,#aaa)",Z.textContent=d),i.appendChild(Z)}),ae.appendChild(i)}),ne.appendChild(ae),ne.appendChild(L("Equal columns",`[grid cols="3" gap="4"]
|
|
2
2
|
[col]One[/col]
|
|
3
3
|
[col]Two[/col]
|
|
4
4
|
[col]Three[/col]
|
|
@@ -46,7 +46,7 @@ Body content here.
|
|
|
46
46
|
[footer]
|
|
47
47
|
[Read more](/about) | [icon name="arrow-right" /]
|
|
48
48
|
[/footer]
|
|
49
|
-
[/card]`,"Use [header] and [footer] sub-tags for Markdown-rendered header/footer regions. Sub-tags override the title/subtitle/icon and footer attributes when both are present.")),se.appendChild(ce);const Ne=document.createElement("div");Ne.appendChild(K("Badge")),Ne.appendChild(U("Inline badge/label elements. Self-closing renders an empty coloured dot."));const Ae=document.createElement("table");Ae.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(([u,g])=>{const l=document.createElement("tr"),i=document.createElement("td");i.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",i.appendChild(G(u));const d=document.createElement("td");d.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",d.textContent=g,l.appendChild(i),l.appendChild(d),Ae.appendChild(l)}),Ne.appendChild(Ae),Ne.appendChild(L("Basic badge",'[badge variant="success"]New[/badge]',null)),Ne.appendChild(L("Pill outline badge",'[badge variant="danger" outline pill]Deprecated[/badge]',null)),se.appendChild(Ne);const V=document.createElement("div");V.appendChild(K("Button")),V.appendChild(U("Renders a styled anchor as a Domma button. Use the Insert \u2192 Button toolbar item for a guided dialog."));const
|
|
49
|
+
[/card]`,"Use [header] and [footer] sub-tags for Markdown-rendered header/footer regions. Sub-tags override the title/subtitle/icon and footer attributes when both are present.")),se.appendChild(ce);const Ne=document.createElement("div");Ne.appendChild(K("Badge")),Ne.appendChild(U("Inline badge/label elements. Self-closing renders an empty coloured dot."));const Ae=document.createElement("table");Ae.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(([u,g])=>{const l=document.createElement("tr"),i=document.createElement("td");i.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",i.appendChild(G(u));const d=document.createElement("td");d.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",d.textContent=g,l.appendChild(i),l.appendChild(d),Ae.appendChild(l)}),Ne.appendChild(Ae),Ne.appendChild(L("Basic badge",'[badge variant="success"]New[/badge]',null)),Ne.appendChild(L("Pill outline badge",'[badge variant="danger" outline pill]Deprecated[/badge]',null)),se.appendChild(Ne);const V=document.createElement("div");V.appendChild(K("Button")),V.appendChild(U("Renders a styled anchor as a Domma button. Use the Insert \u2192 Button toolbar item for a guided dialog."));const M=document.createElement("table");M.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['href="..."',"Link destination (required)"],['label="..."',"Button text (self-closing form)"],['variant="..."',"primary (default), secondary, success, danger, warning, info, outline, ghost, link, outline-*"],['size="sm|lg"',"Button size modifier"],['icon="..."',"Domma icon name before the label"],['icon-after="..."',"Domma icon name after the label"],['target="..."','Link target, e.g. "_blank"'],['class="..."',"Extra CSS classes"],['id="..."',"Element id"]].forEach(([u,g])=>{const l=document.createElement("tr"),i=document.createElement("td");i.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",i.appendChild(G(u));const d=document.createElement("td");d.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",d.textContent=g,l.appendChild(i),l.appendChild(d),M.appendChild(l)}),V.appendChild(M),V.appendChild(L("Primary button",'[button href="/contact" variant="primary"]Get in touch[/button]',null)),V.appendChild(L("Self-closing with icon",'[button href="/download" variant="success" icon="download" size="sm" label="Download" /]',null)),V.appendChild(L("Opens in new tab",'[button href="https://example.com" variant="outline" target="_blank"]Visit site[/button]',null)),se.appendChild(V);const pe=document.createElement("div");pe.appendChild(K("Link (shortcode)")),pe.appendChild(U("Renders a plain anchor with optional icon, target, and class. Use Insert \u2192 Link for a guided dialog. For simple inline links, standard Markdown [text](url) is fine."));const Ee=document.createElement("table");Ee.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[['href="..."',"Link destination (required)"],['label="..."',"Link text (self-closing form)"],['target="..."','Link target, e.g. "_blank"'],['class="..."',"CSS classes on the <a>"],['icon="..."',"Domma icon name before the text"],['icon-after="..."',"Domma icon name after the text"],['id="..."',"Element id"]].forEach(([u,g])=>{const l=document.createElement("tr"),i=document.createElement("td");i.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",i.appendChild(G(u));const d=document.createElement("td");d.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",d.textContent=g,l.appendChild(i),l.appendChild(d),Ee.appendChild(l)}),pe.appendChild(Ee),pe.appendChild(L("External link in new tab",'[link href="https://example.com" target="_blank" icon-after="external-link"]Visit site[/link]',null)),pe.appendChild(L("Self-closing with icon",'[link href="/about" icon="arrow-right" label="About us" /]',null)),se.appendChild(pe);const ke=document.createElement("div");ke.appendChild(K("Spacer")),ke.appendChild(U("Self-closing shortcode that inserts a fixed-height blank gap."));const ue=document.createElement("table");ue.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(([u,g])=>{const l=document.createElement("tr"),i=document.createElement("td");i.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",i.appendChild(G(u));const d=document.createElement("td");d.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",d.textContent=g,l.appendChild(i),l.appendChild(d),ue.appendChild(l)}),ke.appendChild(ue),ke.appendChild(L("32px gap",'[spacer size="32" /]',null)),fe.appendChild(ke);const Te=document.createElement("div");Te.appendChild(K("Icon")),Te.appendChild(U("Self-closing shortcode that renders any Domma icon inline."));const be=document.createElement("table");be.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(([u,g])=>{const l=document.createElement("tr"),i=document.createElement("td");i.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",i.appendChild(G(u));const d=document.createElement("td");d.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",d.textContent=g,l.appendChild(i),l.appendChild(d),be.appendChild(l)}),Te.appendChild(be),Te.appendChild(L("Coloured icon",'[icon name="star" size="24" color="#f5a623" /]',null)),se.appendChild(Te);const ze=document.createElement("div");ze.appendChild(K("Center")),ze.appendChild(U("Wraps content in a centred div. Works with any content including cards, grids, and text.")),ze.appendChild(L("Example","[center]Centred content here[/center]",'Accepts an optional class="..." attribute for extra styling.')),fe.appendChild(ze);const ge=document.createElement("div");ge.appendChild(K("Hero")),ge.appendChild(U("Full-width hero sections \u2014 no plugin required. Uses Domma's built-in Hero CSS component."));const le=document.createElement("table");le.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(([u,g])=>{const l=document.createElement("tr"),i=document.createElement("td");i.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",i.appendChild(G(u));const d=document.createElement("td");d.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",d.textContent=g,l.appendChild(i),l.appendChild(d),le.appendChild(l)}),ge.appendChild(le),ge.appendChild(L("Basic hero",'[hero title="Welcome" tagline="Build something great"][/hero]',null)),ge.appendChild(L("Gradient with body content",`[hero title="Get Started" tagline="Everything you need." size="lg" variant="gradient-blue" align="center"]
|
|
50
50
|
Some introductory **Markdown** content here.
|
|
51
51
|
[/hero]`,null)),ge.appendChild(L("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.")),fe.appendChild(ge);const ie=document.createElement("div");ie.appendChild(K("Interactive Components")),ie.appendChild(U("Tabs, accordion, carousel, and countdown \u2014 no plugin required.")),ie.appendChild(L("Tabs",`[tabs]
|
|
52
52
|
[tab title="First"]Content **A**[/tab]
|
|
@@ -81,25 +81,25 @@ In active development.
|
|
|
81
81
|
Markdown content here.
|
|
82
82
|
[/slideover]`,"Nested [card] and [grid] shortcodes work inside the slideover body.")),se.appendChild(c);const B=document.createElement("div");B.appendChild(K("DConfig \u2014 Declarative Behaviour")),B.appendChild(U("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.")),B.appendChild(L("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.')),B.appendChild(L("Inline shortcode syntax",`[dconfig]
|
|
83
83
|
{ "#my-btn": { "events": { "click": { "target": "#panel", "toggleClass": "hidden" } } } }
|
|
84
|
-
[/dconfig]`,null)),we.appendChild(B);const N=document.createElement("div");N.appendChild(K("CSS Editor")),N.appendChild(U("The toolbar's </> button (left of the Split View icon) swaps the left pane between Markdown and Custom CSS. Edits are saved together with the page \u2014 one Save, one toast."));const A=document.createElement("table");A.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[["</> button","Toggle between Markdown editor and Custom CSS editor"],["Split / Write / Preview","Standard view mode buttons to the right of </>"],["Save","Saves both page content and CSS in a single operation"]].forEach(([u,g])=>{const l=document.createElement("tr"),i=document.createElement("td");i.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",i.appendChild(G(u));const d=document.createElement("td");d.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",d.textContent=g,l.appendChild(i),l.appendChild(d),A.appendChild(l)}),N.appendChild(A),we.appendChild(N);const w=document.createElement("div");w.appendChild(K("Colour Picker")),w.appendChild(U("Insert \u2192 Colour Picker opens a centred colour picker. Pick a colour, then use Copy to copy the hex code to the clipboard or Insert to drop it at the cursor. Click outside the panel to dismiss.")),we.appendChild(w);const k=document.createElement("div");k.appendChild(K("Keyboard Shortcuts"));const T=document.createElement("table");T.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[["Ctrl + B","Bold selected text"],["Ctrl + I","Italic selected text"],["Ctrl + K","Insert link dialog"],["Tab","Indent 2 spaces at cursor"],["Shift + Tab","Dedent 2 spaces on current line"],["Ctrl + X","Cut current line (no selection) or cut selection"],["Ctrl + C","Copy current line (no selection) or copy selection"],["Ctrl + V","Paste from clipboard"],["Ctrl + Z","Undo"],["Ctrl + Y","Redo"]].forEach(([u,g])=>{const l=document.createElement("tr"),i=document.createElement("td");i.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;white-space:nowrap;",i.appendChild(G(u));const d=document.createElement("td");d.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",d.textContent=g,l.appendChild(i),l.appendChild(d),T.appendChild(l)}),k.appendChild(T),$e.appendChild(k);const z=document.createElement("div");z.appendChild(K("Tips"));const
|
|
84
|
+
[/dconfig]`,null)),we.appendChild(B);const N=document.createElement("div");N.appendChild(K("CSS Editor")),N.appendChild(U("The toolbar's </> button (left of the Split View icon) swaps the left pane between Markdown and Custom CSS. Edits are saved together with the page \u2014 one Save, one toast."));const A=document.createElement("table");A.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[["</> button","Toggle between Markdown editor and Custom CSS editor"],["Split / Write / Preview","Standard view mode buttons to the right of </>"],["Save","Saves both page content and CSS in a single operation"]].forEach(([u,g])=>{const l=document.createElement("tr"),i=document.createElement("td");i.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",i.appendChild(G(u));const d=document.createElement("td");d.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",d.textContent=g,l.appendChild(i),l.appendChild(d),A.appendChild(l)}),N.appendChild(A),we.appendChild(N);const w=document.createElement("div");w.appendChild(K("Colour Picker")),w.appendChild(U("Insert \u2192 Colour Picker opens a centred colour picker. Pick a colour, then use Copy to copy the hex code to the clipboard or Insert to drop it at the cursor. Click outside the panel to dismiss.")),we.appendChild(w);const k=document.createElement("div");k.appendChild(K("Keyboard Shortcuts"));const T=document.createElement("table");T.style.cssText="width:100%;border-collapse:collapse;font-size:.85em;margin-bottom:.5rem;",[["Ctrl + B","Bold selected text"],["Ctrl + I","Italic selected text"],["Ctrl + K","Insert link dialog"],["Tab","Indent 2 spaces at cursor"],["Shift + Tab","Dedent 2 spaces on current line"],["Ctrl + X","Cut current line (no selection) or cut selection"],["Ctrl + C","Copy current line (no selection) or copy selection"],["Ctrl + V","Paste from clipboard"],["Ctrl + Z","Undo"],["Ctrl + Y","Redo"]].forEach(([u,g])=>{const l=document.createElement("tr"),i=document.createElement("td");i.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;white-space:nowrap;",i.appendChild(G(u));const d=document.createElement("td");d.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",d.textContent=g,l.appendChild(i),l.appendChild(d),T.appendChild(l)}),k.appendChild(T),$e.appendChild(k);const z=document.createElement("div");z.appendChild(K("Tips"));const q=document.createElement("table");q.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(([u,g])=>{const l=document.createElement("tr"),i=document.createElement("td");i.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;",i.appendChild(G(u));const d=document.createElement("td");d.style.cssText="padding:.3rem .4rem;border-bottom:1px solid var(--dm-border,#333);vertical-align:top;color:var(--dm-text-muted,#aaa);",d.textContent=g,l.appendChild(i),l.appendChild(d),q.appendChild(l)}),z.appendChild(q),z.appendChild(U("Most shortcodes accept a class attribute for custom styling.")),$e.appendChild(z),o.setContent(Ie),o.open(),E.tabs(De),I.scan(Ie)}function it(o,re){const de=o.find("#history-tab").get(0),G=o.find("#version-list").get(0),K=o.find("#version-compare").get(0),Ie=o.find("#version-compare-old").get(0),De=o.find("#version-compare-new").get(0),Me=o.find("#version-compare-label").get(0),Y=o.find("#version-compare-back-btn").get(0),Le=o.find("#version-restore-btn").get(0),$e=o.find("#history-save-named-btn").get(0),fe=o.find("#history-delete-selected-btn").get(0);let se=!1,ve=null;function we(){G.style.display="",K.style.display="none",ve=null}function qe(){G.style.display="none",K.style.display=""}async function me(){G.textContent="";const X=document.createElement("p");X.style.color="var(--dm-text-muted)",X.textContent="Loading\u2026",G.appendChild(X);let oe;try{oe=(await _.versions.list(re)).versions||[]}catch(V){G.textContent="";const M=document.createElement("p");M.style.color="var(--dm-text-danger,#e55)",M.textContent="Failed to load versions: "+V.message,G.appendChild(M);return}if(G.textContent="",fe.style.display="none",!oe.length){const V=document.createElement("p");V.style.color="var(--dm-text-muted)",V.textContent="No versions saved yet. Versions are created automatically on each save.",G.appendChild(V);return}const he=document.createElement("table");he.style.cssText="width:100%;border-collapse:collapse;font-size:.875rem;";const ne=document.createElement("thead"),U=document.createElement("tr"),L=document.createElement("th");L.style.cssText="padding:.5rem .75rem;width:2rem;border-bottom:1px solid var(--dm-border);";const ae=document.createElement("input");ae.type="checkbox",ae.setAttribute("aria-label","Select all versions"),L.appendChild(ae),U.appendChild(L),["Date","Author","Type","Label","Actions"].forEach(V=>{const M=document.createElement("th");M.style.cssText="text-align:left;padding:.5rem .75rem;border-bottom:1px solid var(--dm-border);font-size:.8rem;color:var(--dm-text-muted);",M.textContent=V,U.appendChild(M)}),ne.appendChild(U),he.appendChild(ne);const ce=[];function Ne(){const V=ce.some(pe=>pe.checked),M=ce.length>0&&ce.every(pe=>pe.checked);fe.style.display=V?"":"none",ae.checked=M,ae.indeterminate=V&&!M}ae.addEventListener("change",()=>{ce.forEach(V=>{V.checked=ae.checked}),Ne()});const Ae=document.createElement("tbody");oe.forEach(V=>{const M=document.createElement("tr");M.style.borderBottom="1px solid var(--dm-border)";const pe=document.createElement("td");pe.style.padding=".5rem .75rem";const Ee=document.createElement("input");Ee.type="checkbox",Ee.value=V.filename,Ee.addEventListener("change",Ne),pe.appendChild(Ee),ce.push(Ee);const ke=document.createElement("td");ke.style.padding=".5rem .75rem",ke.textContent=D(V.createdAt).format("D MMM YYYY, HH:mm");const ue=document.createElement("td");ue.style.padding=".5rem .75rem",ue.textContent=V.author||"\u2014";const Te=document.createElement("td");Te.style.padding=".5rem .75rem";const be=document.createElement("span");be.className="badge badge-"+(V.type==="manual"?"primary":"secondary"),be.style.cssText="font-size:.75rem;padding:.2rem .5rem;border-radius:4px;",be.textContent=V.type,Te.appendChild(be);const ze=document.createElement("td");ze.style.cssText="padding:.5rem .75rem;color:var(--dm-text-muted);",ze.textContent=V.label||"\u2014";const ge=document.createElement("td");ge.style.padding=".5rem .75rem";const le=document.createElement("button");le.className="btn btn-ghost btn-xs",le.style.marginRight=".4rem",le.textContent="Compare",le.addEventListener("click",async()=>{try{const ye=await _.versions.get(re,V.filename);Ie.textContent=ye.content,De.textContent=o.find("#markdown-editor").get(0).value,Me.textContent=D(V.createdAt).format("D MMM YYYY, HH:mm")+(V.label?` \u2014 ${V.label}`:""),ve=V.filename,qe()}catch(ye){E.toast("Failed to load version: "+ye.message,{type:"error"})}});const ie=document.createElement("button");ie.className="btn btn-ghost btn-xs",ie.style.color="var(--dm-text-danger,#e55)",ie.textContent="Delete",ie.addEventListener("click",async()=>{if(await E.confirm("Delete this version? This cannot be undone."))try{await _.versions.delete(re,V.filename),E.toast("Version deleted.",{type:"success"}),await me()}catch(ye){E.toast("Failed to delete: "+ye.message,{type:"error"})}}),ge.appendChild(le),ge.appendChild(ie),M.appendChild(pe),M.appendChild(ke),M.appendChild(ue),M.appendChild(Te),M.appendChild(ze),M.appendChild(ge),Ae.appendChild(M)}),he.appendChild(Ae),G.appendChild(he)}de.addEventListener("click",async()=>{se||(se=!0,await me())}),Y.addEventListener("click",()=>{we()}),Le.addEventListener("click",async()=>{if(!(!ve||!await E.confirm("Restore this version? The current page content will be overwritten (a pre-restore snapshot will be saved automatically).")))try{await _.versions.restore(re,ve),E.toast("Page restored. Reloading editor\u2026",{type:"success"}),R.navigate("/pages"),setTimeout(()=>R.navigate(`/pages/edit${re}`),300)}catch(X){E.toast("Restore failed: "+X.message,{type:"error"})}}),fe.addEventListener("click",async()=>{const X=[...G.querySelectorAll("tbody input[type=checkbox]:checked")].map(oe=>oe.value);if(!(!X.length||!await E.confirm(`Delete ${X.length} version${X.length>1?"s":""}? This cannot be undone.`)))try{await _.versions.bulkDelete(re,X),E.toast(`${X.length} version${X.length>1?"s":""} deleted.`,{type:"success"}),await me()}catch(oe){E.toast("Delete failed: "+oe.message,{type:"error"})}}),$e.addEventListener("click",()=>{const X=E.modal({title:"Save Named Version"}),oe=document.createElement("div");oe.style.padding="1rem";const he=document.createElement("label");he.className="form-label",he.textContent="Version Label";const ne=document.createElement("input");ne.type="text",ne.className="form-input",ne.placeholder="e.g. Initial design, Before restructure",ne.style.marginBottom=".75rem";const U=document.createElement("button");U.className="btn btn-primary",U.textContent="Save Version",U.addEventListener("click",async()=>{const L=ne.value.trim();try{await _.versions.create(re,L||null),E.toast("Named version saved.",{type:"success"}),X.close(),se=!1,de.classList.contains("active")&&(se=!0,await me())}catch(ae){E.toast("Failed to save version: "+ae.message,{type:"error"})}}),oe.appendChild(he),oe.appendChild(ne),oe.appendChild(U),X.element.appendChild(oe),X.open(),setTimeout(()=>ne.focus(),100)})}let Re=!1,Ge=null,Ye=!1;function dt(o){return o.split(`
|
|
85
85
|
`).map(re=>re.trimEnd()).join(`
|
|
86
86
|
`).replace(/\n{3,}/g,`
|
|
87
87
|
|
|
88
|
-
`)}const je="editor_prefs";function Je(){return S.get(je)||{viewMode:"split",fullscreen:!1}}function Qe(o,re){const de=Je();de[o]=re,S.set(je,de)}export const pageEditorView={templateUrl:"/admin/js/templates/page-editor.html",async onMount(o){const re=window.location.hash.match(/#\/pages\/edit(\/.*)/),de=re?re[1]:null,G=!!de,K=E.loader(o.get(0),{type:"dots"}),Ie=[_.layouts.get().catch(()=>({})),_.settings.get().catch(()=>({}))];G&&(Ie.push(_.pages.get(de).catch(()=>null)),Ie.push(_.pages.list().catch(()=>[])));const[De,Me,Y,Le]=await Promise.all(Ie);K.destroy(),
|
|
89
|
-
`);for(;L.firstChild;)L.removeChild(L.firstChild);t.forEach((n,s)=>{const a=document.createElement("span");a.className="editor-line-number-row";const m=document.createElement("span");m.className="fold-toggle fold-toggle--empty";const y=document.createElement("span");y.className="editor-line-num",y.textContent=String(s+1),a.appendChild(m),a.appendChild(y),L.appendChild(a)}),L.scrollTop=e.scrollTop}{const e=o.find("#css-editor").get(0);e&&e.addEventListener("scroll",()=>{L.scrollTop=e.scrollTop})}const Ne=()=>{ae.refresh(),L.scrollTop=U.scrollTop};U.addEventListener("input",Ne),U.addEventListener("scroll",()=>{L.scrollTop=U.scrollTop});let Ae=null;const V=()=>{clearTimeout(Ae),Ae=setTimeout(async()=>{const e=ae.getUnfoldedContent();try{const{html:t}=await _.pages.preview(e);X.html(t,{safe:!1}),I.scan(X.get(0)),X.get(0).querySelectorAll(".tabs").forEach(n=>Domma.elements.tabs(n))}catch{window.marked&&X.html(marked.parse(e))}},400)},q=et(me,o.find("#editor-toolbar"),{spacerDefault:$e});let pe=!1,Ee="split",ke=null;const ue=document.createElement("button");ue.className="editor-view-btn",ue.type="button";const Te=document.createElement("span");Te.setAttribute("data-icon","css-code"),ue.appendChild(Te);const be=document.createElement("button");be.className="editor-view-btn",be.type="button";const ze=document.createElement("span");ze.setAttribute("data-icon","code"),be.appendChild(ze);let ge=!1;const le=document.createElement("button");le.className="editor-view-btn",le.type="button",le.textContent="\u25BC\u25BC",le.style.cssText="font-size:9px;letter-spacing:-1px;";const ie=o.find(".editor-toolbar-right").get(0);ie.prepend(le),ie.prepend(be),ie.prepend(ue),I.scan(ue),I.scan(be),E.tooltip(ue,{content:"Edit Custom CSS",position:"top"}),E.tooltip(be,{content:"Format Markdown",position:"top"}),E.tooltip(le,{content:"Fold / unfold all shortcodes",position:"top"}),be.addEventListener("click",()=>{const e=me.get(0),t=e.selectionStart,n=it(ae.getUnfoldedContent());ae.unfoldAll(),ne=!0,e.value=n,ne=!1,e.selectionStart=e.selectionEnd=Math.min(t,n.length),e.dispatchEvent(new Event("input")),ae.refresh(),E.toast("Markdown formatted.",{type:"success"})}),le.addEventListener("click",()=>{ge?(ae.unfoldAll(),le.textContent="\u25BC\u25BC",le.classList.remove("active")):(ae.foldAll(),le.textContent="\u25B6\u25B6",le.classList.add("active")),ge=!ge});const ye=document.createElement("span");ye.className="editor-toolbar-sep",ie.insertBefore(ye,ie.children[3]),ue.addEventListener("click",()=>{pe=!pe;const e=o.find("#markdown-editor"),t=o.find("#css-editor"),n=o.find("#editor-body");if(pe){const s=o.find(".editor-view-btn[data-mode].active").get(0);Ee=s?s.getAttribute("data-mode"):"split",o.find(".editor-view-btn[data-mode]").removeClass("active"),o.find('.editor-view-btn[data-mode="split"]').addClass("active"),n.removeClass("editor-mode-write editor-mode-preview").addClass("editor-mode-split"),e.hide(),t.show(),oe||(t.val(he),oe=!0,Xe(t.get(0))),ce(),ue.classList.add("active")}else o.find(".editor-view-btn[data-mode]").removeClass("active"),o.find(`.editor-view-btn[data-mode="${Ee}"]`).addClass("active"),n.removeClass("editor-mode-split editor-mode-write editor-mode-preview").addClass(`editor-mode-${Ee}`),t.hide(),e.show(),ae.refresh(),ue.classList.remove("active")});const Fe=Je();Fe.viewMode&&Fe.viewMode!=="split"&&(o.find(".editor-view-btn[data-mode]").removeClass("active"),o.find(`.editor-view-btn[data-mode="${Fe.viewMode}"]`).addClass("active"),o.find("#editor-body").removeClass("editor-mode-split editor-mode-write editor-mode-preview").addClass(`editor-mode-${Fe.viewMode}`)),Fe.fullscreen&&o.find(".editor-card").addClass("editor-fullscreen"),o.find("#css-editor").on("input",()=>{ce(),clearTimeout(ke),ke=setTimeout(()=>{let e=document.getElementById("preview-custom-css");e||(e=document.createElement("style"),e.id="preview-custom-css",X.get(0).parentNode.insertBefore(e,X.get(0))),e.textContent=o.find("#css-editor").val()},400)}),q.onLink(async e=>{const t=(await _.pages.list().catch(()=>[])).map(c=>({label:c.title||c.urlPath,value:c.urlPath})),n=document.createElement("div");n.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const s=document.createElement("label");s.className="form-label",s.textContent="URL";const a=document.createElement("input");a.type="text",a.className="form-input",a.placeholder="/about or https://example.com";const m=e.value.substring(e.selectionStart,e.selectionEnd);m&&m.startsWith("/")&&(a.value=m),n.appendChild(s),n.appendChild(a);const y=document.createElement("label");y.className="form-label",y.textContent="Link text";const h=document.createElement("input");h.type="text",h.className="form-input",h.placeholder="Display text",m&&!m.startsWith("/")&&(h.value=m),n.appendChild(y),n.appendChild(h);const x=document.createElement("label");x.className="form-label",x.style.cssText="display:flex;align-items:center;gap:.5rem;cursor:pointer;";const r=document.createElement("input");r.type="checkbox",r.style.cssText="width:1rem;height:1rem;cursor:pointer;",x.appendChild(r),x.appendChild(document.createTextNode("Display as button")),n.appendChild(x);const b=document.createElement("label");b.className="form-label",b.textContent="Button colour",b.style.display="none";const v=document.createElement("select");v.className="form-select",v.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(([c,C])=>{const B=document.createElement("option");B.value=c,B.textContent=C,v.appendChild(B)}),n.appendChild(b),n.appendChild(v),r.addEventListener("change",()=>{const c=r.checked;b.style.display=c?"":"none",v.style.display=c?"":"none"});const f=document.createElement("button");f.type="button",f.className="btn btn-primary",f.textContent="Insert Link",n.appendChild(f);const p=E.slideover({title:"Insert Link",size:"md",position:"right",content:n});p.open(),requestAnimationFrame(()=>{E.autocomplete(a,{data:t,minChars:1,onSelect:c=>{a.value=c.value,h.value||(h.value=c.label)}}),a.focus()}),f.addEventListener("click",()=>{const c=a.value.trim(),C=h.value.trim();if(!c)return;p.close();const B=r.checked?`[button href="${c}" variant="${v.value}"]${C||c}[/button]`:`[${C||c}](${c})`,N=e.selectionStart,A=e.selectionEnd;e.value=e.value.substring(0,N)+B+e.value.substring(A),e.selectionStart=e.selectionEnd=N+B.length,e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),q.onImage(async e=>{const t=(await _.media.list().catch(()=>[])).filter(a=>/\.(png|jpe?g|gif|webp|svg)$/i.test(a.name)),n=document.createElement("div");if(n.className="media-picker-grid",t.length)t.forEach(a=>{const m=document.createElement("div");m.className="media-picker-item",m.dataset.url=a.url;const y=document.createElement("img");y.src=a.url,y.alt=a.name;const h=document.createElement("span");h.textContent=a.name,m.appendChild(y),m.appendChild(h),n.appendChild(m)});else{const a=document.createElement("p");a.className="text-muted p-3",a.textContent="No images uploaded yet.",n.appendChild(a)}const s=E.slideover({title:"Insert Image",size:"lg",position:"right",content:n});$(s.element).on("click",".media-picker-item",function(){const a=$(this).data("url"),m=$(this).find("span").text();xe(e,``),s.close(),me.get(0).dispatchEvent(new Event("input",{bubbles:!0}))}),s.open()}),q.onCollection(async e=>{const[t,n]=await Promise.all([_.collections.list().catch(()=>[]),_.blocks.list().catch(()=>[])]),s=document.createElement("div");s.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const a="display:block;font-size:.85em;font-weight:600;margin-bottom:.25rem;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",m="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;",y=document.createElement("div"),h=document.createElement("label");h.style.cssText=a,h.textContent="Collection";const x=document.createElement("select");if(x.style.cssText=m,t.length)t.forEach(H=>{const te=document.createElement("option");te.value=H.slug,te.textContent=H.title||H.slug,x.appendChild(te)});else{const H=document.createElement("option");H.value="",H.textContent="No collections found",x.appendChild(H)}y.appendChild(h),y.appendChild(x),s.appendChild(y);const r=document.createElement("div"),b=document.createElement("label");b.style.cssText=a,b.textContent="Display";const v=document.createElement("select");v.style.cssText=m,["table","cards","list","block"].forEach(H=>{const te=document.createElement("option");te.value=H,te.textContent=H.charAt(0).toUpperCase()+H.slice(1),v.appendChild(te)}),r.appendChild(b),r.appendChild(v),s.appendChild(r);const f=document.createElement("div");f.style.display="none";const p=document.createElement("label");p.style.cssText=a,p.textContent="Columns (2\u20134)";const c=document.createElement("input");c.type="number",c.min="2",c.max="4",c.value="3",c.style.cssText=m,f.appendChild(p),f.appendChild(c),s.appendChild(f);const C=document.createElement("div");C.style.display="none";const B=document.createElement("label");B.style.cssText=a,B.textContent="Block template";const N=document.createElement("select");if(N.style.cssText=m,n.length)n.forEach(H=>{const te=document.createElement("option");te.value=H.name??H,te.textContent=H.name??H,N.appendChild(te)});else{const H=document.createElement("option");H.value="",H.textContent="No block templates found",N.appendChild(H)}C.appendChild(B),C.appendChild(N),s.appendChild(C);const A=document.createElement("div"),w=document.createElement("label");w.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.9em;cursor:pointer;";const k=document.createElement("input");k.type="checkbox",k.checked=!0,w.appendChild(k),w.appendChild(document.createTextNode("Enable search")),A.appendChild(w),s.appendChild(A);const T=document.createElement("div"),z=document.createElement("label");z.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.9em;cursor:pointer;";const M=document.createElement("input");M.type="checkbox",M.checked=!0,z.appendChild(M),z.appendChild(document.createTextNode("Sortable columns")),T.appendChild(z),s.appendChild(T);const u=document.createElement("div"),g=document.createElement("label");g.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.9em;cursor:pointer;";const l=document.createElement("input");l.type="checkbox",l.checked=!1,g.appendChild(l),g.appendChild(document.createTextNode("CSV export")),u.appendChild(g),s.appendChild(u);const i=document.createElement("div"),d=document.createElement("label");d.style.cssText=a,d.textContent="Rows per page";const j=document.createElement("input");j.type="number",j.min="5",j.max="100",j.value="25",j.style.cssText=m,i.appendChild(d),i.appendChild(j),s.appendChild(i);const Z=document.createElement("div"),J=document.createElement("label");J.style.cssText=a,J.textContent="Limit (optional)";const ee=document.createElement("input");ee.type="number",ee.placeholder="All",ee.style.cssText=m,Z.appendChild(J),Z.appendChild(ee),s.appendChild(Z);const P=document.createElement("button");P.type="button",P.className="btn btn-primary",P.textContent="Insert",s.appendChild(P);const F=[A,T,u,i],O=()=>{const H=v.value,te=H==="table";F.forEach(Se=>{Se.style.display=te?"":"none"}),f.style.display=H==="cards"||H==="block"?"":"none",C.style.display=H==="block"?"":"none"};v.addEventListener("change",O),O();const Q=E.slideover({title:"Insert Collection",size:"md",position:"right",content:s});Q.open(),P.addEventListener("click",()=>{const H=x.value;if(!H)return;const te=v.value;let Se=`[collection slug="${H}" display="${te}"`;if(te==="cards"&&(Se+=` columns="${c.value}"`),te==="block"){const We=N.value;We&&(Se+=` block="${We}"`);const _e=c.value.trim();_e&&(Se+=` cols="${_e}"`)}te==="table"&&!k.checked&&(Se+=' search="false"'),te==="table"&&!M.checked&&(Se+=' sortable="false"'),te==="table"&&l.checked&&(Se+=' exportable="true"'),te==="table"&&j.value!=="25"&&(Se+=` page-size="${j.value}"`);const Ve=ee.value.trim();Ve&&(Se+=` limit="${Ve}"`),Se+=" /]",Q.close(),xe(e,Se),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),q.onBlock(async e=>{let t=[];try{t=await _.blocks.list()}catch{E.toast("Could not load blocks.",{type:"error"});return}if(!t.length){E.toast("No block templates found. Create one in Blocks first.",{type:"warning"});return}const n=document.createElement("div");n.style.cssText="display:flex;gap:1rem;min-height:320px;";const s=E.slideover({title:"Insert Block",size:"lg",position:"right",content:n}),a=document.createElement("div");a.style.cssText="flex:1;display:flex;flex-direction:column;gap:.75rem;";const m=document.createElement("label");m.className="form-label",m.textContent="Block Template";const y=document.createElement("select");y.className="form-select";const h=document.createElement("option");h.value="",h.textContent="\u2014 select a template \u2014",y.appendChild(h),t.forEach(N=>{const A=document.createElement("option");A.value=N.name,A.textContent=N.name,y.appendChild(A)}),a.appendChild(m),a.appendChild(y);const x=document.createElement("div");x.style.cssText="display:flex;flex-direction:column;gap:.5rem;overflow-y:auto;max-height:260px;",a.appendChild(x);const r=document.createElement("button");r.className="btn btn-primary",r.type="button",r.textContent="Insert",r.disabled=!0,a.appendChild(r);const b=document.createElement("div");b.style.cssText="flex:1;border:1px solid var(--dm-border-color);border-radius:var(--dm-radius);padding:.75rem;overflow:auto;background:var(--dm-surface-bg);";const v=document.createElement("p");v.className="text-muted",v.style.cssText="font-size:.8rem;margin:0;",v.textContent="Select a template to see a preview.",b.appendChild(v),n.appendChild(a),n.appendChild(b),s.open();let f=null,p=[];function c(N){return N.replace(/_/g," ").replace(/\b\w/g,A=>A.toUpperCase())}function C(){if(!f)return;const N={};p.forEach(({key:w,input:k})=>{N[w]=k.value});const A=f.replace(/\{\{([\w_]+)\}\}/g,(w,k)=>(N[k]??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"));b.innerHTML=DOMPurify.sanitize(A)}function B(N){x.textContent="",p=[];const A=new Set(["_id","_createdAt","_updatedAt"]),w=new Set;for(const[,k]of N.matchAll(/\{\{([\w_]+)\}\}/g))A.has(k)||w.add(k);if(!w.size){const k=document.createElement("p");k.className="text-muted",k.style.cssText="font-size:.8rem;margin:0;",k.textContent="No editable placeholders in this template.",x.appendChild(k);return}w.forEach(k=>{const T=document.createElement("div"),z=document.createElement("label");z.className="form-label",z.style.cssText="font-size:.8rem;margin-bottom:.2rem;display:block;",z.textContent=c(k);const M=document.createElement("input");M.type="text",M.className="form-input",M.placeholder=c(k),M.addEventListener("input",C),T.appendChild(z),T.appendChild(M),x.appendChild(T),p.push({key:k,input:M})})}y.addEventListener("change",async()=>{const N=y.value;if(!N){f=null,p=[],x.textContent="",b.textContent="",b.appendChild(v),r.disabled=!0;return}f=null,p=[],x.textContent="",r.disabled=!0;try{f=(await _.blocks.get(N)).content||"",B(f),C(),r.disabled=!1}catch{E.toast("Could not load template.",{type:"error"})}}),r.addEventListener("click",()=>{if(!y.value)return;const N=k=>k.replace(/"/g,""");let A=`template="${N(y.value)}"`;p.forEach(({key:k,input:T})=>{T.value!==""&&(A+=` ${k}="${N(T.value)}"`)});const w=`[block ${A} /]`;xe(e,w),e.dispatchEvent(new Event("input",{bubbles:!0})),s.close(),e.focus()})}),q.onForm(async e=>{const t=await _.forms.list().catch(()=>[]),n=document.createElement("div");n.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const s="display:block;font-size:.85em;font-weight:600;margin-bottom:.25rem;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",a="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;",m=document.createElement("div"),y=document.createElement("label");y.style.cssText=s,y.textContent="Form";const h=document.createElement("select");if(h.style.cssText=a,t.length)t.forEach(b=>{const v=document.createElement("option");v.value=b.slug,v.textContent=b.title||b.slug,h.appendChild(v)});else{const b=document.createElement("option");b.value="",b.textContent="No forms found",h.appendChild(b)}m.appendChild(y),m.appendChild(h),n.appendChild(m);const x=document.createElement("button");x.className="btn btn-primary btn-sm",x.style.cssText="align-self:flex-end;margin-top:.5rem;",x.textContent="Insert Form",n.appendChild(x);const r=E.slideover({title:"Insert Form",size:"md",position:"right",content:n});r.open(),x.addEventListener("click",()=>{const b=h.value;b&&(xe(e,`[form slug="${b}" /]`),r.close(),me.get(0).dispatchEvent(new Event("input",{bubbles:!0})))})}),q.onView(async e=>{const t=await _.views.list().catch(()=>[]),n=document.createElement("div");n.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const s="display:block;font-size:.85em;font-weight:600;margin-bottom:.25rem;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",a="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;",m=document.createElement("div"),y=document.createElement("label");y.style.cssText=s,y.textContent="View";const h=document.createElement("select");if(h.style.cssText=a,t.length)t.forEach(p=>{const c=document.createElement("option");c.value=p.slug,c.textContent=p.title||p.slug,h.appendChild(c)});else{const p=document.createElement("option");p.value="",p.textContent="No Views configured yet \u2014 create one under Data \u2192 Views",h.appendChild(p)}m.appendChild(y),m.appendChild(h),n.appendChild(m);const x=document.createElement("div"),r=document.createElement("label");r.style.cssText=s,r.textContent="Display";const b=document.createElement("select");b.style.cssText=a,["table","cards","list"].forEach(p=>{const c=document.createElement("option");c.value=p,c.textContent=p.charAt(0).toUpperCase()+p.slice(1),b.appendChild(c)}),x.appendChild(r),x.appendChild(b),n.appendChild(x);const v=document.createElement("button");v.type="button",v.className="btn btn-primary",v.textContent="Insert",n.appendChild(v);const f=E.slideover({title:"Insert View",size:"md",position:"right",content:n});f.open(),v.addEventListener("click",()=>{const p=h.value;if(!p)return;const c=`[view slug="${p}" display="${b.value}" /]`;f.close(),xe(e,c),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),q.onCta(async e=>{const t=await _.actions.list().catch(()=>[]),n=document.createElement("div");n.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const s=Be(t.length?t.map(p=>[p.slug,p.title||p.slug]):[["","No Actions configured yet \u2014 create one under Data \u2192 Actions"]]);n.appendChild(W("Action",s));const a=Ce("Button label","Run");n.appendChild(W("Label",a)),s.addEventListener("change",()=>{const p=t.find(c=>c.slug===s.value);p?.trigger?.label&&(a.value=p.trigger.label)}),t.length&&t[0]?.trigger?.label&&(a.value=t[0].trigger.label);const m=Ce("Paste entry UUID\u2026","");n.appendChild(W("Entry ID",m));const y=Be([["primary","Primary"],["secondary","Secondary"],["ghost","Ghost"],["danger","Danger"]]);n.appendChild(W("Style",y));const h=Oe("e.g. check, zap, send (optional)","");n.appendChild(W("Icon",h.el));const x=Ce("Confirmation message (optional)","");n.appendChild(W("Confirm prompt",x));const r=document.createElement("div");r.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:auto;padding-top:.75rem;border-top:1px solid var(--dm-border,#333);";const b=document.createElement("button");b.type="button",b.className="btn btn-ghost",b.textContent="Cancel";const v=document.createElement("button");v.type="button",v.className="btn btn-primary",v.textContent="Insert",r.appendChild(b),r.appendChild(v),n.appendChild(r);const f=E.slideover({title:"Insert CTA Button",size:"md",position:"right",content:n});f.open(),I.scan(n),b.addEventListener("click",()=>f.close()),v.addEventListener("click",()=>{const p=s.value;if(!p)return;const c=m.value.trim().replace(/"/g,""),C=(a.value.trim()||"Run").replace(/\[\/cta\]/gi,""),B=y.value,N=h.input.value.trim().replace(/"/g,""),A=x.value.trim().replace(/"/g,"");let w=`action="${p}" style="${B}"`;c&&(w+=` entry="${c}"`),N&&(w+=` icon="${N}"`),A&&(w+=` confirm="${A}"`),f.close(),xe(e,`[cta ${w}]${C}[/cta]`),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),q.onButton(async e=>{const t=(await _.pages.list().catch(()=>[])).map(B=>({label:B.title||B.urlPath,value:B.urlPath})),n=e.value.substring(e.selectionStart,e.selectionEnd),s=document.createElement("div");s.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const a=Ce("/about or https://example.com",n.startsWith("/")?n:"");s.appendChild(W("URL",a));const m=Ce("Button label",n&&!n.startsWith("/")?n:"");s.appendChild(W("Label",m));const y=Be([["primary","Primary"],["secondary","Secondary"],["success","Success (Green)"],["danger","Danger (Red)"],["warning","Warning (Orange)"],["info","Info (Blue)"],["outline","Outline"],["ghost","Ghost"],["link","Link"],["outline-success","Outline Success"],["outline-danger","Outline Danger"]]);s.appendChild(W("Variant",y));const h=Be([["","Default"],["sm","Small"],["lg","Large"]]);s.appendChild(W("Size",h));const x=Oe("e.g. arrow-right, star (optional)","");s.appendChild(W("Icon (before label)",x.el));const r=Oe("e.g. arrow-right (optional)","");s.appendChild(W("Icon (after label)",r.el));const b=He("Open in new tab");s.appendChild(b);const v=b.querySelector("input"),f=document.createElement("div");f.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:auto;padding-top:.75rem;border-top:1px solid var(--dm-border,#333);";const p=document.createElement("button");p.type="button",p.className="btn btn-ghost",p.textContent="Cancel";const c=document.createElement("button");c.type="button",c.className="btn btn-primary",c.textContent="Insert Button",f.appendChild(p),f.appendChild(c),s.appendChild(f);const C=E.slideover({title:"Insert Button",size:"md",position:"right",content:s});C.open(),I.scan(s),p.addEventListener("click",()=>C.close()),requestAnimationFrame(()=>{E.autocomplete(a,{data:t,minChars:1,onSelect:B=>{a.value=B.value,m.value||(m.value=B.label)}}),a.focus()}),c.addEventListener("click",()=>{const B=a.value.trim();if(!B)return;C.close();const N=m.value.trim()||B;let A=`href="${B}" variant="${y.value}"`;h.value&&(A+=` size="${h.value}"`);const w=x.input.value.trim().replace(/"/g,"");w&&(A+=` icon="${w}"`);const k=r.input.value.trim().replace(/"/g,"");k&&(A+=` icon-after="${k}"`),v.checked&&(A+=' target="_blank"'),xe(e,`[button ${A}]${N}[/button]`),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),q.onLinkShortcode(async e=>{const t=(await _.pages.list().catch(()=>[])).map(C=>({label:C.title||C.urlPath,value:C.urlPath})),n=e.value.substring(e.selectionStart,e.selectionEnd),s=document.createElement("div");s.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const a=Ce("/about or https://example.com",n.startsWith("/")?n:"");s.appendChild(W("URL",a));const m=Ce("Link text",n&&!n.startsWith("/")?n:"");s.appendChild(W("Link text",m));const y=Ce("e.g. text-primary, fw-bold (optional)","");s.appendChild(W("CSS class",y));const h=Oe("e.g. arrow-right (optional)","");s.appendChild(W("Icon (before text)",h.el));const x=Oe("e.g. external-link (optional)","");s.appendChild(W("Icon (after text)",x.el));const r=He("Open in new tab");s.appendChild(r);const b=r.querySelector("input"),v=document.createElement("div");v.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:auto;padding-top:.75rem;border-top:1px solid var(--dm-border,#333);";const f=document.createElement("button");f.type="button",f.className="btn btn-ghost",f.textContent="Cancel";const p=document.createElement("button");p.type="button",p.className="btn btn-primary",p.textContent="Insert Link",v.appendChild(f),v.appendChild(p),s.appendChild(v);const c=E.slideover({title:"Insert Link Shortcode",size:"md",position:"right",content:s});c.open(),I.scan(s),f.addEventListener("click",()=>c.close()),requestAnimationFrame(()=>{E.autocomplete(a,{data:t,minChars:1,onSelect:C=>{a.value=C.value,m.value||(m.value=C.label)}}),a.focus()}),p.addEventListener("click",()=>{const C=a.value.trim();if(!C)return;c.close();const B=m.value.trim()||C;let N=`href="${C}"`;const A=y.value.trim().replace(/"/g,"");A&&(N+=` class="${A}"`);const w=h.input.value.trim().replace(/"/g,"");w&&(N+=` icon="${w}"`);const k=x.input.value.trim().replace(/"/g,"");k&&(N+=` icon-after="${k}"`),b.checked&&(N+=' target="_blank"'),xe(e,`[link ${N}]${B}[/link]`),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),q.onTabs(e=>{const t=document.createElement("div");t.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const n=Be([["","Default"],["pills","Pills"],["bordered","Bordered"]]);t.appendChild(W("Style",n));const s=Be([["","Left"],["center","Centre"],["right","Right"]]);t.appendChild(W("Alignment",s));const a=Be([["","None"],["fade","Fade"]]);t.appendChild(W("Animation",a));const m=He("Allow multiple panels open at once"),y=m.querySelector("input");t.appendChild(m);const h=document.createElement("div");h.style.cssText="display:block;font-size:.7rem;font-weight:600;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",h.textContent="Tabs",t.appendChild(h);const x=document.createElement("div");x.style.cssText="display:flex;flex-direction:column;gap:.4rem;";const r=[];function b(){r.forEach(A=>{A.removeBtn.disabled=r.length<=2,A.removeBtn.style.opacity=r.length<=2?"0.3":"1"})}function v(){const A=n.value,w=s.value,k=a.value,T=y.checked,z=[];A&&z.push(`style="${A}"`),w&&z.push(`align="${w}"`),k&&z.push(k),T&&z.push('multiple="true"');let u=`[tabs${z.length?" "+z.join(" "):""}]
|
|
88
|
+
`)}const je="editor_prefs";function Je(){return S.get(je)||{viewMode:"split",fullscreen:!1}}function Qe(o,re){const de=Je();de[o]=re,S.set(je,de)}export const pageEditorView={templateUrl:"/admin/js/templates/page-editor.html",async onMount(o){const re=window.location.hash.match(/#\/pages\/edit(\/.*)/),de=re?re[1]:null,G=!!de,K=E.loader(o.get(0),{type:"dots"}),Ie=[_.layouts.get().catch(()=>({})),_.settings.get().catch(()=>({}))];G&&(Ie.push(_.pages.get(de).catch(()=>null)),Ie.push(_.pages.list().catch(()=>[])));const[De,Me,Y,Le]=await Promise.all(Ie);K.destroy(),nt(o.find("#field-theme").get(0),{includeDefault:!0});const $e=Me?.layoutOptions?.spacerSize??40;if(G&&!Y){E.toast("Page not found.",{type:"error"}),R.navigate("/pages");return}o.find("#editor-title").text(G?`Edit Page \u2014 ${Y.title||de}`:"New Page"),G&&o.find("#page-url-path").val(de),Y&&(o.find("#field-title").val(Y.title||""),o.find("#field-description").val(Y.description||""),o.find("#field-status").val(Y.status||"draft"),o.find("#field-sort-order").val(Y.sortOrder??99),o.find("#field-show-in-nav").prop("checked",!!Y.showInNav),o.find("#field-sidebar").prop("checked",!!Y.sidebar),o.find("#field-show-breadcrumbs").prop("checked",Y.breadcrumbs!==!1),o.find("#field-bundled").prop("checked",!!Y.bundled),o.find("#field-category").val(Y.category||""),o.find("#field-visibility").val(Y.visibility||"public"),o.find("#field-theme").val(Y.theme||""),o.find("#field-seo-title").val(Y.seo?.title||""),o.find("#field-seo-desc").val(Y.seo?.description||""),Y.dconfig&&o.find("#field-dconfig").val(JSON.stringify(Y.dconfig,null,2)));const fe=o.find("#quick-switch");G&&Array.isArray(Le)&&Le.length&&([...Le].sort((e,t)=>(e.title||e.urlPath).localeCompare(t.title||t.urlPath)).forEach(e=>{const t=document.createElement("option");t.value=e.urlPath,t.textContent=`${e.title||"Untitled"} (${e.urlPath})`,e.urlPath===de&&(t.selected=!0),fe.get(0).appendChild(t)}),fe.css("display",""),fe.on("change",function(){const e=this.value;e&&R.navigate("/pages/edit"+e)}));const se=await _.pages.tags().catch(()=>[]),ve=o.find("#field-tags").get(0),we=ve?E.pillbox(ve,{data:se,value:Y?.tags||[],creatable:!0,searchable:!0,placeholder:"Add tag\u2026"}):null,qe=o.find("#field-layout").empty();if(Object.entries(De).forEach(([e,t])=>{const n=(Y?.layout||"default")===e?"selected":"";qe.append(`<option value="${e}" ${n}>${t.label||e}</option>`)}),G){const e=o.find("#view-page-btn").get(0);e.href=de,e.style.display="";const t=o.find("#live-preview-tab").get(0);t.style.display="";const n=o.find("#history-tab").get(0);n.style.display=""}if(E.tabs(o.find("#editor-meta-tabs").get(0)),G){const e=o.find("#live-preview-tab").get(0),t=o.find("#live-preview-frame").get(0);let n=!1;e.addEventListener("click",function(){n||(t.src=de,n=!0)}),it(o,de)}const me=o.find("#markdown-editor"),X=o.find("#markdown-preview");Y&&me.val(Y.content||"");let oe=!1,he="";_.settings.getCustomCss().then(({css:e})=>{if(he=e||"",!he)return;const t=document.createElement("style");t.id="preview-custom-css",t.textContent=he,X.get(0).parentNode.insertBefore(t,X.get(0))}).catch(()=>{});let ne=!1;const U=me.get(0),L=document.createElement("div");L.className="editor-line-numbers editor-line-numbers--foldable",U.parentElement.insertBefore(L,U);const ae=at(U,L,{onFoldChange:()=>{}});ae.setGuard(e=>{ne=e});function ce(){const e=o.find("#css-editor").get(0);if(!e)return;const t=e.value.split(`
|
|
89
|
+
`);for(;L.firstChild;)L.removeChild(L.firstChild);t.forEach((n,s)=>{const a=document.createElement("span");a.className="editor-line-number-row";const m=document.createElement("span");m.className="fold-toggle fold-toggle--empty";const y=document.createElement("span");y.className="editor-line-num",y.textContent=String(s+1),a.appendChild(m),a.appendChild(y),L.appendChild(a)}),L.scrollTop=e.scrollTop}{const e=o.find("#css-editor").get(0);e&&e.addEventListener("scroll",()=>{L.scrollTop=e.scrollTop})}const Ne=()=>{ae.refresh(),L.scrollTop=U.scrollTop};U.addEventListener("input",Ne),U.addEventListener("scroll",()=>{L.scrollTop=U.scrollTop});let Ae=null;const V=()=>{clearTimeout(Ae),Ae=setTimeout(async()=>{const e=ae.getUnfoldedContent();try{const{html:t}=await _.pages.preview(e);X.html(t,{safe:!1}),I.scan(X.get(0)),X.get(0).querySelectorAll(".tabs").forEach(n=>Domma.elements.tabs(n))}catch{window.marked&&X.html(marked.parse(e))}},400)},M=et(me,o.find("#editor-toolbar"),{spacerDefault:$e});let pe=!1,Ee="split",ke=null;const ue=document.createElement("button");ue.className="editor-view-btn",ue.type="button";const Te=document.createElement("span");Te.setAttribute("data-icon","css-code"),ue.appendChild(Te);const be=document.createElement("button");be.className="editor-view-btn",be.type="button";const ze=document.createElement("span");ze.setAttribute("data-icon","code"),be.appendChild(ze);let ge=!1;const le=document.createElement("button");le.className="editor-view-btn",le.type="button",le.textContent="\u25BC\u25BC",le.style.cssText="font-size:9px;letter-spacing:-1px;";const ie=o.find(".editor-toolbar-right").get(0);ie.prepend(le),ie.prepend(be),ie.prepend(ue),I.scan(ue),I.scan(be),E.tooltip(ue,{content:"Edit Custom CSS",position:"top"}),E.tooltip(be,{content:"Format Markdown",position:"top"}),E.tooltip(le,{content:"Fold / unfold all shortcodes",position:"top"}),be.addEventListener("click",()=>{const e=me.get(0),t=e.selectionStart,n=dt(ae.getUnfoldedContent());ae.unfoldAll(),ne=!0,e.value=n,ne=!1,e.selectionStart=e.selectionEnd=Math.min(t,n.length),e.dispatchEvent(new Event("input")),ae.refresh(),E.toast("Markdown formatted.",{type:"success"})}),le.addEventListener("click",()=>{ge?(ae.unfoldAll(),le.textContent="\u25BC\u25BC",le.classList.remove("active")):(ae.foldAll(),le.textContent="\u25B6\u25B6",le.classList.add("active")),ge=!ge});const ye=document.createElement("span");ye.className="editor-toolbar-sep",ie.insertBefore(ye,ie.children[3]),ue.addEventListener("click",()=>{pe=!pe;const e=o.find("#markdown-editor"),t=o.find("#css-editor"),n=o.find("#editor-body");if(pe){const s=o.find(".editor-view-btn[data-mode].active").get(0);Ee=s?s.getAttribute("data-mode"):"split",o.find(".editor-view-btn[data-mode]").removeClass("active"),o.find('.editor-view-btn[data-mode="split"]').addClass("active"),n.removeClass("editor-mode-write editor-mode-preview").addClass("editor-mode-split"),e.hide(),t.show(),oe||(t.val(he),oe=!0,Xe(t.get(0))),ce(),ue.classList.add("active")}else o.find(".editor-view-btn[data-mode]").removeClass("active"),o.find(`.editor-view-btn[data-mode="${Ee}"]`).addClass("active"),n.removeClass("editor-mode-split editor-mode-write editor-mode-preview").addClass(`editor-mode-${Ee}`),t.hide(),e.show(),ae.refresh(),ue.classList.remove("active")});const Fe=Je();Fe.viewMode&&Fe.viewMode!=="split"&&(o.find(".editor-view-btn[data-mode]").removeClass("active"),o.find(`.editor-view-btn[data-mode="${Fe.viewMode}"]`).addClass("active"),o.find("#editor-body").removeClass("editor-mode-split editor-mode-write editor-mode-preview").addClass(`editor-mode-${Fe.viewMode}`)),Fe.fullscreen&&o.find(".editor-card").addClass("editor-fullscreen"),o.find("#css-editor").on("input",()=>{ce(),clearTimeout(ke),ke=setTimeout(()=>{let e=document.getElementById("preview-custom-css");e||(e=document.createElement("style"),e.id="preview-custom-css",X.get(0).parentNode.insertBefore(e,X.get(0))),e.textContent=o.find("#css-editor").val()},400)}),M.onLink(async e=>{const t=(await _.pages.list().catch(()=>[])).map(c=>({label:c.title||c.urlPath,value:c.urlPath})),n=document.createElement("div");n.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const s=document.createElement("label");s.className="form-label",s.textContent="URL";const a=document.createElement("input");a.type="text",a.className="form-input",a.placeholder="/about or https://example.com";const m=e.value.substring(e.selectionStart,e.selectionEnd);m&&m.startsWith("/")&&(a.value=m),n.appendChild(s),n.appendChild(a);const y=document.createElement("label");y.className="form-label",y.textContent="Link text";const h=document.createElement("input");h.type="text",h.className="form-input",h.placeholder="Display text",m&&!m.startsWith("/")&&(h.value=m),n.appendChild(y),n.appendChild(h);const x=document.createElement("label");x.className="form-label",x.style.cssText="display:flex;align-items:center;gap:.5rem;cursor:pointer;";const r=document.createElement("input");r.type="checkbox",r.style.cssText="width:1rem;height:1rem;cursor:pointer;",x.appendChild(r),x.appendChild(document.createTextNode("Display as button")),n.appendChild(x);const b=document.createElement("label");b.className="form-label",b.textContent="Button colour",b.style.display="none";const v=document.createElement("select");v.className="form-select",v.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(([c,C])=>{const B=document.createElement("option");B.value=c,B.textContent=C,v.appendChild(B)}),n.appendChild(b),n.appendChild(v),r.addEventListener("change",()=>{const c=r.checked;b.style.display=c?"":"none",v.style.display=c?"":"none"});const f=document.createElement("button");f.type="button",f.className="btn btn-primary",f.textContent="Insert Link",n.appendChild(f);const p=E.slideover({title:"Insert Link",size:"md",position:"right",content:n});p.open(),requestAnimationFrame(()=>{E.autocomplete(a,{data:t,minChars:1,onSelect:c=>{a.value=c.value,h.value||(h.value=c.label)}}),a.focus()}),f.addEventListener("click",()=>{const c=a.value.trim(),C=h.value.trim();if(!c)return;p.close();const B=r.checked?`[button href="${c}" variant="${v.value}"]${C||c}[/button]`:`[${C||c}](${c})`,N=e.selectionStart,A=e.selectionEnd;e.value=e.value.substring(0,N)+B+e.value.substring(A),e.selectionStart=e.selectionEnd=N+B.length,e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),M.onImage(async e=>{const t=(await _.media.list().catch(()=>[])).filter(a=>/\.(png|jpe?g|gif|webp|svg)$/i.test(a.name)),n=document.createElement("div");if(n.className="media-picker-grid",t.length)t.forEach(a=>{const m=document.createElement("div");m.className="media-picker-item",m.dataset.url=a.url;const y=document.createElement("img");y.src=a.url,y.alt=a.name;const h=document.createElement("span");h.textContent=a.name,m.appendChild(y),m.appendChild(h),n.appendChild(m)});else{const a=document.createElement("p");a.className="text-muted p-3",a.textContent="No images uploaded yet.",n.appendChild(a)}const s=E.slideover({title:"Insert Image",size:"lg",position:"right",content:n});$(s.element).on("click",".media-picker-item",function(){const a=$(this).data("url"),m=$(this).find("span").text();xe(e,``),s.close(),me.get(0).dispatchEvent(new Event("input",{bubbles:!0}))}),s.open()}),M.onCollection(async e=>{const[t,n]=await Promise.all([_.collections.list().catch(()=>[]),_.blocks.list().catch(()=>[])]),s=document.createElement("div");s.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const a="display:block;font-size:.85em;font-weight:600;margin-bottom:.25rem;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",m="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;",y=document.createElement("div"),h=document.createElement("label");h.style.cssText=a,h.textContent="Collection";const x=document.createElement("select");if(x.style.cssText=m,t.length)t.forEach(H=>{const te=document.createElement("option");te.value=H.slug,te.textContent=H.title||H.slug,x.appendChild(te)});else{const H=document.createElement("option");H.value="",H.textContent="No collections found",x.appendChild(H)}y.appendChild(h),y.appendChild(x),s.appendChild(y);const r=document.createElement("div"),b=document.createElement("label");b.style.cssText=a,b.textContent="Display";const v=document.createElement("select");v.style.cssText=m,["table","cards","list","block"].forEach(H=>{const te=document.createElement("option");te.value=H,te.textContent=H.charAt(0).toUpperCase()+H.slice(1),v.appendChild(te)}),r.appendChild(b),r.appendChild(v),s.appendChild(r);const f=document.createElement("div");f.style.display="none";const p=document.createElement("label");p.style.cssText=a,p.textContent="Columns (2\u20134)";const c=document.createElement("input");c.type="number",c.min="2",c.max="4",c.value="3",c.style.cssText=m,f.appendChild(p),f.appendChild(c),s.appendChild(f);const C=document.createElement("div");C.style.display="none";const B=document.createElement("label");B.style.cssText=a,B.textContent="Block template";const N=document.createElement("select");if(N.style.cssText=m,n.length)n.forEach(H=>{const te=document.createElement("option");te.value=H.name??H,te.textContent=H.name??H,N.appendChild(te)});else{const H=document.createElement("option");H.value="",H.textContent="No block templates found",N.appendChild(H)}C.appendChild(B),C.appendChild(N),s.appendChild(C);const A=document.createElement("div"),w=document.createElement("label");w.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.9em;cursor:pointer;";const k=document.createElement("input");k.type="checkbox",k.checked=!0,w.appendChild(k),w.appendChild(document.createTextNode("Enable search")),A.appendChild(w),s.appendChild(A);const T=document.createElement("div"),z=document.createElement("label");z.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.9em;cursor:pointer;";const q=document.createElement("input");q.type="checkbox",q.checked=!0,z.appendChild(q),z.appendChild(document.createTextNode("Sortable columns")),T.appendChild(z),s.appendChild(T);const u=document.createElement("div"),g=document.createElement("label");g.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.9em;cursor:pointer;";const l=document.createElement("input");l.type="checkbox",l.checked=!1,g.appendChild(l),g.appendChild(document.createTextNode("CSV export")),u.appendChild(g),s.appendChild(u);const i=document.createElement("div"),d=document.createElement("label");d.style.cssText=a,d.textContent="Rows per page";const j=document.createElement("input");j.type="number",j.min="5",j.max="100",j.value="25",j.style.cssText=m,i.appendChild(d),i.appendChild(j),s.appendChild(i);const Z=document.createElement("div"),J=document.createElement("label");J.style.cssText=a,J.textContent="Limit (optional)";const ee=document.createElement("input");ee.type="number",ee.placeholder="All",ee.style.cssText=m,Z.appendChild(J),Z.appendChild(ee),s.appendChild(Z);const P=document.createElement("button");P.type="button",P.className="btn btn-primary",P.textContent="Insert",s.appendChild(P);const F=[A,T,u,i],O=()=>{const H=v.value,te=H==="table";F.forEach(Se=>{Se.style.display=te?"":"none"}),f.style.display=H==="cards"||H==="block"?"":"none",C.style.display=H==="block"?"":"none"};v.addEventListener("change",O),O();const Q=E.slideover({title:"Insert Collection",size:"md",position:"right",content:s});Q.open(),P.addEventListener("click",()=>{const H=x.value;if(!H)return;const te=v.value;let Se=`[collection slug="${H}" display="${te}"`;if(te==="cards"&&(Se+=` columns="${c.value}"`),te==="block"){const We=N.value;We&&(Se+=` block="${We}"`);const _e=c.value.trim();_e&&(Se+=` cols="${_e}"`)}te==="table"&&!k.checked&&(Se+=' search="false"'),te==="table"&&!q.checked&&(Se+=' sortable="false"'),te==="table"&&l.checked&&(Se+=' exportable="true"'),te==="table"&&j.value!=="25"&&(Se+=` page-size="${j.value}"`);const Ve=ee.value.trim();Ve&&(Se+=` limit="${Ve}"`),Se+=" /]",Q.close(),xe(e,Se),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),M.onBlock(async e=>{let t=[];try{t=await _.blocks.list()}catch{E.toast("Could not load blocks.",{type:"error"});return}if(!t.length){E.toast("No block templates found. Create one in Blocks first.",{type:"warning"});return}const n=document.createElement("div");n.style.cssText="display:flex;gap:1rem;min-height:320px;";const s=E.slideover({title:"Insert Block",size:"lg",position:"right",content:n}),a=document.createElement("div");a.style.cssText="flex:1;display:flex;flex-direction:column;gap:.75rem;";const m=document.createElement("label");m.className="form-label",m.textContent="Block Template";const y=document.createElement("select");y.className="form-select";const h=document.createElement("option");h.value="",h.textContent="\u2014 select a template \u2014",y.appendChild(h),t.forEach(N=>{const A=document.createElement("option");A.value=N.name,A.textContent=N.name,y.appendChild(A)}),a.appendChild(m),a.appendChild(y);const x=document.createElement("div");x.style.cssText="display:flex;flex-direction:column;gap:.5rem;overflow-y:auto;max-height:260px;",a.appendChild(x);const r=document.createElement("button");r.className="btn btn-primary",r.type="button",r.textContent="Insert",r.disabled=!0,a.appendChild(r);const b=document.createElement("div");b.style.cssText="flex:1;border:1px solid var(--dm-border-color);border-radius:var(--dm-radius);padding:.75rem;overflow:auto;background:var(--dm-surface-bg);";const v=document.createElement("p");v.className="text-muted",v.style.cssText="font-size:.8rem;margin:0;",v.textContent="Select a template to see a preview.",b.appendChild(v),n.appendChild(a),n.appendChild(b),s.open();let f=null,p=[];function c(N){return N.replace(/_/g," ").replace(/\b\w/g,A=>A.toUpperCase())}function C(){if(!f)return;const N={};p.forEach(({key:w,input:k})=>{N[w]=k.value});const A=f.replace(/\{\{([\w_]+)\}\}/g,(w,k)=>(N[k]??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"));b.innerHTML=DOMPurify.sanitize(A)}function B(N){x.textContent="",p=[];const A=new Set(["_id","_createdAt","_updatedAt"]),w=new Set;for(const[,k]of N.matchAll(/\{\{([\w_]+)\}\}/g))A.has(k)||w.add(k);if(!w.size){const k=document.createElement("p");k.className="text-muted",k.style.cssText="font-size:.8rem;margin:0;",k.textContent="No editable placeholders in this template.",x.appendChild(k);return}w.forEach(k=>{const T=document.createElement("div"),z=document.createElement("label");z.className="form-label",z.style.cssText="font-size:.8rem;margin-bottom:.2rem;display:block;",z.textContent=c(k);const q=document.createElement("input");q.type="text",q.className="form-input",q.placeholder=c(k),q.addEventListener("input",C),T.appendChild(z),T.appendChild(q),x.appendChild(T),p.push({key:k,input:q})})}y.addEventListener("change",async()=>{const N=y.value;if(!N){f=null,p=[],x.textContent="",b.textContent="",b.appendChild(v),r.disabled=!0;return}f=null,p=[],x.textContent="",r.disabled=!0;try{f=(await _.blocks.get(N)).content||"",B(f),C(),r.disabled=!1}catch{E.toast("Could not load template.",{type:"error"})}}),r.addEventListener("click",()=>{if(!y.value)return;const N=k=>k.replace(/"/g,""");let A=`template="${N(y.value)}"`;p.forEach(({key:k,input:T})=>{T.value!==""&&(A+=` ${k}="${N(T.value)}"`)});const w=`[block ${A} /]`;xe(e,w),e.dispatchEvent(new Event("input",{bubbles:!0})),s.close(),e.focus()})}),M.onForm(async e=>{const t=await _.forms.list().catch(()=>[]),n=document.createElement("div");n.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const s="display:block;font-size:.85em;font-weight:600;margin-bottom:.25rem;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",a="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;",m=document.createElement("div"),y=document.createElement("label");y.style.cssText=s,y.textContent="Form";const h=document.createElement("select");if(h.style.cssText=a,t.length)t.forEach(b=>{const v=document.createElement("option");v.value=b.slug,v.textContent=b.title||b.slug,h.appendChild(v)});else{const b=document.createElement("option");b.value="",b.textContent="No forms found",h.appendChild(b)}m.appendChild(y),m.appendChild(h),n.appendChild(m);const x=document.createElement("button");x.className="btn btn-primary btn-sm",x.style.cssText="align-self:flex-end;margin-top:.5rem;",x.textContent="Insert Form",n.appendChild(x);const r=E.slideover({title:"Insert Form",size:"md",position:"right",content:n});r.open(),x.addEventListener("click",()=>{const b=h.value;b&&(xe(e,`[form slug="${b}" /]`),r.close(),me.get(0).dispatchEvent(new Event("input",{bubbles:!0})))})}),M.onView(async e=>{const t=await _.views.list().catch(()=>[]),n=document.createElement("div");n.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const s="display:block;font-size:.85em;font-weight:600;margin-bottom:.25rem;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",a="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;",m=document.createElement("div"),y=document.createElement("label");y.style.cssText=s,y.textContent="View";const h=document.createElement("select");if(h.style.cssText=a,t.length)t.forEach(p=>{const c=document.createElement("option");c.value=p.slug,c.textContent=p.title||p.slug,h.appendChild(c)});else{const p=document.createElement("option");p.value="",p.textContent="No Views configured yet \u2014 create one under Data \u2192 Views",h.appendChild(p)}m.appendChild(y),m.appendChild(h),n.appendChild(m);const x=document.createElement("div"),r=document.createElement("label");r.style.cssText=s,r.textContent="Display";const b=document.createElement("select");b.style.cssText=a,["table","cards","list"].forEach(p=>{const c=document.createElement("option");c.value=p,c.textContent=p.charAt(0).toUpperCase()+p.slice(1),b.appendChild(c)}),x.appendChild(r),x.appendChild(b),n.appendChild(x);const v=document.createElement("button");v.type="button",v.className="btn btn-primary",v.textContent="Insert",n.appendChild(v);const f=E.slideover({title:"Insert View",size:"md",position:"right",content:n});f.open(),v.addEventListener("click",()=>{const p=h.value;if(!p)return;const c=`[view slug="${p}" display="${b.value}" /]`;f.close(),xe(e,c),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),M.onCta(async e=>{const t=await _.actions.list().catch(()=>[]),n=document.createElement("div");n.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const s=Be(t.length?t.map(p=>[p.slug,p.title||p.slug]):[["","No Actions configured yet \u2014 create one under Data \u2192 Actions"]]);n.appendChild(W("Action",s));const a=Ce("Button label","Run");n.appendChild(W("Label",a)),s.addEventListener("change",()=>{const p=t.find(c=>c.slug===s.value);p?.trigger?.label&&(a.value=p.trigger.label)}),t.length&&t[0]?.trigger?.label&&(a.value=t[0].trigger.label);const m=Ce("Paste entry UUID\u2026","");n.appendChild(W("Entry ID",m));const y=Be([["primary","Primary"],["secondary","Secondary"],["ghost","Ghost"],["danger","Danger"]]);n.appendChild(W("Style",y));const h=Oe("e.g. check, zap, send (optional)","");n.appendChild(W("Icon",h.el));const x=Ce("Confirmation message (optional)","");n.appendChild(W("Confirm prompt",x));const r=document.createElement("div");r.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:auto;padding-top:.75rem;border-top:1px solid var(--dm-border,#333);";const b=document.createElement("button");b.type="button",b.className="btn btn-ghost",b.textContent="Cancel";const v=document.createElement("button");v.type="button",v.className="btn btn-primary",v.textContent="Insert",r.appendChild(b),r.appendChild(v),n.appendChild(r);const f=E.slideover({title:"Insert CTA Button",size:"md",position:"right",content:n});f.open(),I.scan(n),b.addEventListener("click",()=>f.close()),v.addEventListener("click",()=>{const p=s.value;if(!p)return;const c=m.value.trim().replace(/"/g,""),C=(a.value.trim()||"Run").replace(/\[\/cta\]/gi,""),B=y.value,N=h.input.value.trim().replace(/"/g,""),A=x.value.trim().replace(/"/g,"");let w=`action="${p}" style="${B}"`;c&&(w+=` entry="${c}"`),N&&(w+=` icon="${N}"`),A&&(w+=` confirm="${A}"`),f.close(),xe(e,`[cta ${w}]${C}[/cta]`),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),M.onButton(async e=>{const t=(await _.pages.list().catch(()=>[])).map(B=>({label:B.title||B.urlPath,value:B.urlPath})),n=e.value.substring(e.selectionStart,e.selectionEnd),s=document.createElement("div");s.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const a=Ce("/about or https://example.com",n.startsWith("/")?n:"");s.appendChild(W("URL",a));const m=Ce("Button label",n&&!n.startsWith("/")?n:"");s.appendChild(W("Label",m));const y=Be([["primary","Primary"],["secondary","Secondary"],["success","Success (Green)"],["danger","Danger (Red)"],["warning","Warning (Orange)"],["info","Info (Blue)"],["outline","Outline"],["ghost","Ghost"],["link","Link"],["outline-success","Outline Success"],["outline-danger","Outline Danger"]]);s.appendChild(W("Variant",y));const h=Be([["","Default"],["sm","Small"],["lg","Large"]]);s.appendChild(W("Size",h));const x=Oe("e.g. arrow-right, star (optional)","");s.appendChild(W("Icon (before label)",x.el));const r=Oe("e.g. arrow-right (optional)","");s.appendChild(W("Icon (after label)",r.el));const b=He("Open in new tab");s.appendChild(b);const v=b.querySelector("input"),f=document.createElement("div");f.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:auto;padding-top:.75rem;border-top:1px solid var(--dm-border,#333);";const p=document.createElement("button");p.type="button",p.className="btn btn-ghost",p.textContent="Cancel";const c=document.createElement("button");c.type="button",c.className="btn btn-primary",c.textContent="Insert Button",f.appendChild(p),f.appendChild(c),s.appendChild(f);const C=E.slideover({title:"Insert Button",size:"md",position:"right",content:s});C.open(),I.scan(s),p.addEventListener("click",()=>C.close()),requestAnimationFrame(()=>{E.autocomplete(a,{data:t,minChars:1,onSelect:B=>{a.value=B.value,m.value||(m.value=B.label)}}),a.focus()}),c.addEventListener("click",()=>{const B=a.value.trim();if(!B)return;C.close();const N=m.value.trim()||B;let A=`href="${B}" variant="${y.value}"`;h.value&&(A+=` size="${h.value}"`);const w=x.input.value.trim().replace(/"/g,"");w&&(A+=` icon="${w}"`);const k=r.input.value.trim().replace(/"/g,"");k&&(A+=` icon-after="${k}"`),v.checked&&(A+=' target="_blank"'),xe(e,`[button ${A}]${N}[/button]`),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),M.onLinkShortcode(async e=>{const t=(await _.pages.list().catch(()=>[])).map(C=>({label:C.title||C.urlPath,value:C.urlPath})),n=e.value.substring(e.selectionStart,e.selectionEnd),s=document.createElement("div");s.style.cssText="padding:1rem;display:flex;flex-direction:column;gap:.75rem;";const a=Ce("/about or https://example.com",n.startsWith("/")?n:"");s.appendChild(W("URL",a));const m=Ce("Link text",n&&!n.startsWith("/")?n:"");s.appendChild(W("Link text",m));const y=Ce("e.g. text-primary, fw-bold (optional)","");s.appendChild(W("CSS class",y));const h=Oe("e.g. arrow-right (optional)","");s.appendChild(W("Icon (before text)",h.el));const x=Oe("e.g. external-link (optional)","");s.appendChild(W("Icon (after text)",x.el));const r=He("Open in new tab");s.appendChild(r);const b=r.querySelector("input"),v=document.createElement("div");v.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:auto;padding-top:.75rem;border-top:1px solid var(--dm-border,#333);";const f=document.createElement("button");f.type="button",f.className="btn btn-ghost",f.textContent="Cancel";const p=document.createElement("button");p.type="button",p.className="btn btn-primary",p.textContent="Insert Link",v.appendChild(f),v.appendChild(p),s.appendChild(v);const c=E.slideover({title:"Insert Link Shortcode",size:"md",position:"right",content:s});c.open(),I.scan(s),f.addEventListener("click",()=>c.close()),requestAnimationFrame(()=>{E.autocomplete(a,{data:t,minChars:1,onSelect:C=>{a.value=C.value,m.value||(m.value=C.label)}}),a.focus()}),p.addEventListener("click",()=>{const C=a.value.trim();if(!C)return;c.close();const B=m.value.trim()||C;let N=`href="${C}"`;const A=y.value.trim().replace(/"/g,"");A&&(N+=` class="${A}"`);const w=h.input.value.trim().replace(/"/g,"");w&&(N+=` icon="${w}"`);const k=x.input.value.trim().replace(/"/g,"");k&&(N+=` icon-after="${k}"`),b.checked&&(N+=' target="_blank"'),xe(e,`[link ${N}]${B}[/link]`),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),M.onTabs(e=>{const t=document.createElement("div");t.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const n=Be([["","Default"],["pills","Pills"],["bordered","Bordered"]]);t.appendChild(W("Style",n));const s=Be([["","Left"],["center","Centre"],["right","Right"]]);t.appendChild(W("Alignment",s));const a=Be([["","None"],["fade","Fade"]]);t.appendChild(W("Animation",a));const m=He("Allow multiple panels open at once"),y=m.querySelector("input");t.appendChild(m);const h=document.createElement("div");h.style.cssText="display:block;font-size:.7rem;font-weight:600;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",h.textContent="Tabs",t.appendChild(h);const x=document.createElement("div");x.style.cssText="display:flex;flex-direction:column;gap:.4rem;";const r=[];function b(){r.forEach(A=>{A.removeBtn.disabled=r.length<=2,A.removeBtn.style.opacity=r.length<=2?"0.3":"1"})}function v(){const A=n.value,w=s.value,k=a.value,T=y.checked,z=[];A&&z.push(`style="${A}"`),w&&z.push(`align="${w}"`),k&&z.push(k),T&&z.push('multiple="true"');let u=`[tabs${z.length?" "+z.join(" "):""}]
|
|
90
90
|
`;return r.forEach((g,l)=>{const i=g.titleInput.value.trim()||`Tab ${l+1}`,d=g.iconInput.value.trim(),j=d?` title="${i}" icon="${d}"`:` title="${i}"`;u+=`[tab${j}]Tab content here.[/tab]
|
|
91
|
-
`}),u+="[/tabs]",u}function f(A=""){if(r.length>=10)return;const w=document.createElement("div");w.style.cssText="display:flex;gap:.4rem;align-items:center;";const k=Ce("Tab title",A);k.style.cssText+="flex:1;";const T=Oe("icon (optional)");T.el.style.flex=".6";const z=document.createElement("button");z.type="button",z.textContent="\xD7",z.style.cssText="padding:.3rem .5rem;background:transparent;border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text-muted,#aaa);cursor:pointer;font-size:.85em;flex-shrink:0;";const
|
|
91
|
+
`}),u+="[/tabs]",u}function f(A=""){if(r.length>=10)return;const w=document.createElement("div");w.style.cssText="display:flex;gap:.4rem;align-items:center;";const k=Ce("Tab title",A);k.style.cssText+="flex:1;";const T=Oe("icon (optional)");T.el.style.flex=".6";const z=document.createElement("button");z.type="button",z.textContent="\xD7",z.style.cssText="padding:.3rem .5rem;background:transparent;border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text-muted,#aaa);cursor:pointer;font-size:.85em;flex-shrink:0;";const q={row:w,titleInput:k,iconInput:T.input,removeBtn:z};r.push(q),w.appendChild(k),w.appendChild(T.el),w.appendChild(z),x.appendChild(w),z.addEventListener("click",()=>{if(r.length<=2)return;const u=r.indexOf(q);r.splice(u,1),x.removeChild(w),b(),c(),C.update(v())}),[k,T.input].forEach(u=>u.addEventListener("input",()=>C.update(v()))),b()}f("Overview"),f("Details"),t.appendChild(x);const p=document.createElement("button");p.type="button",p.textContent="+ Add Tab",p.style.cssText="padding:.35rem .75rem;background:transparent;border:1px dashed var(--dm-border,#555);border-radius:4px;color:var(--dm-text-muted,#aaa);cursor:pointer;font-size:.85em;align-self:flex-start;",p.addEventListener("click",()=>{f(""),c(),r[r.length-1].titleInput.focus(),C.update(v())}),t.appendChild(p);function c(){p.disabled=r.length>=10}const C=Ue();C.update(v()),t.appendChild(W("Shortcode preview",C.el)),[n,s,a].forEach(A=>A.addEventListener("change",()=>C.update(v()))),y.addEventListener("change",()=>C.update(v()));const B=document.createElement("button");B.type="button",B.className="btn btn-primary",B.textContent="Insert Tabs",t.appendChild(B);const N=E.slideover({title:"Insert Tabs",size:"md",position:"right",content:t});N.open(),requestAnimationFrame(()=>r[0].titleInput.focus()),B.addEventListener("click",()=>{xe(e,v()),N.close(),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),M.onHero(e=>{const t=document.createElement("div");t.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const n=Ce("Hero title");t.appendChild(W("Title",n));const s=Ce("Tagline (optional)");t.appendChild(W("Tagline",s));const a=Be([["sm","Small"],["md","Medium (default)"],["lg","Large"],["full","Full"]]);a.value="md",t.appendChild(W("Size",a));const m=Be([["","Left"],["center","Centre"],["right","Right"]]);t.appendChild(W("Alignment",m));const y=document.createElement("div");y.style.cssText="display:flex;gap:.75rem;align-items:center;";const h={};["none","colour","image"].forEach(T=>{const z=document.createElement("label");z.style.cssText="display:flex;align-items:center;gap:.3rem;font-size:.9em;cursor:pointer;";const q=document.createElement("input");q.type="radio",q.name="hero-bg",q.value=T,T==="none"&&(q.checked=!0),h[T]=q,z.appendChild(q),z.appendChild(document.createTextNode(T.charAt(0).toUpperCase()+T.slice(1))),y.appendChild(z)}),t.appendChild(W("Background",y));const x=Ce("#hex or CSS colour"),r=W("Colour",x);r.style.display="none",t.appendChild(r);const b=Ce("/media/hero.jpg"),v=Be([["","None"],["dark","Dark"],["light","Light"]]),f=document.createElement("div");f.style.cssText="display:none;flex-direction:column;gap:.5rem;",f.appendChild(W("Image path",b)),f.appendChild(W("Overlay",v)),t.appendChild(f);const p=Ce("e.g. 400px");t.appendChild(W("Min height",p));const c=He("Full-width (break out of page container to span viewport)");t.appendChild(c);const C=c.querySelector("input");function B(){const T=n.value.trim(),z=s.value.trim(),q=a.value,u=m.value,g=Object.keys(h).find(ee=>h[ee].checked)||"none",l=x.value.trim(),i=b.value.trim(),d=v.value,j=p.value.trim(),Z=C.checked,J=[`title="${T}"`];return z&&J.push(`tagline="${z}"`),q&&q!=="md"&&J.push(`size="${q}"`),u&&J.push(`align="${u}"`),g==="colour"&&l&&J.push(`color="${l}"`),g==="image"&&i&&J.push(`image="${i}"`),g==="image"&&d&&J.push(`overlay="${d}"`),j&&J.push(`min-height="${j}"`),Z&&J.push('fullwidth="true"'),`[hero ${J.join(" ")}]
|
|
92
92
|
Content here.
|
|
93
|
-
[/hero]`}const N=Ue();N.update(B()),t.appendChild(W("Shortcode preview",N.el));function A(){const T=Object.keys(h).find(z=>h[z].checked)||"none";r.style.display=T==="colour"?"":"none",f.style.display=T==="image"?"flex":"none",N.update(B())}Object.values(h).forEach(T=>T.addEventListener("change",A)),[n,s,x,b,p].forEach(T=>T.addEventListener("input",()=>N.update(B()))),[a,m,v].forEach(T=>T.addEventListener("change",()=>N.update(B()))),C.addEventListener("change",()=>N.update(B()));const w=document.createElement("button");w.type="button",w.className="btn btn-primary",w.textContent="Insert Hero",t.appendChild(w);const k=E.slideover({title:"Insert Hero",size:"md",position:"right",content:t});k.open(),requestAnimationFrame(()=>n.focus()),w.addEventListener("click",()=>{xe(e,B()),k.close(),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),
|
|
93
|
+
[/hero]`}const N=Ue();N.update(B()),t.appendChild(W("Shortcode preview",N.el));function A(){const T=Object.keys(h).find(z=>h[z].checked)||"none";r.style.display=T==="colour"?"":"none",f.style.display=T==="image"?"flex":"none",N.update(B())}Object.values(h).forEach(T=>T.addEventListener("change",A)),[n,s,x,b,p].forEach(T=>T.addEventListener("input",()=>N.update(B()))),[a,m,v].forEach(T=>T.addEventListener("change",()=>N.update(B()))),C.addEventListener("change",()=>N.update(B()));const w=document.createElement("button");w.type="button",w.className="btn btn-primary",w.textContent="Insert Hero",t.appendChild(w);const k=E.slideover({title:"Insert Hero",size:"md",position:"right",content:t});k.open(),requestAnimationFrame(()=>n.focus()),w.addEventListener("click",()=>{xe(e,B()),k.close(),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),M.onBanner(e=>{const t=document.createElement("div");t.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const n=[{value:"info",label:"Info",color:"#3b82f6"},{value:"success",label:"Success",color:"#22c55e"},{value:"warning",label:"Warning",color:"#f59e0b"},{value:"danger",label:"Danger",color:"#ef4444"},{value:"neutral",label:"Neutral",color:"#888"}];let s="info";const a=document.createElement("div");a.style.cssText="display:flex;gap:.35rem;flex-wrap:wrap;";const m={};n.forEach(c=>{const C=document.createElement("button");C.type="button",C.style.cssText="display:flex;align-items:center;gap:.3rem;padding:.3rem .6rem;border:1px solid var(--dm-border,#333);border-radius:4px;background:transparent;color:var(--dm-text,#eee);cursor:pointer;font-size:.85em;";const B=document.createElement("span");B.style.cssText=`display:inline-block;width:.55rem;height:.55rem;border-radius:50%;background:${c.color};flex-shrink:0;`,C.appendChild(B),C.appendChild(document.createTextNode(c.label)),m[c.value]=C,a.appendChild(C),C.addEventListener("click",()=>{s=c.value,Object.entries(m).forEach(([N,A])=>{A.style.borderColor=N===s?c.color:"var(--dm-border,#333)",A.style.background=N===s?`${c.color}22`:"transparent"}),v.update(b())})}),m.info.style.borderColor="#3b82f6",m.info.style.background="#3b82f622",t.appendChild(W("Type",a));const y=Ce("Title (optional)");t.appendChild(W("Title",y));const h=Oe("Icon name (optional)");t.appendChild(W("Icon",h.el));const x=He("Dismissible"),r=x.querySelector("input");t.appendChild(x);function b(){const c=[];s!=="info"&&c.push(`type="${s}"`);const C=y.value.trim();C&&c.push(`title="${C}"`);const B=h.input.value.trim();return B&&c.push(`icon="${B}"`),r.checked&&c.push("dismissible"),`[banner${c.length?" "+c.join(" "):""}]
|
|
94
94
|
Banner content here.
|
|
95
|
-
[/banner]`}const v=Ue();v.update(b()),t.appendChild(W("Shortcode preview",v.el)),[y,h.input].forEach(c=>c.addEventListener("input",()=>v.update(b()))),r.addEventListener("change",()=>v.update(b()));const f=document.createElement("button");f.type="button",f.className="btn btn-primary",f.textContent="Insert Banner",t.appendChild(f);const p=E.slideover({title:"Insert Banner",size:"md",position:"right",content:t});p.open(),requestAnimationFrame(()=>y.focus()),f.addEventListener("click",()=>{xe(e,b()),p.close(),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),
|
|
95
|
+
[/banner]`}const v=Ue();v.update(b()),t.appendChild(W("Shortcode preview",v.el)),[y,h.input].forEach(c=>c.addEventListener("input",()=>v.update(b()))),r.addEventListener("change",()=>v.update(b()));const f=document.createElement("button");f.type="button",f.className="btn btn-primary",f.textContent="Insert Banner",t.appendChild(f);const p=E.slideover({title:"Insert Banner",size:"md",position:"right",content:t});p.open(),requestAnimationFrame(()=>y.focus()),f.addEventListener("click",()=>{xe(e,b()),p.close(),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),M.onScribe(e=>ot(e)),M.onGrid(e=>{const t=document.createElement("div");t.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const n="display:block;font-size:.85em;font-weight:600;margin-bottom:.25rem;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",s="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 a(J,ee){const P=document.createElement("div"),F=document.createElement("label");return F.style.cssText=n,F.textContent=J,P.appendChild(F),P.appendChild(ee),P}function m(J){const ee=document.createElement("select");return ee.style.cssText=s,J.forEach(([P,F])=>{const O=document.createElement("option");O.value=P,O.textContent=F,ee.appendChild(O)}),ee}let y="grid";const h=document.createElement("div");h.style.cssText="display:flex;gap:.5rem;";const x=document.createElement("button");x.type="button",x.className="btn btn-sm btn-primary",x.textContent="Grid";const r=document.createElement("button");r.type="button",r.className="btn btn-sm btn-ghost",r.textContent="Row",h.appendChild(x),h.appendChild(r),t.appendChild(h);const b=document.createElement("input");b.type="number",b.min="1",b.max="12",b.value="2",b.style.cssText=s,t.appendChild(a("Columns",b));const v=m([["0","0"],["1","1"],["2","2"],["3","3"],["4","4 (default)"],["6","6"],["8","8"],["10","10"],["12","12"],["16","16"]]);v.value="4",t.appendChild(a("Gap",v));const f=document.createElement("div");f.style.cssText="display:flex;flex-direction:column;gap:.5rem;";const p=document.createElement("label");p.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.9em;cursor:pointer;";const c=document.createElement("input");c.type="checkbox",p.appendChild(c),p.appendChild(document.createTextNode("Full width (breakout)")),f.appendChild(p);const C=document.createElement("div");C.style.cssText=n+"margin-top:.25rem;",C.textContent="Column Spans",f.appendChild(C);const B=document.createElement("div");B.style.cssText="display:flex;flex-direction:column;gap:.3rem;",f.appendChild(B),t.appendChild(f);const N=[];function A(){const J=Math.min(Math.max(parseInt(b.value)||2,1),12),ee=N.map(P=>P.value);B.textContent="",N.length=0;for(let P=0;P<J;P++){const F=document.createElement("div");F.style.cssText="display:flex;align-items:center;gap:.5rem;";const O=document.createElement("span");O.style.cssText="font-size:.85em;color:var(--dm-text-muted,#aaa);white-space:nowrap;min-width:3em;",O.textContent=`Col ${P+1}`;const Q=document.createElement("input");Q.type="number",Q.min="1",Q.max="12",Q.placeholder="auto",Q.value=ee[P]||"",Q.style.cssText=s+"width:5em;flex:none;",F.appendChild(O),F.appendChild(Q),B.appendChild(F),N.push(Q)}}b.addEventListener("input",A),A();const w=document.createElement("div");w.style.cssText="display:none;flex-direction:column;gap:.5rem;";const k=document.createElement("label");k.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.9em;cursor:pointer;";const T=document.createElement("input");T.type="checkbox",k.appendChild(T),k.appendChild(document.createTextNode("Enable staggered reveal")),w.appendChild(k);const z=document.createElement("div");z.style.cssText="display:none;flex-direction:column;gap:.5rem;padding-left:.5rem;border-left:2px solid var(--dm-border,#333);";const q=m([["slide-up","Slide Up"],["slide-down","Slide Down"],["slide-left","Slide Left"],["slide-right","Slide Right"],["fade","Fade"],["zoom","Zoom"],["flip","Flip"]]);z.appendChild(a("Animation",q));const u=m([["stagger","Stagger"],["sequence","Sequence"]]);z.appendChild(a("Mode",u));const g=document.createElement("input");g.type="number",g.value="400",g.style.cssText=s,z.appendChild(a("Duration (ms)",g));const l=document.createElement("input");l.type="number",l.value="60",l.style.cssText=s,z.appendChild(a("Stagger (ms)",l));const i=document.createElement("input");i.type="number",i.value="0",i.style.cssText=s,z.appendChild(a("Delay (ms)",i));const d=m([["ltr","Left to Right"],["rtl","Right to Left"]]);z.appendChild(a("Direction",d)),w.appendChild(z),t.appendChild(w),T.addEventListener("change",()=>{z.style.display=T.checked?"flex":"none"});const j=document.createElement("button");j.type="button",j.className="btn btn-primary",j.textContent="Insert Grid",t.appendChild(j),x.addEventListener("click",()=>{y="grid",x.className="btn btn-sm btn-primary",r.className="btn btn-sm btn-ghost",f.style.display="flex",w.style.display="none",j.textContent="Insert Grid"}),r.addEventListener("click",()=>{y="row",r.className="btn btn-sm btn-primary",x.className="btn btn-sm btn-ghost",w.style.display="flex",f.style.display="none",j.textContent="Insert Row"});const Z=E.slideover({title:"Insert Grid / Row",size:"md",position:"right",content:t});Z.open(),requestAnimationFrame(()=>b.focus()),j.addEventListener("click",()=>{Z.close();const J=Math.min(Math.max(parseInt(b.value)||2,1),12),ee=v.value;let P;if(y==="grid"){let F=`cols="${J}"`;ee!=="4"&&(F+=` gap="${ee}"`),c.checked&&(F+=' fullwidth="true"'),P=`[grid ${F}]
|
|
96
96
|
`;for(let O=0;O<J;O++){const Q=N[O]?.value?.trim(),H=Q?` span="${Q}"`:"";P+=`[col${H}]
|
|
97
97
|
Column ${O+1}
|
|
98
98
|
[/col]
|
|
99
|
-
`}P+="[/grid]"}else{let F="";if(ee!=="4"&&(F+=` gap="${ee}"`),T.checked){F+=" reveal",F+=` reveal-mode="${u.value}"`,F+=` reveal-animation="${
|
|
99
|
+
`}P+="[/grid]"}else{let F="";if(ee!=="4"&&(F+=` gap="${ee}"`),T.checked){F+=" reveal",F+=` reveal-mode="${u.value}"`,F+=` reveal-animation="${q.value}"`;const O=g.value.trim();O&&O!=="400"&&(F+=` reveal-duration="${O}"`);const Q=l.value.trim();Q&&Q!=="60"&&(F+=` reveal-stagger="${Q}"`);const H=i.value.trim();H&&H!=="0"&&(F+=` reveal-delay="${H}"`),d.value!=="ltr"&&(F+=` reveal-direction="${d.value}"`)}P=`[row${F?" "+F.trim():""}]
|
|
100
100
|
`;for(let O=0;O<J;O++)P+=`[col]Column ${O+1}[/col]
|
|
101
|
-
`;P+="[/row]"}xe(e,P),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),
|
|
101
|
+
`;P+="[/row]"}xe(e,P),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),M.onAccordion(async e=>{const t=await _.collections.list().catch(()=>[]),n=document.createElement("div");n.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const s="display:block;font-size:.85em;font-weight:600;margin-bottom:.25rem;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",a="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;",m=document.createElement("div");m.style.cssText="display:flex;gap:.5rem;";const y=document.createElement("button");y.type="button",y.className="btn btn-sm btn-primary",y.textContent="Static";const h=document.createElement("button");h.type="button",h.className="btn btn-sm btn-ghost",h.textContent="Collection",m.appendChild(y),m.appendChild(h),n.appendChild(m);let x="static";const r=document.createElement("div");r.style.cssText="display:flex;flex-direction:column;gap:.5rem;";const b=[];function v(P){const F=document.createElement("div");F.style.cssText="display:flex;align-items:center;gap:.5rem;";const O=document.createElement("label");O.style.cssText="font-size:.85em;color:var(--dm-text-muted,#aaa);white-space:nowrap;",O.textContent=`Item ${P}`;const Q=document.createElement("input");return Q.type="text",Q.placeholder="Item title",Q.style.cssText=a,F.appendChild(O),F.appendChild(Q),{row:F,input:Q}}[1,2].forEach(P=>{const F=v(P);b.push(F),r.appendChild(F.row)});const f=document.createElement("button");f.type="button",f.className="btn btn-ghost btn-sm",f.textContent="+ Add Item",f.addEventListener("click",()=>{const P=v(b.length+1);b.push(P),r.insertBefore(P.row,f)}),r.appendChild(f);const p=document.createElement("label");p.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.9em;cursor:pointer;";const c=document.createElement("input");c.type="checkbox",p.appendChild(c),p.appendChild(document.createTextNode("Allow multiple open")),r.appendChild(p),n.appendChild(r);const C=document.createElement("div");C.style.cssText="display:none;flex-direction:column;gap:.5rem;";const B=document.createElement("div"),N=document.createElement("label");N.style.cssText=s,N.textContent="Collection";const A=document.createElement("select");if(A.style.cssText=a,t.length)t.forEach(P=>{const F=document.createElement("option");F.value=P.slug,F.textContent=P.title||P.slug,A.appendChild(F)});else{const P=document.createElement("option");P.value="",P.textContent="No collections found",A.appendChild(P)}B.appendChild(N),B.appendChild(A),C.appendChild(B);const w=document.createElement("div"),k=document.createElement("label");k.style.cssText=s,k.textContent="Title field";const T=document.createElement("select");T.style.cssText=a,w.appendChild(k),w.appendChild(T),C.appendChild(w);const z=document.createElement("div"),q=document.createElement("label");q.style.cssText=s,q.textContent="Body field";const u=document.createElement("select");u.style.cssText=a,z.appendChild(q),z.appendChild(u),C.appendChild(z);async function g(P){T.textContent="",u.textContent="";try{const F=(await _.collections.get(P)).fields||[];if(!F.length){const O=document.createElement("option");O.value="",O.textContent="\u2014 none \u2014",T.appendChild(O.cloneNode(!0)),u.appendChild(O);return}F.forEach((O,Q)=>{const H=()=>{const te=document.createElement("option");return te.value=O.name,te.textContent=O.label||O.name,te};T.appendChild(H()),u.appendChild(H()),O.name==="title"&&(T.value="title"),O.name==="description"&&(u.value="description")}),!T.value&&F[0]&&(T.value=F[0].name),!u.value&&F[1]&&(u.value=F[1].name)}catch{const F=document.createElement("option");F.value="",F.textContent="\u2014 unknown \u2014",T.appendChild(F.cloneNode(!0)),u.appendChild(F)}}t.length&&g(t[0].slug),A.addEventListener("change",()=>g(A.value));const l=document.createElement("div"),i=document.createElement("label");i.style.cssText=s,i.textContent="Limit (optional)";const d=document.createElement("input");d.type="number",d.placeholder="All",d.style.cssText=a,l.appendChild(i),l.appendChild(d),C.appendChild(l);const j=document.createElement("label");j.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.9em;cursor:pointer;";const Z=document.createElement("input");Z.type="checkbox",j.appendChild(Z),j.appendChild(document.createTextNode("Allow multiple open")),C.appendChild(j),n.appendChild(C),y.addEventListener("click",()=>{x="static",y.className="btn btn-sm btn-primary",h.className="btn btn-sm btn-ghost",r.style.display="flex",C.style.display="none"}),h.addEventListener("click",()=>{x="collection",h.className="btn btn-sm btn-primary",y.className="btn btn-sm btn-ghost",C.style.display="flex",r.style.display="none"});const J=document.createElement("button");J.type="button",J.className="btn btn-primary",J.textContent="Insert Accordion",n.appendChild(J);const ee=E.slideover({title:"Insert Accordion",size:"md",position:"right",content:n});ee.open(),requestAnimationFrame(()=>b[0].input.focus()),J.addEventListener("click",()=>{if(x==="collection"&&!A.value)return;ee.close();let P;if(x==="static")P=`[accordion${c.checked?' multiple="true"':""}]
|
|
102
102
|
`,b.forEach((F,O)=>{const Q=F.input.value.trim()||`Item ${O+1}`;P+=`[item title="${Q}"]Content here.[/item]
|
|
103
|
-
`}),P+="[/accordion]";else{const F=A.value;if(!F)return;const O=T.value,Q=u.value;let H=`slug="${F}" display="accordion"`;O&&(H+=` title-field="${O}"`),Q&&(H+=` body-field="${Q}"`);const te=d.value.trim();te&&(H+=` limit="${te}"`),Z.checked&&(H+=' multiple="true"'),P=`[collection ${H} /]`}xe(e,P),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),
|
|
103
|
+
`}),P+="[/accordion]";else{const F=A.value;if(!F)return;const O=T.value,Q=u.value;let H=`slug="${F}" display="accordion"`;O&&(H+=` title-field="${O}"`),Q&&(H+=` body-field="${Q}"`);const te=d.value.trim();te&&(H+=` limit="${te}"`),Z.checked&&(H+=' multiple="true"'),P=`[collection ${H} /]`}xe(e,P),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),M.onListGroup(e=>{const t=document.createElement("div");t.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const n="display:block;font-size:.85em;font-weight:600;margin-bottom:.25rem;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;",s="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 a(w,k){const T=document.createElement("div"),z=document.createElement("label");return z.style.cssText=n,z.textContent=w,T.appendChild(z),T.appendChild(k),T}function m(w){const k=document.createElement("select");return k.style.cssText=s,w.forEach(([T,z])=>{const q=document.createElement("option");q.value=T,q.textContent=z,k.appendChild(q)}),k}const y=m([["","Default"],["flush","Flush (no outer border)"],["numbered","Numbered"],["horizontal","Horizontal"]]);t.appendChild(a("Variant",y));const h=m([["","Default"],["sm","Small"],["lg","Large"]]);t.appendChild(a("Size",h));const x=m([["","Default"],["primary","Primary"],["secondary","Secondary"],["success","Success"],["danger","Danger"],["warning","Warning"],["info","Info"]]);t.appendChild(a("Item Colour",x));const r=document.createElement("label");r.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.9em;cursor:pointer;";const b=document.createElement("input");b.type="checkbox",r.appendChild(b),r.appendChild(document.createTextNode("Actionable items (hover highlight)")),t.appendChild(r);const v=document.createElement("div");v.style.cssText=n+"margin-bottom:.35rem;",v.textContent="Items",t.appendChild(v);const f=document.createElement("div");f.style.cssText="display:flex;flex-direction:column;gap:.4rem;",t.appendChild(f);const p=[];function c(){p.forEach(w=>{w.removeBtn.disabled=p.length<=1,w.removeBtn.style.opacity=p.length<=1?"0.3":"1"})}function C(w){const k=document.createElement("div");k.style.cssText="display:flex;gap:.4rem;align-items:center;";const T=document.createElement("input");T.type="text",T.placeholder="Item text",T.value=w||"",T.style.cssText=s+"flex:1;";const z=document.createElement("button");z.type="button",z.textContent="\u2715",z.style.cssText="padding:.3rem .5rem;background:transparent;border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text-muted,#aaa);cursor:pointer;font-size:.85em;flex-shrink:0;";const q={row:k,input:T,removeBtn:z};p.push(q),k.appendChild(T),k.appendChild(z),f.appendChild(k),z.addEventListener("click",()=>{if(p.length<=1)return;const u=p.indexOf(q);p.splice(u,1),f.removeChild(k),c()}),c()}C("Item 1"),C("Item 2"),C("Item 3");const B=document.createElement("button");B.type="button",B.textContent="+ Add Item",B.style.cssText="padding:.35rem .75rem;background:transparent;border:1px dashed var(--dm-border,#555);border-radius:4px;color:var(--dm-text-muted,#aaa);cursor:pointer;font-size:.85em;align-self:flex-start;",B.addEventListener("click",()=>{p.length>=20||(C(""),p[p.length-1].input.focus())}),t.appendChild(B);const N=document.createElement("button");N.type="button",N.className="btn btn-primary",N.textContent="Insert List Group",t.appendChild(N);const A=E.slideover({title:"Insert List Group",size:"md",position:"right",content:t});A.open(),requestAnimationFrame(()=>p[0].input.focus()),N.addEventListener("click",()=>{A.close();let w="";y.value&&(w+=` variant="${y.value}"`),h.value&&(w+=` size="${h.value}"`),x.value&&(w+=` itemvariant="${x.value}"`),b.checked&&(w+=' action="true"');let k=`[listgroup${w}]
|
|
104
104
|
`;p.forEach(T=>{const z=T.input.value.trim();z&&(k+=`[item]${z}[/item]
|
|
105
|
-
`)}),k+="[/listgroup]",xe(e,k),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),
|
|
105
|
+
`)}),k+="[/listgroup]",xe(e,k),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()})}),M.onCard(e=>{Ke.open(e,{onMediaBrowse:()=>_.media.list().catch(()=>[])})}),M.onTimeline(e=>{Ze.open(e,{onPreview:async t=>{try{return await _.pages.preview(t)}catch{return null}},onListCollections:()=>_.collections.list().catch(()=>[]),onGetCollection:t=>_.collections.get(t).catch(()=>null)})}),M.onEffects(e=>tt.open(e)),M.onHelp(()=>{lt()}),o.find(".editor-view-btn").on("click",function(){const e=$(this).data("mode");e&&(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}`),Qe("viewMode",e))}),o.find("#fullscreen-btn").on("click",function(){o.find(".editor-card").toggleClass("editor-fullscreen"),Qe("fullscreen",o.find(".editor-card").hasClass("editor-fullscreen"))}),Re=!1,o.find("#quick-switch").prop("disabled",!1),Ge&&window.removeEventListener("beforeunload",Ge),Ge=e=>{Re&&e.preventDefault()},window.addEventListener("beforeunload",Ge),Ye||(Ye=!0,R.use((e,t,n)=>{const s=window.location.hash.startsWith("#/pages/edit")||window.location.hash==="#/pages/new";if(!Re||!s)return n();E.confirm("You have unsaved changes. Leave this page?").then(a=>{a&&(Re=!1,n())})})),me.on("input",()=>{ne||(Re=!0,o.find("#quick-switch").prop("disabled",!0),V())});const Pe=o.find("#editor-meta-tabs").get(0);Pe&&(Pe.addEventListener("input",()=>{Re=!0,o.find("#quick-switch").prop("disabled",!0)}),Pe.addEventListener("change",e=>{e.target.id!=="quick-switch"&&(Re=!0,o.find("#quick-switch").prop("disabled",!0))})),V(),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 t=o.find("#field-dconfig").val().trim();let n=null;if(t)try{n=JSON.parse(t)}catch{E.toast("DConfig JSON is invalid. Please check the format before saving.",{type:"warning"});return}const s={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},...o.find("#field-bundled").is(":checked")?{bundled:!0}:{},tags:we?we.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:n,...o.find("#field-theme").val()?{theme:o.find("#field-theme").val()}:{}};try{o.find("#save-btn").prop("disabled",!0).text("Saving\u2026");const a=ae.getUnfoldedContent();if(G){const m={frontmatter:s,body:a};e!==de&&(m.newUrlPath=e),await _.pages.update(de,m),oe&&await _.settings.saveCustomCss(o.find("#css-editor").val()),E.toast(oe?"Page and CSS saved.":"Page saved successfully.",{type:"success"}),Re=!1,o.find("#quick-switch").prop("disabled",!1),e!==de&&R.navigate(`/pages/edit${e}`)}else await _.pages.create({urlPath:e,frontmatter:s,body:a}),oe&&await _.settings.saveCustomCss(o.find("#css-editor").val()),E.toast(oe?"Page and CSS saved.":"Page saved successfully.",{type:"success"}),Re=!1,R.navigate("/pages")}catch(a){E.toast(`Save failed: ${a.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.
|
|
3
|
+
"version": "0.15.0",
|
|
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",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"@fastify/rate-limit": "^10.3.0",
|
|
74
74
|
"@fastify/static": "9.1.1",
|
|
75
75
|
"bcryptjs": "^3.0.3",
|
|
76
|
-
"domma-js": "^0.
|
|
76
|
+
"domma-js": "^0.23.0",
|
|
77
77
|
"dotenv": "^17.2.3",
|
|
78
78
|
"fastify": "5.8.5",
|
|
79
79
|
"gray-matter": "^4.0.3",
|
package/public/js/effects.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(){"use strict";fetch("/api/effects/settings").then(function(
|
|
1
|
+
(function(){"use strict";fetch("/api/effects/settings").then(function(h){return h.json()}).then(function(h){var A=h.respectMotion!==!1,E=h.defaultDuration||600,z=h.defaultAnimation||"fade",S=h.defaultThreshold!=null?h.defaultThreshold:.1,m=A&&window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches,u=document.querySelector(".page-body");if(!u)return;function r(t,e){return t.getAttribute("data-fx-"+e)}function a(t,e,n){var i=r(t,e);return i!==null?parseFloat(i):n}function y(t,e,n){var i=r(t,e);return i===null?n:i==="true"||i==="1"}function g(t,e,n){var i=new IntersectionObserver(function(o){o[0].isIntersecting&&(i.unobserve(t),n())},{threshold:e});i.observe(t)}u.querySelectorAll(".dm-fx-reveal").forEach(function(t){var e=r(t,"animation")||z,n=a(t,"duration",E),i=a(t,"delay",0),o=a(t,"threshold",S),f=y(t,"once",!0);m||(t.style.opacity="0",t.style.transition="none",e==="slide-up"?t.style.transform="translateY(30px)":e==="slide-down"?t.style.transform="translateY(-30px)":e==="zoom"?t.style.transform="scale(0.85)":e==="flip"&&(t.style.transform="rotateY(90deg)"),requestAnimationFrame(function(){requestAnimationFrame(function(){t.style.transition="opacity "+n+"ms ease "+i+"ms, transform "+n+"ms ease "+i+"ms";var s=new IntersectionObserver(function(c){c.forEach(function(v){v.isIntersecting?(t.style.opacity="1",t.style.transform="",f&&s.unobserve(t)):f||(t.style.opacity="0",e==="slide-up"?t.style.transform="translateY(30px)":e==="slide-down"?t.style.transform="translateY(-30px)":e==="zoom"?t.style.transform="scale(0.85)":e==="flip"?t.style.transform="rotateY(90deg)":t.style.transform="")})},{threshold:o});s.observe(t)})}))}),u.querySelectorAll(".dm-fx-counter").forEach(function(t){var e=a(t,"to",0),n=a(t,"from",0),i=a(t,"duration",2e3),o=r(t,"prefix")||"",f=r(t,"suffix")||"",s=a(t,"decimals",0),c=r(t,"separator")||"";function v(d){var x=d.toFixed(s);if(c){var l=x.split(".");l[0]=l[0].replace(/\B(?=(\d{3})+(?!\d))/g,c),x=l.join(".")}return o+x+f}if(m){t.textContent=v(e);return}g(t,.5,function(){var d=null;requestAnimationFrame(function x(l){d||(d=l);var w=Math.min((l-d)/i,1),q=1-Math.pow(1-w,3);t.textContent=v(n+(e-n)*q),w<1&&requestAnimationFrame(x)})})}),m||u.querySelectorAll(".dm-fx-scramble").forEach(function(t){var e=a(t,"speed",50),n=t.textContent,i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&",o=0;g(t,.3,function(){var f=setInterval(function(){for(var s="",c=0;c<n.length;c++)c<o||n[c]===" "?s+=n[c]:s+=i[Math.floor(Math.random()*i.length)];t.textContent=s,o<n.length?o++:clearInterval(f)},e)})}),m||u.querySelectorAll(".dm-fx-shake").forEach(function(t){var e=a(t,"intensity",5),n=a(t,"duration",500),i=r(t,"direction")||"horizontal",o=i==="vertical"?"translateY":"translateX";t.animate([{transform:o+"(0)"},{transform:o+"(-"+e+"px)"},{transform:o+"("+e+"px)"},{transform:o+"(-"+e+"px)"},{transform:o+"("+e+"px)"},{transform:o+"(0)"}],{duration:n,easing:"ease-in-out"})}),u.querySelectorAll(".dm-fx-ripple").forEach(function(t){t.style.position="relative",t.style.overflow="hidden",t.addEventListener("click",function(e){var n=r(t,"colour")||"rgba(255,255,255,0.3)",i=a(t,"duration",600),o=t.getBoundingClientRect(),f=Math.max(o.width,o.height)*2,s=document.createElement("span");s.style.cssText="position:absolute;border-radius:50%;pointer-events:none;width:"+f+"px;height:"+f+"px;left:"+(e.clientX-o.left-f/2)+"px;top:"+(e.clientY-o.top-f/2)+"px;background:"+n+";transform:scale(0);opacity:1;transition:transform "+i+"ms ease,opacity "+i+"ms ease;",t.appendChild(s),requestAnimationFrame(function(){s.style.transform="scale(1)",s.style.opacity="0"}),setTimeout(function(){s.remove()},i)})});var p=window.Domma&&window.Domma.effects;p&&(u.querySelectorAll(".dm-fx-breathe").forEach(function(t){if(!m){var e={};r(t,"amplitude")&&(e.amplitude=a(t,"amplitude",6)),r(t,"duration")&&(e.duration=a(t,"duration",3e3)),r(t,"easing")&&(e.easing=r(t,"easing")),r(t,"stagger")&&(e.stagger=a(t,"stagger",0)),p.breathe(t,e)}}),u.querySelectorAll(".dm-fx-pulse").forEach(function(t){if(!m){var e={};r(t,"scale")&&(e.scale=a(t,"scale",1.05)),r(t,"duration")&&(e.duration=a(t,"duration",2e3)),r(t,"easing")&&(e.easing=r(t,"easing")),p.pulse(t,e)}}),u.querySelectorAll(".dm-fx-scribe").forEach(function(t){var e={};r(t,"speed")&&(e.speed=a(t,"speed",50)),r(t,"delete-speed")&&(e.deleteSpeed=a(t,"delete-speed",30)),r(t,"cursor")&&(e.cursor=y(t,"cursor",!0)),r(t,"cursor-char")&&(e.cursorChar=r(t,"cursor-char")),r(t,"cursor-type")&&(e.cursorType=r(t,"cursor-type")),r(t,"mode")&&(e.mode=r(t,"mode")),r(t,"loop")&&(e.loop=y(t,"loop",!1)),r(t,"loop-delay")&&(e.loopDelay=a(t,"loop-delay",1e3)),r(t,"pause-on-hover")&&(e.pauseOnHover=y(t,"pause-on-hover",!1));var n=r(t,"actions");if(n)try{e.actions=JSON.parse(n)}catch{return}else{var i=t.textContent.trim();if(!i)return;t.textContent="",e.actions=[{render:i}]}p.scribe(t,e)}),u.querySelectorAll(".dm-fx-twinkle").forEach(function(t){var e={};r(t,"count")&&(e.count=a(t,"count",100)),r(t,"shape")&&(e.shape=r(t,"shape")),r(t,"colour")&&(e.colour=r(t,"colour")),r(t,"min-size")&&(e.minSize=a(t,"min-size",1)),r(t,"max-size")&&(e.maxSize=a(t,"max-size",3)),p.twinkle(t,e)}),typeof p.tickerTape=="function"&&u.querySelectorAll(".dm-fx-ticker-tape").forEach(function(t){if(!m){var e={};r(t,"palette")&&(e.palette=r(t,"palette")),r(t,"density")&&(e.density=a(t,"density",50)),r(t,"speed")&&(e.speed=a(t,"speed",1)),r(t,"sway")&&(e.sway=a(t,"sway",60)),r(t,"rotation-speed")&&(e.rotationSpeed=a(t,"rotation-speed",6)),r(t,"min-width")&&(e.minWidth=a(t,"min-width",5)),r(t,"max-width")&&(e.maxWidth=a(t,"max-width",9)),r(t,"min-height")&&(e.minHeight=a(t,"min-height",12)),r(t,"max-height")&&(e.maxHeight=a(t,"max-height",22)),r(t,"fade-start")&&(e.fadeStart=a(t,"fade-start",.55)),r(t,"burst")&&(e.burst=y(t,"burst",!1)),r(t,"burst-count")&&(e.burstCount=a(t,"burst-count",150)),r(t,"z-index")&&(e.zIndex=a(t,"z-index",1));var n=r(t,"mode");n==="page"?p.tickerTape(null,e):p.tickerTape(t,e)}}));var b=u.querySelectorAll(".dm-fx-celebrate");b.length&&!m&&import("/public/js/celebrations/index.js").then(function(t){var e=t.CelebrationsEffect;b.forEach(function(n){var i=n.getAttribute("data-fx-theme")||"auto",o=n.getAttribute("data-fx-intensity")||"medium",f=n.getAttribute("data-fx-z-index")?parseInt(n.getAttribute("data-fx-z-index"),10):999;if(!(i==="auto"&&(i=e.getCurrentTheme(),!i))){var s={},c=["data-fx-theme","data-fx-intensity","data-fx-z-index"];Array.from(n.attributes).forEach(function(d){d.name.startsWith("data-fx-")&&!c.includes(d.name)&&(s[d.name.slice(8)]=d.value)});var v=new e({theme:i,intensity:o,enabled:!0,zIndex:f,overrides:s});v.init().then(function(){v.start()})}})}).catch(function(){})}).catch(function(){})})();
|