domma-cms 0.3.0 → 0.5.2

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 (150) hide show
  1. package/README.md +3 -3
  2. package/admin/css/admin.css +1 -1
  3. package/admin/dist/domma/domma-tools.css +2313 -0
  4. package/admin/dist/domma/domma-tools.min.js +10 -0
  5. package/admin/index.html +4 -0
  6. package/admin/js/api.js +1 -1
  7. package/admin/js/app.js +8 -4
  8. package/admin/js/config/sidebar-config.js +1 -1
  9. package/admin/js/lib/markdown-toolbar.js +18 -10
  10. package/admin/js/templates/action-editor.html +171 -0
  11. package/admin/js/templates/actions-list.html +19 -0
  12. package/admin/js/templates/api-reference.html +1411 -0
  13. package/admin/js/templates/block-editor.html +158 -0
  14. package/admin/js/templates/blocks.html +8 -0
  15. package/admin/js/templates/collection-editor.html +47 -0
  16. package/admin/js/templates/collection-entries.html +3 -0
  17. package/admin/js/templates/collections.html +51 -4
  18. package/admin/js/templates/documentation.html +258 -0
  19. package/{plugins/form-builder/admin → admin/js}/templates/form-editor.html +238 -199
  20. package/{plugins/form-builder/admin → admin/js}/templates/form-submissions.html +30 -30
  21. package/{plugins/form-builder/admin/templates/forms-list.html → admin/js/templates/forms.html} +17 -17
  22. package/admin/js/templates/login.html +29 -4
  23. package/admin/js/templates/my-profile.html +17 -0
  24. package/admin/js/templates/page-editor.html +39 -0
  25. package/admin/js/templates/pages.html +6 -1
  26. package/admin/js/templates/pro-docs.html +259 -0
  27. package/admin/js/templates/role-editor.html +59 -0
  28. package/admin/js/templates/roles.html +10 -0
  29. package/admin/js/templates/settings.html +167 -23
  30. package/admin/js/templates/tutorials.html +81 -0
  31. package/admin/js/templates/user-editor.html +7 -0
  32. package/admin/js/templates/users.html +3 -26
  33. package/admin/js/templates/view-editor.html +201 -0
  34. package/admin/js/templates/view-preview.html +51 -0
  35. package/admin/js/templates/views-list.html +19 -0
  36. package/admin/js/views/action-editor.js +1 -0
  37. package/admin/js/views/actions-list.js +1 -0
  38. package/admin/js/views/api-reference.js +1 -0
  39. package/admin/js/views/block-editor.js +8 -0
  40. package/admin/js/views/blocks.js +4 -0
  41. package/admin/js/views/collection-editor.js +3 -3
  42. package/admin/js/views/collection-entries.js +1 -1
  43. package/admin/js/views/collections.js +1 -1
  44. package/admin/js/views/dashboard.js +1 -1
  45. package/admin/js/views/form-editor.js +8 -0
  46. package/admin/js/views/form-submissions.js +1 -0
  47. package/admin/js/views/forms.js +1 -0
  48. package/admin/js/views/index.js +1 -1
  49. package/admin/js/views/login.js +2 -2
  50. package/admin/js/views/media.js +1 -1
  51. package/admin/js/views/my-profile.js +1 -0
  52. package/admin/js/views/page-editor.js +34 -15
  53. package/admin/js/views/pages.js +5 -5
  54. package/admin/js/views/plugins.js +10 -10
  55. package/admin/js/views/pro-docs.js +1 -0
  56. package/admin/js/views/role-editor.js +1 -0
  57. package/admin/js/views/roles.js +4 -0
  58. package/admin/js/views/settings.js +3 -1
  59. package/admin/js/views/user-editor.js +1 -1
  60. package/admin/js/views/users.js +4 -7
  61. package/admin/js/views/view-editor.js +1 -0
  62. package/admin/js/views/view-preview.js +1 -0
  63. package/admin/js/views/views-list.js +1 -0
  64. package/bin/cli.js +1 -1
  65. package/config/auth.json +1 -0
  66. package/config/connections.json.bak +9 -0
  67. package/config/connections.json.example +9 -0
  68. package/config/navigation.json +5 -15
  69. package/config/plugins.json +19 -29
  70. package/config/server.json +6 -6
  71. package/config/site.json +16 -6
  72. package/package.json +25 -10
  73. package/plugins/example-analytics/stats.json +17 -12
  74. package/plugins/form-builder/data/forms/contacts.json +62 -62
  75. package/plugins/form-builder/data/forms/enquiries.json +103 -0
  76. package/plugins/form-builder/data/forms/feedback.json +17 -16
  77. package/plugins/form-builder/data/forms/notes.json +79 -0
  78. package/plugins/form-builder/data/forms/to-do.json +100 -0
  79. package/plugins/form-builder/data/submissions/contacts.json +1 -26
  80. package/plugins/form-builder/data/submissions/notes.json +1 -0
  81. package/plugins/form-builder/data/submissions/to-do.json +1 -0
  82. package/plugins/theme-roller/admin/templates/theme-roller.html +71 -0
  83. package/plugins/theme-roller/admin/views/theme-roller-view.js +403 -0
  84. package/plugins/theme-roller/config.js +1 -0
  85. package/plugins/theme-roller/plugin.js +233 -0
  86. package/plugins/theme-roller/plugin.json +31 -0
  87. package/plugins/theme-roller/public/active-theme.css +0 -0
  88. package/plugins/theme-roller/public/inject-head-late.html +1 -0
  89. package/public/css/forms.css +1 -0
  90. package/public/css/site.css +1 -1
  91. package/public/js/forms.js +1 -0
  92. package/public/js/site.js +1 -1
  93. package/scripts/build.js +194 -129
  94. package/scripts/pro.js +254 -0
  95. package/scripts/reset.js +33 -8
  96. package/scripts/seed.js +677 -128
  97. package/scripts/setup.js +1 -0
  98. package/server/middleware/auth.js +136 -120
  99. package/server/routes/api/actions.js +200 -0
  100. package/server/routes/api/auth.js +292 -146
  101. package/server/routes/api/blocks.js +84 -0
  102. package/server/routes/api/collections.js +79 -27
  103. package/{plugins/form-builder/plugin.js → server/routes/api/forms.js} +491 -505
  104. package/server/routes/api/layouts.js +49 -39
  105. package/server/routes/api/media.js +118 -92
  106. package/server/routes/api/navigation.js +40 -36
  107. package/server/routes/api/pages.js +132 -118
  108. package/server/routes/api/plugins.js +6 -3
  109. package/server/routes/api/settings.js +104 -88
  110. package/server/routes/api/users.js +27 -19
  111. package/server/routes/api/views.js +148 -0
  112. package/server/routes/public.js +124 -108
  113. package/server/server.js +269 -181
  114. package/server/services/actions.js +387 -0
  115. package/server/services/adapterRegistry.js +98 -0
  116. package/server/services/adapters/FileAdapter.js +192 -0
  117. package/server/services/adapters/MongoAdapter.js +220 -0
  118. package/server/services/blocks.js +162 -0
  119. package/server/services/collections.js +74 -86
  120. package/server/services/connectionManager.js +102 -0
  121. package/server/services/content.js +312 -307
  122. package/server/services/email.js +126 -0
  123. package/server/services/forms.js +173 -0
  124. package/server/services/markdown.js +1378 -747
  125. package/server/services/permissionRegistry.js +173 -0
  126. package/server/services/presetCollections.js +251 -0
  127. package/server/services/renderer.js +98 -2
  128. package/server/services/roles.js +227 -0
  129. package/server/services/rowAccess.js +104 -0
  130. package/server/services/userProfiles.js +199 -0
  131. package/server/services/users.js +281 -212
  132. package/server/services/views.js +280 -0
  133. package/server/templates/page.html +124 -113
  134. package/plugins/form-builder/admin/templates/form-settings.html +0 -29
  135. package/plugins/form-builder/admin/views/form-editor.js +0 -1444
  136. package/plugins/form-builder/admin/views/form-settings.js +0 -38
  137. package/plugins/form-builder/admin/views/form-submissions.js +0 -295
  138. package/plugins/form-builder/admin/views/forms-list.js +0 -164
  139. package/plugins/form-builder/config.js +0 -9
  140. package/plugins/form-builder/data/forms/consent.json +0 -104
  141. package/plugins/form-builder/data/forms/contact-details.json +0 -99
  142. package/plugins/form-builder/data/submissions/consent.json +0 -13
  143. package/plugins/form-builder/plugin.json +0 -52
  144. package/plugins/form-builder/public/inject-body.html +0 -352
  145. package/plugins/form-builder/public/inject-head.html +0 -58
  146. package/plugins/form-builder/public/package.json +0 -1
  147. package/scripts/copy-domma.js +0 -48
  148. package/server/services/userTypes.js +0 -167
  149. /package/plugins/form-builder/data/submissions/{contact-details.json → enquiries.json} +0 -0
  150. /package/{plugins/form-builder/public → public/js}/form-logic-engine.js +0 -0
package/admin/index.html CHANGED
@@ -10,6 +10,7 @@
10
10
  <link rel="stylesheet" href="/dist/domma/grid.css">
11
11
  <link rel="stylesheet" href="/dist/domma/elements.css">
12
12
  <link rel="stylesheet" href="/dist/domma/themes/domma-themes.css">
13
+ <link rel="stylesheet" href="/admin/dist/domma/domma-tools.css">
13
14
 
14
15
  <!-- Cropper.js - image editor -->
15
16
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/cropperjs@1/dist/cropper.min.css">
@@ -60,6 +61,9 @@
60
61
  <!-- DommaJS Syntax Highlighter (adds Domma.syntax) -->
61
62
  <script src="/dist/domma/domma-syntax.min.js"></script>
62
63
 
64
+ <!-- DommaJS Tools (adds E.editor, E.schemaBuilder etc.) -->
65
+ <script src="/admin/dist/domma/domma-tools.min.js"></script>
66
+
63
67
  <!-- Cropper.js - image editor -->
64
68
  <script src="https://cdn.jsdelivr.net/npm/cropperjs@1/dist/cropper.min.js"></script>
65
69
 
