domma-cms 1.8.42 → 1.8.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/admin/js/lib/code-format.js +41 -39
- package/admin/js/views/page-editor.js +7 -7
- package/docs/configuration.md +10 -8
- package/package.json +1 -1
- package/server/services/markdown.js +103 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.8.44] - 2026-10-23
|
|
4
|
+
|
|
5
|
+
- **Blocks with empty fields no longer show raw HTML.** A block placeholder on its own line with nothing to fill it (`{{bio}}` when there is no bio) left a blank line, which ended the block's HTML in the Markdown, and what followed showed on the page as code - the About page's team card printed its contact links as raw tags. Such a line is now dropped when it fills empty (`fillTemplateLines()` in `markdown.js`), for collection displays and block short tags alike; a template's own blank lines stay, since they are how it hands a Markdown value to marked.
|
|
6
|
+
- **Inline code on an indented HTML line shows again.** `<code>` inside an HTML line indented four or more spaces came out as `SC1:0` - the code-region scrub stored the line holding the code's placeholder and restored it only once. Restore now restores what it brings back too.
|
|
7
|
+
|
|
8
|
+
## [1.8.43] - 2026-10-23
|
|
9
|
+
|
|
10
|
+
- **What is inside a short tag is indented too.** Shift+Alt+F now indents the content of `[grid]`, `[col]`, `[card]`, `[tabs]` and the other container short tags by how deeply it nests, not only the tag lines, and Enter after an opening tag goes one deeper. A nested list or fenced code keeps its shape inside. The renderer takes the nesting indent off before anything else (a container's content loses the indent its lines share), so four spaces inside a card is no longer a code block - and a page written flat reads exactly as before (checked across the fleet's 1,700 pages).
|
|
11
|
+
|
|
3
12
|
## [1.8.42] - 2026-10-23
|
|
4
13
|
|
|
5
14
|
- **Format with Shift+Alt+F in the editor workspace.** The page body is tidied: trailing spaces off, runs of blank lines down to one, pipe tables lined up, and the lines holding only a container short tag (`[grid]`, `[col]`, `[card]`, `[tabs]`...) indented by how deeply they nest - the content between them stays at the margin, and the renderer drops a tag line's indent before parsing (`unindentShortTags()` in `markdown.js`), so the indent never reaches the page. Block and component HTML and JS are re-indented (never rewritten), CSS is laid out one declaration a line, JSON one value a line. One Ctrl+Z undoes it; the toolbar's format buttons do the same (`admin/js/lib/code-format.js`).
|
|
@@ -1,42 +1,44 @@
|
|
|
1
|
-
let
|
|
2
|
-
`).filter(
|
|
3
|
-
`);
|
|
4
|
-
`+
|
|
1
|
+
let b=" ";export const DEFAULT_CODE_PREFS=Object.freeze({indent:"spaces",size:2,detect:!0,autoIndent:!0,continueLists:!0,shortTags:!0,tables:!0,formatOnSave:!1}),INDENT_SIZES=Object.freeze([2,4,8]);export function cleanCodePrefs(t={}){const e=DEFAULT_CODE_PREFS,n=s=>typeof t?.[s]=="boolean"?t[s]:e[s];return{indent:t?.indent==="tabs"?"tabs":"spaces",size:INDENT_SIZES.includes(Number(t?.size))?Number(t.size):e.size,detect:n("detect"),autoIndent:n("autoIndent"),continueLists:n("continueLists"),shortTags:n("shortTags"),tables:n("tables"),formatOnSave:n("formatOnSave")}}let w=cleanCodePrefs();export function setCodePrefs(t){w=cleanCodePrefs(t)}export const codePrefs=()=>w,prefUnit=()=>w.indent==="tabs"?" ":" ".repeat(w.size),unitFor=t=>w.detect?detectIndent(t,prefUnit()):prefUnit();export function detectIndent(t,e=" "){const n=String(t).split(`
|
|
2
|
+
`).filter(i=>i.trim()),s=n.filter(i=>i.startsWith(" ")).length,r=n.filter(i=>i.startsWith(" ")).length;if(s>r)return" ";const o=n.map(i=>i.match(/^ */)[0].length).filter(i=>i>0);return o.length?o.filter(i=>i%4===2).length>o.length/5?" ":e===" ".repeat(8)&&o.every(i=>i%8===0)?e:" ":e}export function isFormatKey(t){return!t||!t.altKey||!t.shiftKey||t.ctrlKey||t.metaKey?!1:t.code==="KeyF"||String(t.key||"").toLowerCase()==="f"}export function formatCode(t,e){const n=String(t??"").replace(/\r\n?/g,`
|
|
3
|
+
`);b=e==="md"?prefUnit():unitFor(n);try{switch(e){case"json":return{text:formatJson(n)};case"css":return{text:formatCss(n)};case"js":return{text:formatJs(n)};case"html":return{text:formatHtml(n)};case"md":return{text:formatMarkdown(n)};default:return{error:"Nothing to format here."}}}catch(s){return{error:s.message||"Could not format this."}}}export function formatJson(t){if(!t.trim())return t;try{JSON.parse(t)}catch(r){throw new Error(`Not valid JSON: ${r.message}`)}let e="",n=0;const s=()=>`
|
|
4
|
+
`+b.repeat(n);for(let r=0;r<t.length;r++){const o=t[r];if(o==='"'){let l=r+1;for(;t[l]!=='"';)l+=t[l]==="\\"?2:1;e+=t.slice(r,l+1),r=l}else if(o==="{"||o==="["){const l=o==="{"?"}":"]",i=t.slice(r+1).search(/\S/);if(t[r+1+i]===l){e+=o+l,r+=i+1;continue}n++,e+=o+s()}else o==="}"||o==="]"?(n--,e+=s()+o):o===","?e+=","+s():o===":"?e+=": ":/\s/.test(o)||(e+=o)}return e+(t.endsWith(`
|
|
5
5
|
`)?`
|
|
6
|
-
`:"")}export function formatCss(t){if(!t.trim())return t;if(/\{\{\{?\s*[#/^\w]/.test(t))throw new Error("This CSS has {{placeholders}} in it - left as it is.");const e=[];let n=0,s="",
|
|
7
|
-
`);a(c()+
|
|
8
|
-
`;)
|
|
6
|
+
`:"")}export function formatCss(t){if(!t.trim())return t;if(/\{\{\{?\s*[#/^\w]/.test(t))throw new Error("This CSS has {{placeholders}} in it - left as it is.");const e=[];let n=0,s="",r=!1,o="",l=!1,i=0;const c=()=>b.repeat(n),a=u=>{r&&e.length&&e[e.length-1]!==""&&!e[e.length-1].endsWith("{")&&e.push(""),r=!1,e.push(u)},f=()=>{l&&n===0?(r=!0,l=!1):(o.match(/\n/g)||[]).length>=2&&(r=!0),o=""},p=u=>{const m=u.match(/^(--[\w-]+|[-\w]+)\s*:\s*([\s\S]*)$/);return!m||u.startsWith("@")?u:`${m[1]}: ${m[2]}`},h=u=>{const m=s.trim();if(s="",!!m){if(u==="{"){const d=m.startsWith("@")?[m]:A(m,",");d.forEach((x,g)=>a(c()+x.trim()+(g<d.length-1?",":" {")));return}a(c()+p(m)+";")}};for(let u=0;u<t.length;u++){const m=t[u];if(m==="/"&&t[u+1]==="*"){const d=t.indexOf("*/",u+2),x=d<0?t.length:d+2,g=t.slice(u,x);if(s.trim())s+=g;else{f();const E=g.split(`
|
|
7
|
+
`);a(c()+E[0]),e.push(...E.slice(1))}u=x-1;continue}if(m==='"'||m==="'"){s.trim()||f();let d=u+1;for(;d<t.length&&t[d]!==m&&t[d]!==`
|
|
8
|
+
`;)d+=t[d]==="\\"?2:1;s+=t.slice(u,d+1),u=d;continue}if(/\s/.test(m)){s.trim()?s.endsWith(" ")||(s+=" "):o+=m;continue}if(s.trim()||f(),m==="("?i++:m===")"&&(i=Math.max(0,i-1)),i===0&&m==="{"){h("{"),n++,o="";continue}if(i===0&&m===";"){h(";");continue}if(i===0&&m==="}"){if(h(";"),n--,n<0)throw new Error("There is a } with no { before it.");r=!1,e.push(c()+"}"),o="",n===0&&(l=!0);continue}s+=m}if(s.trim()&&h(";"),n!==0)throw new Error(`${n} { left without a closing }.`);return e.join(`
|
|
9
9
|
`)+`
|
|
10
|
-
`}function
|
|
11
|
-
`),s={comment:!1,str:null,stack:[]},
|
|
12
|
-
`)}const O=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),
|
|
13
|
-
`),n=[],s=[];let
|
|
14
|
-
`);let
|
|
15
|
-
`))},c=(a,
|
|
16
|
-
`),s.push(a),c(a,0,
|
|
17
|
-
`;else{let
|
|
18
|
-
`)}function
|
|
19
|
-
`),n=e.filter(
|
|
20
|
-
`).replace(/^\n+|\n+$/g,"")}function
|
|
21
|
-
`).map(n=>n&&
|
|
22
|
-
`)}export const SHORT_TAG_CONTAINERS=new Set(["grid","row","col","card","box","header","footer","tabs","tab","accordion","item","carousel","slide","timeline","event","hero","banner","slideover","center","listgroup","table","dconfig","reveal","animate","pulse","shake","scramble","ripple","breathe","ambient","twinkle","fireworks","scribe","wait","render","redraw"]);export function shortTagLine(t){const e=String(t).trim().match(/^\[(\/?)([a-z][\w-]*)(?:\s[^\]]*)?\]$/i);return!e||!SHORT_TAG_CONTAINERS.has(e[2].toLowerCase())||!e[1]&&/\/\]$/.test(t.trim())?null:{name:e[2].toLowerCase(),close:!!e[1]}}function
|
|
23
|
-
`)
|
|
10
|
+
`}function A(t,e){const n=[];let s=0,r=null,o="";for(const l of t){if(r)l===r&&(r=null);else if(l==='"'||l==="'")r=l;else if("([{".includes(l))s++;else if(")]}".includes(l))s--;else if(l===e&&s===0){n.push(o),o="";continue}o+=l}return n.push(o),n}const j=/(^|[(,=:[!&|?{};+\-*%<>~^]|\b(?:return|typeof|case|do|else|in|of|new|delete|void|throw|yield|await))\s*$/;function R(t,e,n){let s=0;if(e.str){let r=0;for(;r<t.length&&t[r]!==e.str;)r+=t[r]==="\\"?2:1;if(r>=t.length){t.endsWith("\\")||(e.str=null);return}e.str=null,s=r+1}for(;s<t.length;){const r=e.stack[e.stack.length-1];if(e.comment){const l=t.indexOf("*/",s);if(l<0)return;e.comment=!1,s=l+2;continue}if(r==="`"){const l=t[s];if(l==="\\"){s+=2;continue}if(l==="`"){e.stack.pop(),s++;continue}if(l==="$"&&t[s+1]==="{"){e.stack.push("${"),s+=2;continue}s++;continue}const o=t[s];if(o==="/"&&t[s+1]==="/")return;if(o==="/"&&t[s+1]==="*"){e.comment=!0,s+=2;continue}if(o==='"'||o==="'"){let l=s+1;for(;l<t.length&&t[l]!==o;)l+=t[l]==="\\"?2:1;if(l>=t.length&&t.endsWith("\\")){e.str=o;return}s=l+1;continue}if(o==="`"){e.stack.push("`"),s++;continue}if(o==="/"&&j.test(t.slice(0,s))){let l=s+1,i=!1;for(;l<t.length;){const c=t[l];if(c==="\\"){l+=2;continue}if(c==="[")i=!0;else if(c==="]")i=!1;else if(c==="/"&&!i)break;l++}s=l+1;continue}if(o==="}"&&r==="${"){e.stack.pop(),s++;continue}o==="{"&&e.stack.length?e.stack.push("{"):o==="}"&&r==="{"&&e.stack.pop(),"{[(".includes(o)?n(o,s,!0):"}])".includes(o)&&n(o,s,!1),s++}}function $(t){return t.replace(/(["'`])(?:\\.|(?!\1).)*\1/g,'""').replace(/\/\*.*?(\*\/|$)/g,"").replace(/\/\/.*$/,"").trimEnd()}export function formatJs(t,e=0){const n=t.split(`
|
|
11
|
+
`),s={comment:!1,str:null,stack:[]},r=[],o=[];let l="";const i=()=>{const c=r[r.length-1];return c?c.at+1+(c.sw&&c.inCase?1:0):0};if(n.forEach((c,a)=>{const f=s.stack[s.stack.length-1]==="`"||!!s.str,p=s.comment,h=c.trim();let u=c;if(f)u=c;else if(!h)u="";else if(p)u=b.repeat(e+i())+(h.startsWith("*")?" ":"")+h;else{const d=Math.min(r.length,(h.match(/^[}\])]+/)||[""])[0].length),x=r[r.length-1],g=/^(case\b.*|default\s*):/.test(h);let E;d?E=r[r.length-d].at:x?.sw&&g?E=x.at+1:E=i();const S=$(h),C=/^(\.(?!\.\.)|\?\.|[+\-*/%]|&&|\|\||\?\?|\?(?!\.)|:(?!:))/.test(S)&&!g||/(=>|[^=!<>]=|[+\-*/%]|&&|\|\||\?\?|\?)$/.test(l)&&!d&&!/^[{[(]/.test(S),W=S.startsWith("/")&&j.test(l);C&&!W&&!/^[}\])]/.test(h)&&E++,u=b.repeat(e+E)+h}o.push(u);const m=Math.round(u.match(/^[ \t]*/)[0].length/b.length)-e;if(f||p||h){const d=r[r.length-1];R(c,s,(x,g,E)=>{if(E){const S=x==="{"&&/\bswitch\s*\(.*\)\s*$/.test($(c.slice(0,g)));r.push({at:f||p?i():m,sw:S,inCase:!1})}else r.pop()}),d?.sw&&!f&&!p&&/^(case\b.*|default\s*):/.test(h)&&(d.inCase=!0),!f&&!p&&(l=$(h))}}),s.comment||s.str||s.stack.some(c=>c!=="{"))throw new Error("A comment, string or template is left open.");return o.join(`
|
|
12
|
+
`)}const O=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),z=new Set(["pre","textarea","script","style"]);export function formatHtml(t){const e=t.split(`
|
|
13
|
+
`),n=[],s=[];let r="text",o=null,l=null;const i=a=>{const f=a.lines.join(`
|
|
14
|
+
`);let p=null;try{const h=(a.open.match(/\btype\s*=\s*["']?([^"'\s>]*)/i)||[])[1]||"";a.name==="script"&&(!h||/module|javascript|ecmascript/i.test(h))?p=formatJs(f,a.level+1):a.name==="style"&&(p=_(formatCss(N(f)).replace(/\n$/,""),a.level+1))}catch{p=null}s.push(...(p??f).split(`
|
|
15
|
+
`))},c=(a,f,p)=>{for(;f<a.length;){if(r==="comment"){const h=a.indexOf("-->",f);if(h<0)return;r="text",f=h+3;continue}if(r==="tag"){const h=a[f];if(o.text+=h,f++,o.q){h===o.q&&(o.q=null);continue}if(h==='"'||h==="'"){o.q=h;continue}if(h!==">")continue;r="text";const u=o;if(o=null,O.has(u.name)||/\/\s*>$/.test(u.text))continue;if(z.has(u.name)){const m=new RegExp(`</${u.name}\\s*>`,"i").exec(a.slice(f));if(m){f+=m.index+m[0].length;continue}l={name:u.name,level:u.level,open:u.text,lines:[]},r="raw";return}n.push(u.name);continue}if(a.startsWith("<!--",f)){r="comment",f+=4;continue}if(a.startsWith("{{",f)){const h=a.indexOf("}}",f+2);if(h<0)return;const u=a.slice(f+2,h).trim();/^#\w+/.test(u)?n.push("#"+u.match(/^#(\w+)/)[1]):/^\/\w+/.test(u)&&I(n,"#"+u.slice(1).trim()),f=h+2;continue}if(a[f]==="<"&&a[f+1]==="/"){const h=/^<\/([a-zA-Z][\w:-]*)\s*>/.exec(a.slice(f));if(h){I(n,h[1].toLowerCase()),f+=h[0].length;continue}}if(a[f]==="<"&&/[a-zA-Z]/.test(a[f+1]||"")){const h=/^<([a-zA-Z][\w:-]*)/.exec(a.slice(f))[1];r="tag",o={name:h.toLowerCase(),level:p,text:"<"+h,q:null},f+=1+h.length;continue}f++}};for(const a of e){const f=a.trim();if(r==="raw"){const u=new RegExp(`</${l.name}\\s*>`,"i").exec(a);if(!u){l.lines.push(a);continue}const m=l;if(l=null,r="text",m.name==="pre"||m.name==="textarea")s.push(...m.lines,a);else{const d=a.slice(0,u.index);d.trim()&&m.lines.push(d),m.lines.some(x=>x.trim())?i(m):s.push(...m.lines),s.push(b.repeat(m.level)+a.slice(u.index).trim())}c(a,u.index+u[0].length,m.level);continue}if(r==="comment"||r==="tag"&&o.q){r==="tag"&&(o.text+=`
|
|
16
|
+
`),s.push(a),c(a,0,o?.level??0);continue}if(!f){s.push("");continue}let p;if(r==="tag")p=/^\/?>/.test(f)?o.level:o.level+1,o.text+=`
|
|
17
|
+
`;else{let u=0;const m=/(?:<\/[a-zA-Z][\w:-]*\s*>|\{\{\s*\/\w+\s*\}\}|\{\{\s*else\b[^}]*\}\})\s*/y;for(let d=0;;){m.lastIndex=d;const x=m.exec(f);if(!x||(u++,/^\{\{\s*else/.test(x[0])))break;d=m.lastIndex}p=Math.max(0,n.length-u)}const h=b.repeat(p)+f;s.push(h),c(h,p*b.length,p)}return l&&s.push(...l.lines),s.join(`
|
|
18
|
+
`)}function I(t,e){const n=t.lastIndexOf(e);n>=0&&(t.length=n)}function N(t){const e=t.split(`
|
|
19
|
+
`),n=e.filter(r=>r.trim()).map(r=>r.match(/^[ \t]*/)[0].length),s=n.length?Math.min(...n):0;return e.map(r=>r.slice(Math.min(s,r.match(/^[ \t]*/)[0].length))).join(`
|
|
20
|
+
`).replace(/^\n+|\n+$/g,"")}function _(t,e){return t.split(`
|
|
21
|
+
`).map(n=>n&&b.repeat(e)+n).join(`
|
|
22
|
+
`)}export const SHORT_TAG_CONTAINERS=new Set(["grid","row","col","card","box","header","footer","tabs","tab","accordion","item","carousel","slide","timeline","event","hero","banner","slideover","center","listgroup","table","dconfig","reveal","animate","pulse","shake","scramble","ripple","breathe","ambient","twinkle","fireworks","scribe","wait","render","redraw"]);export function shortTagLine(t){const e=String(t).trim().match(/^\[(\/?)([a-z][\w-]*)(?:\s[^\]]*)?\]$/i);return!e||!SHORT_TAG_CONTAINERS.has(e[2].toLowerCase())||!e[1]&&/\/\]$/.test(t.trim())?null:{name:e[2].toLowerCase(),close:!!e[1]}}export function shortTagLayout(t){const e=t.map(()=>({tag:!1,depth:0,block:-1,fence:!1})),n=new Set;let s=null;t.forEach((i,c)=>{if(s){n.add(c);const f=i.match(/^\s*(`{3,}|~{3,})\s*$/);f&&f[1][0]===s[0]&&f[1].length>=s.length&&(s=null);return}const a=i.match(/^\s*(`{3,}|~{3,})/);a&&(s=a[1])});const r=(i,c)=>{let a=0;const f=new RegExp(`\\[(\\/?)${i}(?:[\\s\\]])`,"gi");for(let p=c;p<t.length;p++)if(!n.has(p))for(const h of t[p].matchAll(f))if(!h[1])a++;else{if(a===0)return!0;a--}return!1},o=[];t.forEach((i,c)=>{const a=o[o.length-1];if(e[c].depth=o.length,e[c].block=a?a.at:-1,n.has(c)){e[c].fence=!0;return}const f=shortTagLine(i);if(f)if(f.close){const p=o.map(h=>h.name).lastIndexOf(f.name);if(p<0)return;o.length=p,Object.assign(e[c],{tag:!0,depth:o.length})}else r(f.name,c+1)&&(e[c].tag=!0,o.push({name:f.name,at:c}))});const l=new Map;return t.forEach((i,c)=>{const a=e[c];if(a.tag||a.fence||a.block<0||!i.trim())return;const f=i.match(/^[ \t]*/)[0].length;l.set(a.block,Math.min(l.get(a.block)??1/0,f))}),e.map((i,c)=>{const a=t[c].match(/^[ \t]*/)[0].length,f=i.tag?a:i.block<0?0:Math.min(a,l.get(i.block)??0);return{...i,cut:f}})}function F(t){const e=shortTagLayout(t);return t.map((n,s)=>e[s].cut?n.slice(e[s].cut):n)}function q(t){const e=shortTagLayout(t);return t.map((n,s)=>{const r=e[s];return r.tag?b.repeat(r.depth)+n.trim():!r.depth||!n.trim()?n:b.repeat(r.depth)+n})}export function formatMarkdown(t){if(!t.trim())return t;const e=w.shortTags?F(t.split(`
|
|
23
|
+
`)):t.split(`
|
|
24
|
+
`),n=[];let s=null,r=!1;const o=i=>/^( {4}|\t)/.test(i),l=i=>{for(let c=i+1;c<e.length;c++)if(e[c].trim())return e[c];return""};for(let i=0;i<e.length;i++){const c=e[i];if(s){n.push(c);const u=c.match(/^ {0,3}(`{3,}|~{3,})\s*$/);u&&u[1][0]===s[0]&&u[1].length>=s.length&&(s=null);continue}if(r||/^\s*<pre[\s>]/i.test(c)){n.push(c),r=!/<\/pre\s*>/i.test(c);continue}const a=c.match(/^ {0,3}(`{3,}|~{3,})/);if(a){s=a[1],n.push(c.trimEnd());continue}if(w.tables&&K(e,i)){let u=i+2;for(;u<e.length&&/\|/.test(e[u])&&e[u].trim();)u++;n.push(...Z(e.slice(i,u))),i=u-1;continue}if(!c.trim()){const u=n.length?n[n.length-1]:"",m=[...n].reverse().find(d=>d.trim())||"";if(!u.trim()&&!(o(m)&&o(l(i))))continue;n.push("");continue}if(o(c)){n.push(c);continue}const f=c.match(/[ \t]*$/)[0],p=c.slice(0,c.length-f.length),h=/ {2,}$/.test(f)&&(e[i+1]||"").trim()&&!/^\s*#/.test(p);n.push(h?p+" ":p)}for(;n.length&&!n[0].trim();)n.shift();for(;n.length&&!n[n.length-1].trim();)n.pop();return(w.shortTags?q(n):n).join(`
|
|
24
25
|
`)+`
|
|
25
|
-
`}const
|
|
26
|
-
`,e-1)+1,
|
|
27
|
-
`,e);return n<0?t.length:n};export function enterEdit(t,e,n,s){const
|
|
28
|
-
`)
|
|
29
|
-
`+
|
|
30
|
-
`+(S
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
`+c
|
|
34
|
-
`+c
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
`)
|
|
39
|
-
`)
|
|
40
|
-
`).length-1,i=t.split(`
|
|
41
|
-
`)
|
|
42
|
-
`),
|
|
26
|
+
`}const B=/^\s*:?-+:?\s*$/;function k(t){let e=t.trim();e.startsWith("|")&&(e=e.slice(1)),e.endsWith("|")&&!e.endsWith("\\|")&&(e=e.slice(0,-1));const n=[];let s="";for(let r=0;r<e.length;r++){if(e[r]==="\\"&&e[r+1]==="|"){s+="\\|",r++;continue}if(e[r]==="|"){n.push(s.trim()),s="";continue}s+=e[r]}return n.push(s.trim()),n}function K(t,e){const n=t[e],s=t[e+1];if(!n||!s||!n.includes("|")||!s.includes("|")||/^\s{4,}/.test(n))return!1;const r=k(s);if(!(r.length>0&&r.every(o=>B.test(o))&&k(n).length===r.length))return!1;for(let o=e+2;o<t.length&&t[o].includes("|")&&t[o].trim();o++)if(k(t[o]).length>r.length)return!1;return!0}function Z(t){const e=t[0].match(/^\s*/)[0],n=t.map(k),s=Math.max(...n.map(i=>i.length)),r=n[1].map(i=>i.startsWith(":")&&i.endsWith(":")?"c":i.endsWith(":")?"r":i.startsWith(":")?"l":""),o=i=>[...i].length,l=Array.from({length:s},(i,c)=>Math.max(3,...n.map((a,f)=>f===1?3:o(a[c]??""))));return n.map((i,c)=>{const a=Array.from({length:s},(f,p)=>{const h=l[p];if(c===1){const d=r[p]||"",x="-".repeat(h-(d==="c"?2:d?1:0));return d==="c"?`:${x}:`:d==="r"?`${x}:`:d==="l"?`:${x}`:x}const u=i[p]??"",m=h-o(u);return r[p]==="r"?" ".repeat(m)+u:r[p]==="c"?" ".repeat(Math.floor(m/2))+u+" ".repeat(Math.ceil(m/2)):u+" ".repeat(m)});return`${e}| ${a.join(" | ")} |`})}const y=(t,e)=>t.lastIndexOf(`
|
|
27
|
+
`,e-1)+1,T=(t,e)=>{const n=t.indexOf(`
|
|
28
|
+
`,e);return n<0?t.length:n};export function enterEdit(t,e,n,s){const r=y(t,e),o=t.slice(r,T(t,e)),l=t.slice(r,e),i=t.slice(n,T(t,n)),c=(l.match(/^[ \t]*/)||[""])[0],a=(x,g=x.length)=>({start:e,end:n,text:x,selStart:e+g,selEnd:e+g});if(s==="md"||!s){const x=shortTagLine(o);if(x&&e===n&&!i.trim()&&!L(t,r))return!w.shortTags||!w.autoIndent?a(`
|
|
29
|
+
`):a(`
|
|
30
|
+
`+c+(x.close?"":prefUnit()));if(w.continueLists&&!L(t,r)){const g=l.match(/^([ \t]*)((?:>[ \t]?)+)?([ \t]*)([-*+]|(\d+)([.)]))?([ \t]+)(\[[ xX]\][ \t]+)?/),E=g?.[2]||"",S=g?.[4]||"",C=/^\s*([-*_])(\s*\1){2,}\s*$/.test(o);if(g&&(E||S)&&!C&&g[0].length<=l.length){if(!o.slice(g[0].length).trim()&&e===n&&e>=r+g[0].length)return{start:r,end:T(t,e),text:g[1],selStart:r+g[1].length,selEnd:r+g[1].length};if(e>=r+g[0].length-(g[7]?.length||0)){const M=g[5]?`${Number(g[5])+1}${g[6]}`:S,v=`
|
|
31
|
+
`+g[1]+E+(g[3]||"")+(S?M+(g[7]||" ")+(g[8]?"[ ] ":""):g[7]||"");return a(v)}}}return a(`
|
|
32
|
+
`+(w.autoIndent?c:""))}if(!w.autoIndent)return a(`
|
|
33
|
+
`);const f=(s==="html"?l:$(l)).trimEnd().slice(-1),p=i.trimStart()[0]||"",h={"{":"}","[":"]","(":")"};let u=!!h[f],m=u&&p===h[f];if(s==="html"||s==="css"){const x=l.match(/<([a-zA-Z][\w:-]*)(?:\s[^<>]*)?>\s*$/);x&&!O.has(x[1].toLowerCase())&&!/\/\s*>\s*$/.test(l)&&(u=!0,m=new RegExp(`^\\s*</${x[1]}\\s*>`,"i").test(i)),/\{\{\s*#\w+[^}]*\}\}\s*$/.test(l)&&(u=!0,m=/^\s*\{\{\s*\//.test(i))}if(!u)return a(`
|
|
34
|
+
`+c);const d=`
|
|
35
|
+
`+c+unitFor(t);if(m){const x=(t.slice(n).match(/^[ \t]*/)||[""])[0].length;return{start:e,end:n+x,text:d+`
|
|
36
|
+
`+c,selStart:e+d.length,selEnd:e+d.length}}return a(d)}function L(t,e){let n=null;for(const s of t.slice(0,e).split(`
|
|
37
|
+
`)){const r=s.match(/^\s*(`{3,}|~{3,})/);r&&(n?r[1][0]===n[0]&&r[1].length>=n.length&&!s.trim().slice(r[1].length).trim()&&(n=null):n=r[1])}return!!n}export function indentEdit(t,e,n,s,r=" "){const o=y(t,e),l=n>e&&t[n-1]===`
|
|
38
|
+
`?n-1:n,i=T(t,l);if(!t.slice(e,n).includes(`
|
|
39
|
+
`)&&!(s&&e===n))return null;const c=t.slice(o,i),a=c.split(`
|
|
40
|
+
`),f=a.map(h=>s?h.startsWith(r)?h.slice(r.length):h.startsWith(" ")?h.slice(2):h.startsWith(" ")?h.slice(1):h.replace(/^ /,""):h.trim()?r+h:h),p=f.join(`
|
|
41
|
+
`);if(p===c)return null;if(e===n){const h=a[0].length-f[0].length,u=Math.max(o,e-h);return{start:o,end:i,text:p,selStart:u,selEnd:u}}return{start:o,end:i,text:p,selStart:o,selEnd:o+p.length}}export function closerEdit(t,e,n,s,r=""){if(e!==n)return null;const o=y(t,e);let l=t.slice(o,e),i=s;if(r==="html"&&s==="/"&&/^[ \t]*<$/.test(l))l=l.slice(0,-1),i="</";else if(!"}])".includes(s))return null;if(!w.autoIndent)return null;const c=unitFor(t);if(!l||l.trim()||!l.endsWith(c))return null;const a=l.slice(0,-c.length),f=o+a.length+i.length;return{start:o,end:e,text:a+i,selStart:f,selEnd:f}}export const tabUnit=(t,e)=>e==="md"?prefUnit():unitFor(t);export function changedSpan(t,e){let n=0;const s=Math.min(t.length,e.length);for(;n<s&&t[n]===e[n];)n++;let r=0;for(;r<s-n&&t[t.length-1-r]===e[e.length-1-r];)r++;return{start:n,end:t.length-r,text:e.slice(n,e.length-r)}}export function caretAfterFormat(t,e,n){const s=t.slice(0,n).split(`
|
|
42
|
+
`).length-1,r=t.split(`
|
|
43
|
+
`)[s]??"",o=n-y(t,n),l=Math.max(0,o-r.match(/^[ \t]*/)[0].length),i=e.split(`
|
|
44
|
+
`),c=Math.min(s,i.length-1);let a=0;for(let h=0;h<c;h++)a+=i[h].length+1;const f=i[c],p=f.match(/^[ \t]*/)[0].length;return a+Math.min(f.length,p+l)}export function applyEdit(t,e){const{start:n,end:s,text:r}=e;let o=!1;if(typeof document<"u"&&typeof document.execCommand=="function"){t.focus(),t.setSelectionRange(n,s);try{o=r?document.execCommand("insertText",!1,r):n===s||document.execCommand("delete")}catch{o=!1}}o||(t.value=t.value.slice(0,n)+r+t.value.slice(s),t.dispatchEvent(new Event("input",{bubbles:!0}))),t.setSelectionRange(e.selStart,e.selEnd)}export function formatTextarea(t,e){const n=t.value,s=formatCode(n,e);if(s.error)return s;if(s.text===n)return{text:n,unchanged:!0};const r=caretAfterFormat(n,s.text,t.selectionStart),o=changedSpan(n,s.text),l=t.scrollTop;return applyEdit(t,{...o,selStart:r,selEnd:r}),t.scrollTop=l,s}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{api as h}from"../api.js";import{CardBuilder as en}from"../lib/card-builder.js";import{TimelineBuilder as tn}from"../lib/timeline-builder.js";import{attachEditorKeybindings as on,createToolbar as an,insertAtCursor as sn}from"../lib/markdown-toolbar.js";import{EffectsBuilder as nn}from"../lib/effects-builder.js";import{attachContextMenu as rn}from"../lib/shortcode-context-menu.js";import{registerCustomInserters as ln}from"../lib/shortcode-insert.js";import{populateThemeSelect as cn}from"../lib/themes.js";import{createFoldingManager as dn}from"../lib/editor-folding.js";import{openScribeComposer as pn}from"../lib/scribe-composer.js";import{openCrudTutorial as un}from"../lib/crud-tutorial.js";import{addBannerAction as dt}from"../lib/plugin-chrome.js";import{exampleJson as mn,parseSchema as Po,schemaText as fn}from"../lib/seo-structured-data.js";import{helpPopovers as qa}from"../lib/help-popover.js";import{onceClosed as jt,openMenuAt as Ce,sidebarCount as pt}from"../lib/tool-kit.js";import{pickMedia as bn}from"../lib/media-picker.js";import{openPageClone as vn}from"../lib/page-clone.js";import{openEditorReference as Va}from"../lib/page-editor-reference.js";import{cleanType as Bt,DEFAULT_TYPE as It,EDITOR_FONTS as gn,fontHref as hn,isDefaultType as kn,LINE_HEIGHTS as yn,SIZES as wn,typeVars as _a}from"../lib/editor-type.js";import{getProjectFromHash as Ft}from"../lib/project-context.js";import{attachSmoothCaret as Ka,getCaretPreference as Sn,setCaretPreference as En}from"../lib/smooth-caret.js";import{SCREEN as Oo,bannerSection as
|
|
1
|
+
import{api as h}from"../api.js";import{CardBuilder as en}from"../lib/card-builder.js";import{TimelineBuilder as tn}from"../lib/timeline-builder.js";import{attachEditorKeybindings as on,createToolbar as an,insertAtCursor as sn}from"../lib/markdown-toolbar.js";import{EffectsBuilder as nn}from"../lib/effects-builder.js";import{attachContextMenu as rn}from"../lib/shortcode-context-menu.js";import{registerCustomInserters as ln}from"../lib/shortcode-insert.js";import{populateThemeSelect as cn}from"../lib/themes.js";import{createFoldingManager as dn}from"../lib/editor-folding.js";import{openScribeComposer as pn}from"../lib/scribe-composer.js";import{openCrudTutorial as un}from"../lib/crud-tutorial.js";import{addBannerAction as dt}from"../lib/plugin-chrome.js";import{exampleJson as mn,parseSchema as Po,schemaText as fn}from"../lib/seo-structured-data.js";import{helpPopovers as qa}from"../lib/help-popover.js";import{onceClosed as jt,openMenuAt as Ce,sidebarCount as pt}from"../lib/tool-kit.js";import{pickMedia as bn}from"../lib/media-picker.js";import{openPageClone as vn}from"../lib/page-clone.js";import{openEditorReference as Va}from"../lib/page-editor-reference.js";import{cleanType as Bt,DEFAULT_TYPE as It,EDITOR_FONTS as gn,fontHref as hn,isDefaultType as kn,LINE_HEIGHTS as yn,SIZES as wn,typeVars as _a}from"../lib/editor-type.js";import{getProjectFromHash as Ft}from"../lib/project-context.js";import{attachSmoothCaret as Ka,getCaretPreference as Sn,setCaretPreference as En}from"../lib/smooth-caret.js";import{SCREEN as Oo,bannerSection as Mn,draftIsStale as Tn,draftKey as ze,initialProject as Cn,makeDraft as Ua,problems as Ro,readDraft as xn,snapshot as W}from"../lib/page-editor-arrange.js";import{fmtDateTime as qe}from"../lib/dates.js";import{hasDraft as xe,heldBlockEdits as Pn,hold as Ao,takeHeld as $o,wireHeldRouter as On}from"../lib/editor-hop.js";import{hopTarget as Ha}from"../lib/editor-hop-arrange.js";import{checkNewName as Rn,copyName as Lo,usageText as An}from"../lib/blocks-arrange.js";import{isCollapsed as Ja,setCollapsed as Wa}from"../lib/sidebar-size.js";import{blockTagDefs as Ya,blocksInBody as $n}from"../lib/block-tags.js";import{draftKey as Ve,draftOffer as Ln,extractPlaceholders as Ga,formFromBlock as Xa,payloadFromForm as Za,rawPlaceholders as No,shortcodeFor as Nn,snapshot as Do}from"../lib/block-editor-arrange.js";import{blockTabs as Dn,notHereLine as jn,otherUses as Qa,outlineBlock as Bn,previewBlocks as In,saveQuestion as Fn,savedMessage as zn,usesLine as qn}from"../lib/page-blocks-arrange.js";import{addressFor as es,blockAloneBody as Vn,blockRows as _n,cleanLayout as jo,closeTab as Kn,componentRows as Un,componentStatusText as Hn,componentsInBody as Jn,moveTab as Wn,nextLayout as Yn,openTab as Bo,pageRows as Gn,pageStatusText as Xn,rememberedTabs as Zn,renameTab as Qn,sampleRows as ts,saveAllLabel as er,startingTabs as tr,tabId as X,tabItem as I,tabsToClose as Io,workspaceKey as or}from"../lib/editor-workspace-arrange.js";import{PANES as zt,STARTER_SOURCE as ar,assembleSource as os,attributeFor as sr,draftKey as _e,draftStatus as nr,nameProblem as rr,previewRows as as,readValue as ir,snapshot as Fo,splitSource as ut,writeValue as ss}from"../lib/component-editor-arrange.js";import{attachColourLayer as ns,createPlainEditor as zo}from"../lib/plain-editor.js";import{cleanCodePrefs as qt,codePrefs as lr,DEFAULT_CODE_PREFS as Vt,formatTextarea as Ke,INDENT_SIZES as cr,setCodePrefs as dr}from"../lib/code-format.js";function mt(d){const b=document.createElement("template");return b.innerHTML=d.trim(),b.content.firstElementChild}const pr=qe,rs=d=>qe(d.createdAt)+(d.label?` - ${d.label}`:""),ur=`
|
|
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>
|
|
@@ -68,8 +68,8 @@ import{api as h}from"../api.js";import{CardBuilder as en}from"../lib/card-builde
|
|
|
68
68
|
<span data-help="Enter keeps the line's indentation, and in code goes one deeper after { ( [ or an opening tag; a closing } or </ typed on an empty line steps back."></span></label>
|
|
69
69
|
<label class="pe-settings-switch"><input type="checkbox" class="form-switch" data-model="continueLists.value"> Carry lists and quotes on
|
|
70
70
|
<span data-help="Enter on a list item starts the next one (numbers count up, a task box comes back empty); Enter on an empty item ends the list. Shift+Enter is always a plain new line."></span></label>
|
|
71
|
-
<label class="pe-settings-switch"><input type="checkbox" class="form-switch" data-model="shortTags.value"> Indent short tags
|
|
72
|
-
<span data-help="Shift+Alt+F indents
|
|
71
|
+
<label class="pe-settings-switch"><input type="checkbox" class="form-switch" data-model="shortTags.value"> Indent inside short tags
|
|
72
|
+
<span data-help="Shift+Alt+F indents [grid], [col], [card]\u2026 and what is inside them by how deeply they nest, and Enter after an opening tag goes one deeper. The site never shows the indent - it is taken off before the page is made. Off: short tags and their content stay at the margin."></span></label>
|
|
73
73
|
<label class="pe-settings-switch"><input type="checkbox" class="form-switch" data-model="tables.value"> Line up tables when formatting</label>
|
|
74
74
|
<label class="pe-settings-switch"><input type="checkbox" class="form-switch" data-model="formatOnSave.value"> Format on save
|
|
75
75
|
<span data-help="Save formats the tab you are on first, as Shift+Alt+F would. Ctrl+Z still undoes it."></span></label>
|
|
@@ -122,7 +122,7 @@ The quick brown fox - 0O 1lI {} [] () => ==
|
|
|
122
122
|
.pe-new [hidden] { display: none !important; }
|
|
123
123
|
.pe-new-err { display: block; margin-top: .3rem; color: var(--dm-danger); font-size: var(--dm-text-xs); }
|
|
124
124
|
.pe-new-actions { display: flex; justify-content: flex-end; gap: .5rem; margin-top: 1.25rem; }
|
|
125
|
-
</style>`;function ls({kind:d,projects:b,taken:v,project:i="",name:y="",check:u,create:w,register:k=()=>{}}){return new Promise(x=>{const j=E.slideover({title:d==="block"?"New block":"New component",size:"sm",position:"right"});k(()=>{try{j.close()}catch{}});const c=mt(`<div>${hr}</div>`);for(const D of b)c.querySelector("#pe-new-project").append(new Option(D.name||D.slug,D.slug));const A={name:M.observable(y),project:M.observable(i),busy:M.observable(!1),hint:M.observable(d==="block"?"A block is a reusable piece of HTML with {{placeholders}}, used on pages with its short tag or by a collection display. Its name is its tag - fixed once created.":"A component is a custom element - <dm-name> - with a template, props, a script and a style. Its name is its tag - fixed once created."),placeholder:M.observable(d==="block"?"e.g. team-card":"e.g. pricing-table")};A.error=M.computed(()=>A.name.value.trim()?u(A.name.value.trim(),v):""),A.canCreate=M.computed(()=>!A.busy.value&&!!A.name.value.trim()&&!u(A.name.value.trim(),v));let V="";A.create=async()=>{const D=A.name.peek().trim();if(!(!D||A.error.peek()||A.busy.peek())){A.busy.value=!0;try{await w(D,A.project.peek()),V=D,j.close()}catch(se){E.toast(`Not created: ${se.message||se}`,{type:"error"})}finally{A.busy.value=!1}}},A.cancel=()=>j.close(),A.key=D=>{D.key==="Enter"&&(D.preventDefault(),A.create())};const Y=M.applyBindings(A,c);jt(j,()=>{Y.dispose(),x(V)}),j.element.appendChild(c),c.querySelector("#pe-new-project").value=i,j.open(),setTimeout(()=>{const D=c.querySelector("#pe-new-name");D?.focus(),D?.select()},100)})}let F=null,cs=!1;function kr(){cs||!window.R?.use||(cs=!0,On(),R.use((d,b,v)=>{if(F&&!F.alive()){const u=F;F=null,u.dispose()}const i=b?.path||"",y=d?.path||"";if(!F||!Oo.test(i))return v();if(Oo.test(y)){const u=Ha(y);if(u){F.open(u,y);return}return v()}return F.dirty()?E.confirm("You have unsaved changes. Leave anyway? They are kept as drafts for your next visit.").then(u=>{u?(F?.keepDraft(),v()):F&&history.replaceState({pe:1},"","#"+F.address)}):v()}),M.subscribe("router:afterChange",({to:d}={})=>{if(F&&!Oo.test(d?.path||"")){const b=F;F=null,b.dispose()}}))}const ds="editor_prefs";function Pe(){return S.get(ds)||{viewMode:"split",fullscreen:!1}}const yr=()=>{try{return Pe()||{}}catch{return{}}};function Ue(d,b){const v=Pe();v[d]=b,S.set(ds,v)}const ps="editor-workspace.tabs",us="editor-workspace.folded",ft="editor-workspace.css",wr="/__block-preview",Sr=d=>d.replace(/<\/head>/i,"<style>body.dm-cloaked{opacity:1 !important;transition:none !important}</style></head>");function Er(d,b,v=()=>{}){let i=0;try{i=d.contentWindow.scrollY||0}catch{}d.addEventListener("load",()=>{try{d.contentWindow.scrollTo(0,i)}catch{}v()},{once:!0}),d.srcdoc=b}const Tr=["urlPath","title","description","layout","status","sortOrder","category","visibility","project","theme","seoTitle","seoDesc","seoSchema","dconfig"],Mr=["showInNav","sidebar","breadcrumbs","bundled"],ms=()=>({urlPath:"",title:"",description:"",layout:"",status:"draft",sortOrder:"99",showInNav:!1,sidebar:!1,breadcrumbs:!0,bundled:!1,tags:[],adminTags:[],category:"",visibility:"public",project:"",theme:"",seoTitle:"",seoDesc:"",seoSchema:"",dconfig:"",body:"",css:null});export const pageEditorView={templateUrl:"/admin/js/templates/page-editor.html",async onMount(d){if(kr(),F){const e=F;F=null,e.dispose()}const b=window.location.hash.replace(/^#/,""),v=[],i=e=>(v.push(e),e);let y=()=>!1,u=()=>{},w=()=>{};const k=d.get(0),x=k.querySelector("#pe-workspace"),j={address:b,alive:()=>x?.isConnected===!0,dirty:()=>y(),keepDraft:()=>u(),open:(e,t)=>w(e,t),dispose:()=>{v.splice(0).forEach(e=>{try{e()}catch{}})}};F=j;const c=()=>F===j&&j.alive();if(yr().closeSidebar===!0){const e=document.querySelector("#admin-sidebar");e&&!Ja(e)&&!window.matchMedia("(max-width: 768px)").matches&&(Wa(e,!0,!1),i(()=>{e.isConnected&&Ja(e)&&Wa(e,!1,!1)}))}const A=E.loader(k,{type:"dots"});let[V,Y,D,se,_t,T,ne,Oe]=await Promise.all([h.layouts.get().catch(()=>({})),h.settings.get().catch(()=>({})),h.pages.list().catch(()=>[]),h.projects.list().catch(()=>[]),h.pages.tags().catch(()=>[]),h.listTags.list("pages").then(e=>e.map(t=>t.name)).catch(()=>[]),h.get("/blocks?usage=1").catch(()=>h.blocks.list()).catch(()=>[]),h.components.list().catch(()=>[])]);if(!c()){A.destroy();return}A.destroy();const O=e=>k.querySelector(e);if(D=Array.isArray(D)?D:[],cn(O("#field-theme"),{includeDefault:!0}),await Rr(O("#field-visibility")),!c())return;const re=Object.keys(V||{});Or(O("#field-layout"),V,re[0]||"default");const Vo=e=>re.includes(e)?e:re[0]||e||"default";for(const e of se)for(const t of["#field-project","#block-project","#comp-project"])O(t).append(new Option(e.name||e.slug,e.slug));let ie=Array.isArray(ne)?ne:[],bt=Ya(ie);const vt=new Map(ie.map(e=>[e.name,e.updatedAt])),He=new Map(ie.map(e=>[e.name,e.usage])),be=new Set(ie.map(e=>e.name)),_o=async()=>{const e=await h.get("/blocks?usage=1").catch(()=>null);if(!(!c()||!Array.isArray(e))){ie=e,bt=Ya(ie);for(const t of ie)be.add(t.name),He.set(t.name,t.usage),vt.has(t.name)||vt.set(t.name,t.updatedAt)}},Kt=Array.isArray(Y?.footer?.blocks)?Y.footer.blocks.filter(e=>typeof e=="string"):[];let Je=Array.isArray(Oe)?Oe.filter(e=>typeof e?.name=="string"):[];const le=new Set(Je.map(e=>e.name)),Ko=async()=>{const e=await h.components.list().catch(()=>null);if(!(!c()||!Array.isArray(e))){Je=e.filter(t=>typeof t?.name=="string");for(const t of Je)le.add(t.name)}},Z=Pe(),p={urlPath:M.observable(""),title:M.observable(""),description:M.observable(""),layout:M.observable(Vo("default")),status:M.observable("draft"),sortOrder:M.observable("99"),showInNav:M.observable(!1),sidebar:M.observable(!1),breadcrumbs:M.observable(!0),bundled:M.observable(!1),tags:M.observable([]),adminTags:M.observable([]),category:M.observable(""),visibility:M.observable("public"),project:M.observable(""),theme:M.observable(""),seoTitle:M.observable(""),seoDesc:M.observable(""),seoSchema:M.observable(""),dconfig:M.observable(""),css:M.observable(""),cssOpened:M.observable(!1)},a={bodyRev:M.observable(0),base:M.observable(""),saving:M.observable(!1),viewMode:M.observable(jo(Z.viewMode)),fullscreen:M.observable(!!Z.fullscreen),explorerOn:M.observable(Z.explorer??!window.matchMedia("(max-width: 1024px)").matches),dockOn:M.observable(!!Z.dock),dockTab:M.observable("details"),seoSchemaNote:M.observable(""),cssMode:M.observable(!1),folded:M.observable(!1),previewStatus:M.observable("Showing unsaved changes"),liveWidth:M.observable("full"),versions:M.observable([]),histLoading:M.observable(!1),histError:M.observable(""),compare:M.observable(null),heldNote:M.observable(""),tabs:M.observable([]),active:M.observable(""),curId:M.observable(null),blockTab:M.observable(""),blockPart:M.observable("html"),blockReady:M.observable(!1),blockNote:M.observable(""),blockUses:M.observable(""),blockProject:M.observable(""),blockBundled:M.observable(!1),blockWhere:M.observable(""),compTab:M.observable(""),compPart:M.observable("template"),compReady:M.observable(!1),compNote:M.observable(""),compReadOnly:M.observable(!1),compState:M.observable(""),compErrors:M.observable(""),compProject:M.observable(""),compBundled:M.observable(!1),blockRev:M.observable(0),compRev:M.observable(0),docRev:M.observable(0),filter:M.observable(""),secHere:M.observable(Z.secHere!==!1),secPages:M.observable(Z.secPages!==!1),secBlocks:M.observable(Z.secBlocks!==!1),secComps:M.observable(Z.secComps!==!1),thisPage:M.observable([]),pageRows:M.observable([]),blockRows:M.observable([]),compRows:M.observable([])},_=O("#markdown-editor"),ve=O("#css-editor"),fs=Ka(_),bs=Ka(ve);i(()=>fs.destroy()),i(()=>bs.destroy());const ce=document.createElement("div");ce.className="editor-line-numbers editor-line-numbers--foldable",_.parentElement.insertBefore(ce,_);const We=ns(_,"md"),Ut=ns(ve,"css");i(()=>{We.destroy(),Ut.destroy()});const Uo=()=>{We.paint(),Ut.paint()},Q=dn(_,ce,{onFoldChange:()=>We.paint()});let gt=!1;Q.setGuard(e=>{gt=e});const Ht=()=>Q.getUnfoldedContent(),Jt=()=>{Q.unfoldAll(),a.folded.value=!1,We.paint()},vs=e=>{if(e?.kind==="page")Jt(),Ke(_,"md");else if(e?.kind==="block")oe&&Ke(oe.textarea,"html"),me&&Ke(me.textarea,"css");else if(e?.kind==="component"){if(N.get(e.id)?.readOnly)return;for(const[o,s]of Object.entries({template:"html",props:"json",script:"js",style:"css"}))ae[o]&&Ke(ae[o].textarea,s)}},Ho=e=>{e.error?E.toast(e.error,{type:"warning"}):E.toast(e.unchanged?"Already tidy":"Formatted",{type:"info",duration:900})},Jo=M.observable(Bt(Z.type)),Wo=M.observable(qt(Z.code));i(M.effect(()=>{const e=Wo.value;dr(e),O("#editor-body")?.style.setProperty("--pe-tab",String(e.size))})),i(M.effect(()=>{const e=Jo.value,t=O("#editor-body");if(t){is(e.font);for(const[o,s]of Object.entries(_a(e)))t.style.setProperty(o,s);t.classList.toggle("pe-nowrap",!e.wrap),_.wrap=e.wrap?"soft":"off",ve.wrap=e.wrap?"soft":"off",requestAnimationFrame(()=>{c()&&Uo()})}}));let Yo=null;i(()=>{try{Yo?.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,seoSchema:p.seoSchema.value,dconfig:p.dconfig.value,body:Ht(),css:null}),Go=()=>M.untracked?M.untracked(K):K(),ee=M.observable(""),ht=()=>p.cssOpened.peek()&&p.css.peek()!==ee.peek(),U=new Map;let m=null;const L=new Map,N=new Map;let P=[];const de=e=>Do(e.form)!==e.base,ge=()=>[...L.entries()].filter(([,e])=>de(e)).map(([e])=>e),he=e=>!e.readOnly&&Fo({name:e.name,...e.form})!==e.base,Ye=()=>[...N.values()].filter(he).map(e=>e.name),Wt=()=>W(Go())!==a.base.peek(),Yt=e=>e.loaded&&(e===m?Wt():W(e.state)!==e.base),Re=()=>[...U.values()].filter(Yt),gs=M.computed(()=>W(K())!==a.base.value),hs=M.computed(()=>p.cssOpened.value&&p.css.value!==ee.value),ks=M.computed(()=>(a.blockRev.value,a.compRev.value,a.docRev.value,W(K())!==a.base.value||p.cssOpened.value&&p.css.value!==ee.value||Re().some(e=>e!==m)||ge().length>0||Ye().length>0)),ys=M.computed(()=>{a.blockRev.value,a.compRev.value;const e=I(a.active.value);if(e?.kind==="block"){const t=L.get(e.id);return!!(t&&de(t))}if(e?.kind==="component"){const t=N.get(e.id);return!!(t&&he(t))}return W(K())!==a.base.value||p.cssOpened.value&&p.css.value!==ee.value}),ws=M.computed(()=>a.curId.value!==null),Xo=M.computed(()=>I(a.active.value)?.kind||""),Zo=M.computed(()=>!!a.curId.value&&I(a.active.value)?.kind==="page"),Ss=M.computed(()=>p.urlPath.value.trim()||a.curId.value||""),Es=M.computed(()=>Ro({urlPath:p.urlPath.value,dconfig:p.dconfig.value})[0]?.text||""),Ts=M.computed(()=>{a.docRev.value;const e=I(a.active.value);return e?e.kind==="block"?a.blockUses.value:e.kind==="component"?Hn({name:e.id,readOnly:a.compReadOnly.value,state:a.compReady.value?a.compState.value:""}):Xn({urlPath:p.urlPath.value,status:p.status.value,isEdit:!!a.curId.value}):""}),Qo=M.computed(()=>a.compTab.value?"component":a.blockTab.value?"block":a.curId.value!==null?"page":""),Ms=M.computed(()=>a.blockTab.value?`[${a.blockTab.value} /]`:""),Cs=M.computed(()=>({compiling:"Compiling\u2026",ok:"Compiles",error:"Does not compile"})[a.compState.value]||""),xs=M.computed(()=>a.compTab.value?`<dm-${a.compTab.value}>`:""),Ps=M.computed(()=>a.versions.value.some(e=>e.checked.value)),Os=M.computed(()=>a.versions.value.length>0&&a.versions.value.every(e=>e.checked.value)),Rs=M.computed(()=>{const e=a.versions.value.filter(t=>t.checked.value).length;return e>0&&e<a.versions.value.length});y=()=>Re().length>0||ge().length>0||Ye().length>0||ht();const Gt=(e,t,o)=>({title:String(e.title||"").trim()||"Untitled",description:String(e.description||"").trim(),layout:e.layout,status:e.status,sortOrder:parseInt(String(e.sortOrder),10)||99,showInNav:!!e.showInNav,sidebar:!!e.sidebar,...e.breadcrumbs?{}:{breadcrumbs:!1},...e.bundled?{bundled:!0}:{},tags:Array.isArray(e.tags)?e.tags:[],...t?.isEdit&&JSON.stringify(e.adminTags)===t.savedAdminTags?{}:{adminTags:Array.isArray(e.adminTags)?e.adminTags:[]},category:String(e.category||"").trim()||null,visibility:e.visibility||"public",project:e.project||"",seo:{title:String(e.seoTitle||"").trim(),description:String(e.seoDesc||"").trim(),...ea(e)?{schema:ea(e)}:t?.isEdit?{schema:null}:{}},dconfig:o,...e.theme?{theme:e.theme}:{}}),ea=e=>Po(e.seoSchema).value,Xt=e=>{const t=String(e.dconfig||"").trim();if(!t)return null;try{return JSON.parse(t)}catch{return null}},As=()=>p.urlPath.peek().trim()||m?.id||"/";let kt=[],ta=[];const yt=e=>kt.includes(e)||Kt.includes(e);let Ae=new Set;const wt=()=>{const e=[...new Set([...kt,...Kt,...L.keys()])],t=Pn(e.filter(s=>!L.has(s)),vt);Ae=new Set(Object.keys(t));const o=Ae.size?`Preview shows unsaved changes to ${[...Ae].join(", ")}`:"";return o!==a.heldNote.peek()&&(a.heldNote.value=o),In(t,[...L.entries()].map(([s,r])=>({name:s,content:r.form.content,css:r.form.css,dirty:de(r)})))};let Zt="";const $s=()=>{oa();const e=Zt;if(e){Zt="";try{$e.contentDocument?.querySelector(`[data-block="${CSS.escape(e)}"]`)?.scrollIntoView({block:"center"})}catch{}}},$e=O("#markdown-preview");h.settings.getCustomCss().then(({css:e})=>{if(!c())return;ee.value=e||"";let t=null;try{t=S.get(ft)}catch{}typeof t=="string"&&t!==ee.peek()?(ia(),p.css.value=t,E.toast("Your unsaved custom CSS is back - the CSS button shows it.",{type:"info"})):p.cssOpened.peek()||(p.css.value=ee.peek()),J(ft)}).catch(()=>{});const oa=()=>{const e=$e?.contentDocument;if(!e?.head)return;const t=p.cssOpened.peek()?p.css.peek():"";if(!t)return;let o=e.getElementById("preview-custom-css");o||(o=e.createElement("style"),o.id="preview-custom-css",e.head.appendChild(o)),o.textContent=t};let Qt=null,eo=0;const H=(e=!1)=>{clearTimeout(Qt),Qt=setTimeout(async()=>{if(!$e||a.viewMode.peek()==="write"||!a.active.peek()||a.compTab.peek())return;const t=++eo,o=a.blockTab.peek();a.previewStatus.value="Rendering\u2026";try{let s;if(o&&(!m||!yt(o)))({html:s}=await h.pages.previewFull(wr,{title:o,layout:"blank",breadcrumbs:!1},Vn(o,_s(o)),wt()));else if(m){const r=K();({html:s}=await h.pages.previewFull(As(),Gt(r,m,Xt(r)),r.body,wt()))}else{$e.srcdoc="";return}if(!c()||t!==eo)return;Er($e,Bn(Sr(s),o),$s),a.previewStatus.value="Showing unsaved changes"}catch(s){if(!c()||t!==eo)return;a.previewStatus.value="Preview failed - "+(s?.message||"unknown error"),$e.srcdoc='<!doctype html><meta charset="utf-8"><body style="font:14px system-ui;padding:1rem;color:#b91c1c;">Preview failed: '+String(s?.message||"unknown error").replace(/[<&]/g,r=>r==="<"?"<":"&")+"</body>"}},e?0:500)};i(()=>clearTimeout(Qt));const to=an(d.find("#markdown-editor"),d.find("#editor-toolbar"),{spacerDefault:Y?.layoutOptions?.spacerSize??40,beforeFormat:()=>Jt()});rn(_,{blocks:()=>bt,openBlock:e=>Be(e)});const Ge=k.querySelector(".editor-toolbar-right");Ge.querySelectorAll(".editor-view-btn[data-mode]").forEach(e=>{const t=e.getAttribute("data-mode");e.classList.remove("active"),e.setAttribute("data-bind-class",`viewMode.value === '${t}' && 'active'`),e.setAttribute("data-on-click",`setMode('${t}')`)}),O("#fullscreen-btn")?.setAttribute("data-on-click","toggleFullscreen");const Ls=mt(`
|
|
125
|
+
</style>`;function ls({kind:d,projects:b,taken:v,project:i="",name:y="",check:u,create:w,register:k=()=>{}}){return new Promise(x=>{const j=E.slideover({title:d==="block"?"New block":"New component",size:"sm",position:"right"});k(()=>{try{j.close()}catch{}});const c=mt(`<div>${hr}</div>`);for(const D of b)c.querySelector("#pe-new-project").append(new Option(D.name||D.slug,D.slug));const A={name:M.observable(y),project:M.observable(i),busy:M.observable(!1),hint:M.observable(d==="block"?"A block is a reusable piece of HTML with {{placeholders}}, used on pages with its short tag or by a collection display. Its name is its tag - fixed once created.":"A component is a custom element - <dm-name> - with a template, props, a script and a style. Its name is its tag - fixed once created."),placeholder:M.observable(d==="block"?"e.g. team-card":"e.g. pricing-table")};A.error=M.computed(()=>A.name.value.trim()?u(A.name.value.trim(),v):""),A.canCreate=M.computed(()=>!A.busy.value&&!!A.name.value.trim()&&!u(A.name.value.trim(),v));let V="";A.create=async()=>{const D=A.name.peek().trim();if(!(!D||A.error.peek()||A.busy.peek())){A.busy.value=!0;try{await w(D,A.project.peek()),V=D,j.close()}catch(se){E.toast(`Not created: ${se.message||se}`,{type:"error"})}finally{A.busy.value=!1}}},A.cancel=()=>j.close(),A.key=D=>{D.key==="Enter"&&(D.preventDefault(),A.create())};const Y=M.applyBindings(A,c);jt(j,()=>{Y.dispose(),x(V)}),j.element.appendChild(c),c.querySelector("#pe-new-project").value=i,j.open(),setTimeout(()=>{const D=c.querySelector("#pe-new-name");D?.focus(),D?.select()},100)})}let F=null,cs=!1;function kr(){cs||!window.R?.use||(cs=!0,On(),R.use((d,b,v)=>{if(F&&!F.alive()){const u=F;F=null,u.dispose()}const i=b?.path||"",y=d?.path||"";if(!F||!Oo.test(i))return v();if(Oo.test(y)){const u=Ha(y);if(u){F.open(u,y);return}return v()}return F.dirty()?E.confirm("You have unsaved changes. Leave anyway? They are kept as drafts for your next visit.").then(u=>{u?(F?.keepDraft(),v()):F&&history.replaceState({pe:1},"","#"+F.address)}):v()}),M.subscribe("router:afterChange",({to:d}={})=>{if(F&&!Oo.test(d?.path||"")){const b=F;F=null,b.dispose()}}))}const ds="editor_prefs";function Pe(){return S.get(ds)||{viewMode:"split",fullscreen:!1}}const yr=()=>{try{return Pe()||{}}catch{return{}}};function Ue(d,b){const v=Pe();v[d]=b,S.set(ds,v)}const ps="editor-workspace.tabs",us="editor-workspace.folded",ft="editor-workspace.css",wr="/__block-preview",Sr=d=>d.replace(/<\/head>/i,"<style>body.dm-cloaked{opacity:1 !important;transition:none !important}</style></head>");function Er(d,b,v=()=>{}){let i=0;try{i=d.contentWindow.scrollY||0}catch{}d.addEventListener("load",()=>{try{d.contentWindow.scrollTo(0,i)}catch{}v()},{once:!0}),d.srcdoc=b}const Mr=["urlPath","title","description","layout","status","sortOrder","category","visibility","project","theme","seoTitle","seoDesc","seoSchema","dconfig"],Tr=["showInNav","sidebar","breadcrumbs","bundled"],ms=()=>({urlPath:"",title:"",description:"",layout:"",status:"draft",sortOrder:"99",showInNav:!1,sidebar:!1,breadcrumbs:!0,bundled:!1,tags:[],adminTags:[],category:"",visibility:"public",project:"",theme:"",seoTitle:"",seoDesc:"",seoSchema:"",dconfig:"",body:"",css:null});export const pageEditorView={templateUrl:"/admin/js/templates/page-editor.html",async onMount(d){if(kr(),F){const e=F;F=null,e.dispose()}const b=window.location.hash.replace(/^#/,""),v=[],i=e=>(v.push(e),e);let y=()=>!1,u=()=>{},w=()=>{};const k=d.get(0),x=k.querySelector("#pe-workspace"),j={address:b,alive:()=>x?.isConnected===!0,dirty:()=>y(),keepDraft:()=>u(),open:(e,t)=>w(e,t),dispose:()=>{v.splice(0).forEach(e=>{try{e()}catch{}})}};F=j;const c=()=>F===j&&j.alive();if(yr().closeSidebar===!0){const e=document.querySelector("#admin-sidebar");e&&!Ja(e)&&!window.matchMedia("(max-width: 768px)").matches&&(Wa(e,!0,!1),i(()=>{e.isConnected&&Ja(e)&&Wa(e,!1,!1)}))}const A=E.loader(k,{type:"dots"});let[V,Y,D,se,_t,T,ne,Oe]=await Promise.all([h.layouts.get().catch(()=>({})),h.settings.get().catch(()=>({})),h.pages.list().catch(()=>[]),h.projects.list().catch(()=>[]),h.pages.tags().catch(()=>[]),h.listTags.list("pages").then(e=>e.map(t=>t.name)).catch(()=>[]),h.get("/blocks?usage=1").catch(()=>h.blocks.list()).catch(()=>[]),h.components.list().catch(()=>[])]);if(!c()){A.destroy();return}A.destroy();const O=e=>k.querySelector(e);if(D=Array.isArray(D)?D:[],cn(O("#field-theme"),{includeDefault:!0}),await Rr(O("#field-visibility")),!c())return;const re=Object.keys(V||{});Or(O("#field-layout"),V,re[0]||"default");const Vo=e=>re.includes(e)?e:re[0]||e||"default";for(const e of se)for(const t of["#field-project","#block-project","#comp-project"])O(t).append(new Option(e.name||e.slug,e.slug));let ie=Array.isArray(ne)?ne:[],bt=Ya(ie);const vt=new Map(ie.map(e=>[e.name,e.updatedAt])),He=new Map(ie.map(e=>[e.name,e.usage])),be=new Set(ie.map(e=>e.name)),_o=async()=>{const e=await h.get("/blocks?usage=1").catch(()=>null);if(!(!c()||!Array.isArray(e))){ie=e,bt=Ya(ie);for(const t of ie)be.add(t.name),He.set(t.name,t.usage),vt.has(t.name)||vt.set(t.name,t.updatedAt)}},Kt=Array.isArray(Y?.footer?.blocks)?Y.footer.blocks.filter(e=>typeof e=="string"):[];let Je=Array.isArray(Oe)?Oe.filter(e=>typeof e?.name=="string"):[];const le=new Set(Je.map(e=>e.name)),Ko=async()=>{const e=await h.components.list().catch(()=>null);if(!(!c()||!Array.isArray(e))){Je=e.filter(t=>typeof t?.name=="string");for(const t of Je)le.add(t.name)}},Z=Pe(),p={urlPath:M.observable(""),title:M.observable(""),description:M.observable(""),layout:M.observable(Vo("default")),status:M.observable("draft"),sortOrder:M.observable("99"),showInNav:M.observable(!1),sidebar:M.observable(!1),breadcrumbs:M.observable(!0),bundled:M.observable(!1),tags:M.observable([]),adminTags:M.observable([]),category:M.observable(""),visibility:M.observable("public"),project:M.observable(""),theme:M.observable(""),seoTitle:M.observable(""),seoDesc:M.observable(""),seoSchema:M.observable(""),dconfig:M.observable(""),css:M.observable(""),cssOpened:M.observable(!1)},a={bodyRev:M.observable(0),base:M.observable(""),saving:M.observable(!1),viewMode:M.observable(jo(Z.viewMode)),fullscreen:M.observable(!!Z.fullscreen),explorerOn:M.observable(Z.explorer??!window.matchMedia("(max-width: 1024px)").matches),dockOn:M.observable(!!Z.dock),dockTab:M.observable("details"),seoSchemaNote:M.observable(""),cssMode:M.observable(!1),folded:M.observable(!1),previewStatus:M.observable("Showing unsaved changes"),liveWidth:M.observable("full"),versions:M.observable([]),histLoading:M.observable(!1),histError:M.observable(""),compare:M.observable(null),heldNote:M.observable(""),tabs:M.observable([]),active:M.observable(""),curId:M.observable(null),blockTab:M.observable(""),blockPart:M.observable("html"),blockReady:M.observable(!1),blockNote:M.observable(""),blockUses:M.observable(""),blockProject:M.observable(""),blockBundled:M.observable(!1),blockWhere:M.observable(""),compTab:M.observable(""),compPart:M.observable("template"),compReady:M.observable(!1),compNote:M.observable(""),compReadOnly:M.observable(!1),compState:M.observable(""),compErrors:M.observable(""),compProject:M.observable(""),compBundled:M.observable(!1),blockRev:M.observable(0),compRev:M.observable(0),docRev:M.observable(0),filter:M.observable(""),secHere:M.observable(Z.secHere!==!1),secPages:M.observable(Z.secPages!==!1),secBlocks:M.observable(Z.secBlocks!==!1),secComps:M.observable(Z.secComps!==!1),thisPage:M.observable([]),pageRows:M.observable([]),blockRows:M.observable([]),compRows:M.observable([])},_=O("#markdown-editor"),ve=O("#css-editor"),fs=Ka(_),bs=Ka(ve);i(()=>fs.destroy()),i(()=>bs.destroy());const ce=document.createElement("div");ce.className="editor-line-numbers editor-line-numbers--foldable",_.parentElement.insertBefore(ce,_);const We=ns(_,"md"),Ut=ns(ve,"css");i(()=>{We.destroy(),Ut.destroy()});const Uo=()=>{We.paint(),Ut.paint()},Q=dn(_,ce,{onFoldChange:()=>We.paint()});let gt=!1;Q.setGuard(e=>{gt=e});const Ht=()=>Q.getUnfoldedContent(),Jt=()=>{Q.unfoldAll(),a.folded.value=!1,We.paint()},vs=e=>{if(e?.kind==="page")Jt(),Ke(_,"md");else if(e?.kind==="block")oe&&Ke(oe.textarea,"html"),me&&Ke(me.textarea,"css");else if(e?.kind==="component"){if(N.get(e.id)?.readOnly)return;for(const[o,s]of Object.entries({template:"html",props:"json",script:"js",style:"css"}))ae[o]&&Ke(ae[o].textarea,s)}},Ho=e=>{e.error?E.toast(e.error,{type:"warning"}):E.toast(e.unchanged?"Already tidy":"Formatted",{type:"info",duration:900})},Jo=M.observable(Bt(Z.type)),Wo=M.observable(qt(Z.code));i(M.effect(()=>{const e=Wo.value;dr(e),O("#editor-body")?.style.setProperty("--pe-tab",String(e.size))})),i(M.effect(()=>{const e=Jo.value,t=O("#editor-body");if(t){is(e.font);for(const[o,s]of Object.entries(_a(e)))t.style.setProperty(o,s);t.classList.toggle("pe-nowrap",!e.wrap),_.wrap=e.wrap?"soft":"off",ve.wrap=e.wrap?"soft":"off",requestAnimationFrame(()=>{c()&&Uo()})}}));let Yo=null;i(()=>{try{Yo?.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,seoSchema:p.seoSchema.value,dconfig:p.dconfig.value,body:Ht(),css:null}),Go=()=>M.untracked?M.untracked(K):K(),ee=M.observable(""),ht=()=>p.cssOpened.peek()&&p.css.peek()!==ee.peek(),U=new Map;let m=null;const L=new Map,N=new Map;let P=[];const de=e=>Do(e.form)!==e.base,ge=()=>[...L.entries()].filter(([,e])=>de(e)).map(([e])=>e),he=e=>!e.readOnly&&Fo({name:e.name,...e.form})!==e.base,Ye=()=>[...N.values()].filter(he).map(e=>e.name),Wt=()=>W(Go())!==a.base.peek(),Yt=e=>e.loaded&&(e===m?Wt():W(e.state)!==e.base),Re=()=>[...U.values()].filter(Yt),gs=M.computed(()=>W(K())!==a.base.value),hs=M.computed(()=>p.cssOpened.value&&p.css.value!==ee.value),ks=M.computed(()=>(a.blockRev.value,a.compRev.value,a.docRev.value,W(K())!==a.base.value||p.cssOpened.value&&p.css.value!==ee.value||Re().some(e=>e!==m)||ge().length>0||Ye().length>0)),ys=M.computed(()=>{a.blockRev.value,a.compRev.value;const e=I(a.active.value);if(e?.kind==="block"){const t=L.get(e.id);return!!(t&&de(t))}if(e?.kind==="component"){const t=N.get(e.id);return!!(t&&he(t))}return W(K())!==a.base.value||p.cssOpened.value&&p.css.value!==ee.value}),ws=M.computed(()=>a.curId.value!==null),Xo=M.computed(()=>I(a.active.value)?.kind||""),Zo=M.computed(()=>!!a.curId.value&&I(a.active.value)?.kind==="page"),Ss=M.computed(()=>p.urlPath.value.trim()||a.curId.value||""),Es=M.computed(()=>Ro({urlPath:p.urlPath.value,dconfig:p.dconfig.value})[0]?.text||""),Ms=M.computed(()=>{a.docRev.value;const e=I(a.active.value);return e?e.kind==="block"?a.blockUses.value:e.kind==="component"?Hn({name:e.id,readOnly:a.compReadOnly.value,state:a.compReady.value?a.compState.value:""}):Xn({urlPath:p.urlPath.value,status:p.status.value,isEdit:!!a.curId.value}):""}),Qo=M.computed(()=>a.compTab.value?"component":a.blockTab.value?"block":a.curId.value!==null?"page":""),Ts=M.computed(()=>a.blockTab.value?`[${a.blockTab.value} /]`:""),Cs=M.computed(()=>({compiling:"Compiling\u2026",ok:"Compiles",error:"Does not compile"})[a.compState.value]||""),xs=M.computed(()=>a.compTab.value?`<dm-${a.compTab.value}>`:""),Ps=M.computed(()=>a.versions.value.some(e=>e.checked.value)),Os=M.computed(()=>a.versions.value.length>0&&a.versions.value.every(e=>e.checked.value)),Rs=M.computed(()=>{const e=a.versions.value.filter(t=>t.checked.value).length;return e>0&&e<a.versions.value.length});y=()=>Re().length>0||ge().length>0||Ye().length>0||ht();const Gt=(e,t,o)=>({title:String(e.title||"").trim()||"Untitled",description:String(e.description||"").trim(),layout:e.layout,status:e.status,sortOrder:parseInt(String(e.sortOrder),10)||99,showInNav:!!e.showInNav,sidebar:!!e.sidebar,...e.breadcrumbs?{}:{breadcrumbs:!1},...e.bundled?{bundled:!0}:{},tags:Array.isArray(e.tags)?e.tags:[],...t?.isEdit&&JSON.stringify(e.adminTags)===t.savedAdminTags?{}:{adminTags:Array.isArray(e.adminTags)?e.adminTags:[]},category:String(e.category||"").trim()||null,visibility:e.visibility||"public",project:e.project||"",seo:{title:String(e.seoTitle||"").trim(),description:String(e.seoDesc||"").trim(),...ea(e)?{schema:ea(e)}:t?.isEdit?{schema:null}:{}},dconfig:o,...e.theme?{theme:e.theme}:{}}),ea=e=>Po(e.seoSchema).value,Xt=e=>{const t=String(e.dconfig||"").trim();if(!t)return null;try{return JSON.parse(t)}catch{return null}},As=()=>p.urlPath.peek().trim()||m?.id||"/";let kt=[],ta=[];const yt=e=>kt.includes(e)||Kt.includes(e);let Ae=new Set;const wt=()=>{const e=[...new Set([...kt,...Kt,...L.keys()])],t=Pn(e.filter(s=>!L.has(s)),vt);Ae=new Set(Object.keys(t));const o=Ae.size?`Preview shows unsaved changes to ${[...Ae].join(", ")}`:"";return o!==a.heldNote.peek()&&(a.heldNote.value=o),In(t,[...L.entries()].map(([s,r])=>({name:s,content:r.form.content,css:r.form.css,dirty:de(r)})))};let Zt="";const $s=()=>{oa();const e=Zt;if(e){Zt="";try{$e.contentDocument?.querySelector(`[data-block="${CSS.escape(e)}"]`)?.scrollIntoView({block:"center"})}catch{}}},$e=O("#markdown-preview");h.settings.getCustomCss().then(({css:e})=>{if(!c())return;ee.value=e||"";let t=null;try{t=S.get(ft)}catch{}typeof t=="string"&&t!==ee.peek()?(ia(),p.css.value=t,E.toast("Your unsaved custom CSS is back - the CSS button shows it.",{type:"info"})):p.cssOpened.peek()||(p.css.value=ee.peek()),J(ft)}).catch(()=>{});const oa=()=>{const e=$e?.contentDocument;if(!e?.head)return;const t=p.cssOpened.peek()?p.css.peek():"";if(!t)return;let o=e.getElementById("preview-custom-css");o||(o=e.createElement("style"),o.id="preview-custom-css",e.head.appendChild(o)),o.textContent=t};let Qt=null,eo=0;const H=(e=!1)=>{clearTimeout(Qt),Qt=setTimeout(async()=>{if(!$e||a.viewMode.peek()==="write"||!a.active.peek()||a.compTab.peek())return;const t=++eo,o=a.blockTab.peek();a.previewStatus.value="Rendering\u2026";try{let s;if(o&&(!m||!yt(o)))({html:s}=await h.pages.previewFull(wr,{title:o,layout:"blank",breadcrumbs:!1},Vn(o,_s(o)),wt()));else if(m){const r=K();({html:s}=await h.pages.previewFull(As(),Gt(r,m,Xt(r)),r.body,wt()))}else{$e.srcdoc="";return}if(!c()||t!==eo)return;Er($e,Bn(Sr(s),o),$s),a.previewStatus.value="Showing unsaved changes"}catch(s){if(!c()||t!==eo)return;a.previewStatus.value="Preview failed - "+(s?.message||"unknown error"),$e.srcdoc='<!doctype html><meta charset="utf-8"><body style="font:14px system-ui;padding:1rem;color:#b91c1c;">Preview failed: '+String(s?.message||"unknown error").replace(/[<&]/g,r=>r==="<"?"<":"&")+"</body>"}},e?0:500)};i(()=>clearTimeout(Qt));const to=an(d.find("#markdown-editor"),d.find("#editor-toolbar"),{spacerDefault:Y?.layoutOptions?.spacerSize??40,beforeFormat:()=>Jt()});rn(_,{blocks:()=>bt,openBlock:e=>Be(e)});const Ge=k.querySelector(".editor-toolbar-right");Ge.querySelectorAll(".editor-view-btn[data-mode]").forEach(e=>{const t=e.getAttribute("data-mode");e.classList.remove("active"),e.setAttribute("data-bind-class",`viewMode.value === '${t}' && 'active'`),e.setAttribute("data-on-click",`setMode('${t}')`)}),O("#fullscreen-btn")?.setAttribute("data-on-click","toggleFullscreen");const Ls=mt(`
|
|
126
126
|
<span class="pe-toolbar-extras" style="display:contents">
|
|
127
127
|
<button type="button" class="editor-view-btn" data-tip="Edit Custom CSS" data-on-click="toggleCss"
|
|
128
128
|
data-bind-class="cssMode.value && 'active'"><span>CSS</span></button>
|
|
@@ -134,8 +134,8 @@ The quick brown fox - 0O 1lI {} [] () => ==
|
|
|
134
134
|
<span class="pe-fold-icon" hidden data-bind-hidden="!folded.value"><span data-icon="chevrons-right" data-icon-size="10"></span></span>
|
|
135
135
|
</button>
|
|
136
136
|
<span class="editor-toolbar-sep"></span>
|
|
137
|
-
</span>`);Ge.prepend(Ls),Ge.before(O(".pe-block-bar")),Ge.before(O(".pe-comp-bar"));const aa=e=>(Array.isArray(e)?e:[]).map(t=>({value:t,label:t})),sa=O("#field-tags");let St=null;St=sa?E.pillbox(sa,{data:aa(_t),value:[],creatable:!0,searchable:!0,placeholder:"Add tag\u2026",onChange:()=>{St&&(p.tags.value=[...St.getValue()])}}):null;const na=O("#field-admin-tags");let Et=null;Et=na?E.pillbox(na,{data:aa(T),value:[],creatable:!0,searchable:!0,placeholder:"Add admin tag\u2026",onChange:()=>{Et&&(p.adminTags.value=[...Et.getValue()])}}):null;const ra=()=>{a.bodyRev.value=a.bodyRev.peek()+1},Le=()=>{a.blockRev.value=a.blockRev.peek()+1},ke=()=>{a.compRev.value=a.compRev.peek()+1},Xe=()=>{a.docRev.value=a.docRev.peek()+1},ia=()=>{p.cssOpened.peek()||(p.css.peek()||(p.css.value=ee.peek()),p.cssOpened.value=!0,on(ve,{language:"css"}))},Ze=e=>{for(const t of Tr)p[t].value=e[t]==null?"":String(e[t]);for(const t of Mr)p[t].value=!!e[t];p.tags.value=Array.isArray(e.tags)?e.tags:[],St?.setValue(p.tags.peek()),Array.isArray(e.adminTags)&&(p.adminTags.value=e.adminTags),Et?.setValue(p.adminTags.peek()),qo(O("#field-visibility"),p.visibility.peek()),Q.unfoldAll(),a.folded.value=!1,gt=!0,_.value=e.body==null?"":String(e.body),gt=!1,Q.refresh(),Uo(),ra()},la=(e,t)=>({urlPath:t||"",title:e?.title||"",description:e?.description||"",layout:Vo(e?.layout||"default"),status:e?.status||"draft",sortOrder:String(e?.sortOrder??99),showInNav:!!e?.showInNav,sidebar:!!e?.sidebar,breadcrumbs:e?e.breadcrumbs!==!1:!0,bundled:!!e?.bundled,tags:Array.isArray(e?.tags)?e.tags:[],adminTags:Array.isArray(e?.adminTags)?e.adminTags:[],category:e?.category||"",visibility:typeof e?.visibility=="string"&&e.visibility?e.visibility:"public",project:Cn({isEdit:!!t,page:e,projects:se,fromHash:t?"":Ft()||"",urlPath:t||""}),theme:e?.theme||"",seoTitle:e?.seo?.title||"",seoDesc:e?.seo?.description||"",seoSchema:fn(e?.seo?.schema),dconfig:e?.dconfig?JSON.stringify(e.dconfig,null,2):"",body:e?.content||"",css:null}),ca=()=>{m&&(m.state=K(),m.at=Date.now())},da=e=>{m!==e&&(ca(),m=e,Ze(e.state),a.base.value=e.base,a.curId.value=e.id,e.at=Date.now(),Mt(),Xe())},Ns=()=>{ca(),m=null;const e=ms();Ze(e),a.base.value=W(e),a.curId.value=null,Mt(),Xe()};Ze(ms()),a.base.value=W(K());const J=e=>{try{S.remove?S.remove(e):S.set(e,null)}catch{}},pa=(e,t)=>({name:e.name,...e.form,base:e.base,at:t});u=()=>{try{for(const t of Re())S.set(ze(t.id),Ua(t===m?Go():t.state,t.base));ht()&&S.set(ft,p.css.peek());const e=new Date().toISOString();for(const t of ge()){const o=L.get(t);S.set(Ve(t),{form:{...o.form},base:o.base,at:e})}for(const t of Ye())S.set(_e(t),pa(N.get(t),e))}catch{}};const ua=e=>{y()&&(u(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",ua),i(()=>window.removeEventListener("beforeunload",ua));let Qe=!1,Tt=null;const Mt=()=>{Qe=!1,Tt=null,a.versions.value=[],a.compare.value=null,a.histError.value="",a.dockOn.peek()&&a.dockTab.peek()==="history"&&(m?.isEdit?pe():a.dockTab.value="details")},pe=async()=>{const e=m;if(e?.isEdit){Qe=!0,a.histLoading.value=!0,a.histError.value="";try{const t=(await h.versions.list(e.id)).versions||[];if(!c()||m!==e)return;a.versions.value=t.map(o=>({...o,when:qe(o.createdAt),author:o.author||"",label:o.label||"",who:[o.author,o.label].filter(Boolean).join(" - "),checked:M.observable(!1)}))}catch(t){m===e&&(a.histError.value="Failed to load versions: "+t.message)}finally{m===e&&(a.histLoading.value=!1)}}},oo=e=>a.versions.peek().find(t=>t.filename===e),ao=async e=>{const t=m;try{const o=await h.versions.get(t.id,e.filename);if(!c()||m!==t)return;Tt=e.filename,a.compare.value={label:rs(e),old:o.content,current:Ht()}}catch(o){E.toast("Failed to load version: "+o.message,{type:"error"})}},Ds=async()=>{const e=m;if(!e?.isEdit)return;const t=await h.pages.get(e.id).catch(()=>null);if(!c()||m!==e||!t)return;e.page=t;const o=la(t,e.id);e.base=W(o),e.savedAdminTags=JSON.stringify(o.adminTags),Ze(o),a.base.value=e.base,Mt(),a.dockOn.peek()&&a.dockTab.peek()==="history"&&pe(),H()},ma=async e=>{if(!(!e||!await E.confirm("Restore this version? The current page content will be overwritten (a pre-restore snapshot will be saved automatically).")))try{await h.versions.restore(m.id,e),E.toast("Page restored.",{type:"success"}),await Ds()}catch(t){E.toast("Restore failed: "+t.message,{type:"error"})}},js=async e=>{if(await E.confirm("Delete this version? This cannot be undone."))try{await h.versions.delete(m.id,e.filename),E.toast("Version deleted.",{type:"success"}),await pe()}catch(t){E.toast("Failed to delete: "+t.message,{type:"error"})}},Ne=new Map,Bs=e=>{if(!Ne.has(e)){const t=I(e);Ne.set(e,{id:e,kind:t.kind,kindText:t.kind==="page"?"Page":t.kind==="component"?"Component":"Block",label:M.observable(""),title:M.observable(""),active:M.observable(!1),dirty:M.observable(!1)})}return Ne.get(e)},fa=e=>{const t=e===m?p.title.peek():e?.loaded?e.state.title:D.find(o=>o.urlPath===e?.id)?.title;return String(t||"").trim()||e?.id||"New page"};let ba=!0,va="";const Is=()=>{if(ba)return;const e={ids:P,active:a.active.peek()},t=JSON.stringify(e);if(t!==va){va=t;try{S.set(ps,e)}catch{}}},q=()=>{const e=P.map(Bs);for(const t of e){const o=I(t.id);let s,r;if(o.kind==="page"){const l=U.get(o.id);s=fa(l),r=l?.loaded?Yt(l):xe(o),t.title.value=o.id?`${s} (${o.id})`:"New page"}else if(o.kind==="component"){const l=N.get(o.id);s=o.id,r=l?he(l):xe(o),t.title.value=`Component <dm-${o.id}>`}else{const l=L.get(o.id);s=o.id,r=l?de(l):Ae.has(o.id)||xe(o),t.title.value=`Block ${o.id}`}t.label.peek()!==s&&(t.label.value=s),t.dirty.peek()!==r&&(t.dirty.value=r);const n=t.id===a.active.peek();t.active.peek()!==n&&(t.active.value=n)}e.map(t=>t.id).join("|")!==a.tabs.peek().map(t=>t.id).join("|")&&(a.tabs.value=e),Is()};let ye=new Set;try{const e=S.get(us);Array.isArray(e)&&(ye=new Set(e.filter(t=>typeof t=="string")))}catch{}const ga=new Map,so=e=>{let t=ga.get(e.key);return t?(t.label.peek()!==e.label&&(t.label.value=e.label),t.meta.peek()!==(e.meta||"")&&(t.meta.value=e.meta||""),t.status.peek()!==(e.status||"")&&(t.status.value=e.status||""),t.folder.peek()!==!!e.folder&&(t.folder.value=!!e.folder)):(t={...e,label:M.observable(e.label),meta:M.observable(e.meta||""),status:M.observable(e.status||""),folder:M.observable(!!e.folder),active:M.observable(!1),dirty:M.observable(!1),folded:M.observable(!1),title:""},ga.set(e.key,t)),t.title=e.kind==="block"||e.kind==="component"?e.meta?`${e.id} - ${e.meta}`:e.id:e.kind==="folder"?e.meta:`${e.label} (${e.meta})`,t},no=new Map,z=()=>{const e=a.filter.peek(),t=I(a.active.peek()),o=t?.kind==="page"?t.id:null,s=t?.kind==="block"?t.id:null,r=t?.kind==="component"?t.id:null,n=new Set(Re().map(f=>f.id)),l=Gn(D,{filter:e,folded:ye}).map(so);for(const f of l){const C=f.kind==="page"&&f.id===o,B=f.kind==="page"&&(n.has(f.id)||!U.get(f.id)?.loaded&&xe({kind:"page",id:f.id})),fe=ye.has(f.id);f.active.peek()!==C&&(f.active.value=C),f.dirty.peek()!==B&&(f.dirty.value=B),f.folded.peek()!==fe&&(f.folded.value=fe)}const g=_n(ie,e).map(so),$=f=>{const C=L.get(f);return C?de(C):Ae.has(f)||xe({kind:"block",id:f})};for(const f of g){const C=f.id===s,B=$(f.id);f.active.peek()!==C&&(f.active.value=C),f.dirty.peek()!==B&&(f.dirty.value=B)}const G=Un(Je,e).map(so),Me=f=>{const C=N.get(f);return C?he(C):xe({kind:"component",id:f})};for(const f of G){const C=f.id===r,B=Me(f.id);f.active.peek()!==C&&(f.active.value=C),f.dirty.peek()!==B&&(f.dirty.value=B)}const Co=m?Dn(kt,Kt,be):[],za=(f,C,B)=>(no.has(f)||no.set(f,{key:f,id:C,label:B,kindText:"",title:"",active:M.observable(!1),dirty:M.observable(!1)}),no.get(f)),xo=Co.map(f=>{const C=za(`block:${f.name}`,f.name,f.name);C.kindText=f.footer?"Footer":"Block",C.title=`${f.name} - ${f.footer?"in the footer, on every page":"a block on this page"}`;const B=f.name===s,fe=$(f.name);return C.active.peek()!==B&&(C.active.value=B),C.dirty.peek()!==fe&&(C.dirty.value=fe),C});for(const f of ta){const C=za(`component:${f}`,f,f);C.kindText="Component",C.title=`<dm-${f}> - a component on this page`;const B=f===r,fe=Me(f);C.active.peek()!==B&&(C.active.value=B),C.dirty.peek()!==fe&&(C.dirty.value=fe),xo.push(C)}const Dt=(f,C)=>f.map(B=>B.key).join("|")===C.map(B=>B.key).join("|");Dt(l,a.pageRows.peek())||(a.pageRows.value=l),Dt(g,a.blockRows.peek())||(a.blockRows.value=g),Dt(G,a.compRows.peek())||(a.compRows.value=G),Dt(xo,a.thisPage.peek())||(a.thisPage.value=xo)},ha=async()=>{const e=await h.pages.list().catch(()=>null);!c()||!Array.isArray(e)||(D=e,z(),q())},te=()=>{const e=I(a.active.peek()),t=e?es(e):"/pages/new",o=window.location.hash.replace(/^#/,"");j.address=o.split("?")[0]===t?o:t,o.split("?")[0]!==t?history.replaceState({pe:1},"","#"+t):history.state?.pe||history.replaceState({pe:1},"",window.location.hash)},Fs=async e=>{if(e.loading)return e.loading;e.loading=(async()=>{let t=null;if(e.isEdit){if(t=await h.pages.get(e.id).catch(()=>null),!c())return!1;if(!t)return E.toast(`Page ${e.id} not found.`,{type:"error"}),!1}e.page=t;const o=la(t,e.id);e.state=o,e.base=W(o),e.savedAdminTags=JSON.stringify(o.adminTags),e.loaded=!0;let s=null;try{s=S.get(ze(e.id))}catch{}const r=xn(s),n=$o({kind:"page",id:e.id});if(r){J(ze(e.id));const l=W(r.state)!==e.base,g=Mn(r,e.base),$={...o,...r.state,adminTags:o.adminTags};if(l&&n&&!g)e.state=$,E.toast(`Your unsaved changes to ${e.id||"the new page"} are back.`,{type:"info"});else if(l){const G=r.at?` (${qe(r.at)})`:"",Me=g?" The saved page has changed since - restoring puts your version back over it (the saved one stays in History).":"";await E.confirm(`You left unsaved changes to ${e.id||"a new page"}${G}. Restore them?${Me}`)&&c()&&(e.state=$)}}return!0})();try{return await e.loading}finally{e.loading=null}},ka=e=>(U.has(e)||U.set(e,{kind:"page",id:e,isEdit:!!e,loaded:!1,page:null,state:null,base:"",savedAdminTags:"[]",at:0}),U.get(e)),ue=async e=>{const t=X({kind:"page",id:e});({ids:P}=Bo(P,t)),a.active.value=t,a.blockTab.value="",vo();const o=ka(e);if(q(),!o.loaded){a.previewStatus.value=`Opening ${e||"a new page"}\u2026`;const s=await Fs(o);if(!c()||!P.includes(t)||U.get(e)!==o)return;if(!s){ya(t);return}if(a.active.peek()!==t){q(),z();return}}da(o),te(),q(),z(),H(),Q.refresh(),_.focus()};w=async(e,t)=>{if(j.address=t,e.kind==="page"){ue(e.id);return}if(!e.id){(e.kind==="block"?Lt:nt)({project:Ft(t)||""});return}const o=e.kind==="block"?be:le;if(o.has(e.id)||await(e.kind==="block"?_o:Ko)(),!!c()){if(!o.has(e.id)){E.toast(`There is no ${e.kind} called "${e.id}".`,{type:"warning"}),te(),z();return}e.kind==="block"?Be(e.id):st(e.id)}};const ya=e=>{const t=a.active.peek(),o=Kn(P,e,t);P=o.ids,Ne.delete(e);const s=I(e),r=o.active!==t?I(o.active):null;if(s.kind==="page"){if(U.delete(s.id),m?.id===s.id){const n=[...U.values()].filter(l=>l.loaded&&l.id!==s.id).sort((l,g)=>g.at-l.at)[0];n?da(n):Ns()}}else s.kind==="component"?(N.delete(s.id),a.compTab.peek()===s.id&&vo()):(L.delete(s.id),a.blockTab.peek()===s.id&&(a.blockTab.value="",a.blockReady.value=!1));if(a.active.value=o.active,r?.kind==="page"){ue(r.id);return}if(r?.kind==="block"){Be(r.id);return}if(r?.kind==="component"){st(r.id);return}te(),Xe(),Le(),ke(),q(),z(),H()},et=e=>{const t=I(e);if(!t||!P.includes(e))return;let o="";try{if(t.kind==="page"){const s=U.get(t.id);s&&Yt(s)&&(S.set(ze(s.id),Ua(s===m?K():s.state,s.base)),Ao({kind:"page",id:s.id}),o=fa(s))}else if(t.kind==="component"){const s=N.get(t.id);s&&he(s)&&(S.set(_e(t.id),pa(s,new Date().toISOString())),Ao({kind:"component",id:t.id}),o=t.id)}else{const s=L.get(t.id);s&&de(s)&&(S.set(Ve(t.id),{form:{...s.form},base:s.base,at:new Date().toISOString()}),Ao({kind:"block",id:t.id}),o=t.id)}}catch{}ya(e),o&&E.toast(`Changes to "${o}" kept as a draft - back when you open it again.`,{type:"info"})},Ct=e=>{const t=I(e);t&&(t.kind==="page"?ue(t.id):t.kind==="component"?st(t.id):Be(t.id))},wa=new Set,zs=async e=>{const t=[];for(const o of e){const s=L.get(o);if(!s)continue;const r={...s.form};try{const n=Za(r,s.block?.meta);await h.blocks.put(o,n),s.block={...s.block,meta:n.meta},s.saved=r,s.base=Do(r),vt.set(o,new Date().toISOString()),c()&&J(Ve(o)),t.push(o)}catch(n){E.toast(`Block "${o}" not saved: ${n.message||"Unknown error"}`,{type:"error"})}}return t.forEach(o=>wa.add(o)),t.length&&(Le(),pt("#/blocks")),t},qs=async e=>{const t=e===m?K():e.state,o=Po(t.seoSchema).error;if(o)return E.toast(`${e.id||"The new page"} not saved - structured data: ${o}`,{type:"error"}),e===m&&(a.seoSchemaNote.value=o,a.dockOn.value=!0,a.dockTab.value="seo"),!1;const s=Gt(t,e,Xt(t)),r=e.id;try{if(e.isEdit){const n={frontmatter:s,body:t.body};t.urlPath!==e.id&&(n.newUrlPath=t.urlPath),await h.pages.update(e.id,n)}else await h.pages.create({urlPath:t.urlPath,frontmatter:s,body:t.body})}catch(n){return E.toast(`${e.id||"The new page"} not saved: ${n.message||"Unknown error"}`,{type:"error"}),!1}if(!c())return!0;if(e.savedAdminTags=JSON.stringify(t.adminTags),e.base=W(t),e===m?a.base.value=e.base:e.state=t,J(ze(r)),!e.isEdit||t.urlPath!==e.id){U.delete(r),e.id=t.urlPath,e.isEdit=!0,U.set(e.id,e);const n=X({kind:"page",id:r}),l=X({kind:"page",id:e.id});Ne.delete(n),{ids:P}=Qn(P,n,l,a.active.peek()),a.active.peek()===n&&(a.active.value=l),e===m&&(a.curId.value=e.id,te())}return!0},Vs=async e=>{const t=[];for(const o of e){const s=N.get(o);if(!s||s.readOnly)continue;if(At===s?(clearTimeout(Rt),At=null,ke(),at(s)):s.state||at(s),s.compiling&&await s.compiling,!c())return t;if(s.state!=="ok"){E.toast(`Component "${o}" not saved: ${s.state==="error"?"it does not compile - fix the errors first.":"it has not compiled yet - try again in a moment."}`,{type:"warning"});continue}const r={...s.form},n={...s.meta||{}};delete n.bundled,r.project?n.project=r.project:delete n.project;try{await h.components.put(o,{source:r.source,bundled:r.bundled,meta:n}),s.meta=n,s.saved=r,s.base=Fo({name:o,...r});const l=Je.find(g=>g.name===o);l&&(l.bundled=r.bundled,l.meta=n),c()&&J(_e(o)),t.push(o)}catch(l){E.toast(`Component "${o}" not saved: ${l.message||"Unknown error"}`,{type:"error"})}}return t.length&&(ke(),pt("#/components")),t};let ro=!1;const io=async(e=!1)=>{if(a.saving.peek()||ro)return;ro=!0;let t,o,s=[];try{if(await Ea(),!c())return;const n=I(a.active.peek());if(lr().formatOnSave&&vs(n),e)t=Re(),o=ge(),s=Ye();else if(n?.kind==="block")t=[],o=L.has(n.id)&&de(L.get(n.id))?[n.id]:[];else if(n?.kind==="component")t=[],o=[],s=N.has(n.id)&&he(N.get(n.id))?[n.id]:[];else if(n?.kind==="page"&&m)o=ge().filter(yt),t=!m.isEdit||Wt()||!o.length?[m]:[];else return;if(!t.length&&!o.length&&!s.length){E.toast("Nothing to save.",{type:"info"});return}for(const g of t){const $=Ro(g===m?K():g.state);if($.length){E.toast(`${g.id||"New page"}: ${$[0].text}`,{type:"warning"}),g!==m&&await ue(g.id),a.dockOn.value=!0,a.dockTab.value=$[0].field==="dconfig"?"dconfig":"details",Ue("dock",!0);return}}const l=Fn(o,Object.fromEntries(o.map(g=>[g,Qa(He.get(g),m?.id||"")])),wa);if(l&&(!await E.confirm(l)||!c()))return}finally{ro=!1}const r=t.length>0&&ht();a.saving.value=!0;try{let n=0;for(const $ of t)await qs($)&&n++;if(!c())return;if(r){const $=p.css.peek();await h.settings.saveCustomCss($),ee.value=$,J(ft)}const l=await zs(o);if(!c())return;const g=await Vs(s);if((n||l.length||g.length)&&E.toast(zn({pages:n,css:r,blocks:l,components:g}),{type:"success"}),Xe(),!c())return;q(),z(),n&&(pt("#/pages"),Mt(),ha())}catch(n){E.toast(`Save failed: ${n.message||"Unknown error"}`,{type:"error"})}finally{a.saving.value=!1}};let oe=null,me=null;const De=new Map,je=new Map,Sa=()=>{const e=L.get(a.blockTab.peek());!e||!oe||(a.blockReady.value=!1,oe.setValue(e.form.content),me.setValue(e.form.css),a.blockProject.value=e.form.project,qo(O("#block-project"),e.form.project),a.blockBundled.value=e.form.bundled,a.blockWhere.value=An({usage:He.get(e.form.name)})||"Where it is used could not be read.",a.blockNote.value="",a.blockReady.value=!0,uo())},_s=e=>{const t=L.get(e);if(!t)return{};const o=t.form.content;return Object.fromEntries(ts(Ga(o),No(o),t.samples||{}).map(s=>[s.name,s.value]))};i(M.effect(()=>{const e=a.blockProject.value,t=a.blockBundled.value,o=L.get(a.blockTab.peek());!o||!a.blockReady.peek()||o.form.project===e&&o.form.bundled===t||(o.form.project=e,o.form.bundled=t,Le())}));const lo=async(e,{quiet:t=!1}={})=>{for(;De.has(e);)if(await De.get(e),L.has(e)||t)return L.get(e)||null;if(L.has(e))return L.get(e);const o=(async()=>{try{const s=await h.blocks.get(e);if(!c()||!t&&!P.includes(X({kind:"block",id:e})))return null;const r=Xa({...s,name:e}),n={block:s,saved:r,base:Do(r),form:{...r},samples:{}};let l=null;try{l=S.get(Ve(e))}catch{}const g=Ln(l,n.base);if(t&&g?.stale)return null;if($o({kind:"block",id:e}),g){const $=g.at?` (${qe(g.at)})`:"",G=!g.stale||await E.confirm(`You left unsaved changes to "${e}"${$}, but the block has been saved since. Restore your version anyway?`);if(!c())return null;G&&(n.form={...n.form,...g.form,name:e},g.stale||E.toast(`Your unsaved changes to "${e}" are back.`,{type:"info"}))}return l&&J(Ve(e)),L.set(e,n),Le(),n}catch(s){return c()&&a.blockTab.peek()===e&&(a.blockNote.value=`Could not open "${e}": ${s.message||"unknown error"}`),null}finally{De.delete(e)}})();return De.set(e,o),o},Ea=async()=>{for(;De.size||je.size;)await Promise.all([...De.values(),...je.values()])},Be=async e=>{if(!e||!be.has(e))return;const t=X({kind:"block",id:e});({ids:P}=Bo(P,t)),a.active.value=t,vo(),a.cssMode.peek()&&(a.cssMode.value=!1),a.viewMode.peek()==="preview"&&(a.viewMode.value="split"),a.blockTab.peek()!==e&&(a.blockReady.value=!1),a.blockTab.value=e,a.blockUses.value=m&&yt(e)?qn(Qa(He.get(e),m.id||"")):jn(He.get(e)),te(),q(),z();let o=L.get(e);o||(a.blockReady.value=!1,a.blockNote.value=`Opening ${e}\u2026`,o=await lo(e)),!(!o||!c()||a.blockTab.peek()!==e)&&(Sa(),q(),z(),Zt=e,H(),(a.blockPart.peek()==="css"?me:oe)?.focus())};let co=null;i(()=>clearTimeout(co));const Ta=(e,t)=>{const o=L.get(a.blockTab.peek());!o||!a.blockReady.peek()||(o.form[e]=t,clearTimeout(co),co=setTimeout(()=>{Le(),e==="content"&&uo()},120),H())},we=O("#pe-block-samples"),Ma=e=>{we.replaceChildren();const t=document.createElement("p");t.className="pe-dock-none",t.textContent=e,we.appendChild(t)};let tt=[],po=null;function uo(){const e=L.get(a.blockTab.peek());if(!e){tt=[],po=null,Ma("Open a block to see its placeholders.");return}const t=ts(Ga(e.form.content),No(e.form.content),e.samples);if(!(po===e&&t.map(o=>o.name).join("|")===tt.join("|"))){if(tt=t.map(o=>o.name),po=e,!t.length){Ma("No {{placeholders}} in the template yet.");return}we.replaceChildren();for(const o of t){const s=document.createElement("div");s.className="pe-comp-prop",s.tabIndex=0,s.dataset.sample=o.name;const r=document.createElement("label");r.className="pe-comp-prop-label",r.textContent=o.name,r.title=o.raw?`{{{${o.name}}}} - raw markup`:`{{${o.name}}}`;const n=document.createElement("input");n.type="text",n.className="form-input form-input--sm pe-comp-prop-input",n.value=o.value,n.setAttribute("aria-label",`Sample value for ${o.name}`),n.addEventListener("input",()=>{e.samples[o.name]=n.value,H()});const l=document.createElement("button");l.type="button",l.className="pe-kebab",l.setAttribute("aria-label",`Actions for ${o.name}`),l.title="Actions",l.textContent="\u22EE",s.append(r,n,l),we.appendChild(s)}}}const mo=(e,t)=>No(e.form.content).has(t)?`{{{${t}}}}`:`{{${t}}}`,Ks=e=>{const t=L.get(a.blockTab.peek());return!t||!tt.includes(e)?[]:[{header:mo(t,e)},{label:"Insert into the template",icon:"corner-down-right",action:()=>{const o=oe?.textarea;o&&(a.blockPart.value="html",o.focus(),o.setRangeText(mo(t,e),o.selectionStart,o.selectionEnd,"end"),o.dispatchEvent(new Event("input",{bubbles:!0})))}},{label:"Copy the placeholder",icon:"copy",action:()=>Ie(mo(t,e),"Placeholder")},{type:"divider"},{label:"Reset the sample",icon:"rotate-ccw",action:()=>{delete t.samples[e],tt=[],uo(),H()}}]};{const e=E.contextMenu(we,{cursor:"glyph",match:".pe-comp-prop",inherit:!1,density:"compact",items:t=>Ks(t.dataset.sample)});i(()=>e?.destroy?.()),we.addEventListener("click",t=>{const o=t.target.closest(".pe-kebab");o&&Ce(o)}),we.addEventListener("keydown",t=>{const o=t.target.closest(".pe-comp-prop");o&&t.key==="F10"&&t.shiftKey&&(t.preventDefault(),Ce(o))})}const ae={},Se=O("#component-preview"),xt=e=>{try{Se?.contentWindow?.postMessage(e,"*")}catch{}};let fo=Se.contentDocument?.readyState==="complete"&&/component-preview/.test(Se.contentDocument?.URL||""),Pt=!1,ot=null;const Ca=()=>{fo=!0,ot&&c()&&(xt({type:"mount",payload:ot}),Pt=!0),ot=null};Se.addEventListener("load",Ca),i(()=>Se.removeEventListener("load",Ca));const Us=e=>{if(!fo){ot=e;return}if(!Pt){Pt=!0,xt({type:"mount",payload:e});return}ot=e,fo=!1,Pt=!1,Se.src=Se.src};let Ot="";const bo=(e=!1)=>{const t=N.get(a.compTab.peek());if(!(!t||t.state!=="ok"||!t.js)){if(a.viewMode.peek()==="write"){t.previewStale=!0;return}!e&&Ot===t.name&&!t.previewStale||(t.previewStale=!1,Ot=t.name,Us({compiledJs:t.js,tagName:`dm-${t.name}`,props:t.previewProps}))}},at=e=>{const t=++e.seq;return e.state="compiling",a.compTab.peek()===e.name&&(a.compState.value="compiling"),e.compiling=(async()=>{let o;try{o=await h.components.compile(e.name,e.form.source)}catch(s){o={errors:[{type:"request",message:s.message||"Compile request failed."}]}}if(!(t!==e.seq||!c())){if(o.errors?.length?(e.state="error",e.errors=o.errors.map(s=>`[${s.type}] ${s.message}`).join(`
|
|
138
|
-
`),e.js=null):(e.state="ok",e.errors="",e.js=o.js),a.compTab.peek()!==e.name){e.previewStale=!0;return}a.compState.value=e.state,a.compErrors.value=e.errors,e.state==="ok"&&(yo(),bo(!0))}})().finally(()=>{e.compiling&&t===e.seq&&(e.compiling=null)}),e.compiling};let Rt=null,At=null;i(()=>clearTimeout(Rt));const xa=()=>{const e=N.get(a.compTab.peek());if(!e||!ae.template)return;a.compReady.value=!1;const t=ut(e.form.source);for(const o of zt)ae[o].setValue(t[o]),ae[o].textarea.readOnly=e.readOnly;a.compReadOnly.value=e.readOnly,a.compProject.value=e.form.project,qo(O("#comp-project"),e.form.project),a.compBundled.value=e.form.bundled,a.compState.value=e.state,a.compErrors.value=e.errors,a.compNote.value="",a.compReady.value=!0,yo(),e.state==="ok"?bo():e.state||at(e)},vo=()=>{a.compTab.peek()&&(a.compTab.value="",a.compReady.value=!1)},Pa={page:"details",block:"details",component:"details"};let $t="";i(M.effect(()=>{const e=Qo.value;e!==$t&&($t&&(Pa[$t]=a.dockTab.peek()),$t=e,e&&(a.dockTab.value=Pa[e]||"details",e==="page"&&a.dockTab.peek()==="history"&&a.dockOn.peek()&&m?.isEdit&&!Qe&&pe()))}));const go=async(e,{quiet:t=!1}={})=>{if(je.has(e))return je.get(e);if(N.has(e))return N.get(e);const o=(async()=>{try{const s=await h.components.get(e);if(!c()||!P.includes(X({kind:"component",id:e})))return null;const r=s.readOnly===!0||s.origin==="plugin",n={project:s.meta?.project||"",bundled:!!s.bundled,source:os(ut(s.source))},l={name:e,comp:s,saved:n,base:Fo({name:e,...n}),form:{...n},meta:s.meta||{},readOnly:r,state:"",errors:"",js:null,seq:0,compiling:null,previewProps:{},previewStale:!1};let g=null;try{g=S.get(_e(e))}catch{}const $=nr(g,l.base);if(t&&(!$.ok||$.stale||r))return null;const G=$o({kind:"component",id:e});if(t&&!G)return null;if($.ok&&!r){const Me=g.at?` (${qe(g.at)})`:"",Co=G&&!$.stale||await E.confirm($.stale?`You left unsaved changes to the component "${e}"${Me}, but it has been saved since. Restore your version anyway?`:`You left unsaved changes to the component "${e}"${Me}. Restore them?`);if(!c())return null;Co&&(l.form={project:String(g.project??""),bundled:g.bundled===!0,source:String(g.source)},G&&!$.stale&&E.toast(`Your unsaved changes to the component "${e}" are back.`,{type:"info"}))}return g&&J(_e(e)),N.set(e,l),ke(),l}catch(s){return c()&&a.compTab.peek()===e&&(a.compNote.value=`Could not open <dm-${e}>: ${s.message||"unknown error"}`),null}finally{je.delete(e)}})();return je.set(e,o),o},st=async e=>{if(!e||!le.has(e))return;const t=X({kind:"component",id:e});({ids:P}=Bo(P,t)),a.active.value=t,a.blockTab.value="",a.cssMode.peek()&&(a.cssMode.value=!1),a.viewMode.peek()==="preview"&&(a.viewMode.value="split"),a.compTab.value=e,te(),q(),z();let o=N.get(e);o||(a.compReady.value=!1,a.compReadOnly.value=!1,a.compState.value="",a.compNote.value=`Opening <dm-${e}>\u2026`,o=await go(e)),!(!o||!c()||a.compTab.peek()!==e)&&(xa(),q(),z(),ae[a.compPart.peek()]?.focus())},Hs=(e,t)=>{const o=N.get(a.compTab.peek());if(!o||!a.compReady.peek()||o.readOnly)return;const s=ut(o.form.source);s[e]=t,o.form.source=os(s),clearTimeout(Rt),At=o,Rt=setTimeout(()=>{At=null,ke(),at(o)},300)};i(M.effect(()=>{const e=a.compProject.value,t=a.compBundled.value,o=N.get(a.compTab.peek());!o||!a.compReady.peek()||o.readOnly||o.form.project===e&&o.form.bundled===t||(o.form.project=e,o.form.bundled=t,ke())}));const Ee=O("#pe-comp-props");let ho=[];const ko=e=>{Ee.replaceChildren();const t=document.createElement("p");t.className="pe-dock-none",t.textContent=e,Ee.appendChild(t)};function yo(){const e=N.get(a.compTab.peek());if(!e){ho=[],ko("Open a component to try its props.");return}const{rows:t,error:o}=as(ut(e.form.source).props,e.previewProps);if(ho=t,o){ko("The <props> block is not valid JSON - fix it to see the props here.");return}if(!t.length){e.previewProps={},ko("No props declared. Add them in the <props> block.");return}e.previewProps=Object.fromEntries(t.map(s=>[s.name,s.value])),Ee.replaceChildren();for(const s of t){const r=document.createElement("div");r.className="pe-comp-prop",r.tabIndex=0,r.dataset.prop=s.name;const n=document.createElement("label");n.className="pe-comp-prop-label",n.textContent=s.label,n.title=`${s.name} (${s.type})`;const l=s.type==="array"||s.type==="object"?document.createElement("textarea"):document.createElement("input");s.type==="boolean"?(l.type="checkbox",l.className="form-check",l.checked=ss(s.type,s.value)):(l.tagName==="INPUT"?l.type=s.type==="number"?"number":"text":l.rows=2,l.className="form-input form-input--sm pe-comp-prop-input",l.value=ss(s.type,s.value)),l.setAttribute("aria-label",s.label);const g=()=>{e.previewProps[s.name]=ir(s.type,s.type==="boolean"?l.checked:l.value),Ot===e.name&&xt({type:"update",payload:{props:e.previewProps}})};l.addEventListener("input",g),l.addEventListener("change",g);const $=document.createElement("button");$.type="button",$.className="pe-kebab",$.setAttribute("aria-label",`Actions for ${s.label}`),$.title="Actions",$.textContent="\u22EE",r.append(n,l,$),Ee.appendChild(r)}}const Ie=async(e,t)=>{try{await navigator.clipboard.writeText(e),E.toast(`${t} copied.`,{type:"success"})}catch{E.toast(e,{type:"info",duration:8e3})}},Js=e=>{const t=N.get(a.compTab.peek()),o=ho.find(n=>n.name===e);if(!t||!o)return[];const s=sr(o.name,o.type,t.previewProps[o.name]),r=`dm-${t.name}`;return[{header:o.label},{label:"Reset to its default",icon:"rotate-ccw",action:()=>{delete t.previewProps[o.name];const n=as(ut(t.form.source).props,{}).rows.find(l=>l.name===o.name);t.previewProps[o.name]=n?n.value:null,yo(),Ot===t.name&&xt({type:"update",payload:{props:t.previewProps}})}},{label:"Copy as an attribute",icon:"copy",disabled:!s,action:()=>Ie(s,"Attribute")},{label:"Copy the tag with this value",icon:"code",action:()=>Ie(`<${r}${s?` ${s}`:""}></${r}>`,"Tag")},{label:"Copy the prop name",icon:"copy",action:()=>Ie(o.name,"Name")},{type:"divider"},{label:"Edit in <props>",icon:"edit",action:()=>{a.compPart.value="props";const n=ae.props?.textarea;if(!n)return;const l=n.value.indexOf(`"${o.name}"`);n.focus(),l>=0&&n.setSelectionRange(l,l+o.name.length+2)}}]};{const e=E.contextMenu(Ee,{cursor:"glyph",match:".pe-comp-prop",inherit:!1,density:"compact",items:t=>Js(t.dataset.prop)});i(()=>e?.destroy?.()),Ee.addEventListener("click",t=>{const o=t.target.closest(".pe-kebab");o&&Ce(o)}),Ee.addEventListener("keydown",t=>{const o=t.target.closest(".pe-comp-prop");o&&t.key==="F10"&&t.shiftKey&&(t.preventDefault(),Ce(o))})}let Fe=!1;const Oa=()=>Ft()||"",Lt=async({project:e=Oa(),seed:t="",content:o=gr,css:s=""}={})=>{if(!Fe){Fe=!0;try{const n=await ls({kind:"block",projects:se,taken:[...be],project:e,name:t,check:Rn,register:i,create:async(l,g)=>{await h.blocks.put(l,Za({name:l,project:g,bundled:!1,content:o,css:s}))}});if(!c())return;if(!n){te();return}if(await _o(),!c())return;pt("#/blocks"),E.toast(`Created the block "${n}".`,{type:"success"}),Be(n)}finally{Fe=!1}}},nt=async({name:e="",source:t=ar,project:o=Oa()}={})=>{if(!Fe){Fe=!0;try{const s=await ls({kind:"component",projects:se,taken:[...le],project:o,name:e,register:i,check:(r,n)=>rr(r,{isNew:!0,taken:n}),create:async(r,n)=>{await h.components.put(r,{source:t,bundled:!1,meta:n?{project:n}:{}})}});if(!c())return;if(!s){te();return}if(await Ko(),!c())return;pt("#/components"),E.toast(`Created the component "${s}".`,{type:"success"}),st(s)}finally{Fe=!1}}},Ra=k.querySelector(".dm-plugin-body")||k,Ws=M.computed(()=>{a.blockRev.value,a.compRev.value,a.docRev.value,W(K()),a.base.value;const e=I(a.active.value),t=[...Re().map(s=>X({kind:"page",id:s.id})),...ge().map(s=>X({kind:"block",id:s})),...Ye().map(s=>X({kind:"component",id:s}))];let o=[];return e?.kind==="block"||e?.kind==="component"?o=[a.active.value]:e?.kind==="page"&&m&&(o=[a.active.value,...ge().filter(yt).map(s=>X({kind:"block",id:s}))]),er(t,o)}),Ys={...p,...a,dirty:ks,pageDirty:gs,activeDirty:ys,cssUnsaved:hs,hasPage:ws,activeKind:Xo,canView:Zo,viewHref:Ss,problem:Es,statusText:Ts,saveAll:Ws,dockKind:Qo,blockTagText:Ms,compStateText:Cs,compTagText:xs,anySelected:Ps,allSelected:Os,someSelected:Rs},rt=(e,t)=>Ue(e,t),wo={save:()=>io(!1),saveAllOpen:()=>io(!0),discard:async()=>{if(await Ea(),!c())return;const e=I(a.active.peek());if(e?.kind==="block"){const t=L.get(e.id);if(!t||!de(t)||!await E.confirm(`Discard the changes to "${e.id}" you have not saved?`))return;t.form={...t.saved},J(Ve(e.id)),Sa(),Le()}else if(e?.kind==="component"){const t=N.get(e.id);if(!t||!he(t)||!await E.confirm(`Discard the changes to the component "${e.id}" you have not saved?`))return;t.form={...t.saved},t.state="",J(_e(e.id)),xa(),ke()}else{if(!m||e?.kind==="page"&&e.id!==m.id||!(Wt()||ht())||!await E.confirm("Discard the changes to this page you have not saved?"))return;Ze(JSON.parse(a.base.peek())),p.cssOpened.peek()&&(p.css.value=ee.peek()),J(ze(m.id)),J(ft),Xe()}q(),z(),H()},newPage:()=>ue(""),newBlock:()=>Lt(),newComponent:()=>nt(),toggleSection:e=>{const t={here:"secHere",pages:"secPages",blocks:"secBlocks",components:"secComps"}[e];t&&(a[t].value=!a[t].peek(),rt(t,a[t].peek()))},toggleExplorer:()=>{a.explorerOn.value=!a.explorerOn.peek(),rt("explorer",a.explorerOn.peek())},toggleDock:()=>{a.dockOn.value=!a.dockOn.peek(),rt("dock",a.dockOn.peek()),a.dockOn.peek()&&a.dockTab.peek()==="history"&&!Qe&&pe()},showDock:e=>{a.dockTab.value=e,e==="history"&&m?.isEdit&&!Qe&&pe()},schemaChanged:()=>{a.seoSchemaNote.value=Po(p.seoSchema.peek()).error},insertSchemaExample:async e=>{const t=e?.target,o=t?.value||"";if(t&&(t.value=""),!o||p.seoSchema.peek().trim()&&!await E.confirm("Replace the structured data with the example?"))return;const s=p.urlPath.peek().trim()||m?.id||"/";p.seoSchema.value=mn(o,{title:p.seoTitle.peek().trim()||p.title.peek().trim(),description:p.seoDesc.peek().trim()||p.description.peek().trim(),url:`${location.origin}${s}`,siteName:Y?.title||Y?.seo?.defaultTitle||""}),a.seoSchemaNote.value=""},setBlockPart:e=>{a.blockPart.value=e==="css"?"css":"html",(e==="css"?me:oe)?.focus()},formatBlock:()=>{const e=a.blockPart.peek()==="css",t=e?me:oe;!t||!a.blockReady.peek()||Ho(Ke(t.textarea,e?"css":"html"))},copyShortcode:async()=>{const e=a.blockTab.peek();e&&await Ie(Nn(e),"Shortcode")},setCompPart:e=>{a.compPart.value=zt.includes(e)?e:"template",ae[a.compPart.peek()]?.focus()},copyComponent:()=>{const e=N.get(a.compTab.peek());e&&nt({name:Lo(e.name,[...le]),source:e.form.source})},setMode:e=>{a.cssMode.peek()&&(a.cssMode.value=!1),a.viewMode.value=jo(e),rt("viewMode",a.viewMode.peek()),e!=="write"&&(H(!0),bo())},toggleFullscreen:()=>{a.fullscreen.value=!a.fullscreen.peek(),rt("fullscreen",a.fullscreen.peek())},toggleCss:()=>{a.blockTab.peek()||a.compTab.peek()||(a.cssMode.peek()?(a.cssMode.value=!1,a.viewMode.value=jo(Pe().viewMode),Q.refresh()):(ia(),a.cssMode.value=!0,a.viewMode.value="split"))},formatMarkdown:()=>{Jt(),Ho(Ke(_,"md"))},toggleFold:()=>{a.folded.peek()?Q.unfoldAll():Q.foldAll(),a.folded.value=!a.folded.peek(),We.paint()},setWidth:e=>{a.liveWidth.value=e},refreshPreview:()=>{if(a.compTab.peek()){const e=N.get(a.compTab.peek());e&&at(e)}else H(!0)},share:async()=>{if(!m?.isEdit){E.toast("Save the page first - a share link needs a page to point at.",{type:"warning"});return}await br(m.id)},toggleAll:e=>{const t=!!e?.target?.checked;a.versions.peek().forEach(o=>{o.checked.value=t})},deleteSelected:async()=>{const e=a.versions.peek().filter(o=>o.checked.peek()).map(o=>o.filename),t=e.length;if(!(!t||!await E.confirm(`Delete ${t} version${t>1?"s":""}? This cannot be undone.`)))try{await h.versions.bulkDelete(m.id,e),E.toast(`${t} version${t>1?"s":""} deleted.`,{type:"success"}),await pe()}catch(o){E.toast("Delete failed: "+o.message,{type:"error"})}},saveNamed:async()=>{if(!m?.isEdit)return;const e=m;await vr(e.id)&&c()&&m===e&&await pe()},closeCompare:()=>{a.compare.value=null,Tt=null},restoreCompared:()=>ma(Tt)};Ge.querySelectorAll("[data-tip]").forEach(e=>E.tooltip(e,{content:e.getAttribute("data-tip"),position:"top"}));const Gs=M.applyBindings(Ys,Ra,{methods:wo});i(()=>Gs.dispose()),oe=zo(O('.pe-block-edit[data-part="html"]'),{ariaLabel:"Block template HTML",language:"html",onChange:e=>Ta("content",e)}),me=zo(O('.pe-block-edit[data-part="css"]'),{ariaLabel:"Block CSS",language:"css",placeholder:`/* Scoped to this block - e.g.
|
|
137
|
+
</span>`);Ge.prepend(Ls),Ge.before(O(".pe-block-bar")),Ge.before(O(".pe-comp-bar"));const aa=e=>(Array.isArray(e)?e:[]).map(t=>({value:t,label:t})),sa=O("#field-tags");let St=null;St=sa?E.pillbox(sa,{data:aa(_t),value:[],creatable:!0,searchable:!0,placeholder:"Add tag\u2026",onChange:()=>{St&&(p.tags.value=[...St.getValue()])}}):null;const na=O("#field-admin-tags");let Et=null;Et=na?E.pillbox(na,{data:aa(T),value:[],creatable:!0,searchable:!0,placeholder:"Add admin tag\u2026",onChange:()=>{Et&&(p.adminTags.value=[...Et.getValue()])}}):null;const ra=()=>{a.bodyRev.value=a.bodyRev.peek()+1},Le=()=>{a.blockRev.value=a.blockRev.peek()+1},ke=()=>{a.compRev.value=a.compRev.peek()+1},Xe=()=>{a.docRev.value=a.docRev.peek()+1},ia=()=>{p.cssOpened.peek()||(p.css.peek()||(p.css.value=ee.peek()),p.cssOpened.value=!0,on(ve,{language:"css"}))},Ze=e=>{for(const t of Mr)p[t].value=e[t]==null?"":String(e[t]);for(const t of Tr)p[t].value=!!e[t];p.tags.value=Array.isArray(e.tags)?e.tags:[],St?.setValue(p.tags.peek()),Array.isArray(e.adminTags)&&(p.adminTags.value=e.adminTags),Et?.setValue(p.adminTags.peek()),qo(O("#field-visibility"),p.visibility.peek()),Q.unfoldAll(),a.folded.value=!1,gt=!0,_.value=e.body==null?"":String(e.body),gt=!1,Q.refresh(),Uo(),ra()},la=(e,t)=>({urlPath:t||"",title:e?.title||"",description:e?.description||"",layout:Vo(e?.layout||"default"),status:e?.status||"draft",sortOrder:String(e?.sortOrder??99),showInNav:!!e?.showInNav,sidebar:!!e?.sidebar,breadcrumbs:e?e.breadcrumbs!==!1:!0,bundled:!!e?.bundled,tags:Array.isArray(e?.tags)?e.tags:[],adminTags:Array.isArray(e?.adminTags)?e.adminTags:[],category:e?.category||"",visibility:typeof e?.visibility=="string"&&e.visibility?e.visibility:"public",project:Cn({isEdit:!!t,page:e,projects:se,fromHash:t?"":Ft()||"",urlPath:t||""}),theme:e?.theme||"",seoTitle:e?.seo?.title||"",seoDesc:e?.seo?.description||"",seoSchema:fn(e?.seo?.schema),dconfig:e?.dconfig?JSON.stringify(e.dconfig,null,2):"",body:e?.content||"",css:null}),ca=()=>{m&&(m.state=K(),m.at=Date.now())},da=e=>{m!==e&&(ca(),m=e,Ze(e.state),a.base.value=e.base,a.curId.value=e.id,e.at=Date.now(),Tt(),Xe())},Ns=()=>{ca(),m=null;const e=ms();Ze(e),a.base.value=W(e),a.curId.value=null,Tt(),Xe()};Ze(ms()),a.base.value=W(K());const J=e=>{try{S.remove?S.remove(e):S.set(e,null)}catch{}},pa=(e,t)=>({name:e.name,...e.form,base:e.base,at:t});u=()=>{try{for(const t of Re())S.set(ze(t.id),Ua(t===m?Go():t.state,t.base));ht()&&S.set(ft,p.css.peek());const e=new Date().toISOString();for(const t of ge()){const o=L.get(t);S.set(Ve(t),{form:{...o.form},base:o.base,at:e})}for(const t of Ye())S.set(_e(t),pa(N.get(t),e))}catch{}};const ua=e=>{y()&&(u(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",ua),i(()=>window.removeEventListener("beforeunload",ua));let Qe=!1,Mt=null;const Tt=()=>{Qe=!1,Mt=null,a.versions.value=[],a.compare.value=null,a.histError.value="",a.dockOn.peek()&&a.dockTab.peek()==="history"&&(m?.isEdit?pe():a.dockTab.value="details")},pe=async()=>{const e=m;if(e?.isEdit){Qe=!0,a.histLoading.value=!0,a.histError.value="";try{const t=(await h.versions.list(e.id)).versions||[];if(!c()||m!==e)return;a.versions.value=t.map(o=>({...o,when:qe(o.createdAt),author:o.author||"",label:o.label||"",who:[o.author,o.label].filter(Boolean).join(" - "),checked:M.observable(!1)}))}catch(t){m===e&&(a.histError.value="Failed to load versions: "+t.message)}finally{m===e&&(a.histLoading.value=!1)}}},oo=e=>a.versions.peek().find(t=>t.filename===e),ao=async e=>{const t=m;try{const o=await h.versions.get(t.id,e.filename);if(!c()||m!==t)return;Mt=e.filename,a.compare.value={label:rs(e),old:o.content,current:Ht()}}catch(o){E.toast("Failed to load version: "+o.message,{type:"error"})}},Ds=async()=>{const e=m;if(!e?.isEdit)return;const t=await h.pages.get(e.id).catch(()=>null);if(!c()||m!==e||!t)return;e.page=t;const o=la(t,e.id);e.base=W(o),e.savedAdminTags=JSON.stringify(o.adminTags),Ze(o),a.base.value=e.base,Tt(),a.dockOn.peek()&&a.dockTab.peek()==="history"&&pe(),H()},ma=async e=>{if(!(!e||!await E.confirm("Restore this version? The current page content will be overwritten (a pre-restore snapshot will be saved automatically).")))try{await h.versions.restore(m.id,e),E.toast("Page restored.",{type:"success"}),await Ds()}catch(t){E.toast("Restore failed: "+t.message,{type:"error"})}},js=async e=>{if(await E.confirm("Delete this version? This cannot be undone."))try{await h.versions.delete(m.id,e.filename),E.toast("Version deleted.",{type:"success"}),await pe()}catch(t){E.toast("Failed to delete: "+t.message,{type:"error"})}},Ne=new Map,Bs=e=>{if(!Ne.has(e)){const t=I(e);Ne.set(e,{id:e,kind:t.kind,kindText:t.kind==="page"?"Page":t.kind==="component"?"Component":"Block",label:M.observable(""),title:M.observable(""),active:M.observable(!1),dirty:M.observable(!1)})}return Ne.get(e)},fa=e=>{const t=e===m?p.title.peek():e?.loaded?e.state.title:D.find(o=>o.urlPath===e?.id)?.title;return String(t||"").trim()||e?.id||"New page"};let ba=!0,va="";const Is=()=>{if(ba)return;const e={ids:P,active:a.active.peek()},t=JSON.stringify(e);if(t!==va){va=t;try{S.set(ps,e)}catch{}}},q=()=>{const e=P.map(Bs);for(const t of e){const o=I(t.id);let s,r;if(o.kind==="page"){const l=U.get(o.id);s=fa(l),r=l?.loaded?Yt(l):xe(o),t.title.value=o.id?`${s} (${o.id})`:"New page"}else if(o.kind==="component"){const l=N.get(o.id);s=o.id,r=l?he(l):xe(o),t.title.value=`Component <dm-${o.id}>`}else{const l=L.get(o.id);s=o.id,r=l?de(l):Ae.has(o.id)||xe(o),t.title.value=`Block ${o.id}`}t.label.peek()!==s&&(t.label.value=s),t.dirty.peek()!==r&&(t.dirty.value=r);const n=t.id===a.active.peek();t.active.peek()!==n&&(t.active.value=n)}e.map(t=>t.id).join("|")!==a.tabs.peek().map(t=>t.id).join("|")&&(a.tabs.value=e),Is()};let ye=new Set;try{const e=S.get(us);Array.isArray(e)&&(ye=new Set(e.filter(t=>typeof t=="string")))}catch{}const ga=new Map,so=e=>{let t=ga.get(e.key);return t?(t.label.peek()!==e.label&&(t.label.value=e.label),t.meta.peek()!==(e.meta||"")&&(t.meta.value=e.meta||""),t.status.peek()!==(e.status||"")&&(t.status.value=e.status||""),t.folder.peek()!==!!e.folder&&(t.folder.value=!!e.folder)):(t={...e,label:M.observable(e.label),meta:M.observable(e.meta||""),status:M.observable(e.status||""),folder:M.observable(!!e.folder),active:M.observable(!1),dirty:M.observable(!1),folded:M.observable(!1),title:""},ga.set(e.key,t)),t.title=e.kind==="block"||e.kind==="component"?e.meta?`${e.id} - ${e.meta}`:e.id:e.kind==="folder"?e.meta:`${e.label} (${e.meta})`,t},no=new Map,z=()=>{const e=a.filter.peek(),t=I(a.active.peek()),o=t?.kind==="page"?t.id:null,s=t?.kind==="block"?t.id:null,r=t?.kind==="component"?t.id:null,n=new Set(Re().map(f=>f.id)),l=Gn(D,{filter:e,folded:ye}).map(so);for(const f of l){const C=f.kind==="page"&&f.id===o,B=f.kind==="page"&&(n.has(f.id)||!U.get(f.id)?.loaded&&xe({kind:"page",id:f.id})),fe=ye.has(f.id);f.active.peek()!==C&&(f.active.value=C),f.dirty.peek()!==B&&(f.dirty.value=B),f.folded.peek()!==fe&&(f.folded.value=fe)}const g=_n(ie,e).map(so),$=f=>{const C=L.get(f);return C?de(C):Ae.has(f)||xe({kind:"block",id:f})};for(const f of g){const C=f.id===s,B=$(f.id);f.active.peek()!==C&&(f.active.value=C),f.dirty.peek()!==B&&(f.dirty.value=B)}const G=Un(Je,e).map(so),Te=f=>{const C=N.get(f);return C?he(C):xe({kind:"component",id:f})};for(const f of G){const C=f.id===r,B=Te(f.id);f.active.peek()!==C&&(f.active.value=C),f.dirty.peek()!==B&&(f.dirty.value=B)}const Co=m?Dn(kt,Kt,be):[],za=(f,C,B)=>(no.has(f)||no.set(f,{key:f,id:C,label:B,kindText:"",title:"",active:M.observable(!1),dirty:M.observable(!1)}),no.get(f)),xo=Co.map(f=>{const C=za(`block:${f.name}`,f.name,f.name);C.kindText=f.footer?"Footer":"Block",C.title=`${f.name} - ${f.footer?"in the footer, on every page":"a block on this page"}`;const B=f.name===s,fe=$(f.name);return C.active.peek()!==B&&(C.active.value=B),C.dirty.peek()!==fe&&(C.dirty.value=fe),C});for(const f of ta){const C=za(`component:${f}`,f,f);C.kindText="Component",C.title=`<dm-${f}> - a component on this page`;const B=f===r,fe=Te(f);C.active.peek()!==B&&(C.active.value=B),C.dirty.peek()!==fe&&(C.dirty.value=fe),xo.push(C)}const Dt=(f,C)=>f.map(B=>B.key).join("|")===C.map(B=>B.key).join("|");Dt(l,a.pageRows.peek())||(a.pageRows.value=l),Dt(g,a.blockRows.peek())||(a.blockRows.value=g),Dt(G,a.compRows.peek())||(a.compRows.value=G),Dt(xo,a.thisPage.peek())||(a.thisPage.value=xo)},ha=async()=>{const e=await h.pages.list().catch(()=>null);!c()||!Array.isArray(e)||(D=e,z(),q())},te=()=>{const e=I(a.active.peek()),t=e?es(e):"/pages/new",o=window.location.hash.replace(/^#/,"");j.address=o.split("?")[0]===t?o:t,o.split("?")[0]!==t?history.replaceState({pe:1},"","#"+t):history.state?.pe||history.replaceState({pe:1},"",window.location.hash)},Fs=async e=>{if(e.loading)return e.loading;e.loading=(async()=>{let t=null;if(e.isEdit){if(t=await h.pages.get(e.id).catch(()=>null),!c())return!1;if(!t)return E.toast(`Page ${e.id} not found.`,{type:"error"}),!1}e.page=t;const o=la(t,e.id);e.state=o,e.base=W(o),e.savedAdminTags=JSON.stringify(o.adminTags),e.loaded=!0;let s=null;try{s=S.get(ze(e.id))}catch{}const r=xn(s),n=$o({kind:"page",id:e.id});if(r){J(ze(e.id));const l=W(r.state)!==e.base,g=Tn(r,e.base),$={...o,...r.state,adminTags:o.adminTags};if(l&&n&&!g)e.state=$,E.toast(`Your unsaved changes to ${e.id||"the new page"} are back.`,{type:"info"});else if(l){const G=r.at?` (${qe(r.at)})`:"",Te=g?" The saved page has changed since - restoring puts your version back over it (the saved one stays in History).":"";await E.confirm(`You left unsaved changes to ${e.id||"a new page"}${G}. Restore them?${Te}`)&&c()&&(e.state=$)}}return!0})();try{return await e.loading}finally{e.loading=null}},ka=e=>(U.has(e)||U.set(e,{kind:"page",id:e,isEdit:!!e,loaded:!1,page:null,state:null,base:"",savedAdminTags:"[]",at:0}),U.get(e)),ue=async e=>{const t=X({kind:"page",id:e});({ids:P}=Bo(P,t)),a.active.value=t,a.blockTab.value="",vo();const o=ka(e);if(q(),!o.loaded){a.previewStatus.value=`Opening ${e||"a new page"}\u2026`;const s=await Fs(o);if(!c()||!P.includes(t)||U.get(e)!==o)return;if(!s){ya(t);return}if(a.active.peek()!==t){q(),z();return}}da(o),te(),q(),z(),H(),Q.refresh(),_.focus()};w=async(e,t)=>{if(j.address=t,e.kind==="page"){ue(e.id);return}if(!e.id){(e.kind==="block"?Lt:nt)({project:Ft(t)||""});return}const o=e.kind==="block"?be:le;if(o.has(e.id)||await(e.kind==="block"?_o:Ko)(),!!c()){if(!o.has(e.id)){E.toast(`There is no ${e.kind} called "${e.id}".`,{type:"warning"}),te(),z();return}e.kind==="block"?Be(e.id):st(e.id)}};const ya=e=>{const t=a.active.peek(),o=Kn(P,e,t);P=o.ids,Ne.delete(e);const s=I(e),r=o.active!==t?I(o.active):null;if(s.kind==="page"){if(U.delete(s.id),m?.id===s.id){const n=[...U.values()].filter(l=>l.loaded&&l.id!==s.id).sort((l,g)=>g.at-l.at)[0];n?da(n):Ns()}}else s.kind==="component"?(N.delete(s.id),a.compTab.peek()===s.id&&vo()):(L.delete(s.id),a.blockTab.peek()===s.id&&(a.blockTab.value="",a.blockReady.value=!1));if(a.active.value=o.active,r?.kind==="page"){ue(r.id);return}if(r?.kind==="block"){Be(r.id);return}if(r?.kind==="component"){st(r.id);return}te(),Xe(),Le(),ke(),q(),z(),H()},et=e=>{const t=I(e);if(!t||!P.includes(e))return;let o="";try{if(t.kind==="page"){const s=U.get(t.id);s&&Yt(s)&&(S.set(ze(s.id),Ua(s===m?K():s.state,s.base)),Ao({kind:"page",id:s.id}),o=fa(s))}else if(t.kind==="component"){const s=N.get(t.id);s&&he(s)&&(S.set(_e(t.id),pa(s,new Date().toISOString())),Ao({kind:"component",id:t.id}),o=t.id)}else{const s=L.get(t.id);s&&de(s)&&(S.set(Ve(t.id),{form:{...s.form},base:s.base,at:new Date().toISOString()}),Ao({kind:"block",id:t.id}),o=t.id)}}catch{}ya(e),o&&E.toast(`Changes to "${o}" kept as a draft - back when you open it again.`,{type:"info"})},Ct=e=>{const t=I(e);t&&(t.kind==="page"?ue(t.id):t.kind==="component"?st(t.id):Be(t.id))},wa=new Set,zs=async e=>{const t=[];for(const o of e){const s=L.get(o);if(!s)continue;const r={...s.form};try{const n=Za(r,s.block?.meta);await h.blocks.put(o,n),s.block={...s.block,meta:n.meta},s.saved=r,s.base=Do(r),vt.set(o,new Date().toISOString()),c()&&J(Ve(o)),t.push(o)}catch(n){E.toast(`Block "${o}" not saved: ${n.message||"Unknown error"}`,{type:"error"})}}return t.forEach(o=>wa.add(o)),t.length&&(Le(),pt("#/blocks")),t},qs=async e=>{const t=e===m?K():e.state,o=Po(t.seoSchema).error;if(o)return E.toast(`${e.id||"The new page"} not saved - structured data: ${o}`,{type:"error"}),e===m&&(a.seoSchemaNote.value=o,a.dockOn.value=!0,a.dockTab.value="seo"),!1;const s=Gt(t,e,Xt(t)),r=e.id;try{if(e.isEdit){const n={frontmatter:s,body:t.body};t.urlPath!==e.id&&(n.newUrlPath=t.urlPath),await h.pages.update(e.id,n)}else await h.pages.create({urlPath:t.urlPath,frontmatter:s,body:t.body})}catch(n){return E.toast(`${e.id||"The new page"} not saved: ${n.message||"Unknown error"}`,{type:"error"}),!1}if(!c())return!0;if(e.savedAdminTags=JSON.stringify(t.adminTags),e.base=W(t),e===m?a.base.value=e.base:e.state=t,J(ze(r)),!e.isEdit||t.urlPath!==e.id){U.delete(r),e.id=t.urlPath,e.isEdit=!0,U.set(e.id,e);const n=X({kind:"page",id:r}),l=X({kind:"page",id:e.id});Ne.delete(n),{ids:P}=Qn(P,n,l,a.active.peek()),a.active.peek()===n&&(a.active.value=l),e===m&&(a.curId.value=e.id,te())}return!0},Vs=async e=>{const t=[];for(const o of e){const s=N.get(o);if(!s||s.readOnly)continue;if(At===s?(clearTimeout(Rt),At=null,ke(),at(s)):s.state||at(s),s.compiling&&await s.compiling,!c())return t;if(s.state!=="ok"){E.toast(`Component "${o}" not saved: ${s.state==="error"?"it does not compile - fix the errors first.":"it has not compiled yet - try again in a moment."}`,{type:"warning"});continue}const r={...s.form},n={...s.meta||{}};delete n.bundled,r.project?n.project=r.project:delete n.project;try{await h.components.put(o,{source:r.source,bundled:r.bundled,meta:n}),s.meta=n,s.saved=r,s.base=Fo({name:o,...r});const l=Je.find(g=>g.name===o);l&&(l.bundled=r.bundled,l.meta=n),c()&&J(_e(o)),t.push(o)}catch(l){E.toast(`Component "${o}" not saved: ${l.message||"Unknown error"}`,{type:"error"})}}return t.length&&(ke(),pt("#/components")),t};let ro=!1;const io=async(e=!1)=>{if(a.saving.peek()||ro)return;ro=!0;let t,o,s=[];try{if(await Ea(),!c())return;const n=I(a.active.peek());if(lr().formatOnSave&&vs(n),e)t=Re(),o=ge(),s=Ye();else if(n?.kind==="block")t=[],o=L.has(n.id)&&de(L.get(n.id))?[n.id]:[];else if(n?.kind==="component")t=[],o=[],s=N.has(n.id)&&he(N.get(n.id))?[n.id]:[];else if(n?.kind==="page"&&m)o=ge().filter(yt),t=!m.isEdit||Wt()||!o.length?[m]:[];else return;if(!t.length&&!o.length&&!s.length){E.toast("Nothing to save.",{type:"info"});return}for(const g of t){const $=Ro(g===m?K():g.state);if($.length){E.toast(`${g.id||"New page"}: ${$[0].text}`,{type:"warning"}),g!==m&&await ue(g.id),a.dockOn.value=!0,a.dockTab.value=$[0].field==="dconfig"?"dconfig":"details",Ue("dock",!0);return}}const l=Fn(o,Object.fromEntries(o.map(g=>[g,Qa(He.get(g),m?.id||"")])),wa);if(l&&(!await E.confirm(l)||!c()))return}finally{ro=!1}const r=t.length>0&&ht();a.saving.value=!0;try{let n=0;for(const $ of t)await qs($)&&n++;if(!c())return;if(r){const $=p.css.peek();await h.settings.saveCustomCss($),ee.value=$,J(ft)}const l=await zs(o);if(!c())return;const g=await Vs(s);if((n||l.length||g.length)&&E.toast(zn({pages:n,css:r,blocks:l,components:g}),{type:"success"}),Xe(),!c())return;q(),z(),n&&(pt("#/pages"),Tt(),ha())}catch(n){E.toast(`Save failed: ${n.message||"Unknown error"}`,{type:"error"})}finally{a.saving.value=!1}};let oe=null,me=null;const De=new Map,je=new Map,Sa=()=>{const e=L.get(a.blockTab.peek());!e||!oe||(a.blockReady.value=!1,oe.setValue(e.form.content),me.setValue(e.form.css),a.blockProject.value=e.form.project,qo(O("#block-project"),e.form.project),a.blockBundled.value=e.form.bundled,a.blockWhere.value=An({usage:He.get(e.form.name)})||"Where it is used could not be read.",a.blockNote.value="",a.blockReady.value=!0,uo())},_s=e=>{const t=L.get(e);if(!t)return{};const o=t.form.content;return Object.fromEntries(ts(Ga(o),No(o),t.samples||{}).map(s=>[s.name,s.value]))};i(M.effect(()=>{const e=a.blockProject.value,t=a.blockBundled.value,o=L.get(a.blockTab.peek());!o||!a.blockReady.peek()||o.form.project===e&&o.form.bundled===t||(o.form.project=e,o.form.bundled=t,Le())}));const lo=async(e,{quiet:t=!1}={})=>{for(;De.has(e);)if(await De.get(e),L.has(e)||t)return L.get(e)||null;if(L.has(e))return L.get(e);const o=(async()=>{try{const s=await h.blocks.get(e);if(!c()||!t&&!P.includes(X({kind:"block",id:e})))return null;const r=Xa({...s,name:e}),n={block:s,saved:r,base:Do(r),form:{...r},samples:{}};let l=null;try{l=S.get(Ve(e))}catch{}const g=Ln(l,n.base);if(t&&g?.stale)return null;if($o({kind:"block",id:e}),g){const $=g.at?` (${qe(g.at)})`:"",G=!g.stale||await E.confirm(`You left unsaved changes to "${e}"${$}, but the block has been saved since. Restore your version anyway?`);if(!c())return null;G&&(n.form={...n.form,...g.form,name:e},g.stale||E.toast(`Your unsaved changes to "${e}" are back.`,{type:"info"}))}return l&&J(Ve(e)),L.set(e,n),Le(),n}catch(s){return c()&&a.blockTab.peek()===e&&(a.blockNote.value=`Could not open "${e}": ${s.message||"unknown error"}`),null}finally{De.delete(e)}})();return De.set(e,o),o},Ea=async()=>{for(;De.size||je.size;)await Promise.all([...De.values(),...je.values()])},Be=async e=>{if(!e||!be.has(e))return;const t=X({kind:"block",id:e});({ids:P}=Bo(P,t)),a.active.value=t,vo(),a.cssMode.peek()&&(a.cssMode.value=!1),a.viewMode.peek()==="preview"&&(a.viewMode.value="split"),a.blockTab.peek()!==e&&(a.blockReady.value=!1),a.blockTab.value=e,a.blockUses.value=m&&yt(e)?qn(Qa(He.get(e),m.id||"")):jn(He.get(e)),te(),q(),z();let o=L.get(e);o||(a.blockReady.value=!1,a.blockNote.value=`Opening ${e}\u2026`,o=await lo(e)),!(!o||!c()||a.blockTab.peek()!==e)&&(Sa(),q(),z(),Zt=e,H(),(a.blockPart.peek()==="css"?me:oe)?.focus())};let co=null;i(()=>clearTimeout(co));const Ma=(e,t)=>{const o=L.get(a.blockTab.peek());!o||!a.blockReady.peek()||(o.form[e]=t,clearTimeout(co),co=setTimeout(()=>{Le(),e==="content"&&uo()},120),H())},we=O("#pe-block-samples"),Ta=e=>{we.replaceChildren();const t=document.createElement("p");t.className="pe-dock-none",t.textContent=e,we.appendChild(t)};let tt=[],po=null;function uo(){const e=L.get(a.blockTab.peek());if(!e){tt=[],po=null,Ta("Open a block to see its placeholders.");return}const t=ts(Ga(e.form.content),No(e.form.content),e.samples);if(!(po===e&&t.map(o=>o.name).join("|")===tt.join("|"))){if(tt=t.map(o=>o.name),po=e,!t.length){Ta("No {{placeholders}} in the template yet.");return}we.replaceChildren();for(const o of t){const s=document.createElement("div");s.className="pe-comp-prop",s.tabIndex=0,s.dataset.sample=o.name;const r=document.createElement("label");r.className="pe-comp-prop-label",r.textContent=o.name,r.title=o.raw?`{{{${o.name}}}} - raw markup`:`{{${o.name}}}`;const n=document.createElement("input");n.type="text",n.className="form-input form-input--sm pe-comp-prop-input",n.value=o.value,n.setAttribute("aria-label",`Sample value for ${o.name}`),n.addEventListener("input",()=>{e.samples[o.name]=n.value,H()});const l=document.createElement("button");l.type="button",l.className="pe-kebab",l.setAttribute("aria-label",`Actions for ${o.name}`),l.title="Actions",l.textContent="\u22EE",s.append(r,n,l),we.appendChild(s)}}}const mo=(e,t)=>No(e.form.content).has(t)?`{{{${t}}}}`:`{{${t}}}`,Ks=e=>{const t=L.get(a.blockTab.peek());return!t||!tt.includes(e)?[]:[{header:mo(t,e)},{label:"Insert into the template",icon:"corner-down-right",action:()=>{const o=oe?.textarea;o&&(a.blockPart.value="html",o.focus(),o.setRangeText(mo(t,e),o.selectionStart,o.selectionEnd,"end"),o.dispatchEvent(new Event("input",{bubbles:!0})))}},{label:"Copy the placeholder",icon:"copy",action:()=>Ie(mo(t,e),"Placeholder")},{type:"divider"},{label:"Reset the sample",icon:"rotate-ccw",action:()=>{delete t.samples[e],tt=[],uo(),H()}}]};{const e=E.contextMenu(we,{cursor:"glyph",match:".pe-comp-prop",inherit:!1,density:"compact",items:t=>Ks(t.dataset.sample)});i(()=>e?.destroy?.()),we.addEventListener("click",t=>{const o=t.target.closest(".pe-kebab");o&&Ce(o)}),we.addEventListener("keydown",t=>{const o=t.target.closest(".pe-comp-prop");o&&t.key==="F10"&&t.shiftKey&&(t.preventDefault(),Ce(o))})}const ae={},Se=O("#component-preview"),xt=e=>{try{Se?.contentWindow?.postMessage(e,"*")}catch{}};let fo=Se.contentDocument?.readyState==="complete"&&/component-preview/.test(Se.contentDocument?.URL||""),Pt=!1,ot=null;const Ca=()=>{fo=!0,ot&&c()&&(xt({type:"mount",payload:ot}),Pt=!0),ot=null};Se.addEventListener("load",Ca),i(()=>Se.removeEventListener("load",Ca));const Us=e=>{if(!fo){ot=e;return}if(!Pt){Pt=!0,xt({type:"mount",payload:e});return}ot=e,fo=!1,Pt=!1,Se.src=Se.src};let Ot="";const bo=(e=!1)=>{const t=N.get(a.compTab.peek());if(!(!t||t.state!=="ok"||!t.js)){if(a.viewMode.peek()==="write"){t.previewStale=!0;return}!e&&Ot===t.name&&!t.previewStale||(t.previewStale=!1,Ot=t.name,Us({compiledJs:t.js,tagName:`dm-${t.name}`,props:t.previewProps}))}},at=e=>{const t=++e.seq;return e.state="compiling",a.compTab.peek()===e.name&&(a.compState.value="compiling"),e.compiling=(async()=>{let o;try{o=await h.components.compile(e.name,e.form.source)}catch(s){o={errors:[{type:"request",message:s.message||"Compile request failed."}]}}if(!(t!==e.seq||!c())){if(o.errors?.length?(e.state="error",e.errors=o.errors.map(s=>`[${s.type}] ${s.message}`).join(`
|
|
138
|
+
`),e.js=null):(e.state="ok",e.errors="",e.js=o.js),a.compTab.peek()!==e.name){e.previewStale=!0;return}a.compState.value=e.state,a.compErrors.value=e.errors,e.state==="ok"&&(yo(),bo(!0))}})().finally(()=>{e.compiling&&t===e.seq&&(e.compiling=null)}),e.compiling};let Rt=null,At=null;i(()=>clearTimeout(Rt));const xa=()=>{const e=N.get(a.compTab.peek());if(!e||!ae.template)return;a.compReady.value=!1;const t=ut(e.form.source);for(const o of zt)ae[o].setValue(t[o]),ae[o].textarea.readOnly=e.readOnly;a.compReadOnly.value=e.readOnly,a.compProject.value=e.form.project,qo(O("#comp-project"),e.form.project),a.compBundled.value=e.form.bundled,a.compState.value=e.state,a.compErrors.value=e.errors,a.compNote.value="",a.compReady.value=!0,yo(),e.state==="ok"?bo():e.state||at(e)},vo=()=>{a.compTab.peek()&&(a.compTab.value="",a.compReady.value=!1)},Pa={page:"details",block:"details",component:"details"};let $t="";i(M.effect(()=>{const e=Qo.value;e!==$t&&($t&&(Pa[$t]=a.dockTab.peek()),$t=e,e&&(a.dockTab.value=Pa[e]||"details",e==="page"&&a.dockTab.peek()==="history"&&a.dockOn.peek()&&m?.isEdit&&!Qe&&pe()))}));const go=async(e,{quiet:t=!1}={})=>{if(je.has(e))return je.get(e);if(N.has(e))return N.get(e);const o=(async()=>{try{const s=await h.components.get(e);if(!c()||!P.includes(X({kind:"component",id:e})))return null;const r=s.readOnly===!0||s.origin==="plugin",n={project:s.meta?.project||"",bundled:!!s.bundled,source:os(ut(s.source))},l={name:e,comp:s,saved:n,base:Fo({name:e,...n}),form:{...n},meta:s.meta||{},readOnly:r,state:"",errors:"",js:null,seq:0,compiling:null,previewProps:{},previewStale:!1};let g=null;try{g=S.get(_e(e))}catch{}const $=nr(g,l.base);if(t&&(!$.ok||$.stale||r))return null;const G=$o({kind:"component",id:e});if(t&&!G)return null;if($.ok&&!r){const Te=g.at?` (${qe(g.at)})`:"",Co=G&&!$.stale||await E.confirm($.stale?`You left unsaved changes to the component "${e}"${Te}, but it has been saved since. Restore your version anyway?`:`You left unsaved changes to the component "${e}"${Te}. Restore them?`);if(!c())return null;Co&&(l.form={project:String(g.project??""),bundled:g.bundled===!0,source:String(g.source)},G&&!$.stale&&E.toast(`Your unsaved changes to the component "${e}" are back.`,{type:"info"}))}return g&&J(_e(e)),N.set(e,l),ke(),l}catch(s){return c()&&a.compTab.peek()===e&&(a.compNote.value=`Could not open <dm-${e}>: ${s.message||"unknown error"}`),null}finally{je.delete(e)}})();return je.set(e,o),o},st=async e=>{if(!e||!le.has(e))return;const t=X({kind:"component",id:e});({ids:P}=Bo(P,t)),a.active.value=t,a.blockTab.value="",a.cssMode.peek()&&(a.cssMode.value=!1),a.viewMode.peek()==="preview"&&(a.viewMode.value="split"),a.compTab.value=e,te(),q(),z();let o=N.get(e);o||(a.compReady.value=!1,a.compReadOnly.value=!1,a.compState.value="",a.compNote.value=`Opening <dm-${e}>\u2026`,o=await go(e)),!(!o||!c()||a.compTab.peek()!==e)&&(xa(),q(),z(),ae[a.compPart.peek()]?.focus())},Hs=(e,t)=>{const o=N.get(a.compTab.peek());if(!o||!a.compReady.peek()||o.readOnly)return;const s=ut(o.form.source);s[e]=t,o.form.source=os(s),clearTimeout(Rt),At=o,Rt=setTimeout(()=>{At=null,ke(),at(o)},300)};i(M.effect(()=>{const e=a.compProject.value,t=a.compBundled.value,o=N.get(a.compTab.peek());!o||!a.compReady.peek()||o.readOnly||o.form.project===e&&o.form.bundled===t||(o.form.project=e,o.form.bundled=t,ke())}));const Ee=O("#pe-comp-props");let ho=[];const ko=e=>{Ee.replaceChildren();const t=document.createElement("p");t.className="pe-dock-none",t.textContent=e,Ee.appendChild(t)};function yo(){const e=N.get(a.compTab.peek());if(!e){ho=[],ko("Open a component to try its props.");return}const{rows:t,error:o}=as(ut(e.form.source).props,e.previewProps);if(ho=t,o){ko("The <props> block is not valid JSON - fix it to see the props here.");return}if(!t.length){e.previewProps={},ko("No props declared. Add them in the <props> block.");return}e.previewProps=Object.fromEntries(t.map(s=>[s.name,s.value])),Ee.replaceChildren();for(const s of t){const r=document.createElement("div");r.className="pe-comp-prop",r.tabIndex=0,r.dataset.prop=s.name;const n=document.createElement("label");n.className="pe-comp-prop-label",n.textContent=s.label,n.title=`${s.name} (${s.type})`;const l=s.type==="array"||s.type==="object"?document.createElement("textarea"):document.createElement("input");s.type==="boolean"?(l.type="checkbox",l.className="form-check",l.checked=ss(s.type,s.value)):(l.tagName==="INPUT"?l.type=s.type==="number"?"number":"text":l.rows=2,l.className="form-input form-input--sm pe-comp-prop-input",l.value=ss(s.type,s.value)),l.setAttribute("aria-label",s.label);const g=()=>{e.previewProps[s.name]=ir(s.type,s.type==="boolean"?l.checked:l.value),Ot===e.name&&xt({type:"update",payload:{props:e.previewProps}})};l.addEventListener("input",g),l.addEventListener("change",g);const $=document.createElement("button");$.type="button",$.className="pe-kebab",$.setAttribute("aria-label",`Actions for ${s.label}`),$.title="Actions",$.textContent="\u22EE",r.append(n,l,$),Ee.appendChild(r)}}const Ie=async(e,t)=>{try{await navigator.clipboard.writeText(e),E.toast(`${t} copied.`,{type:"success"})}catch{E.toast(e,{type:"info",duration:8e3})}},Js=e=>{const t=N.get(a.compTab.peek()),o=ho.find(n=>n.name===e);if(!t||!o)return[];const s=sr(o.name,o.type,t.previewProps[o.name]),r=`dm-${t.name}`;return[{header:o.label},{label:"Reset to its default",icon:"rotate-ccw",action:()=>{delete t.previewProps[o.name];const n=as(ut(t.form.source).props,{}).rows.find(l=>l.name===o.name);t.previewProps[o.name]=n?n.value:null,yo(),Ot===t.name&&xt({type:"update",payload:{props:t.previewProps}})}},{label:"Copy as an attribute",icon:"copy",disabled:!s,action:()=>Ie(s,"Attribute")},{label:"Copy the tag with this value",icon:"code",action:()=>Ie(`<${r}${s?` ${s}`:""}></${r}>`,"Tag")},{label:"Copy the prop name",icon:"copy",action:()=>Ie(o.name,"Name")},{type:"divider"},{label:"Edit in <props>",icon:"edit",action:()=>{a.compPart.value="props";const n=ae.props?.textarea;if(!n)return;const l=n.value.indexOf(`"${o.name}"`);n.focus(),l>=0&&n.setSelectionRange(l,l+o.name.length+2)}}]};{const e=E.contextMenu(Ee,{cursor:"glyph",match:".pe-comp-prop",inherit:!1,density:"compact",items:t=>Js(t.dataset.prop)});i(()=>e?.destroy?.()),Ee.addEventListener("click",t=>{const o=t.target.closest(".pe-kebab");o&&Ce(o)}),Ee.addEventListener("keydown",t=>{const o=t.target.closest(".pe-comp-prop");o&&t.key==="F10"&&t.shiftKey&&(t.preventDefault(),Ce(o))})}let Fe=!1;const Oa=()=>Ft()||"",Lt=async({project:e=Oa(),seed:t="",content:o=gr,css:s=""}={})=>{if(!Fe){Fe=!0;try{const n=await ls({kind:"block",projects:se,taken:[...be],project:e,name:t,check:Rn,register:i,create:async(l,g)=>{await h.blocks.put(l,Za({name:l,project:g,bundled:!1,content:o,css:s}))}});if(!c())return;if(!n){te();return}if(await _o(),!c())return;pt("#/blocks"),E.toast(`Created the block "${n}".`,{type:"success"}),Be(n)}finally{Fe=!1}}},nt=async({name:e="",source:t=ar,project:o=Oa()}={})=>{if(!Fe){Fe=!0;try{const s=await ls({kind:"component",projects:se,taken:[...le],project:o,name:e,register:i,check:(r,n)=>rr(r,{isNew:!0,taken:n}),create:async(r,n)=>{await h.components.put(r,{source:t,bundled:!1,meta:n?{project:n}:{}})}});if(!c())return;if(!s){te();return}if(await Ko(),!c())return;pt("#/components"),E.toast(`Created the component "${s}".`,{type:"success"}),st(s)}finally{Fe=!1}}},Ra=k.querySelector(".dm-plugin-body")||k,Ws=M.computed(()=>{a.blockRev.value,a.compRev.value,a.docRev.value,W(K()),a.base.value;const e=I(a.active.value),t=[...Re().map(s=>X({kind:"page",id:s.id})),...ge().map(s=>X({kind:"block",id:s})),...Ye().map(s=>X({kind:"component",id:s}))];let o=[];return e?.kind==="block"||e?.kind==="component"?o=[a.active.value]:e?.kind==="page"&&m&&(o=[a.active.value,...ge().filter(yt).map(s=>X({kind:"block",id:s}))]),er(t,o)}),Ys={...p,...a,dirty:ks,pageDirty:gs,activeDirty:ys,cssUnsaved:hs,hasPage:ws,activeKind:Xo,canView:Zo,viewHref:Ss,problem:Es,statusText:Ms,saveAll:Ws,dockKind:Qo,blockTagText:Ts,compStateText:Cs,compTagText:xs,anySelected:Ps,allSelected:Os,someSelected:Rs},rt=(e,t)=>Ue(e,t),wo={save:()=>io(!1),saveAllOpen:()=>io(!0),discard:async()=>{if(await Ea(),!c())return;const e=I(a.active.peek());if(e?.kind==="block"){const t=L.get(e.id);if(!t||!de(t)||!await E.confirm(`Discard the changes to "${e.id}" you have not saved?`))return;t.form={...t.saved},J(Ve(e.id)),Sa(),Le()}else if(e?.kind==="component"){const t=N.get(e.id);if(!t||!he(t)||!await E.confirm(`Discard the changes to the component "${e.id}" you have not saved?`))return;t.form={...t.saved},t.state="",J(_e(e.id)),xa(),ke()}else{if(!m||e?.kind==="page"&&e.id!==m.id||!(Wt()||ht())||!await E.confirm("Discard the changes to this page you have not saved?"))return;Ze(JSON.parse(a.base.peek())),p.cssOpened.peek()&&(p.css.value=ee.peek()),J(ze(m.id)),J(ft),Xe()}q(),z(),H()},newPage:()=>ue(""),newBlock:()=>Lt(),newComponent:()=>nt(),toggleSection:e=>{const t={here:"secHere",pages:"secPages",blocks:"secBlocks",components:"secComps"}[e];t&&(a[t].value=!a[t].peek(),rt(t,a[t].peek()))},toggleExplorer:()=>{a.explorerOn.value=!a.explorerOn.peek(),rt("explorer",a.explorerOn.peek())},toggleDock:()=>{a.dockOn.value=!a.dockOn.peek(),rt("dock",a.dockOn.peek()),a.dockOn.peek()&&a.dockTab.peek()==="history"&&!Qe&&pe()},showDock:e=>{a.dockTab.value=e,e==="history"&&m?.isEdit&&!Qe&&pe()},schemaChanged:()=>{a.seoSchemaNote.value=Po(p.seoSchema.peek()).error},insertSchemaExample:async e=>{const t=e?.target,o=t?.value||"";if(t&&(t.value=""),!o||p.seoSchema.peek().trim()&&!await E.confirm("Replace the structured data with the example?"))return;const s=p.urlPath.peek().trim()||m?.id||"/";p.seoSchema.value=mn(o,{title:p.seoTitle.peek().trim()||p.title.peek().trim(),description:p.seoDesc.peek().trim()||p.description.peek().trim(),url:`${location.origin}${s}`,siteName:Y?.title||Y?.seo?.defaultTitle||""}),a.seoSchemaNote.value=""},setBlockPart:e=>{a.blockPart.value=e==="css"?"css":"html",(e==="css"?me:oe)?.focus()},formatBlock:()=>{const e=a.blockPart.peek()==="css",t=e?me:oe;!t||!a.blockReady.peek()||Ho(Ke(t.textarea,e?"css":"html"))},copyShortcode:async()=>{const e=a.blockTab.peek();e&&await Ie(Nn(e),"Shortcode")},setCompPart:e=>{a.compPart.value=zt.includes(e)?e:"template",ae[a.compPart.peek()]?.focus()},copyComponent:()=>{const e=N.get(a.compTab.peek());e&&nt({name:Lo(e.name,[...le]),source:e.form.source})},setMode:e=>{a.cssMode.peek()&&(a.cssMode.value=!1),a.viewMode.value=jo(e),rt("viewMode",a.viewMode.peek()),e!=="write"&&(H(!0),bo())},toggleFullscreen:()=>{a.fullscreen.value=!a.fullscreen.peek(),rt("fullscreen",a.fullscreen.peek())},toggleCss:()=>{a.blockTab.peek()||a.compTab.peek()||(a.cssMode.peek()?(a.cssMode.value=!1,a.viewMode.value=jo(Pe().viewMode),Q.refresh()):(ia(),a.cssMode.value=!0,a.viewMode.value="split"))},formatMarkdown:()=>{Jt(),Ho(Ke(_,"md"))},toggleFold:()=>{a.folded.peek()?Q.unfoldAll():Q.foldAll(),a.folded.value=!a.folded.peek(),We.paint()},setWidth:e=>{a.liveWidth.value=e},refreshPreview:()=>{if(a.compTab.peek()){const e=N.get(a.compTab.peek());e&&at(e)}else H(!0)},share:async()=>{if(!m?.isEdit){E.toast("Save the page first - a share link needs a page to point at.",{type:"warning"});return}await br(m.id)},toggleAll:e=>{const t=!!e?.target?.checked;a.versions.peek().forEach(o=>{o.checked.value=t})},deleteSelected:async()=>{const e=a.versions.peek().filter(o=>o.checked.peek()).map(o=>o.filename),t=e.length;if(!(!t||!await E.confirm(`Delete ${t} version${t>1?"s":""}? This cannot be undone.`)))try{await h.versions.bulkDelete(m.id,e),E.toast(`${t} version${t>1?"s":""} deleted.`,{type:"success"}),await pe()}catch(o){E.toast("Delete failed: "+o.message,{type:"error"})}},saveNamed:async()=>{if(!m?.isEdit)return;const e=m;await vr(e.id)&&c()&&m===e&&await pe()},closeCompare:()=>{a.compare.value=null,Mt=null},restoreCompared:()=>ma(Mt)};Ge.querySelectorAll("[data-tip]").forEach(e=>E.tooltip(e,{content:e.getAttribute("data-tip"),position:"top"}));const Gs=M.applyBindings(Ys,Ra,{methods:wo});i(()=>Gs.dispose()),oe=zo(O('.pe-block-edit[data-part="html"]'),{ariaLabel:"Block template HTML",language:"html",onChange:e=>Ma("content",e)}),me=zo(O('.pe-block-edit[data-part="css"]'),{ariaLabel:"Block CSS",language:"css",placeholder:`/* Scoped to this block - e.g.
|
|
139
139
|
color: var(--dm-primary);
|
|
140
|
-
h3 { font-size: 1.25rem; } */`,onChange:e=>
|
|
140
|
+
h3 { font-size: 1.25rem; } */`,onChange:e=>Ma("css",e)}),i(()=>{oe.destroy(),me.destroy()});for(const e of zt)ae[e]=zo(O(`.pe-comp-edit[data-part="${e}"]`),{ariaLabel:`Component <${e}> block`,language:{template:"html",props:"json",script:"js",style:"css"}[e],onChange:t=>Hs(e,t)});i(()=>{for(const e of zt)ae[e].destroy()});const Me=O(".pe-tabs-list"),Nt=e=>e.target.closest(".pe-tab")?.dataset.id||"",Aa=e=>{const t=Nt(e);t&&(e.target.closest(".pe-tab-close")?et(t):Ct(t))},$a=e=>{e.button===1&&Nt(e)&&(e.preventDefault(),et(Nt(e)))},La=e=>{const t=Nt(e);!t||e.target.closest(".pe-tab-close")||(e.key==="Enter"||e.key===" "?(e.preventDefault(),Ct(t)):e.key==="Delete"?(e.preventDefault(),et(t)):e.key==="F10"&&e.shiftKey&&(e.preventDefault(),Ce(e.target.closest(".pe-tab"))))};Me.addEventListener("click",Aa),Me.addEventListener("auxclick",$a),Me.addEventListener("keydown",La),i(()=>{Me.removeEventListener("click",Aa),Me.removeEventListener("auxclick",$a),Me.removeEventListener("keydown",La)});const So=e=>{for(const t of e)et(t)},Xs=async e=>{const t=I(e);if(t?.id)if(t.kind==="page")a.active.peek()!==e&&await ue(t.id),c()&&m?.id===t.id&&Ba();else if(t.kind==="block"){const s=(L.get(t.id)||await lo(t.id,{quiet:!0}))?.form||Xa(await h.blocks.get(t.id).catch(()=>null));c()&&Lt({seed:Lo(t.id,[...be]),content:s.content,css:s.css})}else{const s=(N.get(t.id)||await go(t.id,{quiet:!0}))?.form.source??(await h.components.get(t.id).catch(()=>null))?.source??"";c()&&nt({name:Lo(t.id,[...le]),source:s})}},Zs=e=>{const t=I(e);if(!t||!P.includes(e))return[];const o=P.indexOf(e),s=Ne.get(e)?.label.peek()||t.id,r=n=>{P=Wn(P,e,n),q()};return[{header:s},{label:"Close",icon:"x",action:()=>et(e)},{label:"Close others",icon:"x-circle",disabled:P.length<2,action:()=>So(Io(P,e,"others"))},{label:"Close to the right",icon:"chevrons-right",disabled:o>=P.length-1,action:()=>So(Io(P,e,"right"))},{label:"Close all",icon:"layers",action:()=>So(Io(P,e,"all"))},{type:"divider"},{label:"Move left",icon:"arrow-left",disabled:o===0,action:()=>r(-1)},{label:"Move right",icon:"arrow-right",disabled:o>=P.length-1,action:()=>r(1)},{type:"divider"},{label:t.kind==="page"?"Clone page\u2026":`Duplicate ${t.kind}\u2026`,icon:"copy",disabled:!t.id,action:()=>Xs(e)},{label:"Copy link",icon:"link",disabled:!t.id,action:()=>Ie(`${location.origin}${location.pathname}#${es(t)}`,"Link")}]};{const e=E.contextMenu(Me,{cursor:"glyph",match:".pe-tab",inherit:!1,density:"compact",items:t=>Zs(t.dataset.id)});i(()=>e?.destroy?.())}const Na=O(".pe-ex-scroll"),Da=e=>{const t=e.target.closest(".pe-ex-item"),o=t?.dataset.id||"";if(!o)return;const s=o.indexOf(":"),r=o.slice(0,s),n=o.slice(s+1),l=()=>{ye.has(n)?ye.delete(n):ye.add(n);try{S.set(us,[...ye])}catch{}z()};if(r==="folder"){l();return}if(r==="page"&&e.target.closest(".pe-ex-chev")&&t.classList.contains("is-folder")){l();return}r==="page"?ue(n):r==="block"?Be(n):r==="component"&&st(n)};Na.addEventListener("click",Da),i(()=>Na.removeEventListener("click",Da));let Eo=null;i(()=>clearTimeout(Eo)),_.addEventListener("input",()=>{Q.refresh(),ce.scrollTop=_.scrollTop,!gt&&(clearTimeout(Eo),Eo=setTimeout(ra,120),H())}),_.addEventListener("scroll",()=>{ce.scrollTop=_.scrollTop}),ve.addEventListener("scroll",()=>{ce.scrollTop=ve.scrollTop}),i(M.effect(()=>{for(const e of Object.keys(p))e!=="css"&&e!=="cssOpened"&&p[e].value;a.bodyRev.value,H()})),i(M.effect(()=>{a.bodyRev.value,a.docRev.value;const e=m?Ht():"";kt=m?$n(e,bt.map(t=>t.name),bt.filter(t=>t.shortTag).map(t=>t.name)):[],ta=m?Jn(e,le):[],wt(),z()})),i(M.effect(()=>{a.blockRev.value,a.compRev.value,a.docRev.value,a.active.value,a.filter.value,p.title.value,q(),z()})),i(M.effect(()=>{ce.hidden=!!(a.blockTab.value||a.compTab.value)}));let Mo=null;i(()=>clearTimeout(Mo)),i(M.effect(()=>{const e=p.css.value;a.cssMode.value&&(Ut.paint(),Cr(ce,e,ve),clearTimeout(Mo),Mo=setTimeout(oa,400))}));{const e=O(".pe-version-list"),t=E.contextMenu(e,{cursor:"glyph",match:"tr[data-file]",inherit:!1,density:"compact",items:o=>{const s=oo(o.dataset.file);return s?[{header:rs(s)},{label:"Show beside the editor",icon:"columns",action:()=>ao(s)},{label:"Restore this version\u2026",icon:"rotate-ccw",action:()=>ma(s.filename)},{type:"divider"},{label:"Delete\u2026",icon:"trash",danger:!0,action:()=>js(s)}]:[]}});i(()=>t?.destroy?.()),e.addEventListener("click",o=>{const s=o.target.closest("tr[data-file]");if(!s||o.target.matches("input"))return;const r=o.target.closest(".pe-kebab");if(r){Ce(r);return}const n=oo(s.dataset.file);n&&ao(n)}),e.addEventListener("keydown",o=>{const s=o.target.closest("tr[data-file]");if(s){if(o.key==="F10"&&o.shiftKey)o.preventDefault(),Ce(s);else if(o.key==="Enter"&&o.target===s){const r=oo(s.dataset.file);r&&ao(r)}}})}xr(O("#editor-body"),O("#editor-divider"),i),ln({image:e=>(a.cssMode.value=!1,Pr(e)),scribe:e=>pn(e),card:e=>en.open(e,{onMediaBrowse:()=>h.media.list().catch(()=>[])}),timeline:e=>tn.open(e,{onPreview:async t=>{try{return await h.pages.preview(t)}catch{return null}},onListCollections:()=>h.collections.list().catch(()=>[]),onGetCollection:t=>h.collections.get(t).catch(()=>null)})}),to.onEffects(e=>nn.open(e)),to.onHelp(()=>Va()),to.onTutorial(e=>un(e));const ja=e=>{if(!c())return;const t=or(e);t&&(e.preventDefault(),t==="save"?io(!1):t==="layout"?wo.setMode(Yn(a.viewMode.peek())):wo.toggleExplorer())};document.addEventListener("keydown",ja),i(()=>document.removeEventListener("keydown",ja));const it=k.querySelector(".dm-plugin-banner-name");i(M.effect(()=>{const e=a.curId.value,t=Xo.value,o=a.blockTab.value||a.compTab.value,s=t==="block"?"Blocks":t==="component"?"Components":"Pages",r=t==="block"||t==="component"?o:e===null?"Editor":Mn(!!e,p.title.value,e);if(!c()||(document.title=`${r} - Domma CMS`,!it))return;const n=it.querySelector(".dm-plugin-banner-parent"),l=it.lastChild;n&&l&&l.nodeType===3&&it.querySelector(".dm-plugin-banner-sep")?(n.textContent=s,l.textContent=r):it.textContent=`${s} \u203A ${r}`})),dt(d,{id:"page-editor-back",icon:"arrow-left",title:"Back to Pages",onClick:()=>R.navigate("/pages")}),dt(d,{id:"page-editor-help",icon:"help-circle",title:"Editor reference",onClick:()=>Va()});const Qs=dt(d,{id:"page-editor-view",icon:"external-link",title:"View on the site",onClick:()=>{m?.isEdit&&window.open(p.urlPath.peek().trim()||m.id,"_blank","noopener")}}),Ba=()=>{if(!m?.isEdit||a.saving.peek()||!c())return;const e=m,t=K(),o=Ro({...t,urlPath:e.id});if(o.length){E.toast(o[0].text,{type:"warning"});return}const s=vn({page:e.page,frontmatter:{...Gt(t,e,Xt(t)),adminTags:t.adminTags},body:t.body,source:e.id,pages:D,onCloned:async r=>{c()&&(await ha(),c()&&ue(r))}});i(()=>s.close())},To=dt(d,{id:"page-editor-clone",icon:"copy",title:"Clone page",onClick:Ba});i(M.effect(()=>{const e=Zo.value;for(const t of[Qs,To])t&&(t.disabled=!e);To&&(To.title=e?"Clone page":"Open a saved page to clone it")})),dt(d,{id:"page-editor-settings",icon:"settings",title:"Editor settings - font, size, spacing",onClick:()=>{Yo=fr(Jo,Wo)}}),i(qa(Ra,{position:"bottom"})),Domma.icons.scan();let Ia={ids:[],active:""};try{Ia=Zn(S.get(ps))}catch{}const lt=Ha(b),ct=tr(lt,Ia,history.state?.pe===1);P=ct.ids.filter(e=>{const t=I(e);return t.kind==="page"||(t.kind==="component"?le:be).has(t.id)}),ba=!1;for(const e of P){const t=I(e);t.kind==="page"&&ka(t.id)}q(),z(),wt();for(const e of Ae)lo(e,{quiet:!0}).then(()=>{c()&&(q(),z())});for(const e of P){const t=I(e);t.kind==="component"&&e!==ct.active&&xe(t)&&go(t.id,{quiet:!0}).then(()=>{c()&&(q(),z())})}const Fa=lt&&!lt.id&<.kind!=="page";ct.active&&P.includes(ct.active)?await Ct(ct.active):P.length?await Ct(P[0]):Fa||te(),Fa&&c()&&(await(lt.kind==="block"?Lt:nt)({project:Ft(b)||""}),c()&&te())}};function qo(d,b){!d||typeof b!="string"||!b||[...d.options].some(v=>v.value===b)||(d.append(Object.assign(document.createElement("option"),{value:b,textContent:`${b} (custom)`})),d.value=b)}function Cr(d,b,v){d.replaceChildren(...String(b).split(`
|
|
141
141
|
`).map((i,y)=>{const u=document.createElement("span");u.className="editor-line-number-row";const w=document.createElement("span");w.className="fold-toggle fold-toggle--empty";const k=document.createElement("span");return k.className="editor-line-num",k.textContent=String(y+1),u.append(w,k),u})),d.scrollTop=v.scrollTop}function xr(d,b,v){if(!d||!b)return;const i=15,y=85,u=c=>Math.min(y,Math.max(i,c));let w=u(parseFloat(Pe().splitRatio)||50);const k=c=>{d.style.setProperty("--dm-editor-split",w+"%"),b.setAttribute("aria-valuenow",String(Math.round(w))),c&&Ue("splitRatio",w)};k(!1);const x=c=>{const A=d.getBoundingClientRect();A.width&&(w=u((c.clientX-A.left)/A.width*100)),k(!1)},j=()=>{document.removeEventListener("pointermove",x),document.removeEventListener("pointerup",j),d.classList.remove("is-resizing"),b.classList.remove("is-dragging"),k(!0)};v(j),b.addEventListener("pointerdown",c=>{window.matchMedia("(max-width: 768px)").matches||(c.preventDefault(),d.classList.add("is-resizing"),b.classList.add("is-dragging"),document.addEventListener("pointermove",x),document.addEventListener("pointerup",j))}),b.addEventListener("dblclick",()=>{w=50,k(!0)}),b.addEventListener("keydown",c=>{const A=c.shiftKey?10:2;let V=w;if(c.key==="ArrowLeft")V=w-A;else if(c.key==="ArrowRight")V=w+A;else if(c.key==="Home")V=i;else if(c.key==="End")V=y;else if(c.key==="Enter"||c.key===" ")V=50;else return;c.preventDefault(),w=u(V),k(!0)})}async function Pr(d){const b=d.selectionStart,v=d.selectionEnd,i=await bn({title:"Upload or insert an image"});if(!i||!d.isConnected)return;let y=i.split("/").pop().split("?")[0];try{y=decodeURIComponent(y)}catch{}const u=y.replace(/[\\[\]]/g,"\\$&"),w=i.replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/ /g,"%20");d.setSelectionRange(b,v),sn(d,``),d.dispatchEvent(new Event("input",{bubbles:!0}))}function Or(d,b,v){const i=Object.entries(b||{});return d?(d.replaceChildren(...i.map(([y,u])=>new Option(u?.label||y,y))),i.some(([y])=>y===v)?v:i[0]?.[0]||v):v}async function Rr(d,b){if(!d)return;let v=[];try{const x=await h.get("/collections/roles/entries?limit=100");v=Array.isArray(x)?x:x?.entries||[]}catch{}const i=v.map(x=>({name:x.data?.name,label:x.data?.label||x.data?.name,level:x.data?.level??99})).filter(x=>x.name&&x.level!==0).sort((x,j)=>j.level-x.level),y=(x,j)=>Object.assign(document.createElement("option"),{value:x,textContent:j}),u=()=>{typeof b=="string"&&b&&![...d.options].some(x=>x.value===b)&&d.append(y(b,`${b} (custom)`))};if(!i.length)return u();const w=document.createElement("optgroup");w.label="Role and above",i.forEach(x=>w.append(y(x.name,`${x.label} and above`)));const k=document.createElement("optgroup");k.label="Role only",i.forEach(x=>k.append(y(`=${x.name}`,`${x.label} only`))),d.replaceChildren(y("public","Public - everyone"),w,k,y("private","Private - super-admin only")),u()}
|
package/docs/configuration.md
CHANGED
|
@@ -1538,11 +1538,13 @@ step Ctrl+Z undoes. The toolbar's format button does the same.
|
|
|
1538
1538
|
more text stays), runs of blank lines down to one, pipe tables lined up.
|
|
1539
1539
|
Indentation is never changed - four spaces would turn text into a code block -
|
|
1540
1540
|
and fenced code is left exactly as written. Folded shortcodes are unfolded first.
|
|
1541
|
-
The exception is
|
|
1542
|
-
`[
|
|
1543
|
-
by nesting, so the structure reads at a glance
|
|
1544
|
-
|
|
1545
|
-
(`unindentShortTags()` in `markdown.js
|
|
1541
|
+
The exception is the container short tags (`[grid]`, `[col]`, `[card]`, `[tabs]`,
|
|
1542
|
+
`[tab]`…, `SHORT_TAG_CONTAINERS`): their lines and the content inside them are
|
|
1543
|
+
indented by nesting, so the structure reads at a glance; a nested list or fenced
|
|
1544
|
+
code keeps its own shape within that. The renderer takes the indent off before
|
|
1545
|
+
anything else (`unindentShortTags()` in `markdown.js`: a tag line loses all of
|
|
1546
|
+
it, a container's content the indent its lines share), so it never reaches the
|
|
1547
|
+
page - and a page written flat is read exactly as before.
|
|
1546
1548
|
- **Block and component HTML, JS**: re-indented by nesting only; the code itself
|
|
1547
1549
|
is never rewritten. `<pre>` and `<textarea>` keep their lines; a `<script>` or
|
|
1548
1550
|
`<style>` inside HTML is indented as JS or CSS.
|
|
@@ -1555,14 +1557,14 @@ browser with the rest of the editor's settings: indent with spaces or tabs, the
|
|
|
1555
1557
|
indent size (2, 4 or 8 - also how wide a tab shows), *Keep a file's own
|
|
1556
1558
|
indentation* (on: a block, component or CSS already in tabs or fours keeps them;
|
|
1557
1559
|
the page body always takes the setting), *Auto-indent*, *Carry lists and quotes
|
|
1558
|
-
on*, *Indent short tags
|
|
1560
|
+
on*, *Indent inside short tags*, *Line up tables when formatting*, and
|
|
1559
1561
|
*Format on save* (off by default: Save formats the tab on show first; text that
|
|
1560
1562
|
cannot be formatted is saved as it is).
|
|
1561
1563
|
|
|
1562
1564
|
While typing, **Enter** keeps the line's indentation. In the body it carries a
|
|
1563
1565
|
list or quote on (numbers count up, a task box comes back empty) and Enter on an
|
|
1564
|
-
empty item ends the list; Shift+Enter is a plain new line. After
|
|
1565
|
-
next line
|
|
1566
|
+
empty item ends the list; Shift+Enter is a plain new line. After an opening short
|
|
1567
|
+
tag the next line goes one deeper; after a closing one it stays level. In code it goes one
|
|
1566
1568
|
deeper after `{`, `(`, `[`, an opening tag or `{{#if}}`, and with the caret
|
|
1567
1569
|
between a pair the closer moves to its own line. **Tab / Shift+Tab** indent or
|
|
1568
1570
|
outdent every selected line when the selection spans lines.
|
package/package.json
CHANGED
|
@@ -423,10 +423,29 @@ export function buildBlockStyleTag(blockName, css) {
|
|
|
423
423
|
* @param {object} e - Collection entry { id, data, meta }
|
|
424
424
|
* @returns {string}
|
|
425
425
|
*/
|
|
426
|
+
/**
|
|
427
|
+
* A filled block goes into Markdown as HTML, and a blank line ends an HTML block:
|
|
428
|
+
* what follows, indented, marked then shows as code - raw tags on the page. A
|
|
429
|
+
* placeholder alone on its line with nothing to fill it (` {{bio}}` with no
|
|
430
|
+
* bio) made exactly that blank line, so such a line goes. The template's OWN
|
|
431
|
+
* blank lines stay: they are how a template hands a Markdown value to marked.
|
|
432
|
+
* Each line is filled by itself, in one pass as before.
|
|
433
|
+
*/
|
|
434
|
+
function fillTemplateLines(template, fill) {
|
|
435
|
+
const PLACEHOLDER = /\{\{\{(\w+)\}\}\}|\{\{([\w_]+)(?::([^{}]+))?\}\}/g;
|
|
436
|
+
const out = [];
|
|
437
|
+
for (const line of template.split('\n')) {
|
|
438
|
+
const filled = line.replace(PLACEHOLDER, fill);
|
|
439
|
+
if (!filled.trim() && line.trim() && /\{\{/.test(line)) continue;
|
|
440
|
+
out.push(filled);
|
|
441
|
+
}
|
|
442
|
+
return out.join('\n');
|
|
443
|
+
}
|
|
444
|
+
|
|
426
445
|
function interpolateBlockTemplate(blockTemplate, e) {
|
|
427
446
|
// One pass: a raw value put in (pre-rendered HTML) is never read again for
|
|
428
447
|
// placeholders - text that shows "{{title}}" keeps it.
|
|
429
|
-
return blockTemplate
|
|
448
|
+
return fillTemplateLines(blockTemplate, (_, raw, key, fmt) => {
|
|
430
449
|
// Entry values never carry a shortcode into the page (inertShortcodes);
|
|
431
450
|
// the template's own markup is untouched.
|
|
432
451
|
if (raw !== undefined) return inertShortcodes(String(e.data?.[raw] ?? ''));
|
|
@@ -510,7 +529,7 @@ function renderCollectionBlocks(entries, blockTemplate, emptyMsg, ctaOpts, cols,
|
|
|
510
529
|
function renderBlockFromAttrs(tmpl, attrs, blockName = '') {
|
|
511
530
|
// Triple-brace raw pass-through and `{{field}}` (optional `:format`, e.g. `{{date:DD MMM YYYY}}`)
|
|
512
531
|
// in one pass, so a raw value is never read again for placeholders.
|
|
513
|
-
const rendered = tmpl
|
|
532
|
+
const rendered = fillTemplateLines(tmpl, (_, rawKey, key, fmt) => {
|
|
514
533
|
if (rawKey !== undefined) return String(attrs[rawKey] ?? '');
|
|
515
534
|
const raw = attrs[key] ?? '';
|
|
516
535
|
if (fmt) {
|
|
@@ -1858,25 +1877,92 @@ export function parseShortcodeAttrs(attrStr) {
|
|
|
1858
1877
|
let scrubCallId = 0;
|
|
1859
1878
|
|
|
1860
1879
|
/**
|
|
1861
|
-
* The container short tags
|
|
1862
|
-
* (SHORT_TAG_CONTAINERS in admin/js/lib/code-format.js -
|
|
1863
|
-
* tests/admin/code-format.test.js checks). Their
|
|
1864
|
-
*
|
|
1865
|
-
*
|
|
1866
|
-
*
|
|
1880
|
+
* The container short tags the editor's format indents by nesting, with the
|
|
1881
|
+
* content inside them (SHORT_TAG_CONTAINERS in admin/js/lib/code-format.js -
|
|
1882
|
+
* keep the two lists the same; tests/admin/code-format.test.js checks). Their
|
|
1883
|
+
* processors replace only the tag, and marked reads four spaces as a code
|
|
1884
|
+
* block, so before anything else runs the indent comes off again: a tag line
|
|
1885
|
+
* loses all of it, and the content of each container loses the indent its
|
|
1886
|
+
* lines share - a page written flat is untouched, and what is indented further
|
|
1887
|
+
* (a nested list, code) keeps the difference. The indent is only ever for the reader.
|
|
1867
1888
|
*/
|
|
1868
1889
|
export const SHORT_TAG_CONTAINERS = Object.freeze([
|
|
1869
1890
|
'grid', 'row', 'col', 'card', 'box', 'header', 'footer', 'tabs', 'tab', 'accordion', 'item', 'carousel', 'slide',
|
|
1870
1891
|
'timeline', 'event', 'hero', 'banner', 'slideover', 'center', 'listgroup', 'table', 'dconfig', 'reveal', 'animate',
|
|
1871
1892
|
'pulse', 'shake', 'scramble', 'ripple', 'breathe', 'ambient', 'twinkle', 'fireworks', 'scribe', 'wait', 'render', 'redraw'
|
|
1872
1893
|
]);
|
|
1873
|
-
const
|
|
1894
|
+
const CONTAINER_SET = new Set(SHORT_TAG_CONTAINERS);
|
|
1895
|
+
|
|
1896
|
+
/** A line that is one container short tag and nothing else: {name, close} or null. */
|
|
1897
|
+
function shortTagOf(line) {
|
|
1898
|
+
const t = line.trim();
|
|
1899
|
+
const m = t.match(/^\[(\/?)([a-z][\w-]*)(?:\s[^\]]*)?\]$/i);
|
|
1900
|
+
if (!m || !CONTAINER_SET.has(m[2].toLowerCase()) || (!m[1] && /\/\]$/.test(t))) return null;
|
|
1901
|
+
return {name: m[2].toLowerCase(), close: Boolean(m[1])};
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
/** Each line's indent as written, and how much of it is nesting (removed by unindentShortTags). */
|
|
1905
|
+
export function shortTagLayout(lines) {
|
|
1906
|
+
const info = lines.map(() => ({tag: false, depth: 0, block: -1, fence: false}));
|
|
1907
|
+
const fenced = new Set();
|
|
1908
|
+
let fence = null;
|
|
1909
|
+
lines.forEach((line, i) => {
|
|
1910
|
+
if (fence) { fenced.add(i); const c = line.match(/^\s*(`{3,}|~{3,})\s*$/); if (c && c[1][0] === fence[0] && c[1].length >= fence.length) fence = null; return; }
|
|
1911
|
+
const f = line.match(/^\s*(`{3,}|~{3,})/);
|
|
1912
|
+
if (f) fence = f[1];
|
|
1913
|
+
});
|
|
1914
|
+
const hasCloser = (name, from) => {
|
|
1915
|
+
let depth = 0;
|
|
1916
|
+
const re = new RegExp(`\\[(\\/?)${name}(?:[\\s\\]])`, 'gi');
|
|
1917
|
+
for (let j = from; j < lines.length; j++) {
|
|
1918
|
+
if (fenced.has(j)) continue;
|
|
1919
|
+
for (const m of lines[j].matchAll(re)) {
|
|
1920
|
+
if (!m[1]) depth++;
|
|
1921
|
+
else if (depth === 0) return true;
|
|
1922
|
+
else depth--;
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
return false;
|
|
1926
|
+
};
|
|
1927
|
+
const stack = [];
|
|
1928
|
+
lines.forEach((line, i) => {
|
|
1929
|
+
const top = stack[stack.length - 1];
|
|
1930
|
+
info[i].depth = stack.length;
|
|
1931
|
+
info[i].block = top ? top.at : -1;
|
|
1932
|
+
if (fenced.has(i)) { info[i].fence = true; return; }
|
|
1933
|
+
const t = shortTagOf(line);
|
|
1934
|
+
if (!t) return;
|
|
1935
|
+
if (t.close) {
|
|
1936
|
+
const at = stack.map(x => x.name).lastIndexOf(t.name);
|
|
1937
|
+
if (at < 0) return;
|
|
1938
|
+
stack.length = at;
|
|
1939
|
+
Object.assign(info[i], {tag: true, depth: stack.length});
|
|
1940
|
+
} else if (hasCloser(t.name, i + 1)) {
|
|
1941
|
+
info[i].tag = true;
|
|
1942
|
+
stack.push({name: t.name, at: i});
|
|
1943
|
+
}
|
|
1944
|
+
});
|
|
1945
|
+
// What the content of each container shares (fenced lines follow their fence's line).
|
|
1946
|
+
const shared = new Map();
|
|
1947
|
+
lines.forEach((line, i) => {
|
|
1948
|
+
const x = info[i];
|
|
1949
|
+
if (x.tag || x.fence || x.block < 0 || !line.trim()) return;
|
|
1950
|
+
const w = line.match(/^[ \t]*/)[0].length;
|
|
1951
|
+
shared.set(x.block, Math.min(shared.get(x.block) ?? Infinity, w));
|
|
1952
|
+
});
|
|
1953
|
+
return info.map((x, i) => {
|
|
1954
|
+
const own = lines[i].match(/^[ \t]*/)[0].length;
|
|
1955
|
+
const cut = x.tag ? own : x.block < 0 ? 0 : Math.min(own, shared.get(x.block) ?? 0);
|
|
1956
|
+
return {...x, cut};
|
|
1957
|
+
});
|
|
1958
|
+
}
|
|
1874
1959
|
|
|
1875
|
-
/**
|
|
1960
|
+
/** The nesting indent off every short tag line and the content inside (above). */
|
|
1876
1961
|
export function unindentShortTags(markdown) {
|
|
1877
|
-
if (!/^[ \t]+\
|
|
1878
|
-
const
|
|
1879
|
-
|
|
1962
|
+
if (!/^[ \t]+\S/m.test(markdown) || !/\[\/?[a-z]/i.test(markdown)) return markdown;
|
|
1963
|
+
const lines = markdown.split('\n');
|
|
1964
|
+
const layout = shortTagLayout(lines);
|
|
1965
|
+
return lines.map((l, i) => (layout[i].cut ? l.slice(layout[i].cut) : l)).join('\n');
|
|
1880
1966
|
}
|
|
1881
1967
|
|
|
1882
1968
|
export function scrubCodeRegions(markdown, preserveIndented = false) {
|
|
@@ -1900,7 +1986,10 @@ export function scrubCodeRegions(markdown, preserveIndented = false) {
|
|
|
1900
1986
|
// Inline code spans (single or multiple backticks, non-greedy)
|
|
1901
1987
|
scrubbed = scrubbed.replace(/`+[^`\n]+`+/g, placeholder);
|
|
1902
1988
|
|
|
1903
|
-
|
|
1989
|
+
// A stored region can hold an earlier placeholder of this same call - an indented
|
|
1990
|
+
// line (preserveIndented) holding an inline <code> - so what comes back is
|
|
1991
|
+
// restored too. Restoring it once left "SC1:0" on the page.
|
|
1992
|
+
const restore = (s) => s.replace(new RegExp(`\\x02SC${callId}:(\\d+)\\x03`, 'g'), (_, i) => restore(store[parseInt(i, 10)]));
|
|
1904
1993
|
return {scrubbed, restore};
|
|
1905
1994
|
}
|
|
1906
1995
|
|