domma-cms 1.8.22 → 1.8.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/admin/css/admin.css +1 -1
- package/admin/js/app.js +1 -1
- package/admin/js/lib/editor-hop-arrange.js +1 -1
- package/admin/js/lib/editor-hop.js +1 -19
- package/admin/js/lib/editor-workspace-arrange.js +1 -0
- package/admin/js/lib/page-blocks-arrange.js +1 -1
- package/admin/js/lib/page-editor-arrange.js +1 -1
- package/admin/js/lib/plain-editor.js +6 -0
- package/admin/js/lib/sidebar-size.js +1 -1
- package/admin/js/lib/syntax-highlight.js +1 -1
- package/admin/js/templates/docs/components-howto.html +2 -2
- package/admin/js/templates/docs/components-walkthrough.html +1 -1
- package/admin/js/templates/docs/usage-blocks.html +19 -15
- package/admin/js/templates/page-editor.html +533 -297
- package/admin/js/views/index.js +1 -1
- package/admin/js/views/page-editor.js +36 -9
- package/docs/DommaFeatures.md +8 -8
- package/docs/configuration.md +13 -12
- package/docs/markdown-shortcodes.md +12 -7
- package/package.json +1 -1
- package/admin/js/templates/block-editor.html +0 -251
- package/admin/js/templates/component-editor.html +0 -150
- package/admin/js/views/block-editor.js +0 -14
- package/admin/js/views/component-editor.js +0 -2
package/admin/js/views/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dashboardView as o}from"./dashboard.js";import{dashboardsView as i}from"./dashboards.js";import{pagesView as r}from"./pages.js";import{pageEditorView as
|
|
1
|
+
import{dashboardView as o}from"./dashboard.js";import{dashboardsView as i}from"./dashboards.js";import{pagesView as r}from"./pages.js";import{pageEditorView as e}from"./page-editor.js";import{settingsView as t}from"./settings.js";import{themeView as m}from"./theme.js";import{notificationsView as p}from"./notifications.js";import{layoutsView as s}from"./layouts.js";import{mediaView as n}from"./media.js";import{loginView as f}from"./login.js";import{usersView as w}from"./users.js";import{userEditorView as c}from"./user-editor.js";import{pluginsView as V}from"./plugins.js";import{pluginCodeView as a}from"./plugin-code.js";import{documentationView as d}from"./documentation.js";import{tutorialsView as l}from"./tutorials.js";import{docViews as u}from"./doc-pages.js";import{pluginGuidesView as E,pluginGuideView as g}from"./plugin-guides.js";import{apiReferenceView as h}from"./api-reference.js";import{collectionsView as v}from"./collections.js";import{collectionEditorView as b}from"./collection-editor.js";import{collectionEntriesView as y}from"./collection-entries.js";import{formsView as j}from"./forms.js";import{formEditorView as L}from"./form-editor.js";import{formSubmissionsView as x}from"./form-submissions.js";import{viewsListView as M}from"./views-list.js";import{viewEditorView as k}from"./view-editor.js";import{viewPreviewView as D}from"./view-preview.js";import{actionsListView as G}from"./actions-list.js";import{actionEditorView as P}from"./action-editor.js";import{proDocsView as R}from"./pro-docs.js";import{blocksView as S}from"./blocks.js";import{componentsView as A}from"./components.js";import{myProfileView as C}from"./my-profile.js";import{rolesView as I}from"./roles.js";import{roleEditorView as N}from"./role-editor.js";import{effectsView as T}from"./effects.js";import{searchView as U}from"./search.js";import{contentApiView as q}from"./content-api.js";import{notesView as z}from"./notes.js";import{todoView as B}from"./todo.js";import{calculatorView as F}from"../lib/calculator.js";import{analyticsView as H}from"./analytics.js";import{seoView as J}from"./seo.js";import{wordpressImportView as K}from"./wordpress-import.js";import{contactsView as O}from"./contacts.js";import{callNewView as Q}from"./call-new.js";import{menusView as W}from"./menus.js";import{contextMenusView as X}from"./context-menus.js";import{contextMenuEditorView as Y}from"./context-menu-editor.js";import{menuEditorView as Z}from"./menu-editor.js";import{menuLocationsView as _}from"./menu-locations.js";import{projectsView as $}from"./projects.js";import{projectDetailView as oo}from"./project-detail.js";import{projectSettingsView as io}from"./project-settings.js";import{apiTokensView as ro}from"./api-tokens.js";import{apiEndpointsView as eo}from"./api-endpoints.js";import{apiEndpointEditorView as to}from"./api-endpoint-editor.js";const mo={templateUrl:"",async onMount(){location.hash="#/menus"}};export const views={projects:$,projectDetail:oo,projectSettings:io,apiTokens:ro,apiEndpoints:eo,apiEndpointEditor:to,dashboard:o,dashboards:i,pages:r,pageEditor:e,settings:t,theme:m,layouts:s,media:n,login:f,users:w,userEditor:c,plugins:V,pluginCode:a,documentation:d,tutorials:l,apiReference:h,collections:v,collectionEditor:b,collectionEntries:y,forms:j,formEditor:L,formSubmissions:x,viewsList:M,viewEditor:k,viewPreview:D,actionsList:G,actionEditor:P,proDocs:R,blocks:S,components:A,myProfile:C,roles:I,roleEditor:N,effects:T,search:U,contentApi:q,notes:z,todo:B,calculator:F,analytics:H,seo:J,wordpressImport:K,contacts:O,callNew:Q,notifications:p,menus:W,contextMenus:X,contextMenuEditor:Y,menuEditor:Z,menuLocations:_,menusRedirect:mo,pluginGuides:E,pluginGuide:g,...u};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{api as
|
|
1
|
+
import{api as k}from"../api.js";import{CardBuilder as Us}from"../lib/card-builder.js";import{TimelineBuilder as _s}from"../lib/timeline-builder.js";import{attachEditorKeybindings as Ws,createToolbar as Ys,insertAtCursor as Js}from"../lib/markdown-toolbar.js";import{EffectsBuilder as Gs}from"../lib/effects-builder.js";import{attachContextMenu as Xs}from"../lib/shortcode-context-menu.js";import{registerCustomInserters as Qs}from"../lib/shortcode-insert.js";import{populateThemeSelect as Zs}from"../lib/themes.js";import{createFoldingManager as en}from"../lib/editor-folding.js";import{openScribeComposer as tn}from"../lib/scribe-composer.js";import{openCrudTutorial as on}from"../lib/crud-tutorial.js";import{addBannerAction as rt}from"../lib/plugin-chrome.js";import{helpPopovers as Da}from"../lib/help-popover.js";import{onceClosed as Lt,openMenuAt as Me,sidebarCount as lt}from"../lib/tool-kit.js";import{pickMedia as an}from"../lib/media-picker.js";import{openPageClone as sn}from"../lib/page-clone.js";import{openEditorReference as La}from"../lib/page-editor-reference.js";import{cleanType as Nt,DEFAULT_TYPE as Bt,EDITOR_FONTS as nn,fontHref as rn,isDefaultType as ln,LINE_HEIGHTS as cn,SIZES as dn,typeVars as Na}from"../lib/editor-type.js";import{getProjectFromHash as jt}from"../lib/project-context.js";import{attachSmoothCaret as Ba,getCaretPreference as pn,setCaretPreference as un}from"../lib/smooth-caret.js";import{SCREEN as Eo,bannerSection as fn,draftIsStale as mn,draftKey as je,initialProject as bn,makeDraft as ja,problems as Mo,readDraft as vn,snapshot as Y}from"../lib/page-editor-arrange.js";import{fmtDateTime as Ie}from"../lib/dates.js";import{hasDraft as Te,heldBlockEdits as gn,hold as To,takeHeld as So,wireHeldRouter as kn}from"../lib/editor-hop.js";import{hopTarget as Ia}from"../lib/editor-hop-arrange.js";import{checkNewName as yn,copyName as Co,usageText as hn}from"../lib/blocks-arrange.js";import{isCollapsed as Fa,setCollapsed as qa}from"../lib/sidebar-size.js";import{blockTagDefs as za,blocksInBody as wn}from"../lib/block-tags.js";import{draftKey as Fe,draftOffer as En,extractPlaceholders as Va,formatHtml as Mn,formFromBlock as Ha,payloadFromForm as Ka,rawPlaceholders as xo,shortcodeFor as Tn,snapshot as Po}from"../lib/block-editor-arrange.js";import{blockTabs as Sn,notHereLine as Cn,otherUses as Ua,outlineBlock as xn,previewBlocks as Pn,saveQuestion as On,savedMessage as Rn,usesLine as An}from"../lib/page-blocks-arrange.js";import{addressFor as _a,blockAloneBody as $n,blockRows as Dn,cleanLayout as Oo,closeTab as Ln,componentRows as Nn,componentStatusText as Bn,componentsInBody as jn,moveTab as In,nextLayout as Fn,openTab as Ro,pageRows as qn,pageStatusText as zn,rememberedTabs as Vn,renameTab as Hn,sampleRows as Wa,saveAllLabel as Kn,startingTabs as Un,tabId as Q,tabItem as I,tabsToClose as Ao,workspaceKey as _n}from"../lib/editor-workspace-arrange.js";import{PANES as It,STARTER_SOURCE as Wn,assembleSource as Ya,attributeFor as Yn,draftKey as qe,draftStatus as Jn,nameProblem as Gn,previewRows as Ja,readValue as Xn,snapshot as $o,splitSource as it,writeValue as Ga}from"../lib/component-editor-arrange.js";import{attachColourLayer as Xa,createPlainEditor as Do}from"../lib/plain-editor.js";function ct(d){const u=document.createElement("template");return u.innerHTML=d.trim(),u.content.firstElementChild}const Qn=Ie,Qa=d=>Ie(d.createdAt)+(d.label?` - ${d.label}`:""),Zn=`
|
|
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 g}from"../api.js";import{CardBuilder as ba}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 Za(d){const u=rn(d);if(!u||document.querySelector(`link[data-pe-font="${CSS.escape(d)}"]`))return;const v=Object.assign(document.createElement("link"),{rel:"stylesheet",href:u});v.dataset.peFont=d,document.head.appendChild(v)}const er=`
|
|
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>
|
|
@@ -43,6 +43,8 @@ import{api as g}from"../api.js";import{CardBuilder as ba}from"../lib/card-builde
|
|
|
43
43
|
</div>
|
|
44
44
|
<label class="pe-settings-switch"><input type="checkbox" class="form-switch" data-model="wrap.value"> Wrap long lines
|
|
45
45
|
<span data-help="Off: a long line runs on and the editor scrolls sideways - each line number is then exactly one line."></span></label>
|
|
46
|
+
<label class="pe-settings-switch"><input type="checkbox" class="form-switch" data-model="sidebar.value"> Close the admin menu when the editor opens
|
|
47
|
+
<span data-help="The left-hand menu folds to its icon rail whenever the editor opens, for the room; it comes back when you leave the editor. The rail's own button still opens it meanwhile."></span></label>
|
|
46
48
|
<label class="form-label" for="pe-set-caret">Caret movement</label>
|
|
47
49
|
<select id="pe-set-caret" class="form-select" data-model="caret.value">
|
|
48
50
|
<option value="auto">Automatic - follow system motion preference</option>
|
|
@@ -68,17 +70,41 @@ The quick brown fox - 0O 1lI {} [] () => ==
|
|
|
68
70
|
.pe-settings-sample { white-space: pre-wrap; padding: .75rem 1rem; border-radius: var(--dm-radius-md); border: 1px solid var(--dm-border);
|
|
69
71
|
background: var(--dm-background-alt); font-family: var(--pe-font); font-size: var(--pe-size); line-height: var(--pe-line); }
|
|
70
72
|
.pe-settings-actions { display: flex; gap: .5rem; border-top: 1px solid var(--dm-border); padding-top: .75rem; }
|
|
71
|
-
</style>`;function
|
|
73
|
+
</style>`;function tr(d){const u=E.slideover({title:"Editor settings",size:"sm",position:"right"}),v=ct(`<div>${er}</div>`),l=Nt(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(pn()),sidebar:M.observable(Se().closeSidebar===!0)},h=M.effect(()=>{const L=b.sidebar.value;L!==(Se().closeSidebar===!0)&&dt("closeSidebar",L)}),w=M.computed(()=>Nt({font:b.font.value,size:b.size.value,line:b.line.value,wrap:b.wrap.value}));b.isDefault=M.computed(()=>ln(w.value)&&b.caret.value==="auto"&&!b.sidebar.value);const y=M.effect(()=>un(b.caret.value)),C=v.querySelector("#pe-set-font");for(const[L,$]of[["mono","Monospace"],["sans","Other"],["serif",null]]){const J=nn.filter(Ce=>Ce.kind===L),ut=$?Object.assign(document.createElement("optgroup"),{label:$}):C.querySelector("optgroup:last-of-type");ut.append(...J.map(Ce=>new Option(Ce.label,Ce.id))),$&&C.appendChild(ut)}v.querySelector("#pe-set-size").append(...dn.map(L=>new Option(`${L}px`,String(L)))),v.querySelector("#pe-set-line").append(...cn.map(L=>new Option({1.3:"Tight",1.45:"Snug",1.6:"Normal",1.8:"Relaxed",2:"Double"}[L]||String(L),String(L))));const N=v.querySelector(".pe-settings-sample"),c=M.effect(()=>{const L=w.value;Za(L.font);for(const[$,J]of Object.entries(Na(L)))N.style.setProperty($,J);N.style.whiteSpace=L.wrap?"pre-wrap":"pre",N.style.overflowX=L.wrap?"":"auto",JSON.stringify(L)!==JSON.stringify(Nt(d.peek()))&&(d.value=L,dt("type",L))});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=()=>u.close(),u.element.appendChild(v);const P=M.applyBindings(b,v),H=Da(v);return Lt(u,()=>{P.dispose(),H(),c?.(),y?.(),h?.()}),Domma.icons?.scan?.(v),u.open(),u}async function or(d){const u=E.slideover({title:"Share preview link",size:"md",position:"right"}),v=ct(`<div>${Zn}</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(y=>!y.revokedAt&&!y.expired).map(y=>({id:y.id,title:y.label||"Untitled link",meta:`Expires ${Qn(y.expiresAt)} - ${y.useCount===1?"1 view":`${y.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(y){E.toast("Could not revoke: "+(y.message||"unknown error"),{type:"error"})}};const h=M.applyBindings(l,v);Lt(u,()=>h.dispose()),u.element.appendChild(v),u.open(),await b()}function ar(d){return new Promise(u=>{const v=E.slideover({title:"Save Named Version",size:"sm",position:"right"}),l=ct(`
|
|
72
74
|
<div style="padding:1rem">
|
|
73
75
|
<label class="form-label" for="pe-version-label">Version Label</label>
|
|
74
76
|
<input id="pe-version-label" type="text" class="form-input" style="margin-bottom:.75rem"
|
|
75
77
|
placeholder="e.g. Initial design, Before restructure" data-model="label.value" data-on-keydown="key">
|
|
76
78
|
<button type="button" class="btn btn-primary" data-on-click="save" data-bind-disabled="saving.value">Save Version</button>
|
|
77
|
-
</div>`);let
|
|
78
|
-
|
|
79
|
+
</div>`);let b=!1;const h={label:M.observable(""),saving:M.observable(!1)};h.save=async()=>{h.saving.value=!0;try{await k.versions.create(d,h.label.peek().trim()||null),E.toast("Named version saved.",{type:"success"}),b=!0,v.close()}catch(y){E.toast("Failed to save version: "+y.message,{type:"error"})}finally{h.saving.value=!1}},h.key=y=>{y.key==="Enter"&&(y.preventDefault(),h.save())};const w=M.applyBindings(h,l);Lt(v,()=>{w.dispose(),u(b)}),v.element.appendChild(l),v.open(),setTimeout(()=>l.querySelector("input")?.focus(),100)})}const sr=`<div class="card">
|
|
80
|
+
<h3>{{title}}</h3>
|
|
81
|
+
<p>{{description}}</p>
|
|
82
|
+
</div>
|
|
83
|
+
`,nr=`
|
|
84
|
+
<div class="pe-new">
|
|
85
|
+
<p class="form-hint" style="margin-top:0" data-bind-text="hint.value"></p>
|
|
86
|
+
<label class="form-label" for="pe-new-name">Name</label>
|
|
87
|
+
<input id="pe-new-name" type="text" class="form-input" maxlength="64" autocomplete="off" spellcheck="false" data-model="name.value"
|
|
88
|
+
data-bind-placeholder="placeholder.value" data-on-keydown="key">
|
|
89
|
+
<small class="pe-new-err" hidden data-bind-hidden="!error.value" data-bind-text="error.value"></small>
|
|
90
|
+
<label class="form-label" for="pe-new-project" style="margin-top:.75rem">Project</label>
|
|
91
|
+
<select id="pe-new-project" class="form-select" data-model="project.value"><option value="">- none -</option></select>
|
|
92
|
+
<div class="pe-new-actions">
|
|
93
|
+
<button type="button" class="btn btn-ghost" data-on-click="cancel">Cancel</button>
|
|
94
|
+
<button type="button" class="btn btn-primary" data-on-click="create" data-bind-disabled="!canCreate.value"
|
|
95
|
+
data-bind-text="busy.value ? 'Creating\u2026' : 'Create'">Create</button>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
<style>
|
|
99
|
+
.pe-new { padding: 1rem; }
|
|
100
|
+
.pe-new [hidden] { display: none !important; }
|
|
101
|
+
.pe-new-err { display: block; margin-top: .3rem; color: var(--dm-danger); font-size: var(--dm-text-xs); }
|
|
102
|
+
.pe-new-actions { display: flex; justify-content: flex-end; gap: .5rem; margin-top: 1.25rem; }
|
|
103
|
+
</style>`;function es({kind:d,projects:u,taken:v,project:l="",name:b="",check:h,create:w,register:y=()=>{}}){return new Promise(C=>{const N=E.slideover({title:d==="block"?"New block":"New component",size:"sm",position:"right"});y(()=>{try{N.close()}catch{}});const c=ct(`<div>${nr}</div>`);for(const $ of u)c.querySelector("#pe-new-project").append(new Option($.name||$.slug,$.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()?h(P.name.value.trim(),v):""),P.canCreate=M.computed(()=>!P.busy.value&&!!P.name.value.trim()&&!h(P.name.value.trim(),v));let H="";P.create=async()=>{const $=P.name.peek().trim();if(!(!$||P.error.peek()||P.busy.peek())){P.busy.value=!0;try{await w($,P.project.peek()),H=$,N.close()}catch(J){E.toast(`Not created: ${J.message||J}`,{type:"error"})}finally{P.busy.value=!1}}},P.cancel=()=>N.close(),P.key=$=>{$.key==="Enter"&&($.preventDefault(),P.create())};const L=M.applyBindings(P,c);Lt(N,()=>{L.dispose(),C(H)}),N.element.appendChild(c),c.querySelector("#pe-new-project").value=l,N.open(),setTimeout(()=>{const $=c.querySelector("#pe-new-name");$?.focus(),$?.select()},100)})}let q=null,ts=!1;function rr(){ts||!window.R?.use||(ts=!0,kn(),R.use((d,u,v)=>{if(q&&!q.alive()){const h=q;q=null,h.dispose()}const l=u?.path||"",b=d?.path||"";if(!q||!Eo.test(l))return v();if(Eo.test(b)){const h=Ia(b);if(h){q.open(h,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(h=>{h?(q?.keepDraft(),v()):q&&history.replaceState({pe:1},"","#"+q.address)}):v()}),M.subscribe("router:afterChange",({to:d}={})=>{if(q&&!Eo.test(d?.path||"")){const u=q;q=null,u.dispose()}}))}function lr(d){return d.split(`
|
|
104
|
+
`).map(u=>u.trimEnd()).join(`
|
|
79
105
|
`).replace(/\n{3,}/g,`
|
|
80
106
|
|
|
81
|
-
`)}const jt="editor_prefs";function ae(){return S.get(jt)||{viewMode:"split",fullscreen:!1}}function he(r,d){const p=ae();p[r]=d,S.set(jt,p)}const bo=["edit","details","seo","dconfig","live","history"],Vt=["split","write","preview"],mo=r=>r.replace(/<\/head>/i,"<style>body.dm-cloaked{opacity:1 !important;transition:none !important}</style></head>");function qt(r,d,p=()=>{}){let o=0;try{o=r.contentWindow.scrollY||0}catch{}r.addEventListener("load",()=>{try{r.contentWindow.scrollTo(0,o)}catch{}p()},{once:!0}),r.srcdoc=d}export const pageEditorView={templateUrl:"/admin/js/templates/page-editor.html",async onMount(r){if(uo(),T){const e=T;T=null,e.dispose()}const d=window.location.hash.replace(/^#/,""),p=[],o=e=>(p.push(e),e);let c=()=>!1,m=()=>{},f=()=>{};const b=window.location.hash.match(/#\/pages\/edit(\/[^?]*)/),l=b?b[1]:null,h=!!l,y={path:d,item:{kind:"page",id:l||""},dirty:()=>c(),keepDraft:()=>m(),hold:()=>f(),dispose:()=>{p.splice(0).forEach(e=>{try{e()}catch{}})}};T=y;const C=r.get(0),u=()=>T===y&&C.isConnected,I=E.loader(C,{type:"dots"}),[Y,H,v,Je,Ye,Ht,Ut,oe]=await Promise.all([g.layouts.get().catch(()=>({})),g.settings.get().catch(()=>({})),h?g.pages.get(l).catch(()=>null):null,g.pages.list().catch(()=>[]),g.projects.list().catch(()=>[]),g.pages.tags().catch(()=>[]),g.listTags.list("pages").then(e=>e.map(t=>t.name)).catch(()=>[]),g.get("/blocks?usage=1").catch(()=>g.blocks.list()).catch(()=>[])]);if(!u()){I.destroy();return}if(I.destroy(),h&&!v){E.toast("Page not found.",{type:"error"}),R.navigate("/pages");return}const k=e=>C.querySelector(e);if(Ea(k("#field-theme"),{includeDefault:!0}),await ko(k("#field-visibility"),v?.visibility),!u())return;const Kt=yo(k("#field-layout"),Y,v?.layout||"default");for(const e of Ye)k("#field-project").append(new Option(e.name||e.slug,e.slug));const ye=Wa(oe),se=ae(),n={urlPath:M.observable(l||""),title:M.observable(v?.title||""),description:M.observable(v?.description||""),layout:M.observable(Kt),status:M.observable(v?.status||"draft"),sortOrder:M.observable(String(v?.sortOrder??99)),showInNav:M.observable(!!v?.showInNav),sidebar:M.observable(!!v?.sidebar),breadcrumbs:M.observable(v?v.breadcrumbs!==!1:!0),bundled:M.observable(!!v?.bundled),tags:M.observable(Array.isArray(v?.tags)?v.tags:[]),adminTags:M.observable(Array.isArray(v?.adminTags)?v.adminTags:[]),category:M.observable(v?.category||""),visibility:M.observable(typeof v?.visibility=="string"&&v.visibility?v.visibility:"public"),project:M.observable(ja({isEdit:h,page:v,projects:Ye,fromHash:Oa()||"",urlPath:l||""})),theme:M.observable(v?.theme||""),seoTitle:M.observable(v?.seo?.title||""),seoDesc:M.observable(v?.seo?.description||""),dconfig:M.observable(v?.dconfig?JSON.stringify(v.dconfig,null,2):""),css:M.observable(""),cssOpened:M.observable(!1)},a={bodyRev:M.observable(0),base:M.observable(""),saving:M.observable(!1),tab:M.observable("edit"),viewMode:M.observable(Vt.includes(se.viewMode)?se.viewMode:"split"),fullscreen:M.observable(!!se.fullscreen),cssMode:M.observable(!1),folded:M.observable(!1),liveStatus: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(""),blockTab:M.observable(""),blockPart:M.observable("html"),blockReady:M.observable(!1),blockNote:M.observable(""),blockUses:M.observable(""),blockTabs:M.observable([]),blockRev:M.observable(0)},x=k("#markdown-editor"),U=k("#css-editor");x.value=v?.content||"";const _t=Rt(x),Wt=Rt(U);o(()=>_t.destroy()),o(()=>Wt.destroy());const F=document.createElement("div");F.className="editor-line-numbers editor-line-numbers--foldable",x.parentElement.insertBefore(F,x);const B=Ma(x,F,{onFoldChange:()=>{}});let K=!1;B.setGuard(e=>{K=e});const _=()=>B.getUnfoldedContent(),Ge=M.observable(me(se.type));o(M.effect(()=>{const e=Ge.value,t=k("#editor-body");if(t){Ft(e.font);for(const[s,i]of Object.entries(Lt(e)))t.style.setProperty(s,i);t.classList.toggle("pe-nowrap",!e.wrap),x.wrap=e.wrap?"soft":"off",U.wrap=e.wrap?"soft":"off"}}));let Xe=null;o(()=>{try{Xe?.close()}catch{}});const z=()=>(a.bodyRev.value,{urlPath:n.urlPath.value.trim(),title:n.title.value,description:n.description.value,layout:n.layout.value,status:n.status.value,sortOrder:n.sortOrder.value,showInNav:n.showInNav.value,sidebar:n.sidebar.value,breadcrumbs:n.breadcrumbs.value,bundled:n.bundled.value,tags:n.tags.value,adminTags:n.adminTags.value,category:n.category.value,visibility:n.visibility.value,project:n.project.value,theme:n.theme.value,seoTitle:n.seoTitle.value,seoDesc:n.seoDesc.value,dconfig:n.dconfig.value,body:_(),css:n.cssOpened.value?n.css.value:null}),P=new Map,ne=e=>We(e.form)!==e.base,W=()=>[...P.entries()].filter(([,e])=>ne(e)).map(([e])=>e),Jt=M.computed(()=>q(z())!==a.base.value),Yt=M.computed(()=>(a.blockRev.value,q(z())!==a.base.value||W().length>0)),Gt=M.computed(()=>_e({urlPath:n.urlPath.value,dconfig:n.dconfig.value})[0]?.text||""),Xt=M.computed(()=>a.versions.value.some(e=>e.checked.value)),Qt=M.computed(()=>a.versions.value.length>0&&a.versions.value.every(e=>e.checked.value)),Zt=M.computed(()=>{const e=a.versions.value.filter(t=>t.checked.value).length;return e>0&&e<a.versions.value.length}),ke=()=>q(z())!==a.base.peek();c=()=>ke()||W().length>0;let Qe=JSON.stringify(n.adminTags.peek());const re=e=>({title:n.title.peek().trim()||"Untitled",description:n.description.peek().trim(),layout:n.layout.peek(),status:n.status.peek(),sortOrder:parseInt(String(n.sortOrder.peek()),10)||99,showInNav:n.showInNav.peek(),sidebar:n.sidebar.peek(),...n.breadcrumbs.peek()?{}:{breadcrumbs:!1},...n.bundled.peek()?{bundled:!0}:{},tags:n.tags.peek(),...h&&JSON.stringify(n.adminTags.peek())===Qe?{}:{adminTags:n.adminTags.peek()},category:n.category.peek().trim()||null,visibility:n.visibility.peek()||"public",project:n.project.peek()||"",seo:{title:n.seoTitle.peek().trim(),description:n.seoDesc.peek().trim()},dconfig:e,...n.theme.peek()?{theme:n.theme.peek()}:{}}),we=()=>{const e=n.dconfig.peek().trim();if(!e)return null;try{return JSON.parse(e)}catch{return null}},Ze=()=>n.urlPath.peek().trim()||l||"/";let Ee=[];const et=Array.isArray(H?.footer?.blocks)?H.footer.blocks.filter(e=>typeof e=="string"):[],Me=Array.isArray(oe)?oe:[],tt=new Map(Me.map(e=>[e.name,e.updatedAt])),at=new Map(Me.map(e=>[e.name,e.usage]));let G=new Set;const Se=()=>{const e=[...new Set([...Ee,...et,...P.keys()])],t=Ha(e.filter(i=>!P.has(i)),tt);G=new Set(Object.keys(t));const s=G.size?`Preview shows unsaved changes to ${[...G].join(", ")}`:"";return s!==a.heldNote.peek()&&(a.heldNote.value=s),to(t,[...P.entries()].map(([i,w])=>({name:i,content:w.form.content,css:w.form.css,dirty:ne(w)})))};let Te="";const ea=()=>{st();const e=Te;if(e){Te="";try{X.contentDocument?.querySelector(`[data-block="${CSS.escape(e)}"]`)?.scrollIntoView({block:"center"})}catch{}}},X=k("#markdown-preview");let ot="";g.settings.getCustomCss().then(({css:e})=>{ot=e||""}).catch(()=>{});const st=()=>{const e=X?.contentDocument;if(!e?.head)return;const t=n.cssOpened.peek()?n.css.peek():"";if(!t)return;let s=e.getElementById("preview-custom-css");s||(s=e.createElement("style"),s.id="preview-custom-css",e.head.appendChild(s)),s.textContent=t};let Ce=null;const O=()=>{clearTimeout(Ce),Ce=setTimeout(async()=>{if(!(!X||a.viewMode.peek()==="write"))try{const{html:e}=await g.pages.previewFull(Ze(),re(we()),_(),Se());u()&&qt(X,eo(mo(e),a.blockTab.peek()),ea)}catch(e){X.srcdoc='<!doctype html><meta charset="utf-8"><body style="font:14px system-ui;padding:1rem;color:#b91c1c;">Preview failed: '+String(e?.message||"unknown error").replace(/[<&]/g,t=>t==="<"?"<":"&")+"</body>"}},500)},nt=k("#live-preview-frame");let xe=!0,Pe=null,Ae=!1;const Le=async()=>{if(!(Ae||!nt)){Ae=!0,a.liveStatus.value="Rendering\u2026";try{const{html:e}=await g.pages.previewFull(Ze(),re(we()),_(),Se());if(!u())return;qt(nt,e),xe=!1,a.liveStatus.value="Showing unsaved changes"}catch(e){a.liveStatus.value="Preview failed - "+(e?.message||"unknown error")}finally{Ae=!1}}},Q=()=>{xe=!0,a.tab.peek()==="live"&&(clearTimeout(Pe),Pe=setTimeout(Le,600))};o(()=>{clearTimeout(Ce),clearTimeout(Pe)});const Re=ga(r.find("#markdown-editor"),r.find("#editor-toolbar"),{spacerDefault:H?.layoutOptions?.spacerSize??40});ka(x,{blocks:()=>ye,openBlock:e=>yt(e)});const le=C.querySelector(".editor-toolbar-right");le.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}')`)}),k("#fullscreen-btn")?.setAttribute("data-on-click","toggleFullscreen");const ta=ge(`
|
|
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(`
|
|
82
108
|
<span class="pe-toolbar-extras" style="display:contents">
|
|
83
109
|
<button type="button" class="editor-view-btn" data-tip="Edit Custom CSS" data-on-click="toggleCss"
|
|
84
110
|
data-bind-class="cssMode.value && 'active'"><span>CSS</span></button>
|
|
@@ -90,7 +116,8 @@ The quick brown fox - 0O 1lI {} [] () => ==
|
|
|
90
116
|
<span class="pe-fold-icon" hidden data-bind-hidden="!folded.value"><span data-icon="chevrons-right" data-icon-size="10"></span></span>
|
|
91
117
|
</button>
|
|
92
118
|
<span class="editor-toolbar-sep"></span>
|
|
93
|
-
</span>`);le.prepend(ta),le.before(k(".pe-block-bar"));const rt=e=>(Array.isArray(e)?e:[]).map(t=>({value:t,label:t})),lt=k("#field-tags");let ie=null;ie=lt?E.pillbox(lt,{data:rt(Ht),value:n.tags.peek(),creatable:!0,searchable:!0,placeholder:"Add tag\u2026",onChange:()=>{ie&&(n.tags.value=[...ie.getValue()])}}):null;const it=k("#field-admin-tags");let ce=null;ce=it?E.pillbox(it,{data:rt(Ut),value:n.adminTags.peek(),creatable:!0,searchable:!0,placeholder:"Add admin tag\u2026",onChange:()=>{ce&&(n.adminTags.value=[...ce.getValue()])}}):null;let De=!1,Be=null;const de=async()=>{De=!0,a.histLoading.value=!0,a.histError.value="";try{const e=(await g.versions.list(l)).versions||[];if(!u())return;a.versions.value=e.map(t=>({...t,when:ee(t.createdAt),author:t.author||"",label:t.label||"",checked:M.observable(!1)}))}catch(e){a.histError.value="Failed to load versions: "+e.message}finally{a.histLoading.value=!1}},Oe=e=>a.versions.peek().find(t=>t.filename===e),Ne=async e=>{try{const t=await g.versions.get(l,e.filename);Be=e.filename,a.compare.value={label:It(e),old:t.content,current:_()}}catch(t){E.toast("Failed to load version: "+t.message,{type:"error"})}},ct=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 g.versions.restore(l,e),E.toast("Page restored. Reloading editor\u2026",{type:"success"}),R.navigate("/pages"),setTimeout(()=>R.navigate(`/pages/edit${l}`),300)}catch(t){E.toast("Restore failed: "+t.message,{type:"error"})}},aa=async e=>{if(await E.confirm("Delete this version? This cannot be undone."))try{await g.versions.delete(l,e.filename),E.toast("Version deleted.",{type:"success"}),await de()}catch(t){E.toast("Failed to delete: "+t.message,{type:"error"})}},dt=()=>{a.bodyRev.value=a.bodyRev.peek()+1},pt=()=>{n.cssOpened.peek()||(n.css.value=ot,n.cssOpened.value=!0,va(U))},$e=e=>{for(const t of["urlPath","title","description","layout","status","sortOrder","category","visibility","project","theme","seoTitle","seoDesc","dconfig"])n[t].value=e[t]==null?"":String(e[t]);for(const t of["showInNav","sidebar","breadcrumbs","bundled"])n[t].value=!!e[t];n.tags.value=Array.isArray(e.tags)?e.tags:[],ie?.setValue(n.tags.peek()),Array.isArray(e.adminTags)&&(n.adminTags.value=e.adminTags),ce?.setValue(n.adminTags.peek()),B.unfoldAll(),K=!0,x.value=e.body==null?"":String(e.body),K=!1,B.refresh(),e.css!=null&&(pt(),n.css.value=String(e.css)),dt()};a.base.value=q(z());const Ie=za(l||""),pe=e=>{try{S.remove?S.remove(e):S.set(e,null)}catch{}};m=()=>{try{ke()&&S.set(Ie,Va(z(),a.base.peek()));const e=new Date().toISOString();for(const t of W()){const s=P.get(t);S.set(te(t),{form:{...s.form},base:s.base,at:e})}}catch{}},f=()=>{Bt(y.item);for(const e of W())Bt({kind:"block",id:e})};const Fe=()=>pe(Ie),ut=e=>{c()&&(m(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",ut),o(()=>window.removeEventListener("beforeunload",ut));const ft=new Set,oa=async e=>{const t=[];for(const s of e){const i=P.get(s);if(!i)continue;const w={...i.form};try{const A=Qa(w,i.block?.meta);await g.blocks.put(s,A),i.block={...i.block,meta:A.meta},i.saved=w,i.base=We(w),tt.set(s,new Date().toISOString()),u()&&pe(te(s)),t.push(s)}catch(A){E.toast(`Block "${s}" not saved: ${A.message||"Unknown error"}`,{type:"error"})}}return t.forEach(s=>ft.add(s)),t.length&&(a.blockRev.value=a.blockRev.peek()+1,Ca("#/blocks"),u()&&V()),t};let ze=!1;const bt=async()=>{if(a.saving.peek()||ze)return;ze=!0;let e,t,s;try{if(await ht(),!u())return;e=z(),t=W(),s=!h||ke()||!t.length;const D=s?_e(e):[];if(D.length){E.toast(D[0].text,{type:"warning"}),C.querySelectorAll("#editor-meta-tabs .tab-item")[bo.indexOf(D[0].field==="dconfig"?"dconfig":"details")]?.click();return}const L=ao(t,Object.fromEntries(t.map(be=>[be,Nt(at.get(be),l||"")])),ft);if(L&&(!await E.confirm(L)||!u()))return}finally{ze=!1}const i=(e.dconfig||"").trim(),w=re(i?JSON.parse(i):null),A=s&&n.cssOpened.peek();a.saving.value=!0;try{if(s&&h){const L={frontmatter:w,body:e.body};e.urlPath!==l&&(L.newUrlPath=e.urlPath),await g.pages.update(l,L)}else s&&await g.pages.create({urlPath:e.urlPath,frontmatter:w,body:e.body});s&&u()&&(Qe=JSON.stringify(e.adminTags),a.base.value=q(e)),A&&await g.settings.saveCustomCss(n.css.peek());const D=await oa(t);if((s||D.length)&&E.toast(oo({page:s,css:A,blocks:D}),{type:"success"}),!s||!u())return;Fe(),h&&Ka({kind:"page",id:e.urlPath,label:n.title.peek().trim()||"Untitled"}),h?e.urlPath!==l&&R.navigate(`/pages/edit${e.urlPath}`):R.navigate("/pages")}catch(D){E.toast(`Save failed: ${D.message||"Unknown error"}`,{type:"error"})}finally{a.saving.value=!1}};let N=null,j=null;const J=new Map,je=new Map,ue=()=>{a.blockRev.value=a.blockRev.peek()+1},sa=new Set(Me.map(e=>e.name)),na=(e,t)=>{const s=`${t?"footer":"body"}:${e}`;return je.has(s)||je.set(s,{id:s,name:e,footer:t,title:t?`${e} - in the footer, on every page`:`${e} - a block on this page`,active:M.observable(!1),dirty:M.observable(!1)}),je.get(s)},mt=()=>{for(const e of a.blockTabs.peek()){const t=P.get(e.name),s=e.name===a.blockTab.peek(),i=t?ne(t):G.has(e.name);e.active.peek()!==s&&(e.active.value=s),e.dirty.peek()!==i&&(e.dirty.value=i)}},V=()=>{const e=Za(Ee,et,sa),t=[...W(),a.blockTab.peek()].filter(i=>i&&!e.some(w=>w.name===i));for(const i of new Set(t))e.push({name:i,footer:!1});const s=e.map(i=>na(i.name,i.footer));s.map(i=>i.id).join("|")!==a.blockTabs.peek().map(i=>i.id).join("|")&&(a.blockTabs.value=s),mt()},vt=()=>{const e=P.get(a.blockTab.peek());!e||!N||(N.setValue(e.form.content),j.setValue(e.form.css),a.blockNote.value="",a.blockReady.value=!0)},gt=async(e,{quiet:t=!1}={})=>{for(;J.has(e);)if(await J.get(e),P.has(e)||t)return P.get(e)||null;if(P.has(e))return P.get(e);const s=(async()=>{try{const i=await g.blocks.get(e);if(!u())return null;const w=Xa({...i,name:e}),A={block:i,saved:w,base:We(w),form:{...w}};let D=null;try{D=S.get(te(e))}catch{}const L=Ya(D,A.base);if(t&&L?.stale)return null;if(Ot({kind:"block",id:e}),L){const be=L.at?` (${ee(L.at)})`:"",fa=!L.stale||await E.confirm(`You left unsaved changes to "${e}"${be}, but the block has been saved since. Restore your version anyway?`);if(!u())return null;fa&&(A.form={...A.form,...L.form,name:e},L.stale||E.toast(`Your unsaved changes to "${e}" are back.`,{type:"info"}))}return D&&pe(te(e)),P.set(e,A),ue(),A}catch(i){return u()&&a.blockTab.peek()===e&&(a.blockNote.value=`Could not open "${e}": ${i.message||"unknown error"}`),null}finally{J.delete(e)}})();return J.set(e,s),s},ht=async()=>{for(;J.size;)await Promise.all([...J.values()])},yt=async e=>{if(!e){if(!a.blockTab.peek())return;a.blockTab.value="",V(),B.refresh(),O(),x.focus();return}a.tab.peek()!=="edit"&&C.querySelectorAll("#editor-meta-tabs .tab-item")[0]?.click(),a.cssMode.peek()&&(a.cssMode.value=!1),a.viewMode.peek()==="preview"&&(a.viewMode.value="split"),a.blockTab.value=e,a.blockUses.value=so(Nt(at.get(e),l||"")),V();let t=P.get(e);t||(a.blockReady.value=!1,a.blockNote.value=`Opening ${e}\u2026`,t=await gt(e)),!(!t||!u()||a.blockTab.peek()!==e)&&(vt(),V(),Te=e,O(),(a.blockPart.peek()==="css"?j:N)?.focus())};let Ve=null;o(()=>clearTimeout(Ve));const kt=(e,t)=>{const s=P.get(a.blockTab.peek());!s||!a.blockReady.peek()||(s.form[e]=t,clearTimeout(Ve),Ve=setTimeout(ue,120),O(),Q())},wt=C.querySelector(".dm-plugin-body")||C,ra={...n,...a,dirty:Yt,pageDirty:Jt,problem:Gt,anySelected:Xt,allSelected:Qt,someSelected:Zt,isEdit:h,savedPath:l||""},la={save:()=>bt(),discard:async()=>{if(await ht(),!(!u()||!c()||!await E.confirm("Discard the changes you have not saved?"))){$e(JSON.parse(a.base.peek())),Fe();for(const[e,t]of P)ne(t)&&(t.form={...t.saved},pe(te(e)));vt(),ue(),V(),O(),Q()}},setBlockPart:e=>{a.blockPart.value=e==="css"?"css":"html",(e==="css"?j:N)?.focus()},formatBlock:()=>{const e=P.get(a.blockTab.peek());!e||!N||(e.form.content=Ga(e.form.content),N.setValue(e.form.content),ue(),O(),Q())},openBlockEditor:()=>{const e=a.blockTab.peek();e&&R.navigate(`/blocks/edit/${encodeURIComponent(e)}`)},showTab:e=>{a.tab.value=e,e==="live"&&xe&&Le(),e==="history"&&h&&!De&&de()},setMode:e=>{a.cssMode.peek()&&(a.cssMode.value=!1),a.viewMode.value=e,he("viewMode",e),e!=="write"&&O()},toggleFullscreen:()=>{a.fullscreen.value=!a.fullscreen.peek(),he("fullscreen",a.fullscreen.peek())},toggleCss:()=>{a.cssMode.peek()?(a.cssMode.value=!1,a.viewMode.value=Vt.includes(ae().viewMode)?ae().viewMode:"split",B.refresh()):(pt(),a.cssMode.value=!0,a.viewMode.value="split")},formatMarkdown:()=>{const e=x.selectionStart,t=fo(_());B.unfoldAll(),K=!0,x.value=t,K=!1,x.selectionStart=x.selectionEnd=Math.min(e,t.length),x.dispatchEvent(new Event("input")),B.refresh(),E.toast("Markdown formatted.",{type:"success"})},toggleFold:()=>{a.folded.peek()?B.unfoldAll():B.foldAll(),a.folded.value=!a.folded.peek()},setWidth:e=>{a.liveWidth.value=e;const t=k("#live-preview-stage");t&&(t.scrollTop=0)},refreshLive:()=>Le(),share:async()=>{if(!h){E.toast("Save the page first - a share link needs a page to point at.",{type:"warning"});return}await co(l)},toggleAll:e=>{const t=!!e?.target?.checked;a.versions.peek().forEach(s=>{s.checked.value=t})},deleteSelected:async()=>{const e=a.versions.peek().filter(s=>s.checked.peek()).map(s=>s.filename),t=e.length;if(!(!t||!await E.confirm(`Delete ${t} version${t>1?"s":""}? This cannot be undone.`)))try{await g.versions.bulkDelete(l,e),E.toast(`${t} version${t>1?"s":""} deleted.`,{type:"success"}),await de()}catch(s){E.toast("Delete failed: "+s.message,{type:"error"})}},saveNamed:async()=>{await po(l)&&u()&&(a.tab.peek()==="history"?await de():De=!1)},closeCompare:()=>{a.compare.value=null,Be=null},restoreCompared:()=>ct(Be)};le.querySelectorAll("[data-tip]").forEach(e=>E.tooltip(e,{content:e.getAttribute("data-tip"),position:"top"}));const ia=M.applyBindings(ra,wt,{methods:la});o(()=>ia.dispose());const ca=Ua(k("#page-hop"),{current:{kind:"page",id:l||"",label:v?.title||l||""},pages:Je,blocks:oe});o(()=>ca.dispose()),N=$t(k('.pe-block-edit[data-part="html"]'),{initialValue:"",language:"html",autoIndent:!0,onChange:e=>kt("content",e)}),j=$t(k('.pe-block-edit[data-part="css"]'),{initialValue:"",language:"css",autoIndent:!0,onChange:e=>kt("css",e)}),N.textarea.setAttribute("aria-label","Block template HTML"),j.textarea.setAttribute("aria-label","Block CSS"),j.textarea.placeholder=`/* Scoped to this block - e.g.
|
|
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.
|
|
94
121
|
color: var(--dm-primary);
|
|
95
|
-
h3 { font-size: 1.25rem; } */`,
|
|
96
|
-
`).map((
|
|
122
|
+
h3 { font-size: 1.25rem; } */`,onChange:e=>va("css",e)}),l(()=>{oe.destroy(),Ge.destroy()});for(const e of It)ce[e]=Do(D(`.pe-comp-edit[data-part="${e}"]`),{ariaLabel:`Component <${e}> block`,language:{template:"html",props:"json",script:"js",style:"css"}[e],onChange:t=>js(e,t)});l(()=>{for(const e of It)ce[e].destroy()});const we=D(".pe-tabs-list"),$t=e=>e.target.closest(".pe-tab")?.dataset.id||"",Ma=e=>{const t=$t(e);t&&(e.target.closest(".pe-tab-close")?Je(t):Tt(t))},Ta=e=>{e.button===1&&$t(e)&&(e.preventDefault(),Je($t(e)))},Sa=e=>{const t=$t(e);!t||e.target.closest(".pe-tab-close")||(e.key==="Enter"||e.key===" "?(e.preventDefault(),Tt(t)):e.key==="Delete"?(e.preventDefault(),Je(t)):e.key==="F10"&&e.shiftKey&&(e.preventDefault(),Me(e.target.closest(".pe-tab"))))};we.addEventListener("click",Ma),we.addEventListener("auxclick",Ta),we.addEventListener("keydown",Sa),l(()=>{we.removeEventListener("click",Ma),we.removeEventListener("auxclick",Ta),we.removeEventListener("keydown",Sa)});const vo=e=>{for(const t of e)Je(t)},Vs=async e=>{const t=I(e);if(t?.id)if(t.kind==="page")a.active.peek()!==e&&await ie(t.id),c()&&f?.id===t.id&&Oa();else if(t.kind==="block"){const s=(A.get(t.id)||await oo(t.id,{quiet:!0}))?.form||Ha(await k.blocks.get(t.id).catch(()=>null));c()&&At({seed:Co(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:Co(t.id,[...se]),source:s})}},Hs=e=>{const t=I(e);if(!t||!x.includes(e))return[];const o=x.indexOf(e),s=Ae.get(e)?.label.peek()||t.id,n=r=>{x=In(x,e,r),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:o>=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:o===0,action:()=>n(-1)},{label:"Move right",icon:"arrow-right",disabled:o>=x.length-1,action:()=>n(1)},{type:"divider"},{label:t.kind==="page"?"Clone page\u2026":`Duplicate ${t.kind}\u2026`,icon:"copy",disabled:!t.id,action:()=>Vs(e)},{label:"Copy link",icon:"link",disabled:!t.id,action:()=>Ne(`${location.origin}${location.pathname}#${_a(t)}`,"Link")}]};{const e=E.contextMenu(we,{cursor:"glyph",match:".pe-tab",inherit:!1,density:"compact",items:t=>Hs(t.dataset.id)});l(()=>e?.destroy?.())}const Ca=D(".pe-ex-scroll"),xa=e=>{const t=e.target.closest(".pe-ex-item"),o=t?.dataset.id||"";if(!o)return;const s=o.indexOf(":"),n=o.slice(0,s),r=o.slice(s+1),i=()=>{ge.has(r)?ge.delete(r):ge.add(r);try{S.set(ss,[...ge])}catch{}z()};if(n==="folder"){i();return}if(n==="page"&&e.target.closest(".pe-ex-chev")&&t.classList.contains("is-folder")){i();return}n==="page"?ie(r):n==="block"?Le(r):n==="component"&&et(r)};Ca.addEventListener("click",xa),l(()=>Ca.removeEventListener("click",xa));let go=null;l(()=>clearTimeout(go)),F.addEventListener("input",()=>{G.refresh(),ne.scrollTop=F.scrollTop,!xe&&(clearTimeout(go),go=setTimeout(Qo,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;a.bodyRev.value,U()})),l(M.effect(()=>{a.bodyRev.value,a.docRev.value;const e=f?bt():"";gt=f?wn(e,ft.map(t=>t.name),ft.filter(t=>t.shortTag).map(t=>t.name)):[],Wo=f?jn(e,se):[],yt(),z()})),l(M.effect(()=>{a.blockRev.value,a.compRev.value,a.docRev.value,a.active.value,a.filter.value,p.title.value,V(),z()})),l(M.effect(()=>{ne.hidden=!!(a.blockTab.value||a.compTab.value)}));let ko=null;l(()=>clearTimeout(ko)),l(M.effect(()=>{const e=p.css.value;a.cssMode.value&&(zt.paint(),mr(ne,e,ue),clearTimeout(ko),ko=setTimeout(Yo,400))}));{const e=D(".pe-version-list"),t=E.contextMenu(e,{cursor:"glyph",match:"tr[data-file]",inherit:!1,density:"compact",items:o=>{const s=Gt(o.dataset.file);return s?[{header:Qa(s)},{label:"Show beside the editor",icon:"columns",action:()=>Xt(s)},{label:"Restore this version\u2026",icon:"rotate-ccw",action:()=>na(s.filename)},{type:"divider"},{label:"Delete\u2026",icon:"trash",danger:!0,action:()=>xs(s)}]:[]}});l(()=>t?.destroy?.()),e.addEventListener("click",o=>{const s=o.target.closest("tr[data-file]");if(!s||o.target.matches("input"))return;const n=o.target.closest(".pe-kebab");if(n){Me(n);return}const r=Gt(s.dataset.file);r&&Xt(r)}),e.addEventListener("keydown",o=>{const s=o.target.closest("tr[data-file]");if(s){if(o.key==="F10"&&o.shiftKey)o.preventDefault(),Me(s);else if(o.key==="Enter"&&o.target===s){const n=Gt(s.dataset.file);n&&Xt(n)}}})}br(D("#editor-body"),D("#editor-divider"),l),Qs({image:e=>(a.cssMode.value=!1,vr(e)),scribe:e=>tn(e),card:e=>Us.open(e,{onMediaBrowse:()=>k.media.list().catch(()=>[])}),timeline:e=>_s.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=>Gs.open(e)),Jt.onHelp(()=>La()),Jt.onTutorial(e=>on(e));const Pa=e=>{if(!c())return;const t=_n(e);t&&(e.preventDefault(),t==="save"?to(!1):t==="layout"?bo.setMode(Fn(a.viewMode.peek())):bo.toggleExplorer())};document.addEventListener("keydown",Pa),l(()=>document.removeEventListener("keydown",Pa));const at=y.querySelector(".dm-plugin-banner-name");l(M.effect(()=>{const e=a.curId.value,t=Ko.value,o=a.blockTab.value||a.compTab.value,s=t==="block"?"Blocks":t==="component"?"Components":"Pages",n=t==="block"||t==="component"?o:e===null?"Editor":fn(!!e,p.title.value,e);if(!c()||(document.title=`${n} - Domma CMS`,!at))return;const r=at.querySelector(".dm-plugin-banner-parent"),i=at.lastChild;r&&i&&i.nodeType===3&&at.querySelector(".dm-plugin-banner-sep")?(r.textContent=s,i.textContent=n):at.textContent=`${s} \u203A ${n}`})),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:()=>La()});const Ks=rt(d,{id:"page-editor-view",icon:"external-link",title:"View on the site",onClick:()=>{f?.isEdit&&window.open(p.urlPath.peek().trim()||f.id,"_blank","noopener")}}),Oa=()=>{if(!f?.isEdit||a.saving.peek()||!c())return;const e=f,t=K(),o=Mo({...t,urlPath:e.id});if(o.length){E.toast(o[0].text,{type:"warning"});return}const s=sn({page:e.page,frontmatter:{...Kt(t,e,Ut(t)),adminTags:t.adminTags},body:t.body,source:e.id,pages:$,onCloned:async n=>{c()&&(await da(),c()&&ie(n))}});l(()=>s.close())},yo=rt(d,{id:"page-editor-clone",icon:"copy",title:"Clone page",onClick:Oa});l(M.effect(()=>{const e=Uo.value;for(const t of[Ks,yo])t&&(t.disabled=!e);yo&&(yo.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:()=>{Vo=tr(zo)}}),l(Da(Ea,{position:"bottom"})),Domma.icons.scan();let Ra={ids:[],active:""};try{Ra=Vn(S.get(as))}catch{}const st=Ia(u),nt=Un(st,Ra,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)}),la=!1;for(const e of x){const t=I(e);t.kind==="page"&&pa(t.id)}V(),z(),yt();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&&Te(t)&&po(t.id,{quiet:!0}).then(()=>{c()&&(V(),z())})}const Aa=st&&!st.id&&st.kind!=="page";nt.active&&x.includes(nt.active)?await Tt(nt.active):x.length?await Tt(x[0]):Aa||ee(),Aa&&c()&&(await(st.kind==="block"?At:tt)({project:jt(u)||""}),c()&&ee())}};function Lo(d,u){!d||typeof u!="string"||!u||[...d.options].some(v=>v.value===u)||(d.append(Object.assign(document.createElement("option"),{value:u,textContent:`${u} (custom)`})),d.value=u)}function mr(d,u,v){d.replaceChildren(...String(u).split(`
|
|
123
|
+
`).map((l,b)=>{const h=document.createElement("span");h.className="editor-line-number-row";const w=document.createElement("span");w.className="fold-toggle fold-toggle--empty";const y=document.createElement("span");return y.className="editor-line-num",y.textContent=String(b+1),h.append(w,y),h})),d.scrollTop=v.scrollTop}function br(d,u,v){if(!d||!u)return;const l=15,b=85,h=c=>Math.min(b,Math.max(l,c));let w=h(parseFloat(Se().splitRatio)||50);const y=c=>{d.style.setProperty("--dm-editor-split",w+"%"),u.setAttribute("aria-valuenow",String(Math.round(w))),c&&dt("splitRatio",w)};y(!1);const C=c=>{const P=d.getBoundingClientRect();P.width&&(w=h((c.clientX-P.left)/P.width*100)),y(!1)},N=()=>{document.removeEventListener("pointermove",C),document.removeEventListener("pointerup",N),d.classList.remove("is-resizing"),u.classList.remove("is-dragging"),y(!0)};v(N),u.addEventListener("pointerdown",c=>{window.matchMedia("(max-width: 768px)").matches||(c.preventDefault(),d.classList.add("is-resizing"),u.classList.add("is-dragging"),document.addEventListener("pointermove",C),document.addEventListener("pointerup",N))}),u.addEventListener("dblclick",()=>{w=50,y(!0)}),u.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=h(H),y(!0)})}async function vr(d){const u=d.selectionStart,v=d.selectionEnd,l=await an({title:"Upload or insert an image"});if(!l||!d.isConnected)return;let b=l.split("/").pop().split("?")[0];try{b=decodeURIComponent(b)}catch{}const h=b.replace(/[\\[\]]/g,"\\$&"),w=l.replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/ /g,"%20");d.setSelectionRange(u,v),Js(d,``),d.dispatchEvent(new Event("input",{bubbles:!0}))}function gr(d,u,v){const l=Object.entries(u||{});return d?(d.replaceChildren(...l.map(([b,h])=>new Option(h?.label||b,b))),l.some(([b])=>b===v)?v:l[0]?.[0]||v):v}async function kr(d,u){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,N)=>N.level-C.level),b=(C,N)=>Object.assign(document.createElement("option"),{value:C,textContent:N}),h=()=>{typeof u=="string"&&u&&![...d.options].some(C=>C.value===u)&&d.append(b(u,`${u} (custom)`))};if(!l.length)return h();const w=document.createElement("optgroup");w.label="Role and above",l.forEach(C=>w.append(b(C.name,`${C.label} and above`)));const y=document.createElement("optgroup");y.label="Role only",l.forEach(C=>y.append(b(`=${C.name}`,`${C.label} only`))),d.replaceChildren(b("public","Public - everyone"),w,y,b("private","Private - super-admin only")),h()}
|
package/docs/DommaFeatures.md
CHANGED
|
@@ -127,7 +127,7 @@ visibility: ["=candidate", "=employer"] # those two roles and nobody else
|
|
|
127
127
|
- Since: 0.50.0
|
|
128
128
|
|
|
129
129
|
- **Share (preview) links** - mint a signed, revocable link to one unpublished page for someone without an account (client sign-off, external designer). Choose expiry (1 hour, 24 hours, 7 days, 30 days) and an optional note; list and revoke live links. The recipient sees the page rendered as anonymous with a "Shared preview" banner showing the expiry; links are single-page, `no-referrer`, `noindex`, and stop working if the page's project is disabled.
|
|
130
|
-
- Where: **Page editor >
|
|
130
|
+
- Where: **Page editor > preview only > Share link**; public URL `/_preview?token=…`; `server/services/previewLinks.js`, index in `content/preview-links.json`
|
|
131
131
|
- Since: 0.51.0
|
|
132
132
|
|
|
133
133
|
- **Custom 404 page** - create a page at `/404` (`content/pages/404.md`) and publish it; it is rendered with the full site theme for every not-found URL (with a real 404 status). Without it a minimal inline 404 is served. It is excluded from the sitemap.
|
|
@@ -287,12 +287,12 @@ Fast, file-based and friendly.
|
|
|
287
287
|
- **Reusable HTML blocks** - an HTML fragment with `{{placeholder}}` fields, used on pages via `[block]` or its own short tag, and as the per-entry template for collection displays (`display="block"`, carousel and listgroup `block=`). Values are HTML-escaped; `{{{field}}}` passes raw; `{{field:DD MMM YYYY}}` formats dates; `{{_id}}`, `{{_createdAt}}`, `{{_updatedAt}}` are available for entries. Template HTML comments are stripped before rendering. 15 bundled blocks (contact-card, contact-info, cta-banner, feedback-card, plugin-card, quickstart-step and more).
|
|
288
288
|
- Where: **Data > Blocks**; `content/blocks/<name>.html`
|
|
289
289
|
- **Companion CSS** - each block may have `<name>.css`, written as plain rules and automatically scoped to `[data-block="<name>"]` using CSS nesting, emitted once alongside the block.
|
|
290
|
-
- Where:
|
|
291
|
-
- **Block
|
|
292
|
-
- Where: **Data > Blocks > (block)
|
|
293
|
-
- Since: Tool redesign 0.77.0
|
|
294
|
-
- **Block preview in the site's own page** - the preview is
|
|
295
|
-
- Where: `admin/js/views/
|
|
290
|
+
- Where: the page editor's workspace, a block's tab, **CSS** part; `content/blocks/<name>.css`
|
|
291
|
+
- **Block editing** - a tab in the page editor's workspace: HTML and CSS editors with syntax highlighting, the three layouts (editor, split, preview), the dock's Block panel (project, Bundled, where it is used, Copy shortcode) and Samples panel (one value per placeholder, driving the preview; right-click to insert the placeholder), and unsaved-change drafts. A new block starts from a name, which cannot be one already taken.
|
|
292
|
+
- Where: **Data > Blocks > (block)**, `#/blocks/edit/<name>`; `admin/js/views/page-editor.js`
|
|
293
|
+
- Since: Tool redesign 0.77.0; in the workspace since Unreleased
|
|
294
|
+
- **Block preview in the site's own page** - the preview is the site's real page from `POST /api/pages/preview/full`, so the theme, fonts, `site.css` and Custom CSS apply: the open page that uses the block, with the block as typed and outlined - or, when no open page uses it, the block on its own on a blank page, its placeholders filled from the sample values (sent as the `[block]` shortcode's attributes). Width (full / 1024 / 390) in preview only. `{{{raw}}}` placeholders render raw and template comments are stripped, as on the site; image fields sample a grey picture (`/admin/preview/sample-image.svg` - the site's sanitiser would drop a data: URL), links `#`, class/style hooks nothing.
|
|
295
|
+
- Where: `admin/js/views/page-editor.js`, `blockAloneBody()` / `sampleRows()` in `admin/js/lib/editor-workspace-arrange.js`, `sampleValue()` in `admin/js/lib/block-editor-arrange.js`
|
|
296
296
|
- Since: Unreleased
|
|
297
297
|
|
|
298
298
|
```markdown
|
|
@@ -304,7 +304,7 @@ Fast, file-based and friendly.
|
|
|
304
304
|
|
|
305
305
|
- **Custom web components (`.dmc`)** - single-file components with `<template>` (mustache-style, `{{#if}}`), `<props>` (typed JSON with defaults and labels), `<script>` (`data`, `methods`, `onMount`, `this.set`) and optional `<style>`, compiled to shadow-DOM custom elements `<dm-name>`. Ships with copy-button, countdown-timer, counter, reveal-panel and testimonial-card.
|
|
306
306
|
- Where: **Data > Components**; `content/components/<name>.dmc`; `server/services/components.js`
|
|
307
|
-
- **Component
|
|
307
|
+
- **Component editing** - in the page editor's workspace, a tab beside pages and blocks: four source blocks, a live preview recompiled as you type, preview-prop rows in the dock (reset, copy as attribute or whole tag, jump to `<props>`), unsaved drafts; plugin components open read-only with "Copy to a new component". The list supports duplicate, export and import of component bundles.
|
|
308
308
|
- Where: **Data > Components > (component)**
|
|
309
309
|
- Since: Tool redesign 0.77.0
|
|
310
310
|
|
package/docs/configuration.md
CHANGED
|
@@ -1494,24 +1494,25 @@ Notes:
|
|
|
1494
1494
|
|
|
1495
1495
|
### Full-page preview in the editor
|
|
1496
1496
|
|
|
1497
|
-
The page editor's
|
|
1498
|
-
|
|
1499
|
-
public site runs on save - so layout, navbar, footer, theme, menus, `dconfig
|
|
1500
|
-
custom CSS
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1497
|
+
The page editor's preview renders the *unsaved* editor state through the
|
|
1498
|
+
server's real page pipeline - the same `parseMarkdown` and `renderPage` the
|
|
1499
|
+
public site runs on save - so layout, navbar, footer, theme, menus, `dconfig`,
|
|
1500
|
+
custom CSS and every open block's unsaved edits are exactly what publishing
|
|
1501
|
+
would produce. It is the right-hand pane of the split layout and the whole
|
|
1502
|
+
stage of the **preview only** layout (the toolbar's buttons, or Ctrl+\ cycles
|
|
1503
|
+
editor and preview, editor only, preview only).
|
|
1504
|
+
|
|
1505
|
+
It re-renders about half a second after you stop typing, but only while a
|
|
1506
|
+
preview pane is on screen. In preview only, width buttons constrain the frame to
|
|
1507
|
+
1024px or 390px to check tablet and mobile layout. The banner is deliberately
|
|
1508
|
+
absent here: the editor is already telling you what you are looking at.
|
|
1508
1509
|
|
|
1509
1510
|
Backed by `POST /api/pages/preview/full` (`pages.read`), which takes
|
|
1510
1511
|
`{urlPath, frontmatter, body}` and returns `{html}`.
|
|
1511
1512
|
|
|
1512
1513
|
### Share links
|
|
1513
1514
|
|
|
1514
|
-
**Share link** in the
|
|
1515
|
+
**Share link** in the preview-only layout's bar mints a URL that renders one unpublished
|
|
1515
1516
|
page to someone with no account - a client, an external designer, a copywriter.
|
|
1516
1517
|
|
|
1517
1518
|
- Scoped to **one page**. The page is named inside the signed token, so a holder
|
|
@@ -1281,13 +1281,18 @@ the hints and choices from the "Parameters:" list in the block's opening comment
|
|
|
1281
1281
|
-->
|
|
1282
1282
|
```
|
|
1283
1283
|
|
|
1284
|
-
**Editing a block from its page:**
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
and
|
|
1289
|
-
|
|
1290
|
-
|
|
1284
|
+
**Editing a block from its page:** the page editor is a workspace - pages, blocks and components open side by
|
|
1285
|
+
side, a tab each, like a code editor. The list on the left shows **This page** (the blocks the open page uses - its
|
|
1286
|
+
tag's blocks first, then the footer's, which are on every page - and its components), every page as a tree, every
|
|
1287
|
+
block and every component; click one and it opens in a tab (Ctrl+Shift+E hides the list). A block's tab edits its
|
|
1288
|
+
HTML and CSS; the preview shows the open page with the block as you type it, outlined - a block the page does not use
|
|
1289
|
+
is shown on its own. **Save** (Ctrl+S) saves the tab you are on - a page together with the changed blocks it uses;
|
|
1290
|
+
**Save all** saves every open page, block and component. A block is shared, so if it is also used on other pages, in
|
|
1291
|
+
a view or in the footer, the first save that changes it says so and asks. **Discard** undoes the tab's changes. A dot
|
|
1292
|
+
marks unsaved work; closing a tab keeps it as a draft, back when you open it again. The dock (the sliders button) shows
|
|
1293
|
+
a block's project, where it is used, its shortcode and the **sample values** its on-its-own preview fills in. **+** by
|
|
1294
|
+
Blocks or Components makes a new one: a name, then its tab. Every editor address - `#/blocks/edit/<name>`,
|
|
1295
|
+
`#/components/edit/<name>`, the lists' Edit - opens in this workspace.
|
|
1291
1296
|
|
|
1292
1297
|
**Example:**
|
|
1293
1298
|
|