domma-cms 0.92.1 → 0.94.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/CLAUDE.md +5 -3
  2. package/admin/css/admin.css +1 -1
  3. package/admin/js/app.js +2 -2
  4. package/admin/js/lib/action-editor-arrange.js +1 -1
  5. package/admin/js/lib/api-tokens-arrange.js +2 -2
  6. package/admin/js/lib/block-editor-arrange.js +1 -1
  7. package/admin/js/lib/blocks-arrange.js +1 -1
  8. package/admin/js/lib/collection-entries-arrange.js +1 -1
  9. package/admin/js/lib/components-arrange.js +1 -1
  10. package/admin/js/lib/dashboard-arrange.js +1 -1
  11. package/admin/js/lib/dates.js +1 -0
  12. package/admin/js/lib/forms-arrange.js +1 -1
  13. package/admin/js/lib/media-arrange.js +1 -1
  14. package/admin/js/lib/notifications-arrange.js +1 -1
  15. package/admin/js/lib/pages-arrange.js +1 -1
  16. package/admin/js/lib/related.js +1 -1
  17. package/admin/js/lib/timeline-builder.js +2 -2
  18. package/admin/js/templates/action-editor.html +6 -5
  19. package/admin/js/templates/actions-list.html +1 -1
  20. package/admin/js/templates/contacts.html +1 -1
  21. package/admin/js/templates/docs/api-actions.html +86 -60
  22. package/admin/js/templates/docs/api-authentication.html +159 -123
  23. package/admin/js/templates/docs/api-builder.html +197 -0
  24. package/admin/js/templates/docs/api-collections.html +199 -259
  25. package/admin/js/templates/docs/api-external.html +225 -0
  26. package/admin/js/templates/docs/api-forms.html +268 -0
  27. package/admin/js/templates/docs/api-layouts.html +70 -45
  28. package/admin/js/templates/docs/api-media.html +57 -80
  29. package/admin/js/templates/docs/api-navigation.html +66 -22
  30. package/admin/js/templates/docs/api-pages.html +109 -129
  31. package/admin/js/templates/docs/api-plugins.html +123 -61
  32. package/admin/js/templates/docs/api-scaffold.html +185 -0
  33. package/admin/js/templates/docs/api-settings.html +72 -64
  34. package/admin/js/templates/docs/api-users.html +74 -107
  35. package/admin/js/templates/docs/api-views.html +68 -54
  36. package/admin/js/templates/docs/components-howto.html +20 -17
  37. package/admin/js/templates/docs/components-reference.html +13 -16
  38. package/admin/js/templates/docs/components-rules.html +7 -6
  39. package/admin/js/templates/docs/components-walkthrough.html +19 -19
  40. package/admin/js/templates/docs/tutorial-crud.html +71 -40
  41. package/admin/js/templates/docs/tutorial-forms.html +51 -35
  42. package/admin/js/templates/docs/tutorial-plugin.html +132 -56
  43. package/admin/js/templates/docs/usage-actions.html +61 -15
  44. package/admin/js/templates/docs/usage-collections.html +108 -0
  45. package/admin/js/templates/docs/usage-cta-shortcode.html +14 -3
  46. package/admin/js/templates/docs/usage-dconfig.html +0 -3
  47. package/admin/js/templates/docs/usage-editions.html +213 -0
  48. package/admin/js/templates/docs/usage-media.html +22 -6
  49. package/admin/js/templates/docs/usage-navigation.html +74 -18
  50. package/admin/js/templates/docs/usage-pages.html +60 -20
  51. package/admin/js/templates/docs/usage-plugins.html +89 -17
  52. package/admin/js/templates/docs/usage-shortcodes.html +123 -70
  53. package/admin/js/templates/docs/usage-site-settings.html +50 -18
  54. package/admin/js/templates/docs/usage-tools.html +73 -0
  55. package/admin/js/templates/docs/usage-users-roles.html +99 -20
  56. package/admin/js/templates/docs/usage-views.html +36 -19
  57. package/admin/js/templates/documentation.html +153 -32
  58. package/admin/js/templates/page-editor.html +0 -5
  59. package/admin/js/templates/plugin-guide.html +15 -0
  60. package/admin/js/templates/plugin-guides.html +21 -0
  61. package/admin/js/templates/pro-docs.html +53 -234
  62. package/admin/js/templates/tutorials.html +5 -4
  63. package/admin/js/views/actions-list.js +3 -3
  64. package/admin/js/views/analytics.js +5 -5
  65. package/admin/js/views/api-endpoint-editor.js +2 -2
  66. package/admin/js/views/block-editor.js +4 -4
  67. package/admin/js/views/blocks.js +4 -4
  68. package/admin/js/views/collection-editor.js +4 -4
  69. package/admin/js/views/collection-entries.js +7 -7
  70. package/admin/js/views/component-editor.js +2 -2
  71. package/admin/js/views/contacts.js +22 -20
  72. package/admin/js/views/context-menu-editor.js +5 -5
  73. package/admin/js/views/doc-pages.js +1 -1
  74. package/admin/js/views/form-editor.js +4 -4
  75. package/admin/js/views/form-submissions.js +2 -2
  76. package/admin/js/views/index.js +1 -1
  77. package/admin/js/views/media.js +3 -3
  78. package/admin/js/views/menu-editor.js +13 -13
  79. package/admin/js/views/menu-locations.js +2 -2
  80. package/admin/js/views/my-profile.js +1 -1
  81. package/admin/js/views/page-editor.js +8 -8
  82. package/admin/js/views/plugin-guides.js +5 -0
  83. package/admin/js/views/project-detail.js +2 -2
  84. package/admin/js/views/project-settings.js +1 -1
  85. package/admin/js/views/role-editor.js +4 -4
  86. package/admin/js/views/search.js +2 -2
  87. package/admin/js/views/seo.js +17 -17
  88. package/admin/js/views/settings.js +3 -3
  89. package/admin/js/views/theme.js +3 -3
  90. package/admin/js/views/user-editor.js +1 -1
  91. package/admin/js/views/users.js +2 -2
  92. package/admin/js/views/view-editor.js +1 -1
  93. package/bin/cli.js +13 -13
  94. package/bin/lib/node-version.js +29 -0
  95. package/package.json +1 -1
  96. package/plugins/_lib/admin/mail/compose-window.js +3 -2
  97. package/plugins/_lib/admin/mail/reader-view.js +7 -6
  98. package/plugins/_lib/admin/mail/scheduling.js +4 -2
  99. package/plugins/_lib/admin/mail/templates.js +4 -4
  100. package/plugins/_lib/admin/ui/dates.js +85 -0
  101. package/plugins/blog/CLAUDE.md +31 -22
  102. package/plugins/blog/admin/views/blog.js +3 -2
  103. package/plugins/blog/admin/views/comments.js +2 -1
  104. package/plugins/blog/admin/views/post-editor.js +4 -4
  105. package/plugins/blog/blocks/blog-card-row.html +1 -1
  106. package/plugins/blog/blocks/blog-card.html +2 -2
  107. package/plugins/blog/blocks/blog-post-classic.html +2 -2
  108. package/plugins/blog/blocks/blog-post-essay.html +2 -2
  109. package/plugins/blog/blocks/blog-post-feature.html +2 -2
  110. package/plugins/blog/blocks/blog-post-minimal.html +2 -2
  111. package/plugins/blog/blocks/blog-post-sidebar.html +2 -2
  112. package/plugins/blog/blocks/blog-post-split.html +2 -2
  113. package/plugins/blog/docs/guide.md +205 -0
  114. package/plugins/blog/lib/layouts.js +3 -3
  115. package/plugins/blog/lib/page.js +2 -1
  116. package/plugins/blog/plugin.js +3 -3
  117. package/plugins/blog/plugin.json +4 -4
  118. package/plugins/blog/tests/layouts.test.js +6 -0
  119. package/plugins/feedback/CLAUDE.md +22 -3
  120. package/plugins/feedback/admin/lib/kit.js +6 -7
  121. package/plugins/feedback/admin/views/feedback.js +79 -10
  122. package/plugins/feedback/admin/views/send.js +28 -6
  123. package/plugins/feedback/docs/guide.md +95 -0
  124. package/plugins/feedback/lib/receiver.js +9 -2
  125. package/plugins/feedback/lib/sender.js +3 -2
  126. package/plugins/feedback/plugin.js +54 -6
  127. package/plugins/feedback/plugin.json +4 -4
  128. package/plugins/feedback/tests/api.test.js +74 -2
  129. package/plugins/free-tier.lock.json +49 -44
  130. package/plugins/mail-reader/CLAUDE.md +33 -18
  131. package/plugins/mail-reader/docs/guide.md +147 -0
  132. package/plugins/mail-reader/plugin.json +1 -1
  133. package/plugins/security/CLAUDE.md +4 -1
  134. package/plugins/security/admin/views/security.js +5 -5
  135. package/plugins/security/docs/guide.md +170 -0
  136. package/plugins/security/plugin.js +2 -1
  137. package/plugins/security/plugin.json +2 -1
  138. package/plugins/shopping-cart/CLAUDE.md +7 -1
  139. package/plugins/shopping-cart/admin/lib/kit.js +5 -2
  140. package/plugins/shopping-cart/admin/views/orders.js +4 -4
  141. package/plugins/shopping-cart/admin/views/overview.js +2 -2
  142. package/plugins/shopping-cart/docs/guide.md +191 -0
  143. package/plugins/shopping-cart/lib/render.js +2 -1
  144. package/plugins/shopping-cart/plugin.json +3 -3
  145. package/public/js/collection-browser.js +2 -2
  146. package/public/js/site.js +1 -1
  147. package/scripts/gen-instance-secret.js +3 -1
  148. package/scripts/setup.js +3 -1
  149. package/server/middleware/auth.js +2 -1
  150. package/server/routes/api/actions.js +47 -27
  151. package/server/routes/api/blocks.js +2 -1
  152. package/server/routes/api/collections.js +16 -52
  153. package/server/routes/api/contacts.js +66 -3
  154. package/server/routes/api/documentation.js +42 -0
  155. package/server/routes/api/notifications.js +3 -2
  156. package/server/routes/api/users.js +10 -6
  157. package/server/server.js +16 -1
  158. package/server/services/actions.js +110 -34
  159. package/server/services/adapterRegistry.js +169 -16
  160. package/server/services/adapters/FileAdapter.js +25 -0
  161. package/server/services/adapters/MongoAdapter.js +23 -0
  162. package/server/services/collections.js +104 -1
  163. package/server/services/connectionManager.js +12 -0
  164. package/server/services/dates.js +81 -0
  165. package/server/services/docs.js +13 -2
  166. package/server/services/markdown.js +75 -26
  167. package/server/services/notification-sources.js +6 -5
  168. package/server/services/passwordReset.js +2 -1
  169. package/server/services/permissionRegistry.js +3 -2
  170. package/server/services/pluginGuides.js +255 -0
  171. package/server/services/pluginInstaller.js +54 -13
  172. package/server/services/plugins.js +29 -1
  173. package/server/services/presetCollections.js +31 -5
  174. package/server/services/renderer.js +2 -2
  175. package/server/services/sidebarBadges.js +3 -1
  176. package/server/services/tools.js +4 -2
  177. package/server/templates/page.html +2 -2