package/admin/js/api.js CHANGED
@@ -1 +1 @@
1
- const r="/api";function d(){return S.get("auth_token")}function h(){return S.get("auth_refresh_token")}function c(e){S.set("auth_token",e)}function u(){S.remove("auth_token"),S.remove("auth_refresh_token"),S.remove("auth_user")}async function m(){const e=h();if(!e)throw new Error("No refresh token");const o=await fetch(`${r}/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(!o.ok)throw u(),R.navigate("/login"),new Error("Token refresh failed");const{token:n}=await o.json();return c(n),n}async function t(e,o={}){let n=d();const a=i=>({...o.body!==void 0?{"Content-Type":"application/json"}:{},...o.headers,...i?{Authorization:`Bearer ${i}`}:{}});let s=await fetch(`${r}${e}`,{...o,headers:a(n)});if(s.status===401&&h())try{n=await m(),s=await fetch(`${r}${e}`,{...o,headers:a(n)})}catch{return}if(!s.ok){const i=await s.json().catch(()=>({error:"Request failed"}));throw new Error(i.error||i.message||`HTTP ${s.status}`)}return s.status===204?null:s.json()}async function l(e,o){const n=d(),a=n?{Authorization:`Bearer ${n}`}:{},s=await fetch(`${r}${e}`,{method:"POST",headers:a,body:o});if(!s.ok){const i=await s.json().catch(()=>({error:"Upload failed"}));throw new Error(i.error||i.message||`HTTP ${s.status}`)}return s.json()}export const api={auth:{setupStatus:()=>t("/auth/setup-status",{method:"GET"}),setup:e=>t("/auth/setup",{method:"POST",body:JSON.stringify(e)}),login:e=>t("/auth/login",{method:"POST",body:JSON.stringify(e)}),me:()=>t("/auth/me",{method:"GET"}),refresh:e=>t("/auth/refresh",{method:"POST",body:JSON.stringify({refreshToken:e})})},pages:{list:()=>t("/pages",{method:"GET"}),get:e=>t(`/pages${e}`,{method:"GET"}),create:e=>t("/pages",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/pages${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/pages${e}`,{method:"DELETE"}),preview:e=>t("/pages/preview",{method:"POST",body:JSON.stringify({markdown:e})})},settings:{get:()=>t("/settings",{method:"GET"}),save:e=>t("/settings",{method:"PUT",body:JSON.stringify(e)})},navigation:{get:()=>t("/navigation",{method:"GET"}),save:e=>t("/navigation",{method:"PUT",body:JSON.stringify(e)})},layouts:{get:()=>t("/layouts",{method:"GET"}),save:e=>t("/layouts",{method:"PUT",body:JSON.stringify(e)}),getOptions:()=>t("/layouts/options",{method:"GET"}),saveOptions:e=>t("/layouts/options",{method:"PUT",body:JSON.stringify(e)})},media:{list:()=>t("/media",{method:"GET"}),upload:e=>l("/media",e),delete:e=>t(`/media/${encodeURIComponent(e)}`,{method:"DELETE"}),rename:(e,o)=>t(`/media/${encodeURIComponent(e)}`,{method:"PATCH",body:JSON.stringify({newName:o})}),info:e=>t(`/media/${encodeURIComponent(e)}/info`,{method:"GET"}),transform:(e,o)=>t(`/media/${encodeURIComponent(e)}/transform`,{method:"POST",body:JSON.stringify(o)})},users:{list:()=>t("/users",{method:"GET"}),get:e=>t(`/users/${e}`,{method:"GET"}),create:e=>t("/users",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/users/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/users/${e}`,{method:"DELETE"})},plugins:{list:()=>t("/plugins",{method:"GET"}),update:(e,o)=>t(`/plugins/${e}`,{method:"PUT",body:JSON.stringify(o)}),adminConfig:()=>t("/plugins/admin-config",{method:"GET"})},collections:{list:()=>t("/collections",{method:"GET"}),get:e=>t(`/collections/${e}`,{method:"GET"}),create:e=>t("/collections",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/collections/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/collections/${e}`,{method:"DELETE"}),listEntries:(e,o={})=>{const n=new URLSearchParams(o).toString();return t(`/collections/${e}/entries${n?"?"+n:""}`,{method:"GET"})},getEntry:(e,o)=>t(`/collections/${e}/entries/${o}`,{method:"GET"}),createEntry:(e,o)=>t(`/collections/${e}/entries`,{method:"POST",body:JSON.stringify({data:o})}),updateEntry:(e,o,n)=>t(`/collections/${e}/entries/${o}`,{method:"PUT",body:JSON.stringify({data:n})}),deleteEntry:(e,o)=>t(`/collections/${e}/entries/${o}`,{method:"DELETE"}),clearEntries:e=>t(`/collections/${e}/entries`,{method:"DELETE"}),import:(e,o)=>t(`/collections/${e}/import`,{method:"POST",body:JSON.stringify({entries:o})}),publicList:(e,o={})=>{const n=new URLSearchParams(o).toString();return t(`/collections/${e}/public${n?"?"+n:""}`,{method:"GET"})}},settingsExt:{testEmail:e=>t("/settings/test-email",{method:"POST",body:JSON.stringify({to:e})})}};export function isAuthenticated(){return!!d()}export function getUser(){return S.get("auth_user")}export function setAuthData({token:e,refreshToken:o,user:n}){e&&c(e),o&&S.set("auth_refresh_token",o),n&&S.set("auth_user",n)}export function logout(){const e=h();e&&fetch(`${r}/auth/logout`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})}).catch(()=>{}),u(),R.navigate("/login")}export{t as apiRequest};
1
+ const r="/api";function a(){return S.get("auth_token")}function h(){return S.get("auth_refresh_token")}function m(e){S.set("auth_token",e)}function c(){S.remove("auth_token"),S.remove("auth_refresh_token"),S.remove("auth_user")}async function l(){const e=h();if(!e)throw new Error("No refresh token");const o=await fetch(`${r}/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(!o.ok)throw c(),R.navigate("/login"),new Error("Token refresh failed");const{token:s}=await o.json();return m(s),s}async function t(e,o={}){let s=a();const d=i=>({...o.body!==void 0?{"Content-Type":"application/json"}:{},...o.headers,...i?{Authorization:`Bearer ${i}`}:{}});let n=await fetch(`${r}${e}`,{...o,headers:d(s)});if(n.status===401&&h())try{s=await l(),n=await fetch(`${r}${e}`,{...o,headers:d(s)})}catch{return}if(!n.ok){const i=await n.json().catch(()=>({error:"Request failed"}));throw new Error(i.error||i.message||`HTTP ${n.status}`)}return n.status===204?null:n.json()}async function u(e,o){const s=a(),d=s?{Authorization:`Bearer ${s}`}:{},n=await fetch(`${r}${e}`,{method:"POST",headers:d,body:o});if(!n.ok){const i=await n.json().catch(()=>({error:"Upload failed"}));throw new Error(i.error||i.message||`HTTP ${n.status}`)}return n.json()}export const api={auth:{setupStatus:()=>t("/auth/setup-status",{method:"GET"}),setup:e=>t("/auth/setup",{method:"POST",body:JSON.stringify(e)}),login:e=>t("/auth/login",{method:"POST",body:JSON.stringify(e)}),me:()=>t("/auth/me",{method:"GET"}),updateMe:e=>t("/auth/me",{method:"PUT",body:JSON.stringify(e)}),refresh:e=>t("/auth/refresh",{method:"POST",body:JSON.stringify({refreshToken:e})}),forgotPassword:e=>t("/auth/forgot-password",{method:"POST",body:JSON.stringify({email:e})}),resetPassword:(e,o)=>t("/auth/reset-password",{method:"POST",body:JSON.stringify({token:e,password:o})})},pages:{list:()=>t("/pages",{method:"GET"}),get:e=>t(`/pages${e}`,{method:"GET"}),create:e=>t("/pages",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/pages${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/pages${e}`,{method:"DELETE"}),preview:e=>t("/pages/preview",{method:"POST",body:JSON.stringify({markdown:e})}),tags:()=>t("/pages/tags",{method:"GET"}).then(e=>e.tags||[])},settings:{get:()=>t("/settings",{method:"GET"}),save:e=>t("/settings",{method:"PUT",body:JSON.stringify(e)})},navigation:{get:()=>t("/navigation",{method:"GET"}),save:e=>t("/navigation",{method:"PUT",body:JSON.stringify(e)})},layouts:{get:()=>t("/layouts",{method:"GET"}),save:e=>t("/layouts",{method:"PUT",body:JSON.stringify(e)}),getOptions:()=>t("/layouts/options",{method:"GET"}),saveOptions:e=>t("/layouts/options",{method:"PUT",body:JSON.stringify(e)})},media:{list:()=>t("/media",{method:"GET"}),upload:e=>u("/media",e),delete:e=>t(`/media/${encodeURIComponent(e)}`,{method:"DELETE"}),rename:(e,o)=>t(`/media/${encodeURIComponent(e)}`,{method:"PATCH",body:JSON.stringify({newName:o})}),info:e=>t(`/media/${encodeURIComponent(e)}/info`,{method:"GET"}),transform:(e,o)=>t(`/media/${encodeURIComponent(e)}/transform`,{method:"POST",body:JSON.stringify(o)})},users:{list:()=>t("/users",{method:"GET"}),get:e=>t(`/users/${e}`,{method:"GET"}),create:e=>t("/users",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/users/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/users/${e}`,{method:"DELETE"})},plugins:{list:()=>t("/plugins",{method:"GET"}),update:(e,o)=>t(`/plugins/${e}`,{method:"PUT",body:JSON.stringify(o)}),adminConfig:()=>t("/plugins/admin-config",{method:"GET"})},collections:{list:()=>t("/collections",{method:"GET"}),proStatus:()=>t("/collections/pro-status",{method:"GET"}),get:e=>t(`/collections/${e}`,{method:"GET"}),create:e=>t("/collections",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/collections/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/collections/${e}`,{method:"DELETE"}),listEntries:(e,o={})=>{const s=new URLSearchParams(o).toString();return t(`/collections/${e}/entries${s?"?"+s:""}`,{method:"GET"})},getEntry:(e,o)=>t(`/collections/${e}/entries/${o}`,{method:"GET"}),createEntry:(e,o)=>t(`/collections/${e}/entries`,{method:"POST",body:JSON.stringify({data:o})}),updateEntry:(e,o,s)=>t(`/collections/${e}/entries/${o}`,{method:"PUT",body:JSON.stringify({data:s})}),deleteEntry:(e,o)=>t(`/collections/${e}/entries/${o}`,{method:"DELETE"}),clearEntries:e=>t(`/collections/${e}/entries`,{method:"DELETE"}),import:(e,o)=>t(`/collections/${e}/import`,{method:"POST",body:JSON.stringify({entries:o})}),publicList:(e,o={})=>{const s=new URLSearchParams(o).toString();return t(`/collections/${e}/public${s?"?"+s:""}`,{method:"GET"})},getConnections:()=>t("/collections/connections",{method:"GET"}),saveConnections:e=>t("/collections/connections",{method:"PUT",body:JSON.stringify(e)})},forms:{list:()=>t("/forms",{method:"GET"}),create:e=>t("/forms",{method:"POST",body:JSON.stringify(e)}),get:e=>t(`/forms/${e}`,{method:"GET"}),update:(e,o)=>t(`/forms/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/forms/${e}`,{method:"DELETE"}),listSubmissions:e=>t(`/forms/${e}/submissions`,{method:"GET"}),clearSubmissions:e=>t(`/forms/${e}/submissions`,{method:"DELETE"}),deleteSubmission:(e,o)=>t(`/forms/${e}/submissions/${o}`,{method:"DELETE"}),testEmail:e=>t("/forms/test-email",{method:"POST",body:JSON.stringify({to:e})})},views:{list:()=>t("/views",{method:"GET"}),get:e=>t(`/views/${e}`,{method:"GET"}),create:e=>t("/views",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/views/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/views/${e}`,{method:"DELETE"}),execute:(e,o={})=>{const s=new URLSearchParams(o).toString();return t(`/views/${e}/execute${s?"?"+s:""}`,{method:"GET"})},forCollection:e=>t(`/views/collection/${e}`,{method:"GET"})},actions:{list:()=>t("/actions",{method:"GET"}),get:e=>t(`/actions/${e}`,{method:"GET"}),create:e=>t("/actions",{method:"POST",body:JSON.stringify(e)}),update:(e,o)=>t(`/actions/${e}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/actions/${e}`,{method:"DELETE"}),execute:(e,o)=>t(`/actions/${e}/execute`,{method:"POST",body:JSON.stringify({entryId:o})}),forCollection:e=>t(`/actions/collection/${e}`,{method:"GET"}),checkAccess:(e,o)=>t(`/actions/${e}/check-access`,{method:"POST",body:JSON.stringify({entryIds:o})})},blocks:{list:()=>t("/blocks",{method:"GET"}),get:e=>t(`/blocks/${encodeURIComponent(e)}`,{method:"GET"}),put:(e,o)=>t(`/blocks/${encodeURIComponent(e)}`,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(`/blocks/${encodeURIComponent(e)}`,{method:"DELETE"})},get:e=>t(e,{method:"GET"}),post:(e,o)=>t(e,{method:"POST",body:JSON.stringify(o)}),put:(e,o)=>t(e,{method:"PUT",body:JSON.stringify(o)}),delete:e=>t(e,{method:"DELETE"}),themes:{list:()=>t("/plugins/theme-roller/themes",{method:"GET"})},settingsExt:{testEmail:e=>t("/settings/test-email",{method:"POST",body:JSON.stringify({to:e})})}};export function isAuthenticated(){return!!a()}export function getUser(){return S.get("auth_user")}export function setAuthData({token:e,refreshToken:o,user:s}){e&&m(e),o&&S.set("auth_refresh_token",o),s&&S.set("auth_user",s)}export function logout(){const e=h();e&&fetch(`${r}/auth/logout`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})}).catch(()=>{}),c(),R.navigate("/login")}export{t as apiRequest};
package/admin/js/app.js CHANGED
@@ -1,7 +1,11 @@
1
- import{getSidebarConfig as S}from"./config/sidebar-config.js";import{views as D}from"./views/index.js";import{api as n,getUser as d,isAuthenticated as r,logout as y}from"./api.js";$(()=>{(async()=>{try{const t=await n.settings.get();Domma.theme.init({theme:t.adminTheme||"charcoal-dark",persist:!0})}catch{Domma.theme.init({theme:"charcoal-dark",persist:!0})}})(),R.use(async(t,a,e)=>{if(t.path==="/login")return e();if(!r()){R.navigate("/login");return}e()});let o=null;async function f(){if(!r())return{};try{const[t,a,e,i,s]=await Promise.all([n.pages.list().catch(()=>[]),n.media.list().catch(()=>[]),n.users.list().catch(()=>[]),n.plugins.list().catch(()=>[]),n.collections.list().catch(()=>[])]);return{pages:t.length,media:a.length,users:e.length,plugins:i.filter(c=>c.enabled).length||i.length,collections:s.length}}catch{return{}}}async function p(t){o&&$("#admin-sidebar").empty();const a=await f();o=Domma.elements.sidebar("#admin-sidebar",{header:{title:"CMS Admin",icon:"layout"},items:S(t,a,u),collapsible:!0,collapseAt:992,push:!0,contentSelector:".dashboard-main",top:"60px"}),w(),v()}function w(){$("#admin-sidebar .sidebar-link").each(function(){const t=$(this).find(".sidebar-text").text().trim();t&&$(this).attr("data-tooltip",t)}),E.tooltip("#admin-sidebar [data-tooltip]",{placement:"right"})}function v(){$("#admin-sidebar .sidebar-heading").each(function(){const t=$(this);t.addClass("sidebar-heading--collapsible"),t.append('<span class="sidebar-heading-toggle"><span data-icon="chevron-down"></span></span>'),t.on("click",function(){const a=!t.hasClass("is-collapsed");t.toggleClass("is-collapsed",a);let e=t.next();for(;e.length&&!e.hasClass("sidebar-heading")&&!e.hasClass("sidebar-divider");)e.toggle(!a),e=e.next()})}),Domma.icons.scan("#admin-sidebar")}M.subscribe("router:afterChange",({to:t})=>{o&&t.path!=="/login"&&o.setActive("#"+t.path)});const C=[{path:"/",view:"dashboard",title:"Dashboard - Domma CMS"},{path:"/pages",view:"pages",title:"Pages - Domma CMS"},{path:"/pages/new",view:"pageEditor",title:"New Page - Domma CMS"},{path:"/pages/edit/*",view:"pageEditor",title:"Edit Page - Domma CMS"},{path:"/media",view:"media",title:"Media - Domma CMS"},{path:"/navigation",view:"navigation",title:"Navigation - Domma CMS"},{path:"/layouts",view:"layouts",title:"Layouts - Domma CMS"},{path:"/settings",view:"settings",title:"Settings - Domma CMS"},{path:"/users",view:"users",title:"Users - Domma CMS"},{path:"/users/new",view:"userEditor",title:"New User - Domma CMS"},{path:"/users/edit/:id",view:"userEditor",title:"Edit User - Domma CMS"},{path:"/plugins",view:"plugins",title:"Plugins - Domma CMS"},{path:"/documentation",view:"documentation",title:"Usage - Domma CMS"},{path:"/tutorials",view:"tutorials",title:"Tutorials - Domma CMS"},{path:"/collections",view:"collections",title:"Collections - Domma CMS"},{path:"/collections/new",view:"collectionEditor",title:"New Collection - Domma CMS"},{path:"/collections/edit/:slug",view:"collectionEditor",title:"Edit Collection - Domma CMS"},{path:"/collections/:slug/entries",view:"collectionEntries",title:"Entries - Domma CMS"},{path:"/login",view:"login",title:"Sign in - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}}];M.subscribe("router:afterChange",async({to:t,from:a})=>{if(t.path!=="/login"){if($("#admin-sidebar").show(),$("#admin-topbar").show(),a?.path==="/login"){await g();const e=d();e&&p(e.role)}h()}}),M.subscribe("router:afterChange",()=>{setTimeout(()=>{$(".btn-primary, .btn-danger").length&&Domma.effects.reveal(".btn-primary, .btn-danger",{animation:"fade",stagger:40,duration:300})},50)}),$("#view-container").on("click",".card-collapsible .card-header",function(t){$(t.target).closest("button, a").length||$(this).closest(".card").toggleClass("card-collapsed")}),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash==="#/login")return;const a=document.querySelector("#view-container .view-header button.btn-primary");a&&(t.preventDefault(),a.click())});const m={...D},l=[...C];let u=[];async function g(){if(r())try{const t=await n.plugins.adminConfig();u=t.sidebar||[],t.routes?.length&&l.push(...t.routes);for(const[a,e]of Object.entries(t.views||{}))try{const i=await import(`/plugins/${e.entry}`);m[a]=i[e.exportName]}catch{}}catch{}}function h(){const t=d();if(!t||$("#topbar-user-name").length)return;const e={admin:"Admin",manager:"Manager",editor:"Editor",subscriber:"Subscriber"}[t.role]||t.role;$("#topbar-user").html(`
2
- <span id="topbar-user-name" class="topbar-user-name">${b(t.name)}</span>
3
- <span class="topbar-role-badge topbar-role-badge--${b(t.role)}">${e}</span>
1
+ import{getSidebarConfig as L}from"./config/sidebar-config.js";import{views as U}from"./views/index.js";import{api as s,getUser as b,isAuthenticated as n,logout as V}from"./api.js";$(()=>{(async()=>{try{const t=n()?await s.settings.get():null;Domma.theme.init({theme:t?.adminTheme||"charcoal-dark",persist:!0})}catch{Domma.theme.init({theme:"charcoal-dark",persist:!0})}})(),R.use(async(t,a,e)=>{if(t.path==="/login"||t.path==="/reset-password")return e();if(!n()){R.navigate("/login");return}e()});let r=null;async function f(){if(!n())return{};try{const[t,a,e,i,o,c,D,y,k,P,N,x,A]=await Promise.all([s.pages.list().catch(()=>[]),s.media.list().catch(()=>[]),s.users.list().catch(()=>[]),s.plugins.list().catch(()=>[]),s.collections.list().catch(()=>[]),s.forms.list().catch(()=>[]),s.themes.list().catch(()=>[]),s.views.list().catch(()=>[]),s.actions.list().catch(()=>[]),s.blocks.list().catch(()=>[]),s.navigation.get().catch(()=>({})),s.layouts.get().catch(()=>({})),s.get("/collections/roles/entries?limit=100").catch(()=>({entries:[]}))]);return{pages:t.length,media:a.length,users:e.length,plugins:i.filter(W=>W.enabled).length||i.length,collections:o.length,forms:c.length,themes:D.length,views:y.length,actions:k.length,blocks:P.length,navigation:(N.items||[]).length,layouts:Object.keys(x).length,roles:(A.entries||[]).length}}catch{return{}}}async function m(){try{return(await s.get("/auth/permissions")).permissions||[]}catch{return[]}}async function d(t){r&&$("#admin-sidebar").empty();const a=await f(),e=h.map(i=>{if(!i.countKey)return i;const o=a[i.countKey];return{...i,badge:o!=null&&o>0?String(o):null}});r=Domma.elements.sidebar("#admin-sidebar",{header:{title:"CMS Admin",icon:"layout"},items:L(t,a,e),collapsible:!0,collapseAt:992,push:!0,contentSelector:".dashboard-main",top:"60px"}),v(),C()}function v(){$("#admin-sidebar .sidebar-link").each(function(){const t=$(this).find(".sidebar-text").text().trim();t&&$(this).attr("data-tooltip",t)}),E.tooltip("#admin-sidebar [data-tooltip]",{placement:"right"})}function C(){$("#admin-sidebar .sidebar-heading").each(function(){const t=$(this);t.addClass("sidebar-heading--collapsible"),t.append('<span class="sidebar-heading-toggle"><span data-icon="chevron-down"></span></span>'),t.on("click",function(){const a=!t.hasClass("is-collapsed");t.toggleClass("is-collapsed",a);let e=t.next();for(;e.length&&!e.hasClass("sidebar-heading")&&!e.hasClass("sidebar-divider");)e.toggle(!a),e=e.next()})}),Domma.icons.scan("#admin-sidebar")}M.subscribe("router:afterChange",({to:t})=>{r&&t.path!=="/login"&&t.path!=="/reset-password"&&r.setActive("#"+t.path)});const S=[{path:"/",view:"dashboard",title:"Dashboard - Domma CMS"},{path:"/pages",view:"pages",title:"Pages - Domma CMS"},{path:"/pages/new",view:"pageEditor",title:"New Page - Domma CMS"},{path:"/pages/edit/*",view:"pageEditor",title:"Edit Page - Domma CMS"},{path:"/media",view:"media",title:"Media - Domma CMS"},{path:"/navigation",view:"navigation",title:"Navigation - Domma CMS"},{path:"/layouts",view:"layouts",title:"Layouts - Domma CMS"},{path:"/settings",view:"settings",title:"Settings - Domma CMS"},{path:"/users",view:"users",title:"Users - Domma CMS"},{path:"/users/new",view:"userEditor",title:"New User - Domma CMS"},{path:"/users/edit/:id",view:"userEditor",title:"Edit User - Domma CMS"},{path:"/plugins",view:"plugins",title:"Plugins - Domma CMS"},{path:"/documentation",view:"documentation",title:"Usage - Domma CMS"},{path:"/tutorials",view:"tutorials",title:"Tutorials - Domma CMS"},{path:"/api-reference",view:"apiReference",title:"API Reference - Domma CMS"},{path:"/collections",view:"collections",title:"Collections - Domma CMS"},{path:"/collections/new",view:"collectionEditor",title:"New Collection - Domma CMS"},{path:"/collections/edit/:slug",view:"collectionEditor",title:"Edit Collection - Domma CMS"},{path:"/collections/:slug/entries",view:"collectionEntries",title:"Entries - Domma CMS"},{path:"/forms",view:"forms",title:"Forms - Domma CMS"},{path:"/forms/new",view:"formEditor",title:"New Form - Domma CMS"},{path:"/forms/edit/:slug",view:"formEditor",title:"Edit Form - Domma CMS"},{path:"/forms/:slug/submissions",view:"formSubmissions",title:"Submissions - Domma CMS"},{path:"/views",view:"viewsList",title:"Views - Domma CMS"},{path:"/views/new",view:"viewEditor",title:"New View - Domma CMS"},{path:"/views/edit/:slug",view:"viewEditor",title:"Edit View - Domma CMS"},{path:"/views/:slug/preview",view:"viewPreview",title:"View Preview - Domma CMS"},{path:"/actions",view:"actionsList",title:"Actions - Domma CMS"},{path:"/actions/new",view:"actionEditor",title:"New Action - Domma CMS"},{path:"/actions/edit/:slug",view:"actionEditor",title:"Edit Action - Domma CMS"},{path:"/pro/docs",view:"proDocs",title:"Pro Documentation - Domma CMS"},{path:"/blocks",view:"blocks",title:"Blocks - Domma CMS"},{path:"/blocks/new",view:"blockEditor",title:"New Block - Domma CMS"},{path:"/blocks/edit/:name",view:"blockEditor",title:"Edit Block - Domma CMS"},{path:"/my-profile",view:"myProfile",title:"My Profile - Domma CMS"},{path:"/roles",view:"roles",title:"Roles & Permissions - Domma CMS"},{path:"/roles/edit/:id",view:"roleEditor",title:"Edit Role - Domma CMS"},{path:"/login",view:"login",title:"Sign in - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}},{path:"/reset-password",view:"login",title:"Reset Password - Domma CMS",onEnter:()=>{$("#admin-sidebar").hide(),$("#admin-topbar").hide()}}];M.subscribe("router:afterChange",async({to:t,from:a})=>{if(t.path!=="/login"&&t.path!=="/reset-password"){if($("#admin-sidebar").show(),$("#admin-topbar").show(),a?.path==="/login"||a?.path==="/reset-password"){await u();const e=await m();d(e)}g()}}),M.subscribe("router:afterChange",()=>{setTimeout(()=>{$(".btn-primary, .btn-danger").length&&Domma.effects.reveal(".btn-primary, .btn-danger",{animation:"fade",stagger:40,duration:300})},50)}),$("#view-container").on("click",".card-collapsible .card-header",function(t){$(t.target).closest("button, a").length||$(this).closest(".card").toggleClass("card-collapsed")}),document.addEventListener("keydown",t=>{if(!(t.ctrlKey||t.metaKey)||t.key!=="s"||window.location.hash==="#/login"||window.location.hash.startsWith("#/reset-password"))return;const a=document.querySelector("#view-container .view-header button.btn-primary");a&&(t.preventDefault(),a.click())});const p={...U},l=[...S];let h=[];async function u(){if(n())try{const t=await s.plugins.adminConfig();h=t.sidebar||[],t.routes?.length&&l.push(...t.routes);for(const[a,e]of Object.entries(t.views||{}))try{const i=await import(`/plugins/${e.entry}`);p[a]=i[e.exportName]}catch{}}catch{}}function g(){const t=b();if(!t||$("#topbar-user-name").length)return;const e={admin:"Admin",manager:"Manager",editor:"Editor",subscriber:"Subscriber"}[t.role]||t.role;$("#topbar-user").html(`
2
+ <span id="topbar-user-name" class="topbar-user-name">${w(t.name)}</span>
3
+ <span class="topbar-role-badge topbar-role-badge--${w(t.role)}">${e}</span>
4
4
  `),$("#topbar-actions").html(`
5
+ <a href="#/my-profile" class="topbar-action-link" data-tooltip="My Profile" data-tooltip-placement="bottom">
6
+ <span data-icon="user"></span>
7
+ <span>My Profile</span>
8
+ </a>
5
9
  <a href="#/settings" class="topbar-action-link" data-tooltip="Settings" data-tooltip-placement="bottom">
6
10
  <span data-icon="settings"></span>
7
11
  <span>Settings</span>
@@ -10,4 +14,4 @@ import{getSidebarConfig as S}from"./config/sidebar-config.js";import{views as D}
10
14
  <span data-icon="log-out"></span>
11
15
  <span>Sign out</span>
12
16
  </a>
13
- `),$("#topbar-logout-btn").on("click",i=>{i.preventDefault(),y()}),Domma.icons.scan("#admin-topbar"),E.tooltip("#topbar-actions [data-tooltip]",{placement:"bottom"})}function b(t){return String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}(async()=>{if(!r())window.location.hash="#/login";else{await g();const i=d();i&&(await p(i.role),h())}R.init({container:"#view-container",routes:l,views:m,default:"/",transitions:{enter:"fadeIn",leave:"fadeOut",duration:150}});const t=R._extractParams.bind(R);R._extractParams=function(i,s){if(i.endsWith("/*")){const c=i.slice(0,-2);return s.startsWith(c+"/")?{}:null}return t(i,s)};const a=(window.location.hash||"#/").slice(1)||"/";l.filter(i=>i.path.endsWith("/*")).some(i=>a.startsWith(i.path.slice(0,-2)+"/"))&&R._handleRouteChange()})()});
17
+ `),$("#topbar-logout-btn").on("click",i=>{i.preventDefault(),V()}),Domma.icons.scan("#admin-topbar"),E.tooltip("#topbar-actions [data-tooltip]",{placement:"bottom"})}function w(t){return String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}(async()=>{if(!n()&&!window.location.hash.startsWith("#/reset-password"))window.location.hash="#/login";else if(await u(),b()){const o=await m();await d(o),g()}R.init({container:"#view-container",routes:l,views:p,default:"/",transitions:{enter:"fadeIn",leave:"fadeOut",duration:150}});const t=R._extractParams.bind(R);R._extractParams=function(i,o){if(i.endsWith("/*")){const c=i.slice(0,-2);return o.startsWith(c+"/")?{}:null}return t(i,o)};const a=(window.location.hash||"#/").slice(1)||"/";l.filter(i=>i.path.endsWith("/*")).some(i=>a.startsWith(i.path.slice(0,-2)+"/"))&&R._handleRouteChange()})()});
@@ -1 +1 @@
1
- export function getSidebarConfig(a,e={},u=[]){const o=a==="admin",n=o||a==="manager",t=s=>s!=null&&s>0?String(s):null,i=[];return i.push({heading:"Overview"},{id:"dashboard",text:"Dashboard",icon:"home",url:"#/",section:"#/"},{divider:!0}),n&&i.push({heading:"Structure"},{id:"navigation",text:"Navigation",icon:"menu",url:"#/navigation",section:"#/navigation"},{id:"layouts",text:"Layouts",icon:"layout",url:"#/layouts",section:"#/layouts"},{divider:!0}),i.push({heading:"Content"},{id:"pages",text:"Pages",icon:"file-text",url:"#/pages",section:"#/pages",badge:t(e.pages)},{id:"media",text:"Media",icon:"image",url:"#/media",section:"#/media",badge:t(e.media)}),n&&i.push({divider:!0},{heading:"Data"},{id:"collections",text:"Collections",icon:"database",url:"#/collections",section:"#/collections",badge:t(e.collections)}),i.push({divider:!0},{heading:"Configuration"}),n&&i.push({id:"users",text:"Users",icon:"users",url:"#/users",section:"#/users",badge:t(e.users)},{id:"settings",text:"Site Settings",icon:"settings",url:"#/settings",section:"#/settings"}),i.push({divider:!0},{heading:"Plugins"}),o&&(i.push({id:"plugins",text:"Plugins",icon:"package",url:"#/plugins",section:"#/plugins",badge:t(e.plugins)}),i.push(...u)),i.push({divider:!0},{heading:"View Site"}),i.push({id:"view-site",text:"View Site",icon:"external-link",url:"/",section:"/"}),i.push({divider:!0},{heading:"Documentation"}),i.push({id:"documentation",text:"Usage",icon:"book",url:"#/documentation",section:"#/documentation",badge:t(e.documents)},{id:"tutorials",text:"Tutorials",icon:"document",url:"#/tutorials",section:"#/documentation",badge:t(e.tutorials)}),i}
1
+ export function getSidebarConfig(n=[],t={},a=[]){const e=o=>n.includes(o)||["read","create","update","delete"].some(d=>n.includes(`${o}.${d}`)),s=o=>o!=null&&o>0?String(o):null,i=[];return i.push({heading:"Overview"},{id:"dashboard",text:"Dashboard",icon:"home",url:"#/",section:"#/"},{divider:!0}),(e("navigation")||e("layouts"))&&i.push({heading:"Structure"},...e("navigation")?[{id:"navigation",text:"Navigation",icon:"menu",url:"#/navigation",section:"#/navigation",badge:s(t.navigation)}]:[],...e("layouts")?[{id:"layouts",text:"Layouts",icon:"layout",url:"#/layouts",section:"#/layouts",badge:s(t.layouts)}]:[],{divider:!0}),i.push({heading:"Content"}),e("pages")&&i.push({id:"pages",text:"Pages",icon:"file-text",url:"#/pages",section:"#/pages",badge:s(t.pages)}),e("media")&&i.push({id:"media",text:"Media",icon:"image",url:"#/media",section:"#/media",badge:s(t.media)}),(e("collections")||e("views")||e("actions"))&&(i.push({divider:!0},{heading:"Data"}),e("collections")&&(i.push({id:"collections",text:"Collections",icon:"database",url:"#/collections",section:"#/collections",badge:s(t.collections)}),i.push({id:"forms",text:"Forms",icon:"layout",url:"#/forms",section:"#/forms",badge:s(t.forms)})),e("views")&&i.push({id:"views",text:"Views",icon:"eye",url:"#/views",section:"#/views",badge:s(t.views)}),e("actions")&&i.push({id:"actions",text:"Actions",icon:"zap",url:"#/actions",section:"#/actions",badge:s(t.actions)}),e("pages")&&i.push({id:"blocks",text:"Blocks",icon:"box",url:"#/blocks",section:"#/blocks",badge:s(t.blocks)})),i.push({divider:!0},{heading:"Configuration"}),e("plugins")&&i.push({id:"roles",text:"Roles",icon:"shield",url:"#/roles",section:"#/roles",badge:s(t.roles)}),e("users")&&i.push({id:"users",text:"Users",icon:"users",url:"#/users",section:"#/users",badge:s(t.users)}),e("settings")&&i.push({id:"settings",text:"Site Settings",icon:"settings",url:"#/settings",section:"#/settings"}),i.push({divider:!0},{heading:"Plugins"}),e("plugins")&&(i.push({id:"plugins",text:"Plugins",icon:"package",url:"#/plugins",section:"#/plugins",badge:s(t.plugins)}),i.push(...a)),(e("navigation")||e("layouts")||e("settings"))&&i.push({divider:!0},{heading:"Pro"},{id:"pro-docs",text:"Documentation",icon:"zap",url:"#/pro/docs",section:"#/pro/docs"}),i.push({divider:!0},{heading:"Account"},{id:"my-profile",text:"My Profile",icon:"user",url:"#/my-profile",section:"#/my-profile"}),i.push({divider:!0},{heading:"View Site"},{id:"view-site",text:"View Site",icon:"external-link",url:"/",section:"/"}),i.push({divider:!0},{heading:"Documentation"},{id:"documentation",text:"Usage",icon:"book",url:"#/documentation",section:"#/documentation",badge:s(t.documents)},{id:"tutorials",text:"Tutorials",icon:"document",url:"#/tutorials",section:"#/documentation",badge:s(t.tutorials)},{id:"api-reference",text:"API Reference",icon:"code",url:"#/api-reference",section:"#/documentation"}),i}
@@ -1,6 +1,6 @@
1
- function z(){I.register("bold",{viewBox:"0 0 24 24",path:"M7 5H14a3 3 0 0 1 0 6H7V5zM7 11H15a3 3 0 0 1 0 6H7V11z",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("italic",{viewBox:"0 0 24 24",path:"M11 5h4M9 19h4M13 5l-2 14",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("strikethrough",{viewBox:"0 0 24 24",path:"M16 4H9a3 3 0 0 0 0 6h6a3 3 0 0 1 0 6H6M3 12h18",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("quote",{viewBox:"0 0 24 24",path:"M3 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1zM15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("eye",{viewBox:"0 0 24 24",path:"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8zM12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("expand",{viewBox:"0 0 24 24",path:"M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("columns",{viewBox:"0 0 24 24",path:"M3 4h18a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1zM12 4v16",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("card",{viewBox:"0 0 24 24",paths:["M3 5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z","M3 9h18"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("help-circle",{viewBox:"0 0 24 24",path:"M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10zM12 17h.01M12 13a2 2 0 0 0 2-2 2 2 0 0 0-2-2 2 2 0 0 0-2 2",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("spacer-insert",{viewBox:"0 0 24 24",paths:["M3 8h18","M3 16h18","M12 8v8"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("icon-pick",{viewBox:"0 0 24 24",paths:["M4 4h6v6H4z","M14 4h6v6h-6z","M4 14h6v6H4z","M14 14h6v6h-6z"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("sparkles",{viewBox:"0 0 24 24",paths:["M12 3L13.5 8.5H19L14.5 11.5L16 17L12 14L8 17L9.5 11.5L5 8.5H10.5L12 3Z","M19 3L19.7 5.3H22L20.2 6.6L20.9 9L19 7.7L17.1 9L17.8 6.6L16 5.3H18.3L19 3Z","M5 13L5.5 14.7H7L5.8 15.5L6.3 17.2L5 16.3L3.7 17.2L4.2 15.5L3 14.7H4.5L5 13Z"],stroke:"currentColor",fill:"none",strokeWidth:1.5,strokeLinecap:"round",strokeLinejoin:"round"})}export function wrapSelection(e,t,d){const o=e.selectionStart,l=e.selectionEnd,n=e.value,c=n.substring(o,l);c?(e.value=n.substring(0,o)+t+c+d+n.substring(l),e.selectionStart=o+t.length,e.selectionEnd=l+t.length):(e.value=n.substring(0,o)+t+d+n.substring(o),e.selectionStart=e.selectionEnd=o+t.length),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()}export function insertAtCursor(e,t){const d=e.selectionStart,o=e.value;e.value=o.substring(0,d)+t+o.substring(d),e.selectionStart=e.selectionEnd=d+t.length,e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()}export function insertLine(e,t){const d=e.selectionStart,o=e.value,l=o.lastIndexOf(`
2
- `,d-1)+1,n=o.indexOf(`
3
- `,l),c=o.substring(l,n===-1?o.length:n);if(c.startsWith(t)){const r=n===-1?"":o.substring(n);e.value=o.substring(0,l)+c.substring(t.length)+r,e.selectionStart=e.selectionEnd=Math.max(l,d-t.length)}else e.value=o.substring(0,l)+t+o.substring(l),e.selectionStart=e.selectionEnd=d+t.length;e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()}const N=[{category:"Entrance"},{label:"Reveal (fade)",snippet:e=>`[reveal animation="fade"]
1
+ function z(){I.register("bold",{viewBox:"0 0 24 24",path:"M7 5H14a3 3 0 0 1 0 6H7V5zM7 11H15a3 3 0 0 1 0 6H7V11z",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("italic",{viewBox:"0 0 24 24",path:"M11 5h4M9 19h4M13 5l-2 14",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("strikethrough",{viewBox:"0 0 24 24",path:"M16 4H9a3 3 0 0 0 0 6h6a3 3 0 0 1 0 6H6M3 12h18",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("quote",{viewBox:"0 0 24 24",path:"M3 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1zM15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("eye",{viewBox:"0 0 24 24",path:"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8zM12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("expand",{viewBox:"0 0 24 24",path:"M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("columns",{viewBox:"0 0 24 24",path:"M3 4h18a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1zM12 4v16",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("card",{viewBox:"0 0 24 24",paths:["M3 5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z","M3 9h18"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("help-circle",{viewBox:"0 0 24 24",path:"M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10zM12 17h.01M12 13a2 2 0 0 0 2-2 2 2 0 0 0-2-2 2 2 0 0 0-2 2",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("spacer-insert",{viewBox:"0 0 24 24",paths:["M3 8h18","M3 16h18","M12 8v8"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("icon-pick",{viewBox:"0 0 24 24",paths:["M4 4h6v6H4z","M14 4h6v6h-6z","M4 14h6v6H4z","M14 14h6v6h-6z"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("sparkles",{viewBox:"0 0 24 24",paths:["M12 3L13.5 8.5H19L14.5 11.5L16 17L12 14L8 17L9.5 11.5L5 8.5H10.5L12 3Z","M19 3L19.7 5.3H22L20.2 6.6L20.9 9L19 7.7L17.1 9L17.8 6.6L16 5.3H18.3L19 3Z","M5 13L5.5 14.7H7L5.8 15.5L6.3 17.2L5 16.3L3.7 17.2L4.2 15.5L3 14.7H4.5L5 13Z"],stroke:"currentColor",fill:"none",strokeWidth:1.5,strokeLinecap:"round",strokeLinejoin:"round"})}export function wrapSelection(e,t,p){const i=e.selectionStart,c=e.selectionEnd,n=e.value,r=n.substring(i,c);r?(e.value=n.substring(0,i)+t+r+p+n.substring(c),e.selectionStart=i+t.length,e.selectionEnd=c+t.length):(e.value=n.substring(0,i)+t+p+n.substring(i),e.selectionStart=e.selectionEnd=i+t.length),e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()}export function insertAtCursor(e,t){const p=e.selectionStart,i=e.value;e.value=i.substring(0,p)+t+i.substring(p),e.selectionStart=e.selectionEnd=p+t.length,e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()}export function insertLine(e,t){const p=e.selectionStart,i=e.value,c=i.lastIndexOf(`
2
+ `,p-1)+1,n=i.indexOf(`
3
+ `,c),r=i.substring(c,n===-1?i.length:n);if(r.startsWith(t)){const l=n===-1?"":i.substring(n);e.value=i.substring(0,c)+r.substring(t.length)+l,e.selectionStart=e.selectionEnd=Math.max(c,p-t.length)}else e.value=i.substring(0,c)+t+i.substring(c),e.selectionStart=e.selectionEnd=p+t.length;e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()}const N=[{category:"Entrance"},{label:"Reveal (fade)",snippet:e=>`[reveal animation="fade"]
4
4
  ${e||"Content"}
5
5
  [/reveal]`},{label:"Reveal (slide up)",snippet:e=>`[reveal animation="slide-up"]
6
6
  ${e||"Content"}
@@ -29,10 +29,10 @@ ${e||"Content"}
29
29
  [/firework]`},{label:"Fireworks show",snippet:()=>`[fireworks]
30
30
  [firework type="burst" colour="rainbow" /]
31
31
  [firework type="sparkle" colour="primary" /]
32
- [/fireworks]`,insert:!0},{label:"Celebrate",snippet:()=>'[celebrate theme="auto" intensity="medium" /]',insert:!0}];function B(e,t){const d=document.querySelector(".editor-effects-dropdown-menu");if(d){d.remove();return}const o=document.createElement("div");o.className="editor-effects-dropdown-menu",N.forEach(function(r){if(r.category){const u=document.createElement("div");u.className="editor-effects-category",u.textContent=r.category,o.appendChild(u)}else{const u=document.createElement("button");u.type="button",u.className="editor-effects-item",u.textContent=r.label,u.addEventListener("click",function(){if(o.remove(),r.insert)insertAtCursor(e,r.snippet(""));else{const p=e.selectionStart,m=e.selectionEnd,i=e.value.substring(p,m),s=r.snippet(i);e.value=e.value.substring(0,p)+s+e.value.substring(m),e.selectionStart=p,e.selectionEnd=p+s.length,e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()}}),o.appendChild(u)}});const l=t.getBoundingClientRect(),n=t.closest(".editor-toolbar").getBoundingClientRect();o.style.top=l.bottom-n.top+4+"px",o.style.left=l.left-n.left+"px",t.closest(".editor-toolbar").appendChild(o);function c(r){!o.contains(r.target)&&r.target!==t&&(o.remove(),document.removeEventListener("click",c,!0))}setTimeout(function(){document.addEventListener("click",c,!0)},0)}const L=["activity","airpods","airport","alert-circle","alert-warning","ambulance","angry","annoyed","apartment","arch","archive","arrow-down","arrow-down-left","arrow-down-right","arrow-left","arrow-right","arrow-up","arrow-up-left","arrow-up-right","astonished","at-sign","attachment","award","badge","ban","bandage","bank","banknote","barbell","barcode","barn","baseball","basketball","battery","battery-charging","bauble","bell","bell-off","bells","bicycle","bitcoin","blood-drop","blush","bold","bone","book","book-closed","book-open","bookmark","bookmark-filled","bowling","box","boxing-glove","brain","bridge","briefcase","building","bus","cabin","cake","calculator","calendar","camera","camera-off","candy-cane","capsule","car","cart","cart-add","cash","cast","castle","chart-area","chart-bar","chart-bar-horizontal","chart-candlestick","chart-line","chart-pie","chat","chat-alt","check","check-circle","check-circle-filled","check-square","chevron-down","chevron-left","chevron-right","chevron-up","chevrons-down","chevrons-left","chevrons-right","chevrons-up","church","circle","circle-filled","clear-format","clipboard","clipboard-check","clipboard-list","clock","close","cloud","cloud-download","cloud-drizzle","cloud-lightning","cloud-off","cloud-rain","cloud-sun","cloud-upload","code","code-block","code-inline","cog","coins","columns","comment","compass","component","confused","cool","copy","corner-down-left","corner-down-right","corner-up-left","corner-up-right","court","cpu","credit-card","crown","crutch","cry","currency-dollar","currency-euro","currency-pound","cycling","database","dead","desktop","disappointed","dna","document","document-add","document-check","document-remove","document-text","dollar-sign","dome","dot","dots-horizontal","dots-vertical","download","droplet","droplets","dumbbell","edit","embed","emoji-happy","emoji-sad","euro-sign","exclamation","expand","expressionless","external-link","eye","eye-medical","eye-off","factory","fast-forward","feather","file-code","file-text","file-zip","fill","film","filter","fire","first-aid","fishing","flag","fog","folder","folder-add","folder-minus","folder-open","folder-plus","folder-remove","football","fortress","fuel","gamepad","garage","gauge","gift","git-branch","github","globe","golf","grid","grin","growth","hard-drive","hash","haze","heading-1","heading-2","heading-3","headphones","heart","heart-eyes","heart-filled","heart-pulse","heartbeat","help-circle","holly","home","hospital","hotel","house","image","image-add","images","inbox","indent","info","info-filled","investment","invoice","italic","joy","joystick","key","kettlebell","keyboard","laptop","laugh","layers","layout","library","lighthouse","lightning","link","link-2","link-add","linkedin","list","list-bullet","list-numbered","loader","loading","lock","log-in","log-out","lungs","mail","mail-open","map","map-pin","mask","maximize","medal","medical-cross","menu","menu-alt","message-circle","message-square","mic","mic-off","microscope","minimize","minus","minus-circle","minus-square","monitor","monument","moon","moon-star","more-horizontal","more-vertical","mosque","motorcycle","mountain","mouse","mouse-pointer","move","museum","music","nerd","neutral","notification","obelisk","office","outdent","package","palette","panel-bottom","panel-left","panel-right","panel-top","paperclip","parking","pause","pause-filled","percent","phone","phone-call","phone-incoming","phone-off","phone-outgoing","piggy-bank","pill","pill-bottle","pin","play","play-circle","play-filled","plug","plus","plus-circle","plus-square","podium","pound-sign","printer","pulse","pyramid","qrcode","question","quote","racket","radio","rage","rain","rainbow","receipt","redo","refresh","refresh-cw","reindeer","rewind","road","rotate-ccw","rotate-cw","router","rss","running","sad","safe","save","scale","scale-weight","school","search","send","server","settings","share","share-2","share-alt","shield","shield-alert","shield-check","shield-x","shipping","shocked","shopping-bag","shrink","sick","sidebar-left","sidebar-right","silly","skate","ski","skip-back","skip-forward","skyscraper","sleepy","sleigh","sliders","smartphone","smartwatch","smile","snow","snowflake","snowman","sob","soccer","sort","sparkles","speaker","spinner","square","stadium","star","star-decoration","star-eyes","star-filled","stethoscope","stop","stop-filled","stopwatch","store","strikethrough","sun","sunrise","sunset","surfboard","surprised","swimming","sync","syringe","tablet","tablet-smartphone","tag","tags","target","taxi","temple","tennis","tent","terminal","test-tube","text-center","text-left","text-right","thermometer","thermometer-medical","thermometer-sun","thinking","thumb-down","thumb-up","tongue","tool","tooth","tower","train-station","trash","tree","trending-down","trending-neutral","trending-up","trophy","truck","tv","tv-minimal","twitter","type","umbrella","underline","undo","university","unlink","unlock","upload","usb","user","user-add","user-check","user-group","user-plus","user-remove","users","vaccine","van","vault","video","video-off","volleyball","volume","volume-down","volume-mute","volume-off","volume-up","wallet","warehouse","warning","warning-filled","watch","webcam","wheelchair","whistle","wifi","wifi-off","wind","windmill","wink","wreath","x","x-circle","x-circle-filled","yoga","youtube","zany","zap"];function H(e,t,d){const o=document.querySelector(".editor-spacer-picker");if(o){o.remove();return}const l=document.createElement("div");l.className="editor-spacer-picker";const n=document.createElement("div");n.className="editor-spacer-picker-label",n.textContent="Spacer height",l.appendChild(n);const c=document.createElement("div");c.className="editor-spacer-picker-row";const r=document.createElement("input");r.type="range",r.className="editor-spacer-slider",r.min="4",r.max="200",r.step="4",r.value=d;const u=document.createElement("span");u.className="editor-spacer-slider-value",u.textContent=d+"px",r.addEventListener("input",function(){u.textContent=this.value+"px"}),c.appendChild(r),c.appendChild(u),l.appendChild(c);const p=document.createElement("button");p.type="button",p.className="btn btn-primary btn-sm editor-spacer-insert-btn",p.textContent="Insert",l.appendChild(p);function m(h){!l.contains(h.target)&&h.target!==t&&(l.remove(),document.removeEventListener("click",m,!0))}p.addEventListener("click",function(){l.remove(),document.removeEventListener("click",m,!0),insertAtCursor(e,`[spacer size="${r.value}" /]
33
- `)});const i=t.getBoundingClientRect(),s=t.closest(".editor-toolbar").getBoundingClientRect();l.style.top=i.bottom-s.top+4+"px";const a=i.left-s.left;l.style.left=Math.min(a,s.width-240-8)+"px",t.closest(".editor-toolbar").appendChild(l),setTimeout(function(){document.addEventListener("click",m,!0)},0)}function q(e,t){const d=document.querySelector(".editor-icon-picker");if(d){d.remove();return}const o=document.createElement("div");o.className="editor-icon-picker";const l=document.createElement("input");l.type="text",l.className="form-input editor-icon-picker-search",l.placeholder="Search icons\u2026",o.appendChild(l);const n=document.createElement("div");n.className="editor-icon-picker-size-row";const c=document.createElement("label");c.textContent="Size (px)";const r=document.createElement("input");r.type="number",r.className="form-input editor-icon-picker-size",r.placeholder="default",r.min="8",r.max="256",n.appendChild(c),n.appendChild(r),o.appendChild(n);const u=document.createElement("div");u.className="editor-icon-picker-grid",o.appendChild(u);function p(g){!o.contains(g.target)&&g.target!==t&&(o.remove(),document.removeEventListener("click",p,!0))}function m(g){u.textContent="";const v=g?L.filter(f=>f.includes(g.toLowerCase())):L;if(v.length===0){const f=document.createElement("div");f.className="editor-icon-picker-empty",f.textContent="No icons found",u.appendChild(f);return}v.forEach(function(f){const k=document.createElement("button");k.type="button",k.className="editor-icon-picker-item";const w=document.createElement("span");w.setAttribute("data-icon",f);const y=document.createElement("span");y.textContent=f,k.appendChild(w),k.appendChild(y),k.addEventListener("click",function(){o.remove(),document.removeEventListener("click",p,!0);const C=r.value.trim(),M=C?`[icon name="${f}" size="${C}" /]`:`[icon name="${f}" /]`;insertAtCursor(e,M)}),u.appendChild(k)}),Domma.icons.scan(u)}m(""),l.addEventListener("input",function(){m(this.value.trim())});const i=t.getBoundingClientRect(),s=t.closest(".editor-toolbar").getBoundingClientRect();o.style.top=i.bottom-s.top+4+"px";const a=i.left-s.left,h=320,b=s.width;o.style.left=Math.min(a,b-h-8)+"px",t.closest(".editor-toolbar").appendChild(o),requestAnimationFrame(function(){l.focus()}),setTimeout(function(){document.addEventListener("click",p,!0)},0)}function A(e,t,d,o){const l=document.querySelector(".editor-toolbar-dropdown");if(l){l.remove();return}const n=document.createElement("div");n.className="editor-toolbar-dropdown",d.forEach(function(m){const i=document.createElement("button");if(i.type="button",i.className="editor-toolbar-dropdown-item",m.icon){const a=document.createElement("span");a.setAttribute("data-icon",m.icon),i.appendChild(a)}const s=document.createElement("span");s.textContent=m.label,i.appendChild(s),i.addEventListener("click",function(){n.remove(),document.removeEventListener("click",p,!0),x(m.action,e,t,o)}),n.appendChild(i)});const c=t.getBoundingClientRect(),r=t.closest(".editor-toolbar").getBoundingClientRect();n.style.top=c.bottom-r.top+4+"px";const u=c.left-r.left;n.style.left=Math.min(u,r.width-180-8)+"px",t.closest(".editor-toolbar").appendChild(n),Domma.icons.scan(n);function p(m){!n.contains(m.target)&&m.target!==t&&(n.remove(),document.removeEventListener("click",p,!0))}setTimeout(function(){document.addEventListener("click",p,!0)},0)}function x(e,t,d,o){const{spacerDefault:l,handlers:n}=o;if(typeof e=="function")e(t);else if(e==="link")n.link&&n.link(t);else if(e==="image")n.image&&n.image(t);else if(e==="card")if(n.card)n.card(t);else{const c=t.selectionStart,r=t.selectionEnd,p=`[card title="Card Title"]
34
- ${t.value.substring(c,r)||"Content here"}
35
- [/card]`;t.value=t.value.substring(0,c)+p+t.value.substring(r),t.selectionStart=c+13,t.selectionEnd=c+23,t.dispatchEvent(new Event("input",{bubbles:!0})),t.focus()}else if(e==="grid")if(n.grid)n.grid(t);else{const c=t.selectionStart,r=`[grid cols="2" gap="4"]
32
+ [/fireworks]`,insert:!0},{label:"Celebrate",snippet:()=>'[celebrate theme="auto" intensity="medium" /]',insert:!0}];function B(e,t){const p=document.querySelector(".editor-effects-dropdown-menu");if(p){p.remove();return}const i=document.createElement("div");i.className="editor-effects-dropdown-menu",N.forEach(function(l){if(l.category){const d=document.createElement("div");d.className="editor-effects-category",d.textContent=l.category,i.appendChild(d)}else{const d=document.createElement("button");d.type="button",d.className="editor-effects-item",d.textContent=l.label,d.addEventListener("click",function(){if(i.remove(),l.insert)insertAtCursor(e,l.snippet(""));else{const a=e.selectionStart,m=e.selectionEnd,o=e.value.substring(a,m),s=l.snippet(o);e.value=e.value.substring(0,a)+s+e.value.substring(m),e.selectionStart=a,e.selectionEnd=a+s.length,e.dispatchEvent(new Event("input",{bubbles:!0})),e.focus()}}),i.appendChild(d)}});const c=t.getBoundingClientRect(),n=t.closest(".editor-toolbar").getBoundingClientRect();i.style.top=c.bottom-n.top+4+"px",i.style.left=c.left-n.left+"px",t.closest(".editor-toolbar").appendChild(i);function r(l){!i.contains(l.target)&&l.target!==t&&(i.remove(),document.removeEventListener("click",r,!0))}setTimeout(function(){document.addEventListener("click",r,!0)},0)}const L=["activity","airpods","airport","alert-circle","alert-warning","ambulance","angry","annoyed","apartment","arch","archive","arrow-down","arrow-down-left","arrow-down-right","arrow-left","arrow-right","arrow-up","arrow-up-left","arrow-up-right","astonished","at-sign","attachment","award","badge","ban","bandage","bank","banknote","barbell","barcode","barn","baseball","basketball","battery","battery-charging","bauble","bell","bell-off","bells","bicycle","bitcoin","blood-drop","blush","bold","bone","book","book-closed","book-open","bookmark","bookmark-filled","bowling","box","boxing-glove","brain","bridge","briefcase","building","bus","cabin","cake","calculator","calendar","camera","camera-off","candy-cane","capsule","car","cart","cart-add","cash","cast","castle","chart-area","chart-bar","chart-bar-horizontal","chart-candlestick","chart-line","chart-pie","chat","chat-alt","check","check-circle","check-circle-filled","check-square","chevron-down","chevron-left","chevron-right","chevron-up","chevrons-down","chevrons-left","chevrons-right","chevrons-up","church","circle","circle-filled","clear-format","clipboard","clipboard-check","clipboard-list","clock","close","cloud","cloud-download","cloud-drizzle","cloud-lightning","cloud-off","cloud-rain","cloud-sun","cloud-upload","code","code-block","code-inline","cog","coins","columns","comment","compass","component","confused","cool","copy","corner-down-left","corner-down-right","corner-up-left","corner-up-right","court","cpu","credit-card","crown","crutch","cry","currency-dollar","currency-euro","currency-pound","cycling","database","dead","desktop","disappointed","dna","document","document-add","document-check","document-remove","document-text","dollar-sign","dome","dot","dots-horizontal","dots-vertical","download","droplet","droplets","dumbbell","edit","embed","emoji-happy","emoji-sad","euro-sign","exclamation","expand","expressionless","external-link","eye","eye-medical","eye-off","factory","fast-forward","feather","file-code","file-text","file-zip","fill","film","filter","fire","first-aid","fishing","flag","fog","folder","folder-add","folder-minus","folder-open","folder-plus","folder-remove","football","fortress","fuel","gamepad","garage","gauge","gift","git-branch","github","globe","golf","grid","grin","growth","hard-drive","hash","haze","heading-1","heading-2","heading-3","headphones","heart","heart-eyes","heart-filled","heart-pulse","heartbeat","help-circle","holly","home","hospital","hotel","house","image","image-add","images","inbox","indent","info","info-filled","investment","invoice","italic","joy","joystick","key","kettlebell","keyboard","laptop","laugh","layers","layout","library","lighthouse","lightning","link","link-2","link-add","linkedin","list","list-bullet","list-numbered","loader","loading","lock","log-in","log-out","lungs","mail","mail-open","map","map-pin","mask","maximize","medal","medical-cross","menu","menu-alt","message-circle","message-square","mic","mic-off","microscope","minimize","minus","minus-circle","minus-square","monitor","monument","moon","moon-star","more-horizontal","more-vertical","mosque","motorcycle","mountain","mouse","mouse-pointer","move","museum","music","nerd","neutral","notification","obelisk","office","outdent","package","palette","panel-bottom","panel-left","panel-right","panel-top","paperclip","parking","pause","pause-filled","percent","phone","phone-call","phone-incoming","phone-off","phone-outgoing","piggy-bank","pill","pill-bottle","pin","play","play-circle","play-filled","plug","plus","plus-circle","plus-square","podium","pound-sign","printer","pulse","pyramid","qrcode","question","quote","racket","radio","rage","rain","rainbow","receipt","redo","refresh","refresh-cw","reindeer","rewind","road","rotate-ccw","rotate-cw","router","rss","running","sad","safe","save","scale","scale-weight","school","search","send","server","settings","share","share-2","share-alt","shield","shield-alert","shield-check","shield-x","shipping","shocked","shopping-bag","shrink","sick","sidebar-left","sidebar-right","silly","skate","ski","skip-back","skip-forward","skyscraper","sleepy","sleigh","sliders","smartphone","smartwatch","smile","snow","snowflake","snowman","sob","soccer","sort","sparkles","speaker","spinner","square","stadium","star","star-decoration","star-eyes","star-filled","stethoscope","stop","stop-filled","stopwatch","store","strikethrough","sun","sunrise","sunset","surfboard","surprised","swimming","sync","syringe","tablet","tablet-smartphone","tag","tags","target","taxi","temple","tennis","tent","terminal","test-tube","text-center","text-left","text-right","thermometer","thermometer-medical","thermometer-sun","thinking","thumb-down","thumb-up","tongue","tool","tooth","tower","train-station","trash","tree","trending-down","trending-neutral","trending-up","trophy","truck","tv","tv-minimal","twitter","type","umbrella","underline","undo","university","unlink","unlock","upload","usb","user","user-add","user-check","user-group","user-plus","user-remove","users","vaccine","van","vault","video","video-off","volleyball","volume","volume-down","volume-mute","volume-off","volume-up","wallet","warehouse","warning","warning-filled","watch","webcam","wheelchair","whistle","wifi","wifi-off","wind","windmill","wink","wreath","x","x-circle","x-circle-filled","yoga","youtube","zany","zap"];function H(e,t,p){const i=document.querySelector(".editor-spacer-picker");if(i){i.remove();return}const c=document.createElement("div");c.className="editor-spacer-picker";const n=document.createElement("div");n.className="editor-spacer-picker-label",n.textContent="Spacer height",c.appendChild(n);const r=document.createElement("div");r.className="editor-spacer-picker-row";const l=document.createElement("input");l.type="range",l.className="editor-spacer-slider",l.min="4",l.max="200",l.step="4",l.value=p;const d=document.createElement("span");d.className="editor-spacer-slider-value",d.textContent=p+"px",l.addEventListener("input",function(){d.textContent=this.value+"px"}),r.appendChild(l),r.appendChild(d),c.appendChild(r);const a=document.createElement("button");a.type="button",a.className="btn btn-primary btn-sm editor-spacer-insert-btn",a.textContent="Insert",c.appendChild(a);function m(h){!c.contains(h.target)&&h.target!==t&&(c.remove(),document.removeEventListener("click",m,!0))}a.addEventListener("click",function(){c.remove(),document.removeEventListener("click",m,!0),insertAtCursor(e,`[spacer size="${l.value}" /]
33
+ `)});const o=t.getBoundingClientRect(),s=t.closest(".editor-toolbar").getBoundingClientRect();c.style.top=o.bottom-s.top+4+"px";const u=o.left-s.left;c.style.left=Math.min(u,s.width-240-8)+"px",t.closest(".editor-toolbar").appendChild(c),setTimeout(function(){document.addEventListener("click",m,!0)},0)}function q(e,t){const p=document.querySelector(".editor-icon-picker");if(p){p.remove();return}const i=document.createElement("div");i.className="editor-icon-picker";const c=document.createElement("input");c.type="text",c.className="form-input editor-icon-picker-search",c.placeholder="Search icons\u2026",i.appendChild(c);const n=document.createElement("div");n.className="editor-icon-picker-size-row";const r=document.createElement("label");r.textContent="Size (px)";const l=document.createElement("input");l.type="number",l.className="form-input editor-icon-picker-size",l.placeholder="default",l.min="8",l.max="256",n.appendChild(r),n.appendChild(l),i.appendChild(n);const d=document.createElement("div");d.className="editor-icon-picker-grid",i.appendChild(d);function a(g){!i.contains(g.target)&&g.target!==t&&(i.remove(),document.removeEventListener("click",a,!0))}function m(g){d.textContent="";const k=g?L.filter(f=>f.includes(g.toLowerCase())):L;if(k.length===0){const f=document.createElement("div");f.className="editor-icon-picker-empty",f.textContent="No icons found",d.appendChild(f);return}k.forEach(function(f){const v=document.createElement("button");v.type="button",v.className="editor-icon-picker-item";const w=document.createElement("span");w.setAttribute("data-icon",f);const y=document.createElement("span");y.textContent=f,v.appendChild(w),v.appendChild(y),v.addEventListener("click",function(){i.remove(),document.removeEventListener("click",a,!0);const C=l.value.trim(),M=C?`[icon name="${f}" size="${C}" /]`:`[icon name="${f}" /]`;insertAtCursor(e,M)}),d.appendChild(v)}),Domma.icons.scan(d)}m(""),c.addEventListener("input",function(){m(this.value.trim())});const o=t.getBoundingClientRect(),s=t.closest(".editor-toolbar").getBoundingClientRect();i.style.top=o.bottom-s.top+4+"px";const u=o.left-s.left,h=320,b=s.width;i.style.left=Math.min(u,b-h-8)+"px",t.closest(".editor-toolbar").appendChild(i),requestAnimationFrame(function(){c.focus()}),setTimeout(function(){document.addEventListener("click",a,!0)},0)}function A(e,t,p,i){const c=document.querySelector(".editor-toolbar-dropdown");if(c){c.remove();return}const n=document.createElement("div");n.className="editor-toolbar-dropdown",p.forEach(function(m){const o=document.createElement("button");if(o.type="button",o.className="editor-toolbar-dropdown-item",m.icon){const u=document.createElement("span");u.setAttribute("data-icon",m.icon),o.appendChild(u)}const s=document.createElement("span");s.textContent=m.label,o.appendChild(s),o.addEventListener("click",function(){n.remove(),document.removeEventListener("click",a,!0),x(m.action,e,t,i)}),n.appendChild(o)});const r=t.getBoundingClientRect(),l=t.closest(".editor-toolbar").getBoundingClientRect();n.style.top=r.bottom-l.top+4+"px";const d=r.left-l.left;n.style.left=Math.min(d,l.width-180-8)+"px",t.closest(".editor-toolbar").appendChild(n),Domma.icons.scan(n);function a(m){!n.contains(m.target)&&m.target!==t&&(n.remove(),document.removeEventListener("click",a,!0))}setTimeout(function(){document.addEventListener("click",a,!0)},0)}function x(e,t,p,i){const{spacerDefault:c,handlers:n}=i;if(typeof e=="function")e(t);else if(e==="link")n.link&&n.link(t);else if(e==="image")n.image&&n.image(t);else if(e==="card")if(n.card)n.card(t);else{const r=t.selectionStart,l=t.selectionEnd,a=`[card title="Card Title"]
34
+ ${t.value.substring(r,l)||"Content here"}
35
+ [/card]`;t.value=t.value.substring(0,r)+a+t.value.substring(l),t.selectionStart=r+13,t.selectionEnd=r+23,t.dispatchEvent(new Event("input",{bubbles:!0})),t.focus()}else if(e==="grid")if(n.grid)n.grid(t);else{const r=t.selectionStart,l=`[grid cols="2" gap="4"]
36
36
  [col]
37
37
  Column 1
38
38
  [/col]
@@ -40,7 +40,15 @@ Column 1
40
40
  Column 2
41
41
  [/col]
42
42
  [/grid]
43
- `;t.value=t.value.substring(0,c)+r+t.value.substring(c),t.selectionStart=t.selectionEnd=c+r.length,t.dispatchEvent(new Event("input",{bubbles:!0})),t.focus()}else e==="effects"?n.effects?n.effects(t):B(t,d):e==="spacerpick"?H(t,d,l):e==="iconpick"?q(t,d):e==="help"&&n.help&&n.help(t)}const S=[{icon:"bold",title:"Bold (Ctrl+B)",action:e=>wrapSelection(e,"**","**")},{icon:"italic",title:"Italic (Ctrl+I)",action:e=>wrapSelection(e,"_","_")},{icon:"strikethrough",title:"Strikethrough",action:e=>wrapSelection(e,"~~","~~")},"|",{type:"dropdown",icon:"heading-1",title:"Headings",items:[{label:"Heading 1",icon:"heading-1",action:e=>insertLine(e,"# ")},{label:"Heading 2",icon:"heading-2",action:e=>insertLine(e,"## ")},{label:"Heading 3",icon:"heading-3",action:e=>insertLine(e,"### ")}]},"|",{icon:"list-bullet",title:"Bullet list",action:e=>insertLine(e,"- ")},{icon:"list-numbered",title:"Numbered list",action:e=>insertLine(e,"1. ")},"|",{type:"dropdown",icon:"quote",title:"Paragraph",items:[{label:"Blockquote",icon:"quote",action:e=>insertLine(e,"> ")},{label:"Horizontal rule",icon:"minus-circle",action:e=>insertAtCursor(e,`
43
+ `;t.value=t.value.substring(0,r)+l+t.value.substring(r),t.selectionStart=t.selectionEnd=r+l.length,t.dispatchEvent(new Event("input",{bubbles:!0})),t.focus()}else if(e==="badge"){const r=t.selectionStart,l=t.selectionEnd,a=`[badge variant="primary"]${t.value.substring(r,l)||"New"}[/badge]`;t.value=t.value.substring(0,r)+a+t.value.substring(l),t.selectionStart=r+16,t.selectionEnd=r+23,t.dispatchEvent(new Event("input",{bubbles:!0})),t.focus()}else if(e==="timeline"){const r=t.selectionStart,l=`[timeline layout="vertical"]
44
+ [event title="First Event" date="${new Date().toISOString().slice(0,10)}" status="completed"]
45
+ Describe this event.
46
+ [/event]
47
+ [event title="Second Event" date="" status="planned"]
48
+ Describe this event.
49
+ [/event]
50
+ [/timeline]
51
+ `;t.value=t.value.substring(0,r)+l+t.value.substring(r),t.selectionStart=t.selectionEnd=r+l.length,t.dispatchEvent(new Event("input",{bubbles:!0})),t.focus()}else e==="effects"?n.effects?n.effects(t):B(t,p):e==="spacerpick"?H(t,p,c):e==="iconpick"?q(t,p):e==="collection"?n.collection?n.collection(t):insertAtCursor(t,'[collection slug="" display="table" /]'):e==="view"?n.view&&n.view(t):e==="cta"?n.cta&&n.cta(t):e==="form"?n.form?n.form(t):insertAtCursor(t,'[form slug="" /]'):e==="help"&&n.help&&n.help(t)}const S=[{icon:"bold",title:"Bold (Ctrl+B)",action:e=>wrapSelection(e,"**","**")},{icon:"italic",title:"Italic (Ctrl+I)",action:e=>wrapSelection(e,"_","_")},{icon:"strikethrough",title:"Strikethrough",action:e=>wrapSelection(e,"~~","~~")},"|",{type:"dropdown",icon:"heading-1",title:"Headings",items:[{label:"Heading 1",icon:"heading-1",action:e=>insertLine(e,"# ")},{label:"Heading 2",icon:"heading-2",action:e=>insertLine(e,"## ")},{label:"Heading 3",icon:"heading-3",action:e=>insertLine(e,"### ")}]},"|",{icon:"list-bullet",title:"Bullet list",action:e=>insertLine(e,"- ")},{icon:"list-numbered",title:"Numbered list",action:e=>insertLine(e,"1. ")},"|",{type:"dropdown",icon:"quote",title:"Paragraph",items:[{label:"Blockquote",icon:"quote",action:e=>insertLine(e,"> ")},{label:"Horizontal rule",icon:"minus-circle",action:e=>insertAtCursor(e,`
44
52
  ---
45
- `)}]},"|",{icon:"link-2",title:"Link (Ctrl+K)",action:"link"},{icon:"image-add",title:"Image",action:"image"},"|",{type:"dropdown",icon:"code-inline",title:"Code",items:[{label:"Inline code",icon:"code-inline",action:e=>wrapSelection(e,"`","`")},{label:"Code block",icon:"code-block",action:e=>wrapSelection(e,"\n```\n","\n```\n")}]},"|",{type:"dropdown",icon:"plus-circle",title:"Insert",items:[{label:"Card",icon:"card",action:"card"},{label:"Grid",icon:"columns",action:"grid"},{label:"Spacer",icon:"spacer-insert",action:"spacerpick"},{label:"Icon",icon:"icon-pick",action:"iconpick"}]},"|",{icon:"sparkles",title:"Effects",action:"effects"},{icon:"help-circle",title:"Editor help",action:"help"}];export function createToolbar(e,t,d={}){z();const o={link:null,image:null,card:null,grid:null,help:null,effects:null},l=d.spacerDefault??40,n=e.get(0),c=t.get(0);c.textContent="",S.forEach((i,s)=>{if(i==="|"){const a=document.createElement("span");a.className="editor-toolbar-sep",c.appendChild(a)}else{const a=document.createElement("button");a.className=i.type==="dropdown"?"editor-toolbar-btn editor-toolbar-dropdown-trigger":"editor-toolbar-btn",a.setAttribute("data-tooltip",i.title),a.setAttribute("data-idx",String(s)),a.type="button";const h=document.createElement("span");if(h.setAttribute("data-icon",i.icon),a.appendChild(h),i.type==="dropdown"){const b=document.createElement("span");b.className="editor-toolbar-caret",b.textContent="\u25BE",a.appendChild(b)}c.appendChild(a)}});const r=document.createElement("div");r.className="editor-toolbar-right",[{mode:"split",icon:"columns",label:"Split view",active:!0},{mode:"write",icon:"file-text",label:"Write only",active:!1},{mode:"preview",icon:"eye",label:"Preview only",active:!1}].forEach(i=>{const s=document.createElement("button");s.className="editor-view-btn"+(i.active?" active":""),s.setAttribute("data-mode",i.mode),s.setAttribute("data-tooltip",i.label),s.type="button";const a=document.createElement("span");a.setAttribute("data-icon",i.icon),s.appendChild(a),r.appendChild(s)});const u=document.createElement("span");u.className="editor-toolbar-sep",r.appendChild(u);const p=document.createElement("button");p.id="fullscreen-btn",p.className="editor-toolbar-btn",p.setAttribute("data-tooltip","Toggle fullscreen"),p.type="button";const m=document.createElement("span");return m.setAttribute("data-icon","expand"),p.appendChild(m),r.appendChild(p),c.appendChild(r),Domma.icons.scan(),c.querySelectorAll("[data-tooltip]").forEach(i=>{E.tooltip(i,{content:i.getAttribute("data-tooltip"),position:"top"})}),t.on("click",".editor-toolbar-btn[data-idx]",function(){const i=parseInt($(this).data("idx"),10),s=S[i];if(!s||s==="|")return;const a=t.get(0).querySelector(`[data-idx="${i}"]`),h={spacerDefault:l,handlers:o};s.type==="dropdown"?A(n,a,s.items,h):x(s.action,n,a,h)}),e.on("keydown",function(i){const s=i.key.toLowerCase();if(i.ctrlKey||i.metaKey)s==="b"?(i.preventDefault(),wrapSelection(n,"**","**")):s==="i"?(i.preventDefault(),wrapSelection(n,"_","_")):s==="k"&&(i.preventDefault(),o.link&&o.link(n));else if(i.key==="Tab")if(i.preventDefault(),i.shiftKey){const a=n.value.lastIndexOf(`
46
- `,n.selectionStart-1)+1,h=n.value.substring(a),b=h.match(/^ {1,4}/);if(b){const g=n.value.substring(0,a),v=n.value.substring(a+b[0].length);n.value=g+h.substring(b[0].length),n.selectionStart=n.selectionEnd=Math.max(a,n.selectionStart-b[0].length),n.dispatchEvent(new Event("input",{bubbles:!0}))}}else insertAtCursor(n," ")}),{$toolbar:t,onLink(i){o.link=i},onImage(i){o.image=i},onCard(i){o.card=i},onGrid(i){o.grid=i},onHelp(i){o.help=i},onEffects(i){o.effects=i}}}
53
+ `)}]},"|",{icon:"link-2",title:"Link (Ctrl+K)",action:"link"},{icon:"image-add",title:"Image",action:"image"},"|",{type:"dropdown",icon:"code-inline",title:"Code",items:[{label:"Inline code",icon:"code-inline",action:e=>wrapSelection(e,"`","`")},{label:"Code block",icon:"code-block",action:e=>wrapSelection(e,"\n```\n","\n```\n")}]},"|",{type:"dropdown",icon:"plus-circle",title:"Insert",items:[{label:"Card",icon:"card",action:"card"},{label:"Grid",icon:"columns",action:"grid"},{label:"Badge",icon:"badge",action:"badge"},{label:"Timeline",icon:"activity",action:"timeline"},{label:"Spacer",icon:"spacer-insert",action:"spacerpick"},{label:"Icon",icon:"icon-pick",action:"iconpick"},{label:"Collection",icon:"database",action:"collection"},{label:"View",icon:"eye",action:"view"},{label:"CTA Button",icon:"mouse-pointer",action:"cta"},{label:"Form",icon:"file-text",action:"form"}]},"|",{icon:"sparkles",title:"Effects",action:"effects"},{icon:"help-circle",title:"Editor help",action:"help"}];export function createToolbar(e,t,p={}){z();const i={link:null,image:null,card:null,grid:null,help:null,effects:null,collection:null,view:null,cta:null,form:null},c=p.spacerDefault??40,n=e.get(0),r=t.get(0);r.textContent="",S.forEach((o,s)=>{if(o==="|"){const u=document.createElement("span");u.className="editor-toolbar-sep",r.appendChild(u)}else{const u=document.createElement("button");u.className=o.type==="dropdown"?"editor-toolbar-btn editor-toolbar-dropdown-trigger":"editor-toolbar-btn",u.setAttribute("data-tooltip",o.title),u.setAttribute("data-idx",String(s)),u.type="button";const h=document.createElement("span");if(h.setAttribute("data-icon",o.icon),u.appendChild(h),o.type==="dropdown"){const b=document.createElement("span");b.className="editor-toolbar-caret",b.textContent="\u25BE",u.appendChild(b)}r.appendChild(u)}});const l=document.createElement("div");l.className="editor-toolbar-right",[{mode:"split",icon:"columns",label:"Split view",active:!0},{mode:"write",icon:"file-text",label:"Write only",active:!1},{mode:"preview",icon:"eye",label:"Preview only",active:!1}].forEach(o=>{const s=document.createElement("button");s.className="editor-view-btn"+(o.active?" active":""),s.setAttribute("data-mode",o.mode),s.setAttribute("data-tooltip",o.label),s.type="button";const u=document.createElement("span");u.setAttribute("data-icon",o.icon),s.appendChild(u),l.appendChild(s)});const d=document.createElement("span");d.className="editor-toolbar-sep",l.appendChild(d);const a=document.createElement("button");a.id="fullscreen-btn",a.className="editor-toolbar-btn",a.setAttribute("data-tooltip","Toggle fullscreen"),a.type="button";const m=document.createElement("span");return m.setAttribute("data-icon","expand"),a.appendChild(m),l.appendChild(a),r.appendChild(l),Domma.icons.scan(),r.querySelectorAll("[data-tooltip]").forEach(o=>{E.tooltip(o,{content:o.getAttribute("data-tooltip"),position:"top"})}),t.on("click",".editor-toolbar-btn[data-idx]",function(){const o=parseInt($(this).data("idx"),10),s=S[o];if(!s||s==="|")return;const u=t.get(0).querySelector(`[data-idx="${o}"]`),h={spacerDefault:c,handlers:i};s.type==="dropdown"?A(n,u,s.items,h):x(s.action,n,u,h)}),e.on("keydown",function(o){const s=o.key.toLowerCase();if(o.ctrlKey||o.metaKey)s==="b"?(o.preventDefault(),wrapSelection(n,"**","**")):s==="i"?(o.preventDefault(),wrapSelection(n,"_","_")):s==="k"&&(o.preventDefault(),i.link&&i.link(n));else if(o.key==="Tab")if(o.preventDefault(),o.shiftKey){const u=n.value.lastIndexOf(`
54
+ `,n.selectionStart-1)+1,h=n.value.substring(u),b=h.match(/^ {1,4}/);if(b){const g=n.value.substring(0,u),k=n.value.substring(u+b[0].length);n.value=g+h.substring(b[0].length),n.selectionStart=n.selectionEnd=Math.max(u,n.selectionStart-b[0].length),n.dispatchEvent(new Event("input",{bubbles:!0}))}}else insertAtCursor(n," ")}),{$toolbar:t,onLink(o){i.link=o},onImage(o){i.image=o},onCard(o){i.card=o},onGrid(o){i.grid=o},onHelp(o){i.help=o},onEffects(o){i.effects=o},onCollection(o){i.collection=o},onView(o){i.view=o},onCta(o){i.cta=o},onForm(o){i.form=o}}}
@@ -0,0 +1,171 @@
1
+ <div class="view-header">
2
+ <h1><span data-icon="zap"></span> <span id="action-editor-title">New Action</span></h1>
3
+ <div style="display:flex;gap:.5rem;">
4
+ <a href="#/actions" class="btn btn-ghost btn-sm">
5
+ <span data-icon="arrow-left"></span> All Actions
6
+ </a>
7
+ <button id="save-action-btn" class="btn btn-primary">
8
+ <span data-icon="save"></span> Save Action
9
+ </button>
10
+ </div>
11
+ </div>
12
+
13
+ <div class="tabs" id="action-editor-tabs">
14
+ <div class="tab-list">
15
+ <button class="tab-item active">General</button>
16
+ <button class="tab-item">Trigger</button>
17
+ <button class="tab-item">Steps</button>
18
+ <button class="tab-item">Access</button>
19
+ </div>
20
+ <div class="tab-content">
21
+
22
+ <!-- General tab -->
23
+ <div class="tab-panel active">
24
+ <div class="card">
25
+ <div class="card-body" style="display:flex;flex-direction:column;gap:1rem;max-width:600px;">
26
+ <div>
27
+ <label class="form-label">Title <span style="color:var(--dm-danger,#f87171);">*</span></label>
28
+ <input id="action-title" type="text" class="form-input" placeholder="e.g. Approve Application">
29
+ </div>
30
+ <div>
31
+ <label class="form-label">Slug</label>
32
+ <input id="action-slug" type="text" class="form-input" placeholder="Auto-generated from title">
33
+ <small class="text-muted">URL-safe identifier. Leave blank to auto-generate.</small>
34
+ </div>
35
+ <div>
36
+ <label class="form-label">Description</label>
37
+ <textarea id="action-description" class="form-input" rows="2" placeholder="Describe what this action does…"></textarea>
38
+ </div>
39
+ <div>
40
+ <label class="form-label">Target Collection <span style="color:var(--dm-danger,#f87171);">*</span></label>
41
+ <select id="action-collection" class="form-input">
42
+ <option value="">— select collection —</option>
43
+ </select>
44
+ <small class="text-muted">The collection this action operates on.</small>
45
+ </div>
46
+ </div>
47
+ </div>
48
+ </div>
49
+
50
+ <!-- Trigger tab -->
51
+ <div class="tab-panel">
52
+ <div class="card">
53
+ <div class="card-body" style="display:flex;flex-direction:column;gap:1rem;max-width:500px;">
54
+ <div>
55
+ <label class="form-label">Trigger Type</label>
56
+ <select id="action-trigger-type" class="form-input">
57
+ <option value="manual">Manual (button in entry list)</option>
58
+ </select>
59
+ </div>
60
+ <div>
61
+ <label class="form-label">Button Label</label>
62
+ <input id="action-trigger-label" type="text" class="form-input" value="Run" placeholder="e.g. Approve">
63
+ </div>
64
+ <div>
65
+ <label class="form-label">Button Icon</label>
66
+ <input id="action-trigger-icon" type="text" class="form-input" value="zap" placeholder="e.g. check-circle, zap, send">
67
+ <small class="text-muted">Domma icon name.</small>
68
+ </div>
69
+ <div>
70
+ <label class="form-label">Confirmation Message</label>
71
+ <input id="action-trigger-confirm" type="text" class="form-input"
72
+ placeholder="e.g. Are you sure you want to run this action?">
73
+ <small class="text-muted">Leave blank to skip the confirmation prompt.</small>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </div>
78
+
79
+ <!-- Steps tab -->
80
+ <div class="tab-panel">
81
+ <div class="card mb-3">
82
+ <div class="card-header" style="display:flex;justify-content:space-between;align-items:center;">
83
+ <h2>Steps</h2>
84
+ <div style="display:flex;gap:.5rem;">
85
+ <select id="add-step-type" class="form-input form-input--sm">
86
+ <option value="updateField">Update Field</option>
87
+ <option value="deleteEntry">Delete Entry</option>
88
+ <option value="moveToCollection">Move to Collection</option>
89
+ <option value="webhook">Webhook</option>
90
+ <option value="email">Email</option>
91
+ </select>
92
+ <button id="add-step-btn" class="btn btn-ghost btn-sm">
93
+ <span data-icon="plus"></span> Add Step
94
+ </button>
95
+ </div>
96
+ </div>
97
+ <div class="card-body">
98
+ <div id="action-steps-list">
99
+ <p class="text-muted steps-empty-placeholder" style="text-align:center;padding:2rem 0;">
100
+ No steps yet. Add a step to define what this action does.
101
+ </p>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ <div class="card">
106
+ <div class="card-body" style="font-size:.8rem;color:var(--dm-text-muted,#888);">
107
+ <strong>Template variables:</strong>
108
+ <code>{{entry.data.fieldName}}</code> — entry field value &nbsp;|&nbsp;
109
+ <code>{{now}}</code> — current ISO timestamp &nbsp;|&nbsp;
110
+ <code>{{user.name}}</code> — executing user's name &nbsp;|&nbsp;
111
+ <code>{{env.CMS_PUBLIC_*}}</code> — public environment variables
112
+ </div>
113
+ </div>
114
+ </div>
115
+
116
+ <!-- Access tab -->
117
+ <div class="tab-panel">
118
+ <div class="card mb-3">
119
+ <div class="card-body" style="max-width:500px;">
120
+ <label class="form-label">Allowed Roles</label>
121
+ <small class="text-muted" style="display:block;margin-bottom:.75rem;">
122
+ Select which roles can trigger this action.
123
+ </small>
124
+ <div id="action-roles-checkboxes" style="display:flex;flex-direction:column;gap:.5rem;"></div>
125
+ </div>
126
+ </div>
127
+ <div class="card">
128
+ <div class="card-header"><h2>Row-Level Access</h2></div>
129
+ <div class="card-body" style="max-width:500px;display:flex;flex-direction:column;gap:1rem;">
130
+ <div>
131
+ <label style="display:flex;align-items:center;gap:.5rem;cursor:pointer;">
132
+ <input type="checkbox" id="action-rowlevel-enabled">
133
+ <span>Restrict to specific entries</span>
134
+ </label>
135
+ <small class="text-muted" style="display:block;margin-top:.4rem;">
136
+ When enabled, users can only run this action on entries they own or are assigned to.
137
+ Admins always bypass this restriction.
138
+ </small>
139
+ </div>
140
+ <div id="action-rowlevel-config" style="display:none;flex-direction:column;gap:1rem;">
141
+ <div>
142
+ <label class="form-label">Mode</label>
143
+ <select id="action-rowlevel-mode" class="form-input">
144
+ <option value="owner">Owner — entry was created by the current user</option>
145
+ <option value="field">Field Match — a field on the entry matches the user</option>
146
+ </select>
147
+ </div>
148
+ <div id="action-rowlevel-field-group" style="display:none;">
149
+ <label class="form-label">Field Name</label>
150
+ <input id="action-rowlevel-field" type="text" class="form-input"
151
+ placeholder="e.g. assigned_to">
152
+ <small class="text-muted">The entry field whose value must match the user property
153
+ below.</small>
154
+ </div>
155
+ <div>
156
+ <label class="form-label">User Property</label>
157
+ <select id="action-rowlevel-userkey" class="form-input">
158
+ <option value="id">id (UUID)</option>
159
+ <option value="email">email</option>
160
+ <option value="name">name</option>
161
+ <option value="role">role</option>
162
+ </select>
163
+ <small class="text-muted">Which property of the logged-in user to compare against.</small>
164
+ </div>
165
+ </div>
166
+ </div>
167
+ </div>
168
+ </div>
169
+
170
+ </div>
171
+ </div>
@@ -0,0 +1,19 @@
1
+ <div class="view-header">
2
+ <h1><span data-icon="zap"></span> Actions</h1>
3
+ <button id="create-action-btn" class="btn btn-primary">
4
+ <span data-icon="plus"></span> New Action
5
+ </button>
6
+ </div>
7
+
8
+ <div id="actions-pro-notice" class="card mb-3" style="display:none;">
9
+ <div class="card-body" style="color:var(--dm-warning,#f59e0b);display:flex;align-items:center;gap:.6rem;">
10
+ <span data-icon="alert-triangle"></span>
11
+ <span>Actions require a MongoDB connection (pro mode). Configure one under Collections → Options.</span>
12
+ </div>
13
+ </div>
14
+
15
+ <div class="card">
16
+ <div class="card-body">
17
+ <div id="actions-table"></div>
18
+ </div>
19
+ </div>