domma-cms 1.8.27 → 1.8.29
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 +9 -0
- package/README.md +3 -0
- package/admin/css/admin.css +1 -1
- package/admin/js/lib/page-editor-arrange.js +1 -1
- package/admin/js/lib/seo-structured-data.js +1 -0
- package/admin/js/templates/page-editor.html +16 -0
- package/admin/js/views/page-editor.js +12 -12
- package/docs/cache.md +4 -0
- package/package.json +1 -1
- package/public/css/site.css +1 -1
- package/server/routes/api/cache.js +2 -1
- package/server/server.js +7 -0
- package/server/services/assetCache.js +141 -0
- package/server/services/htmlResponse.js +6 -3
- package/server/services/renderer.js +30 -0
- package/server/templates/page.html +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{api as k}from"../api.js";import{CardBuilder as
|
|
1
|
+
import{api as k}from"../api.js";import{CardBuilder as Ws}from"../lib/card-builder.js";import{TimelineBuilder as Ys}from"../lib/timeline-builder.js";import{attachEditorKeybindings as Js,createToolbar as Gs,insertAtCursor as Xs}from"../lib/markdown-toolbar.js";import{EffectsBuilder as Qs}from"../lib/effects-builder.js";import{attachContextMenu as Zs}from"../lib/shortcode-context-menu.js";import{registerCustomInserters as en}from"../lib/shortcode-insert.js";import{populateThemeSelect as tn}from"../lib/themes.js";import{createFoldingManager as on}from"../lib/editor-folding.js";import{openScribeComposer as an}from"../lib/scribe-composer.js";import{openCrudTutorial as sn}from"../lib/crud-tutorial.js";import{addBannerAction as rt}from"../lib/plugin-chrome.js";import{exampleJson as nn,parseSchema as Eo,schemaText as rn}from"../lib/seo-structured-data.js";import{helpPopovers as La}from"../lib/help-popover.js";import{onceClosed as Nt,openMenuAt as Se,sidebarCount as lt}from"../lib/tool-kit.js";import{pickMedia as ln}from"../lib/media-picker.js";import{openPageClone as cn}from"../lib/page-clone.js";import{openEditorReference as Ba}from"../lib/page-editor-reference.js";import{cleanType as Lt,DEFAULT_TYPE as Bt,EDITOR_FONTS as dn,fontHref as pn,isDefaultType as un,LINE_HEIGHTS as mn,SIZES as fn,typeVars as ja}from"../lib/editor-type.js";import{getProjectFromHash as jt}from"../lib/project-context.js";import{attachSmoothCaret as Ia,getCaretPreference as bn,setCaretPreference as vn}from"../lib/smooth-caret.js";import{SCREEN as So,bannerSection as gn,draftIsStale as kn,draftKey as je,initialProject as hn,makeDraft as Fa,problems as Mo,readDraft as yn,snapshot as Y}from"../lib/page-editor-arrange.js";import{fmtDateTime as Ie}from"../lib/dates.js";import{hasDraft as Me,heldBlockEdits as wn,hold as To,takeHeld as Co,wireHeldRouter as En}from"../lib/editor-hop.js";import{hopTarget as qa}from"../lib/editor-hop-arrange.js";import{checkNewName as Sn,copyName as xo,usageText as Mn}from"../lib/blocks-arrange.js";import{isCollapsed as za,setCollapsed as Va}from"../lib/sidebar-size.js";import{blockTagDefs as Ha,blocksInBody as Tn}from"../lib/block-tags.js";import{draftKey as Fe,draftOffer as Cn,extractPlaceholders as Ka,formatHtml as xn,formFromBlock as Ua,payloadFromForm as _a,rawPlaceholders as Po,shortcodeFor as Pn,snapshot as Oo}from"../lib/block-editor-arrange.js";import{blockTabs as On,notHereLine as Rn,otherUses as Wa,outlineBlock as $n,previewBlocks as An,saveQuestion as Dn,savedMessage as Nn,usesLine as Ln}from"../lib/page-blocks-arrange.js";import{addressFor as Ya,blockAloneBody as Bn,blockRows as jn,cleanLayout as Ro,closeTab as In,componentRows as Fn,componentStatusText as qn,componentsInBody as zn,moveTab as Vn,nextLayout as Hn,openTab as $o,pageRows as Kn,pageStatusText as Un,rememberedTabs as _n,renameTab as Wn,sampleRows as Ja,saveAllLabel as Yn,startingTabs as Jn,tabId as Q,tabItem as I,tabsToClose as Ao,workspaceKey as Gn}from"../lib/editor-workspace-arrange.js";import{PANES as It,STARTER_SOURCE as Xn,assembleSource as Ga,attributeFor as Qn,draftKey as qe,draftStatus as Zn,nameProblem as er,previewRows as Xa,readValue as tr,snapshot as Do,splitSource as it,writeValue as Qa}from"../lib/component-editor-arrange.js";import{attachColourLayer as Za,createPlainEditor as No}from"../lib/plain-editor.js";function ct(d){const m=document.createElement("template");return m.innerHTML=d.trim(),m.content.firstElementChild}const or=Ie,es=d=>Ie(d.createdAt)+(d.label?` - ${d.label}`:""),ar=`
|
|
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 k}from"../api.js";import{CardBuilder as Us}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
|
|
33
|
+
</style>`;function ts(d){const m=pn(d);if(!m||document.querySelector(`link[data-pe-font="${CSS.escape(d)}"]`))return;const v=Object.assign(document.createElement("link"),{rel:"stylesheet",href:m});v.dataset.peFont=d,document.head.appendChild(v)}const sr=`
|
|
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>
|
|
@@ -70,17 +70,17 @@ The quick brown fox - 0O 1lI {} [] () => ==
|
|
|
70
70
|
.pe-settings-sample { white-space: pre-wrap; padding: .75rem 1rem; border-radius: var(--dm-radius-md); border: 1px solid var(--dm-border);
|
|
71
71
|
background: var(--dm-background-alt); font-family: var(--pe-font); font-size: var(--pe-size); line-height: var(--pe-line); }
|
|
72
72
|
.pe-settings-actions { display: flex; gap: .5rem; border-top: 1px solid var(--dm-border); padding-top: .75rem; }
|
|
73
|
-
</style>`;function
|
|
73
|
+
</style>`;function nr(d){const m=E.slideover({title:"Editor settings",size:"sm",position:"right"}),v=ct(`<div>${sr}</div>`),l=Lt(d.peek()),b={font:M.observable(l.font),size:M.observable(String(l.size)),line:M.observable(String(l.line)),wrap:M.observable(l.wrap),caret:M.observable(bn()),sidebar:M.observable(Te().closeSidebar===!0)},y=M.effect(()=>{const A=b.sidebar.value;A!==(Te().closeSidebar===!0)&&dt("closeSidebar",A)}),w=M.computed(()=>Lt({font:b.font.value,size:b.size.value,line:b.line.value,wrap:b.wrap.value}));b.isDefault=M.computed(()=>un(w.value)&&b.caret.value==="auto"&&!b.sidebar.value);const h=M.effect(()=>vn(b.caret.value)),C=v.querySelector("#pe-set-font");for(const[A,D]of[["mono","Monospace"],["sans","Other"],["serif",null]]){const J=dn.filter(Ce=>Ce.kind===A),ut=D?Object.assign(document.createElement("optgroup"),{label:D}):C.querySelector("optgroup:last-of-type");ut.append(...J.map(Ce=>new Option(Ce.label,Ce.id))),D&&C.appendChild(ut)}v.querySelector("#pe-set-size").append(...fn.map(A=>new Option(`${A}px`,String(A)))),v.querySelector("#pe-set-line").append(...mn.map(A=>new Option({1.3:"Tight",1.45:"Snug",1.6:"Normal",1.8:"Relaxed",2:"Double"}[A]||String(A),String(A))));const L=v.querySelector(".pe-settings-sample"),c=M.effect(()=>{const A=w.value;ts(A.font);for(const[D,J]of Object.entries(ja(A)))L.style.setProperty(D,J);L.style.whiteSpace=A.wrap?"pre-wrap":"pre",L.style.overflowX=A.wrap?"":"auto",JSON.stringify(A)!==JSON.stringify(Lt(d.peek()))&&(d.value=A,dt("type",A))});b.reset=()=>{b.font.value=Bt.font,b.size.value=String(Bt.size),b.line.value=String(Bt.line),b.wrap.value=Bt.wrap,b.caret.value="auto",b.sidebar.value=!1},b.done=()=>m.close(),m.element.appendChild(v);const P=M.applyBindings(b,v),H=La(v);return Nt(m,()=>{P.dispose(),H(),c?.(),h?.(),y?.()}),Domma.icons?.scan?.(v),m.open(),m}async function rr(d){const m=E.slideover({title:"Share preview link",size:"md",position:"right"}),v=ct(`<div>${ar}</div>`),l={expiry:M.observable("7d"),note:M.observable(""),creating:M.observable(!1),url:M.observable(""),links:M.observable([]),loaded:M.observable(!1),listError:M.observable("")},b=async()=>{try{const w=await k.pages.previewLinks.list(d);l.links.value=w.filter(h=>!h.revokedAt&&!h.expired).map(h=>({id:h.id,title:h.label||"Untitled link",meta:`Expires ${or(h.expiresAt)} - ${h.useCount===1?"1 view":`${h.useCount||0} views`}`})),l.listError.value=""}catch(w){l.listError.value="Could not load existing links: "+(w.message||"unknown error")}finally{l.loaded.value=!0}};l.create=async()=>{l.creating.value=!0;try{const w=await k.pages.previewLinks.create(d,l.expiry.peek(),l.note.peek().trim());l.url.value=w.url,l.note.value="",E.toast("Share link created.",{type:"success"}),await b(),v.querySelector(".pe-share-url")?.select()}catch(w){E.toast("Could not create link: "+(w.message||"unknown error"),{type:"error"})}finally{l.creating.value=!1}},l.copy=async()=>{try{await navigator.clipboard.writeText(l.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"})}},l.revoke=async w=>{if(await E.confirm("Revoke this link? Anyone holding it loses access immediately."))try{await k.pages.previewLinks.revoke(w),E.toast("Link revoked.",{type:"success"}),await b()}catch(h){E.toast("Could not revoke: "+(h.message||"unknown error"),{type:"error"})}};const y=M.applyBindings(l,v);Nt(m,()=>y.dispose()),m.element.appendChild(v),m.open(),await b()}function lr(d){return new Promise(m=>{const v=E.slideover({title:"Save Named Version",size:"sm",position:"right"}),l=ct(`
|
|
74
74
|
<div style="padding:1rem">
|
|
75
75
|
<label class="form-label" for="pe-version-label">Version Label</label>
|
|
76
76
|
<input id="pe-version-label" type="text" class="form-input" style="margin-bottom:.75rem"
|
|
77
77
|
placeholder="e.g. Initial design, Before restructure" data-model="label.value" data-on-keydown="key">
|
|
78
78
|
<button type="button" class="btn btn-primary" data-on-click="save" data-bind-disabled="saving.value">Save Version</button>
|
|
79
|
-
</div>`);let b=!1;const
|
|
79
|
+
</div>`);let b=!1;const y={label:M.observable(""),saving:M.observable(!1)};y.save=async()=>{y.saving.value=!0;try{await k.versions.create(d,y.label.peek().trim()||null),E.toast("Named version saved.",{type:"success"}),b=!0,v.close()}catch(h){E.toast("Failed to save version: "+h.message,{type:"error"})}finally{y.saving.value=!1}},y.key=h=>{h.key==="Enter"&&(h.preventDefault(),y.save())};const w=M.applyBindings(y,l);Nt(v,()=>{w.dispose(),m(b)}),v.element.appendChild(l),v.open(),setTimeout(()=>l.querySelector("input")?.focus(),100)})}const ir=`<div class="card">
|
|
80
80
|
<h3>{{title}}</h3>
|
|
81
81
|
<p>{{description}}</p>
|
|
82
82
|
</div>
|
|
83
|
-
`,
|
|
83
|
+
`,cr=`
|
|
84
84
|
<div class="pe-new">
|
|
85
85
|
<p class="form-hint" style="margin-top:0" data-bind-text="hint.value"></p>
|
|
86
86
|
<label class="form-label" for="pe-new-name">Name</label>
|
|
@@ -100,11 +100,11 @@ The quick brown fox - 0O 1lI {} [] () => ==
|
|
|
100
100
|
.pe-new [hidden] { display: none !important; }
|
|
101
101
|
.pe-new-err { display: block; margin-top: .3rem; color: var(--dm-danger); font-size: var(--dm-text-xs); }
|
|
102
102
|
.pe-new-actions { display: flex; justify-content: flex-end; gap: .5rem; margin-top: 1.25rem; }
|
|
103
|
-
</style>`;function
|
|
104
|
-
`).map(
|
|
103
|
+
</style>`;function os({kind:d,projects:m,taken:v,project:l="",name:b="",check:y,create:w,register:h=()=>{}}){return new Promise(C=>{const L=E.slideover({title:d==="block"?"New block":"New component",size:"sm",position:"right"});h(()=>{try{L.close()}catch{}});const c=ct(`<div>${cr}</div>`);for(const D of m)c.querySelector("#pe-new-project").append(new Option(D.name||D.slug,D.slug));const P={name:M.observable(b),project:M.observable(l),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")};P.error=M.computed(()=>P.name.value.trim()?y(P.name.value.trim(),v):""),P.canCreate=M.computed(()=>!P.busy.value&&!!P.name.value.trim()&&!y(P.name.value.trim(),v));let H="";P.create=async()=>{const D=P.name.peek().trim();if(!(!D||P.error.peek()||P.busy.peek())){P.busy.value=!0;try{await w(D,P.project.peek()),H=D,L.close()}catch(J){E.toast(`Not created: ${J.message||J}`,{type:"error"})}finally{P.busy.value=!1}}},P.cancel=()=>L.close(),P.key=D=>{D.key==="Enter"&&(D.preventDefault(),P.create())};const A=M.applyBindings(P,c);Nt(L,()=>{A.dispose(),C(H)}),L.element.appendChild(c),c.querySelector("#pe-new-project").value=l,L.open(),setTimeout(()=>{const D=c.querySelector("#pe-new-name");D?.focus(),D?.select()},100)})}let q=null,as=!1;function dr(){as||!window.R?.use||(as=!0,En(),R.use((d,m,v)=>{if(q&&!q.alive()){const y=q;q=null,y.dispose()}const l=m?.path||"",b=d?.path||"";if(!q||!So.test(l))return v();if(So.test(b)){const y=qa(b);if(y){q.open(y,b);return}return v()}return q.dirty()?E.confirm("You have unsaved changes. Leave anyway? They are kept as drafts for your next visit.").then(y=>{y?(q?.keepDraft(),v()):q&&history.replaceState({pe:1},"","#"+q.address)}):v()}),M.subscribe("router:afterChange",({to:d}={})=>{if(q&&!So.test(d?.path||"")){const m=q;q=null,m.dispose()}}))}function pr(d){return d.split(`
|
|
104
|
+
`).map(m=>m.trimEnd()).join(`
|
|
105
105
|
`).replace(/\n{3,}/g,`
|
|
106
106
|
|
|
107
|
-
`)}const os="editor_prefs";function Se(){return S.get(os)||{viewMode:"split",fullscreen:!1}}const ir=()=>{try{return Se()||{}}catch{return{}}};function dt(d,u){const v=Se();v[d]=u,S.set(os,v)}const as="editor-workspace.tabs",ss="editor-workspace.folded",pt="editor-workspace.css",cr="/__block-preview",dr=d=>d.replace(/<\/head>/i,"<style>body.dm-cloaked{opacity:1 !important;transition:none !important}</style></head>");function pr(d,u,v=()=>{}){let l=0;try{l=d.contentWindow.scrollY||0}catch{}d.addEventListener("load",()=>{try{d.contentWindow.scrollTo(0,l)}catch{}v()},{once:!0}),d.srcdoc=u}const ur=["urlPath","title","description","layout","status","sortOrder","category","visibility","project","theme","seoTitle","seoDesc","dconfig"],fr=["showInNav","sidebar","breadcrumbs","bundled"],ns=()=>({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:"",dconfig:"",body:"",css:null});export const pageEditorView={templateUrl:"/admin/js/templates/page-editor.html",async onMount(d){if(rr(),q){const e=q;q=null,e.dispose()}const u=window.location.hash.replace(/^#/,""),v=[],l=e=>(v.push(e),e);let b=()=>!1,h=()=>{},w=()=>{};const y=d.get(0),C=y.querySelector("#pe-workspace"),N={address:u,alive:()=>C?.isConnected===!0,dirty:()=>b(),keepDraft:()=>h(),open:(e,t)=>w(e,t),dispose:()=>{v.splice(0).forEach(e=>{try{e()}catch{}})}};q=N;const c=()=>q===N&&N.alive();if(ir().closeSidebar===!0){const e=document.querySelector("#admin-sidebar");e&&!Fa(e)&&!window.matchMedia("(max-width: 768px)").matches&&(qa(e,!0,!1),l(()=>{e.isConnected&&Fa(e)&&qa(e,!1,!1)}))}const P=E.loader(y,{type:"dots"});let[H,L,$,J,ut,Ce,No,Bo]=await Promise.all([k.layouts.get().catch(()=>({})),k.settings.get().catch(()=>({})),k.pages.list().catch(()=>[]),k.projects.list().catch(()=>[]),k.pages.tags().catch(()=>[]),k.listTags.list("pages").then(e=>e.map(t=>t.name)).catch(()=>[]),k.get("/blocks?usage=1").catch(()=>k.blocks.list()).catch(()=>[]),k.components.list().catch(()=>[])]);if(!c()){P.destroy();return}P.destroy();const D=e=>y.querySelector(e);if($=Array.isArray($)?$:[],Zs(D("#field-theme"),{includeDefault:!0}),await kr(D("#field-visibility")),!c())return;const Ft=Object.keys(H||{});gr(D("#field-layout"),H,Ft[0]||"default");const jo=e=>Ft.includes(e)?e:Ft[0]||e||"default";for(const e of J)for(const t of["#field-project","#block-project","#comp-project"])D(t).append(new Option(e.name||e.slug,e.slug));let ae=Array.isArray(No)?No:[],ft=za(ae);const mt=new Map(ae.map(e=>[e.name,e.updatedAt])),ze=new Map(ae.map(e=>[e.name,e.usage])),pe=new Set(ae.map(e=>e.name)),Io=async()=>{const e=await k.get("/blocks?usage=1").catch(()=>null);if(!(!c()||!Array.isArray(e))){ae=e,ft=za(ae);for(const t of ae)pe.add(t.name),ze.set(t.name,t.usage),mt.has(t.name)||mt.set(t.name,t.updatedAt)}},qt=Array.isArray(L?.footer?.blocks)?L.footer.blocks.filter(e=>typeof e=="string"):[];let Ve=Array.isArray(Bo)?Bo.filter(e=>typeof e?.name=="string"):[];const se=new Set(Ve.map(e=>e.name)),Fo=async()=>{const e=await k.components.list().catch(()=>null);if(!(!c()||!Array.isArray(e))){Ve=e.filter(t=>typeof t?.name=="string");for(const t of Ve)se.add(t.name)}},te=Se(),p={urlPath:M.observable(""),title:M.observable(""),description:M.observable(""),layout:M.observable(jo("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(""),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(Oo(te.viewMode)),fullscreen:M.observable(!!te.fullscreen),explorerOn:M.observable(te.explorer??!window.matchMedia("(max-width: 1024px)").matches),dockOn:M.observable(!!te.dock),dockTab:M.observable("details"),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(te.secHere!==!1),secPages:M.observable(te.secPages!==!1),secBlocks:M.observable(te.secBlocks!==!1),secComps:M.observable(te.secComps!==!1),thisPage:M.observable([]),pageRows:M.observable([]),blockRows:M.observable([]),compRows:M.observable([])},F=D("#markdown-editor"),ue=D("#css-editor"),rs=Ba(F),ls=Ba(ue);l(()=>rs.destroy()),l(()=>ls.destroy());const ne=document.createElement("div");ne.className="editor-line-numbers editor-line-numbers--foldable",F.parentElement.insertBefore(ne,F);const He=Xa(F,"md"),zt=Xa(ue,"css");l(()=>{He.destroy(),zt.destroy()});const qo=()=>{He.paint(),zt.paint()},G=en(F,ne,{onFoldChange:()=>He.paint()});let xe=!1;G.setGuard(e=>{xe=e});const bt=()=>G.getUnfoldedContent(),zo=M.observable(Nt(te.type));l(M.effect(()=>{const e=zo.value,t=D("#editor-body");if(t){Za(e.font);for(const[o,s]of Object.entries(Na(e)))t.style.setProperty(o,s);t.classList.toggle("pe-nowrap",!e.wrap),F.wrap=e.wrap?"soft":"off",ue.wrap=e.wrap?"soft":"off",requestAnimationFrame(()=>{c()&&qo()})}}));let Vo=null;l(()=>{try{Vo?.close()}catch{}});const K=()=>(a.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,dconfig:p.dconfig.value,body:bt(),css:null}),Ho=()=>M.untracked?M.untracked(K):K(),Z=M.observable(""),vt=()=>p.cssOpened.peek()&&p.css.peek()!==Z.peek(),_=new Map;let f=null;const A=new Map,B=new Map;let x=[];const re=e=>Po(e.form)!==e.base,fe=()=>[...A.entries()].filter(([,e])=>re(e)).map(([e])=>e),me=e=>!e.readOnly&&$o({name:e.name,...e.form})!==e.base,Ke=()=>[...B.values()].filter(me).map(e=>e.name),Vt=()=>Y(Ho())!==a.base.peek(),Ht=e=>e.loaded&&(e===f?Vt():Y(e.state)!==e.base),Pe=()=>[..._.values()].filter(Ht),is=M.computed(()=>Y(K())!==a.base.value),cs=M.computed(()=>p.cssOpened.value&&p.css.value!==Z.value),ds=M.computed(()=>(a.blockRev.value,a.compRev.value,a.docRev.value,Y(K())!==a.base.value||p.cssOpened.value&&p.css.value!==Z.value||Pe().some(e=>e!==f)||fe().length>0||Ke().length>0)),ps=M.computed(()=>{a.blockRev.value,a.compRev.value;const e=I(a.active.value);if(e?.kind==="block"){const t=A.get(e.id);return!!(t&&re(t))}if(e?.kind==="component"){const t=B.get(e.id);return!!(t&&me(t))}return Y(K())!==a.base.value||p.cssOpened.value&&p.css.value!==Z.value}),us=M.computed(()=>a.curId.value!==null),Ko=M.computed(()=>I(a.active.value)?.kind||""),Uo=M.computed(()=>!!a.curId.value&&I(a.active.value)?.kind==="page"),fs=M.computed(()=>p.urlPath.value.trim()||a.curId.value||""),ms=M.computed(()=>Mo({urlPath:p.urlPath.value,dconfig:p.dconfig.value})[0]?.text||""),bs=M.computed(()=>{a.docRev.value;const e=I(a.active.value);return e?e.kind==="block"?a.blockUses.value:e.kind==="component"?Bn({name:e.id,readOnly:a.compReadOnly.value,state:a.compReady.value?a.compState.value:""}):zn({urlPath:p.urlPath.value,status:p.status.value,isEdit:!!a.curId.value}):""}),_o=M.computed(()=>a.compTab.value?"component":a.blockTab.value?"block":a.curId.value!==null?"page":""),vs=M.computed(()=>a.blockTab.value?`[${a.blockTab.value} /]`:""),gs=M.computed(()=>({compiling:"Compiling\u2026",ok:"Compiles",error:"Does not compile"})[a.compState.value]||""),ks=M.computed(()=>a.compTab.value?`<dm-${a.compTab.value}>`:""),ys=M.computed(()=>a.versions.value.some(e=>e.checked.value)),hs=M.computed(()=>a.versions.value.length>0&&a.versions.value.every(e=>e.checked.value)),ws=M.computed(()=>{const e=a.versions.value.filter(t=>t.checked.value).length;return e>0&&e<a.versions.value.length});b=()=>Pe().length>0||fe().length>0||Ke().length>0||vt();const Kt=(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()},dconfig:o,...e.theme?{theme:e.theme}:{}}),Ut=e=>{const t=String(e.dconfig||"").trim();if(!t)return null;try{return JSON.parse(t)}catch{return null}},Es=()=>p.urlPath.peek().trim()||f?.id||"/";let gt=[],Wo=[];const kt=e=>gt.includes(e)||qt.includes(e);let Oe=new Set;const yt=()=>{const e=[...new Set([...gt,...qt,...A.keys()])],t=gn(e.filter(s=>!A.has(s)),mt);Oe=new Set(Object.keys(t));const o=Oe.size?`Preview shows unsaved changes to ${[...Oe].join(", ")}`:"";return o!==a.heldNote.peek()&&(a.heldNote.value=o),Pn(t,[...A.entries()].map(([s,n])=>({name:s,content:n.form.content,css:n.form.css,dirty:re(n)})))};let _t="";const Ms=()=>{Yo();const e=_t;if(e){_t="";try{Re.contentDocument?.querySelector(`[data-block="${CSS.escape(e)}"]`)?.scrollIntoView({block:"center"})}catch{}}},Re=D("#markdown-preview");k.settings.getCustomCss().then(({css:e})=>{if(!c())return;Z.value=e||"";let t=null;try{t=S.get(pt)}catch{}typeof t=="string"&&t!==Z.peek()?(Zo(),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=Z.peek()),W(pt)}).catch(()=>{});const Yo=()=>{const e=Re?.contentDocument;if(!e?.head)return;const t=p.cssOpened.peek()?p.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 Wt=null,Yt=0;const U=(e=!1)=>{clearTimeout(Wt),Wt=setTimeout(async()=>{if(!Re||a.viewMode.peek()==="write"||!a.active.peek()||a.compTab.peek())return;const t=++Yt,o=a.blockTab.peek();a.previewStatus.value="Rendering\u2026";try{let s;if(o&&(!f||!kt(o)))({html:s}=await k.pages.previewFull(cr,{title:o,layout:"blank",breadcrumbs:!1},$n(o,Ls(o)),yt()));else if(f){const n=K();({html:s}=await k.pages.previewFull(Es(),Kt(n,f,Ut(n)),n.body,yt()))}else{Re.srcdoc="";return}if(!c()||t!==Yt)return;pr(Re,xn(dr(s),o),Ms),a.previewStatus.value="Showing unsaved changes"}catch(s){if(!c()||t!==Yt)return;a.previewStatus.value="Preview failed - "+(s?.message||"unknown error"),Re.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,n=>n==="<"?"<":"&")+"</body>"}},e?0:500)};l(()=>clearTimeout(Wt));const Jt=Ys(d.find("#markdown-editor"),d.find("#editor-toolbar"),{spacerDefault:L?.layoutOptions?.spacerSize??40});Xs(F,{blocks:()=>ft,openBlock:e=>Le(e)});const Ue=y.querySelector(".editor-toolbar-right");Ue.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}')`)}),D("#fullscreen-btn")?.setAttribute("data-on-click","toggleFullscreen");const Ts=ct(`
|
|
107
|
+
`)}const ss="editor_prefs";function Te(){return S.get(ss)||{viewMode:"split",fullscreen:!1}}const ur=()=>{try{return Te()||{}}catch{return{}}};function dt(d,m){const v=Te();v[d]=m,S.set(ss,v)}const ns="editor-workspace.tabs",rs="editor-workspace.folded",pt="editor-workspace.css",mr="/__block-preview",fr=d=>d.replace(/<\/head>/i,"<style>body.dm-cloaked{opacity:1 !important;transition:none !important}</style></head>");function br(d,m,v=()=>{}){let l=0;try{l=d.contentWindow.scrollY||0}catch{}d.addEventListener("load",()=>{try{d.contentWindow.scrollTo(0,l)}catch{}v()},{once:!0}),d.srcdoc=m}const vr=["urlPath","title","description","layout","status","sortOrder","category","visibility","project","theme","seoTitle","seoDesc","seoSchema","dconfig"],gr=["showInNav","sidebar","breadcrumbs","bundled"],ls=()=>({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(dr(),q){const e=q;q=null,e.dispose()}const m=window.location.hash.replace(/^#/,""),v=[],l=e=>(v.push(e),e);let b=()=>!1,y=()=>{},w=()=>{};const h=d.get(0),C=h.querySelector("#pe-workspace"),L={address:m,alive:()=>C?.isConnected===!0,dirty:()=>b(),keepDraft:()=>y(),open:(e,t)=>w(e,t),dispose:()=>{v.splice(0).forEach(e=>{try{e()}catch{}})}};q=L;const c=()=>q===L&&L.alive();if(ur().closeSidebar===!0){const e=document.querySelector("#admin-sidebar");e&&!za(e)&&!window.matchMedia("(max-width: 768px)").matches&&(Va(e,!0,!1),l(()=>{e.isConnected&&za(e)&&Va(e,!1,!1)}))}const P=E.loader(h,{type:"dots"});let[H,A,D,J,ut,Ce,Bo,jo]=await Promise.all([k.layouts.get().catch(()=>({})),k.settings.get().catch(()=>({})),k.pages.list().catch(()=>[]),k.projects.list().catch(()=>[]),k.pages.tags().catch(()=>[]),k.listTags.list("pages").then(e=>e.map(t=>t.name)).catch(()=>[]),k.get("/blocks?usage=1").catch(()=>k.blocks.list()).catch(()=>[]),k.components.list().catch(()=>[])]);if(!c()){P.destroy();return}P.destroy();const N=e=>h.querySelector(e);if(D=Array.isArray(D)?D:[],tn(N("#field-theme"),{includeDefault:!0}),await Er(N("#field-visibility")),!c())return;const Ft=Object.keys(H||{});wr(N("#field-layout"),H,Ft[0]||"default");const Io=e=>Ft.includes(e)?e:Ft[0]||e||"default";for(const e of J)for(const t of["#field-project","#block-project","#comp-project"])N(t).append(new Option(e.name||e.slug,e.slug));let ae=Array.isArray(Bo)?Bo:[],mt=Ha(ae);const ft=new Map(ae.map(e=>[e.name,e.updatedAt])),ze=new Map(ae.map(e=>[e.name,e.usage])),pe=new Set(ae.map(e=>e.name)),Fo=async()=>{const e=await k.get("/blocks?usage=1").catch(()=>null);if(!(!c()||!Array.isArray(e))){ae=e,mt=Ha(ae);for(const t of ae)pe.add(t.name),ze.set(t.name,t.usage),ft.has(t.name)||ft.set(t.name,t.updatedAt)}},qt=Array.isArray(A?.footer?.blocks)?A.footer.blocks.filter(e=>typeof e=="string"):[];let Ve=Array.isArray(jo)?jo.filter(e=>typeof e?.name=="string"):[];const se=new Set(Ve.map(e=>e.name)),qo=async()=>{const e=await k.components.list().catch(()=>null);if(!(!c()||!Array.isArray(e))){Ve=e.filter(t=>typeof t?.name=="string");for(const t of Ve)se.add(t.name)}},te=Te(),p={urlPath:M.observable(""),title:M.observable(""),description:M.observable(""),layout:M.observable(Io("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(Ro(te.viewMode)),fullscreen:M.observable(!!te.fullscreen),explorerOn:M.observable(te.explorer??!window.matchMedia("(max-width: 1024px)").matches),dockOn:M.observable(!!te.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(te.secHere!==!1),secPages:M.observable(te.secPages!==!1),secBlocks:M.observable(te.secBlocks!==!1),secComps:M.observable(te.secComps!==!1),thisPage:M.observable([]),pageRows:M.observable([]),blockRows:M.observable([]),compRows:M.observable([])},F=N("#markdown-editor"),ue=N("#css-editor"),is=Ia(F),cs=Ia(ue);l(()=>is.destroy()),l(()=>cs.destroy());const ne=document.createElement("div");ne.className="editor-line-numbers editor-line-numbers--foldable",F.parentElement.insertBefore(ne,F);const He=Za(F,"md"),zt=Za(ue,"css");l(()=>{He.destroy(),zt.destroy()});const zo=()=>{He.paint(),zt.paint()},G=on(F,ne,{onFoldChange:()=>He.paint()});let xe=!1;G.setGuard(e=>{xe=e});const bt=()=>G.getUnfoldedContent(),Vo=M.observable(Lt(te.type));l(M.effect(()=>{const e=Vo.value,t=N("#editor-body");if(t){ts(e.font);for(const[a,s]of Object.entries(ja(e)))t.style.setProperty(a,s);t.classList.toggle("pe-nowrap",!e.wrap),F.wrap=e.wrap?"soft":"off",ue.wrap=e.wrap?"soft":"off",requestAnimationFrame(()=>{c()&&zo()})}}));let Ho=null;l(()=>{try{Ho?.close()}catch{}});const K=()=>(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:bt(),css:null}),Ko=()=>M.untracked?M.untracked(K):K(),Z=M.observable(""),vt=()=>p.cssOpened.peek()&&p.css.peek()!==Z.peek(),_=new Map;let u=null;const $=new Map,B=new Map;let x=[];const re=e=>Oo(e.form)!==e.base,me=()=>[...$.entries()].filter(([,e])=>re(e)).map(([e])=>e),fe=e=>!e.readOnly&&Do({name:e.name,...e.form})!==e.base,Ke=()=>[...B.values()].filter(fe).map(e=>e.name),Vt=()=>Y(Ko())!==o.base.peek(),Ht=e=>e.loaded&&(e===u?Vt():Y(e.state)!==e.base),Pe=()=>[..._.values()].filter(Ht),ds=M.computed(()=>Y(K())!==o.base.value),ps=M.computed(()=>p.cssOpened.value&&p.css.value!==Z.value),us=M.computed(()=>(o.blockRev.value,o.compRev.value,o.docRev.value,Y(K())!==o.base.value||p.cssOpened.value&&p.css.value!==Z.value||Pe().some(e=>e!==u)||me().length>0||Ke().length>0)),ms=M.computed(()=>{o.blockRev.value,o.compRev.value;const e=I(o.active.value);if(e?.kind==="block"){const t=$.get(e.id);return!!(t&&re(t))}if(e?.kind==="component"){const t=B.get(e.id);return!!(t&&fe(t))}return Y(K())!==o.base.value||p.cssOpened.value&&p.css.value!==Z.value}),fs=M.computed(()=>o.curId.value!==null),Uo=M.computed(()=>I(o.active.value)?.kind||""),_o=M.computed(()=>!!o.curId.value&&I(o.active.value)?.kind==="page"),bs=M.computed(()=>p.urlPath.value.trim()||o.curId.value||""),vs=M.computed(()=>Mo({urlPath:p.urlPath.value,dconfig:p.dconfig.value})[0]?.text||""),gs=M.computed(()=>{o.docRev.value;const e=I(o.active.value);return e?e.kind==="block"?o.blockUses.value:e.kind==="component"?qn({name:e.id,readOnly:o.compReadOnly.value,state:o.compReady.value?o.compState.value:""}):Un({urlPath:p.urlPath.value,status:p.status.value,isEdit:!!o.curId.value}):""}),Wo=M.computed(()=>o.compTab.value?"component":o.blockTab.value?"block":o.curId.value!==null?"page":""),ks=M.computed(()=>o.blockTab.value?`[${o.blockTab.value} /]`:""),hs=M.computed(()=>({compiling:"Compiling\u2026",ok:"Compiles",error:"Does not compile"})[o.compState.value]||""),ys=M.computed(()=>o.compTab.value?`<dm-${o.compTab.value}>`:""),ws=M.computed(()=>o.versions.value.some(e=>e.checked.value)),Es=M.computed(()=>o.versions.value.length>0&&o.versions.value.every(e=>e.checked.value)),Ss=M.computed(()=>{const e=o.versions.value.filter(t=>t.checked.value).length;return e>0&&e<o.versions.value.length});b=()=>Pe().length>0||me().length>0||Ke().length>0||vt();const Kt=(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(),...Yo(e)?{schema:Yo(e)}:t?.isEdit?{schema:null}:{}},dconfig:a,...e.theme?{theme:e.theme}:{}}),Yo=e=>Eo(e.seoSchema).value,Ut=e=>{const t=String(e.dconfig||"").trim();if(!t)return null;try{return JSON.parse(t)}catch{return null}},Ms=()=>p.urlPath.peek().trim()||u?.id||"/";let gt=[],Jo=[];const kt=e=>gt.includes(e)||qt.includes(e);let Oe=new Set;const ht=()=>{const e=[...new Set([...gt,...qt,...$.keys()])],t=wn(e.filter(s=>!$.has(s)),ft);Oe=new Set(Object.keys(t));const a=Oe.size?`Preview shows unsaved changes to ${[...Oe].join(", ")}`:"";return a!==o.heldNote.peek()&&(o.heldNote.value=a),An(t,[...$.entries()].map(([s,r])=>({name:s,content:r.form.content,css:r.form.css,dirty:re(r)})))};let _t="";const Ts=()=>{Go();const e=_t;if(e){_t="";try{Re.contentDocument?.querySelector(`[data-block="${CSS.escape(e)}"]`)?.scrollIntoView({block:"center"})}catch{}}},Re=N("#markdown-preview");k.settings.getCustomCss().then(({css:e})=>{if(!c())return;Z.value=e||"";let t=null;try{t=S.get(pt)}catch{}typeof t=="string"&&t!==Z.peek()?(ta(),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=Z.peek()),W(pt)}).catch(()=>{});const Go=()=>{const e=Re?.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 Wt=null,Yt=0;const U=(e=!1)=>{clearTimeout(Wt),Wt=setTimeout(async()=>{if(!Re||o.viewMode.peek()==="write"||!o.active.peek()||o.compTab.peek())return;const t=++Yt,a=o.blockTab.peek();o.previewStatus.value="Rendering\u2026";try{let s;if(a&&(!u||!kt(a)))({html:s}=await k.pages.previewFull(mr,{title:a,layout:"blank",breadcrumbs:!1},Bn(a,Bs(a)),ht()));else if(u){const r=K();({html:s}=await k.pages.previewFull(Ms(),Kt(r,u,Ut(r)),r.body,ht()))}else{Re.srcdoc="";return}if(!c()||t!==Yt)return;br(Re,$n(fr(s),a),Ts),o.previewStatus.value="Showing unsaved changes"}catch(s){if(!c()||t!==Yt)return;o.previewStatus.value="Preview failed - "+(s?.message||"unknown error"),Re.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)};l(()=>clearTimeout(Wt));const Jt=Gs(d.find("#markdown-editor"),d.find("#editor-toolbar"),{spacerDefault:A?.layoutOptions?.spacerSize??40});Zs(F,{blocks:()=>mt,openBlock:e=>Ne(e)});const Ue=h.querySelector(".editor-toolbar-right");Ue.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}')`)}),N("#fullscreen-btn")?.setAttribute("data-on-click","toggleFullscreen");const Cs=ct(`
|
|
108
108
|
<span class="pe-toolbar-extras" style="display:contents">
|
|
109
109
|
<button type="button" class="editor-view-btn" data-tip="Edit Custom CSS" data-on-click="toggleCss"
|
|
110
110
|
data-bind-class="cssMode.value && 'active'"><span>CSS</span></button>
|
|
@@ -116,8 +116,8 @@ The quick brown fox - 0O 1lI {} [] () => ==
|
|
|
116
116
|
<span class="pe-fold-icon" hidden data-bind-hidden="!folded.value"><span data-icon="chevrons-right" data-icon-size="10"></span></span>
|
|
117
117
|
</button>
|
|
118
118
|
<span class="editor-toolbar-sep"></span>
|
|
119
|
-
</span>`);Ue.prepend(Ts),Ue.before(D(".pe-block-bar")),Ue.before(D(".pe-comp-bar"));const Jo=e=>(Array.isArray(e)?e:[]).map(t=>({value:t,label:t})),Go=D("#field-tags");let ht=null;ht=Go?E.pillbox(Go,{data:Jo(ut),value:[],creatable:!0,searchable:!0,placeholder:"Add tag\u2026",onChange:()=>{ht&&(p.tags.value=[...ht.getValue()])}}):null;const Xo=D("#field-admin-tags");let wt=null;wt=Xo?E.pillbox(Xo,{data:Jo(Ce),value:[],creatable:!0,searchable:!0,placeholder:"Add admin tag\u2026",onChange:()=>{wt&&(p.adminTags.value=[...wt.getValue()])}}):null;const Qo=()=>{a.bodyRev.value=a.bodyRev.peek()+1},be=()=>{a.blockRev.value=a.blockRev.peek()+1},ve=()=>{a.compRev.value=a.compRev.peek()+1},_e=()=>{a.docRev.value=a.docRev.peek()+1},Zo=()=>{p.cssOpened.peek()||(p.css.peek()||(p.css.value=Z.peek()),p.cssOpened.value=!0,Ws(ue))},We=e=>{for(const t of ur)p[t].value=e[t]==null?"":String(e[t]);for(const t of fr)p[t].value=!!e[t];p.tags.value=Array.isArray(e.tags)?e.tags:[],ht?.setValue(p.tags.peek()),Array.isArray(e.adminTags)&&(p.adminTags.value=e.adminTags),wt?.setValue(p.adminTags.peek()),Lo(D("#field-visibility"),p.visibility.peek()),G.unfoldAll(),a.folded.value=!1,xe=!0,F.value=e.body==null?"":String(e.body),xe=!1,G.refresh(),qo(),Qo()},ea=(e,t)=>({urlPath:t||"",title:e?.title||"",description:e?.description||"",layout:jo(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:bn({isEdit:!!t,page:e,projects:J,fromHash:t?"":jt()||"",urlPath:t||""}),theme:e?.theme||"",seoTitle:e?.seo?.title||"",seoDesc:e?.seo?.description||"",dconfig:e?.dconfig?JSON.stringify(e.dconfig,null,2):"",body:e?.content||"",css:null}),ta=()=>{f&&(f.state=K(),f.at=Date.now())},oa=e=>{f!==e&&(ta(),f=e,We(e.state),a.base.value=e.base,a.curId.value=e.id,e.at=Date.now(),Mt(),_e())},Ss=()=>{ta(),f=null;const e=ns();We(e),a.base.value=Y(e),a.curId.value=null,Mt(),_e()};We(ns()),a.base.value=Y(K());const W=e=>{try{S.remove?S.remove(e):S.set(e,null)}catch{}},aa=(e,t)=>({name:e.name,...e.form,base:e.base,at:t});h=()=>{try{for(const t of Pe())S.set(je(t.id),ja(t===f?Ho():t.state,t.base));vt()&&S.set(pt,p.css.peek());const e=new Date().toISOString();for(const t of fe()){const o=A.get(t);S.set(Fe(t),{form:{...o.form},base:o.base,at:e})}for(const t of Ke())S.set(qe(t),aa(B.get(t),e))}catch{}};const sa=e=>{b()&&(h(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",sa),l(()=>window.removeEventListener("beforeunload",sa));let Ye=!1,Et=null;const Mt=()=>{Ye=!1,Et=null,a.versions.value=[],a.compare.value=null,a.histError.value="",a.dockOn.peek()&&a.dockTab.peek()==="history"&&(f?.isEdit?le():a.dockTab.value="details")},le=async()=>{const e=f;if(e?.isEdit){Ye=!0,a.histLoading.value=!0,a.histError.value="";try{const t=(await k.versions.list(e.id)).versions||[];if(!c()||f!==e)return;a.versions.value=t.map(o=>({...o,when:Ie(o.createdAt),author:o.author||"",label:o.label||"",who:[o.author,o.label].filter(Boolean).join(" - "),checked:M.observable(!1)}))}catch(t){f===e&&(a.histError.value="Failed to load versions: "+t.message)}finally{f===e&&(a.histLoading.value=!1)}}},Gt=e=>a.versions.peek().find(t=>t.filename===e),Xt=async e=>{const t=f;try{const o=await k.versions.get(t.id,e.filename);if(!c()||f!==t)return;Et=e.filename,a.compare.value={label:Qa(e),old:o.content,current:bt()}}catch(o){E.toast("Failed to load version: "+o.message,{type:"error"})}},Cs=async()=>{const e=f;if(!e?.isEdit)return;const t=await k.pages.get(e.id).catch(()=>null);if(!c()||f!==e||!t)return;e.page=t;const o=ea(t,e.id);e.base=Y(o),e.savedAdminTags=JSON.stringify(o.adminTags),We(o),a.base.value=e.base,Mt(),a.dockOn.peek()&&a.dockTab.peek()==="history"&&le(),U()},na=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 k.versions.restore(f.id,e),E.toast("Page restored.",{type:"success"}),await Cs()}catch(t){E.toast("Restore failed: "+t.message,{type:"error"})}},xs=async e=>{if(await E.confirm("Delete this version? This cannot be undone."))try{await k.versions.delete(f.id,e.filename),E.toast("Version deleted.",{type:"success"}),await le()}catch(t){E.toast("Failed to delete: "+t.message,{type:"error"})}},Ae=new Map,Ps=e=>{if(!Ae.has(e)){const t=I(e);Ae.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 Ae.get(e)},ra=e=>{const t=e===f?p.title.peek():e?.loaded?e.state.title:$.find(o=>o.urlPath===e?.id)?.title;return String(t||"").trim()||e?.id||"New page"};let la=!0,ia="";const Os=()=>{if(la)return;const e={ids:x,active:a.active.peek()},t=JSON.stringify(e);if(t!==ia){ia=t;try{S.set(as,e)}catch{}}},V=()=>{const e=x.map(Ps);for(const t of e){const o=I(t.id);let s,n;if(o.kind==="page"){const i=_.get(o.id);s=ra(i),n=i?.loaded?Ht(i):Te(o),t.title.value=o.id?`${s} (${o.id})`:"New page"}else if(o.kind==="component"){const i=B.get(o.id);s=o.id,n=i?me(i):Te(o),t.title.value=`Component <dm-${o.id}>`}else{const i=A.get(o.id);s=o.id,n=i?re(i):Oe.has(o.id)||Te(o),t.title.value=`Block ${o.id}`}t.label.peek()!==s&&(t.label.value=s),t.dirty.peek()!==n&&(t.dirty.value=n);const r=t.id===a.active.peek();t.active.peek()!==r&&(t.active.value=r)}e.map(t=>t.id).join("|")!==a.tabs.peek().map(t=>t.id).join("|")&&(a.tabs.value=e),Os()};let ge=new Set;try{const e=S.get(ss);Array.isArray(e)&&(ge=new Set(e.filter(t=>typeof t=="string")))}catch{}const ca=new Map,Qt=e=>{let t=ca.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:""},ca.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},Zt=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,n=t?.kind==="component"?t.id:null,r=new Set(Pe().map(m=>m.id)),i=qn($,{filter:e,folded:ge}).map(Qt);for(const m of i){const T=m.kind==="page"&&m.id===o,j=m.kind==="page"&&(r.has(m.id)||!_.get(m.id)?.loaded&&Te({kind:"page",id:m.id})),de=ge.has(m.id);m.active.peek()!==T&&(m.active.value=T),m.dirty.peek()!==j&&(m.dirty.value=j),m.folded.peek()!==de&&(m.folded.value=de)}const g=Dn(ae,e).map(Qt),O=m=>{const T=A.get(m);return T?re(T):Oe.has(m)||Te({kind:"block",id:m})};for(const m of g){const T=m.id===s,j=O(m.id);m.active.peek()!==T&&(m.active.value=T),m.dirty.peek()!==j&&(m.dirty.value=j)}const X=Nn(Ve,e).map(Qt),Ee=m=>{const T=B.get(m);return T?me(T):Te({kind:"component",id:m})};for(const m of X){const T=m.id===n,j=Ee(m.id);m.active.peek()!==T&&(m.active.value=T),m.dirty.peek()!==j&&(m.dirty.value=j)}const ho=f?Sn(gt,qt,pe):[],$a=(m,T,j)=>(Zt.has(m)||Zt.set(m,{key:m,id:T,label:j,kindText:"",title:"",active:M.observable(!1),dirty:M.observable(!1)}),Zt.get(m)),wo=ho.map(m=>{const T=$a(`block:${m.name}`,m.name,m.name);T.kindText=m.footer?"Footer":"Block",T.title=`${m.name} - ${m.footer?"in the footer, on every page":"a block on this page"}`;const j=m.name===s,de=O(m.name);return T.active.peek()!==j&&(T.active.value=j),T.dirty.peek()!==de&&(T.dirty.value=de),T});for(const m of Wo){const T=$a(`component:${m}`,m,m);T.kindText="Component",T.title=`<dm-${m}> - a component on this page`;const j=m===n,de=Ee(m);T.active.peek()!==j&&(T.active.value=j),T.dirty.peek()!==de&&(T.dirty.value=de),wo.push(T)}const Dt=(m,T)=>m.map(j=>j.key).join("|")===T.map(j=>j.key).join("|");Dt(i,a.pageRows.peek())||(a.pageRows.value=i),Dt(g,a.blockRows.peek())||(a.blockRows.value=g),Dt(X,a.compRows.peek())||(a.compRows.value=X),Dt(wo,a.thisPage.peek())||(a.thisPage.value=wo)},da=async()=>{const e=await k.pages.list().catch(()=>null);!c()||!Array.isArray(e)||($=e,z(),V())},ee=()=>{const e=I(a.active.peek()),t=e?_a(e):"/pages/new",o=window.location.hash.replace(/^#/,"");N.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)},Rs=async e=>{if(e.loading)return e.loading;e.loading=(async()=>{let t=null;if(e.isEdit){if(t=await k.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=ea(t,e.id);e.state=o,e.base=Y(o),e.savedAdminTags=JSON.stringify(o.adminTags),e.loaded=!0;let s=null;try{s=S.get(je(e.id))}catch{}const n=vn(s),r=So({kind:"page",id:e.id});if(n){W(je(e.id));const i=Y(n.state)!==e.base,g=mn(n,e.base),O={...o,...n.state,adminTags:o.adminTags};if(i&&r&&!g)e.state=O,E.toast(`Your unsaved changes to ${e.id||"the new page"} are back.`,{type:"info"});else if(i){const X=n.at?` (${Ie(n.at)})`:"",Ee=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"}${X}. Restore them?${Ee}`)&&c()&&(e.state=O)}}return!0})();try{return await e.loading}finally{e.loading=null}},pa=e=>(_.has(e)||_.set(e,{kind:"page",id:e,isEdit:!!e,loaded:!1,page:null,state:null,base:"",savedAdminTags:"[]",at:0}),_.get(e)),ie=async e=>{const t=Q({kind:"page",id:e});({ids:x}=Ro(x,t)),a.active.value=t,a.blockTab.value="",co();const o=pa(e);if(V(),!o.loaded){a.previewStatus.value=`Opening ${e||"a new page"}\u2026`;const s=await Rs(o);if(!c()||!x.includes(t)||_.get(e)!==o)return;if(!s){ua(t);return}if(a.active.peek()!==t){V(),z();return}}oa(o),ee(),V(),z(),U(),G.refresh(),F.focus()};w=async(e,t)=>{if(N.address=t,e.kind==="page"){ie(e.id);return}if(!e.id){(e.kind==="block"?At:tt)({project:jt(t)||""});return}const o=e.kind==="block"?pe:se;if(o.has(e.id)||await(e.kind==="block"?Io:Fo)(),!!c()){if(!o.has(e.id)){E.toast(`There is no ${e.kind} called "${e.id}".`,{type:"warning"}),ee(),z();return}e.kind==="block"?Le(e.id):et(e.id)}};const ua=e=>{const t=a.active.peek(),o=Ln(x,e,t);x=o.ids,Ae.delete(e);const s=I(e),n=o.active!==t?I(o.active):null;if(s.kind==="page"){if(_.delete(s.id),f?.id===s.id){const r=[..._.values()].filter(i=>i.loaded&&i.id!==s.id).sort((i,g)=>g.at-i.at)[0];r?oa(r):Ss()}}else s.kind==="component"?(B.delete(s.id),a.compTab.peek()===s.id&&co()):(A.delete(s.id),a.blockTab.peek()===s.id&&(a.blockTab.value="",a.blockReady.value=!1));if(a.active.value=o.active,n?.kind==="page"){ie(n.id);return}if(n?.kind==="block"){Le(n.id);return}if(n?.kind==="component"){et(n.id);return}ee(),_e(),be(),ve(),V(),z(),U()},Je=e=>{const t=I(e);if(!t||!x.includes(e))return;let o="";try{if(t.kind==="page"){const s=_.get(t.id);s&&Ht(s)&&(S.set(je(s.id),ja(s===f?K():s.state,s.base)),To({kind:"page",id:s.id}),o=ra(s))}else if(t.kind==="component"){const s=B.get(t.id);s&&me(s)&&(S.set(qe(t.id),aa(s,new Date().toISOString())),To({kind:"component",id:t.id}),o=t.id)}else{const s=A.get(t.id);s&&re(s)&&(S.set(Fe(t.id),{form:{...s.form},base:s.base,at:new Date().toISOString()}),To({kind:"block",id:t.id}),o=t.id)}}catch{}ua(e),o&&E.toast(`Changes to "${o}" kept as a draft - back when you open it again.`,{type:"info"})},Tt=e=>{const t=I(e);t&&(t.kind==="page"?ie(t.id):t.kind==="component"?et(t.id):Le(t.id))},fa=new Set,As=async e=>{const t=[];for(const o of e){const s=A.get(o);if(!s)continue;const n={...s.form};try{const r=Ka(n,s.block?.meta);await k.blocks.put(o,r),s.block={...s.block,meta:r.meta},s.saved=n,s.base=Po(n),mt.set(o,new Date().toISOString()),c()&&W(Fe(o)),t.push(o)}catch(r){E.toast(`Block "${o}" not saved: ${r.message||"Unknown error"}`,{type:"error"})}}return t.forEach(o=>fa.add(o)),t.length&&(be(),lt("#/blocks")),t},$s=async e=>{const t=e===f?K():e.state,o=Kt(t,e,Ut(t)),s=e.id;try{if(e.isEdit){const n={frontmatter:o,body:t.body};t.urlPath!==e.id&&(n.newUrlPath=t.urlPath),await k.pages.update(e.id,n)}else await k.pages.create({urlPath:t.urlPath,frontmatter:o,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=Y(t),e===f?a.base.value=e.base:e.state=t,W(je(s)),!e.isEdit||t.urlPath!==e.id){_.delete(s),e.id=t.urlPath,e.isEdit=!0,_.set(e.id,e);const n=Q({kind:"page",id:s}),r=Q({kind:"page",id:e.id});Ae.delete(n),{ids:x}=Hn(x,n,r,a.active.peek()),a.active.peek()===n&&(a.active.value=r),e===f&&(a.curId.value=e.id,ee())}return!0},Ds=async e=>{const t=[];for(const o of e){const s=B.get(o);if(!s||s.readOnly)continue;if(Ot===s?(clearTimeout(Pt),Ot=null,ve(),Ze(s)):s.state||Ze(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 n={...s.form},r={...s.meta||{}};delete r.bundled,n.project?r.project=n.project:delete r.project;try{await k.components.put(o,{source:n.source,bundled:n.bundled,meta:r}),s.meta=r,s.saved=n,s.base=$o({name:o,...n});const i=Ve.find(g=>g.name===o);i&&(i.bundled=n.bundled,i.meta=r),c()&&W(qe(o)),t.push(o)}catch(i){E.toast(`Component "${o}" not saved: ${i.message||"Unknown error"}`,{type:"error"})}}return t.length&&(ve(),lt("#/components")),t};let eo=!1;const to=async(e=!1)=>{if(a.saving.peek()||eo)return;eo=!0;let t,o,s=[];try{if(await ba(),!c())return;const r=I(a.active.peek());if(e)t=Pe(),o=fe(),s=Ke();else if(r?.kind==="block")t=[],o=A.has(r.id)&&re(A.get(r.id))?[r.id]:[];else if(r?.kind==="component")t=[],o=[],s=B.has(r.id)&&me(B.get(r.id))?[r.id]:[];else if(r?.kind==="page"&&f)o=fe().filter(kt),t=!f.isEdit||Vt()||!o.length?[f]:[];else return;if(!t.length&&!o.length&&!s.length){E.toast("Nothing to save.",{type:"info"});return}for(const g of t){const O=Mo(g===f?K():g.state);if(O.length){E.toast(`${g.id||"New page"}: ${O[0].text}`,{type:"warning"}),g!==f&&await ie(g.id),a.dockOn.value=!0,a.dockTab.value=O[0].field==="dconfig"?"dconfig":"details",dt("dock",!0);return}}const i=On(o,Object.fromEntries(o.map(g=>[g,Ua(ze.get(g),f?.id||"")])),fa);if(i&&(!await E.confirm(i)||!c()))return}finally{eo=!1}const n=t.length>0&&vt();a.saving.value=!0;try{let r=0;for(const O of t)await $s(O)&&r++;if(!c())return;if(n){const O=p.css.peek();await k.settings.saveCustomCss(O),Z.value=O,W(pt)}const i=await As(o);if(!c())return;const g=await Ds(s);if((r||i.length||g.length)&&E.toast(Rn({pages:r,css:n,blocks:i,components:g}),{type:"success"}),_e(),!c())return;V(),z(),r&&(lt("#/pages"),Mt(),da())}catch(r){E.toast(`Save failed: ${r.message||"Unknown error"}`,{type:"error"})}finally{a.saving.value=!1}};let oe=null,Ge=null;const $e=new Map,De=new Map,ma=()=>{const e=A.get(a.blockTab.peek());!e||!oe||(a.blockReady.value=!1,oe.setValue(e.form.content),Ge.setValue(e.form.css),a.blockProject.value=e.form.project,Lo(D("#block-project"),e.form.project),a.blockBundled.value=e.form.bundled,a.blockWhere.value=hn({usage:ze.get(e.form.name)})||"Where it is used could not be read.",a.blockNote.value="",a.blockReady.value=!0,no())},Ls=e=>{const t=A.get(e);if(!t)return{};const o=t.form.content;return Object.fromEntries(Wa(Va(o),xo(o),t.samples||{}).map(s=>[s.name,s.value]))};l(M.effect(()=>{const e=a.blockProject.value,t=a.blockBundled.value,o=A.get(a.blockTab.peek());!o||!a.blockReady.peek()||o.form.project===e&&o.form.bundled===t||(o.form.project=e,o.form.bundled=t,be())}));const oo=async(e,{quiet:t=!1}={})=>{for(;$e.has(e);)if(await $e.get(e),A.has(e)||t)return A.get(e)||null;if(A.has(e))return A.get(e);const o=(async()=>{try{const s=await k.blocks.get(e);if(!c()||!t&&!x.includes(Q({kind:"block",id:e})))return null;const n=Ha({...s,name:e}),r={block:s,saved:n,base:Po(n),form:{...n},samples:{}};let i=null;try{i=S.get(Fe(e))}catch{}const g=En(i,r.base);if(t&&g?.stale)return null;if(So({kind:"block",id:e}),g){const O=g.at?` (${Ie(g.at)})`:"",X=!g.stale||await E.confirm(`You left unsaved changes to "${e}"${O}, but the block has been saved since. Restore your version anyway?`);if(!c())return null;X&&(r.form={...r.form,...g.form,name:e},g.stale||E.toast(`Your unsaved changes to "${e}" are back.`,{type:"info"}))}return i&&W(Fe(e)),A.set(e,r),be(),r}catch(s){return c()&&a.blockTab.peek()===e&&(a.blockNote.value=`Could not open "${e}": ${s.message||"unknown error"}`),null}finally{$e.delete(e)}})();return $e.set(e,o),o},ba=async()=>{for(;$e.size||De.size;)await Promise.all([...$e.values(),...De.values()])},Le=async e=>{if(!e||!pe.has(e))return;const t=Q({kind:"block",id:e});({ids:x}=Ro(x,t)),a.active.value=t,co(),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=f&&kt(e)?An(Ua(ze.get(e),f.id||"")):Cn(ze.get(e)),ee(),V(),z();let o=A.get(e);o||(a.blockReady.value=!1,a.blockNote.value=`Opening ${e}\u2026`,o=await oo(e)),!(!o||!c()||a.blockTab.peek()!==e)&&(ma(),V(),z(),_t=e,U(),(a.blockPart.peek()==="css"?Ge:oe)?.focus())};let ao=null;l(()=>clearTimeout(ao));const va=(e,t)=>{const o=A.get(a.blockTab.peek());!o||!a.blockReady.peek()||(o.form[e]=t,clearTimeout(ao),ao=setTimeout(()=>{be(),e==="content"&&no()},120),U())},ke=D("#pe-block-samples"),ga=e=>{ke.replaceChildren();const t=document.createElement("p");t.className="pe-dock-none",t.textContent=e,ke.appendChild(t)};let Xe=[],so=null;function no(){const e=A.get(a.blockTab.peek());if(!e){Xe=[],so=null,ga("Open a block to see its placeholders.");return}const t=Wa(Va(e.form.content),xo(e.form.content),e.samples);if(!(so===e&&t.map(o=>o.name).join("|")===Xe.join("|"))){if(Xe=t.map(o=>o.name),so=e,!t.length){ga("No {{placeholders}} in the template yet.");return}ke.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 n=document.createElement("label");n.className="pe-comp-prop-label",n.textContent=o.name,n.title=o.raw?`{{{${o.name}}}} - raw markup`:`{{${o.name}}}`;const r=document.createElement("input");r.type="text",r.className="form-input form-input--sm pe-comp-prop-input",r.value=o.value,r.setAttribute("aria-label",`Sample value for ${o.name}`),r.addEventListener("input",()=>{e.samples[o.name]=r.value,U()});const i=document.createElement("button");i.type="button",i.className="pe-kebab",i.setAttribute("aria-label",`Actions for ${o.name}`),i.title="Actions",i.textContent="\u22EE",s.append(n,r,i),ke.appendChild(s)}}}const ro=(e,t)=>xo(e.form.content).has(t)?`{{{${t}}}}`:`{{${t}}}`,Ns=e=>{const t=A.get(a.blockTab.peek());return!t||!Xe.includes(e)?[]:[{header:ro(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(ro(t,e),o.selectionStart,o.selectionEnd,"end"),o.dispatchEvent(new Event("input",{bubbles:!0})))}},{label:"Copy the placeholder",icon:"copy",action:()=>Ne(ro(t,e),"Placeholder")},{type:"divider"},{label:"Reset the sample",icon:"rotate-ccw",action:()=>{delete t.samples[e],Xe=[],no(),U()}}]};{const e=E.contextMenu(ke,{cursor:"glyph",match:".pe-comp-prop",inherit:!1,density:"compact",items:t=>Ns(t.dataset.sample)});l(()=>e?.destroy?.()),ke.addEventListener("click",t=>{const o=t.target.closest(".pe-kebab");o&&Me(o)}),ke.addEventListener("keydown",t=>{const o=t.target.closest(".pe-comp-prop");o&&t.key==="F10"&&t.shiftKey&&(t.preventDefault(),Me(o))})}const ce={},ye=D("#component-preview"),St=e=>{try{ye?.contentWindow?.postMessage(e,"*")}catch{}};let lo=ye.contentDocument?.readyState==="complete"&&/component-preview/.test(ye.contentDocument?.URL||""),Ct=!1,Qe=null;const ka=()=>{lo=!0,Qe&&c()&&(St({type:"mount",payload:Qe}),Ct=!0),Qe=null};ye.addEventListener("load",ka),l(()=>ye.removeEventListener("load",ka));const Bs=e=>{if(!lo){Qe=e;return}if(!Ct){Ct=!0,St({type:"mount",payload:e});return}Qe=e,lo=!1,Ct=!1,ye.src=ye.src};let xt="";const io=(e=!1)=>{const t=B.get(a.compTab.peek());if(!(!t||t.state!=="ok"||!t.js)){if(a.viewMode.peek()==="write"){t.previewStale=!0;return}!e&&xt===t.name&&!t.previewStale||(t.previewStale=!1,xt=t.name,Bs({compiledJs:t.js,tagName:`dm-${t.name}`,props:t.previewProps}))}},Ze=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 k.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(`
|
|
120
|
-
`),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"&&(mo(),io(!0))}})().finally(()=>{e.compiling&&t===e.seq&&(e.compiling=null)}),e.compiling};let Pt=null,Ot=null;l(()=>clearTimeout(Pt));const ya=()=>{const e=B.get(a.compTab.peek());if(!e||!ce.template)return;a.compReady.value=!1;const t=it(e.form.source);for(const o of It)ce[o].setValue(t[o]),ce[o].textarea.readOnly=e.readOnly;a.compReadOnly.value=e.readOnly,a.compProject.value=e.form.project,Lo(D("#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,mo(),e.state==="ok"?io():e.state||Ze(e)},co=()=>{a.compTab.peek()&&(a.compTab.value="",a.compReady.value=!1)},ha={page:"details",block:"details",component:"details"};let Rt="";l(M.effect(()=>{const e=_o.value;e!==Rt&&(Rt&&(ha[Rt]=a.dockTab.peek()),Rt=e,e&&(a.dockTab.value=ha[e]||"details",e==="page"&&a.dockTab.peek()==="history"&&a.dockOn.peek()&&f?.isEdit&&!Ye&&le()))}));const po=async(e,{quiet:t=!1}={})=>{if(De.has(e))return De.get(e);if(B.has(e))return B.get(e);const o=(async()=>{try{const s=await k.components.get(e);if(!c()||!x.includes(Q({kind:"component",id:e})))return null;const n=s.readOnly===!0||s.origin==="plugin",r={project:s.meta?.project||"",bundled:!!s.bundled,source:Ya(it(s.source))},i={name:e,comp:s,saved:r,base:$o({name:e,...r}),form:{...r},meta:s.meta||{},readOnly:n,state:"",errors:"",js:null,seq:0,compiling:null,previewProps:{},previewStale:!1};let g=null;try{g=S.get(qe(e))}catch{}const O=Jn(g,i.base);if(t&&(!O.ok||O.stale||n))return null;const X=So({kind:"component",id:e});if(t&&!X)return null;if(O.ok&&!n){const Ee=g.at?` (${Ie(g.at)})`:"",ho=X&&!O.stale||await E.confirm(O.stale?`You left unsaved changes to the component "${e}"${Ee}, but it has been saved since. Restore your version anyway?`:`You left unsaved changes to the component "${e}"${Ee}. Restore them?`);if(!c())return null;ho&&(i.form={project:String(g.project??""),bundled:g.bundled===!0,source:String(g.source)},X&&!O.stale&&E.toast(`Your unsaved changes to the component "${e}" are back.`,{type:"info"}))}return g&&W(qe(e)),B.set(e,i),ve(),i}catch(s){return c()&&a.compTab.peek()===e&&(a.compNote.value=`Could not open <dm-${e}>: ${s.message||"unknown error"}`),null}finally{De.delete(e)}})();return De.set(e,o),o},et=async e=>{if(!e||!se.has(e))return;const t=Q({kind:"component",id:e});({ids:x}=Ro(x,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,ee(),V(),z();let o=B.get(e);o||(a.compReady.value=!1,a.compReadOnly.value=!1,a.compState.value="",a.compNote.value=`Opening <dm-${e}>\u2026`,o=await po(e)),!(!o||!c()||a.compTab.peek()!==e)&&(ya(),V(),z(),ce[a.compPart.peek()]?.focus())},js=(e,t)=>{const o=B.get(a.compTab.peek());if(!o||!a.compReady.peek()||o.readOnly)return;const s=it(o.form.source);s[e]=t,o.form.source=Ya(s),clearTimeout(Pt),Ot=o,Pt=setTimeout(()=>{Ot=null,ve(),Ze(o)},300)};l(M.effect(()=>{const e=a.compProject.value,t=a.compBundled.value,o=B.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,ve())}));const he=D("#pe-comp-props");let uo=[];const fo=e=>{he.replaceChildren();const t=document.createElement("p");t.className="pe-dock-none",t.textContent=e,he.appendChild(t)};function mo(){const e=B.get(a.compTab.peek());if(!e){uo=[],fo("Open a component to try its props.");return}const{rows:t,error:o}=Ja(it(e.form.source).props,e.previewProps);if(uo=t,o){fo("The <props> block is not valid JSON - fix it to see the props here.");return}if(!t.length){e.previewProps={},fo("No props declared. Add them in the <props> block.");return}e.previewProps=Object.fromEntries(t.map(s=>[s.name,s.value])),he.replaceChildren();for(const s of t){const n=document.createElement("div");n.className="pe-comp-prop",n.tabIndex=0,n.dataset.prop=s.name;const r=document.createElement("label");r.className="pe-comp-prop-label",r.textContent=s.label,r.title=`${s.name} (${s.type})`;const i=s.type==="array"||s.type==="object"?document.createElement("textarea"):document.createElement("input");s.type==="boolean"?(i.type="checkbox",i.className="form-check",i.checked=Ga(s.type,s.value)):(i.tagName==="INPUT"?i.type=s.type==="number"?"number":"text":i.rows=2,i.className="form-input form-input--sm pe-comp-prop-input",i.value=Ga(s.type,s.value)),i.setAttribute("aria-label",s.label);const g=()=>{e.previewProps[s.name]=Xn(s.type,s.type==="boolean"?i.checked:i.value),xt===e.name&&St({type:"update",payload:{props:e.previewProps}})};i.addEventListener("input",g),i.addEventListener("change",g);const O=document.createElement("button");O.type="button",O.className="pe-kebab",O.setAttribute("aria-label",`Actions for ${s.label}`),O.title="Actions",O.textContent="\u22EE",n.append(r,i,O),he.appendChild(n)}}const Ne=async(e,t)=>{try{await navigator.clipboard.writeText(e),E.toast(`${t} copied.`,{type:"success"})}catch{E.toast(e,{type:"info",duration:8e3})}},Is=e=>{const t=B.get(a.compTab.peek()),o=uo.find(r=>r.name===e);if(!t||!o)return[];const s=Yn(o.name,o.type,t.previewProps[o.name]),n=`dm-${t.name}`;return[{header:o.label},{label:"Reset to its default",icon:"rotate-ccw",action:()=>{delete t.previewProps[o.name];const r=Ja(it(t.form.source).props,{}).rows.find(i=>i.name===o.name);t.previewProps[o.name]=r?r.value:null,mo(),xt===t.name&&St({type:"update",payload:{props:t.previewProps}})}},{label:"Copy as an attribute",icon:"copy",disabled:!s,action:()=>Ne(s,"Attribute")},{label:"Copy the tag with this value",icon:"code",action:()=>Ne(`<${n}${s?` ${s}`:""}></${n}>`,"Tag")},{label:"Copy the prop name",icon:"copy",action:()=>Ne(o.name,"Name")},{type:"divider"},{label:"Edit in <props>",icon:"edit",action:()=>{a.compPart.value="props";const r=ce.props?.textarea;if(!r)return;const i=r.value.indexOf(`"${o.name}"`);r.focus(),i>=0&&r.setSelectionRange(i,i+o.name.length+2)}}]};{const e=E.contextMenu(he,{cursor:"glyph",match:".pe-comp-prop",inherit:!1,density:"compact",items:t=>Is(t.dataset.prop)});l(()=>e?.destroy?.()),he.addEventListener("click",t=>{const o=t.target.closest(".pe-kebab");o&&Me(o)}),he.addEventListener("keydown",t=>{const o=t.target.closest(".pe-comp-prop");o&&t.key==="F10"&&t.shiftKey&&(t.preventDefault(),Me(o))})}let Be=!1;const wa=()=>jt()||"",At=async({project:e=wa(),seed:t="",content:o=sr,css:s=""}={})=>{if(!Be){Be=!0;try{const r=await es({kind:"block",projects:J,taken:[...pe],project:e,name:t,check:yn,register:l,create:async(i,g)=>{await k.blocks.put(i,Ka({name:i,project:g,bundled:!1,content:o,css:s}))}});if(!c())return;if(!r){ee();return}if(await Io(),!c())return;lt("#/blocks"),E.toast(`Created the block "${r}".`,{type:"success"}),Le(r)}finally{Be=!1}}},tt=async({name:e="",source:t=Wn,project:o=wa()}={})=>{if(!Be){Be=!0;try{const s=await es({kind:"component",projects:J,taken:[...se],project:o,name:e,register:l,check:(n,r)=>Gn(n,{isNew:!0,taken:r}),create:async(n,r)=>{await k.components.put(n,{source:t,bundled:!1,meta:r?{project:r}:{}})}});if(!c())return;if(!s){ee();return}if(await Fo(),!c())return;lt("#/components"),E.toast(`Created the component "${s}".`,{type:"success"}),et(s)}finally{Be=!1}}},Ea=y.querySelector(".dm-plugin-body")||y,Fs=M.computed(()=>{a.blockRev.value,a.compRev.value,a.docRev.value,Y(K()),a.base.value;const e=I(a.active.value),t=[...Pe().map(s=>Q({kind:"page",id:s.id})),...fe().map(s=>Q({kind:"block",id:s})),...Ke().map(s=>Q({kind:"component",id:s}))];let o=[];return e?.kind==="block"||e?.kind==="component"?o=[a.active.value]:e?.kind==="page"&&f&&(o=[a.active.value,...fe().filter(kt).map(s=>Q({kind:"block",id:s}))]),Kn(t,o)}),qs={...p,...a,dirty:ds,pageDirty:is,activeDirty:ps,cssUnsaved:cs,hasPage:us,activeKind:Ko,canView:Uo,viewHref:fs,problem:ms,statusText:bs,saveAll:Fs,dockKind:_o,blockTagText:vs,compStateText:gs,compTagText:ks,anySelected:ys,allSelected:hs,someSelected:ws},ot=(e,t)=>dt(e,t),bo={save:()=>to(!1),saveAllOpen:()=>to(!0),discard:async()=>{if(await ba(),!c())return;const e=I(a.active.peek());if(e?.kind==="block"){const t=A.get(e.id);if(!t||!re(t)||!await E.confirm(`Discard the changes to "${e.id}" you have not saved?`))return;t.form={...t.saved},W(Fe(e.id)),ma(),be()}else if(e?.kind==="component"){const t=B.get(e.id);if(!t||!me(t)||!await E.confirm(`Discard the changes to the component "${e.id}" you have not saved?`))return;t.form={...t.saved},t.state="",W(qe(e.id)),ya(),ve()}else{if(!f||e?.kind==="page"&&e.id!==f.id||!(Vt()||vt())||!await E.confirm("Discard the changes to this page you have not saved?"))return;We(JSON.parse(a.base.peek())),p.cssOpened.peek()&&(p.css.value=Z.peek()),W(je(f.id)),W(pt),_e()}V(),z(),U()},newPage:()=>ie(""),newBlock:()=>At(),newComponent:()=>tt(),toggleSection:e=>{const t={here:"secHere",pages:"secPages",blocks:"secBlocks",components:"secComps"}[e];t&&(a[t].value=!a[t].peek(),ot(t,a[t].peek()))},toggleExplorer:()=>{a.explorerOn.value=!a.explorerOn.peek(),ot("explorer",a.explorerOn.peek())},toggleDock:()=>{a.dockOn.value=!a.dockOn.peek(),ot("dock",a.dockOn.peek()),a.dockOn.peek()&&a.dockTab.peek()==="history"&&!Ye&&le()},showDock:e=>{a.dockTab.value=e,e==="history"&&f?.isEdit&&!Ye&&le()},setBlockPart:e=>{a.blockPart.value=e==="css"?"css":"html",(e==="css"?Ge:oe)?.focus()},formatBlock:()=>{const e=A.get(a.blockTab.peek());!e||!oe||(e.form.content=Mn(e.form.content),oe.setValue(e.form.content),be(),U())},copyShortcode:async()=>{const e=a.blockTab.peek();e&&await Ne(Tn(e),"Shortcode")},setCompPart:e=>{a.compPart.value=It.includes(e)?e:"template",ce[a.compPart.peek()]?.focus()},copyComponent:()=>{const e=B.get(a.compTab.peek());e&&tt({name:Co(e.name,[...se]),source:e.form.source})},setMode:e=>{a.cssMode.peek()&&(a.cssMode.value=!1),a.viewMode.value=Oo(e),ot("viewMode",a.viewMode.peek()),e!=="write"&&(U(!0),io())},toggleFullscreen:()=>{a.fullscreen.value=!a.fullscreen.peek(),ot("fullscreen",a.fullscreen.peek())},toggleCss:()=>{a.blockTab.peek()||a.compTab.peek()||(a.cssMode.peek()?(a.cssMode.value=!1,a.viewMode.value=Oo(Se().viewMode),G.refresh()):(Zo(),a.cssMode.value=!0,a.viewMode.value="split"))},formatMarkdown:()=>{const e=F.selectionStart,t=lr(bt());G.unfoldAll(),xe=!0,F.value=t,xe=!1,F.selectionStart=F.selectionEnd=Math.min(e,t.length),F.dispatchEvent(new Event("input")),He.paint(),G.refresh(),E.toast("Markdown formatted.",{type:"success"})},toggleFold:()=>{a.folded.peek()?G.unfoldAll():G.foldAll(),a.folded.value=!a.folded.peek(),He.paint()},setWidth:e=>{a.liveWidth.value=e},refreshPreview:()=>{if(a.compTab.peek()){const e=B.get(a.compTab.peek());e&&Ze(e)}else U(!0)},share:async()=>{if(!f?.isEdit){E.toast("Save the page first - a share link needs a page to point at.",{type:"warning"});return}await or(f.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 k.versions.bulkDelete(f.id,e),E.toast(`${t} version${t>1?"s":""} deleted.`,{type:"success"}),await le()}catch(o){E.toast("Delete failed: "+o.message,{type:"error"})}},saveNamed:async()=>{if(!f?.isEdit)return;const e=f;await ar(e.id)&&c()&&f===e&&await le()},closeCompare:()=>{a.compare.value=null,Et=null},restoreCompared:()=>na(Et)};Ue.querySelectorAll("[data-tip]").forEach(e=>E.tooltip(e,{content:e.getAttribute("data-tip"),position:"top"}));const zs=M.applyBindings(qs,Ea,{methods:bo});l(()=>zs.dispose()),oe=Do(D('.pe-block-edit[data-part="html"]'),{ariaLabel:"Block template HTML",language:"html",onChange:e=>va("content",e)}),Ge=Do(D('.pe-block-edit[data-part="css"]'),{ariaLabel:"Block CSS",language:"css",placeholder:`/* Scoped to this block - e.g.
|
|
119
|
+
</span>`);Ue.prepend(Cs),Ue.before(N(".pe-block-bar")),Ue.before(N(".pe-comp-bar"));const Xo=e=>(Array.isArray(e)?e:[]).map(t=>({value:t,label:t})),Qo=N("#field-tags");let yt=null;yt=Qo?E.pillbox(Qo,{data:Xo(ut),value:[],creatable:!0,searchable:!0,placeholder:"Add tag\u2026",onChange:()=>{yt&&(p.tags.value=[...yt.getValue()])}}):null;const Zo=N("#field-admin-tags");let wt=null;wt=Zo?E.pillbox(Zo,{data:Xo(Ce),value:[],creatable:!0,searchable:!0,placeholder:"Add admin tag\u2026",onChange:()=>{wt&&(p.adminTags.value=[...wt.getValue()])}}):null;const ea=()=>{o.bodyRev.value=o.bodyRev.peek()+1},be=()=>{o.blockRev.value=o.blockRev.peek()+1},ve=()=>{o.compRev.value=o.compRev.peek()+1},_e=()=>{o.docRev.value=o.docRev.peek()+1},ta=()=>{p.cssOpened.peek()||(p.css.peek()||(p.css.value=Z.peek()),p.cssOpened.value=!0,Js(ue))},We=e=>{for(const t of vr)p[t].value=e[t]==null?"":String(e[t]);for(const t of gr)p[t].value=!!e[t];p.tags.value=Array.isArray(e.tags)?e.tags:[],yt?.setValue(p.tags.peek()),Array.isArray(e.adminTags)&&(p.adminTags.value=e.adminTags),wt?.setValue(p.adminTags.peek()),Lo(N("#field-visibility"),p.visibility.peek()),G.unfoldAll(),o.folded.value=!1,xe=!0,F.value=e.body==null?"":String(e.body),xe=!1,G.refresh(),zo(),ea()},oa=(e,t)=>({urlPath:t||"",title:e?.title||"",description:e?.description||"",layout:Io(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:hn({isEdit:!!t,page:e,projects:J,fromHash:t?"":jt()||"",urlPath:t||""}),theme:e?.theme||"",seoTitle:e?.seo?.title||"",seoDesc:e?.seo?.description||"",seoSchema:rn(e?.seo?.schema),dconfig:e?.dconfig?JSON.stringify(e.dconfig,null,2):"",body:e?.content||"",css:null}),aa=()=>{u&&(u.state=K(),u.at=Date.now())},sa=e=>{u!==e&&(aa(),u=e,We(e.state),o.base.value=e.base,o.curId.value=e.id,e.at=Date.now(),St(),_e())},xs=()=>{aa(),u=null;const e=ls();We(e),o.base.value=Y(e),o.curId.value=null,St(),_e()};We(ls()),o.base.value=Y(K());const W=e=>{try{S.remove?S.remove(e):S.set(e,null)}catch{}},na=(e,t)=>({name:e.name,...e.form,base:e.base,at:t});y=()=>{try{for(const t of Pe())S.set(je(t.id),Fa(t===u?Ko():t.state,t.base));vt()&&S.set(pt,p.css.peek());const e=new Date().toISOString();for(const t of me()){const a=$.get(t);S.set(Fe(t),{form:{...a.form},base:a.base,at:e})}for(const t of Ke())S.set(qe(t),na(B.get(t),e))}catch{}};const ra=e=>{b()&&(y(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",ra),l(()=>window.removeEventListener("beforeunload",ra));let Ye=!1,Et=null;const St=()=>{Ye=!1,Et=null,o.versions.value=[],o.compare.value=null,o.histError.value="",o.dockOn.peek()&&o.dockTab.peek()==="history"&&(u?.isEdit?le():o.dockTab.value="details")},le=async()=>{const e=u;if(e?.isEdit){Ye=!0,o.histLoading.value=!0,o.histError.value="";try{const t=(await k.versions.list(e.id)).versions||[];if(!c()||u!==e)return;o.versions.value=t.map(a=>({...a,when:Ie(a.createdAt),author:a.author||"",label:a.label||"",who:[a.author,a.label].filter(Boolean).join(" - "),checked:M.observable(!1)}))}catch(t){u===e&&(o.histError.value="Failed to load versions: "+t.message)}finally{u===e&&(o.histLoading.value=!1)}}},Gt=e=>o.versions.peek().find(t=>t.filename===e),Xt=async e=>{const t=u;try{const a=await k.versions.get(t.id,e.filename);if(!c()||u!==t)return;Et=e.filename,o.compare.value={label:es(e),old:a.content,current:bt()}}catch(a){E.toast("Failed to load version: "+a.message,{type:"error"})}},Ps=async()=>{const e=u;if(!e?.isEdit)return;const t=await k.pages.get(e.id).catch(()=>null);if(!c()||u!==e||!t)return;e.page=t;const a=oa(t,e.id);e.base=Y(a),e.savedAdminTags=JSON.stringify(a.adminTags),We(a),o.base.value=e.base,St(),o.dockOn.peek()&&o.dockTab.peek()==="history"&&le(),U()},la=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 k.versions.restore(u.id,e),E.toast("Page restored.",{type:"success"}),await Ps()}catch(t){E.toast("Restore failed: "+t.message,{type:"error"})}},Os=async e=>{if(await E.confirm("Delete this version? This cannot be undone."))try{await k.versions.delete(u.id,e.filename),E.toast("Version deleted.",{type:"success"}),await le()}catch(t){E.toast("Failed to delete: "+t.message,{type:"error"})}},$e=new Map,Rs=e=>{if(!$e.has(e)){const t=I(e);$e.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 $e.get(e)},ia=e=>{const t=e===u?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 ca=!0,da="";const $s=()=>{if(ca)return;const e={ids:x,active:o.active.peek()},t=JSON.stringify(e);if(t!==da){da=t;try{S.set(ns,e)}catch{}}},V=()=>{const e=x.map(Rs);for(const t of e){const a=I(t.id);let s,r;if(a.kind==="page"){const i=_.get(a.id);s=ia(i),r=i?.loaded?Ht(i):Me(a),t.title.value=a.id?`${s} (${a.id})`:"New page"}else if(a.kind==="component"){const i=B.get(a.id);s=a.id,r=i?fe(i):Me(a),t.title.value=`Component <dm-${a.id}>`}else{const i=$.get(a.id);s=a.id,r=i?re(i):Oe.has(a.id)||Me(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),$s()};let ge=new Set;try{const e=S.get(rs);Array.isArray(e)&&(ge=new Set(e.filter(t=>typeof t=="string")))}catch{}const pa=new Map,Qt=e=>{let t=pa.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:""},pa.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},Zt=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(Pe().map(f=>f.id)),i=Kn(D,{filter:e,folded:ge}).map(Qt);for(const f of i){const T=f.kind==="page"&&f.id===a,j=f.kind==="page"&&(n.has(f.id)||!_.get(f.id)?.loaded&&Me({kind:"page",id:f.id})),de=ge.has(f.id);f.active.peek()!==T&&(f.active.value=T),f.dirty.peek()!==j&&(f.dirty.value=j),f.folded.peek()!==de&&(f.folded.value=de)}const g=jn(ae,e).map(Qt),O=f=>{const T=$.get(f);return T?re(T):Oe.has(f)||Me({kind:"block",id:f})};for(const f of g){const T=f.id===s,j=O(f.id);f.active.peek()!==T&&(f.active.value=T),f.dirty.peek()!==j&&(f.dirty.value=j)}const X=Fn(Ve,e).map(Qt),Ee=f=>{const T=B.get(f);return T?fe(T):Me({kind:"component",id:f})};for(const f of X){const T=f.id===r,j=Ee(f.id);f.active.peek()!==T&&(f.active.value=T),f.dirty.peek()!==j&&(f.dirty.value=j)}const yo=u?On(gt,qt,pe):[],Na=(f,T,j)=>(Zt.has(f)||Zt.set(f,{key:f,id:T,label:j,kindText:"",title:"",active:M.observable(!1),dirty:M.observable(!1)}),Zt.get(f)),wo=yo.map(f=>{const T=Na(`block:${f.name}`,f.name,f.name);T.kindText=f.footer?"Footer":"Block",T.title=`${f.name} - ${f.footer?"in the footer, on every page":"a block on this page"}`;const j=f.name===s,de=O(f.name);return T.active.peek()!==j&&(T.active.value=j),T.dirty.peek()!==de&&(T.dirty.value=de),T});for(const f of Jo){const T=Na(`component:${f}`,f,f);T.kindText="Component",T.title=`<dm-${f}> - a component on this page`;const j=f===r,de=Ee(f);T.active.peek()!==j&&(T.active.value=j),T.dirty.peek()!==de&&(T.dirty.value=de),wo.push(T)}const Dt=(f,T)=>f.map(j=>j.key).join("|")===T.map(j=>j.key).join("|");Dt(i,o.pageRows.peek())||(o.pageRows.value=i),Dt(g,o.blockRows.peek())||(o.blockRows.value=g),Dt(X,o.compRows.peek())||(o.compRows.value=X),Dt(wo,o.thisPage.peek())||(o.thisPage.value=wo)},ua=async()=>{const e=await k.pages.list().catch(()=>null);!c()||!Array.isArray(e)||(D=e,z(),V())},ee=()=>{const e=I(o.active.peek()),t=e?Ya(e):"/pages/new",a=window.location.hash.replace(/^#/,"");L.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)},As=async e=>{if(e.loading)return e.loading;e.loading=(async()=>{let t=null;if(e.isEdit){if(t=await k.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=oa(t,e.id);e.state=a,e.base=Y(a),e.savedAdminTags=JSON.stringify(a.adminTags),e.loaded=!0;let s=null;try{s=S.get(je(e.id))}catch{}const r=yn(s),n=Co({kind:"page",id:e.id});if(r){W(je(e.id));const i=Y(r.state)!==e.base,g=kn(r,e.base),O={...a,...r.state,adminTags:a.adminTags};if(i&&n&&!g)e.state=O,E.toast(`Your unsaved changes to ${e.id||"the new page"} are back.`,{type:"info"});else if(i){const X=r.at?` (${Ie(r.at)})`:"",Ee=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"}${X}. Restore them?${Ee}`)&&c()&&(e.state=O)}}return!0})();try{return await e.loading}finally{e.loading=null}},ma=e=>(_.has(e)||_.set(e,{kind:"page",id:e,isEdit:!!e,loaded:!1,page:null,state:null,base:"",savedAdminTags:"[]",at:0}),_.get(e)),ie=async e=>{const t=Q({kind:"page",id:e});({ids:x}=$o(x,t)),o.active.value=t,o.blockTab.value="",co();const a=ma(e);if(V(),!a.loaded){o.previewStatus.value=`Opening ${e||"a new page"}\u2026`;const s=await As(a);if(!c()||!x.includes(t)||_.get(e)!==a)return;if(!s){fa(t);return}if(o.active.peek()!==t){V(),z();return}}sa(a),ee(),V(),z(),U(),G.refresh(),F.focus()};w=async(e,t)=>{if(L.address=t,e.kind==="page"){ie(e.id);return}if(!e.id){(e.kind==="block"?$t:tt)({project:jt(t)||""});return}const a=e.kind==="block"?pe:se;if(a.has(e.id)||await(e.kind==="block"?Fo:qo)(),!!c()){if(!a.has(e.id)){E.toast(`There is no ${e.kind} called "${e.id}".`,{type:"warning"}),ee(),z();return}e.kind==="block"?Ne(e.id):et(e.id)}};const fa=e=>{const t=o.active.peek(),a=In(x,e,t);x=a.ids,$e.delete(e);const s=I(e),r=a.active!==t?I(a.active):null;if(s.kind==="page"){if(_.delete(s.id),u?.id===s.id){const n=[..._.values()].filter(i=>i.loaded&&i.id!==s.id).sort((i,g)=>g.at-i.at)[0];n?sa(n):xs()}}else s.kind==="component"?(B.delete(s.id),o.compTab.peek()===s.id&&co()):($.delete(s.id),o.blockTab.peek()===s.id&&(o.blockTab.value="",o.blockReady.value=!1));if(o.active.value=a.active,r?.kind==="page"){ie(r.id);return}if(r?.kind==="block"){Ne(r.id);return}if(r?.kind==="component"){et(r.id);return}ee(),_e(),be(),ve(),V(),z(),U()},Je=e=>{const t=I(e);if(!t||!x.includes(e))return;let a="";try{if(t.kind==="page"){const s=_.get(t.id);s&&Ht(s)&&(S.set(je(s.id),Fa(s===u?K():s.state,s.base)),To({kind:"page",id:s.id}),a=ia(s))}else if(t.kind==="component"){const s=B.get(t.id);s&&fe(s)&&(S.set(qe(t.id),na(s,new Date().toISOString())),To({kind:"component",id:t.id}),a=t.id)}else{const s=$.get(t.id);s&&re(s)&&(S.set(Fe(t.id),{form:{...s.form},base:s.base,at:new Date().toISOString()}),To({kind:"block",id:t.id}),a=t.id)}}catch{}fa(e),a&&E.toast(`Changes to "${a}" kept as a draft - back when you open it again.`,{type:"info"})},Mt=e=>{const t=I(e);t&&(t.kind==="page"?ie(t.id):t.kind==="component"?et(t.id):Ne(t.id))},ba=new Set,Ds=async e=>{const t=[];for(const a of e){const s=$.get(a);if(!s)continue;const r={...s.form};try{const n=_a(r,s.block?.meta);await k.blocks.put(a,n),s.block={...s.block,meta:n.meta},s.saved=r,s.base=Oo(r),ft.set(a,new Date().toISOString()),c()&&W(Fe(a)),t.push(a)}catch(n){E.toast(`Block "${a}" not saved: ${n.message||"Unknown error"}`,{type:"error"})}}return t.forEach(a=>ba.add(a)),t.length&&(be(),lt("#/blocks")),t},Ns=async e=>{const t=e===u?K():e.state,a=Eo(t.seoSchema).error;if(a)return E.toast(`${e.id||"The new page"} not saved - structured data: ${a}`,{type:"error"}),e===u&&(o.seoSchemaNote.value=a,o.dockOn.value=!0,o.dockTab.value="seo"),!1;const s=Kt(t,e,Ut(t)),r=e.id;try{if(e.isEdit){const n={frontmatter:s,body:t.body};t.urlPath!==e.id&&(n.newUrlPath=t.urlPath),await k.pages.update(e.id,n)}else await k.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=Y(t),e===u?o.base.value=e.base:e.state=t,W(je(r)),!e.isEdit||t.urlPath!==e.id){_.delete(r),e.id=t.urlPath,e.isEdit=!0,_.set(e.id,e);const n=Q({kind:"page",id:r}),i=Q({kind:"page",id:e.id});$e.delete(n),{ids:x}=Wn(x,n,i,o.active.peek()),o.active.peek()===n&&(o.active.value=i),e===u&&(o.curId.value=e.id,ee())}return!0},Ls=async e=>{const t=[];for(const a of e){const s=B.get(a);if(!s||s.readOnly)continue;if(Ot===s?(clearTimeout(Pt),Ot=null,ve(),Ze(s)):s.state||Ze(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 k.components.put(a,{source:r.source,bundled:r.bundled,meta:n}),s.meta=n,s.saved=r,s.base=Do({name:a,...r});const i=Ve.find(g=>g.name===a);i&&(i.bundled=r.bundled,i.meta=n),c()&&W(qe(a)),t.push(a)}catch(i){E.toast(`Component "${a}" not saved: ${i.message||"Unknown error"}`,{type:"error"})}}return t.length&&(ve(),lt("#/components")),t};let eo=!1;const to=async(e=!1)=>{if(o.saving.peek()||eo)return;eo=!0;let t,a,s=[];try{if(await ga(),!c())return;const n=I(o.active.peek());if(e)t=Pe(),a=me(),s=Ke();else if(n?.kind==="block")t=[],a=$.has(n.id)&&re($.get(n.id))?[n.id]:[];else if(n?.kind==="component")t=[],a=[],s=B.has(n.id)&&fe(B.get(n.id))?[n.id]:[];else if(n?.kind==="page"&&u)a=me().filter(kt),t=!u.isEdit||Vt()||!a.length?[u]:[];else return;if(!t.length&&!a.length&&!s.length){E.toast("Nothing to save.",{type:"info"});return}for(const g of t){const O=Mo(g===u?K():g.state);if(O.length){E.toast(`${g.id||"New page"}: ${O[0].text}`,{type:"warning"}),g!==u&&await ie(g.id),o.dockOn.value=!0,o.dockTab.value=O[0].field==="dconfig"?"dconfig":"details",dt("dock",!0);return}}const i=Dn(a,Object.fromEntries(a.map(g=>[g,Wa(ze.get(g),u?.id||"")])),ba);if(i&&(!await E.confirm(i)||!c()))return}finally{eo=!1}const r=t.length>0&&vt();o.saving.value=!0;try{let n=0;for(const O of t)await Ns(O)&&n++;if(!c())return;if(r){const O=p.css.peek();await k.settings.saveCustomCss(O),Z.value=O,W(pt)}const i=await Ds(a);if(!c())return;const g=await Ls(s);if((n||i.length||g.length)&&E.toast(Nn({pages:n,css:r,blocks:i,components:g}),{type:"success"}),_e(),!c())return;V(),z(),n&&(lt("#/pages"),St(),ua())}catch(n){E.toast(`Save failed: ${n.message||"Unknown error"}`,{type:"error"})}finally{o.saving.value=!1}};let oe=null,Ge=null;const Ae=new Map,De=new Map,va=()=>{const e=$.get(o.blockTab.peek());!e||!oe||(o.blockReady.value=!1,oe.setValue(e.form.content),Ge.setValue(e.form.css),o.blockProject.value=e.form.project,Lo(N("#block-project"),e.form.project),o.blockBundled.value=e.form.bundled,o.blockWhere.value=Mn({usage:ze.get(e.form.name)})||"Where it is used could not be read.",o.blockNote.value="",o.blockReady.value=!0,no())},Bs=e=>{const t=$.get(e);if(!t)return{};const a=t.form.content;return Object.fromEntries(Ja(Ka(a),Po(a),t.samples||{}).map(s=>[s.name,s.value]))};l(M.effect(()=>{const e=o.blockProject.value,t=o.blockBundled.value,a=$.get(o.blockTab.peek());!a||!o.blockReady.peek()||a.form.project===e&&a.form.bundled===t||(a.form.project=e,a.form.bundled=t,be())}));const oo=async(e,{quiet:t=!1}={})=>{for(;Ae.has(e);)if(await Ae.get(e),$.has(e)||t)return $.get(e)||null;if($.has(e))return $.get(e);const a=(async()=>{try{const s=await k.blocks.get(e);if(!c()||!t&&!x.includes(Q({kind:"block",id:e})))return null;const r=Ua({...s,name:e}),n={block:s,saved:r,base:Oo(r),form:{...r},samples:{}};let i=null;try{i=S.get(Fe(e))}catch{}const g=Cn(i,n.base);if(t&&g?.stale)return null;if(Co({kind:"block",id:e}),g){const O=g.at?` (${Ie(g.at)})`:"",X=!g.stale||await E.confirm(`You left unsaved changes to "${e}"${O}, but the block has been saved since. Restore your version anyway?`);if(!c())return null;X&&(n.form={...n.form,...g.form,name:e},g.stale||E.toast(`Your unsaved changes to "${e}" are back.`,{type:"info"}))}return i&&W(Fe(e)),$.set(e,n),be(),n}catch(s){return c()&&o.blockTab.peek()===e&&(o.blockNote.value=`Could not open "${e}": ${s.message||"unknown error"}`),null}finally{Ae.delete(e)}})();return Ae.set(e,a),a},ga=async()=>{for(;Ae.size||De.size;)await Promise.all([...Ae.values(),...De.values()])},Ne=async e=>{if(!e||!pe.has(e))return;const t=Q({kind:"block",id:e});({ids:x}=$o(x,t)),o.active.value=t,co(),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=u&&kt(e)?Ln(Wa(ze.get(e),u.id||"")):Rn(ze.get(e)),ee(),V(),z();let a=$.get(e);a||(o.blockReady.value=!1,o.blockNote.value=`Opening ${e}\u2026`,a=await oo(e)),!(!a||!c()||o.blockTab.peek()!==e)&&(va(),V(),z(),_t=e,U(),(o.blockPart.peek()==="css"?Ge:oe)?.focus())};let ao=null;l(()=>clearTimeout(ao));const ka=(e,t)=>{const a=$.get(o.blockTab.peek());!a||!o.blockReady.peek()||(a.form[e]=t,clearTimeout(ao),ao=setTimeout(()=>{be(),e==="content"&&no()},120),U())},ke=N("#pe-block-samples"),ha=e=>{ke.replaceChildren();const t=document.createElement("p");t.className="pe-dock-none",t.textContent=e,ke.appendChild(t)};let Xe=[],so=null;function no(){const e=$.get(o.blockTab.peek());if(!e){Xe=[],so=null,ha("Open a block to see its placeholders.");return}const t=Ja(Ka(e.form.content),Po(e.form.content),e.samples);if(!(so===e&&t.map(a=>a.name).join("|")===Xe.join("|"))){if(Xe=t.map(a=>a.name),so=e,!t.length){ha("No {{placeholders}} in the template yet.");return}ke.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 i=document.createElement("button");i.type="button",i.className="pe-kebab",i.setAttribute("aria-label",`Actions for ${a.name}`),i.title="Actions",i.textContent="\u22EE",s.append(r,n,i),ke.appendChild(s)}}}const ro=(e,t)=>Po(e.form.content).has(t)?`{{{${t}}}}`:`{{${t}}}`,js=e=>{const t=$.get(o.blockTab.peek());return!t||!Xe.includes(e)?[]:[{header:ro(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(ro(t,e),a.selectionStart,a.selectionEnd,"end"),a.dispatchEvent(new Event("input",{bubbles:!0})))}},{label:"Copy the placeholder",icon:"copy",action:()=>Le(ro(t,e),"Placeholder")},{type:"divider"},{label:"Reset the sample",icon:"rotate-ccw",action:()=>{delete t.samples[e],Xe=[],no(),U()}}]};{const e=E.contextMenu(ke,{cursor:"glyph",match:".pe-comp-prop",inherit:!1,density:"compact",items:t=>js(t.dataset.sample)});l(()=>e?.destroy?.()),ke.addEventListener("click",t=>{const a=t.target.closest(".pe-kebab");a&&Se(a)}),ke.addEventListener("keydown",t=>{const a=t.target.closest(".pe-comp-prop");a&&t.key==="F10"&&t.shiftKey&&(t.preventDefault(),Se(a))})}const ce={},he=N("#component-preview"),Tt=e=>{try{he?.contentWindow?.postMessage(e,"*")}catch{}};let lo=he.contentDocument?.readyState==="complete"&&/component-preview/.test(he.contentDocument?.URL||""),Ct=!1,Qe=null;const ya=()=>{lo=!0,Qe&&c()&&(Tt({type:"mount",payload:Qe}),Ct=!0),Qe=null};he.addEventListener("load",ya),l(()=>he.removeEventListener("load",ya));const Is=e=>{if(!lo){Qe=e;return}if(!Ct){Ct=!0,Tt({type:"mount",payload:e});return}Qe=e,lo=!1,Ct=!1,he.src=he.src};let xt="";const io=(e=!1)=>{const t=B.get(o.compTab.peek());if(!(!t||t.state!=="ok"||!t.js)){if(o.viewMode.peek()==="write"){t.previewStale=!0;return}!e&&xt===t.name&&!t.previewStale||(t.previewStale=!1,xt=t.name,Is({compiledJs:t.js,tagName:`dm-${t.name}`,props:t.previewProps}))}},Ze=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 k.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(`
|
|
120
|
+
`),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"&&(fo(),io(!0))}})().finally(()=>{e.compiling&&t===e.seq&&(e.compiling=null)}),e.compiling};let Pt=null,Ot=null;l(()=>clearTimeout(Pt));const wa=()=>{const e=B.get(o.compTab.peek());if(!e||!ce.template)return;o.compReady.value=!1;const t=it(e.form.source);for(const a of It)ce[a].setValue(t[a]),ce[a].textarea.readOnly=e.readOnly;o.compReadOnly.value=e.readOnly,o.compProject.value=e.form.project,Lo(N("#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,fo(),e.state==="ok"?io():e.state||Ze(e)},co=()=>{o.compTab.peek()&&(o.compTab.value="",o.compReady.value=!1)},Ea={page:"details",block:"details",component:"details"};let Rt="";l(M.effect(()=>{const e=Wo.value;e!==Rt&&(Rt&&(Ea[Rt]=o.dockTab.peek()),Rt=e,e&&(o.dockTab.value=Ea[e]||"details",e==="page"&&o.dockTab.peek()==="history"&&o.dockOn.peek()&&u?.isEdit&&!Ye&&le()))}));const po=async(e,{quiet:t=!1}={})=>{if(De.has(e))return De.get(e);if(B.has(e))return B.get(e);const a=(async()=>{try{const s=await k.components.get(e);if(!c()||!x.includes(Q({kind:"component",id:e})))return null;const r=s.readOnly===!0||s.origin==="plugin",n={project:s.meta?.project||"",bundled:!!s.bundled,source:Ga(it(s.source))},i={name:e,comp:s,saved:n,base:Do({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(qe(e))}catch{}const O=Zn(g,i.base);if(t&&(!O.ok||O.stale||r))return null;const X=Co({kind:"component",id:e});if(t&&!X)return null;if(O.ok&&!r){const Ee=g.at?` (${Ie(g.at)})`:"",yo=X&&!O.stale||await E.confirm(O.stale?`You left unsaved changes to the component "${e}"${Ee}, but it has been saved since. Restore your version anyway?`:`You left unsaved changes to the component "${e}"${Ee}. Restore them?`);if(!c())return null;yo&&(i.form={project:String(g.project??""),bundled:g.bundled===!0,source:String(g.source)},X&&!O.stale&&E.toast(`Your unsaved changes to the component "${e}" are back.`,{type:"info"}))}return g&&W(qe(e)),B.set(e,i),ve(),i}catch(s){return c()&&o.compTab.peek()===e&&(o.compNote.value=`Could not open <dm-${e}>: ${s.message||"unknown error"}`),null}finally{De.delete(e)}})();return De.set(e,a),a},et=async e=>{if(!e||!se.has(e))return;const t=Q({kind:"component",id:e});({ids:x}=$o(x,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,ee(),V(),z();let a=B.get(e);a||(o.compReady.value=!1,o.compReadOnly.value=!1,o.compState.value="",o.compNote.value=`Opening <dm-${e}>\u2026`,a=await po(e)),!(!a||!c()||o.compTab.peek()!==e)&&(wa(),V(),z(),ce[o.compPart.peek()]?.focus())},Fs=(e,t)=>{const a=B.get(o.compTab.peek());if(!a||!o.compReady.peek()||a.readOnly)return;const s=it(a.form.source);s[e]=t,a.form.source=Ga(s),clearTimeout(Pt),Ot=a,Pt=setTimeout(()=>{Ot=null,ve(),Ze(a)},300)};l(M.effect(()=>{const e=o.compProject.value,t=o.compBundled.value,a=B.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,ve())}));const ye=N("#pe-comp-props");let uo=[];const mo=e=>{ye.replaceChildren();const t=document.createElement("p");t.className="pe-dock-none",t.textContent=e,ye.appendChild(t)};function fo(){const e=B.get(o.compTab.peek());if(!e){uo=[],mo("Open a component to try its props.");return}const{rows:t,error:a}=Xa(it(e.form.source).props,e.previewProps);if(uo=t,a){mo("The <props> block is not valid JSON - fix it to see the props here.");return}if(!t.length){e.previewProps={},mo("No props declared. Add them in the <props> block.");return}e.previewProps=Object.fromEntries(t.map(s=>[s.name,s.value])),ye.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 i=s.type==="array"||s.type==="object"?document.createElement("textarea"):document.createElement("input");s.type==="boolean"?(i.type="checkbox",i.className="form-check",i.checked=Qa(s.type,s.value)):(i.tagName==="INPUT"?i.type=s.type==="number"?"number":"text":i.rows=2,i.className="form-input form-input--sm pe-comp-prop-input",i.value=Qa(s.type,s.value)),i.setAttribute("aria-label",s.label);const g=()=>{e.previewProps[s.name]=tr(s.type,s.type==="boolean"?i.checked:i.value),xt===e.name&&Tt({type:"update",payload:{props:e.previewProps}})};i.addEventListener("input",g),i.addEventListener("change",g);const O=document.createElement("button");O.type="button",O.className="pe-kebab",O.setAttribute("aria-label",`Actions for ${s.label}`),O.title="Actions",O.textContent="\u22EE",r.append(n,i,O),ye.appendChild(r)}}const Le=async(e,t)=>{try{await navigator.clipboard.writeText(e),E.toast(`${t} copied.`,{type:"success"})}catch{E.toast(e,{type:"info",duration:8e3})}},qs=e=>{const t=B.get(o.compTab.peek()),a=uo.find(n=>n.name===e);if(!t||!a)return[];const s=Qn(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=Xa(it(t.form.source).props,{}).rows.find(i=>i.name===a.name);t.previewProps[a.name]=n?n.value:null,fo(),xt===t.name&&Tt({type:"update",payload:{props:t.previewProps}})}},{label:"Copy as an attribute",icon:"copy",disabled:!s,action:()=>Le(s,"Attribute")},{label:"Copy the tag with this value",icon:"code",action:()=>Le(`<${r}${s?` ${s}`:""}></${r}>`,"Tag")},{label:"Copy the prop name",icon:"copy",action:()=>Le(a.name,"Name")},{type:"divider"},{label:"Edit in <props>",icon:"edit",action:()=>{o.compPart.value="props";const n=ce.props?.textarea;if(!n)return;const i=n.value.indexOf(`"${a.name}"`);n.focus(),i>=0&&n.setSelectionRange(i,i+a.name.length+2)}}]};{const e=E.contextMenu(ye,{cursor:"glyph",match:".pe-comp-prop",inherit:!1,density:"compact",items:t=>qs(t.dataset.prop)});l(()=>e?.destroy?.()),ye.addEventListener("click",t=>{const a=t.target.closest(".pe-kebab");a&&Se(a)}),ye.addEventListener("keydown",t=>{const a=t.target.closest(".pe-comp-prop");a&&t.key==="F10"&&t.shiftKey&&(t.preventDefault(),Se(a))})}let Be=!1;const Sa=()=>jt()||"",$t=async({project:e=Sa(),seed:t="",content:a=ir,css:s=""}={})=>{if(!Be){Be=!0;try{const n=await os({kind:"block",projects:J,taken:[...pe],project:e,name:t,check:Sn,register:l,create:async(i,g)=>{await k.blocks.put(i,_a({name:i,project:g,bundled:!1,content:a,css:s}))}});if(!c())return;if(!n){ee();return}if(await Fo(),!c())return;lt("#/blocks"),E.toast(`Created the block "${n}".`,{type:"success"}),Ne(n)}finally{Be=!1}}},tt=async({name:e="",source:t=Xn,project:a=Sa()}={})=>{if(!Be){Be=!0;try{const s=await os({kind:"component",projects:J,taken:[...se],project:a,name:e,register:l,check:(r,n)=>er(r,{isNew:!0,taken:n}),create:async(r,n)=>{await k.components.put(r,{source:t,bundled:!1,meta:n?{project:n}:{}})}});if(!c())return;if(!s){ee();return}if(await qo(),!c())return;lt("#/components"),E.toast(`Created the component "${s}".`,{type:"success"}),et(s)}finally{Be=!1}}},Ma=h.querySelector(".dm-plugin-body")||h,zs=M.computed(()=>{o.blockRev.value,o.compRev.value,o.docRev.value,Y(K()),o.base.value;const e=I(o.active.value),t=[...Pe().map(s=>Q({kind:"page",id:s.id})),...me().map(s=>Q({kind:"block",id:s})),...Ke().map(s=>Q({kind:"component",id:s}))];let a=[];return e?.kind==="block"||e?.kind==="component"?a=[o.active.value]:e?.kind==="page"&&u&&(a=[o.active.value,...me().filter(kt).map(s=>Q({kind:"block",id:s}))]),Yn(t,a)}),Vs={...p,...o,dirty:us,pageDirty:ds,activeDirty:ms,cssUnsaved:ps,hasPage:fs,activeKind:Uo,canView:_o,viewHref:bs,problem:vs,statusText:gs,saveAll:zs,dockKind:Wo,blockTagText:ks,compStateText:hs,compTagText:ys,anySelected:ws,allSelected:Es,someSelected:Ss},ot=(e,t)=>dt(e,t),bo={save:()=>to(!1),saveAllOpen:()=>to(!0),discard:async()=>{if(await ga(),!c())return;const e=I(o.active.peek());if(e?.kind==="block"){const t=$.get(e.id);if(!t||!re(t)||!await E.confirm(`Discard the changes to "${e.id}" you have not saved?`))return;t.form={...t.saved},W(Fe(e.id)),va(),be()}else if(e?.kind==="component"){const t=B.get(e.id);if(!t||!fe(t)||!await E.confirm(`Discard the changes to the component "${e.id}" you have not saved?`))return;t.form={...t.saved},t.state="",W(qe(e.id)),wa(),ve()}else{if(!u||e?.kind==="page"&&e.id!==u.id||!(Vt()||vt())||!await E.confirm("Discard the changes to this page you have not saved?"))return;We(JSON.parse(o.base.peek())),p.cssOpened.peek()&&(p.css.value=Z.peek()),W(je(u.id)),W(pt),_e()}V(),z(),U()},newPage:()=>ie(""),newBlock:()=>$t(),newComponent:()=>tt(),toggleSection:e=>{const t={here:"secHere",pages:"secPages",blocks:"secBlocks",components:"secComps"}[e];t&&(o[t].value=!o[t].peek(),ot(t,o[t].peek()))},toggleExplorer:()=>{o.explorerOn.value=!o.explorerOn.peek(),ot("explorer",o.explorerOn.peek())},toggleDock:()=>{o.dockOn.value=!o.dockOn.peek(),ot("dock",o.dockOn.peek()),o.dockOn.peek()&&o.dockTab.peek()==="history"&&!Ye&&le()},showDock:e=>{o.dockTab.value=e,e==="history"&&u?.isEdit&&!Ye&&le()},schemaChanged:()=>{o.seoSchemaNote.value=Eo(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()||u?.id||"/";p.seoSchema.value=nn(a,{title:p.seoTitle.peek().trim()||p.title.peek().trim(),description:p.seoDesc.peek().trim()||p.description.peek().trim(),url:`${location.origin}${s}`,siteName:A?.title||A?.seo?.defaultTitle||""}),o.seoSchemaNote.value=""},setBlockPart:e=>{o.blockPart.value=e==="css"?"css":"html",(e==="css"?Ge:oe)?.focus()},formatBlock:()=>{const e=$.get(o.blockTab.peek());!e||!oe||(e.form.content=xn(e.form.content),oe.setValue(e.form.content),be(),U())},copyShortcode:async()=>{const e=o.blockTab.peek();e&&await Le(Pn(e),"Shortcode")},setCompPart:e=>{o.compPart.value=It.includes(e)?e:"template",ce[o.compPart.peek()]?.focus()},copyComponent:()=>{const e=B.get(o.compTab.peek());e&&tt({name:xo(e.name,[...se]),source:e.form.source})},setMode:e=>{o.cssMode.peek()&&(o.cssMode.value=!1),o.viewMode.value=Ro(e),ot("viewMode",o.viewMode.peek()),e!=="write"&&(U(!0),io())},toggleFullscreen:()=>{o.fullscreen.value=!o.fullscreen.peek(),ot("fullscreen",o.fullscreen.peek())},toggleCss:()=>{o.blockTab.peek()||o.compTab.peek()||(o.cssMode.peek()?(o.cssMode.value=!1,o.viewMode.value=Ro(Te().viewMode),G.refresh()):(ta(),o.cssMode.value=!0,o.viewMode.value="split"))},formatMarkdown:()=>{const e=F.selectionStart,t=pr(bt());G.unfoldAll(),xe=!0,F.value=t,xe=!1,F.selectionStart=F.selectionEnd=Math.min(e,t.length),F.dispatchEvent(new Event("input")),He.paint(),G.refresh(),E.toast("Markdown formatted.",{type:"success"})},toggleFold:()=>{o.folded.peek()?G.unfoldAll():G.foldAll(),o.folded.value=!o.folded.peek(),He.paint()},setWidth:e=>{o.liveWidth.value=e},refreshPreview:()=>{if(o.compTab.peek()){const e=B.get(o.compTab.peek());e&&Ze(e)}else U(!0)},share:async()=>{if(!u?.isEdit){E.toast("Save the page first - a share link needs a page to point at.",{type:"warning"});return}await rr(u.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 k.versions.bulkDelete(u.id,e),E.toast(`${t} version${t>1?"s":""} deleted.`,{type:"success"}),await le()}catch(a){E.toast("Delete failed: "+a.message,{type:"error"})}},saveNamed:async()=>{if(!u?.isEdit)return;const e=u;await lr(e.id)&&c()&&u===e&&await le()},closeCompare:()=>{o.compare.value=null,Et=null},restoreCompared:()=>la(Et)};Ue.querySelectorAll("[data-tip]").forEach(e=>E.tooltip(e,{content:e.getAttribute("data-tip"),position:"top"}));const Hs=M.applyBindings(Vs,Ma,{methods:bo});l(()=>Hs.dispose()),oe=No(N('.pe-block-edit[data-part="html"]'),{ariaLabel:"Block template HTML",language:"html",onChange:e=>ka("content",e)}),Ge=No(N('.pe-block-edit[data-part="css"]'),{ariaLabel:"Block CSS",language:"css",placeholder:`/* Scoped to this block - e.g.
|
|
121
121
|
color: var(--dm-primary);
|
|
122
|
-
h3 { font-size: 1.25rem; } */`,onChange:e=>
|
|
123
|
-
`).map((l,b)=>{const
|
|
122
|
+
h3 { font-size: 1.25rem; } */`,onChange:e=>ka("css",e)}),l(()=>{oe.destroy(),Ge.destroy()});for(const e of It)ce[e]=No(N(`.pe-comp-edit[data-part="${e}"]`),{ariaLabel:`Component <${e}> block`,language:{template:"html",props:"json",script:"js",style:"css"}[e],onChange:t=>Fs(e,t)});l(()=>{for(const e of It)ce[e].destroy()});const we=N(".pe-tabs-list"),At=e=>e.target.closest(".pe-tab")?.dataset.id||"",Ta=e=>{const t=At(e);t&&(e.target.closest(".pe-tab-close")?Je(t):Mt(t))},Ca=e=>{e.button===1&&At(e)&&(e.preventDefault(),Je(At(e)))},xa=e=>{const t=At(e);!t||e.target.closest(".pe-tab-close")||(e.key==="Enter"||e.key===" "?(e.preventDefault(),Mt(t)):e.key==="Delete"?(e.preventDefault(),Je(t)):e.key==="F10"&&e.shiftKey&&(e.preventDefault(),Se(e.target.closest(".pe-tab"))))};we.addEventListener("click",Ta),we.addEventListener("auxclick",Ca),we.addEventListener("keydown",xa),l(()=>{we.removeEventListener("click",Ta),we.removeEventListener("auxclick",Ca),we.removeEventListener("keydown",xa)});const vo=e=>{for(const t of e)Je(t)},Ks=async e=>{const t=I(e);if(t?.id)if(t.kind==="page")o.active.peek()!==e&&await ie(t.id),c()&&u?.id===t.id&&$a();else if(t.kind==="block"){const s=($.get(t.id)||await oo(t.id,{quiet:!0}))?.form||Ua(await k.blocks.get(t.id).catch(()=>null));c()&&$t({seed:xo(t.id,[...pe]),content:s.content,css:s.css})}else{const s=(B.get(t.id)||await po(t.id,{quiet:!0}))?.form.source??(await k.components.get(t.id).catch(()=>null))?.source??"";c()&&tt({name:xo(t.id,[...se]),source:s})}},Us=e=>{const t=I(e);if(!t||!x.includes(e))return[];const a=x.indexOf(e),s=$e.get(e)?.label.peek()||t.id,r=n=>{x=Vn(x,e,n),V()};return[{header:s},{label:"Close",icon:"x",action:()=>Je(e)},{label:"Close others",icon:"x-circle",disabled:x.length<2,action:()=>vo(Ao(x,e,"others"))},{label:"Close to the right",icon:"chevrons-right",disabled:a>=x.length-1,action:()=>vo(Ao(x,e,"right"))},{label:"Close all",icon:"layers",action:()=>vo(Ao(x,e,"all"))},{type:"divider"},{label:"Move left",icon:"arrow-left",disabled:a===0,action:()=>r(-1)},{label:"Move right",icon:"arrow-right",disabled:a>=x.length-1,action:()=>r(1)},{type:"divider"},{label:t.kind==="page"?"Clone page\u2026":`Duplicate ${t.kind}\u2026`,icon:"copy",disabled:!t.id,action:()=>Ks(e)},{label:"Copy link",icon:"link",disabled:!t.id,action:()=>Le(`${location.origin}${location.pathname}#${Ya(t)}`,"Link")}]};{const e=E.contextMenu(we,{cursor:"glyph",match:".pe-tab",inherit:!1,density:"compact",items:t=>Us(t.dataset.id)});l(()=>e?.destroy?.())}const Pa=N(".pe-ex-scroll"),Oa=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),i=()=>{ge.has(n)?ge.delete(n):ge.add(n);try{S.set(rs,[...ge])}catch{}z()};if(r==="folder"){i();return}if(r==="page"&&e.target.closest(".pe-ex-chev")&&t.classList.contains("is-folder")){i();return}r==="page"?ie(n):r==="block"?Ne(n):r==="component"&&et(n)};Pa.addEventListener("click",Oa),l(()=>Pa.removeEventListener("click",Oa));let go=null;l(()=>clearTimeout(go)),F.addEventListener("input",()=>{G.refresh(),ne.scrollTop=F.scrollTop,!xe&&(clearTimeout(go),go=setTimeout(ea,120),U())}),F.addEventListener("scroll",()=>{ne.scrollTop=F.scrollTop}),ue.addEventListener("scroll",()=>{ne.scrollTop=ue.scrollTop}),l(M.effect(()=>{for(const e of Object.keys(p))e!=="css"&&e!=="cssOpened"&&p[e].value;o.bodyRev.value,U()})),l(M.effect(()=>{o.bodyRev.value,o.docRev.value;const e=u?bt():"";gt=u?Tn(e,mt.map(t=>t.name),mt.filter(t=>t.shortTag).map(t=>t.name)):[],Jo=u?zn(e,se):[],ht(),z()})),l(M.effect(()=>{o.blockRev.value,o.compRev.value,o.docRev.value,o.active.value,o.filter.value,p.title.value,V(),z()})),l(M.effect(()=>{ne.hidden=!!(o.blockTab.value||o.compTab.value)}));let ko=null;l(()=>clearTimeout(ko)),l(M.effect(()=>{const e=p.css.value;o.cssMode.value&&(zt.paint(),kr(ne,e,ue),clearTimeout(ko),ko=setTimeout(Go,400))}));{const e=N(".pe-version-list"),t=E.contextMenu(e,{cursor:"glyph",match:"tr[data-file]",inherit:!1,density:"compact",items:a=>{const s=Gt(a.dataset.file);return s?[{header:es(s)},{label:"Show beside the editor",icon:"columns",action:()=>Xt(s)},{label:"Restore this version\u2026",icon:"rotate-ccw",action:()=>la(s.filename)},{type:"divider"},{label:"Delete\u2026",icon:"trash",danger:!0,action:()=>Os(s)}]:[]}});l(()=>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){Se(r);return}const n=Gt(s.dataset.file);n&&Xt(n)}),e.addEventListener("keydown",a=>{const s=a.target.closest("tr[data-file]");if(s){if(a.key==="F10"&&a.shiftKey)a.preventDefault(),Se(s);else if(a.key==="Enter"&&a.target===s){const r=Gt(s.dataset.file);r&&Xt(r)}}})}hr(N("#editor-body"),N("#editor-divider"),l),en({image:e=>(o.cssMode.value=!1,yr(e)),scribe:e=>an(e),card:e=>Ws.open(e,{onMediaBrowse:()=>k.media.list().catch(()=>[])}),timeline:e=>Ys.open(e,{onPreview:async t=>{try{return await k.pages.preview(t)}catch{return null}},onListCollections:()=>k.collections.list().catch(()=>[]),onGetCollection:t=>k.collections.get(t).catch(()=>null)})}),Jt.onEffects(e=>Qs.open(e)),Jt.onHelp(()=>Ba()),Jt.onTutorial(e=>sn(e));const Ra=e=>{if(!c())return;const t=Gn(e);t&&(e.preventDefault(),t==="save"?to(!1):t==="layout"?bo.setMode(Hn(o.viewMode.peek())):bo.toggleExplorer())};document.addEventListener("keydown",Ra),l(()=>document.removeEventListener("keydown",Ra));const at=h.querySelector(".dm-plugin-banner-name");l(M.effect(()=>{const e=o.curId.value,t=Uo.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":gn(!!e,p.title.value,e);if(!c()||(document.title=`${r} - Domma CMS`,!at))return;const n=at.querySelector(".dm-plugin-banner-parent"),i=at.lastChild;n&&i&&i.nodeType===3&&at.querySelector(".dm-plugin-banner-sep")?(n.textContent=s,i.textContent=r):at.textContent=`${s} \u203A ${r}`})),rt(d,{id:"page-editor-back",icon:"arrow-left",title:"Back to Pages",onClick:()=>R.navigate("/pages")}),rt(d,{id:"page-editor-help",icon:"help-circle",title:"Editor reference",onClick:()=>Ba()});const _s=rt(d,{id:"page-editor-view",icon:"external-link",title:"View on the site",onClick:()=>{u?.isEdit&&window.open(p.urlPath.peek().trim()||u.id,"_blank","noopener")}}),$a=()=>{if(!u?.isEdit||o.saving.peek()||!c())return;const e=u,t=K(),a=Mo({...t,urlPath:e.id});if(a.length){E.toast(a[0].text,{type:"warning"});return}const s=cn({page:e.page,frontmatter:{...Kt(t,e,Ut(t)),adminTags:t.adminTags},body:t.body,source:e.id,pages:D,onCloned:async r=>{c()&&(await ua(),c()&&ie(r))}});l(()=>s.close())},ho=rt(d,{id:"page-editor-clone",icon:"copy",title:"Clone page",onClick:$a});l(M.effect(()=>{const e=_o.value;for(const t of[_s,ho])t&&(t.disabled=!e);ho&&(ho.title=e?"Clone page":"Open a saved page to clone it")})),rt(d,{id:"page-editor-settings",icon:"settings",title:"Editor settings - font, size, spacing",onClick:()=>{Ho=nr(Vo)}}),l(La(Ma,{position:"bottom"})),Domma.icons.scan();let Aa={ids:[],active:""};try{Aa=_n(S.get(ns))}catch{}const st=qa(m),nt=Jn(st,Aa,history.state?.pe===1);x=nt.ids.filter(e=>{const t=I(e);return t.kind==="page"||(t.kind==="component"?se:pe).has(t.id)}),ca=!1;for(const e of x){const t=I(e);t.kind==="page"&&ma(t.id)}V(),z(),ht();for(const e of Oe)oo(e,{quiet:!0}).then(()=>{c()&&(V(),z())});for(const e of x){const t=I(e);t.kind==="component"&&e!==nt.active&&Me(t)&&po(t.id,{quiet:!0}).then(()=>{c()&&(V(),z())})}const Da=st&&!st.id&&st.kind!=="page";nt.active&&x.includes(nt.active)?await Mt(nt.active):x.length?await Mt(x[0]):Da||ee(),Da&&c()&&(await(st.kind==="block"?$t:tt)({project:jt(m)||""}),c()&&ee())}};function Lo(d,m){!d||typeof m!="string"||!m||[...d.options].some(v=>v.value===m)||(d.append(Object.assign(document.createElement("option"),{value:m,textContent:`${m} (custom)`})),d.value=m)}function kr(d,m,v){d.replaceChildren(...String(m).split(`
|
|
123
|
+
`).map((l,b)=>{const y=document.createElement("span");y.className="editor-line-number-row";const w=document.createElement("span");w.className="fold-toggle fold-toggle--empty";const h=document.createElement("span");return h.className="editor-line-num",h.textContent=String(b+1),y.append(w,h),y})),d.scrollTop=v.scrollTop}function hr(d,m,v){if(!d||!m)return;const l=15,b=85,y=c=>Math.min(b,Math.max(l,c));let w=y(parseFloat(Te().splitRatio)||50);const h=c=>{d.style.setProperty("--dm-editor-split",w+"%"),m.setAttribute("aria-valuenow",String(Math.round(w))),c&&dt("splitRatio",w)};h(!1);const C=c=>{const P=d.getBoundingClientRect();P.width&&(w=y((c.clientX-P.left)/P.width*100)),h(!1)},L=()=>{document.removeEventListener("pointermove",C),document.removeEventListener("pointerup",L),d.classList.remove("is-resizing"),m.classList.remove("is-dragging"),h(!0)};v(L),m.addEventListener("pointerdown",c=>{window.matchMedia("(max-width: 768px)").matches||(c.preventDefault(),d.classList.add("is-resizing"),m.classList.add("is-dragging"),document.addEventListener("pointermove",C),document.addEventListener("pointerup",L))}),m.addEventListener("dblclick",()=>{w=50,h(!0)}),m.addEventListener("keydown",c=>{const P=c.shiftKey?10:2;let H=w;if(c.key==="ArrowLeft")H=w-P;else if(c.key==="ArrowRight")H=w+P;else if(c.key==="Home")H=l;else if(c.key==="End")H=b;else if(c.key==="Enter"||c.key===" ")H=50;else return;c.preventDefault(),w=y(H),h(!0)})}async function yr(d){const m=d.selectionStart,v=d.selectionEnd,l=await ln({title:"Upload or insert an image"});if(!l||!d.isConnected)return;let b=l.split("/").pop().split("?")[0];try{b=decodeURIComponent(b)}catch{}const y=b.replace(/[\\[\]]/g,"\\$&"),w=l.replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/ /g,"%20");d.setSelectionRange(m,v),Xs(d,``),d.dispatchEvent(new Event("input",{bubbles:!0}))}function wr(d,m,v){const l=Object.entries(m||{});return d?(d.replaceChildren(...l.map(([b,y])=>new Option(y?.label||b,b))),l.some(([b])=>b===v)?v:l[0]?.[0]||v):v}async function Er(d,m){if(!d)return;let v=[];try{const C=await k.get("/collections/roles/entries?limit=100");v=Array.isArray(C)?C:C?.entries||[]}catch{}const l=v.map(C=>({name:C.data?.name,label:C.data?.label||C.data?.name,level:C.data?.level??99})).filter(C=>C.name&&C.level!==0).sort((C,L)=>L.level-C.level),b=(C,L)=>Object.assign(document.createElement("option"),{value:C,textContent:L}),y=()=>{typeof m=="string"&&m&&![...d.options].some(C=>C.value===m)&&d.append(b(m,`${m} (custom)`))};if(!l.length)return y();const w=document.createElement("optgroup");w.label="Role and above",l.forEach(C=>w.append(b(C.name,`${C.label} and above`)));const h=document.createElement("optgroup");h.label="Role only",l.forEach(C=>h.append(b(`=${C.name}`,`${C.label} only`))),d.replaceChildren(b("public","Public - everyone"),w,h,b("private","Private - super-admin only")),y()}
|
package/docs/cache.md
CHANGED
|
@@ -94,6 +94,10 @@ Before the response cache is asked, the route decides whether the viewer may see
|
|
|
94
94
|
|
|
95
95
|
`services/htmlResponse.js` `sendHtml()`: a page that came from the response cache (`{cached: true}` - the page route and the docs route) is also kept compressed, one entry per encoding under `enc:<br|gzip>:<sha1 of the HTML>`. Content-addressed, so never stale and never invalidated; bounded by the cache's LRU and TTL. The negotiation and parameters match @fastify/compress (brotli quality 4), which steps aside when `content-encoding` is already set. A 404, a draft or a per-viewer page is not kept and goes out through @fastify/compress as before.
|
|
96
96
|
|
|
97
|
+
## Static assets
|
|
98
|
+
|
|
99
|
+
`services/assetCache.js`: an `onSend` hook added before @fastify/compress's. When @fastify/static has answered a GET with a 200 for a compressible type (its stream carries the file's path), the hook swaps the stream for a buffer kept in memory - the bytes as sent, per encoding (`br`, `gzip`, `identity`), keyed on the path and checked against the file's mtime and size on every request. @fastify/static still resolves the path, sets the type, cache-control, ETag and Last-Modified and answers 304s, HEADs and 404s. Files under 1 KB or over 2 MB, Range requests and non-text types are left to stream. Bounded at 64 MB (LRU by bytes); on while the response cache is on; emptied on a clear or re-init. `GET /api/cache/status` reports it as `assets`.
|
|
100
|
+
|
|
97
101
|
## Thundering-herd safety
|
|
98
102
|
|
|
99
103
|
If three concurrent requests hit a cold key, the producer runs exactly once - the other two await the first's result via an in-flight promise map. This matters for popular pages on cache cold-start: without coalescing, N parallel requests trigger N parallel renders, each writing the same value back to the cache.
|