@@ -1,4 +1,4 @@
1
- import{api as ue}from"../api.js";import{addBannerAction as qe}from"../lib/plugin-chrome.js";import{helpPopovers as Ne}from"../lib/help-popover.js";import{onceClosed as it,openMenuAt as Re}from"../lib/tool-kit.js";import{makeIconInput as rt}from"/admin/js/lib/shortcode-modal.js";import{makePageInput as ct}from"/admin/js/lib/page-picker.js";import{ACCENTS as lt,ACTION_ARG as B,ACTION_GROUPS as dt,EDITABLE_TYPES as ut,NEEDS_TARGET as mt,SELF_DISABLING as pt,checkMenu as vt,duplicateItem as ft,formFromMenu as Fe,insertItem as ht,itemForm as bt,itemFromForm as He,itemRow as yt,moveItem as gt,newItem as xt,previewItems as kt,previewable as Pe,recordFromForm as wt,removeItem as Q,slugify as Et,snapshot as me,splitList as X,themeFromForm as Mt}from"../lib/context-menu-editor-arrange.js";const St=/^\/context-menus\/(new|edit\/[^/?#]+)\/?(\?.*)?$/,F=c=>`context-menu-editor.draft.${c||"new"}`,At=`
1
+ import{api as de}from"../api.js";import{addBannerAction as je}from"../lib/plugin-chrome.js";import{helpPopovers as qe}from"../lib/help-popover.js";import{onceClosed as st,openMenuAt as Ne}from"../lib/tool-kit.js";import{makeIconInput as it}from"/admin/js/lib/shortcode-modal.js";import{makePageInput as rt}from"/admin/js/lib/page-picker.js";import{ACCENTS as ct,ACTION_ARG as K,ACTION_GROUPS as lt,EDITABLE_TYPES as dt,NEEDS_TARGET as ut,SELF_DISABLING as mt,checkMenu as pt,duplicateItem as vt,formFromMenu as Re,insertItem as ft,itemForm as ht,itemFromForm as Fe,itemRow as bt,moveItem as yt,newItem as gt,previewItems as xt,previewable as Pe,recordFromForm as kt,removeItem as Y,slugify as wt,snapshot as ue,splitList as Q,themeFromForm as Et}from"../lib/context-menu-editor-arrange.js";import{fmtDateTime as Mt}from"../lib/dates.js";const St=/^\/context-menus\/(new|edit\/[^/?#]+)\/?(\?.*)?$/,N=c=>`context-menu-editor.draft.${c||"new"}`,At=`
2
2
  <div class="cmx-row" data-index="{{index}}" tabindex="0" data-bind-class="cls">
3
3
  <span class="cmx-icon" aria-hidden="true" data-name="{{icon}}"></span>
4
4
  <span class="cmx-type">{{typeLabel}}</span>
@@ -9,7 +9,7 @@ import{api as ue}from"../api.js";import{addBannerAction as qe}from"../lib/plugin
9
9
  <button type="button" class="cmx-down" data-bind-disabled="last" aria-label="Move down" title="Move down (Alt+\u2193)">\u2193</button>
10
10
  </span>
11
11
  <button type="button" class="cmx-kebab" aria-label="Actions for {{text}}" title="Actions">\u22EE</button>
12
- </div>`,It='<span class="cmx-chip" data-bind-class="cls">{{text}}</span>',Lt="<small>{{text}}</small>";function C(c){return String(c??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function Tt(c){const u=document.createElement("template");return u.innerHTML=c.trim(),u.content.firstElementChild}let Z=null;function pe(){if(Z){try{Z.destroy()}catch{}Z=null}const c=document.getElementById("ctx-preview");if(c&&E.contextMenu&&E.contextMenu.registry)for(const u of E.contextMenu.registry(c))try{u.destroy()}catch{}}function ve(c,u){const l=c.querySelector(".dm-plugin-banner-name");if(!l)return;const T=l.querySelector(".dm-plugin-banner-sep");if(T){for(;T.nextSibling;)T.nextSibling.remove();l.appendChild(document.createTextNode(u))}}let k=null,Ke=!1;const ee=c=>String(c||"").split("?")[0].replace(/\/$/,"");function Ct(){Ke||!window.R?.use||(Ke=!0,R.use((c,u,l)=>!(St.test(u?.path||"")&&ee(c?.path)!==ee(u?.path))||!k?.dirty()?l():E.confirm("This context menu has unsaved changes. Leave anyway? They are kept as a draft for your next visit.").then(w=>{w?l():history.replaceState(null,"",`#${u.path}`)})),M.subscribe("router:afterChange",({to:c}={})=>{if(k&&ee(c?.path)!==k.path){const u=k;k=null,u.dispose()}}))}export const contextMenuEditorView={templateUrl:"/admin/js/templates/context-menu-editor.html",async onMount(c){if(Ct(),k){const e=k;k=null,e.dispose()}pe();const u=[],l=e=>(u.push(e),e);let T=!1;const w=c.get(0),m=w.querySelector(".cmx")||w,_=()=>!T&&m.isConnected,fe=location.hash.match(/^#\/context-menus\/edit\/([^?#]+)/),p=fe?decodeURIComponent(fe[1]):null,h=!p,Be=ee(location.hash.replace(/^#/,""));let g=null;if(!h){const e=E.loader(w,{type:"dots"});try{g=await ue.contextMenus.get(p)}catch(n){e.destroy(),E.toast(`Could not load "${p}": ${n.message||n}`,{type:"error"});return}if(e.destroy(),!m.isConnected)return}const O=Fe(g),r={};for(const[e,n]of Object.entries(O))r[e]=M.observable(n);const U=()=>Object.fromEntries(Object.keys(r).map(e=>[e,r[e].peek()])),he=()=>Object.fromEntries(Object.keys(r).map(e=>[e,r[e].value])),s=M.observable(Array.isArray(g?.items)?g.items:[]),z=M.observable(me(O,s.peek())),G=M.computed(()=>me(he(),s.value)!==z.value),V=M.observable(!1),J=M.observable(!1),H=M.observable([]),$=M.computed(()=>vt(he(),s.value,{isNew:h})),_e=M.computed(()=>{const e=$.value.errors;if(J.value)return e;const n={};for(const t of Object.keys(e))t!=="items"&&String(r[t]?.value??"").trim()&&(n[t]=e[t]);return n}),be=M.computed(()=>{const e=J.value?$.value.errors:{};return{binding:!!(e.name||e.slug||e.selector),items:!!e.items||$.value.itemErrors.some(Boolean),appearance:!!(e.opacity||e.accent)}});let te=!h;l(M.effect(()=>{const e=r.name.value;te||(r.slug.value=Et(e))}));const ne=M.observable([]);l(M.effect(()=>{const e=s.value,n=$.value.itemErrors;ne.value=e.map((t,i)=>({...yt(t,i,e.length,n[i]||""),key:`${i}:${JSON.stringify(t)}`}))}));const x=m.querySelector("#ctx-items");l(M.effect(()=>{ne.value,requestAnimationFrame(()=>{x?.isConnected&&(x.querySelectorAll(".cmx-icon[data-name]").forEach(e=>{const n=e.dataset.name;if(!n||e.dataset.drawn===n)return;e.dataset.drawn=n;const t=document.createElement("span");t.setAttribute("data-icon",n),t.setAttribute("data-icon-size","14"),e.replaceChildren(t)}),I.scan(x))})}));const Ue=M.computed(()=>{const e=[];e.push(r.enabled.value?{text:"On",cls:"is-ok"}:{text:"Off",cls:"is-warn"});const n=X(r.pages.value).length,t=X(r.projects.value).length;e.push({text:n||t?`${n+t} scope rule${n+t===1?"":"s"}`:"Site-wide",cls:""});const i=s.value.filter(o=>!["divider","header"].includes(o?.type||"item")).length;return e.push({text:`${i} item${i===1?"":"s"} to click`,cls:i?"":"is-danger"}),r.selector.value.trim()&&e.push({text:r.selector.value.trim(),cls:""}),e});let ae=null;function ze(){if(!_())return;pe();const e=s.peek();if(!Pe(e))return;const n=Mt(U());Z=E.contextMenu("#ctx-preview",{cursor:"glyph",items:kt(e),inherit:!1,accent:n.accent,transition:n.transition,density:n.density,radius:n.radius,shadow:n.shadow,opacity:n.opacity})}l(M.effect(()=>{s.value,r.accent.value,r.transition.value,r.density.value,r.radius.value,r.shadow.value,r.opacity.value,clearTimeout(ae),ae=setTimeout(ze,120)})),l(()=>{clearTimeout(ae),pe()});const Ge=M.computed(()=>Pe(s.value)?"Right-click here to try the menu":"Add an item to try the menu here"),oe=e=>ye(e),A=e=>{s.value=e};function P(e){requestAnimationFrame(()=>x?.querySelector(`.cmx-row[data-index="${e}"]`)?.focus())}function j(e,n){n<0||n>=s.peek().length||(A(gt(s.peek(),e,n)),P(n))}function q(e,n=s.peek().length){const t=xt(e);A(ht(s.peek(),n,t)),e!=="divider"&&ye(n,{isAdd:!0})}function Ve(e){const n=s.peek(),t=n[e];if(!t)return[];const i=t.type||"item";return[{header:i==="divider"?"Divider":t.text||"(no text)"},{label:i==="divider"?"Change\u2026":"Edit\u2026",icon:"edit",action:()=>oe(e)},{type:"divider"},{label:"Move up",icon:"arrow-up",disabled:e===0,action:()=>j(e,e-1)},{label:"Move down",icon:"arrow-down",disabled:e===n.length-1,action:()=>j(e,e+1)},{label:"Duplicate",icon:"copy",action:()=>{A(ft(s.peek(),e)),P(e+1)}},{label:"Add below",icon:"plus",submenu:[{label:"Item",action:()=>q("item",e+1)},{label:"Heading",action:()=>q("header",e+1)},{label:"Divider",action:()=>q("divider",e+1)}]},{type:"divider"},{label:"Remove",icon:"trash",danger:!0,action:()=>{A(Q(s.peek(),e)),P(Math.max(0,e-1))}}]}let v=null;function ye(e,{isAdd:n=!1}={}){const t=s.peek()[e];if(!t)return;if(v)try{v.so.close(!0)}catch{}const i=bt(t),o={};for(const[a,d]of Object.entries(i))o[a]=M.observable(d);const W=()=>Object.fromEntries(Object.keys(o).map(a=>[a,o[a].peek()])),Ze=JSON.stringify(i),ie=()=>JSON.stringify(W())!==Ze,Ie=!ut.includes(i.type),re=Array.isArray(t.submenu)&&t.submenu.length,Le={...o,locked:Ie,notLocked:!Ie,hasSub:re,subText:re?`Opens a submenu of ${t.submenu.length} - kept as it is (edit it in the file).`:"",isItem:M.computed(()=>o.type.value!=="divider"&&o.type.value!=="header"),hasText:M.computed(()=>o.type.value!=="divider"),showAction:M.computed(()=>o.type.value!=="divider"&&o.type.value!=="header"&&!re),argLabel:M.computed(()=>B[o.action.value]?.label||""),note:M.computed(()=>pt[o.action.value]||mt[o.action.value]||""),problem:M.computed(()=>{const a=He(Object.fromEntries(Object.keys(o).map(d=>[d,o[d].value])),t);return a.type==="divider"?"":String(a.text||"").trim()?B[a.action?.type]?.required&&!String(a.action?.[B[a.action.type].key]||"").trim()?`Needs a ${B[a.action.type].label.toLowerCase()}.`:"":"Needs text."})},y=E.slideover({title:n?"New item":i.type==="divider"?"Divider":`Edit ${C(i.text||"item")}`,size:"sm",position:"right"}),et=dt.map(a=>`<optgroup label="${C(a.label)}">${Object.entries(a.actions).map(([d,Y])=>`<option value="${C(d)}">${C(Y)}</option>`).join("")}</optgroup>`).join(""),L=Tt(`
12
+ </div>`,Tt='<span class="cmx-chip" data-bind-class="cls">{{text}}</span>',It="<small>{{text}}</small>";function C(c){return String(c??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function Lt(c){const u=document.createElement("template");return u.innerHTML=c.trim(),u.content.firstElementChild}let X=null;function me(){if(X){try{X.destroy()}catch{}X=null}const c=document.getElementById("ctx-preview");if(c&&E.contextMenu&&E.contextMenu.registry)for(const u of E.contextMenu.registry(c))try{u.destroy()}catch{}}function pe(c,u){const l=c.querySelector(".dm-plugin-banner-name");if(!l)return;const L=l.querySelector(".dm-plugin-banner-sep");if(L){for(;L.nextSibling;)L.nextSibling.remove();l.appendChild(document.createTextNode(u))}}let k=null,He=!1;const Z=c=>String(c||"").split("?")[0].replace(/\/$/,"");function Ct(){He||!window.R?.use||(He=!0,R.use((c,u,l)=>!(St.test(u?.path||"")&&Z(c?.path)!==Z(u?.path))||!k?.dirty()?l():E.confirm("This context menu has unsaved changes. Leave anyway? They are kept as a draft for your next visit.").then(w=>{w?l():history.replaceState(null,"",`#${u.path}`)})),M.subscribe("router:afterChange",({to:c}={})=>{if(k&&Z(c?.path)!==k.path){const u=k;k=null,u.dispose()}}))}export const contextMenuEditorView={templateUrl:"/admin/js/templates/context-menu-editor.html",async onMount(c){if(Ct(),k){const e=k;k=null,e.dispose()}me();const u=[],l=e=>(u.push(e),e);let L=!1;const w=c.get(0),m=w.querySelector(".cmx")||w,B=()=>!L&&m.isConnected,ve=location.hash.match(/^#\/context-menus\/edit\/([^?#]+)/),p=ve?decodeURIComponent(ve[1]):null,h=!p,Ke=Z(location.hash.replace(/^#/,""));let g=null;if(!h){const e=E.loader(w,{type:"dots"});try{g=await de.contextMenus.get(p)}catch(n){e.destroy(),E.toast(`Could not load "${p}": ${n.message||n}`,{type:"error"});return}if(e.destroy(),!m.isConnected)return}const O=Re(g),r={};for(const[e,n]of Object.entries(O))r[e]=M.observable(n);const _=()=>Object.fromEntries(Object.keys(r).map(e=>[e,r[e].peek()])),fe=()=>Object.fromEntries(Object.keys(r).map(e=>[e,r[e].value])),s=M.observable(Array.isArray(g?.items)?g.items:[]),U=M.observable(ue(O,s.peek())),z=M.computed(()=>ue(fe(),s.value)!==U.value),G=M.observable(!1),V=M.observable(!1),F=M.observable([]),$=M.computed(()=>pt(fe(),s.value,{isNew:h})),Be=M.computed(()=>{const e=$.value.errors;if(V.value)return e;const n={};for(const t of Object.keys(e))t!=="items"&&String(r[t]?.value??"").trim()&&(n[t]=e[t]);return n}),he=M.computed(()=>{const e=V.value?$.value.errors:{};return{binding:!!(e.name||e.slug||e.selector),items:!!e.items||$.value.itemErrors.some(Boolean),appearance:!!(e.opacity||e.accent)}});let ee=!h;l(M.effect(()=>{const e=r.name.value;ee||(r.slug.value=wt(e))}));const te=M.observable([]);l(M.effect(()=>{const e=s.value,n=$.value.itemErrors;te.value=e.map((t,i)=>({...bt(t,i,e.length,n[i]||""),key:`${i}:${JSON.stringify(t)}`}))}));const x=m.querySelector("#ctx-items");l(M.effect(()=>{te.value,requestAnimationFrame(()=>{x?.isConnected&&(x.querySelectorAll(".cmx-icon[data-name]").forEach(e=>{const n=e.dataset.name;if(!n||e.dataset.drawn===n)return;e.dataset.drawn=n;const t=document.createElement("span");t.setAttribute("data-icon",n),t.setAttribute("data-icon-size","14"),e.replaceChildren(t)}),I.scan(x))})}));const _e=M.computed(()=>{const e=[];e.push(r.enabled.value?{text:"On",cls:"is-ok"}:{text:"Off",cls:"is-warn"});const n=Q(r.pages.value).length,t=Q(r.projects.value).length;e.push({text:n||t?`${n+t} scope rule${n+t===1?"":"s"}`:"Site-wide",cls:""});const i=s.value.filter(o=>!["divider","header"].includes(o?.type||"item")).length;return e.push({text:`${i} item${i===1?"":"s"} to click`,cls:i?"":"is-danger"}),r.selector.value.trim()&&e.push({text:r.selector.value.trim(),cls:""}),e});let ne=null;function Ue(){if(!B())return;me();const e=s.peek();if(!Pe(e))return;const n=Et(_());X=E.contextMenu("#ctx-preview",{cursor:"glyph",items:xt(e),inherit:!1,accent:n.accent,transition:n.transition,density:n.density,radius:n.radius,shadow:n.shadow,opacity:n.opacity})}l(M.effect(()=>{s.value,r.accent.value,r.transition.value,r.density.value,r.radius.value,r.shadow.value,r.opacity.value,clearTimeout(ne),ne=setTimeout(Ue,120)})),l(()=>{clearTimeout(ne),me()});const ze=M.computed(()=>Pe(s.value)?"Right-click here to try the menu":"Add an item to try the menu here"),ae=e=>be(e),A=e=>{s.value=e};function P(e){requestAnimationFrame(()=>x?.querySelector(`.cmx-row[data-index="${e}"]`)?.focus())}function D(e,n){n<0||n>=s.peek().length||(A(yt(s.peek(),e,n)),P(n))}function j(e,n=s.peek().length){const t=gt(e);A(ft(s.peek(),n,t)),e!=="divider"&&be(n,{isAdd:!0})}function Ge(e){const n=s.peek(),t=n[e];if(!t)return[];const i=t.type||"item";return[{header:i==="divider"?"Divider":t.text||"(no text)"},{label:i==="divider"?"Change\u2026":"Edit\u2026",icon:"edit",action:()=>ae(e)},{type:"divider"},{label:"Move up",icon:"arrow-up",disabled:e===0,action:()=>D(e,e-1)},{label:"Move down",icon:"arrow-down",disabled:e===n.length-1,action:()=>D(e,e+1)},{label:"Duplicate",icon:"copy",action:()=>{A(vt(s.peek(),e)),P(e+1)}},{label:"Add below",icon:"plus",submenu:[{label:"Item",action:()=>j("item",e+1)},{label:"Heading",action:()=>j("header",e+1)},{label:"Divider",action:()=>j("divider",e+1)}]},{type:"divider"},{label:"Remove",icon:"trash",danger:!0,action:()=>{A(Y(s.peek(),e)),P(Math.max(0,e-1))}}]}let v=null;function be(e,{isAdd:n=!1}={}){const t=s.peek()[e];if(!t)return;if(v)try{v.so.close(!0)}catch{}const i=ht(t),o={};for(const[a,d]of Object.entries(i))o[a]=M.observable(d);const J=()=>Object.fromEntries(Object.keys(o).map(a=>[a,o[a].peek()])),Xe=JSON.stringify(i),se=()=>JSON.stringify(J())!==Xe,Ae=!dt.includes(i.type),ie=Array.isArray(t.submenu)&&t.submenu.length,Te={...o,locked:Ae,notLocked:!Ae,hasSub:ie,subText:ie?`Opens a submenu of ${t.submenu.length} - kept as it is (edit it in the file).`:"",isItem:M.computed(()=>o.type.value!=="divider"&&o.type.value!=="header"),hasText:M.computed(()=>o.type.value!=="divider"),showAction:M.computed(()=>o.type.value!=="divider"&&o.type.value!=="header"&&!ie),argLabel:M.computed(()=>K[o.action.value]?.label||""),note:M.computed(()=>mt[o.action.value]||ut[o.action.value]||""),problem:M.computed(()=>{const a=Fe(Object.fromEntries(Object.keys(o).map(d=>[d,o[d].value])),t);return a.type==="divider"?"":String(a.text||"").trim()?K[a.action?.type]?.required&&!String(a.action?.[K[a.action.type].key]||"").trim()?`Needs a ${K[a.action.type].label.toLowerCase()}.`:"":"Needs text."})},y=E.slideover({title:n?"New item":i.type==="divider"?"Divider":`Edit ${C(i.text||"item")}`,size:"sm",position:"right"}),Ze=lt.map(a=>`<optgroup label="${C(a.label)}">${Object.entries(a.actions).map(([d,W])=>`<option value="${C(d)}">${C(W)}</option>`).join("")}</optgroup>`).join(""),T=Lt(`
13
13
  <div class="cmx-form">
14
14
  <div data-bind-hidden="locked">
15
15
  <label class="form-label" for="cmx-i-type">Type
@@ -27,11 +27,11 @@ import{api as ue}from"../api.js";import{addBannerAction as qe}from"../lib/plugin
27
27
  <label class="form-label" for="cmx-i-icon">Icon <small class="cmx-hint-inline">(optional)</small></label>
28
28
  <div class="cmx-icon-mount"></div>
29
29
  </div>
30
- <p class="cmx-note" data-bind-hidden="!hasSub">${C(Le.subText)}</p>
30
+ <p class="cmx-note" data-bind-hidden="!hasSub">${C(Te.subText)}</p>
31
31
  <div data-bind-hidden="!showAction.value">
32
32
  <label class="form-label" for="cmx-i-action">Does
33
33
  <span data-help-title="What it does" data-help="A fixed set of actions, on purpose: nothing typed here runs as script on your pages. Some grey themselves out when they do not apply to the click - the note under the choice says when."></span></label>
34
- <select id="cmx-i-action" class="form-select" data-model="action.value">${et}</select>
34
+ <select id="cmx-i-action" class="form-select" data-model="action.value">${Ze}</select>
35
35
  <small class="cmx-hint" data-bind-hidden="!note.value" data-bind-text="note.value"></small>
36
36
  </div>
37
37
  <div data-bind-hidden="!argLabel.value || !showAction.value">
@@ -51,4 +51,4 @@ import{api as ue}from"../api.js";import{addBannerAction as qe}from"../lib/plugin
51
51
  <button type="button" class="btn btn-sm btn-ghost" data-on-click="cancel">Cancel</button>
52
52
  <button type="button" class="btn btn-sm btn-primary" data-on-click="done"><span data-icon="check" data-icon-size="13"></span> Done</button>
53
53
  </div>
54
- </div>`),N=rt("edit",i.icon);N.input.id="cmx-i-icon",N.input.classList.add("form-input");const Te=()=>{o.icon.value=N.input.value};N.input.addEventListener("input",Te),L.querySelector(".cmx-icon-mount").appendChild(N.el);const ce=L.querySelector(".cmx-arg-mount");let f=null;const le=()=>{o.arg.value=f.value};function Ce(){f?.removeEventListener("input",le),ce.replaceChildren();const a=B[o.action.peek()];if(!a){f=null;return}if(a.key==="url"){const d=ct(a.placeholder,o.arg.peek());f=d.input,ce.appendChild(d.el)}else f=document.createElement("input"),f.type="text",f.className="form-input",f.placeholder=a.placeholder,f.value=o.arg.peek(),ce.appendChild(f);f.id="cmx-i-arg",f.addEventListener("input",le)}let Oe=o.action.peek();const tt=M.effect(()=>{const a=o.action.value;a!==Oe&&(Oe=a,o.arg.value="",Ce())});Ce();const $e=()=>{const a=He(W(),t),d=[...s.peek()],Y=d.indexOf(t);Y!==-1&&(d[Y]=a,A(d))};let K=!1,de=!1;const De={done:()=>{$e(),K=!0,y.close(),P(e)},cancel:()=>y.close(),remove:()=>{const a=s.peek().indexOf(t);a!==-1&&A(Q(s.peek(),a)),K=!0,y.close()}};y.element.appendChild(L);const nt=Ne(L),at=M.applyBindings(Le,L,{methods:De}),ot=a=>{(a.ctrlKey||a.metaKey)&&a.key==="Enter"&&(a.preventDefault(),De.done())};L.addEventListener("keydown",ot);const je={so:y,dirty:ie,apply:$e};v=je,it(y,()=>{tt(),at.dispose(),nt(),N.input.removeEventListener("input",Te),f?.removeEventListener("input",le),v===je&&(v=null)});const st=y.close.bind(y);y.close=async a=>{if(!K&&a!==!0&&ie()){if(de)return;de=!0;const d=await E.confirm("Discard the changes to this item?");if(de=!1,!d)return}if(!K&&n&&a!==!0&&!ie()){const d=s.peek().indexOf(t);d!==-1&&A(Q(s.peek(),d))}return K=!0,st()},I.scan(L),y.open(),requestAnimationFrame(()=>L.querySelector(i.type==="divider"?"#cmx-i-type":"#cmx-i-text")?.focus())}l(()=>{if(v){const e=v;e.dirty()&&e.apply();try{e.so.close(!0)}catch{}}});async function se(){if(V.peek())return;J.value=!0,H.value=[];const e=$.peek();if(!e.ok){const t=Object.keys(e.errors).length;E.toast(`Not saved yet - ${t===1?"one thing needs":`${t} things need`} fixing (marked in red).`,{type:"warning"});const i=be.peek(),o=i.binding?0:i.items?1:i.appearance?2:-1;o>=0&&Ye?.activate?.(o);return}V.value=!0;const n=wt(U(),s.peek());try{if(h)await ue.contextMenus.create(n);else{const t=await ue.contextMenus.update(p,n);t?.items&&(s.value=t.items)}if(!_())return;z.value=me(U(),s.peek());try{S.remove?S.remove(F(p)):S.set(F(p),null)}catch{}window.dispatchEvent(new CustomEvent("dm:sidebar-count",{detail:{url:"#/context-menus"}})),h?(E.toast("Context menu created.",{type:"success"}),location.hash="#/context-menus"):(E.toast("Context menu saved.",{type:"success"}),ve(w,r.name.peek()||p))}catch(t){H.value=String(t.message||t).split("; ").map((i,o)=>({key:String(o),text:i})),m.querySelector(".cmx-alert")?.scrollIntoView({behavior:"smooth",block:"nearest"})}finally{V.value=!1}}function Je(){E.confirm("Throw away your unsaved changes to this menu?").then(e=>{if(!e||!_())return;const n=Fe(g);for(const[t,i]of Object.entries(n))r[t].value=i;s.value=Array.isArray(g?.items)?g.items:[],H.value=[]})}m.querySelector("#ctx-items").innerHTML=At,m.querySelector(".cmx-chips").innerHTML=It,m.querySelector(".cmx-server-errors").innerHTML=Lt;const ge=m.querySelector("#ctx-accent");if(O.accent&&!lt.includes(O.accent)&&ge){const e=document.createElement("option");e.value=O.accent,e.textContent=`Custom (${O.accent})`,ge.appendChild(e)}const We={...r,items:s,rows:ne,chips:Ue,dirty:G,saving:V,err:_e,tabBad:be,serverErrors:H,isNew:h,notNew:!h,previewText:Ge,serverError:M.computed(()=>H.value.length>0),saveText:M.computed(()=>h?"Create menu":"Save"),problemText:M.computed(()=>J.value&&!$.value.ok?" - some need fixing before they can be saved":""),scopeText:M.computed(()=>{const e=X(r.pages.value),n=X(r.projects.value);return!e.length&&!n.length?"Site-wide: every page carries this menu.":`Only ${[e.length&&`pages matching ${e.join(", ")}`,n.length&&`pages in ${n.join(", ")}`].filter(Boolean).join(", or ")}.`})},Ye=E.tabs(m.querySelector("#ctx-tabs"),{tabSelector:".tab-item",panelSelector:".tab-panel"}),Qe=M.applyBindings(We,m,{methods:{save:()=>se(),discard:()=>Je(),addItem:()=>q("item"),addHeader:()=>q("header"),addDivider:()=>q("divider")}});l(()=>Qe.dispose()),l(Ne(m,{position:"bottom"}));const xe=m.querySelector("#ctx-slug"),ke=()=>{te=!0};xe?.addEventListener("input",ke),l(()=>xe?.removeEventListener("input",ke));const Xe=E.contextMenu(x,{cursor:"glyph",match:".cmx-row",inherit:!1,density:"compact",items:e=>Ve(Number(e.dataset.index))});l(()=>Xe?.destroy?.());const we=e=>{const n=e.target.closest(".cmx-row");if(!n)return;const t=Number(n.dataset.index);if(e.target.closest(".cmx-kebab")){Re(e.target.closest(".cmx-kebab"));return}if(e.target.closest(".cmx-up")){j(t,t-1);return}if(e.target.closest(".cmx-down")){j(t,t+1);return}oe(t)},Ee=e=>{const n=e.target.closest(".cmx-row");if(!n||e.target!==n)return;const t=Number(n.dataset.index);e.key==="F10"&&e.shiftKey?(e.preventDefault(),Re(n)):e.key==="Enter"?(e.preventDefault(),oe(t)):e.altKey&&e.key==="ArrowUp"?(e.preventDefault(),j(t,t-1)):e.altKey&&e.key==="ArrowDown"?(e.preventDefault(),j(t,t+1)):e.key==="Delete"&&(e.preventDefault(),A(Q(s.peek(),t)),P(Math.max(0,t-1)))};x.addEventListener("click",we),x.addEventListener("keydown",Ee),l(()=>{x.removeEventListener("click",we),x.removeEventListener("keydown",Ee)}),h?ve(w,"New menu"):ve(w,g?.name||p),qe(c,{id:"ctx-editor-back",icon:"arrow-left",title:"All context menus",onClick:()=>{location.hash="#/context-menus"}}),qe(c,{id:"ctx-editor-save",icon:"save",title:"Save (Ctrl+S)",onClick:()=>se()});const Me=e=>{!(e.ctrlKey||e.metaKey)||e.key!=="s"||v||(e.preventDefault(),e.stopImmediatePropagation(),se())};document.addEventListener("keydown",Me,!0),l(()=>document.removeEventListener("keydown",Me,!0));const Se=()=>{if(G.peek())try{S.set(F(p),{form:U(),items:s.peek(),base:z.peek(),at:new Date().toISOString()})}catch{}},Ae=e=>{v?.dirty()&&v.apply(),G.peek()&&(Se(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",Ae),l(()=>window.removeEventListener("beforeunload",Ae)),k={path:Be,dirty:()=>G.peek()||!!v?.dirty(),dispose(){if(T=!0,v){const e=v;e.dirty()&&e.apply();try{e.so.close(!0)}catch{}}Se(),u.splice(0).reverse().forEach(e=>{try{e()}catch{}})}},I.scan(m);let b=null;try{b=S.get(F(p))}catch{}if(b?.form){try{S.remove?S.remove(F(p)):S.set(F(p),null)}catch{}const e=b.base&&b.base!==z.peek(),n=b.at?` (${D(b.at).format("D MMM HH:mm")})`:"",t=h?"the new context menu you started":`"${C(g?.name||p)}"`,i=e?`You left unsaved changes to ${t}${n}, but it has been saved since. Restore your version anyway?`:`You left unsaved changes to ${t}${n}. Restore them?`;if(await E.confirm(i)&&_()){for(const[o,W]of Object.entries(b.form))r[o]&&o!==(h?"":"slug")&&(r[o].value=W);h&&b.form.slug&&(te=!0),Array.isArray(b.items)&&(s.value=b.items)}}}};
54
+ </div>`),q=it("edit",i.icon);q.input.id="cmx-i-icon",q.input.classList.add("form-input");const Ie=()=>{o.icon.value=q.input.value};q.input.addEventListener("input",Ie),T.querySelector(".cmx-icon-mount").appendChild(q.el);const re=T.querySelector(".cmx-arg-mount");let f=null;const ce=()=>{o.arg.value=f.value};function Le(){f?.removeEventListener("input",ce),re.replaceChildren();const a=K[o.action.peek()];if(!a){f=null;return}if(a.key==="url"){const d=rt(a.placeholder,o.arg.peek());f=d.input,re.appendChild(d.el)}else f=document.createElement("input"),f.type="text",f.className="form-input",f.placeholder=a.placeholder,f.value=o.arg.peek(),re.appendChild(f);f.id="cmx-i-arg",f.addEventListener("input",ce)}let Ce=o.action.peek();const et=M.effect(()=>{const a=o.action.value;a!==Ce&&(Ce=a,o.arg.value="",Le())});Le();const Oe=()=>{const a=Fe(J(),t),d=[...s.peek()],W=d.indexOf(t);W!==-1&&(d[W]=a,A(d))};let H=!1,le=!1;const $e={done:()=>{Oe(),H=!0,y.close(),P(e)},cancel:()=>y.close(),remove:()=>{const a=s.peek().indexOf(t);a!==-1&&A(Y(s.peek(),a)),H=!0,y.close()}};y.element.appendChild(T);const tt=qe(T),nt=M.applyBindings(Te,T,{methods:$e}),at=a=>{(a.ctrlKey||a.metaKey)&&a.key==="Enter"&&(a.preventDefault(),$e.done())};T.addEventListener("keydown",at);const De={so:y,dirty:se,apply:Oe};v=De,st(y,()=>{et(),nt.dispose(),tt(),q.input.removeEventListener("input",Ie),f?.removeEventListener("input",ce),v===De&&(v=null)});const ot=y.close.bind(y);y.close=async a=>{if(!H&&a!==!0&&se()){if(le)return;le=!0;const d=await E.confirm("Discard the changes to this item?");if(le=!1,!d)return}if(!H&&n&&a!==!0&&!se()){const d=s.peek().indexOf(t);d!==-1&&A(Y(s.peek(),d))}return H=!0,ot()},I.scan(T),y.open(),requestAnimationFrame(()=>T.querySelector(i.type==="divider"?"#cmx-i-type":"#cmx-i-text")?.focus())}l(()=>{if(v){const e=v;e.dirty()&&e.apply();try{e.so.close(!0)}catch{}}});async function oe(){if(G.peek())return;V.value=!0,F.value=[];const e=$.peek();if(!e.ok){const t=Object.keys(e.errors).length;E.toast(`Not saved yet - ${t===1?"one thing needs":`${t} things need`} fixing (marked in red).`,{type:"warning"});const i=he.peek(),o=i.binding?0:i.items?1:i.appearance?2:-1;o>=0&&We?.activate?.(o);return}G.value=!0;const n=kt(_(),s.peek());try{if(h)await de.contextMenus.create(n);else{const t=await de.contextMenus.update(p,n);t?.items&&(s.value=t.items)}if(!B())return;U.value=ue(_(),s.peek());try{S.remove?S.remove(N(p)):S.set(N(p),null)}catch{}window.dispatchEvent(new CustomEvent("dm:sidebar-count",{detail:{url:"#/context-menus"}})),h?(E.toast("Context menu created.",{type:"success"}),location.hash="#/context-menus"):(E.toast("Context menu saved.",{type:"success"}),pe(w,r.name.peek()||p))}catch(t){F.value=String(t.message||t).split("; ").map((i,o)=>({key:String(o),text:i})),m.querySelector(".cmx-alert")?.scrollIntoView({behavior:"smooth",block:"nearest"})}finally{G.value=!1}}function Ve(){E.confirm("Throw away your unsaved changes to this menu?").then(e=>{if(!e||!B())return;const n=Re(g);for(const[t,i]of Object.entries(n))r[t].value=i;s.value=Array.isArray(g?.items)?g.items:[],F.value=[]})}m.querySelector("#ctx-items").innerHTML=At,m.querySelector(".cmx-chips").innerHTML=Tt,m.querySelector(".cmx-server-errors").innerHTML=It;const ye=m.querySelector("#ctx-accent");if(O.accent&&!ct.includes(O.accent)&&ye){const e=document.createElement("option");e.value=O.accent,e.textContent=`Custom (${O.accent})`,ye.appendChild(e)}const Je={...r,items:s,rows:te,chips:_e,dirty:z,saving:G,err:Be,tabBad:he,serverErrors:F,isNew:h,notNew:!h,previewText:ze,serverError:M.computed(()=>F.value.length>0),saveText:M.computed(()=>h?"Create menu":"Save"),problemText:M.computed(()=>V.value&&!$.value.ok?" - some need fixing before they can be saved":""),scopeText:M.computed(()=>{const e=Q(r.pages.value),n=Q(r.projects.value);return!e.length&&!n.length?"Site-wide: every page carries this menu.":`Only ${[e.length&&`pages matching ${e.join(", ")}`,n.length&&`pages in ${n.join(", ")}`].filter(Boolean).join(", or ")}.`})},We=E.tabs(m.querySelector("#ctx-tabs"),{tabSelector:".tab-item",panelSelector:".tab-panel"}),Ye=M.applyBindings(Je,m,{methods:{save:()=>oe(),discard:()=>Ve(),addItem:()=>j("item"),addHeader:()=>j("header"),addDivider:()=>j("divider")}});l(()=>Ye.dispose()),l(qe(m,{position:"bottom"}));const ge=m.querySelector("#ctx-slug"),xe=()=>{ee=!0};ge?.addEventListener("input",xe),l(()=>ge?.removeEventListener("input",xe));const Qe=E.contextMenu(x,{cursor:"glyph",match:".cmx-row",inherit:!1,density:"compact",items:e=>Ge(Number(e.dataset.index))});l(()=>Qe?.destroy?.());const ke=e=>{const n=e.target.closest(".cmx-row");if(!n)return;const t=Number(n.dataset.index);if(e.target.closest(".cmx-kebab")){Ne(e.target.closest(".cmx-kebab"));return}if(e.target.closest(".cmx-up")){D(t,t-1);return}if(e.target.closest(".cmx-down")){D(t,t+1);return}ae(t)},we=e=>{const n=e.target.closest(".cmx-row");if(!n||e.target!==n)return;const t=Number(n.dataset.index);e.key==="F10"&&e.shiftKey?(e.preventDefault(),Ne(n)):e.key==="Enter"?(e.preventDefault(),ae(t)):e.altKey&&e.key==="ArrowUp"?(e.preventDefault(),D(t,t-1)):e.altKey&&e.key==="ArrowDown"?(e.preventDefault(),D(t,t+1)):e.key==="Delete"&&(e.preventDefault(),A(Y(s.peek(),t)),P(Math.max(0,t-1)))};x.addEventListener("click",ke),x.addEventListener("keydown",we),l(()=>{x.removeEventListener("click",ke),x.removeEventListener("keydown",we)}),h?pe(w,"New menu"):pe(w,g?.name||p),je(c,{id:"ctx-editor-back",icon:"arrow-left",title:"All context menus",onClick:()=>{location.hash="#/context-menus"}}),je(c,{id:"ctx-editor-save",icon:"save",title:"Save (Ctrl+S)",onClick:()=>oe()});const Ee=e=>{!(e.ctrlKey||e.metaKey)||e.key!=="s"||v||(e.preventDefault(),e.stopImmediatePropagation(),oe())};document.addEventListener("keydown",Ee,!0),l(()=>document.removeEventListener("keydown",Ee,!0));const Me=()=>{if(z.peek())try{S.set(N(p),{form:_(),items:s.peek(),base:U.peek(),at:new Date().toISOString()})}catch{}},Se=e=>{v?.dirty()&&v.apply(),z.peek()&&(Me(),e.preventDefault(),e.returnValue="")};window.addEventListener("beforeunload",Se),l(()=>window.removeEventListener("beforeunload",Se)),k={path:Ke,dirty:()=>z.peek()||!!v?.dirty(),dispose(){if(L=!0,v){const e=v;e.dirty()&&e.apply();try{e.so.close(!0)}catch{}}Me(),u.splice(0).reverse().forEach(e=>{try{e()}catch{}})}},I.scan(m);let b=null;try{b=S.get(N(p))}catch{}if(b?.form){try{S.remove?S.remove(N(p)):S.set(N(p),null)}catch{}const e=b.base&&b.base!==U.peek(),n=b.at?` (${Mt(b.at)})`:"",t=h?"the new context menu you started":`"${C(g?.name||p)}"`,i=e?`You left unsaved changes to ${t}${n}, but it has been saved since. Restore your version anyway?`:`You left unsaved changes to ${t}${n}. Restore them?`;if(await E.confirm(i)&&B()){for(const[o,J]of Object.entries(b.form))r[o]&&o!==(h?"":"slug")&&(r[o].value=J);h&&b.form.slug&&(ee=!0),Array.isArray(b.items)&&(s.value=b.items)}}}};
@@ -1 +1 @@
1
- import{mountScaffolder as i}from"../lib/crud-tutorial.js";function t(o,n){return{templateUrl:`/admin/js/templates/docs/${o}.html`,async onMount(e){typeof n=="function"&&n(e),Domma.icons.scan(),Domma.syntax.scan()}}}export const docViews={tutorialCrud:t("tutorial-crud",o=>{const n=o.find("#tutorial-scaffolder-mount").get(0);n&&i(n,null,null)}),tutorialPlugin:t("tutorial-plugin"),tutorialForms:t("tutorial-forms"),componentsReference:t("components-reference"),componentsHowto:t("components-howto"),componentsWalkthrough:t("components-walkthrough"),componentsRules:t("components-rules")};const a=["pages","media","navigation","dconfig","shortcodes","site-settings","plugins","users-roles","views","actions","cta-shortcode"],c=["authentication","pages","settings","layouts","navigation","media","users","plugins","collections","views","actions"],s=o=>o.split("-").map(n=>n.charAt(0).toUpperCase()+n.slice(1)).join("");for(const o of a)docViews["usage"+s(o)]=t(`usage-${o}`);for(const o of c)docViews["apiRef"+s(o)]=t(`api-${o}`);
1
+ import{mountScaffolder as i}from"../lib/crud-tutorial.js";function t(o,s){return{templateUrl:`/admin/js/templates/docs/${o}.html`,async onMount(e){typeof s=="function"&&s(e),Domma.icons.scan(),Domma.syntax.scan()}}}export const docViews={tutorialCrud:t("tutorial-crud",o=>{const s=o.find("#tutorial-scaffolder-mount").get(0);s&&i(s,null,null)}),tutorialPlugin:t("tutorial-plugin"),tutorialForms:t("tutorial-forms"),componentsReference:t("components-reference"),componentsHowto:t("components-howto"),componentsWalkthrough:t("components-walkthrough"),componentsRules:t("components-rules")};const a=["pages","media","navigation","dconfig","shortcodes","site-settings","plugins","users-roles","views","actions","cta-shortcode","editions","collections","tools"],c=["authentication","pages","settings","layouts","navigation","media","users","plugins","collections","views","actions","forms","scaffold","external","builder"],n=o=>o.split("-").map(s=>s.charAt(0).toUpperCase()+s.slice(1)).join("");for(const o of a)docViews["usage"+n(o)]=t(`usage-${o}`);for(const o of c)docViews["apiRef"+n(o)]=t(`api-${o}`);