domma-cms 1.10.22 → 1.10.24
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/CHANGELOG.md +10 -0
- package/admin/css/admin.css +1 -1
- package/admin/dist/domma/domma-tools.css +3 -3
- package/admin/dist/domma/domma-tools.min.js +3 -3
- package/admin/js/api.js +1 -1
- package/admin/js/lib/dconfig-check.js +3 -0
- package/admin/js/templates/page-editor.html +4 -0
- package/admin/js/views/page-editor.js +10 -10
- package/docs/markdown-shortcodes.md +4 -0
- package/docs/plugin-development.md +2 -1
- package/package.json +2 -2
- package/public/js/site.js +1 -1
- package/server/routes/api/pages.js +9 -0
- package/server/services/content.js +2 -15
- package/server/services/dommaCatalogue.js +24 -0
- package/server/services/hooks.js +24 -1
- package/server/templates/page.html +1 -1
|
@@ -407,6 +407,10 @@
|
|
|
407
407
|
placeholder='{ "#my-btn": { "events": { "click": { "target": "#panel", "toggleClass": "hidden" } } } }'></textarea>
|
|
408
408
|
<small class="form-hint">Declarative click handlers and class toggles applied on page load. Merged with any <code>[dconfig]</code>
|
|
409
409
|
shortcodes in the content body - inline shortcodes win on selector conflict. Leave blank if unused.</small>
|
|
410
|
+
<div class="pe-dconfig-notes" hidden data-bind-hidden="!dconfigNoteRows.value.length">
|
|
411
|
+
<p class="pe-dconfig-notes-head">Checked against Domma's types - these settings will be ignored or misread:</p>
|
|
412
|
+
<ul data-each="dconfigNoteRows.value key=key"><li data-bind-text="text"></li></ul>
|
|
413
|
+
</div>
|
|
410
414
|
</div>
|
|
411
415
|
</div>
|
|
412
416
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{api as h}from"../api.js";import{CardBuilder as
|
|
1
|
+
import{api as h}from"../api.js";import{CardBuilder as tn}from"../lib/card-builder.js";import{TimelineBuilder as on}from"../lib/timeline-builder.js";import{attachEditorKeybindings as an,createToolbar as sn,insertAtCursor as nn}from"../lib/markdown-toolbar.js";import{EffectsBuilder as rn}from"../lib/effects-builder.js";import{attachContextMenu as ln}from"../lib/shortcode-context-menu.js";import{registerCustomInserters as cn}from"../lib/shortcode-insert.js";import{populateThemeSelect as dn}from"../lib/themes.js";import{createFoldingManager as pn}from"../lib/editor-folding.js";import{openScribeComposer as un}from"../lib/scribe-composer.js";import{openCrudTutorial as mn}from"../lib/crud-tutorial.js";import{addBannerAction as dt}from"../lib/plugin-chrome.js";import{exampleJson as fn,parseSchema as Po,schemaText as bn}from"../lib/seo-structured-data.js";import{helpPopovers as qa}from"../lib/help-popover.js";import{onceClosed as Bt,openMenuAt as Ce,sidebarCount as pt}from"../lib/tool-kit.js";import{pickMedia as vn}from"../lib/media-picker.js";import{openPageClone as gn}from"../lib/page-clone.js";import{openEditorReference as Ha}from"../lib/page-editor-reference.js";import{dconfigNotes as hn}from"../lib/dconfig-check.js";import{cleanType as It,DEFAULT_TYPE as Ft,EDITOR_FONTS as yn,fontHref as kn,isDefaultType as wn,LINE_HEIGHTS as Sn,SIZES as En,typeVars as Va}from"../lib/editor-type.js";import{getProjectFromHash as zt}from"../lib/project-context.js";import{attachSmoothCaret as _a,getCaretPreference as Mn,setCaretPreference as Tn}from"../lib/smooth-caret.js";import{SCREEN as Oo,bannerSection as Cn,draftIsStale as xn,draftKey as ze,initialProject as Pn,makeDraft as Ka,problems as Ro,readDraft as On,snapshot as W}from"../lib/page-editor-arrange.js";import{fmtDateTime as qe}from"../lib/dates.js";import{hasDraft as xe,heldBlockEdits as Rn,hold as Ao,takeHeld as $o,wireHeldRouter as An}from"../lib/editor-hop.js";import{hopTarget as Ua}from"../lib/editor-hop-arrange.js";import{checkNewName as $n,copyName as Lo,usageText as Ln}from"../lib/blocks-arrange.js";import{isCollapsed as Ja,setCollapsed as Wa}from"../lib/sidebar-size.js";import{blockTagDefs as Ya,blocksInBody as Nn}from"../lib/block-tags.js";import{draftKey as He,draftOffer as Dn,extractPlaceholders as Ga,formFromBlock as Xa,payloadFromForm as Za,rawPlaceholders as No,shortcodeFor as jn,snapshot as Do}from"../lib/block-editor-arrange.js";import{blockTabs as Bn,notHereLine as In,otherUses as Qa,outlineBlock as Fn,previewBlocks as zn,saveQuestion as qn,savedMessage as Hn,usesLine as Vn}from"../lib/page-blocks-arrange.js";import{addressFor as es,blockAloneBody as _n,blockRows as Kn,cleanLayout as jo,closeTab as Un,componentRows as Jn,componentStatusText as Wn,componentsInBody as Yn,moveTab as Gn,nextLayout as Xn,openTab as Bo,pageRows as Zn,pageStatusText as Qn,rememberedTabs as er,renameTab as tr,sampleRows as ts,saveAllLabel as or,startingTabs as ar,tabId as X,tabItem as I,tabsToClose as Io,workspaceKey as sr}from"../lib/editor-workspace-arrange.js";import{PANES as qt,STARTER_SOURCE as nr,assembleSource as os,attributeFor as rr,draftKey as Ve,draftStatus as ir,nameProblem as lr,previewRows as as,readValue as cr,snapshot as Fo,splitSource as ut,writeValue as ss}from"../lib/component-editor-arrange.js";import{attachColourLayer as ns,createPlainEditor as zo}from"../lib/plain-editor.js";import{cleanCodePrefs as Ht,codePrefs as dr,DEFAULT_CODE_PREFS as Vt,formatTextarea as _e,INDENT_SIZES as pr,setCodePrefs as ur}from"../lib/code-format.js";function mt(d){const b=document.createElement("template");return b.innerHTML=d.trim(),b.content.firstElementChild}const mr=qe,rs=d=>qe(d.createdAt)+(d.label?` - ${d.label}`:""),fr=`
|
|
2
2
|
<div class="pe-share">
|
|
3
3
|
<p class="form-hint" style="margin-top:0">Anyone with the link can view this page even while it is unpublished, without
|
|
4
4
|
signing in. The link works for this page only, expires on its own, and can be revoked at any time.</p>
|
|
@@ -30,7 +30,7 @@ import{api as h}from"../api.js";import{CardBuilder as en}from"../lib/card-builde
|
|
|
30
30
|
.pe-share [hidden] { display: none !important; }
|
|
31
31
|
.pe-share-row { display: flex; align-items: center; gap: .6rem; padding: .5rem 0; border-bottom: 1px solid var(--dm-border-light); }
|
|
32
32
|
.pe-share-text { flex: 1 1 auto; min-width: 0; font-size: .82rem; }
|
|
33
|
-
</style>`;function is(d){const b=
|
|
33
|
+
</style>`;function is(d){const b=kn(d);if(!b||document.querySelector(`link[data-pe-font="${CSS.escape(d)}"]`))return;const v=Object.assign(document.createElement("link"),{rel:"stylesheet",href:b});v.dataset.peFont=d,document.head.appendChild(v)}const br=`
|
|
34
34
|
<div class="pe-settings">
|
|
35
35
|
<p class="pe-settings-note">How the editor looks to you - kept in this browser, for every page.</p>
|
|
36
36
|
<label class="form-label" for="pe-set-font">Font <span data-help="The lettering in the editor (not on the site). Monospace keeps every character the same width, so shortcodes and Markdown line up."></span></label>
|
|
@@ -94,17 +94,17 @@ The quick brown fox - 0O 1lI {} [] () => ==
|
|
|
94
94
|
.pe-settings-sample { white-space: pre-wrap; padding: .75rem 1rem; border-radius: var(--dm-radius-md); border: 1px solid var(--dm-border);
|
|
95
95
|
background: var(--dm-background-alt); font-family: var(--pe-font); font-size: var(--pe-size); line-height: var(--pe-line); }
|
|
96
96
|
.pe-settings-actions { display: flex; gap: .5rem; border-top: 1px solid var(--dm-border); padding-top: .75rem; }
|
|
97
|
-
</style>`;function
|
|
97
|
+
</style>`;function vr(d,b){const v=E.slideover({title:"Editor settings",size:"sm",position:"right"}),i=mt(`<div>${br}</div>`),k=It(d.peek()),u={font:M.observable(k.font),size:M.observable(String(k.size)),line:M.observable(String(k.line)),wrap:M.observable(k.wrap),caret:M.observable(Mn()),sidebar:M.observable(Pe().closeSidebar===!0)},w=Ht(b.peek());u.indent=M.observable(w.indent),u.indentSize=M.observable(String(w.size));for(const T of["detect","autoIndent","continueLists","shortTags","tables","html","formatOnSave"])u[T]=M.observable(w[T]);const y=M.computed(()=>Ht({indent:u.indent.value,size:u.indentSize.value,detect:u.detect.value,autoIndent:u.autoIndent.value,continueLists:u.continueLists.value,shortTags:u.shortTags.value,tables:u.tables.value,html:u.html.value,formatOnSave:u.formatOnSave.value})),x=M.effect(()=>{const T=y.value;JSON.stringify(T)!==JSON.stringify(Ht(b.peek()))&&(b.value=T,Ke("code",T))}),j=M.effect(()=>{const T=u.sidebar.value;T!==(Pe().closeSidebar===!0)&&Ke("closeSidebar",T)}),c=M.computed(()=>It({font:u.font.value,size:u.size.value,line:u.line.value,wrap:u.wrap.value}));u.isDefault=M.computed(()=>wn(c.value)&&u.caret.value==="auto"&&!u.sidebar.value&&JSON.stringify(y.value)===JSON.stringify(Vt));const A=M.effect(()=>Tn(u.caret.value)),H=i.querySelector("#pe-set-font");for(const[T,ne]of[["mono","Monospace"],["sans","Other"],["serif",null]]){const Oe=yn.filter(re=>re.kind===T),O=ne?Object.assign(document.createElement("optgroup"),{label:ne}):H.querySelector("optgroup:last-of-type");O.append(...Oe.map(re=>new Option(re.label,re.id))),ne&&H.appendChild(O)}i.querySelector("#pe-set-indent-size").append(...pr.map(T=>new Option(String(T),String(T)))),i.querySelector("#pe-set-size").append(...En.map(T=>new Option(`${T}px`,String(T)))),i.querySelector("#pe-set-line").append(...Sn.map(T=>new Option({1.3:"Tight",1.45:"Snug",1.6:"Normal",1.8:"Relaxed",2:"Double"}[T]||String(T),String(T))));const Y=i.querySelector(".pe-settings-sample"),D=M.effect(()=>{const T=c.value;is(T.font);for(const[ne,Oe]of Object.entries(Va(T)))Y.style.setProperty(ne,Oe);Y.style.whiteSpace=T.wrap?"pre-wrap":"pre",Y.style.overflowX=T.wrap?"":"auto",JSON.stringify(T)!==JSON.stringify(It(d.peek()))&&(d.value=T,Ke("type",T))});u.reset=()=>{u.font.value=Ft.font,u.size.value=String(Ft.size),u.line.value=String(Ft.line),u.wrap.value=Ft.wrap,u.caret.value="auto",u.sidebar.value=!1,u.indent.value=Vt.indent,u.indentSize.value=String(Vt.size);for(const T of["detect","autoIndent","continueLists","shortTags","tables","html","formatOnSave"])u[T].value=Vt[T]},u.done=()=>v.close(),v.element.appendChild(i);const se=M.applyBindings(u,i),_t=qa(i);return Bt(v,()=>{se.dispose(),_t(),D?.(),A?.(),j?.(),x?.()}),Domma.icons?.scan?.(i),v.open(),v}async function gr(d){const b=E.slideover({title:"Share preview link",size:"md",position:"right"}),v=mt(`<div>${fr}</div>`),i={expiry:M.observable("7d"),note:M.observable(""),creating:M.observable(!1),url:M.observable(""),links:M.observable([]),loaded:M.observable(!1),listError:M.observable("")},k=async()=>{try{const w=await h.pages.previewLinks.list(d);i.links.value=w.filter(y=>!y.revokedAt&&!y.expired).map(y=>({id:y.id,title:y.label||"Untitled link",meta:`Expires ${mr(y.expiresAt)} - ${y.useCount===1?"1 view":`${y.useCount||0} views`}`})),i.listError.value=""}catch(w){i.listError.value="Could not load existing links: "+(w.message||"unknown error")}finally{i.loaded.value=!0}};i.create=async()=>{i.creating.value=!0;try{const w=await h.pages.previewLinks.create(d,i.expiry.peek(),i.note.peek().trim());i.url.value=w.url,i.note.value="",E.toast("Share link created.",{type:"success"}),await k(),v.querySelector(".pe-share-url")?.select()}catch(w){E.toast("Could not create link: "+(w.message||"unknown error"),{type:"error"})}finally{i.creating.value=!1}},i.copy=async()=>{try{await navigator.clipboard.writeText(i.url.peek()),E.toast("Link copied to clipboard.",{type:"success"})}catch{v.querySelector(".pe-share-url")?.select(),E.toast("Press Ctrl+C to copy the selected link.",{type:"info"})}},i.revoke=async w=>{if(await E.confirm("Revoke this link? Anyone holding it loses access immediately."))try{await h.pages.previewLinks.revoke(w),E.toast("Link revoked.",{type:"success"}),await k()}catch(y){E.toast("Could not revoke: "+(y.message||"unknown error"),{type:"error"})}};const u=M.applyBindings(i,v);Bt(b,()=>u.dispose()),b.element.appendChild(v),b.open(),await k()}function hr(d){return new Promise(b=>{const v=E.slideover({title:"Save Named Version",size:"sm",position:"right"}),i=mt(`
|
|
98
98
|
<div style="padding:1rem">
|
|
99
99
|
<label class="form-label" for="pe-version-label">Version Label</label>
|
|
100
100
|
<input id="pe-version-label" type="text" class="form-input" style="margin-bottom:.75rem"
|
|
101
101
|
placeholder="e.g. Initial design, Before restructure" data-model="label.value" data-on-keydown="key">
|
|
102
102
|
<button type="button" class="btn btn-primary" data-on-click="save" data-bind-disabled="saving.value">Save Version</button>
|
|
103
|
-
</div>`);let
|
|
103
|
+
</div>`);let k=!1;const u={label:M.observable(""),saving:M.observable(!1)};u.save=async()=>{u.saving.value=!0;try{await h.versions.create(d,u.label.peek().trim()||null),E.toast("Named version saved.",{type:"success"}),k=!0,v.close()}catch(y){E.toast("Failed to save version: "+y.message,{type:"error"})}finally{u.saving.value=!1}},u.key=y=>{y.key==="Enter"&&(y.preventDefault(),u.save())};const w=M.applyBindings(u,i);Bt(v,()=>{w.dispose(),b(k)}),v.element.appendChild(i),v.open(),setTimeout(()=>i.querySelector("input")?.focus(),100)})}const yr=`<div class="card">
|
|
104
104
|
<h3>{{title}}</h3>
|
|
105
105
|
<p>{{description}}</p>
|
|
106
106
|
</div>
|
|
107
|
-
`,
|
|
107
|
+
`,kr=`
|
|
108
108
|
<div class="pe-new">
|
|
109
109
|
<p class="form-hint" style="margin-top:0" data-bind-text="hint.value"></p>
|
|
110
110
|
<label class="form-label" for="pe-new-name">Name</label>
|
|
@@ -124,7 +124,7 @@ The quick brown fox - 0O 1lI {} [] () => ==
|
|
|
124
124
|
.pe-new [hidden] { display: none !important; }
|
|
125
125
|
.pe-new-err { display: block; margin-top: .3rem; color: var(--dm-danger); font-size: var(--dm-text-xs); }
|
|
126
126
|
.pe-new-actions { display: flex; justify-content: flex-end; gap: .5rem; margin-top: 1.25rem; }
|
|
127
|
-
</style>`;function ls({kind:d,projects:b,taken:v,project:i="",name:y="",check:p,create:w,register:k=()=>{}}){return new Promise(x=>{const j=E.slideover({title:d==="block"?"New block":"New component",size:"sm",position:"right"});k(()=>{try{j.close()}catch{}});const c=mt(`<div>${hr}</div>`);for(const D of b)c.querySelector("#pe-new-project").append(new Option(D.name||D.slug,D.slug));const A={name:M.observable(y),project:M.observable(i),busy:M.observable(!1),hint:M.observable(d==="block"?"A block is a reusable piece of HTML with {{placeholders}}, used on pages with its short tag or by a collection display. Its name is its tag - fixed once created.":"A component is a custom element - <dm-name> - with a template, props, a script and a style. Its name is its tag - fixed once created."),placeholder:M.observable(d==="block"?"e.g. team-card":"e.g. pricing-table")};A.error=M.computed(()=>A.name.value.trim()?p(A.name.value.trim(),v):""),A.canCreate=M.computed(()=>!A.busy.value&&!!A.name.value.trim()&&!p(A.name.value.trim(),v));let H="";A.create=async()=>{const D=A.name.peek().trim();if(!(!D||A.error.peek()||A.busy.peek())){A.busy.value=!0;try{await w(D,A.project.peek()),H=D,j.close()}catch(se){E.toast(`Not created: ${se.message||se}`,{type:"error"})}finally{A.busy.value=!1}}},A.cancel=()=>j.close(),A.key=D=>{D.key==="Enter"&&(D.preventDefault(),A.create())};const Y=M.applyBindings(A,c);jt(j,()=>{Y.dispose(),x(H)}),j.element.appendChild(c),c.querySelector("#pe-new-project").value=i,j.open(),setTimeout(()=>{const D=c.querySelector("#pe-new-name");D?.focus(),D?.select()},100)})}let F=null,cs=!1;function kr(){cs||!window.R?.use||(cs=!0,On(),R.use((d,b,v)=>{if(F&&!F.alive()){const p=F;F=null,p.dispose()}const i=b?.path||"",y=d?.path||"";if(!F||!Oo.test(i))return v();if(Oo.test(y)){const p=Ua(y);if(p){F.open(p,y);return}return v()}return F.dirty()?E.confirm("You have unsaved changes. Leave anyway? They are kept as drafts for your next visit.").then(p=>{p?(F?.keepDraft(),v()):F&&history.replaceState({pe:1},"","#"+F.address)}):v()}),M.subscribe("router:afterChange",({to:d}={})=>{if(F&&!Oo.test(d?.path||"")){const b=F;F=null,b.dispose()}}))}const ds="editor_prefs";function Pe(){return S.get(ds)||{viewMode:"split",fullscreen:!1}}const yr=()=>{try{return Pe()||{}}catch{return{}}};function Ke(d,b){const v=Pe();v[d]=b,S.set(ds,v)}const ps="editor-workspace.tabs",us="editor-workspace.folded",ft="editor-workspace.css",wr="/__block-preview",Sr=d=>d.replace(/<\/head>/i,"<style>body.dm-cloaked{opacity:1 !important;transition:none !important}</style></head>");function Er(d,b,v=()=>{}){let i=0;try{i=d.contentWindow.scrollY||0}catch{}d.addEventListener("load",()=>{try{d.contentWindow.scrollTo(0,i)}catch{}v()},{once:!0}),d.srcdoc=b}const Tr=["urlPath","title","description","layout","status","sortOrder","category","visibility","project","theme","seoTitle","seoDesc","seoSchema","dconfig"],Mr=["showInNav","sidebar","breadcrumbs","bundled"],ms=()=>({urlPath:"",title:"",description:"",layout:"",status:"draft",sortOrder:"99",showInNav:!1,sidebar:!1,breadcrumbs:!0,bundled:!1,tags:[],adminTags:[],category:"",visibility:"public",project:"",theme:"",seoTitle:"",seoDesc:"",seoSchema:"",dconfig:"",body:"",css:null});export const pageEditorView={templateUrl:"/admin/js/templates/page-editor.html",async onMount(d){if(kr(),F){const e=F;F=null,e.dispose()}const b=window.location.hash.replace(/^#/,""),v=[],i=e=>(v.push(e),e);let y=()=>!1,p=()=>{},w=()=>{};const k=d.get(0),x=k.querySelector("#pe-workspace"),j={address:b,alive:()=>x?.isConnected===!0,dirty:()=>y(),keepDraft:()=>p(),open:(e,t)=>w(e,t),dispose:()=>{v.splice(0).forEach(e=>{try{e()}catch{}})}};F=j;const c=()=>F===j&&j.alive();if(yr().closeSidebar===!0){const e=document.querySelector("#admin-sidebar");e&&!Ja(e)&&!window.matchMedia("(max-width: 768px)").matches&&(Wa(e,!0,!1),i(()=>{e.isConnected&&Ja(e)&&Wa(e,!1,!1)}))}const A=E.loader(k,{type:"dots"});let[H,Y,D,se,Vt,T,ne,Oe]=await Promise.all([h.layouts.get().catch(()=>({})),h.settings.get().catch(()=>({})),h.pages.list().catch(()=>[]),h.projects.list().catch(()=>[]),h.pages.tags().catch(()=>[]),h.listTags.list("pages").then(e=>e.map(t=>t.name)).catch(()=>[]),h.get("/blocks?usage=1").catch(()=>h.blocks.list()).catch(()=>[]),h.components.list().catch(()=>[])]);if(!c()){A.destroy();return}A.destroy();const O=e=>k.querySelector(e);if(D=Array.isArray(D)?D:[],cn(O("#field-theme"),{includeDefault:!0}),await Ar(O("#field-visibility")),!c())return;const re=Object.keys(H||{});Or(O("#field-layout"),H,re[0]||"default");const Ho=e=>re.includes(e)?e:re[0]||e||"default";for(const e of se)for(const t of["#field-project","#block-project","#comp-project"])O(t).append(new Option(e.name||e.slug,e.slug));let ie=Array.isArray(ne)?ne:[],bt=Ya(ie);const vt=new Map(ie.map(e=>[e.name,e.updatedAt])),Ue=new Map(ie.map(e=>[e.name,e.usage])),be=new Set(ie.map(e=>e.name)),Vo=async()=>{const e=await h.get("/blocks?usage=1").catch(()=>null);if(!(!c()||!Array.isArray(e))){ie=e,bt=Ya(ie);for(const t of ie)be.add(t.name),Ue.set(t.name,t.usage),vt.has(t.name)||vt.set(t.name,t.updatedAt)}},_t=Array.isArray(Y?.footer?.blocks)?Y.footer.blocks.filter(e=>typeof e=="string"):[];let Je=Array.isArray(Oe)?Oe.filter(e=>typeof e?.name=="string"):[];const le=new Set(Je.map(e=>e.name)),_o=async()=>{const e=await h.components.list().catch(()=>null);if(!(!c()||!Array.isArray(e))){Je=e.filter(t=>typeof t?.name=="string");for(const t of Je)le.add(t.name)}},Z=Pe(),u={urlPath:M.observable(""),title:M.observable(""),description:M.observable(""),layout:M.observable(Ho("default")),status:M.observable("draft"),sortOrder:M.observable("99"),showInNav:M.observable(!1),sidebar:M.observable(!1),breadcrumbs:M.observable(!0),bundled:M.observable(!1),tags:M.observable([]),adminTags:M.observable([]),category:M.observable(""),visibility:M.observable("public"),project:M.observable(""),theme:M.observable(""),seoTitle:M.observable(""),seoDesc:M.observable(""),seoSchema:M.observable(""),dconfig:M.observable(""),css:M.observable(""),cssOpened:M.observable(!1)},a={bodyRev:M.observable(0),base:M.observable(""),saving:M.observable(!1),viewMode:M.observable(jo(Z.viewMode)),fullscreen:M.observable(!!Z.fullscreen),explorerOn:M.observable(Z.explorer??!window.matchMedia("(max-width: 1024px)").matches),dockOn:M.observable(!!Z.dock),dockTab:M.observable("details"),seoSchemaNote:M.observable(""),cssMode:M.observable(!1),folded:M.observable(!1),previewStatus:M.observable("Showing unsaved changes"),liveWidth:M.observable("full"),versions:M.observable([]),histLoading:M.observable(!1),histError:M.observable(""),compare:M.observable(null),heldNote:M.observable(""),tabs:M.observable([]),active:M.observable(""),curId:M.observable(null),blockTab:M.observable(""),blockPart:M.observable("html"),blockReady:M.observable(!1),blockNote:M.observable(""),blockUses:M.observable(""),blockProject:M.observable(""),blockBundled:M.observable(!1),blockWhere:M.observable(""),compTab:M.observable(""),compPart:M.observable("template"),compReady:M.observable(!1),compNote:M.observable(""),compReadOnly:M.observable(!1),compState:M.observable(""),compErrors:M.observable(""),compProject:M.observable(""),compBundled:M.observable(!1),blockRev:M.observable(0),compRev:M.observable(0),docRev:M.observable(0),filter:M.observable(""),secHere:M.observable(Z.secHere!==!1),secPages:M.observable(Z.secPages!==!1),secBlocks:M.observable(Z.secBlocks!==!1),secComps:M.observable(Z.secComps!==!1),thisPage:M.observable([]),pageRows:M.observable([]),blockRows:M.observable([]),compRows:M.observable([])},V=O("#markdown-editor"),ve=O("#css-editor"),fs=_a(V),bs=_a(ve);i(()=>fs.destroy()),i(()=>bs.destroy());const ce=document.createElement("div");ce.className="editor-line-numbers editor-line-numbers--foldable",V.parentElement.insertBefore(ce,V);const We=ns(V,"md"),Kt=ns(ve,"css");i(()=>{We.destroy(),Kt.destroy()});const Ko=()=>{We.paint(),Kt.paint()},Q=dn(V,ce,{onFoldChange:()=>We.paint()});let gt=!1;Q.setGuard(e=>{gt=e});const Ut=()=>Q.getUnfoldedContent(),Jt=()=>{Q.unfoldAll(),a.folded.value=!1,We.paint()},vs=e=>{if(e?.kind==="page")Jt(),_e(V,"md");else if(e?.kind==="block")oe&&_e(oe.textarea,"html"),me&&_e(me.textarea,"css");else if(e?.kind==="component"){if(N.get(e.id)?.readOnly)return;for(const[o,s]of Object.entries({template:"html",props:"json",script:"js",style:"css"}))ae[o]&&_e(ae[o].textarea,s)}},Uo=e=>{e.error?E.toast(e.error,{type:"warning"}):E.toast(e.unchanged?"Already tidy":"Formatted",{type:"info",duration:900})},Jo=M.observable(Bt(Z.type)),Wo=M.observable(qt(Z.code));i(M.effect(()=>{const e=Wo.value;dr(e),O("#editor-body")?.style.setProperty("--pe-tab",String(e.size))})),i(M.effect(()=>{const e=Jo.value,t=O("#editor-body");if(t){is(e.font);for(const[o,s]of Object.entries(Va(e)))t.style.setProperty(o,s);t.classList.toggle("pe-nowrap",!e.wrap),V.wrap=e.wrap?"soft":"off",ve.wrap=e.wrap?"soft":"off",requestAnimationFrame(()=>{c()&&Ko()})}}));let Yo=null;i(()=>{try{Yo?.close()}catch{}});const _=()=>(a.bodyRev.value,{urlPath:u.urlPath.value.trim(),title:u.title.value,description:u.description.value,layout:u.layout.value,status:u.status.value,sortOrder:u.sortOrder.value,showInNav:u.showInNav.value,sidebar:u.sidebar.value,breadcrumbs:u.breadcrumbs.value,bundled:u.bundled.value,tags:u.tags.value,adminTags:u.adminTags.value,category:u.category.value,visibility:u.visibility.value,project:u.project.value,theme:u.theme.value,seoTitle:u.seoTitle.value,seoDesc:u.seoDesc.value,seoSchema:u.seoSchema.value,dconfig:u.dconfig.value,body:Ut(),css:null}),Go=()=>M.untracked?M.untracked(_):_(),ee=M.observable(""),ht=()=>u.cssOpened.peek()&&u.css.peek()!==ee.peek(),K=new Map;let m=null;const L=new Map,N=new Map;let P=[];const de=e=>Do(e.form)!==e.base,ge=()=>[...L.entries()].filter(([,e])=>de(e)).map(([e])=>e),he=e=>!e.readOnly&&Fo({name:e.name,...e.form})!==e.base,Ye=()=>[...N.values()].filter(he).map(e=>e.name),Wt=()=>W(Go())!==a.base.peek(),Yt=e=>e.loaded&&(e===m?Wt():W(e.state)!==e.base),Ae=()=>[...K.values()].filter(Yt),gs=M.computed(()=>W(_())!==a.base.value),hs=M.computed(()=>u.cssOpened.value&&u.css.value!==ee.value),ks=M.computed(()=>(a.blockRev.value,a.compRev.value,a.docRev.value,W(_())!==a.base.value||u.cssOpened.value&&u.css.value!==ee.value||Ae().some(e=>e!==m)||ge().length>0||Ye().length>0)),ys=M.computed(()=>{a.blockRev.value,a.compRev.value;const e=I(a.active.value);if(e?.kind==="block"){const t=L.get(e.id);return!!(t&&de(t))}if(e?.kind==="component"){const t=N.get(e.id);return!!(t&&he(t))}return W(_())!==a.base.value||u.cssOpened.value&&u.css.value!==ee.value}),ws=M.computed(()=>a.curId.value!==null),Xo=M.computed(()=>I(a.active.value)?.kind||""),Zo=M.computed(()=>!!a.curId.value&&I(a.active.value)?.kind==="page"),Ss=M.computed(()=>u.urlPath.value.trim()||a.curId.value||""),Es=M.computed(()=>Ao({urlPath:u.urlPath.value,dconfig:u.dconfig.value})[0]?.text||""),Ts=M.computed(()=>{a.docRev.value;const e=I(a.active.value);return e?e.kind==="block"?a.blockUses.value:e.kind==="component"?Un({name:e.id,readOnly:a.compReadOnly.value,state:a.compReady.value?a.compState.value:""}):Xn({urlPath:u.urlPath.value,status:u.status.value,isEdit:!!a.curId.value}):""}),Qo=M.computed(()=>a.compTab.value?"component":a.blockTab.value?"block":a.curId.value!==null?"page":""),Ms=M.computed(()=>a.blockTab.value?`[${a.blockTab.value} /]`:""),Cs=M.computed(()=>({compiling:"Compiling\u2026",ok:"Compiles",error:"Does not compile"})[a.compState.value]||""),xs=M.computed(()=>a.compTab.value?`<dm-${a.compTab.value}>`:""),Ps=M.computed(()=>a.versions.value.some(e=>e.checked.value)),Os=M.computed(()=>a.versions.value.length>0&&a.versions.value.every(e=>e.checked.value)),As=M.computed(()=>{const e=a.versions.value.filter(t=>t.checked.value).length;return e>0&&e<a.versions.value.length});y=()=>Ae().length>0||ge().length>0||Ye().length>0||ht();const Gt=(e,t,o)=>({title:String(e.title||"").trim()||"Untitled",description:String(e.description||"").trim(),layout:e.layout,status:e.status,sortOrder:parseInt(String(e.sortOrder),10)||99,showInNav:!!e.showInNav,sidebar:!!e.sidebar,...e.breadcrumbs?{}:{breadcrumbs:!1},...e.bundled?{bundled:!0}:{},tags:Array.isArray(e.tags)?e.tags:[],...t?.isEdit&&JSON.stringify(e.adminTags)===t.savedAdminTags?{}:{adminTags:Array.isArray(e.adminTags)?e.adminTags:[]},category:String(e.category||"").trim()||null,visibility:e.visibility||"public",project:e.project||"",seo:{title:String(e.seoTitle||"").trim(),description:String(e.seoDesc||"").trim(),...ea(e)?{schema:ea(e)}:t?.isEdit?{schema:null}:{}},dconfig:o,...e.theme?{theme:e.theme}:{}}),ea=e=>Po(e.seoSchema).value,Xt=e=>{const t=String(e.dconfig||"").trim();if(!t)return null;try{return JSON.parse(t)}catch{return null}},Rs=()=>u.urlPath.peek().trim()||m?.id||"/";let kt=[],ta=[];const yt=e=>kt.includes(e)||_t.includes(e);let Re=new Set;const wt=()=>{const e=[...new Set([...kt,..._t,...L.keys()])],t=Pn(e.filter(s=>!L.has(s)),vt);Re=new Set(Object.keys(t));const o=Re.size?`Preview shows unsaved changes to ${[...Re].join(", ")}`:"";return o!==a.heldNote.peek()&&(a.heldNote.value=o),In(t,[...L.entries()].map(([s,r])=>({name:s,content:r.form.content,css:r.form.css,dirty:de(r)})))};let Zt="";const $s=()=>{oa();const e=Zt;if(e){Zt="";try{$e.contentDocument?.querySelector(`[data-block="${CSS.escape(e)}"]`)?.scrollIntoView({block:"center"})}catch{}}},$e=O("#markdown-preview");h.settings.getCustomCss().then(({css:e})=>{if(!c())return;ee.value=e||"";let t=null;try{t=S.get(ft)}catch{}typeof t=="string"&&t!==ee.peek()?(ia(),u.css.value=t,E.toast("Your unsaved custom CSS is back - the CSS button shows it.",{type:"info"})):u.cssOpened.peek()||(u.css.value=ee.peek()),J(ft)}).catch(()=>{});const oa=()=>{const e=$e?.contentDocument;if(!e?.head)return;const t=u.cssOpened.peek()?u.css.peek():"";if(!t)return;let o=e.getElementById("preview-custom-css");o||(o=e.createElement("style"),o.id="preview-custom-css",e.head.appendChild(o)),o.textContent=t};let Qt=null,eo=0;const U=(e=!1)=>{clearTimeout(Qt),Qt=setTimeout(async()=>{if(!$e||a.viewMode.peek()==="write"||!a.active.peek()||a.compTab.peek())return;const t=++eo,o=a.blockTab.peek();a.previewStatus.value="Rendering\u2026";try{let s;if(o&&(!m||!yt(o)))({html:s}=await h.pages.previewFull(wr,{title:o,layout:"blank",breadcrumbs:!1},Hn(o,Vs(o)),wt()));else if(m){const r=_();({html:s}=await h.pages.previewFull(Rs(),Gt(r,m,Xt(r)),r.body,wt()))}else{$e.srcdoc="";return}if(!c()||t!==eo)return;Er($e,Bn(Sr(s),o),$s),a.previewStatus.value="Showing unsaved changes"}catch(s){if(!c()||t!==eo)return;a.previewStatus.value="Preview failed - "+(s?.message||"unknown error"),$e.srcdoc='<!doctype html><meta charset="utf-8"><body style="font:14px system-ui;padding:1rem;color:#b91c1c;">Preview failed: '+String(s?.message||"unknown error").replace(/[<&]/g,r=>r==="<"?"<":"&")+"</body>"}},e?0:500)};i(()=>clearTimeout(Qt));const to=an(d.find("#markdown-editor"),d.find("#editor-toolbar"),{spacerDefault:Y?.layoutOptions?.spacerSize??40,beforeFormat:()=>Jt()});rn(V,{blocks:()=>bt,openBlock:e=>Be(e)});const Ge=k.querySelector(".editor-toolbar-right");Ge.querySelectorAll(".editor-view-btn[data-mode]").forEach(e=>{const t=e.getAttribute("data-mode");e.classList.remove("active"),e.setAttribute("data-bind-class",`viewMode.value === '${t}' && 'active'`),e.setAttribute("data-on-click",`setMode('${t}')`)}),O("#fullscreen-btn")?.setAttribute("data-on-click","toggleFullscreen");const Ls=mt(`
|
|
127
|
+
</style>`;function ls({kind:d,projects:b,taken:v,project:i="",name:k="",check:u,create:w,register:y=()=>{}}){return new Promise(x=>{const j=E.slideover({title:d==="block"?"New block":"New component",size:"sm",position:"right"});y(()=>{try{j.close()}catch{}});const c=mt(`<div>${kr}</div>`);for(const D of b)c.querySelector("#pe-new-project").append(new Option(D.name||D.slug,D.slug));const A={name:M.observable(k),project:M.observable(i),busy:M.observable(!1),hint:M.observable(d==="block"?"A block is a reusable piece of HTML with {{placeholders}}, used on pages with its short tag or by a collection display. Its name is its tag - fixed once created.":"A component is a custom element - <dm-name> - with a template, props, a script and a style. Its name is its tag - fixed once created."),placeholder:M.observable(d==="block"?"e.g. team-card":"e.g. pricing-table")};A.error=M.computed(()=>A.name.value.trim()?u(A.name.value.trim(),v):""),A.canCreate=M.computed(()=>!A.busy.value&&!!A.name.value.trim()&&!u(A.name.value.trim(),v));let H="";A.create=async()=>{const D=A.name.peek().trim();if(!(!D||A.error.peek()||A.busy.peek())){A.busy.value=!0;try{await w(D,A.project.peek()),H=D,j.close()}catch(se){E.toast(`Not created: ${se.message||se}`,{type:"error"})}finally{A.busy.value=!1}}},A.cancel=()=>j.close(),A.key=D=>{D.key==="Enter"&&(D.preventDefault(),A.create())};const Y=M.applyBindings(A,c);Bt(j,()=>{Y.dispose(),x(H)}),j.element.appendChild(c),c.querySelector("#pe-new-project").value=i,j.open(),setTimeout(()=>{const D=c.querySelector("#pe-new-name");D?.focus(),D?.select()},100)})}let F=null,cs=!1;function wr(){cs||!window.R?.use||(cs=!0,An(),R.use((d,b,v)=>{if(F&&!F.alive()){const u=F;F=null,u.dispose()}const i=b?.path||"",k=d?.path||"";if(!F||!Oo.test(i))return v();if(Oo.test(k)){const u=Ua(k);if(u){F.open(u,k);return}return v()}return F.dirty()?E.confirm("You have unsaved changes. Leave anyway? They are kept as drafts for your next visit.").then(u=>{u?(F?.keepDraft(),v()):F&&history.replaceState({pe:1},"","#"+F.address)}):v()}),M.subscribe("router:afterChange",({to:d}={})=>{if(F&&!Oo.test(d?.path||"")){const b=F;F=null,b.dispose()}}))}const ds="editor_prefs";function Pe(){return S.get(ds)||{viewMode:"split",fullscreen:!1}}let Sr=null;const Er=()=>Sr??(Sr=h.pages.dommaCatalogue().then(d=>d?.types?d:null)),Mr=d=>{try{return document.createDocumentFragment().querySelector(d),!0}catch{return!1}},Tr=()=>{try{return Pe()||{}}catch{return{}}};function Ke(d,b){const v=Pe();v[d]=b,S.set(ds,v)}const ps="editor-workspace.tabs",us="editor-workspace.folded",ft="editor-workspace.css",Cr="/__block-preview",xr=d=>d.replace(/<\/head>/i,"<style>body.dm-cloaked{opacity:1 !important;transition:none !important}</style></head>");function Pr(d,b,v=()=>{}){let i=0;try{i=d.contentWindow.scrollY||0}catch{}d.addEventListener("load",()=>{try{d.contentWindow.scrollTo(0,i)}catch{}v()},{once:!0}),d.srcdoc=b}const Or=["urlPath","title","description","layout","status","sortOrder","category","visibility","project","theme","seoTitle","seoDesc","seoSchema","dconfig"],Rr=["showInNav","sidebar","breadcrumbs","bundled"],ms=()=>({urlPath:"",title:"",description:"",layout:"",status:"draft",sortOrder:"99",showInNav:!1,sidebar:!1,breadcrumbs:!0,bundled:!1,tags:[],adminTags:[],category:"",visibility:"public",project:"",theme:"",seoTitle:"",seoDesc:"",seoSchema:"",dconfig:"",body:"",css:null});export const pageEditorView={templateUrl:"/admin/js/templates/page-editor.html",async onMount(d){if(wr(),F){const e=F;F=null,e.dispose()}const b=window.location.hash.replace(/^#/,""),v=[],i=e=>(v.push(e),e);let k=()=>!1,u=()=>{},w=()=>{};const y=d.get(0),x=y.querySelector("#pe-workspace"),j={address:b,alive:()=>x?.isConnected===!0,dirty:()=>k(),keepDraft:()=>u(),open:(e,t)=>w(e,t),dispose:()=>{v.splice(0).forEach(e=>{try{e()}catch{}})}};F=j;const c=()=>F===j&&j.alive();if(Tr().closeSidebar===!0){const e=document.querySelector("#admin-sidebar");e&&!Ja(e)&&!window.matchMedia("(max-width: 768px)").matches&&(Wa(e,!0,!1),i(()=>{e.isConnected&&Ja(e)&&Wa(e,!1,!1)}))}const A=E.loader(y,{type:"dots"});let[H,Y,D,se,_t,T,ne,Oe]=await Promise.all([h.layouts.get().catch(()=>({})),h.settings.get().catch(()=>({})),h.pages.list().catch(()=>[]),h.projects.list().catch(()=>[]),h.pages.tags().catch(()=>[]),h.listTags.list("pages").then(e=>e.map(t=>t.name)).catch(()=>[]),h.get("/blocks?usage=1").catch(()=>h.blocks.list()).catch(()=>[]),h.components.list().catch(()=>[])]);if(!c()){A.destroy();return}A.destroy();const O=e=>y.querySelector(e);if(D=Array.isArray(D)?D:[],dn(O("#field-theme"),{includeDefault:!0}),await Dr(O("#field-visibility")),!c())return;const re=Object.keys(H||{});Nr(O("#field-layout"),H,re[0]||"default");const Ho=e=>re.includes(e)?e:re[0]||e||"default";for(const e of se)for(const t of["#field-project","#block-project","#comp-project"])O(t).append(new Option(e.name||e.slug,e.slug));let ie=Array.isArray(ne)?ne:[],bt=Ya(ie);const vt=new Map(ie.map(e=>[e.name,e.updatedAt])),Ue=new Map(ie.map(e=>[e.name,e.usage])),be=new Set(ie.map(e=>e.name)),Vo=async()=>{const e=await h.get("/blocks?usage=1").catch(()=>null);if(!(!c()||!Array.isArray(e))){ie=e,bt=Ya(ie);for(const t of ie)be.add(t.name),Ue.set(t.name,t.usage),vt.has(t.name)||vt.set(t.name,t.updatedAt)}},Kt=Array.isArray(Y?.footer?.blocks)?Y.footer.blocks.filter(e=>typeof e=="string"):[];let Je=Array.isArray(Oe)?Oe.filter(e=>typeof e?.name=="string"):[];const le=new Set(Je.map(e=>e.name)),_o=async()=>{const e=await h.components.list().catch(()=>null);if(!(!c()||!Array.isArray(e))){Je=e.filter(t=>typeof t?.name=="string");for(const t of Je)le.add(t.name)}},Z=Pe(),p={urlPath:M.observable(""),title:M.observable(""),description:M.observable(""),layout:M.observable(Ho("default")),status:M.observable("draft"),sortOrder:M.observable("99"),showInNav:M.observable(!1),sidebar:M.observable(!1),breadcrumbs:M.observable(!0),bundled:M.observable(!1),tags:M.observable([]),adminTags:M.observable([]),category:M.observable(""),visibility:M.observable("public"),project:M.observable(""),theme:M.observable(""),seoTitle:M.observable(""),seoDesc:M.observable(""),seoSchema:M.observable(""),dconfig:M.observable(""),css:M.observable(""),cssOpened:M.observable(!1)},o={bodyRev:M.observable(0),base:M.observable(""),saving:M.observable(!1),viewMode:M.observable(jo(Z.viewMode)),fullscreen:M.observable(!!Z.fullscreen),explorerOn:M.observable(Z.explorer??!window.matchMedia("(max-width: 1024px)").matches),dockOn:M.observable(!!Z.dock),dockTab:M.observable("details"),seoSchemaNote:M.observable(""),catalogue:M.observable(null),cssMode:M.observable(!1),folded:M.observable(!1),previewStatus:M.observable("Showing unsaved changes"),liveWidth:M.observable("full"),versions:M.observable([]),histLoading:M.observable(!1),histError:M.observable(""),compare:M.observable(null),heldNote:M.observable(""),tabs:M.observable([]),active:M.observable(""),curId:M.observable(null),blockTab:M.observable(""),blockPart:M.observable("html"),blockReady:M.observable(!1),blockNote:M.observable(""),blockUses:M.observable(""),blockProject:M.observable(""),blockBundled:M.observable(!1),blockWhere:M.observable(""),compTab:M.observable(""),compPart:M.observable("template"),compReady:M.observable(!1),compNote:M.observable(""),compReadOnly:M.observable(!1),compState:M.observable(""),compErrors:M.observable(""),compProject:M.observable(""),compBundled:M.observable(!1),blockRev:M.observable(0),compRev:M.observable(0),docRev:M.observable(0),filter:M.observable(""),secHere:M.observable(Z.secHere!==!1),secPages:M.observable(Z.secPages!==!1),secBlocks:M.observable(Z.secBlocks!==!1),secComps:M.observable(Z.secComps!==!1),thisPage:M.observable([]),pageRows:M.observable([]),blockRows:M.observable([]),compRows:M.observable([])},V=O("#markdown-editor"),ve=O("#css-editor"),fs=_a(V),bs=_a(ve);i(()=>fs.destroy()),i(()=>bs.destroy());const ce=document.createElement("div");ce.className="editor-line-numbers editor-line-numbers--foldable",V.parentElement.insertBefore(ce,V);const We=ns(V,"md"),Ut=ns(ve,"css");i(()=>{We.destroy(),Ut.destroy()});const Ko=()=>{We.paint(),Ut.paint()},Q=pn(V,ce,{onFoldChange:()=>We.paint()});let gt=!1;Q.setGuard(e=>{gt=e});const ht=()=>Q.getUnfoldedContent(),Jt=()=>{Q.unfoldAll(),o.folded.value=!1,We.paint()},vs=e=>{if(e?.kind==="page")Jt(),_e(V,"md");else if(e?.kind==="block")oe&&_e(oe.textarea,"html"),me&&_e(me.textarea,"css");else if(e?.kind==="component"){if(N.get(e.id)?.readOnly)return;for(const[a,s]of Object.entries({template:"html",props:"json",script:"js",style:"css"}))ae[a]&&_e(ae[a].textarea,s)}},Uo=e=>{e.error?E.toast(e.error,{type:"warning"}):E.toast(e.unchanged?"Already tidy":"Formatted",{type:"info",duration:900})},Jo=M.observable(It(Z.type)),Wo=M.observable(Ht(Z.code));i(M.effect(()=>{const e=Wo.value;ur(e),O("#editor-body")?.style.setProperty("--pe-tab",String(e.size))})),i(M.effect(()=>{const e=Jo.value,t=O("#editor-body");if(t){is(e.font);for(const[a,s]of Object.entries(Va(e)))t.style.setProperty(a,s);t.classList.toggle("pe-nowrap",!e.wrap),V.wrap=e.wrap?"soft":"off",ve.wrap=e.wrap?"soft":"off",requestAnimationFrame(()=>{c()&&Ko()})}}));let Yo=null;i(()=>{try{Yo?.close()}catch{}});const _=()=>(o.bodyRev.value,{urlPath:p.urlPath.value.trim(),title:p.title.value,description:p.description.value,layout:p.layout.value,status:p.status.value,sortOrder:p.sortOrder.value,showInNav:p.showInNav.value,sidebar:p.sidebar.value,breadcrumbs:p.breadcrumbs.value,bundled:p.bundled.value,tags:p.tags.value,adminTags:p.adminTags.value,category:p.category.value,visibility:p.visibility.value,project:p.project.value,theme:p.theme.value,seoTitle:p.seoTitle.value,seoDesc:p.seoDesc.value,seoSchema:p.seoSchema.value,dconfig:p.dconfig.value,body:ht(),css:null}),Go=()=>M.untracked?M.untracked(_):_(),ee=M.observable(""),yt=()=>p.cssOpened.peek()&&p.css.peek()!==ee.peek(),K=new Map;let m=null;const L=new Map,N=new Map;let P=[];const de=e=>Do(e.form)!==e.base,ge=()=>[...L.entries()].filter(([,e])=>de(e)).map(([e])=>e),he=e=>!e.readOnly&&Fo({name:e.name,...e.form})!==e.base,Ye=()=>[...N.values()].filter(he).map(e=>e.name),Wt=()=>W(Go())!==o.base.peek(),Yt=e=>e.loaded&&(e===m?Wt():W(e.state)!==e.base),Re=()=>[...K.values()].filter(Yt),gs=M.computed(()=>W(_())!==o.base.value),hs=M.computed(()=>p.cssOpened.value&&p.css.value!==ee.value),ys=M.computed(()=>(o.blockRev.value,o.compRev.value,o.docRev.value,W(_())!==o.base.value||p.cssOpened.value&&p.css.value!==ee.value||Re().some(e=>e!==m)||ge().length>0||Ye().length>0)),ks=M.computed(()=>{o.blockRev.value,o.compRev.value;const e=I(o.active.value);if(e?.kind==="block"){const t=L.get(e.id);return!!(t&&de(t))}if(e?.kind==="component"){const t=N.get(e.id);return!!(t&&he(t))}return W(_())!==o.base.value||p.cssOpened.value&&p.css.value!==ee.value}),ws=M.computed(()=>o.curId.value!==null),Xo=M.computed(()=>I(o.active.value)?.kind||""),Zo=M.computed(()=>!!o.curId.value&&I(o.active.value)?.kind==="page"),Ss=M.computed(()=>p.urlPath.value.trim()||o.curId.value||""),Es=M.computed(()=>Ro({urlPath:p.urlPath.value,dconfig:p.dconfig.value})[0]?.text||""),Ms=M.computed(()=>(o.bodyRev.value,o.catalogue.value?hn({dconfig:p.dconfig.value,body:ht()},o.catalogue.value,{isSelector:Mr}).map((e,t)=>({key:`${t}:${e}`,text:e})):[]));Er().then(e=>{o.catalogue.value=e});const Ts=M.computed(()=>{o.docRev.value;const e=I(o.active.value);return e?e.kind==="block"?o.blockUses.value:e.kind==="component"?Wn({name:e.id,readOnly:o.compReadOnly.value,state:o.compReady.value?o.compState.value:""}):Qn({urlPath:p.urlPath.value,status:p.status.value,isEdit:!!o.curId.value}):""}),Qo=M.computed(()=>o.compTab.value?"component":o.blockTab.value?"block":o.curId.value!==null?"page":""),Cs=M.computed(()=>o.blockTab.value?`[${o.blockTab.value} /]`:""),xs=M.computed(()=>({compiling:"Compiling\u2026",ok:"Compiles",error:"Does not compile"})[o.compState.value]||""),Ps=M.computed(()=>o.compTab.value?`<dm-${o.compTab.value}>`:""),Os=M.computed(()=>o.versions.value.some(e=>e.checked.value)),Rs=M.computed(()=>o.versions.value.length>0&&o.versions.value.every(e=>e.checked.value)),As=M.computed(()=>{const e=o.versions.value.filter(t=>t.checked.value).length;return e>0&&e<o.versions.value.length});k=()=>Re().length>0||ge().length>0||Ye().length>0||yt();const Gt=(e,t,a)=>({title:String(e.title||"").trim()||"Untitled",description:String(e.description||"").trim(),layout:e.layout,status:e.status,sortOrder:parseInt(String(e.sortOrder),10)||99,showInNav:!!e.showInNav,sidebar:!!e.sidebar,...e.breadcrumbs?{}:{breadcrumbs:!1},...e.bundled?{bundled:!0}:{},tags:Array.isArray(e.tags)?e.tags:[],...t?.isEdit&&JSON.stringify(e.adminTags)===t.savedAdminTags?{}:{adminTags:Array.isArray(e.adminTags)?e.adminTags:[]},category:String(e.category||"").trim()||null,visibility:e.visibility||"public",project:e.project||"",seo:{title:String(e.seoTitle||"").trim(),description:String(e.seoDesc||"").trim(),...ea(e)?{schema:ea(e)}:t?.isEdit?{schema:null}:{}},dconfig:a,...e.theme?{theme:e.theme}:{}}),ea=e=>Po(e.seoSchema).value,Xt=e=>{const t=String(e.dconfig||"").trim();if(!t)return null;try{return JSON.parse(t)}catch{return null}},$s=()=>p.urlPath.peek().trim()||m?.id||"/";let kt=[],ta=[];const wt=e=>kt.includes(e)||Kt.includes(e);let Ae=new Set;const St=()=>{const e=[...new Set([...kt,...Kt,...L.keys()])],t=Rn(e.filter(s=>!L.has(s)),vt);Ae=new Set(Object.keys(t));const a=Ae.size?`Preview shows unsaved changes to ${[...Ae].join(", ")}`:"";return a!==o.heldNote.peek()&&(o.heldNote.value=a),zn(t,[...L.entries()].map(([s,r])=>({name:s,content:r.form.content,css:r.form.css,dirty:de(r)})))};let Zt="";const Ls=()=>{oa();const e=Zt;if(e){Zt="";try{$e.contentDocument?.querySelector(`[data-block="${CSS.escape(e)}"]`)?.scrollIntoView({block:"center"})}catch{}}},$e=O("#markdown-preview");h.settings.getCustomCss().then(({css:e})=>{if(!c())return;ee.value=e||"";let t=null;try{t=S.get(ft)}catch{}typeof t=="string"&&t!==ee.peek()?(ia(),p.css.value=t,E.toast("Your unsaved custom CSS is back - the CSS button shows it.",{type:"info"})):p.cssOpened.peek()||(p.css.value=ee.peek()),J(ft)}).catch(()=>{});const oa=()=>{const e=$e?.contentDocument;if(!e?.head)return;const t=p.cssOpened.peek()?p.css.peek():"";if(!t)return;let a=e.getElementById("preview-custom-css");a||(a=e.createElement("style"),a.id="preview-custom-css",e.head.appendChild(a)),a.textContent=t};let Qt=null,eo=0;const U=(e=!1)=>{clearTimeout(Qt),Qt=setTimeout(async()=>{if(!$e||o.viewMode.peek()==="write"||!o.active.peek()||o.compTab.peek())return;const t=++eo,a=o.blockTab.peek();o.previewStatus.value="Rendering\u2026";try{let s;if(a&&(!m||!wt(a)))({html:s}=await h.pages.previewFull(Cr,{title:a,layout:"blank",breadcrumbs:!1},_n(a,_s(a)),St()));else if(m){const r=_();({html:s}=await h.pages.previewFull($s(),Gt(r,m,Xt(r)),r.body,St()))}else{$e.srcdoc="";return}if(!c()||t!==eo)return;Pr($e,Fn(xr(s),a),Ls),o.previewStatus.value="Showing unsaved changes"}catch(s){if(!c()||t!==eo)return;o.previewStatus.value="Preview failed - "+(s?.message||"unknown error"),$e.srcdoc='<!doctype html><meta charset="utf-8"><body style="font:14px system-ui;padding:1rem;color:#b91c1c;">Preview failed: '+String(s?.message||"unknown error").replace(/[<&]/g,r=>r==="<"?"<":"&")+"</body>"}},e?0:500)};i(()=>clearTimeout(Qt));const to=sn(d.find("#markdown-editor"),d.find("#editor-toolbar"),{spacerDefault:Y?.layoutOptions?.spacerSize??40,beforeFormat:()=>Jt()});ln(V,{blocks:()=>bt,openBlock:e=>Be(e)});const Ge=y.querySelector(".editor-toolbar-right");Ge.querySelectorAll(".editor-view-btn[data-mode]").forEach(e=>{const t=e.getAttribute("data-mode");e.classList.remove("active"),e.setAttribute("data-bind-class",`viewMode.value === '${t}' && 'active'`),e.setAttribute("data-on-click",`setMode('${t}')`)}),O("#fullscreen-btn")?.setAttribute("data-on-click","toggleFullscreen");const Ns=mt(`
|
|
128
128
|
<span class="pe-toolbar-extras" style="display:contents">
|
|
129
129
|
<button type="button" class="editor-view-btn" data-tip="Edit Custom CSS" data-on-click="toggleCss"
|
|
130
130
|
data-bind-class="cssMode.value && 'active'"><span>CSS</span></button>
|
|
@@ -136,8 +136,8 @@ The quick brown fox - 0O 1lI {} [] () => ==
|
|
|
136
136
|
<span class="pe-fold-icon" hidden data-bind-hidden="!folded.value"><span data-icon="chevrons-right" data-icon-size="10"></span></span>
|
|
137
137
|
</button>
|
|
138
138
|
<span class="editor-toolbar-sep"></span>
|
|
139
|
-
</span>`);Ge.prepend(Ls),Ge.before(O(".pe-block-bar")),Ge.before(O(".pe-comp-bar"));const aa=e=>(Array.isArray(e)?e:[]).map(t=>({value:t,label:t})),sa=O("#field-tags");let St=null;St=sa?E.pillbox(sa,{data:aa(Vt),value:[],creatable:!0,searchable:!0,placeholder:"Add tag\u2026",onChange:()=>{St&&(u.tags.value=[...St.getValue()])}}):null;const na=O("#field-admin-tags");let Et=null;Et=na?E.pillbox(na,{data:aa(T),value:[],creatable:!0,searchable:!0,placeholder:"Add admin tag\u2026",onChange:()=>{Et&&(u.adminTags.value=[...Et.getValue()])}}):null;const ra=()=>{a.bodyRev.value=a.bodyRev.peek()+1},Le=()=>{a.blockRev.value=a.blockRev.peek()+1},ke=()=>{a.compRev.value=a.compRev.peek()+1},Xe=()=>{a.docRev.value=a.docRev.peek()+1},ia=()=>{u.cssOpened.peek()||(u.css.peek()||(u.css.value=ee.peek()),u.cssOpened.value=!0,on(ve,{language:"css"}))},Ze=e=>{for(const t of Tr)u[t].value=e[t]==null?"":String(e[t]);for(const t of Mr)u[t].value=!!e[t];u.tags.value=Array.isArray(e.tags)?e.tags:[],St?.setValue(u.tags.peek()),Array.isArray(e.adminTags)&&(u.adminTags.value=e.adminTags),Et?.setValue(u.adminTags.peek()),qo(O("#field-visibility"),u.visibility.peek()),Q.unfoldAll(),a.folded.value=!1,gt=!0,V.value=e.body==null?"":String(e.body),gt=!1,Q.refresh(),Ko(),ra()},la=(e,t)=>({urlPath:t||"",title:e?.title||"",description:e?.description||"",layout:Ho(e?.layout||"default"),status:e?.status||"draft",sortOrder:String(e?.sortOrder??99),showInNav:!!e?.showInNav,sidebar:!!e?.sidebar,breadcrumbs:e?e.breadcrumbs!==!1:!0,bundled:!!e?.bundled,tags:Array.isArray(e?.tags)?e.tags:[],adminTags:Array.isArray(e?.adminTags)?e.adminTags:[],category:e?.category||"",visibility:typeof e?.visibility=="string"&&e.visibility?e.visibility:"public",project:Cn({isEdit:!!t,page:e,projects:se,fromHash:t?"":Ft()||"",urlPath:t||""}),theme:e?.theme||"",seoTitle:e?.seo?.title||"",seoDesc:e?.seo?.description||"",seoSchema:fn(e?.seo?.schema),dconfig:e?.dconfig?JSON.stringify(e.dconfig,null,2):"",body:e?.content||"",css:null}),ca=()=>{m&&(m.state=_(),m.at=Date.now())},da=e=>{m!==e&&(ca(),m=e,Ze(e.state),a.base.value=e.base,a.curId.value=e.id,e.at=Date.now(),Mt(),Xe())},Ns=()=>{ca(),m=null;const e=ms();Ze(e),a.base.value=W(e),a.curId.value=null,Mt(),Xe()};Ze(ms()),a.base.value=W(_());const J=e=>{try{S.remove?S.remove(e):S.set(e,null)}catch{}},pa=(e,t)=>({name:e.name,...e.form,base:e.base,at:t});p=()=>{try{for(const t of Ae())S.set(ze(t.id),Ka(t===m?Go():t.state,t.base));ht()&&S.set(ft,u.css.peek());const e=new Date().toISOString();for(const t of ge()){const o=L.get(t);S.set(He(t),{form:{...o.form},base:o.base,at:e})}for(const t of Ye())S.set(Ve(t),pa(N.get(t),e))}catch{}};const ua=e=>{y()&&(p(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",ua),i(()=>window.removeEventListener("beforeunload",ua));let Qe=!1,Tt=null;const Mt=()=>{Qe=!1,Tt=null,a.versions.value=[],a.compare.value=null,a.histError.value="",a.dockOn.peek()&&a.dockTab.peek()==="history"&&(m?.isEdit?pe():a.dockTab.value="details")},pe=async()=>{const e=m;if(e?.isEdit){Qe=!0,a.histLoading.value=!0,a.histError.value="";try{const t=(await h.versions.list(e.id)).versions||[];if(!c()||m!==e)return;a.versions.value=t.map(o=>({...o,when:qe(o.createdAt),author:o.author||"",label:o.label||"",who:[o.author,o.label].filter(Boolean).join(" - "),checked:M.observable(!1)}))}catch(t){m===e&&(a.histError.value="Failed to load versions: "+t.message)}finally{m===e&&(a.histLoading.value=!1)}}},oo=e=>a.versions.peek().find(t=>t.filename===e),ao=async e=>{const t=m;try{const o=await h.versions.get(t.id,e.filename);if(!c()||m!==t)return;Tt=e.filename,a.compare.value={label:rs(e),old:o.content,current:Ut()}}catch(o){E.toast("Failed to load version: "+o.message,{type:"error"})}},Ds=async()=>{const e=m;if(!e?.isEdit)return;const t=await h.pages.get(e.id).catch(()=>null);if(!c()||m!==e||!t)return;e.page=t;const o=la(t,e.id);e.base=W(o),e.savedAdminTags=JSON.stringify(o.adminTags),Ze(o),a.base.value=e.base,Mt(),a.dockOn.peek()&&a.dockTab.peek()==="history"&&pe(),U()},ma=async e=>{if(!(!e||!await E.confirm("Restore this version? The current page content will be overwritten (a pre-restore snapshot will be saved automatically).")))try{await h.versions.restore(m.id,e),E.toast("Page restored.",{type:"success"}),await Ds()}catch(t){E.toast("Restore failed: "+t.message,{type:"error"})}},js=async e=>{if(await E.confirm("Delete this version? This cannot be undone."))try{await h.versions.delete(m.id,e.filename),E.toast("Version deleted.",{type:"success"}),await pe()}catch(t){E.toast("Failed to delete: "+t.message,{type:"error"})}},Ne=new Map,Bs=e=>{if(!Ne.has(e)){const t=I(e);Ne.set(e,{id:e,kind:t.kind,kindText:t.kind==="page"?"Page":t.kind==="component"?"Component":"Block",label:M.observable(""),title:M.observable(""),active:M.observable(!1),dirty:M.observable(!1)})}return Ne.get(e)},fa=e=>{const t=e===m?u.title.peek():e?.loaded?e.state.title:D.find(o=>o.urlPath===e?.id)?.title;return String(t||"").trim()||e?.id||"New page"};let ba=!0,va="";const Is=()=>{if(ba)return;const e={ids:P,active:a.active.peek()},t=JSON.stringify(e);if(t!==va){va=t;try{S.set(ps,e)}catch{}}},q=()=>{const e=P.map(Bs);for(const t of e){const o=I(t.id);let s,r;if(o.kind==="page"){const l=K.get(o.id);s=fa(l),r=l?.loaded?Yt(l):xe(o),t.title.value=o.id?`${s} (${o.id})`:"New page"}else if(o.kind==="component"){const l=N.get(o.id);s=o.id,r=l?he(l):xe(o),t.title.value=`Component <dm-${o.id}>`}else{const l=L.get(o.id);s=o.id,r=l?de(l):Re.has(o.id)||xe(o),t.title.value=`Block ${o.id}`}t.label.peek()!==s&&(t.label.value=s),t.dirty.peek()!==r&&(t.dirty.value=r);const n=t.id===a.active.peek();t.active.peek()!==n&&(t.active.value=n)}e.map(t=>t.id).join("|")!==a.tabs.peek().map(t=>t.id).join("|")&&(a.tabs.value=e),Is()};let ye=new Set;try{const e=S.get(us);Array.isArray(e)&&(ye=new Set(e.filter(t=>typeof t=="string")))}catch{}const ga=new Map,so=e=>{let t=ga.get(e.key);return t?(t.label.peek()!==e.label&&(t.label.value=e.label),t.meta.peek()!==(e.meta||"")&&(t.meta.value=e.meta||""),t.status.peek()!==(e.status||"")&&(t.status.value=e.status||""),t.folder.peek()!==!!e.folder&&(t.folder.value=!!e.folder)):(t={...e,label:M.observable(e.label),meta:M.observable(e.meta||""),status:M.observable(e.status||""),folder:M.observable(!!e.folder),active:M.observable(!1),dirty:M.observable(!1),folded:M.observable(!1),title:""},ga.set(e.key,t)),t.title=e.kind==="block"||e.kind==="component"?e.meta?`${e.id} - ${e.meta}`:e.id:e.kind==="folder"?e.meta:`${e.label} (${e.meta})`,t},no=new Map,z=()=>{const e=a.filter.peek(),t=I(a.active.peek()),o=t?.kind==="page"?t.id:null,s=t?.kind==="block"?t.id:null,r=t?.kind==="component"?t.id:null,n=new Set(Ae().map(f=>f.id)),l=Gn(D,{filter:e,folded:ye}).map(so);for(const f of l){const C=f.kind==="page"&&f.id===o,B=f.kind==="page"&&(n.has(f.id)||!K.get(f.id)?.loaded&&xe({kind:"page",id:f.id})),fe=ye.has(f.id);f.active.peek()!==C&&(f.active.value=C),f.dirty.peek()!==B&&(f.dirty.value=B),f.folded.peek()!==fe&&(f.folded.value=fe)}const g=Vn(ie,e).map(so),$=f=>{const C=L.get(f);return C?de(C):Re.has(f)||xe({kind:"block",id:f})};for(const f of g){const C=f.id===s,B=$(f.id);f.active.peek()!==C&&(f.active.value=C),f.dirty.peek()!==B&&(f.dirty.value=B)}const G=Kn(Je,e).map(so),Me=f=>{const C=N.get(f);return C?he(C):xe({kind:"component",id:f})};for(const f of G){const C=f.id===r,B=Me(f.id);f.active.peek()!==C&&(f.active.value=C),f.dirty.peek()!==B&&(f.dirty.value=B)}const Co=m?Dn(kt,_t,be):[],za=(f,C,B)=>(no.has(f)||no.set(f,{key:f,id:C,label:B,kindText:"",title:"",active:M.observable(!1),dirty:M.observable(!1)}),no.get(f)),xo=Co.map(f=>{const C=za(`block:${f.name}`,f.name,f.name);C.kindText=f.footer?"Footer":"Block",C.title=`${f.name} - ${f.footer?"in the footer, on every page":"a block on this page"}`;const B=f.name===s,fe=$(f.name);return C.active.peek()!==B&&(C.active.value=B),C.dirty.peek()!==fe&&(C.dirty.value=fe),C});for(const f of ta){const C=za(`component:${f}`,f,f);C.kindText="Component",C.title=`<dm-${f}> - a component on this page`;const B=f===r,fe=Me(f);C.active.peek()!==B&&(C.active.value=B),C.dirty.peek()!==fe&&(C.dirty.value=fe),xo.push(C)}const Dt=(f,C)=>f.map(B=>B.key).join("|")===C.map(B=>B.key).join("|");Dt(l,a.pageRows.peek())||(a.pageRows.value=l),Dt(g,a.blockRows.peek())||(a.blockRows.value=g),Dt(G,a.compRows.peek())||(a.compRows.value=G),Dt(xo,a.thisPage.peek())||(a.thisPage.value=xo)},ha=async()=>{const e=await h.pages.list().catch(()=>null);!c()||!Array.isArray(e)||(D=e,z(),q())},te=()=>{const e=I(a.active.peek()),t=e?es(e):"/pages/new",o=window.location.hash.replace(/^#/,"");j.address=o.split("?")[0]===t?o:t,o.split("?")[0]!==t?history.replaceState({pe:1},"","#"+t):history.state?.pe||history.replaceState({pe:1},"",window.location.hash)},Fs=async e=>{if(e.loading)return e.loading;e.loading=(async()=>{let t=null;if(e.isEdit){if(t=await h.pages.get(e.id).catch(()=>null),!c())return!1;if(!t)return E.toast(`Page ${e.id} not found.`,{type:"error"}),!1}e.page=t;const o=la(t,e.id);e.state=o,e.base=W(o),e.savedAdminTags=JSON.stringify(o.adminTags),e.loaded=!0;let s=null;try{s=S.get(ze(e.id))}catch{}const r=xn(s),n=$o({kind:"page",id:e.id});if(r){J(ze(e.id));const l=W(r.state)!==e.base,g=Mn(r,e.base),$={...o,...r.state,adminTags:o.adminTags};if(l&&n&&!g)e.state=$,E.toast(`Your unsaved changes to ${e.id||"the new page"} are back.`,{type:"info"});else if(l){const G=r.at?` (${qe(r.at)})`:"",Me=g?" The saved page has changed since - restoring puts your version back over it (the saved one stays in History).":"";await E.confirm(`You left unsaved changes to ${e.id||"a new page"}${G}. Restore them?${Me}`)&&c()&&(e.state=$)}}return!0})();try{return await e.loading}finally{e.loading=null}},ka=e=>(K.has(e)||K.set(e,{kind:"page",id:e,isEdit:!!e,loaded:!1,page:null,state:null,base:"",savedAdminTags:"[]",at:0}),K.get(e)),ue=async e=>{const t=X({kind:"page",id:e});({ids:P}=Bo(P,t)),a.active.value=t,a.blockTab.value="",vo();const o=ka(e);if(q(),!o.loaded){a.previewStatus.value=`Opening ${e||"a new page"}\u2026`;const s=await Fs(o);if(!c()||!P.includes(t)||K.get(e)!==o)return;if(!s){ya(t);return}if(a.active.peek()!==t){q(),z();return}}da(o),te(),q(),z(),U(),Q.refresh(),V.focus()};w=async(e,t)=>{if(j.address=t,e.kind==="page"){ue(e.id);return}if(!e.id){(e.kind==="block"?Lt:nt)({project:Ft(t)||""});return}const o=e.kind==="block"?be:le;if(o.has(e.id)||await(e.kind==="block"?Vo:_o)(),!!c()){if(!o.has(e.id)){E.toast(`There is no ${e.kind} called "${e.id}".`,{type:"warning"}),te(),z();return}e.kind==="block"?Be(e.id):st(e.id)}};const ya=e=>{const t=a.active.peek(),o=_n(P,e,t);P=o.ids,Ne.delete(e);const s=I(e),r=o.active!==t?I(o.active):null;if(s.kind==="page"){if(K.delete(s.id),m?.id===s.id){const n=[...K.values()].filter(l=>l.loaded&&l.id!==s.id).sort((l,g)=>g.at-l.at)[0];n?da(n):Ns()}}else s.kind==="component"?(N.delete(s.id),a.compTab.peek()===s.id&&vo()):(L.delete(s.id),a.blockTab.peek()===s.id&&(a.blockTab.value="",a.blockReady.value=!1));if(a.active.value=o.active,r?.kind==="page"){ue(r.id);return}if(r?.kind==="block"){Be(r.id);return}if(r?.kind==="component"){st(r.id);return}te(),Xe(),Le(),ke(),q(),z(),U()},et=e=>{const t=I(e);if(!t||!P.includes(e))return;let o="";try{if(t.kind==="page"){const s=K.get(t.id);s&&Yt(s)&&(S.set(ze(s.id),Ka(s===m?_():s.state,s.base)),Ro({kind:"page",id:s.id}),o=fa(s))}else if(t.kind==="component"){const s=N.get(t.id);s&&he(s)&&(S.set(Ve(t.id),pa(s,new Date().toISOString())),Ro({kind:"component",id:t.id}),o=t.id)}else{const s=L.get(t.id);s&&de(s)&&(S.set(He(t.id),{form:{...s.form},base:s.base,at:new Date().toISOString()}),Ro({kind:"block",id:t.id}),o=t.id)}}catch{}ya(e),o&&E.toast(`Changes to "${o}" kept as a draft - back when you open it again.`,{type:"info"})},Ct=e=>{const t=I(e);t&&(t.kind==="page"?ue(t.id):t.kind==="component"?st(t.id):Be(t.id))},wa=new Set,zs=async e=>{const t=[];for(const o of e){const s=L.get(o);if(!s)continue;const r={...s.form};try{const n=Za(r,s.block?.meta);await h.blocks.put(o,n),s.block={...s.block,meta:n.meta},s.saved=r,s.base=Do(r),vt.set(o,new Date().toISOString()),c()&&J(He(o)),t.push(o)}catch(n){E.toast(`Block "${o}" not saved: ${n.message||"Unknown error"}`,{type:"error"})}}return t.forEach(o=>wa.add(o)),t.length&&(Le(),pt("#/blocks")),t},qs=async e=>{const t=e===m?_():e.state,o=Po(t.seoSchema).error;if(o)return E.toast(`${e.id||"The new page"} not saved - structured data: ${o}`,{type:"error"}),e===m&&(a.seoSchemaNote.value=o,a.dockOn.value=!0,a.dockTab.value="seo"),!1;const s=Gt(t,e,Xt(t)),r=e.id;try{if(e.isEdit){const n={frontmatter:s,body:t.body};t.urlPath!==e.id&&(n.newUrlPath=t.urlPath),await h.pages.update(e.id,n)}else await h.pages.create({urlPath:t.urlPath,frontmatter:s,body:t.body})}catch(n){return E.toast(`${e.id||"The new page"} not saved: ${n.message||"Unknown error"}`,{type:"error"}),!1}if(!c())return!0;if(e.savedAdminTags=JSON.stringify(t.adminTags),e.base=W(t),e===m?a.base.value=e.base:e.state=t,J(ze(r)),!e.isEdit||t.urlPath!==e.id){K.delete(r),e.id=t.urlPath,e.isEdit=!0,K.set(e.id,e);const n=X({kind:"page",id:r}),l=X({kind:"page",id:e.id});Ne.delete(n),{ids:P}=Qn(P,n,l,a.active.peek()),a.active.peek()===n&&(a.active.value=l),e===m&&(a.curId.value=e.id,te())}return!0},Hs=async e=>{const t=[];for(const o of e){const s=N.get(o);if(!s||s.readOnly)continue;if(Rt===s?(clearTimeout(At),Rt=null,ke(),at(s)):s.state||at(s),s.compiling&&await s.compiling,!c())return t;if(s.state!=="ok"){E.toast(`Component "${o}" not saved: ${s.state==="error"?"it does not compile - fix the errors first.":"it has not compiled yet - try again in a moment."}`,{type:"warning"});continue}const r={...s.form},n={...s.meta||{}};delete n.bundled,r.project?n.project=r.project:delete n.project;try{await h.components.put(o,{source:r.source,bundled:r.bundled,meta:n}),s.meta=n,s.saved=r,s.base=Fo({name:o,...r});const l=Je.find(g=>g.name===o);l&&(l.bundled=r.bundled,l.meta=n),c()&&J(Ve(o)),t.push(o)}catch(l){E.toast(`Component "${o}" not saved: ${l.message||"Unknown error"}`,{type:"error"})}}return t.length&&(ke(),pt("#/components")),t};let ro=!1;const io=async(e=!1)=>{if(a.saving.peek()||ro)return;ro=!0;let t,o,s=[];try{if(await Ea(),!c())return;const n=I(a.active.peek());if(lr().formatOnSave&&vs(n),e)t=Ae(),o=ge(),s=Ye();else if(n?.kind==="block")t=[],o=L.has(n.id)&&de(L.get(n.id))?[n.id]:[];else if(n?.kind==="component")t=[],o=[],s=N.has(n.id)&&he(N.get(n.id))?[n.id]:[];else if(n?.kind==="page"&&m)o=ge().filter(yt),t=!m.isEdit||Wt()||!o.length?[m]:[];else return;if(!t.length&&!o.length&&!s.length){E.toast("Nothing to save.",{type:"info"});return}for(const g of t){const $=Ao(g===m?_():g.state);if($.length){E.toast(`${g.id||"New page"}: ${$[0].text}`,{type:"warning"}),g!==m&&await ue(g.id),a.dockOn.value=!0,a.dockTab.value=$[0].field==="dconfig"?"dconfig":"details",Ke("dock",!0);return}}const l=Fn(o,Object.fromEntries(o.map(g=>[g,Qa(Ue.get(g),m?.id||"")])),wa);if(l&&(!await E.confirm(l)||!c()))return}finally{ro=!1}const r=t.length>0&&ht();a.saving.value=!0;try{let n=0;for(const $ of t)await qs($)&&n++;if(!c())return;if(r){const $=u.css.peek();await h.settings.saveCustomCss($),ee.value=$,J(ft)}const l=await zs(o);if(!c())return;const g=await Hs(s);if((n||l.length||g.length)&&E.toast(zn({pages:n,css:r,blocks:l,components:g}),{type:"success"}),Xe(),!c())return;q(),z(),n&&(pt("#/pages"),Mt(),ha())}catch(n){E.toast(`Save failed: ${n.message||"Unknown error"}`,{type:"error"})}finally{a.saving.value=!1}};let oe=null,me=null;const De=new Map,je=new Map,Sa=()=>{const e=L.get(a.blockTab.peek());!e||!oe||(a.blockReady.value=!1,oe.setValue(e.form.content),me.setValue(e.form.css),a.blockProject.value=e.form.project,qo(O("#block-project"),e.form.project),a.blockBundled.value=e.form.bundled,a.blockWhere.value=Rn({usage:Ue.get(e.form.name)})||"Where it is used could not be read.",a.blockNote.value="",a.blockReady.value=!0,uo())},Vs=e=>{const t=L.get(e);if(!t)return{};const o=t.form.content;return Object.fromEntries(ts(Ga(o),No(o),t.samples||{}).map(s=>[s.name,s.value]))};i(M.effect(()=>{const e=a.blockProject.value,t=a.blockBundled.value,o=L.get(a.blockTab.peek());!o||!a.blockReady.peek()||o.form.project===e&&o.form.bundled===t||(o.form.project=e,o.form.bundled=t,Le())}));const lo=async(e,{quiet:t=!1}={})=>{for(;De.has(e);)if(await De.get(e),L.has(e)||t)return L.get(e)||null;if(L.has(e))return L.get(e);const o=(async()=>{try{const s=await h.blocks.get(e);if(!c()||!t&&!P.includes(X({kind:"block",id:e})))return null;const r=Xa({...s,name:e}),n={block:s,saved:r,base:Do(r),form:{...r},samples:{}};let l=null;try{l=S.get(He(e))}catch{}const g=Ln(l,n.base);if(t&&g?.stale)return null;if($o({kind:"block",id:e}),g){const $=g.at?` (${qe(g.at)})`:"",G=!g.stale||await E.confirm(`You left unsaved changes to "${e}"${$}, but the block has been saved since. Restore your version anyway?`);if(!c())return null;G&&(n.form={...n.form,...g.form,name:e},g.stale||E.toast(`Your unsaved changes to "${e}" are back.`,{type:"info"}))}return l&&J(He(e)),L.set(e,n),Le(),n}catch(s){return c()&&a.blockTab.peek()===e&&(a.blockNote.value=`Could not open "${e}": ${s.message||"unknown error"}`),null}finally{De.delete(e)}})();return De.set(e,o),o},Ea=async()=>{for(;De.size||je.size;)await Promise.all([...De.values(),...je.values()])},Be=async e=>{if(!e||!be.has(e))return;const t=X({kind:"block",id:e});({ids:P}=Bo(P,t)),a.active.value=t,vo(),a.cssMode.peek()&&(a.cssMode.value=!1),a.viewMode.peek()==="preview"&&(a.viewMode.value="split"),a.blockTab.peek()!==e&&(a.blockReady.value=!1),a.blockTab.value=e,a.blockUses.value=m&&yt(e)?qn(Qa(Ue.get(e),m.id||"")):jn(Ue.get(e)),te(),q(),z();let o=L.get(e);o||(a.blockReady.value=!1,a.blockNote.value=`Opening ${e}\u2026`,o=await lo(e)),!(!o||!c()||a.blockTab.peek()!==e)&&(Sa(),q(),z(),Zt=e,U(),(a.blockPart.peek()==="css"?me:oe)?.focus())};let co=null;i(()=>clearTimeout(co));const Ta=(e,t)=>{const o=L.get(a.blockTab.peek());!o||!a.blockReady.peek()||(o.form[e]=t,clearTimeout(co),co=setTimeout(()=>{Le(),e==="content"&&uo()},120),U())},we=O("#pe-block-samples"),Ma=e=>{we.replaceChildren();const t=document.createElement("p");t.className="pe-dock-none",t.textContent=e,we.appendChild(t)};let tt=[],po=null;function uo(){const e=L.get(a.blockTab.peek());if(!e){tt=[],po=null,Ma("Open a block to see its placeholders.");return}const t=ts(Ga(e.form.content),No(e.form.content),e.samples);if(!(po===e&&t.map(o=>o.name).join("|")===tt.join("|"))){if(tt=t.map(o=>o.name),po=e,!t.length){Ma("No {{placeholders}} in the template yet.");return}we.replaceChildren();for(const o of t){const s=document.createElement("div");s.className="pe-comp-prop",s.tabIndex=0,s.dataset.sample=o.name;const r=document.createElement("label");r.className="pe-comp-prop-label",r.textContent=o.name,r.title=o.raw?`{{{${o.name}}}} - raw markup`:`{{${o.name}}}`;const n=document.createElement("input");n.type="text",n.className="form-input form-input--sm pe-comp-prop-input",n.value=o.value,n.setAttribute("aria-label",`Sample value for ${o.name}`),n.addEventListener("input",()=>{e.samples[o.name]=n.value,U()});const l=document.createElement("button");l.type="button",l.className="pe-kebab",l.setAttribute("aria-label",`Actions for ${o.name}`),l.title="Actions",l.textContent="\u22EE",s.append(r,n,l),we.appendChild(s)}}}const mo=(e,t)=>No(e.form.content).has(t)?`{{{${t}}}}`:`{{${t}}}`,_s=e=>{const t=L.get(a.blockTab.peek());return!t||!tt.includes(e)?[]:[{header:mo(t,e)},{label:"Insert into the template",icon:"corner-down-right",action:()=>{const o=oe?.textarea;o&&(a.blockPart.value="html",o.focus(),o.setRangeText(mo(t,e),o.selectionStart,o.selectionEnd,"end"),o.dispatchEvent(new Event("input",{bubbles:!0})))}},{label:"Copy the placeholder",icon:"copy",action:()=>Ie(mo(t,e),"Placeholder")},{type:"divider"},{label:"Reset the sample",icon:"rotate-ccw",action:()=>{delete t.samples[e],tt=[],uo(),U()}}]};{const e=E.contextMenu(we,{cursor:"glyph",match:".pe-comp-prop",inherit:!1,density:"compact",items:t=>_s(t.dataset.sample)});i(()=>e?.destroy?.()),we.addEventListener("click",t=>{const o=t.target.closest(".pe-kebab");o&&Ce(o)}),we.addEventListener("keydown",t=>{const o=t.target.closest(".pe-comp-prop");o&&t.key==="F10"&&t.shiftKey&&(t.preventDefault(),Ce(o))})}const ae={},Se=O("#component-preview"),xt=e=>{try{Se?.contentWindow?.postMessage(e,"*")}catch{}};let fo=Se.contentDocument?.readyState==="complete"&&/component-preview/.test(Se.contentDocument?.URL||""),Pt=!1,ot=null;const Ca=()=>{fo=!0,ot&&c()&&(xt({type:"mount",payload:ot}),Pt=!0),ot=null};Se.addEventListener("load",Ca),i(()=>Se.removeEventListener("load",Ca));const Ks=e=>{if(!fo){ot=e;return}if(!Pt){Pt=!0,xt({type:"mount",payload:e});return}ot=e,fo=!1,Pt=!1,Se.src=Se.src};let Ot="";const bo=(e=!1)=>{const t=N.get(a.compTab.peek());if(!(!t||t.state!=="ok"||!t.js)){if(a.viewMode.peek()==="write"){t.previewStale=!0;return}!e&&Ot===t.name&&!t.previewStale||(t.previewStale=!1,Ot=t.name,Ks({compiledJs:t.js,tagName:`dm-${t.name}`,props:t.previewProps}))}},at=e=>{const t=++e.seq;return e.state="compiling",a.compTab.peek()===e.name&&(a.compState.value="compiling"),e.compiling=(async()=>{let o;try{o=await h.components.compile(e.name,e.form.source)}catch(s){o={errors:[{type:"request",message:s.message||"Compile request failed."}]}}if(!(t!==e.seq||!c())){if(o.errors?.length?(e.state="error",e.errors=o.errors.map(s=>`[${s.type}] ${s.message}`).join(`
|
|
140
|
-
`),e.js=null):(e.state="ok",e.errors="",e.js=o.js),a.compTab.peek()!==e.name){e.previewStale=!0;return}a.compState.value=e.state,a.compErrors.value=e.errors,e.state==="ok"&&(yo(),bo(!0))}})().finally(()=>{e.compiling&&t===e.seq&&(e.compiling=null)}),e.compiling};let At=null,Rt=null;i(()=>clearTimeout(At));const xa=()=>{const e=N.get(a.compTab.peek());if(!e||!ae.template)return;a.compReady.value=!1;const t=ut(e.form.source);for(const o of zt)ae[o].setValue(t[o]),ae[o].textarea.readOnly=e.readOnly;a.compReadOnly.value=e.readOnly,a.compProject.value=e.form.project,qo(O("#comp-project"),e.form.project),a.compBundled.value=e.form.bundled,a.compState.value=e.state,a.compErrors.value=e.errors,a.compNote.value="",a.compReady.value=!0,yo(),e.state==="ok"?bo():e.state||at(e)},vo=()=>{a.compTab.peek()&&(a.compTab.value="",a.compReady.value=!1)},Pa={page:"details",block:"details",component:"details"};let $t="";i(M.effect(()=>{const e=Qo.value;e!==$t&&($t&&(Pa[$t]=a.dockTab.peek()),$t=e,e&&(a.dockTab.value=Pa[e]||"details",e==="page"&&a.dockTab.peek()==="history"&&a.dockOn.peek()&&m?.isEdit&&!Qe&&pe()))}));const go=async(e,{quiet:t=!1}={})=>{if(je.has(e))return je.get(e);if(N.has(e))return N.get(e);const o=(async()=>{try{const s=await h.components.get(e);if(!c()||!P.includes(X({kind:"component",id:e})))return null;const r=s.readOnly===!0||s.origin==="plugin",n={project:s.meta?.project||"",bundled:!!s.bundled,source:os(ut(s.source))},l={name:e,comp:s,saved:n,base:Fo({name:e,...n}),form:{...n},meta:s.meta||{},readOnly:r,state:"",errors:"",js:null,seq:0,compiling:null,previewProps:{},previewStale:!1};let g=null;try{g=S.get(Ve(e))}catch{}const $=nr(g,l.base);if(t&&(!$.ok||$.stale||r))return null;const G=$o({kind:"component",id:e});if(t&&!G)return null;if($.ok&&!r){const Me=g.at?` (${qe(g.at)})`:"",Co=G&&!$.stale||await E.confirm($.stale?`You left unsaved changes to the component "${e}"${Me}, but it has been saved since. Restore your version anyway?`:`You left unsaved changes to the component "${e}"${Me}. Restore them?`);if(!c())return null;Co&&(l.form={project:String(g.project??""),bundled:g.bundled===!0,source:String(g.source)},G&&!$.stale&&E.toast(`Your unsaved changes to the component "${e}" are back.`,{type:"info"}))}return g&&J(Ve(e)),N.set(e,l),ke(),l}catch(s){return c()&&a.compTab.peek()===e&&(a.compNote.value=`Could not open <dm-${e}>: ${s.message||"unknown error"}`),null}finally{je.delete(e)}})();return je.set(e,o),o},st=async e=>{if(!e||!le.has(e))return;const t=X({kind:"component",id:e});({ids:P}=Bo(P,t)),a.active.value=t,a.blockTab.value="",a.cssMode.peek()&&(a.cssMode.value=!1),a.viewMode.peek()==="preview"&&(a.viewMode.value="split"),a.compTab.value=e,te(),q(),z();let o=N.get(e);o||(a.compReady.value=!1,a.compReadOnly.value=!1,a.compState.value="",a.compNote.value=`Opening <dm-${e}>\u2026`,o=await go(e)),!(!o||!c()||a.compTab.peek()!==e)&&(xa(),q(),z(),ae[a.compPart.peek()]?.focus())},Us=(e,t)=>{const o=N.get(a.compTab.peek());if(!o||!a.compReady.peek()||o.readOnly)return;const s=ut(o.form.source);s[e]=t,o.form.source=os(s),clearTimeout(At),Rt=o,At=setTimeout(()=>{Rt=null,ke(),at(o)},300)};i(M.effect(()=>{const e=a.compProject.value,t=a.compBundled.value,o=N.get(a.compTab.peek());!o||!a.compReady.peek()||o.readOnly||o.form.project===e&&o.form.bundled===t||(o.form.project=e,o.form.bundled=t,ke())}));const Ee=O("#pe-comp-props");let ho=[];const ko=e=>{Ee.replaceChildren();const t=document.createElement("p");t.className="pe-dock-none",t.textContent=e,Ee.appendChild(t)};function yo(){const e=N.get(a.compTab.peek());if(!e){ho=[],ko("Open a component to try its props.");return}const{rows:t,error:o}=as(ut(e.form.source).props,e.previewProps);if(ho=t,o){ko("The <props> block is not valid JSON - fix it to see the props here.");return}if(!t.length){e.previewProps={},ko("No props declared. Add them in the <props> block.");return}e.previewProps=Object.fromEntries(t.map(s=>[s.name,s.value])),Ee.replaceChildren();for(const s of t){const r=document.createElement("div");r.className="pe-comp-prop",r.tabIndex=0,r.dataset.prop=s.name;const n=document.createElement("label");n.className="pe-comp-prop-label",n.textContent=s.label,n.title=`${s.name} (${s.type})`;const l=s.type==="array"||s.type==="object"?document.createElement("textarea"):document.createElement("input");s.type==="boolean"?(l.type="checkbox",l.className="form-check",l.checked=ss(s.type,s.value)):(l.tagName==="INPUT"?l.type=s.type==="number"?"number":"text":l.rows=2,l.className="form-input form-input--sm pe-comp-prop-input",l.value=ss(s.type,s.value)),l.setAttribute("aria-label",s.label);const g=()=>{e.previewProps[s.name]=ir(s.type,s.type==="boolean"?l.checked:l.value),Ot===e.name&&xt({type:"update",payload:{props:e.previewProps}})};l.addEventListener("input",g),l.addEventListener("change",g);const $=document.createElement("button");$.type="button",$.className="pe-kebab",$.setAttribute("aria-label",`Actions for ${s.label}`),$.title="Actions",$.textContent="\u22EE",r.append(n,l,$),Ee.appendChild(r)}}const Ie=async(e,t)=>{try{await navigator.clipboard.writeText(e),E.toast(`${t} copied.`,{type:"success"})}catch{E.toast(e,{type:"info",duration:8e3})}},Js=e=>{const t=N.get(a.compTab.peek()),o=ho.find(n=>n.name===e);if(!t||!o)return[];const s=sr(o.name,o.type,t.previewProps[o.name]),r=`dm-${t.name}`;return[{header:o.label},{label:"Reset to its default",icon:"rotate-ccw",action:()=>{delete t.previewProps[o.name];const n=as(ut(t.form.source).props,{}).rows.find(l=>l.name===o.name);t.previewProps[o.name]=n?n.value:null,yo(),Ot===t.name&&xt({type:"update",payload:{props:t.previewProps}})}},{label:"Copy as an attribute",icon:"copy",disabled:!s,action:()=>Ie(s,"Attribute")},{label:"Copy the tag with this value",icon:"code",action:()=>Ie(`<${r}${s?` ${s}`:""}></${r}>`,"Tag")},{label:"Copy the prop name",icon:"copy",action:()=>Ie(o.name,"Name")},{type:"divider"},{label:"Edit in <props>",icon:"edit",action:()=>{a.compPart.value="props";const n=ae.props?.textarea;if(!n)return;const l=n.value.indexOf(`"${o.name}"`);n.focus(),l>=0&&n.setSelectionRange(l,l+o.name.length+2)}}]};{const e=E.contextMenu(Ee,{cursor:"glyph",match:".pe-comp-prop",inherit:!1,density:"compact",items:t=>Js(t.dataset.prop)});i(()=>e?.destroy?.()),Ee.addEventListener("click",t=>{const o=t.target.closest(".pe-kebab");o&&Ce(o)}),Ee.addEventListener("keydown",t=>{const o=t.target.closest(".pe-comp-prop");o&&t.key==="F10"&&t.shiftKey&&(t.preventDefault(),Ce(o))})}let Fe=!1;const Oa=()=>Ft()||"",Lt=async({project:e=Oa(),seed:t="",content:o=gr,css:s=""}={})=>{if(!Fe){Fe=!0;try{const n=await ls({kind:"block",projects:se,taken:[...be],project:e,name:t,check:An,register:i,create:async(l,g)=>{await h.blocks.put(l,Za({name:l,project:g,bundled:!1,content:o,css:s}))}});if(!c())return;if(!n){te();return}if(await Vo(),!c())return;pt("#/blocks"),E.toast(`Created the block "${n}".`,{type:"success"}),Be(n)}finally{Fe=!1}}},nt=async({name:e="",source:t=ar,project:o=Oa()}={})=>{if(!Fe){Fe=!0;try{const s=await ls({kind:"component",projects:se,taken:[...le],project:o,name:e,register:i,check:(r,n)=>rr(r,{isNew:!0,taken:n}),create:async(r,n)=>{await h.components.put(r,{source:t,bundled:!1,meta:n?{project:n}:{}})}});if(!c())return;if(!s){te();return}if(await _o(),!c())return;pt("#/components"),E.toast(`Created the component "${s}".`,{type:"success"}),st(s)}finally{Fe=!1}}},Aa=k.querySelector(".dm-plugin-body")||k,Ws=M.computed(()=>{a.blockRev.value,a.compRev.value,a.docRev.value,W(_()),a.base.value;const e=I(a.active.value),t=[...Ae().map(s=>X({kind:"page",id:s.id})),...ge().map(s=>X({kind:"block",id:s})),...Ye().map(s=>X({kind:"component",id:s}))];let o=[];return e?.kind==="block"||e?.kind==="component"?o=[a.active.value]:e?.kind==="page"&&m&&(o=[a.active.value,...ge().filter(yt).map(s=>X({kind:"block",id:s}))]),er(t,o)}),Ys={...u,...a,dirty:ks,pageDirty:gs,activeDirty:ys,cssUnsaved:hs,hasPage:ws,activeKind:Xo,canView:Zo,viewHref:Ss,problem:Es,statusText:Ts,saveAll:Ws,dockKind:Qo,blockTagText:Ms,compStateText:Cs,compTagText:xs,anySelected:Ps,allSelected:Os,someSelected:As},rt=(e,t)=>Ke(e,t),wo={save:()=>io(!1),saveAllOpen:()=>io(!0),discard:async()=>{if(await Ea(),!c())return;const e=I(a.active.peek());if(e?.kind==="block"){const t=L.get(e.id);if(!t||!de(t)||!await E.confirm(`Discard the changes to "${e.id}" you have not saved?`))return;t.form={...t.saved},J(He(e.id)),Sa(),Le()}else if(e?.kind==="component"){const t=N.get(e.id);if(!t||!he(t)||!await E.confirm(`Discard the changes to the component "${e.id}" you have not saved?`))return;t.form={...t.saved},t.state="",J(Ve(e.id)),xa(),ke()}else{if(!m||e?.kind==="page"&&e.id!==m.id||!(Wt()||ht())||!await E.confirm("Discard the changes to this page you have not saved?"))return;Ze(JSON.parse(a.base.peek())),u.cssOpened.peek()&&(u.css.value=ee.peek()),J(ze(m.id)),J(ft),Xe()}q(),z(),U()},newPage:()=>ue(""),newBlock:()=>Lt(),newComponent:()=>nt(),toggleSection:e=>{const t={here:"secHere",pages:"secPages",blocks:"secBlocks",components:"secComps"}[e];t&&(a[t].value=!a[t].peek(),rt(t,a[t].peek()))},toggleExplorer:()=>{a.explorerOn.value=!a.explorerOn.peek(),rt("explorer",a.explorerOn.peek())},toggleDock:()=>{a.dockOn.value=!a.dockOn.peek(),rt("dock",a.dockOn.peek()),a.dockOn.peek()&&a.dockTab.peek()==="history"&&!Qe&&pe()},showDock:e=>{a.dockTab.value=e,e==="history"&&m?.isEdit&&!Qe&&pe()},schemaChanged:()=>{a.seoSchemaNote.value=Po(u.seoSchema.peek()).error},insertSchemaExample:async e=>{const t=e?.target,o=t?.value||"";if(t&&(t.value=""),!o||u.seoSchema.peek().trim()&&!await E.confirm("Replace the structured data with the example?"))return;const s=u.urlPath.peek().trim()||m?.id||"/";u.seoSchema.value=mn(o,{title:u.seoTitle.peek().trim()||u.title.peek().trim(),description:u.seoDesc.peek().trim()||u.description.peek().trim(),url:`${location.origin}${s}`,siteName:Y?.title||Y?.seo?.defaultTitle||""}),a.seoSchemaNote.value=""},setBlockPart:e=>{a.blockPart.value=e==="css"?"css":"html",(e==="css"?me:oe)?.focus()},formatBlock:()=>{const e=a.blockPart.peek()==="css",t=e?me:oe;!t||!a.blockReady.peek()||Uo(_e(t.textarea,e?"css":"html"))},copyShortcode:async()=>{const e=a.blockTab.peek();e&&await Ie(Nn(e),"Shortcode")},setCompPart:e=>{a.compPart.value=zt.includes(e)?e:"template",ae[a.compPart.peek()]?.focus()},copyComponent:()=>{const e=N.get(a.compTab.peek());e&&nt({name:Lo(e.name,[...le]),source:e.form.source})},setMode:e=>{a.cssMode.peek()&&(a.cssMode.value=!1),a.viewMode.value=jo(e),rt("viewMode",a.viewMode.peek()),e!=="write"&&(U(!0),bo())},toggleFullscreen:()=>{a.fullscreen.value=!a.fullscreen.peek(),rt("fullscreen",a.fullscreen.peek())},toggleCss:()=>{a.blockTab.peek()||a.compTab.peek()||(a.cssMode.peek()?(a.cssMode.value=!1,a.viewMode.value=jo(Pe().viewMode),Q.refresh()):(ia(),a.cssMode.value=!0,a.viewMode.value="split"))},formatMarkdown:()=>{Jt(),Uo(_e(V,"md"))},toggleFold:()=>{a.folded.peek()?Q.unfoldAll():Q.foldAll(),a.folded.value=!a.folded.peek(),We.paint()},setWidth:e=>{a.liveWidth.value=e},refreshPreview:()=>{if(a.compTab.peek()){const e=N.get(a.compTab.peek());e&&at(e)}else U(!0)},share:async()=>{if(!m?.isEdit){E.toast("Save the page first - a share link needs a page to point at.",{type:"warning"});return}await br(m.id)},toggleAll:e=>{const t=!!e?.target?.checked;a.versions.peek().forEach(o=>{o.checked.value=t})},deleteSelected:async()=>{const e=a.versions.peek().filter(o=>o.checked.peek()).map(o=>o.filename),t=e.length;if(!(!t||!await E.confirm(`Delete ${t} version${t>1?"s":""}? This cannot be undone.`)))try{await h.versions.bulkDelete(m.id,e),E.toast(`${t} version${t>1?"s":""} deleted.`,{type:"success"}),await pe()}catch(o){E.toast("Delete failed: "+o.message,{type:"error"})}},saveNamed:async()=>{if(!m?.isEdit)return;const e=m;await vr(e.id)&&c()&&m===e&&await pe()},closeCompare:()=>{a.compare.value=null,Tt=null},restoreCompared:()=>ma(Tt)};Ge.querySelectorAll("[data-tip]").forEach(e=>E.tooltip(e,{content:e.getAttribute("data-tip"),position:"top"}));const Gs=M.applyBindings(Ys,Aa,{methods:wo});i(()=>Gs.dispose()),oe=zo(O('.pe-block-edit[data-part="html"]'),{ariaLabel:"Block template HTML",language:"html",onChange:e=>Ta("content",e)}),me=zo(O('.pe-block-edit[data-part="css"]'),{ariaLabel:"Block CSS",language:"css",placeholder:`/* Scoped to this block - e.g.
|
|
139
|
+
</span>`);Ge.prepend(Ns),Ge.before(O(".pe-block-bar")),Ge.before(O(".pe-comp-bar"));const aa=e=>(Array.isArray(e)?e:[]).map(t=>({value:t,label:t})),sa=O("#field-tags");let Et=null;Et=sa?E.pillbox(sa,{data:aa(_t),value:[],creatable:!0,searchable:!0,placeholder:"Add tag\u2026",onChange:()=>{Et&&(p.tags.value=[...Et.getValue()])}}):null;const na=O("#field-admin-tags");let Mt=null;Mt=na?E.pillbox(na,{data:aa(T),value:[],creatable:!0,searchable:!0,placeholder:"Add admin tag\u2026",onChange:()=>{Mt&&(p.adminTags.value=[...Mt.getValue()])}}):null;const ra=()=>{o.bodyRev.value=o.bodyRev.peek()+1},Le=()=>{o.blockRev.value=o.blockRev.peek()+1},ye=()=>{o.compRev.value=o.compRev.peek()+1},Xe=()=>{o.docRev.value=o.docRev.peek()+1},ia=()=>{p.cssOpened.peek()||(p.css.peek()||(p.css.value=ee.peek()),p.cssOpened.value=!0,an(ve,{language:"css"}))},Ze=e=>{for(const t of Or)p[t].value=e[t]==null?"":String(e[t]);for(const t of Rr)p[t].value=!!e[t];p.tags.value=Array.isArray(e.tags)?e.tags:[],Et?.setValue(p.tags.peek()),Array.isArray(e.adminTags)&&(p.adminTags.value=e.adminTags),Mt?.setValue(p.adminTags.peek()),qo(O("#field-visibility"),p.visibility.peek()),Q.unfoldAll(),o.folded.value=!1,gt=!0,V.value=e.body==null?"":String(e.body),gt=!1,Q.refresh(),Ko(),ra()},la=(e,t)=>({urlPath:t||"",title:e?.title||"",description:e?.description||"",layout:Ho(e?.layout||"default"),status:e?.status||"draft",sortOrder:String(e?.sortOrder??99),showInNav:!!e?.showInNav,sidebar:!!e?.sidebar,breadcrumbs:e?e.breadcrumbs!==!1:!0,bundled:!!e?.bundled,tags:Array.isArray(e?.tags)?e.tags:[],adminTags:Array.isArray(e?.adminTags)?e.adminTags:[],category:e?.category||"",visibility:typeof e?.visibility=="string"&&e.visibility?e.visibility:"public",project:Pn({isEdit:!!t,page:e,projects:se,fromHash:t?"":zt()||"",urlPath:t||""}),theme:e?.theme||"",seoTitle:e?.seo?.title||"",seoDesc:e?.seo?.description||"",seoSchema:bn(e?.seo?.schema),dconfig:e?.dconfig?JSON.stringify(e.dconfig,null,2):"",body:e?.content||"",css:null}),ca=()=>{m&&(m.state=_(),m.at=Date.now())},da=e=>{m!==e&&(ca(),m=e,Ze(e.state),o.base.value=e.base,o.curId.value=e.id,e.at=Date.now(),Ct(),Xe())},Ds=()=>{ca(),m=null;const e=ms();Ze(e),o.base.value=W(e),o.curId.value=null,Ct(),Xe()};Ze(ms()),o.base.value=W(_());const J=e=>{try{S.remove?S.remove(e):S.set(e,null)}catch{}},pa=(e,t)=>({name:e.name,...e.form,base:e.base,at:t});u=()=>{try{for(const t of Re())S.set(ze(t.id),Ka(t===m?Go():t.state,t.base));yt()&&S.set(ft,p.css.peek());const e=new Date().toISOString();for(const t of ge()){const a=L.get(t);S.set(He(t),{form:{...a.form},base:a.base,at:e})}for(const t of Ye())S.set(Ve(t),pa(N.get(t),e))}catch{}};const ua=e=>{k()&&(u(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",ua),i(()=>window.removeEventListener("beforeunload",ua));let Qe=!1,Tt=null;const Ct=()=>{Qe=!1,Tt=null,o.versions.value=[],o.compare.value=null,o.histError.value="",o.dockOn.peek()&&o.dockTab.peek()==="history"&&(m?.isEdit?pe():o.dockTab.value="details")},pe=async()=>{const e=m;if(e?.isEdit){Qe=!0,o.histLoading.value=!0,o.histError.value="";try{const t=(await h.versions.list(e.id)).versions||[];if(!c()||m!==e)return;o.versions.value=t.map(a=>({...a,when:qe(a.createdAt),author:a.author||"",label:a.label||"",who:[a.author,a.label].filter(Boolean).join(" - "),checked:M.observable(!1)}))}catch(t){m===e&&(o.histError.value="Failed to load versions: "+t.message)}finally{m===e&&(o.histLoading.value=!1)}}},oo=e=>o.versions.peek().find(t=>t.filename===e),ao=async e=>{const t=m;try{const a=await h.versions.get(t.id,e.filename);if(!c()||m!==t)return;Tt=e.filename,o.compare.value={label:rs(e),old:a.content,current:ht()}}catch(a){E.toast("Failed to load version: "+a.message,{type:"error"})}},js=async()=>{const e=m;if(!e?.isEdit)return;const t=await h.pages.get(e.id).catch(()=>null);if(!c()||m!==e||!t)return;e.page=t;const a=la(t,e.id);e.base=W(a),e.savedAdminTags=JSON.stringify(a.adminTags),Ze(a),o.base.value=e.base,Ct(),o.dockOn.peek()&&o.dockTab.peek()==="history"&&pe(),U()},ma=async e=>{if(!(!e||!await E.confirm("Restore this version? The current page content will be overwritten (a pre-restore snapshot will be saved automatically).")))try{await h.versions.restore(m.id,e),E.toast("Page restored.",{type:"success"}),await js()}catch(t){E.toast("Restore failed: "+t.message,{type:"error"})}},Bs=async e=>{if(await E.confirm("Delete this version? This cannot be undone."))try{await h.versions.delete(m.id,e.filename),E.toast("Version deleted.",{type:"success"}),await pe()}catch(t){E.toast("Failed to delete: "+t.message,{type:"error"})}},Ne=new Map,Is=e=>{if(!Ne.has(e)){const t=I(e);Ne.set(e,{id:e,kind:t.kind,kindText:t.kind==="page"?"Page":t.kind==="component"?"Component":"Block",label:M.observable(""),title:M.observable(""),active:M.observable(!1),dirty:M.observable(!1)})}return Ne.get(e)},fa=e=>{const t=e===m?p.title.peek():e?.loaded?e.state.title:D.find(a=>a.urlPath===e?.id)?.title;return String(t||"").trim()||e?.id||"New page"};let ba=!0,va="";const Fs=()=>{if(ba)return;const e={ids:P,active:o.active.peek()},t=JSON.stringify(e);if(t!==va){va=t;try{S.set(ps,e)}catch{}}},q=()=>{const e=P.map(Is);for(const t of e){const a=I(t.id);let s,r;if(a.kind==="page"){const l=K.get(a.id);s=fa(l),r=l?.loaded?Yt(l):xe(a),t.title.value=a.id?`${s} (${a.id})`:"New page"}else if(a.kind==="component"){const l=N.get(a.id);s=a.id,r=l?he(l):xe(a),t.title.value=`Component <dm-${a.id}>`}else{const l=L.get(a.id);s=a.id,r=l?de(l):Ae.has(a.id)||xe(a),t.title.value=`Block ${a.id}`}t.label.peek()!==s&&(t.label.value=s),t.dirty.peek()!==r&&(t.dirty.value=r);const n=t.id===o.active.peek();t.active.peek()!==n&&(t.active.value=n)}e.map(t=>t.id).join("|")!==o.tabs.peek().map(t=>t.id).join("|")&&(o.tabs.value=e),Fs()};let ke=new Set;try{const e=S.get(us);Array.isArray(e)&&(ke=new Set(e.filter(t=>typeof t=="string")))}catch{}const ga=new Map,so=e=>{let t=ga.get(e.key);return t?(t.label.peek()!==e.label&&(t.label.value=e.label),t.meta.peek()!==(e.meta||"")&&(t.meta.value=e.meta||""),t.status.peek()!==(e.status||"")&&(t.status.value=e.status||""),t.folder.peek()!==!!e.folder&&(t.folder.value=!!e.folder)):(t={...e,label:M.observable(e.label),meta:M.observable(e.meta||""),status:M.observable(e.status||""),folder:M.observable(!!e.folder),active:M.observable(!1),dirty:M.observable(!1),folded:M.observable(!1),title:""},ga.set(e.key,t)),t.title=e.kind==="block"||e.kind==="component"?e.meta?`${e.id} - ${e.meta}`:e.id:e.kind==="folder"?e.meta:`${e.label} (${e.meta})`,t},no=new Map,z=()=>{const e=o.filter.peek(),t=I(o.active.peek()),a=t?.kind==="page"?t.id:null,s=t?.kind==="block"?t.id:null,r=t?.kind==="component"?t.id:null,n=new Set(Re().map(f=>f.id)),l=Zn(D,{filter:e,folded:ke}).map(so);for(const f of l){const C=f.kind==="page"&&f.id===a,B=f.kind==="page"&&(n.has(f.id)||!K.get(f.id)?.loaded&&xe({kind:"page",id:f.id})),fe=ke.has(f.id);f.active.peek()!==C&&(f.active.value=C),f.dirty.peek()!==B&&(f.dirty.value=B),f.folded.peek()!==fe&&(f.folded.value=fe)}const g=Kn(ie,e).map(so),$=f=>{const C=L.get(f);return C?de(C):Ae.has(f)||xe({kind:"block",id:f})};for(const f of g){const C=f.id===s,B=$(f.id);f.active.peek()!==C&&(f.active.value=C),f.dirty.peek()!==B&&(f.dirty.value=B)}const G=Jn(Je,e).map(so),Te=f=>{const C=N.get(f);return C?he(C):xe({kind:"component",id:f})};for(const f of G){const C=f.id===r,B=Te(f.id);f.active.peek()!==C&&(f.active.value=C),f.dirty.peek()!==B&&(f.dirty.value=B)}const Co=m?Bn(kt,Kt,be):[],za=(f,C,B)=>(no.has(f)||no.set(f,{key:f,id:C,label:B,kindText:"",title:"",active:M.observable(!1),dirty:M.observable(!1)}),no.get(f)),xo=Co.map(f=>{const C=za(`block:${f.name}`,f.name,f.name);C.kindText=f.footer?"Footer":"Block",C.title=`${f.name} - ${f.footer?"in the footer, on every page":"a block on this page"}`;const B=f.name===s,fe=$(f.name);return C.active.peek()!==B&&(C.active.value=B),C.dirty.peek()!==fe&&(C.dirty.value=fe),C});for(const f of ta){const C=za(`component:${f}`,f,f);C.kindText="Component",C.title=`<dm-${f}> - a component on this page`;const B=f===r,fe=Te(f);C.active.peek()!==B&&(C.active.value=B),C.dirty.peek()!==fe&&(C.dirty.value=fe),xo.push(C)}const jt=(f,C)=>f.map(B=>B.key).join("|")===C.map(B=>B.key).join("|");jt(l,o.pageRows.peek())||(o.pageRows.value=l),jt(g,o.blockRows.peek())||(o.blockRows.value=g),jt(G,o.compRows.peek())||(o.compRows.value=G),jt(xo,o.thisPage.peek())||(o.thisPage.value=xo)},ha=async()=>{const e=await h.pages.list().catch(()=>null);!c()||!Array.isArray(e)||(D=e,z(),q())},te=()=>{const e=I(o.active.peek()),t=e?es(e):"/pages/new",a=window.location.hash.replace(/^#/,"");j.address=a.split("?")[0]===t?a:t,a.split("?")[0]!==t?history.replaceState({pe:1},"","#"+t):history.state?.pe||history.replaceState({pe:1},"",window.location.hash)},zs=async e=>{if(e.loading)return e.loading;e.loading=(async()=>{let t=null;if(e.isEdit){if(t=await h.pages.get(e.id).catch(()=>null),!c())return!1;if(!t)return E.toast(`Page ${e.id} not found.`,{type:"error"}),!1}e.page=t;const a=la(t,e.id);e.state=a,e.base=W(a),e.savedAdminTags=JSON.stringify(a.adminTags),e.loaded=!0;let s=null;try{s=S.get(ze(e.id))}catch{}const r=On(s),n=$o({kind:"page",id:e.id});if(r){J(ze(e.id));const l=W(r.state)!==e.base,g=xn(r,e.base),$={...a,...r.state,adminTags:a.adminTags};if(l&&n&&!g)e.state=$,E.toast(`Your unsaved changes to ${e.id||"the new page"} are back.`,{type:"info"});else if(l){const G=r.at?` (${qe(r.at)})`:"",Te=g?" The saved page has changed since - restoring puts your version back over it (the saved one stays in History).":"";await E.confirm(`You left unsaved changes to ${e.id||"a new page"}${G}. Restore them?${Te}`)&&c()&&(e.state=$)}}return!0})();try{return await e.loading}finally{e.loading=null}},ya=e=>(K.has(e)||K.set(e,{kind:"page",id:e,isEdit:!!e,loaded:!1,page:null,state:null,base:"",savedAdminTags:"[]",at:0}),K.get(e)),ue=async e=>{const t=X({kind:"page",id:e});({ids:P}=Bo(P,t)),o.active.value=t,o.blockTab.value="",vo();const a=ya(e);if(q(),!a.loaded){o.previewStatus.value=`Opening ${e||"a new page"}\u2026`;const s=await zs(a);if(!c()||!P.includes(t)||K.get(e)!==a)return;if(!s){ka(t);return}if(o.active.peek()!==t){q(),z();return}}da(a),te(),q(),z(),U(),Q.refresh(),V.focus()};w=async(e,t)=>{if(j.address=t,e.kind==="page"){ue(e.id);return}if(!e.id){(e.kind==="block"?Nt:nt)({project:zt(t)||""});return}const a=e.kind==="block"?be:le;if(a.has(e.id)||await(e.kind==="block"?Vo:_o)(),!!c()){if(!a.has(e.id)){E.toast(`There is no ${e.kind} called "${e.id}".`,{type:"warning"}),te(),z();return}e.kind==="block"?Be(e.id):st(e.id)}};const ka=e=>{const t=o.active.peek(),a=Un(P,e,t);P=a.ids,Ne.delete(e);const s=I(e),r=a.active!==t?I(a.active):null;if(s.kind==="page"){if(K.delete(s.id),m?.id===s.id){const n=[...K.values()].filter(l=>l.loaded&&l.id!==s.id).sort((l,g)=>g.at-l.at)[0];n?da(n):Ds()}}else s.kind==="component"?(N.delete(s.id),o.compTab.peek()===s.id&&vo()):(L.delete(s.id),o.blockTab.peek()===s.id&&(o.blockTab.value="",o.blockReady.value=!1));if(o.active.value=a.active,r?.kind==="page"){ue(r.id);return}if(r?.kind==="block"){Be(r.id);return}if(r?.kind==="component"){st(r.id);return}te(),Xe(),Le(),ye(),q(),z(),U()},et=e=>{const t=I(e);if(!t||!P.includes(e))return;let a="";try{if(t.kind==="page"){const s=K.get(t.id);s&&Yt(s)&&(S.set(ze(s.id),Ka(s===m?_():s.state,s.base)),Ao({kind:"page",id:s.id}),a=fa(s))}else if(t.kind==="component"){const s=N.get(t.id);s&&he(s)&&(S.set(Ve(t.id),pa(s,new Date().toISOString())),Ao({kind:"component",id:t.id}),a=t.id)}else{const s=L.get(t.id);s&&de(s)&&(S.set(He(t.id),{form:{...s.form},base:s.base,at:new Date().toISOString()}),Ao({kind:"block",id:t.id}),a=t.id)}}catch{}ka(e),a&&E.toast(`Changes to "${a}" kept as a draft - back when you open it again.`,{type:"info"})},xt=e=>{const t=I(e);t&&(t.kind==="page"?ue(t.id):t.kind==="component"?st(t.id):Be(t.id))},wa=new Set,qs=async e=>{const t=[];for(const a of e){const s=L.get(a);if(!s)continue;const r={...s.form};try{const n=Za(r,s.block?.meta);await h.blocks.put(a,n),s.block={...s.block,meta:n.meta},s.saved=r,s.base=Do(r),vt.set(a,new Date().toISOString()),c()&&J(He(a)),t.push(a)}catch(n){E.toast(`Block "${a}" not saved: ${n.message||"Unknown error"}`,{type:"error"})}}return t.forEach(a=>wa.add(a)),t.length&&(Le(),pt("#/blocks")),t},Hs=async e=>{const t=e===m?_():e.state,a=Po(t.seoSchema).error;if(a)return E.toast(`${e.id||"The new page"} not saved - structured data: ${a}`,{type:"error"}),e===m&&(o.seoSchemaNote.value=a,o.dockOn.value=!0,o.dockTab.value="seo"),!1;const s=Gt(t,e,Xt(t)),r=e.id;try{if(e.isEdit){const n={frontmatter:s,body:t.body};t.urlPath!==e.id&&(n.newUrlPath=t.urlPath),await h.pages.update(e.id,n)}else await h.pages.create({urlPath:t.urlPath,frontmatter:s,body:t.body})}catch(n){return E.toast(`${e.id||"The new page"} not saved: ${n.message||"Unknown error"}`,{type:"error"}),!1}if(!c())return!0;if(e.savedAdminTags=JSON.stringify(t.adminTags),e.base=W(t),e===m?o.base.value=e.base:e.state=t,J(ze(r)),!e.isEdit||t.urlPath!==e.id){K.delete(r),e.id=t.urlPath,e.isEdit=!0,K.set(e.id,e);const n=X({kind:"page",id:r}),l=X({kind:"page",id:e.id});Ne.delete(n),{ids:P}=tr(P,n,l,o.active.peek()),o.active.peek()===n&&(o.active.value=l),e===m&&(o.curId.value=e.id,te())}return!0},Vs=async e=>{const t=[];for(const a of e){const s=N.get(a);if(!s||s.readOnly)continue;if($t===s?(clearTimeout(At),$t=null,ye(),at(s)):s.state||at(s),s.compiling&&await s.compiling,!c())return t;if(s.state!=="ok"){E.toast(`Component "${a}" not saved: ${s.state==="error"?"it does not compile - fix the errors first.":"it has not compiled yet - try again in a moment."}`,{type:"warning"});continue}const r={...s.form},n={...s.meta||{}};delete n.bundled,r.project?n.project=r.project:delete n.project;try{await h.components.put(a,{source:r.source,bundled:r.bundled,meta:n}),s.meta=n,s.saved=r,s.base=Fo({name:a,...r});const l=Je.find(g=>g.name===a);l&&(l.bundled=r.bundled,l.meta=n),c()&&J(Ve(a)),t.push(a)}catch(l){E.toast(`Component "${a}" not saved: ${l.message||"Unknown error"}`,{type:"error"})}}return t.length&&(ye(),pt("#/components")),t};let ro=!1;const io=async(e=!1)=>{if(o.saving.peek()||ro)return;ro=!0;let t,a,s=[];try{if(await Ea(),!c())return;const n=I(o.active.peek());if(dr().formatOnSave&&vs(n),e)t=Re(),a=ge(),s=Ye();else if(n?.kind==="block")t=[],a=L.has(n.id)&&de(L.get(n.id))?[n.id]:[];else if(n?.kind==="component")t=[],a=[],s=N.has(n.id)&&he(N.get(n.id))?[n.id]:[];else if(n?.kind==="page"&&m)a=ge().filter(wt),t=!m.isEdit||Wt()||!a.length?[m]:[];else return;if(!t.length&&!a.length&&!s.length){E.toast("Nothing to save.",{type:"info"});return}for(const g of t){const $=Ro(g===m?_():g.state);if($.length){E.toast(`${g.id||"New page"}: ${$[0].text}`,{type:"warning"}),g!==m&&await ue(g.id),o.dockOn.value=!0,o.dockTab.value=$[0].field==="dconfig"?"dconfig":"details",Ke("dock",!0);return}}const l=qn(a,Object.fromEntries(a.map(g=>[g,Qa(Ue.get(g),m?.id||"")])),wa);if(l&&(!await E.confirm(l)||!c()))return}finally{ro=!1}const r=t.length>0&&yt();o.saving.value=!0;try{let n=0;for(const $ of t)await Hs($)&&n++;if(!c())return;if(r){const $=p.css.peek();await h.settings.saveCustomCss($),ee.value=$,J(ft)}const l=await qs(a);if(!c())return;const g=await Vs(s);if((n||l.length||g.length)&&E.toast(Hn({pages:n,css:r,blocks:l,components:g}),{type:"success"}),Xe(),!c())return;q(),z(),n&&(pt("#/pages"),Ct(),ha())}catch(n){E.toast(`Save failed: ${n.message||"Unknown error"}`,{type:"error"})}finally{o.saving.value=!1}};let oe=null,me=null;const De=new Map,je=new Map,Sa=()=>{const e=L.get(o.blockTab.peek());!e||!oe||(o.blockReady.value=!1,oe.setValue(e.form.content),me.setValue(e.form.css),o.blockProject.value=e.form.project,qo(O("#block-project"),e.form.project),o.blockBundled.value=e.form.bundled,o.blockWhere.value=Ln({usage:Ue.get(e.form.name)})||"Where it is used could not be read.",o.blockNote.value="",o.blockReady.value=!0,uo())},_s=e=>{const t=L.get(e);if(!t)return{};const a=t.form.content;return Object.fromEntries(ts(Ga(a),No(a),t.samples||{}).map(s=>[s.name,s.value]))};i(M.effect(()=>{const e=o.blockProject.value,t=o.blockBundled.value,a=L.get(o.blockTab.peek());!a||!o.blockReady.peek()||a.form.project===e&&a.form.bundled===t||(a.form.project=e,a.form.bundled=t,Le())}));const lo=async(e,{quiet:t=!1}={})=>{for(;De.has(e);)if(await De.get(e),L.has(e)||t)return L.get(e)||null;if(L.has(e))return L.get(e);const a=(async()=>{try{const s=await h.blocks.get(e);if(!c()||!t&&!P.includes(X({kind:"block",id:e})))return null;const r=Xa({...s,name:e}),n={block:s,saved:r,base:Do(r),form:{...r},samples:{}};let l=null;try{l=S.get(He(e))}catch{}const g=Dn(l,n.base);if(t&&g?.stale)return null;if($o({kind:"block",id:e}),g){const $=g.at?` (${qe(g.at)})`:"",G=!g.stale||await E.confirm(`You left unsaved changes to "${e}"${$}, but the block has been saved since. Restore your version anyway?`);if(!c())return null;G&&(n.form={...n.form,...g.form,name:e},g.stale||E.toast(`Your unsaved changes to "${e}" are back.`,{type:"info"}))}return l&&J(He(e)),L.set(e,n),Le(),n}catch(s){return c()&&o.blockTab.peek()===e&&(o.blockNote.value=`Could not open "${e}": ${s.message||"unknown error"}`),null}finally{De.delete(e)}})();return De.set(e,a),a},Ea=async()=>{for(;De.size||je.size;)await Promise.all([...De.values(),...je.values()])},Be=async e=>{if(!e||!be.has(e))return;const t=X({kind:"block",id:e});({ids:P}=Bo(P,t)),o.active.value=t,vo(),o.cssMode.peek()&&(o.cssMode.value=!1),o.viewMode.peek()==="preview"&&(o.viewMode.value="split"),o.blockTab.peek()!==e&&(o.blockReady.value=!1),o.blockTab.value=e,o.blockUses.value=m&&wt(e)?Vn(Qa(Ue.get(e),m.id||"")):In(Ue.get(e)),te(),q(),z();let a=L.get(e);a||(o.blockReady.value=!1,o.blockNote.value=`Opening ${e}\u2026`,a=await lo(e)),!(!a||!c()||o.blockTab.peek()!==e)&&(Sa(),q(),z(),Zt=e,U(),(o.blockPart.peek()==="css"?me:oe)?.focus())};let co=null;i(()=>clearTimeout(co));const Ma=(e,t)=>{const a=L.get(o.blockTab.peek());!a||!o.blockReady.peek()||(a.form[e]=t,clearTimeout(co),co=setTimeout(()=>{Le(),e==="content"&&uo()},120),U())},we=O("#pe-block-samples"),Ta=e=>{we.replaceChildren();const t=document.createElement("p");t.className="pe-dock-none",t.textContent=e,we.appendChild(t)};let tt=[],po=null;function uo(){const e=L.get(o.blockTab.peek());if(!e){tt=[],po=null,Ta("Open a block to see its placeholders.");return}const t=ts(Ga(e.form.content),No(e.form.content),e.samples);if(!(po===e&&t.map(a=>a.name).join("|")===tt.join("|"))){if(tt=t.map(a=>a.name),po=e,!t.length){Ta("No {{placeholders}} in the template yet.");return}we.replaceChildren();for(const a of t){const s=document.createElement("div");s.className="pe-comp-prop",s.tabIndex=0,s.dataset.sample=a.name;const r=document.createElement("label");r.className="pe-comp-prop-label",r.textContent=a.name,r.title=a.raw?`{{{${a.name}}}} - raw markup`:`{{${a.name}}}`;const n=document.createElement("input");n.type="text",n.className="form-input form-input--sm pe-comp-prop-input",n.value=a.value,n.setAttribute("aria-label",`Sample value for ${a.name}`),n.addEventListener("input",()=>{e.samples[a.name]=n.value,U()});const l=document.createElement("button");l.type="button",l.className="pe-kebab",l.setAttribute("aria-label",`Actions for ${a.name}`),l.title="Actions",l.textContent="\u22EE",s.append(r,n,l),we.appendChild(s)}}}const mo=(e,t)=>No(e.form.content).has(t)?`{{{${t}}}}`:`{{${t}}}`,Ks=e=>{const t=L.get(o.blockTab.peek());return!t||!tt.includes(e)?[]:[{header:mo(t,e)},{label:"Insert into the template",icon:"corner-down-right",action:()=>{const a=oe?.textarea;a&&(o.blockPart.value="html",a.focus(),a.setRangeText(mo(t,e),a.selectionStart,a.selectionEnd,"end"),a.dispatchEvent(new Event("input",{bubbles:!0})))}},{label:"Copy the placeholder",icon:"copy",action:()=>Ie(mo(t,e),"Placeholder")},{type:"divider"},{label:"Reset the sample",icon:"rotate-ccw",action:()=>{delete t.samples[e],tt=[],uo(),U()}}]};{const e=E.contextMenu(we,{cursor:"glyph",match:".pe-comp-prop",inherit:!1,density:"compact",items:t=>Ks(t.dataset.sample)});i(()=>e?.destroy?.()),we.addEventListener("click",t=>{const a=t.target.closest(".pe-kebab");a&&Ce(a)}),we.addEventListener("keydown",t=>{const a=t.target.closest(".pe-comp-prop");a&&t.key==="F10"&&t.shiftKey&&(t.preventDefault(),Ce(a))})}const ae={},Se=O("#component-preview"),Pt=e=>{try{Se?.contentWindow?.postMessage(e,"*")}catch{}};let fo=Se.contentDocument?.readyState==="complete"&&/component-preview/.test(Se.contentDocument?.URL||""),Ot=!1,ot=null;const Ca=()=>{fo=!0,ot&&c()&&(Pt({type:"mount",payload:ot}),Ot=!0),ot=null};Se.addEventListener("load",Ca),i(()=>Se.removeEventListener("load",Ca));const Us=e=>{if(!fo){ot=e;return}if(!Ot){Ot=!0,Pt({type:"mount",payload:e});return}ot=e,fo=!1,Ot=!1,Se.src=Se.src};let Rt="";const bo=(e=!1)=>{const t=N.get(o.compTab.peek());if(!(!t||t.state!=="ok"||!t.js)){if(o.viewMode.peek()==="write"){t.previewStale=!0;return}!e&&Rt===t.name&&!t.previewStale||(t.previewStale=!1,Rt=t.name,Us({compiledJs:t.js,tagName:`dm-${t.name}`,props:t.previewProps}))}},at=e=>{const t=++e.seq;return e.state="compiling",o.compTab.peek()===e.name&&(o.compState.value="compiling"),e.compiling=(async()=>{let a;try{a=await h.components.compile(e.name,e.form.source)}catch(s){a={errors:[{type:"request",message:s.message||"Compile request failed."}]}}if(!(t!==e.seq||!c())){if(a.errors?.length?(e.state="error",e.errors=a.errors.map(s=>`[${s.type}] ${s.message}`).join(`
|
|
140
|
+
`),e.js=null):(e.state="ok",e.errors="",e.js=a.js),o.compTab.peek()!==e.name){e.previewStale=!0;return}o.compState.value=e.state,o.compErrors.value=e.errors,e.state==="ok"&&(ko(),bo(!0))}})().finally(()=>{e.compiling&&t===e.seq&&(e.compiling=null)}),e.compiling};let At=null,$t=null;i(()=>clearTimeout(At));const xa=()=>{const e=N.get(o.compTab.peek());if(!e||!ae.template)return;o.compReady.value=!1;const t=ut(e.form.source);for(const a of qt)ae[a].setValue(t[a]),ae[a].textarea.readOnly=e.readOnly;o.compReadOnly.value=e.readOnly,o.compProject.value=e.form.project,qo(O("#comp-project"),e.form.project),o.compBundled.value=e.form.bundled,o.compState.value=e.state,o.compErrors.value=e.errors,o.compNote.value="",o.compReady.value=!0,ko(),e.state==="ok"?bo():e.state||at(e)},vo=()=>{o.compTab.peek()&&(o.compTab.value="",o.compReady.value=!1)},Pa={page:"details",block:"details",component:"details"};let Lt="";i(M.effect(()=>{const e=Qo.value;e!==Lt&&(Lt&&(Pa[Lt]=o.dockTab.peek()),Lt=e,e&&(o.dockTab.value=Pa[e]||"details",e==="page"&&o.dockTab.peek()==="history"&&o.dockOn.peek()&&m?.isEdit&&!Qe&&pe()))}));const go=async(e,{quiet:t=!1}={})=>{if(je.has(e))return je.get(e);if(N.has(e))return N.get(e);const a=(async()=>{try{const s=await h.components.get(e);if(!c()||!P.includes(X({kind:"component",id:e})))return null;const r=s.readOnly===!0||s.origin==="plugin",n={project:s.meta?.project||"",bundled:!!s.bundled,source:os(ut(s.source))},l={name:e,comp:s,saved:n,base:Fo({name:e,...n}),form:{...n},meta:s.meta||{},readOnly:r,state:"",errors:"",js:null,seq:0,compiling:null,previewProps:{},previewStale:!1};let g=null;try{g=S.get(Ve(e))}catch{}const $=ir(g,l.base);if(t&&(!$.ok||$.stale||r))return null;const G=$o({kind:"component",id:e});if(t&&!G)return null;if($.ok&&!r){const Te=g.at?` (${qe(g.at)})`:"",Co=G&&!$.stale||await E.confirm($.stale?`You left unsaved changes to the component "${e}"${Te}, but it has been saved since. Restore your version anyway?`:`You left unsaved changes to the component "${e}"${Te}. Restore them?`);if(!c())return null;Co&&(l.form={project:String(g.project??""),bundled:g.bundled===!0,source:String(g.source)},G&&!$.stale&&E.toast(`Your unsaved changes to the component "${e}" are back.`,{type:"info"}))}return g&&J(Ve(e)),N.set(e,l),ye(),l}catch(s){return c()&&o.compTab.peek()===e&&(o.compNote.value=`Could not open <dm-${e}>: ${s.message||"unknown error"}`),null}finally{je.delete(e)}})();return je.set(e,a),a},st=async e=>{if(!e||!le.has(e))return;const t=X({kind:"component",id:e});({ids:P}=Bo(P,t)),o.active.value=t,o.blockTab.value="",o.cssMode.peek()&&(o.cssMode.value=!1),o.viewMode.peek()==="preview"&&(o.viewMode.value="split"),o.compTab.value=e,te(),q(),z();let a=N.get(e);a||(o.compReady.value=!1,o.compReadOnly.value=!1,o.compState.value="",o.compNote.value=`Opening <dm-${e}>\u2026`,a=await go(e)),!(!a||!c()||o.compTab.peek()!==e)&&(xa(),q(),z(),ae[o.compPart.peek()]?.focus())},Js=(e,t)=>{const a=N.get(o.compTab.peek());if(!a||!o.compReady.peek()||a.readOnly)return;const s=ut(a.form.source);s[e]=t,a.form.source=os(s),clearTimeout(At),$t=a,At=setTimeout(()=>{$t=null,ye(),at(a)},300)};i(M.effect(()=>{const e=o.compProject.value,t=o.compBundled.value,a=N.get(o.compTab.peek());!a||!o.compReady.peek()||a.readOnly||a.form.project===e&&a.form.bundled===t||(a.form.project=e,a.form.bundled=t,ye())}));const Ee=O("#pe-comp-props");let ho=[];const yo=e=>{Ee.replaceChildren();const t=document.createElement("p");t.className="pe-dock-none",t.textContent=e,Ee.appendChild(t)};function ko(){const e=N.get(o.compTab.peek());if(!e){ho=[],yo("Open a component to try its props.");return}const{rows:t,error:a}=as(ut(e.form.source).props,e.previewProps);if(ho=t,a){yo("The <props> block is not valid JSON - fix it to see the props here.");return}if(!t.length){e.previewProps={},yo("No props declared. Add them in the <props> block.");return}e.previewProps=Object.fromEntries(t.map(s=>[s.name,s.value])),Ee.replaceChildren();for(const s of t){const r=document.createElement("div");r.className="pe-comp-prop",r.tabIndex=0,r.dataset.prop=s.name;const n=document.createElement("label");n.className="pe-comp-prop-label",n.textContent=s.label,n.title=`${s.name} (${s.type})`;const l=s.type==="array"||s.type==="object"?document.createElement("textarea"):document.createElement("input");s.type==="boolean"?(l.type="checkbox",l.className="form-check",l.checked=ss(s.type,s.value)):(l.tagName==="INPUT"?l.type=s.type==="number"?"number":"text":l.rows=2,l.className="form-input form-input--sm pe-comp-prop-input",l.value=ss(s.type,s.value)),l.setAttribute("aria-label",s.label);const g=()=>{e.previewProps[s.name]=cr(s.type,s.type==="boolean"?l.checked:l.value),Rt===e.name&&Pt({type:"update",payload:{props:e.previewProps}})};l.addEventListener("input",g),l.addEventListener("change",g);const $=document.createElement("button");$.type="button",$.className="pe-kebab",$.setAttribute("aria-label",`Actions for ${s.label}`),$.title="Actions",$.textContent="\u22EE",r.append(n,l,$),Ee.appendChild(r)}}const Ie=async(e,t)=>{try{await navigator.clipboard.writeText(e),E.toast(`${t} copied.`,{type:"success"})}catch{E.toast(e,{type:"info",duration:8e3})}},Ws=e=>{const t=N.get(o.compTab.peek()),a=ho.find(n=>n.name===e);if(!t||!a)return[];const s=rr(a.name,a.type,t.previewProps[a.name]),r=`dm-${t.name}`;return[{header:a.label},{label:"Reset to its default",icon:"rotate-ccw",action:()=>{delete t.previewProps[a.name];const n=as(ut(t.form.source).props,{}).rows.find(l=>l.name===a.name);t.previewProps[a.name]=n?n.value:null,ko(),Rt===t.name&&Pt({type:"update",payload:{props:t.previewProps}})}},{label:"Copy as an attribute",icon:"copy",disabled:!s,action:()=>Ie(s,"Attribute")},{label:"Copy the tag with this value",icon:"code",action:()=>Ie(`<${r}${s?` ${s}`:""}></${r}>`,"Tag")},{label:"Copy the prop name",icon:"copy",action:()=>Ie(a.name,"Name")},{type:"divider"},{label:"Edit in <props>",icon:"edit",action:()=>{o.compPart.value="props";const n=ae.props?.textarea;if(!n)return;const l=n.value.indexOf(`"${a.name}"`);n.focus(),l>=0&&n.setSelectionRange(l,l+a.name.length+2)}}]};{const e=E.contextMenu(Ee,{cursor:"glyph",match:".pe-comp-prop",inherit:!1,density:"compact",items:t=>Ws(t.dataset.prop)});i(()=>e?.destroy?.()),Ee.addEventListener("click",t=>{const a=t.target.closest(".pe-kebab");a&&Ce(a)}),Ee.addEventListener("keydown",t=>{const a=t.target.closest(".pe-comp-prop");a&&t.key==="F10"&&t.shiftKey&&(t.preventDefault(),Ce(a))})}let Fe=!1;const Oa=()=>zt()||"",Nt=async({project:e=Oa(),seed:t="",content:a=yr,css:s=""}={})=>{if(!Fe){Fe=!0;try{const n=await ls({kind:"block",projects:se,taken:[...be],project:e,name:t,check:$n,register:i,create:async(l,g)=>{await h.blocks.put(l,Za({name:l,project:g,bundled:!1,content:a,css:s}))}});if(!c())return;if(!n){te();return}if(await Vo(),!c())return;pt("#/blocks"),E.toast(`Created the block "${n}".`,{type:"success"}),Be(n)}finally{Fe=!1}}},nt=async({name:e="",source:t=nr,project:a=Oa()}={})=>{if(!Fe){Fe=!0;try{const s=await ls({kind:"component",projects:se,taken:[...le],project:a,name:e,register:i,check:(r,n)=>lr(r,{isNew:!0,taken:n}),create:async(r,n)=>{await h.components.put(r,{source:t,bundled:!1,meta:n?{project:n}:{}})}});if(!c())return;if(!s){te();return}if(await _o(),!c())return;pt("#/components"),E.toast(`Created the component "${s}".`,{type:"success"}),st(s)}finally{Fe=!1}}},Ra=y.querySelector(".dm-plugin-body")||y,Ys=M.computed(()=>{o.blockRev.value,o.compRev.value,o.docRev.value,W(_()),o.base.value;const e=I(o.active.value),t=[...Re().map(s=>X({kind:"page",id:s.id})),...ge().map(s=>X({kind:"block",id:s})),...Ye().map(s=>X({kind:"component",id:s}))];let a=[];return e?.kind==="block"||e?.kind==="component"?a=[o.active.value]:e?.kind==="page"&&m&&(a=[o.active.value,...ge().filter(wt).map(s=>X({kind:"block",id:s}))]),or(t,a)}),Gs={...p,...o,dirty:ys,pageDirty:gs,activeDirty:ks,cssUnsaved:hs,hasPage:ws,activeKind:Xo,canView:Zo,viewHref:Ss,problem:Es,dconfigNoteRows:Ms,statusText:Ts,saveAll:Ys,dockKind:Qo,blockTagText:Cs,compStateText:xs,compTagText:Ps,anySelected:Os,allSelected:Rs,someSelected:As},rt=(e,t)=>Ke(e,t),wo={save:()=>io(!1),saveAllOpen:()=>io(!0),discard:async()=>{if(await Ea(),!c())return;const e=I(o.active.peek());if(e?.kind==="block"){const t=L.get(e.id);if(!t||!de(t)||!await E.confirm(`Discard the changes to "${e.id}" you have not saved?`))return;t.form={...t.saved},J(He(e.id)),Sa(),Le()}else if(e?.kind==="component"){const t=N.get(e.id);if(!t||!he(t)||!await E.confirm(`Discard the changes to the component "${e.id}" you have not saved?`))return;t.form={...t.saved},t.state="",J(Ve(e.id)),xa(),ye()}else{if(!m||e?.kind==="page"&&e.id!==m.id||!(Wt()||yt())||!await E.confirm("Discard the changes to this page you have not saved?"))return;Ze(JSON.parse(o.base.peek())),p.cssOpened.peek()&&(p.css.value=ee.peek()),J(ze(m.id)),J(ft),Xe()}q(),z(),U()},newPage:()=>ue(""),newBlock:()=>Nt(),newComponent:()=>nt(),toggleSection:e=>{const t={here:"secHere",pages:"secPages",blocks:"secBlocks",components:"secComps"}[e];t&&(o[t].value=!o[t].peek(),rt(t,o[t].peek()))},toggleExplorer:()=>{o.explorerOn.value=!o.explorerOn.peek(),rt("explorer",o.explorerOn.peek())},toggleDock:()=>{o.dockOn.value=!o.dockOn.peek(),rt("dock",o.dockOn.peek()),o.dockOn.peek()&&o.dockTab.peek()==="history"&&!Qe&&pe()},showDock:e=>{o.dockTab.value=e,e==="history"&&m?.isEdit&&!Qe&&pe()},schemaChanged:()=>{o.seoSchemaNote.value=Po(p.seoSchema.peek()).error},insertSchemaExample:async e=>{const t=e?.target,a=t?.value||"";if(t&&(t.value=""),!a||p.seoSchema.peek().trim()&&!await E.confirm("Replace the structured data with the example?"))return;const s=p.urlPath.peek().trim()||m?.id||"/";p.seoSchema.value=fn(a,{title:p.seoTitle.peek().trim()||p.title.peek().trim(),description:p.seoDesc.peek().trim()||p.description.peek().trim(),url:`${location.origin}${s}`,siteName:Y?.title||Y?.seo?.defaultTitle||""}),o.seoSchemaNote.value=""},setBlockPart:e=>{o.blockPart.value=e==="css"?"css":"html",(e==="css"?me:oe)?.focus()},formatBlock:()=>{const e=o.blockPart.peek()==="css",t=e?me:oe;!t||!o.blockReady.peek()||Uo(_e(t.textarea,e?"css":"html"))},copyShortcode:async()=>{const e=o.blockTab.peek();e&&await Ie(jn(e),"Shortcode")},setCompPart:e=>{o.compPart.value=qt.includes(e)?e:"template",ae[o.compPart.peek()]?.focus()},copyComponent:()=>{const e=N.get(o.compTab.peek());e&&nt({name:Lo(e.name,[...le]),source:e.form.source})},setMode:e=>{o.cssMode.peek()&&(o.cssMode.value=!1),o.viewMode.value=jo(e),rt("viewMode",o.viewMode.peek()),e!=="write"&&(U(!0),bo())},toggleFullscreen:()=>{o.fullscreen.value=!o.fullscreen.peek(),rt("fullscreen",o.fullscreen.peek())},toggleCss:()=>{o.blockTab.peek()||o.compTab.peek()||(o.cssMode.peek()?(o.cssMode.value=!1,o.viewMode.value=jo(Pe().viewMode),Q.refresh()):(ia(),o.cssMode.value=!0,o.viewMode.value="split"))},formatMarkdown:()=>{Jt(),Uo(_e(V,"md"))},toggleFold:()=>{o.folded.peek()?Q.unfoldAll():Q.foldAll(),o.folded.value=!o.folded.peek(),We.paint()},setWidth:e=>{o.liveWidth.value=e},refreshPreview:()=>{if(o.compTab.peek()){const e=N.get(o.compTab.peek());e&&at(e)}else U(!0)},share:async()=>{if(!m?.isEdit){E.toast("Save the page first - a share link needs a page to point at.",{type:"warning"});return}await gr(m.id)},toggleAll:e=>{const t=!!e?.target?.checked;o.versions.peek().forEach(a=>{a.checked.value=t})},deleteSelected:async()=>{const e=o.versions.peek().filter(a=>a.checked.peek()).map(a=>a.filename),t=e.length;if(!(!t||!await E.confirm(`Delete ${t} version${t>1?"s":""}? This cannot be undone.`)))try{await h.versions.bulkDelete(m.id,e),E.toast(`${t} version${t>1?"s":""} deleted.`,{type:"success"}),await pe()}catch(a){E.toast("Delete failed: "+a.message,{type:"error"})}},saveNamed:async()=>{if(!m?.isEdit)return;const e=m;await hr(e.id)&&c()&&m===e&&await pe()},closeCompare:()=>{o.compare.value=null,Tt=null},restoreCompared:()=>ma(Tt)};Ge.querySelectorAll("[data-tip]").forEach(e=>E.tooltip(e,{content:e.getAttribute("data-tip"),position:"top"}));const Xs=M.applyBindings(Gs,Ra,{methods:wo});i(()=>Xs.dispose()),oe=zo(O('.pe-block-edit[data-part="html"]'),{ariaLabel:"Block template HTML",language:"html",onChange:e=>Ma("content",e)}),me=zo(O('.pe-block-edit[data-part="css"]'),{ariaLabel:"Block CSS",language:"css",placeholder:`/* Scoped to this block - e.g.
|
|
141
141
|
color: var(--dm-primary);
|
|
142
|
-
h3 { font-size: 1.25rem; } */`,onChange:e=>
|
|
143
|
-
`).map((i,
|
|
142
|
+
h3 { font-size: 1.25rem; } */`,onChange:e=>Ma("css",e)}),i(()=>{oe.destroy(),me.destroy()});for(const e of qt)ae[e]=zo(O(`.pe-comp-edit[data-part="${e}"]`),{ariaLabel:`Component <${e}> block`,language:{template:"html",props:"json",script:"js",style:"css"}[e],onChange:t=>Js(e,t)});i(()=>{for(const e of qt)ae[e].destroy()});const Me=O(".pe-tabs-list"),Dt=e=>e.target.closest(".pe-tab")?.dataset.id||"",Aa=e=>{const t=Dt(e);t&&(e.target.closest(".pe-tab-close")?et(t):xt(t))},$a=e=>{e.button===1&&Dt(e)&&(e.preventDefault(),et(Dt(e)))},La=e=>{const t=Dt(e);!t||e.target.closest(".pe-tab-close")||(e.key==="Enter"||e.key===" "?(e.preventDefault(),xt(t)):e.key==="Delete"?(e.preventDefault(),et(t)):e.key==="F10"&&e.shiftKey&&(e.preventDefault(),Ce(e.target.closest(".pe-tab"))))};Me.addEventListener("click",Aa),Me.addEventListener("auxclick",$a),Me.addEventListener("keydown",La),i(()=>{Me.removeEventListener("click",Aa),Me.removeEventListener("auxclick",$a),Me.removeEventListener("keydown",La)});const So=e=>{for(const t of e)et(t)},Zs=async e=>{const t=I(e);if(t?.id)if(t.kind==="page")o.active.peek()!==e&&await ue(t.id),c()&&m?.id===t.id&&Ba();else if(t.kind==="block"){const s=(L.get(t.id)||await lo(t.id,{quiet:!0}))?.form||Xa(await h.blocks.get(t.id).catch(()=>null));c()&&Nt({seed:Lo(t.id,[...be]),content:s.content,css:s.css})}else{const s=(N.get(t.id)||await go(t.id,{quiet:!0}))?.form.source??(await h.components.get(t.id).catch(()=>null))?.source??"";c()&&nt({name:Lo(t.id,[...le]),source:s})}},Qs=e=>{const t=I(e);if(!t||!P.includes(e))return[];const a=P.indexOf(e),s=Ne.get(e)?.label.peek()||t.id,r=n=>{P=Gn(P,e,n),q()};return[{header:s},{label:"Close",icon:"x",action:()=>et(e)},{label:"Close others",icon:"x-circle",disabled:P.length<2,action:()=>So(Io(P,e,"others"))},{label:"Close to the right",icon:"chevrons-right",disabled:a>=P.length-1,action:()=>So(Io(P,e,"right"))},{label:"Close all",icon:"layers",action:()=>So(Io(P,e,"all"))},{type:"divider"},{label:"Move left",icon:"arrow-left",disabled:a===0,action:()=>r(-1)},{label:"Move right",icon:"arrow-right",disabled:a>=P.length-1,action:()=>r(1)},{type:"divider"},{label:t.kind==="page"?"Clone page\u2026":`Duplicate ${t.kind}\u2026`,icon:"copy",disabled:!t.id,action:()=>Zs(e)},{label:"Copy link",icon:"link",disabled:!t.id,action:()=>Ie(`${location.origin}${location.pathname}#${es(t)}`,"Link")}]};{const e=E.contextMenu(Me,{cursor:"glyph",match:".pe-tab",inherit:!1,density:"compact",items:t=>Qs(t.dataset.id)});i(()=>e?.destroy?.())}const Na=O(".pe-ex-scroll"),Da=e=>{const t=e.target.closest(".pe-ex-item"),a=t?.dataset.id||"";if(!a)return;const s=a.indexOf(":"),r=a.slice(0,s),n=a.slice(s+1),l=()=>{ke.has(n)?ke.delete(n):ke.add(n);try{S.set(us,[...ke])}catch{}z()};if(r==="folder"){l();return}if(r==="page"&&e.target.closest(".pe-ex-chev")&&t.classList.contains("is-folder")){l();return}r==="page"?ue(n):r==="block"?Be(n):r==="component"&&st(n)};Na.addEventListener("click",Da),i(()=>Na.removeEventListener("click",Da));let Eo=null;i(()=>clearTimeout(Eo)),V.addEventListener("input",()=>{Q.refresh(),ce.scrollTop=V.scrollTop,!gt&&(clearTimeout(Eo),Eo=setTimeout(ra,120),U())}),V.addEventListener("scroll",()=>{ce.scrollTop=V.scrollTop}),ve.addEventListener("scroll",()=>{ce.scrollTop=ve.scrollTop}),i(M.effect(()=>{for(const e of Object.keys(p))e!=="css"&&e!=="cssOpened"&&p[e].value;o.bodyRev.value,U()})),i(M.effect(()=>{o.bodyRev.value,o.docRev.value;const e=m?ht():"";kt=m?Nn(e,bt.map(t=>t.name),bt.filter(t=>t.shortTag).map(t=>t.name)):[],ta=m?Yn(e,le):[],St(),z()})),i(M.effect(()=>{o.blockRev.value,o.compRev.value,o.docRev.value,o.active.value,o.filter.value,p.title.value,q(),z()})),i(M.effect(()=>{ce.hidden=!!(o.blockTab.value||o.compTab.value)}));let Mo=null;i(()=>clearTimeout(Mo)),i(M.effect(()=>{const e=p.css.value;o.cssMode.value&&(Ut.paint(),Ar(ce,e,ve),clearTimeout(Mo),Mo=setTimeout(oa,400))}));{const e=O(".pe-version-list"),t=E.contextMenu(e,{cursor:"glyph",match:"tr[data-file]",inherit:!1,density:"compact",items:a=>{const s=oo(a.dataset.file);return s?[{header:rs(s)},{label:"Show beside the editor",icon:"columns",action:()=>ao(s)},{label:"Restore this version\u2026",icon:"rotate-ccw",action:()=>ma(s.filename)},{type:"divider"},{label:"Delete\u2026",icon:"trash",danger:!0,action:()=>Bs(s)}]:[]}});i(()=>t?.destroy?.()),e.addEventListener("click",a=>{const s=a.target.closest("tr[data-file]");if(!s||a.target.matches("input"))return;const r=a.target.closest(".pe-kebab");if(r){Ce(r);return}const n=oo(s.dataset.file);n&&ao(n)}),e.addEventListener("keydown",a=>{const s=a.target.closest("tr[data-file]");if(s){if(a.key==="F10"&&a.shiftKey)a.preventDefault(),Ce(s);else if(a.key==="Enter"&&a.target===s){const r=oo(s.dataset.file);r&&ao(r)}}})}$r(O("#editor-body"),O("#editor-divider"),i),cn({image:e=>(o.cssMode.value=!1,Lr(e)),scribe:e=>un(e),card:e=>tn.open(e,{onMediaBrowse:()=>h.media.list().catch(()=>[])}),timeline:e=>on.open(e,{onPreview:async t=>{try{return await h.pages.preview(t)}catch{return null}},onListCollections:()=>h.collections.list().catch(()=>[]),onGetCollection:t=>h.collections.get(t).catch(()=>null)})}),to.onEffects(e=>rn.open(e)),to.onHelp(()=>Ha()),to.onTutorial(e=>mn(e));const ja=e=>{if(!c())return;const t=sr(e);t&&(e.preventDefault(),t==="save"?io(!1):t==="layout"?wo.setMode(Xn(o.viewMode.peek())):wo.toggleExplorer())};document.addEventListener("keydown",ja),i(()=>document.removeEventListener("keydown",ja));const it=y.querySelector(".dm-plugin-banner-name");i(M.effect(()=>{const e=o.curId.value,t=Xo.value,a=o.blockTab.value||o.compTab.value,s=t==="block"?"Blocks":t==="component"?"Components":"Pages",r=t==="block"||t==="component"?a:e===null?"Editor":Cn(!!e,p.title.value,e);if(!c()||(document.title=`${r} - Domma CMS`,!it))return;const n=it.querySelector(".dm-plugin-banner-parent"),l=it.lastChild;n&&l&&l.nodeType===3&&it.querySelector(".dm-plugin-banner-sep")?(n.textContent=s,l.textContent=r):it.textContent=`${s} \u203A ${r}`})),dt(d,{id:"page-editor-back",icon:"arrow-left",title:"Back to Pages",onClick:()=>R.navigate("/pages")}),dt(d,{id:"page-editor-help",icon:"help-circle",title:"Editor reference",onClick:()=>Ha()});const en=dt(d,{id:"page-editor-view",icon:"external-link",title:"View on the site",onClick:()=>{m?.isEdit&&window.open(p.urlPath.peek().trim()||m.id,"_blank","noopener")}}),Ba=()=>{if(!m?.isEdit||o.saving.peek()||!c())return;const e=m,t=_(),a=Ro({...t,urlPath:e.id});if(a.length){E.toast(a[0].text,{type:"warning"});return}const s=gn({page:e.page,frontmatter:{...Gt(t,e,Xt(t)),adminTags:t.adminTags},body:t.body,source:e.id,pages:D,onCloned:async r=>{c()&&(await ha(),c()&&ue(r))}});i(()=>s.close())},To=dt(d,{id:"page-editor-clone",icon:"copy",title:"Clone page",onClick:Ba});i(M.effect(()=>{const e=Zo.value;for(const t of[en,To])t&&(t.disabled=!e);To&&(To.title=e?"Clone page":"Open a saved page to clone it")})),dt(d,{id:"page-editor-settings",icon:"settings",title:"Editor settings - font, size, spacing",onClick:()=>{Yo=vr(Jo,Wo)}}),i(qa(Ra,{position:"bottom"})),Domma.icons.scan();let Ia={ids:[],active:""};try{Ia=er(S.get(ps))}catch{}const lt=Ua(b),ct=ar(lt,Ia,history.state?.pe===1);P=ct.ids.filter(e=>{const t=I(e);return t.kind==="page"||(t.kind==="component"?le:be).has(t.id)}),ba=!1;for(const e of P){const t=I(e);t.kind==="page"&&ya(t.id)}q(),z(),St();for(const e of Ae)lo(e,{quiet:!0}).then(()=>{c()&&(q(),z())});for(const e of P){const t=I(e);t.kind==="component"&&e!==ct.active&&xe(t)&&go(t.id,{quiet:!0}).then(()=>{c()&&(q(),z())})}const Fa=lt&&!lt.id&<.kind!=="page";ct.active&&P.includes(ct.active)?await xt(ct.active):P.length?await xt(P[0]):Fa||te(),Fa&&c()&&(await(lt.kind==="block"?Nt:nt)({project:zt(b)||""}),c()&&te())}};function qo(d,b){!d||typeof b!="string"||!b||[...d.options].some(v=>v.value===b)||(d.append(Object.assign(document.createElement("option"),{value:b,textContent:`${b} (custom)`})),d.value=b)}function Ar(d,b,v){d.replaceChildren(...String(b).split(`
|
|
143
|
+
`).map((i,k)=>{const u=document.createElement("span");u.className="editor-line-number-row";const w=document.createElement("span");w.className="fold-toggle fold-toggle--empty";const y=document.createElement("span");return y.className="editor-line-num",y.textContent=String(k+1),u.append(w,y),u})),d.scrollTop=v.scrollTop}function $r(d,b,v){if(!d||!b)return;const i=15,k=85,u=c=>Math.min(k,Math.max(i,c));let w=u(parseFloat(Pe().splitRatio)||50);const y=c=>{d.style.setProperty("--dm-editor-split",w+"%"),b.setAttribute("aria-valuenow",String(Math.round(w))),c&&Ke("splitRatio",w)};y(!1);const x=c=>{const A=d.getBoundingClientRect();A.width&&(w=u((c.clientX-A.left)/A.width*100)),y(!1)},j=()=>{document.removeEventListener("pointermove",x),document.removeEventListener("pointerup",j),d.classList.remove("is-resizing"),b.classList.remove("is-dragging"),y(!0)};v(j),b.addEventListener("pointerdown",c=>{window.matchMedia("(max-width: 768px)").matches||(c.preventDefault(),d.classList.add("is-resizing"),b.classList.add("is-dragging"),document.addEventListener("pointermove",x),document.addEventListener("pointerup",j))}),b.addEventListener("dblclick",()=>{w=50,y(!0)}),b.addEventListener("keydown",c=>{const A=c.shiftKey?10:2;let H=w;if(c.key==="ArrowLeft")H=w-A;else if(c.key==="ArrowRight")H=w+A;else if(c.key==="Home")H=i;else if(c.key==="End")H=k;else if(c.key==="Enter"||c.key===" ")H=50;else return;c.preventDefault(),w=u(H),y(!0)})}async function Lr(d){const b=d.selectionStart,v=d.selectionEnd,i=await vn({title:"Upload or insert an image"});if(!i||!d.isConnected)return;let k=i.split("/").pop().split("?")[0];try{k=decodeURIComponent(k)}catch{}const u=k.replace(/[\\[\]]/g,"\\$&"),w=i.replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/ /g,"%20");d.setSelectionRange(b,v),nn(d,``),d.dispatchEvent(new Event("input",{bubbles:!0}))}function Nr(d,b,v){const i=Object.entries(b||{});return d?(d.replaceChildren(...i.map(([k,u])=>new Option(u?.label||k,k))),i.some(([k])=>k===v)?v:i[0]?.[0]||v):v}async function Dr(d,b){if(!d)return;let v=[];try{const x=await h.get("/collections/roles/entries?limit=100");v=Array.isArray(x)?x:x?.entries||[]}catch{}const i=v.map(x=>({name:x.data?.name,label:x.data?.label||x.data?.name,level:x.data?.level??99})).filter(x=>x.name&&x.level!==0).sort((x,j)=>j.level-x.level),k=(x,j)=>Object.assign(document.createElement("option"),{value:x,textContent:j}),u=()=>{typeof b=="string"&&b&&![...d.options].some(x=>x.value===b)&&d.append(k(b,`${b} (custom)`))};if(!i.length)return u();const w=document.createElement("optgroup");w.label="Role and above",i.forEach(x=>w.append(k(x.name,`${x.label} and above`)));const y=document.createElement("optgroup");y.label="Role only",i.forEach(x=>y.append(k(`=${x.name}`,`${x.label} only`))),d.replaceChildren(k("public","Public - everyone"),w,y,k("private","Private - super-admin only")),u()}
|
|
@@ -447,6 +447,10 @@ You can use multiple `[dconfig]` blocks on one page. All valid blocks are merged
|
|
|
447
447
|
### Notes
|
|
448
448
|
|
|
449
449
|
- Invalid JSON is silently dropped (no output, no error)
|
|
450
|
+
- The page editor checks DConfig against Domma's types (domma-js 0.59.0 and later): the DConfig tab lists a misspelt
|
|
451
|
+
key, an unknown component, an option the component does not have or a value of the wrong kind - in the field and in
|
|
452
|
+
every `[dconfig]` block in the body - with the likely name when it is a near miss. It is advice; Save is only refused
|
|
453
|
+
for JSON that is not an object
|
|
450
454
|
- The `[dconfig]` shortcode is processed first in the pipeline, before all other shortcodes
|
|
451
455
|
- `$.setup()` config can also be set per-page in the page editor's **DConfig** card (stored as `dconfig` in frontmatter)
|
|
452
456
|
- Window-level `__CMS_DCONFIG__` (from frontmatter) and inline `dm-page-config` divs are merged at page load, with inline blocks taking precedence
|
|
@@ -375,7 +375,8 @@ export default async function myPlugin(fastify, options) {
|
|
|
375
375
|
### 1. Event Bus
|
|
376
376
|
|
|
377
377
|
Subscribe to fire-and-forget lifecycle events emitted by the CMS. Handlers receive a payload object and must not return
|
|
378
|
-
a value -
|
|
378
|
+
a value - an error thrown inside a handler (or a rejected promise from an async one) is logged, and the other
|
|
379
|
+
handlers still run.
|
|
379
380
|
|
|
380
381
|
```js
|
|
381
382
|
options.hooks.on('content:pageCreated', ({ page, urlPath }) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "domma-cms",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.24",
|
|
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",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"bcryptjs": "^3.0.3",
|
|
87
87
|
"css-select": "^7.0.0",
|
|
88
88
|
"dom-serializer": "^3.1.1",
|
|
89
|
-
"domma-js": "0.
|
|
89
|
+
"domma-js": "0.59.0",
|
|
90
90
|
"dotenv": "^17.2.3",
|
|
91
91
|
"fastify": "5.12.7",
|
|
92
92
|
"gray-matter": "^4.0.3",
|