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
@@ -0,0 +1,10 @@
1
+ /*!
2
+ * Domma Tools v0.19.3
3
+ * Developer tools: Theme Roller & Page Roller
4
+ * (c) 2026 Darryl Waterhouse & DCBW-IT
5
+ * Built: 2026-03-06T15:12:47.301Z
6
+ * Commit: 17c6e27
7
+ *
8
+ * Requires: domma.min.js
9
+ */
10
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).DommaTools={})}(this,function(e){"use strict";const t="domma-theme",n="domma-theme-variant",a="dm-theme-",i=["ocean-light","ocean-dark","forest-light","forest-dark","sunset-light","sunset-dark","royal-light","royal-dark","lemon-light","lemon-dark","silver-light","silver-dark","charcoal-light","charcoal-dark","christmas-light","christmas-dark","unicorn-light","unicorn-dark","dreamy-light","dreamy-dark","grayve-light","grayve-dark","core-light"],s="charcoal-dark";const o=new class{constructor(){this._theme=s,this._listeners=[],this._target=null,this._initialised=!1,this._systemMediaQuery=null,this._autoDetect=!1,this._disabled=!1}init(e={}){const{theme:t=null,autoDetect:n=!1,persist:a=!0,disabled:o=!1,target:r=document.body}=e;if(o)return this._disabled=!0,this._initialised=!0,this;this._target="string"==typeof r?document.querySelector(r):r,this._target||(this._target=document.body),this._persist=a,this._autoDetect=n;let l=t;if(a){const e=this._loadFromStorage();e&&!t&&(l=e)}if(n&&!l){l="dark"===this._getSystemPreference()?"charcoal-dark":"charcoal-light",this._setupSystemListener()}return l=l||s,i.includes(l)||(console.warn(`Invalid theme: ${l}. Using default: ${s}`),l=s),this._theme=l,this._applyTheme(),this._initialised=!0,this}get(){return this._theme}getBase(){return this._theme.split("-").slice(0,-1).join("-")}getMode(){return this._theme.split("-").pop()}isDark(){return"dark"===this.getMode()}isLight(){return"light"===this.getMode()}set(e){if(!i.includes(e))return console.warn(`Invalid theme: ${e}. Available themes: ${i.join(", ")}`),this;const t=this._theme;return t===e||(this._theme=e,this._applyTheme(),this._saveToStorage(),this._notifyListeners(t,e)),this}setVariant(){return console.warn('[ThemeEngine] setVariant() is deprecated. Use set() with full theme name (e.g., set("ocean-dark"))'),this}toggle(){return console.warn("[ThemeEngine] toggle() is deprecated. Use variant selector UI or set() method."),this}onChange(e){return"function"!=typeof e?(console.warn("onChange requires a function callback"),()=>{}):(this._listeners.push(e),()=>{const t=this._listeners.indexOf(e);t>-1&&this._listeners.splice(t,1)})}preview(e){if(!i.includes(e))return console.warn(`Invalid theme for preview: ${e}`),()=>{};const t=this._theme;return this._theme=e,this._applyTheme(!1),()=>{this._theme=t,this._applyTheme(!1)}}listThemes(){return[...i]}listBases(){return["ocean","forest","sunset","royal","lemon","silver","charcoal","christmas","unicorn","dreamy","grayve"]}listVariants(){return console.warn("[ThemeEngine] listVariants() is deprecated. Use listThemes() instead."),this.listBases()}getConfig(){return{theme:this._theme,base:this.getBase(),mode:this.getMode(),autoDetect:this._autoDetect,persist:this._persist,disabled:this._disabled}}_updateMetaThemeColor(){const e={light:"#ffffff",dark:"#121212"};let t=document.querySelector('meta[name="theme-color"]');t||(t=document.createElement("meta"),t.name="theme-color",document.head.appendChild(t)),t.content=e[this.getMode()]||e.dark}isDisabled(){return this._disabled}enable(){return this._disabled=!1,this._target||(this._target=document.body),this._applyTheme(),this}disable(){if(this._disabled=!0,this._target){const e=this._target.className.split(" ").filter(e=>!e.startsWith(a));this._target.className=e.join(" ").trim(),this._target.removeAttribute("data-mode")}return this}_applyTheme(e=!0){if(this._disabled)return;const t=this._target||document.body;if(!t)return void console.error("[ThemeEngine] No target element available for theme application");const n=t.className.split(" ").filter(e=>!e.startsWith(a));n.push(`${a}${this._theme}`),t.className=n.join(" ").trim();const i=this.isDark()?"dark":"light";t.setAttribute("data-mode",i),e&&this._updateMetaThemeColor()}_saveToStorage(){if(this._persist&&"undefined"!=typeof localStorage)try{localStorage.setItem(t,this._theme),localStorage.removeItem(n)}catch(e){}}_loadFromStorage(){if("undefined"==typeof localStorage)return null;try{const e=localStorage.getItem(t);if(e&&i.includes(e))return e;const a=e,s=localStorage.getItem(n);if(a&&["light","dark"].includes(a)){const e=`${s||"charcoal"}-${a}`;if(console.log(`[ThemeEngine] Migrating theme: ${a} + ${s} → ${e}`),i.includes(e))return localStorage.setItem(t,e),localStorage.removeItem(n),e}return null}catch(e){return null}}_getSystemPreference(){return"undefined"!=typeof window&&window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}_setupSystemListener(){if("undefined"==typeof window||!window.matchMedia)return;this._systemMediaQuery=window.matchMedia("(prefers-color-scheme: dark)");const e=e=>{if(!this._autoDetect)return;const t=e.matches?"dark":"light",n=`${this.getBase()}-${t}`;i.includes(n)&&this.set(n)};this._systemMediaQuery.addEventListener?this._systemMediaQuery.addEventListener("change",e):this._systemMediaQuery.addListener(e)}_notifyListeners(e,t){this._listeners.forEach(n=>{try{n(e,t)}catch(e){console.error("[ThemeEngine] Error in change listener:",e)}})}destroy(){this._systemMediaQuery&&this._systemMediaQuery.removeEventListener,this._listeners=[],this._target=null,this._initialised=!1}};"undefined"!=typeof document&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",()=>{o._initialised||o.init({persist:!0,autoDetect:!1})}):o._initialised||o.init({persist:!0,autoDetect:!1}));const r={_prefix:"domma:",_available:null,isAvailable(){if(null!==this._available)return this._available;try{const e=`${this._prefix}__test__`;localStorage.setItem(e,"test"),localStorage.removeItem(e),this._available=!0}catch(e){this._available=!1}return this._available},_prefixKey(e){return`${this._prefix}${e}`},_unprefixKey(e){return e.slice(this._prefix.length)},get(e,t=null){if(!this.isAvailable())return t;try{const n=this._prefixKey(e),a=localStorage.getItem(n);return null===a?t:JSON.parse(a)}catch(n){return console.warn(`Domma storage: Failed to parse key "${e}"`,n),t}},set(e,t){if(!this.isAvailable())return!1;try{const n=this._prefixKey(e),a=JSON.stringify(t);return localStorage.setItem(n,a),!0}catch(t){return console.warn(`Domma storage: Failed to set key "${e}"`,t),!1}},remove(e){if(!this.isAvailable())return!1;try{const t=this._prefixKey(e);return localStorage.removeItem(t),!0}catch(t){return console.warn(`Domma storage: Failed to remove key "${e}"`,t),!1}},has(e){if(!this.isAvailable())return!1;try{const t=this._prefixKey(e);return null!==localStorage.getItem(t)}catch(e){return!1}},clear(){if(!this.isAvailable())return 0;try{const e=this.keys();return e.forEach(e=>this.remove(e)),e.length}catch(e){return console.warn("Domma storage: Failed to clear storage",e),0}},keys(){if(!this.isAvailable())return[];try{const e=[];for(let t=0;t<localStorage.length;t++){const n=localStorage.key(t);n&&n.startsWith(this._prefix)&&e.push(this._unprefixKey(n))}return e}catch(e){return[]}},size(e){if(!this.isAvailable())return 0;try{const t=this._prefixKey(e),n=localStorage.getItem(t);return n?new Blob([n]).size:0}catch(e){return 0}},totalSize(){if(!this.isAvailable())return 0;try{let e=0;return this.keys().forEach(t=>{e+=this.size(t)}),e}catch(e){return 0}},getAll(){if(!this.isAvailable())return{};const e={};return this.keys().forEach(t=>{e[t]=this.get(t)}),e},setAll(e){if(!this.isAvailable()||!e)return!1;let t=!0;return Object.keys(e).forEach(n=>{this.set(n,e[n])||(t=!1)}),t}},l={chunk(e,t=1){if(!e||t<1)return[];const n=[];for(let a=0;a<e.length;a+=t)n.push(e.slice(a,a+t));return n},compact:e=>e?e.filter(Boolean):[],concat:(e,...t)=>e?e.concat(...t):[],difference(e,...t){if(!e)return[];const n=new Set(t.flat());return e.filter(e=>!n.has(e))},differenceBy(e,t,n){if(!e)return[];const a=new Set(t.map(n));return e.filter(e=>!a.has(n(e)))},differenceWith:(e,t,n)=>e?e.filter(e=>!t.some(t=>n(e,t))):[],drop:(e,t=1)=>e?e.slice(t):[],dropRight(e,t=1){if(!e)return[];const n=e.length-t;return n>0?e.slice(0,n):[]},dropRightWhile(e,t){if(!e)return[];let n=e.length;for(;n--&&t(e[n],n,e););return e.slice(0,n+1)},dropWhile(e,t){if(!e)return[];let n=0;for(;n<e.length&&t(e[n],n,e);)n++;return e.slice(n)},eq:(e,t)=>e===t||e!=e&&t!=t,fill:(e,t,n=0,a)=>e?e.fill(t,n,a):[],findIndex(e,t,n=0){if(!e)return-1;for(let a=n;a<e.length;a++)if(t(e[a],a,e))return a;return-1},findLastIndex(e,t,n){if(!e)return-1;for(let a=void 0!==n?n:e.length-1;a>=0;a--)if(t(e[a],a,e))return a;return-1},first:e=>e?e[0]:void 0,head(e){return this.first(e)},flatten:e=>e?e.flat(1):[],flattenDeep:e=>e?e.flat(1/0):[],flattenDepth:(e,t=1)=>e?e.flat(t):[],fromPairs:e=>e?Object.fromEntries(e):{},indexOf:(e,t,n=0)=>e?e.indexOf(t,n):-1,initial:e=>e?e.slice(0,-1):[],intersection(...e){if(!e.length)return[];return e[0].filter(t=>e.every(e=>e.includes(t)))},join:(e,t=",")=>e?e.join(t):"",last:e=>e?e[e.length-1]:void 0,lastIndexOf:(e,t,n)=>e?void 0!==n?e.lastIndexOf(t,n):e.lastIndexOf(t):-1,nth(e,t=0){if(e)return t<0?e[e.length+t]:e[t]},pull(e,...t){if(!e)return[];const n=new Set(t);for(let t=e.length-1;t>=0;t--)n.has(e[t])&&e.splice(t,1);return e},pullAt(e,...t){if(!e)return[];const n=t.flat().sort((e,t)=>t-e),a=[];return n.forEach(t=>{t>=0&&t<e.length&&a.unshift(e.splice(t,1)[0])}),a},reverse:e=>e?e.reverse():[],slice:(e,t=0,n)=>e?void 0!==n?e.slice(t,n):e.slice(t):[],tail:e=>e?e.slice(1):[],take:(e,t=1)=>e?e.slice(0,t):[],takeRight(e,t=1){if(!e)return[];const n=Math.max(0,e.length-t);return e.slice(n)},union:(...e)=>[...new Set(e.flat())],uniq:e=>e?[...new Set(e)]:[],uniqBy(e,t){if(!e)return[];const n=new Set;return e.filter(e=>{const a=t(e);return!n.has(a)&&(n.add(a),!0)})},without(e,...t){if(!e)return[];const n=new Set(t);return e.filter(e=>!n.has(e))},xor(...e){if(!e.length)return[];const t=new Map;return e.flat().forEach(e=>{t.set(e,(t.get(e)||0)+1)}),[...t.entries()].filter(([,e])=>1===e).map(([e])=>e)},zip(...e){if(!e.length)return[];const t=Math.max(...e.map(e=>e.length)),n=[];for(let a=0;a<t;a++)n.push(e.map(e=>e[a]));return n},zipObject(e,t){if(!e)return{};const n={};return e.forEach((e,a)=>{n[e]=t?t[a]:void 0}),n},times(e,t){if((e=Math.floor(e))<1)return[];const n=new Array(e);for(let a=0;a<e;a++)n[a]=t?t(a):a;return n},range(e,t,n){if(void 0===t&&(t=e,e=0),0===(n=void 0===n?e<t?1:-1:n))return[];const a=Math.max(Math.ceil((t-e)/(n||1)),0),i=new Array(a);for(let t=0;t<a;t++)i[t]=e+t*n;return i},uniqueId:(()=>{let e=0;return function(t=""){const n=++e;return t?`${t}${n}`:String(n)}})(),countBy(e,t){const n={};return this.each(e,e=>{const a=t(e);n[a]=(n[a]||0)+1}),n},each:(e,t)=>(Array.isArray(e)?e.forEach((n,a)=>t(n,a,e)):e&&"object"==typeof e&&Object.keys(e).forEach(n=>t(e[n],n,e)),e),forEach(e,t){return this.each(e,t)},eachRight(e,t){if(Array.isArray(e))for(let n=e.length-1;n>=0;n--)t(e[n],n,e);else if(e&&"object"==typeof e){Object.keys(e).reverse().forEach(n=>t(e[n],n,e))}return e},forEachRight(e,t){return this.eachRight(e,t)},every:(e,t)=>Array.isArray(e)?e.every(t):!e||"object"!=typeof e||Object.values(e).every(t),filter:(e,t)=>Array.isArray(e)?e.filter(t):e&&"object"==typeof e?Object.values(e).filter(t):[],find:(e,t)=>Array.isArray(e)?e.find(t):e&&"object"==typeof e?Object.values(e).find(t):void 0,findLast(e,t){if(Array.isArray(e)){for(let n=e.length-1;n>=0;n--)if(t(e[n],n,e))return e[n]}else if(e&&"object"==typeof e){return Object.values(e).reverse().find(t)}},flatMap(e,t){return this.map(e,t).flat(1)},flatMapDeep(e,t){return this.map(e,t).flat(1/0)},groupBy(e,t){const n={};return this.each(e,e=>{const a="function"==typeof t?t(e):e[t];n[a]||(n[a]=[]),n[a].push(e)}),n},includes:(e,t,n=0)=>"string"==typeof e||Array.isArray(e)?e.includes(t,n):!(!e||"object"!=typeof e)&&Object.values(e).includes(t),keyBy(e,t){const n={};return this.each(e,e=>{const a="function"==typeof t?t(e):e[t];n[a]=e}),n},map:(e,t)=>Array.isArray(e)?e.map(t):e&&"object"==typeof e?Object.entries(e).map(([n,a])=>t(a,n,e)):[],orderBy(e,t,n){const a=Array.isArray(e)?[...e]:Object.values(e),i=Array.isArray(t)?t:[t],s=Array.isArray(n)?n:[n||"asc"];return a.sort((e,t)=>{for(let n=0;n<i.length;n++){const a=i[n],o=s[n]||"asc",r="function"==typeof a?a(e):e[a],l="function"==typeof a?a(t):t[a];if(r<l)return"asc"===o?-1:1;if(r>l)return"asc"===o?1:-1}return 0})},partition(e,t){const n=[],a=[];return this.each(e,(i,s)=>{t(i,s,e)?n.push(i):a.push(i)}),[n,a]},reduce(e,t,n){if(Array.isArray(e))return void 0!==n?e.reduce(t,n):e.reduce(t);if(e&&"object"==typeof e){const a=Object.entries(e);let i=n,s=0;void 0===i&&a.length>0&&(i=a[0][1],s=1);for(let n=s;n<a.length;n++){const[s,o]=a[n];i=t(i,o,s,e)}return i}return n},reduceRight(e,t,n){if(Array.isArray(e))return void 0!==n?e.reduceRight(t,n):e.reduceRight(t);if(e&&"object"==typeof e){const a=Object.entries(e).reverse();let i=n,s=0;void 0===i&&a.length>0&&(i=a[0][1],s=1);for(let n=s;n<a.length;n++){const[s,o]=a[n];i=t(i,o,s,e)}return i}return n},reject(e,t){return this.filter(e,(e,n,a)=>!t(e,n,a))},sample(e){const t=Array.isArray(e)?e:Object.values(e||{});return t[Math.floor(Math.random()*t.length)]},sampleSize(e,t=1){return this.shuffle(Array.isArray(e)?[...e]:Object.values(e||{})).slice(0,t)},shuffle(e){const t=Array.isArray(e)?[...e]:Object.values(e||{});for(let e=t.length-1;e>0;e--){const n=Math.floor(Math.random()*(e+1));[t[e],t[n]]=[t[n],t[e]]}return t},size:e=>null==e?0:"string"==typeof e||Array.isArray(e)?e.length:"object"==typeof e?Object.keys(e).length:0,some:(e,t)=>Array.isArray(e)?e.some(t):!(!e||"object"!=typeof e)&&Object.values(e).some(t),sortBy:(e,t)=>(Array.isArray(e)?[...e]:Object.values(e)).sort((e,n)=>{const a="function"==typeof t?t(e):e[t],i="function"==typeof t?t(n):n[t];return a<i?-1:a>i?1:0}),after(e,t){let n=0;return function(...a){if(++n>=e)return t.apply(this,a)}},ary:(e,t=e.length)=>function(...n){return e.apply(this,n.slice(0,t))},before(e,t){let n,a=0;return function(...i){return++a<e&&(n=t.apply(this,i)),n}},bind:(e,t,...n)=>function(...a){return e.apply(t,[...n,...a])},curry:(e,t=e.length)=>function n(...a){return a.length>=t?e.apply(this,a):function(...e){return n.apply(this,[...a,...e])}},curryRight:(e,t=e.length)=>function n(...a){return a.length>=t?e.apply(this,a):function(...e){return n.apply(this,[...e,...a])}},debounce(e,t=0,n={}){let a,i,s,o,r,l=0;const d=n.leading||!1,c=!1!==n.trailing,p=n.maxWait;function m(t){const n=i,a=s;return i=s=void 0,l=t,o=e.apply(a,n),o}function h(e){const n=e-r;return void 0===r||n>=t||n<0||void 0!==p&&e-l>=p}function u(){const e=Date.now();if(h(e))return g(e);a=setTimeout(u,function(e){const n=e-r,a=e-l,i=t-n;return void 0!==p?Math.min(i,p-a):i}(e))}function g(e){return a=void 0,c&&i?m(e):(i=s=void 0,o)}function b(...e){const n=Date.now(),c=h(n);if(i=e,s=this,r=n,c){if(void 0===a)return function(e){return l=e,a=setTimeout(u,t),d?m(e):o}(r);if(void 0!==p)return a=setTimeout(u,t),m(r)}return void 0===a&&(a=setTimeout(u,t)),o}return b.cancel=function(){void 0!==a&&clearTimeout(a),l=0,i=r=s=a=void 0},b.flush=function(){return void 0===a?o:g(Date.now())},b},defer(e,...t){return setTimeout(()=>e.apply(this,t),1)},delay(e,t,...n){return setTimeout(()=>e.apply(this,n),t)},flip:e=>function(...t){return e.apply(this,t.reverse())},flow(...e){const t=e.length;return 0===t?function(e){return e}:1===t?e[0]:function(...n){let a=e[0].apply(this,n);for(let n=1;n<t;n++)a=e[n].call(this,a);return a}},compose(...e){return this.flow(...e.reverse())},memoize(e,t){const n=new Map,a=function(...a){const i=t?t.apply(this,a):a[0];if(n.has(i))return n.get(i);const s=e.apply(this,a);return n.set(i,s),s};return a.cache=n,a},negate:e=>function(...t){return!e.apply(this,t)},once(e){return this.before(2,e)},partial:(e,...t)=>function(...n){return e.apply(this,[...t,...n])},partialRight:(e,...t)=>function(...n){return e.apply(this,[...n,...t])},throttle(e,t=0,n={}){const a=!1!==n.leading,i=!1!==n.trailing;return this.debounce(e,t,{leading:a,trailing:i,maxWait:t})},unary(e){return this.ary(e,1)},wrap:(e,t)=>function(...n){return t.apply(this,[e,...n])},assign:(e,...t)=>Object.assign(e||{},...t),assignIn(e,...t){const n=e||{};return t.forEach(e=>{if(e)for(const t in e)n[t]=e[t]}),n},extend(e,...t){return this.assignIn(e,...t)},at(e,...t){return t.flat().map(t=>this.get(e,t))},clone:e=>null===e||"object"!=typeof e?e:Array.isArray(e)?[...e]:{...e},cloneDeep(e){if(null===e||"object"!=typeof e)return e;if(Array.isArray(e))return e.map(e=>this.cloneDeep(e));if(e instanceof Date)return new Date(e);if(e instanceof RegExp)return new RegExp(e);const t={};for(const n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=this.cloneDeep(e[n]));return t},defaults(e,...t){const n=e||{};return t.forEach(e=>{e&&Object.keys(e).forEach(t=>{void 0===n[t]&&(n[t]=e[t])})}),n},defaultsDeep(e,...t){const n=e||{};return t.forEach(e=>{e&&"object"==typeof e&&Object.keys(e).forEach(t=>{const a=n[t],i=e[t];void 0===a?n[t]=this.cloneDeep(i):a&&"object"==typeof a&&i&&"object"==typeof i&&!Array.isArray(a)&&!Array.isArray(i)&&this.defaultsDeep(a,i)})}),n},entries:e=>e?Object.entries(e):[],toPairs(e){return this.entries(e)},findKey(e,t){if(e)for(const n of Object.keys(e))if(t(e[n],n,e))return n},findLastKey(e,t){if(!e)return;const n=Object.keys(e).reverse();for(const a of n)if(t(e[a],a,e))return a},forIn(e,t){if(e)for(const n in e)if(!1===t(e[n],n,e))break;return e},forOwn(e,t){if(e)for(const n of Object.keys(e))if(!1===t(e[n],n,e))break;return e},get(e,t,n){if(!e)return n;const a=Array.isArray(t)?t:t.replace(/\[(\d+)]/g,".$1").split(".");let i=e;for(const e of a){if(null==i)return n;i=i[e]}return void 0===i?n:i},has(e,t){if(!e)return!1;const n=Array.isArray(t)?t:t.replace(/\[(\d+)]/g,".$1").split(".");let a=e;for(const e of n){if(!Object.prototype.hasOwnProperty.call(a,e))return!1;a=a[e]}return!0},invert(e){const t={};return e&&Object.entries(e).forEach(([e,n])=>{t[n]=e}),t},invertBy(e,t=e=>e){const n={};return e&&Object.entries(e).forEach(([e,a])=>{const i=t(a);n[i]||(n[i]=[]),n[i].push(e)}),n},keys:e=>e?Object.keys(e):[],keysIn(e){const t=[];for(const n in e)t.push(n);return t},mapKeys(e,t){const n={};return e&&Object.entries(e).forEach(([a,i])=>{n[t(i,a,e)]=i}),n},mapValues(e,t){const n={};return e&&Object.entries(e).forEach(([a,i])=>{n[a]=t(i,a,e)}),n},merge(e,...t){const n=e||{};return t.forEach(e=>{e&&"object"==typeof e&&Object.keys(e).forEach(t=>{const a=n[t],i=e[t];a&&"object"==typeof a&&i&&"object"==typeof i&&!Array.isArray(a)&&!Array.isArray(i)?this.merge(a,i):n[t]=i})}),n},omit(e,...t){if(!e)return{};const n=new Set(t.flat()),a={};return Object.keys(e).forEach(t=>{n.has(t)||(a[t]=e[t])}),a},omitBy(e,t){if(!e)return{};const n={};return Object.entries(e).forEach(([e,a])=>{t(a,e)||(n[e]=a)}),n},pick(e,...t){if(!e)return{};const n=new Set(t.flat()),a={};return n.forEach(t=>{t in e&&(a[t]=e[t])}),a},pickBy(e,t){if(!e)return{};const n={};return Object.entries(e).forEach(([e,a])=>{t(a,e)&&(n[e]=a)}),n},set(e,t,n){if(!e)return e;const a=Array.isArray(t)?t:t.replace(/\[(\d+)]/g,".$1").split(".");let i=e;for(let e=0;e<a.length-1;e++){const t=a[e],n=a[e+1];t in i&&null!==i[t]&&"object"==typeof i[t]||(i[t]=/^\d+$/.test(n)?[]:{}),i=i[t]}return i[a[a.length-1]]=n,e},unset(e,t){if(!e)return!0;const n=Array.isArray(t)?t:t.replace(/\[(\d+)]/g,".$1").split(".");let a=e;for(let e=0;e<n.length-1;e++){const t=n[e];if(!(t in a))return!0;a=a[t]}return delete a[n[n.length-1]]},setIfUndefined(e,t,n){return void 0===this.get(e,t)&&this.set(e,t,n),e},values:e=>e?Object.values(e):[],valuesIn(e){const t=[];for(const n in e)t.push(e[n]);return t},isArray:e=>Array.isArray(e),isBoolean:e=>"boolean"==typeof e||e instanceof Boolean,isDate:e=>e instanceof Date,isEmpty:e=>null==e||(Array.isArray(e)||"string"==typeof e?0===e.length:e instanceof Map||e instanceof Set?0===e.size:"object"!=typeof e||0===Object.keys(e).length),isEqual(e,t){if(e===t)return!0;if(null===e||null===t)return e===t;if(typeof e!=typeof t)return!1;if("object"!=typeof e)return!1;if(Array.isArray(e)&&Array.isArray(t))return e.length===t.length&&e.every((e,n)=>this.isEqual(e,t[n]));if(Array.isArray(e)||Array.isArray(t))return!1;const n=Object.keys(e),a=Object.keys(t);return n.length===a.length&&n.every(n=>this.isEqual(e[n],t[n]))},isMatch(e,t){if(e===t)return!0;if(null==e||null==t)return!1;if("object"!=typeof e||"object"!=typeof t)return!1;const n=Object.keys(t);for(const a of n){const n=e[a],i=t[a];if("object"==typeof i&&null!==i){if(!this.isMatch(n,i))return!1}else if(n!==i)return!1}return!0},isFinite:e=>Number.isFinite(e),isFunction:e=>"function"==typeof e,isInteger:e=>Number.isInteger(e),isNaN:e=>Number.isNaN(e),isNil:e=>null==e,isNull:e=>null===e,isNumber:e=>"number"==typeof e||e instanceof Number,isObject:e=>null!==e&&"object"==typeof e,isPlainObject(e){if(null===e||"object"!=typeof e)return!1;const t=Object.getPrototypeOf(e);return null===t||t===Object.prototype},isRegExp:e=>e instanceof RegExp,isString:e=>"string"==typeof e||e instanceof String,isSymbol:e=>"symbol"==typeof e,isUndefined:e=>void 0===e,parseInt:(e,t)=>(void 0===t&&(t=/^0x/i.test(e)?16:10),null==e?NaN:"number"==typeof e?Math.trunc(e):(e=String(e).trim(),parseInt(e,t))),toNumber(e){if("number"==typeof e)return e;if("symbol"==typeof e)return NaN;if(this.isObject(e)){const t="function"==typeof e.valueOf?e.valueOf():e;e=this.isObject(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.trim();const t=/^0b[01]+$/i.test(e),n=/^0o[0-7]+$/i.test(e),a=/^[-+]0x[0-9a-f]+$/i.test(e);return t||n?parseInt(e.slice(2),t?2:8):a?NaN:+e},toInteger(e){const t=this.toNumber(e);return Number.isNaN(t)?0:0!==t&&Number.isFinite(t)?Math.trunc(t):t},toFinite(e){const t=17976931348623157e292;if(!e)return 0===e?e:0;const n=this.toNumber(e);return n===1/0?t:n===-1/0?-t:n==n?n:0},toSafeInteger(e){const t=9007199254740991,n=this.toInteger(e);return n<-9007199254740991?-9007199254740991:n>t?t:n},toString(e){if(null==e)return"";if("string"==typeof e)return e;if(Array.isArray(e))return e.map(e=>null==e?"":this.toString(e)).join(",");if("symbol"==typeof e)return e.toString();const t=String(e);return"0"===t&&1/e==-1/0?"-0":t},toArray(e){return e?this.isArray(e)?[...e]:"string"==typeof e?e.split(""):this.isObject(e)?"function"==typeof e[Symbol.iterator]?[...e]:Object.values(e):[]:[]},castArray(...e){if(!e.length)return[];const t=e[0];return this.isArray(t)?t:[t]},toLength(e){const t=4294967295,n=this.toInteger(e);return n<0?0:n>t?t:n},toPlainObject(e){const t={};for(const n in e)t[n]=e[n];return t},add:(e,t)=>e+t,ceil(e,t=0){const n=Math.pow(10,t);return Math.ceil(e*n)/n},divide:(e,t)=>e/t,floor(e,t=0){const n=Math.pow(10,t);return Math.floor(e*n)/n},max:e=>e&&e.length?Math.max(...e):void 0,maxBy(e,t){if(e&&e.length)return e.reduce((e,n)=>("function"==typeof t?t(n):n[t])>("function"==typeof t?t(e):e[t])?n:e)},mean(e){return e&&e.length?this.sum(e)/e.length:NaN},meanBy(e,t){return e&&e.length?this.sumBy(e,t)/e.length:NaN},min:e=>e&&e.length?Math.min(...e):void 0,minBy(e,t){if(e&&e.length)return e.reduce((e,n)=>("function"==typeof t?t(n):n[t])<("function"==typeof t?t(e):e[t])?n:e)},multiply:(e,t)=>e*t,round(e,t=0){const n=Math.pow(10,t);return Math.round(e*n)/n},subtract:(e,t)=>e-t,sum:e=>e?e.reduce((e,t)=>e+t,0):0,sumBy:(e,t)=>e?e.reduce((e,n)=>e+("function"==typeof t?t(n):n[t]),0):0,clamp:(e,t,n)=>(void 0===n&&(n=t,t=void 0),void 0!==t&&e<t?t:void 0!==n&&e>n?n:e),inRange:(e,t,n)=>(void 0===n&&(n=t,t=0),t>n&&([t,n]=[n,t]),e>=t&&e<n),random:(e=0,t=1,n)=>("boolean"==typeof e?(n=e,e=0,t=1):"boolean"==typeof t&&(n=t,t=e,e=0),e>t&&([e,t]=[t,e]),n||e%1!=0||t%1!=0?e+Math.random()*(t-e):e+Math.floor(Math.random()*(t-e+1))),camelCase:e=>e?e.replace(/[^a-zA-Z0-9]+(.)/g,(e,t)=>t.toUpperCase()).replace(/^[A-Z]/,e=>e.toLowerCase()):"",capitalize:e=>e?e.charAt(0).toUpperCase()+e.slice(1).toLowerCase():"",endsWith:(e,t,n)=>!!e&&(void 0!==n?e.slice(0,n).endsWith(t):e.endsWith(t)),escape(e){if(!e)return"";const t={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"};return e.replace(/[&<>"']/g,e=>t[e])},kebabCase:e=>e?e.replace(/([a-z])([A-Z])/g,"$1-$2").replace(/[\s_]+/g,"-").toLowerCase():"",lowerCase(e){return e?this.words(e).join(" ").toLowerCase():""},lowerFirst:e=>e?e.charAt(0).toLowerCase()+e.slice(1):"",pad(e="",t=0,n=" "){const a=e.length;if(a>=t)return e;const i=t-a,s=Math.floor(i/2),o=i-s;return n.repeat(Math.ceil(s/n.length)).slice(0,s)+e+n.repeat(Math.ceil(o/n.length)).slice(0,o)},padEnd(e="",t=0,n=" "){const a=e.length;if(a>=t)return e;const i=t-a;return e+n.repeat(Math.ceil(i/n.length)).slice(0,i)},padStart(e="",t=0,n=" "){const a=e.length;if(a>=t)return e;const i=t-a;return n.repeat(Math.ceil(i/n.length)).slice(0,i)+e},repeat:(e="",t=1)=>e.repeat(Math.max(0,Math.floor(t))),replace:(e="",t,n)=>e.replace(t,n),snakeCase(e){return e?this.words(e).join("_").toLowerCase():""},split:(e="",t,n)=>e.split(t,n),startCase(e){return e?this.words(e).map(e=>this.capitalize(e)).join(" "):""},startsWith:(e,t,n=0)=>!!e&&e.startsWith(t,n),toLower:e=>e?e.toLowerCase():"",toUpper:e=>e?e.toUpperCase():"",trim(e="",t){if(!t)return e.trim();const n=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return e.replace(new RegExp(`^[${n}]+|[${n}]+$`,"g"),"")},trimEnd(e="",t){if(!t)return e.trimEnd();const n=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return e.replace(new RegExp(`[${n}]+$`,"g"),"")},trimStart(e="",t){if(!t)return e.trimStart();const n=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return e.replace(new RegExp(`^[${n}]+`,"g"),"")},truncate(e="",t={}){const n=t.length||30,a=t.omission||"...",i=t.separator;if(e.length<=n)return e;let s=n-a.length;if(s<1)return a;let o=e.slice(0,s);if(i){const e="string"==typeof i?o.lastIndexOf(i):o.search(new RegExp(i.source+"(?!.*"+i.source+")"));e>-1&&(o=o.slice(0,e))}return o+a},unescape(e){if(!e)return"";const t={"&amp;":"&","&lt;":"<","&gt;":">","&quot;":'"',"&#39;":"'"};return e.replace(/&(?:amp|lt|gt|quot|#39);/g,e=>t[e])},upperCase(e){return e?this.words(e).join(" ").toUpperCase():""},upperFirst:e=>e?e.charAt(0).toUpperCase()+e.slice(1):"",words:(e="",t)=>t?e.match(t)||[]:(e=e.replace(/([a-z])([A-Z])/g,"$1 $2")).match(/[a-zA-Z0-9]+/g)||[],sprintf(e,...t){if(!e)return"";let n=0;return e.replace(/%(?:(\d+)\$)?([+-])?(\d+)?(?:\.(\d+))?([sdifojx%])/g,(e,a,i,s,o,r)=>{if("%"===r)return"%";const l=a?parseInt(a,10)-1:n++,d=t[l];if(void 0===d)return e;if(null===d)return"null";let c;switch(r){case"s":default:c=String(d);break;case"d":case"i":c=String(parseInt(d,10));break;case"f":const e=parseFloat(d);c=void 0!==o?e.toFixed(parseInt(o,10)):String(e);break;case"o":c=Object.prototype.toString.call(d);break;case"j":try{c=JSON.stringify(d)}catch(e){c="[Circular]"}break;case"x":c=parseInt(d,10).toString(16)}if(s){const e=parseInt(s,10),t="0"===i?"0":" ";c="-"===i?c.padEnd(e," "):c.padStart(e,t)}if("+"===i&&("d"===r||"i"===r||"f"===r)){parseFloat(c)>=0&&!c.startsWith("+")&&(c="+"+c)}return c})},format(e,...t){return this.sprintf(e,...t)},template(e,t={}){const{partials:n={},helpers:a={}}=t,i=e=>null==e?"":String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;"),s=(e,t)=>"."===t?e:t.startsWith("@")?e[t]:t.split(".").reduce((e,t)=>e&&void 0!==e[t]?e[t]:"",e),o=(e,t,r=null)=>{let l=e;return l=l.replace(/\{\{#with\s+([^}]+)\}\}([\s\S]*?)\{\{\/with\}\}/g,(e,n,a)=>{const i=s(t,n.trim());return i&&"object"==typeof i?o(a,i,t):""}),l=l.replace(/\{\{#each\s+([^}]+)\}\}([\s\S]*?)\{\{\/each\}\}/g,(e,n,a)=>{const i=s(t,n.trim());return Array.isArray(i)?i.map((e,n)=>{const s="object"==typeof e?{...e,"@index":n,"@first":0===n,"@last":n===i.length-1}:{".":e,"@index":n,"@first":0===n,"@last":n===i.length-1};return o(a,s,t)}).join(""):""}),l=l.replace(/\{\{#if\s+([^}]+)\}\}([\s\S]*?)(?:\{\{else\}\}([\s\S]*?))?\{\{\/if\}\}/g,(e,n,a,i="")=>{const l=s(t,n.trim()),d=Array.isArray(l)?l.length>0:Boolean(l);return o(d?a:i,t,r)}),l=l.replace(/\{\{#unless\s+([^}]+)\}\}([\s\S]*?)\{\{\/unless\}\}/g,(e,n,a)=>{const i=s(t,n.trim());return(Array.isArray(i)?0===i.length:!i)?o(a,t,r):""}),l=l.replace(/\{\{>\s*([^}]+)\}\}/g,(e,a)=>{const i=n[a.trim()];return i?o(i,t,r):""}),l=l.replace(/\{\{\{([^}]+)\}\}\}/g,(e,n)=>{const a=s(t,n.trim());return null!=a?String(a):""}),l=l.replace(/\{\{([^#/>][^}]*)\}\}/g,(e,n)=>{const o=n.trim(),r=o.match(/^(\w+)\s+(.+)$/);if(r&&a[r[1]]){const e=r[2].split(/\s+/).map(e=>/^["'].*["']$/.test(e)?e.slice(1,-1):s(t,e));return i(a[r[1]](...e))}const l=s(t,o);return i(l)}),l};return(t={})=>o(e,t)},render(e,t,n={}){return this.template(e,n)(t)},async copyToClipboard(e){if(navigator.clipboard?.writeText)try{return await navigator.clipboard.writeText(e),!0}catch(e){}const t=document.createElement("textarea");t.value=e,t.style.cssText="position:fixed;opacity:0;pointer-events:none",document.body.appendChild(t),t.select();try{return document.execCommand("copy")}catch(e){return!1}finally{document.body.removeChild(t)}},escapeHtml:e=>null==e?"":String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;"),chain(e){const t={_value:e,value(){return this._value}};["chunk","compact","concat","difference","drop","dropRight","fill","findIndex","findLastIndex","flatten","flattenDeep","flattenDepth","fromPairs","head","indexOf","initial","intersection","join","last","lastIndexOf","nth","pull","pullAll","pullAt","remove","reverse","slice","sortedIndex","sortedUniq","tail","take","takeRight","union","uniq","uniqBy","unzip","without","xor","zip","countBy","each","every","filter","find","findLast","flatMap","groupBy","includes","invokeMap","keyBy","map","orderBy","partition","reduce","reduceRight","reject","sample","sampleSize","shuffle","size","some","sortBy","assign","assignIn","at","defaults","entries","extend","findKey","findLastKey","forIn","forOwn","functions","get","has","invert","invertBy","invoke","keys","mapKeys","mapValues","merge","omit","omitBy","pick","pickBy","result","set","toPairs","transform","unset","update","values","camelCase","capitalize","deburr","endsWith","escape","escapeRegExp","kebabCase","lowerCase","lowerFirst","pad","padEnd","padStart","parseInt","repeat","replace","snakeCase","split","startCase","startsWith","toLower","toUpper","trim","trimEnd","trimStart","truncate","unescape","upperCase","upperFirst","words"].forEach(e=>{"function"==typeof this[e]&&(t[e]=function(...t){return this._value=l[e](this._value,...t),this})});return["sum","mean","min","max","toArray","toJSON","toString"].forEach(e=>{"function"==typeof this[e]&&(t[e]=function(...t){return l[e](this._value,...t)})}),t}},d={"--dm-primary":{category:"colours",subCategory:"brand",type:"color",label:"Primary",defaultLight:"#6495ED",defaultDark:"#6ea8fe"},"--dm-primary-hover":{category:"colours",subCategory:"brand",type:"color",label:"Primary Hover",defaultLight:"#5280d8",defaultDark:"#8bb9fe"},"--dm-primary-active":{category:"colours",subCategory:"brand",type:"color",label:"Primary Active",defaultLight:"#4169c0",defaultDark:"#a8cafe"},"--dm-secondary":{category:"colours",subCategory:"brand",type:"color",label:"Secondary",defaultLight:"#6c757d",defaultDark:"#adb5bd"},"--dm-secondary-hover":{category:"colours",subCategory:"brand",type:"color",label:"Secondary Hover",defaultLight:"#5c636a",defaultDark:"#b1b8bf"},"--dm-success":{category:"colours",subCategory:"status",type:"color",label:"Success",defaultLight:"#198754",defaultDark:"#75b798"},"--dm-success-hover":{category:"colours",subCategory:"status",type:"color",label:"Success Hover",defaultLight:"#157347",defaultDark:"#8cc5a7"},"--dm-danger":{category:"colours",subCategory:"status",type:"color",label:"Danger",defaultLight:"#dc3545",defaultDark:"#ea868f"},"--dm-danger-hover":{category:"colours",subCategory:"status",type:"color",label:"Danger Hover",defaultLight:"#bb2d3b",defaultDark:"#ee9aa1"},"--dm-warning":{category:"colours",subCategory:"status",type:"color",label:"Warning",defaultLight:"#ffc107",defaultDark:"#ffda6a"},"--dm-warning-hover":{category:"colours",subCategory:"status",type:"color",label:"Warning Hover",defaultLight:"#ffca2c",defaultDark:"#ffe083"},"--dm-info":{category:"colours",subCategory:"status",type:"color",label:"Info",defaultLight:"#0dcaf0",defaultDark:"#6edff6"},"--dm-info-hover":{category:"colours",subCategory:"status",type:"color",label:"Info Hover",defaultLight:"#31d2f2",defaultDark:"#89e5f8"},"--dm-background":{category:"colours",subCategory:"backgrounds",type:"color",label:"Background",defaultLight:"#ffffff",defaultDark:"#121212"},"--dm-background-alt":{category:"colours",subCategory:"backgrounds",type:"color",label:"Background Alt",defaultLight:"#f8f9fa",defaultDark:"#1e1e1e"},"--dm-surface":{category:"colours",subCategory:"backgrounds",type:"color",label:"Surface",defaultLight:"#ffffff",defaultDark:"#1e1e1e"},"--dm-surface-raised":{category:"colours",subCategory:"backgrounds",type:"color",label:"Surface Raised",defaultLight:"#ffffff",defaultDark:"#2d2d2d"},"--dm-text":{category:"colours",subCategory:"text",type:"color",label:"Text",defaultLight:"#212529",defaultDark:"#e9ecef"},"--dm-text-secondary":{category:"colours",subCategory:"text",type:"color",label:"Text Secondary",defaultLight:"#495057",defaultDark:"#adb5bd"},"--dm-text-muted":{category:"colours",subCategory:"text",type:"color",label:"Text Muted",defaultLight:"#6c757d",defaultDark:"#868e96"},"--dm-border":{category:"colours",subCategory:"borders",type:"color",label:"Border",defaultLight:"#dee2e6",defaultDark:"#3d3d3d"},"--dm-border-light":{category:"colours",subCategory:"borders",type:"color",label:"Border Light",defaultLight:"#e9ecef",defaultDark:"#2d2d2d"},"--dm-divider":{category:"colours",subCategory:"borders",type:"color",label:"Divider",defaultLight:"#e9ecef",defaultDark:"#3d3d3d"},"--dm-font-sans":{category:"typography",subCategory:"families",type:"select",label:"Sans-serif Font",default:"system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",options:[{value:"system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",label:"System UI"},{value:"'Inter', sans-serif",label:"Inter"},{value:"'Open Sans', sans-serif",label:"Open Sans"},{value:"'Roboto', sans-serif",label:"Roboto"},{value:"'Lato', sans-serif",label:"Lato"},{value:"'Poppins', sans-serif",label:"Poppins"}]},"--dm-font-mono":{category:"typography",subCategory:"families",type:"select",label:"Monospace Font",default:"'SF Mono', Monaco, 'Cascadia Code', Consolas, monospace",options:[{value:"'SF Mono', Monaco, 'Cascadia Code', Consolas, monospace",label:"SF Mono"},{value:"'Fira Code', monospace",label:"Fira Code"},{value:"'JetBrains Mono', monospace",label:"JetBrains Mono"},{value:"'Source Code Pro', monospace",label:"Source Code Pro"}]},"--dm-text-xs":{category:"typography",subCategory:"sizes",type:"size",label:"Extra Small",default:"0.75rem",unit:"rem",min:.5,max:1,step:.0625},"--dm-text-sm":{category:"typography",subCategory:"sizes",type:"size",label:"Small",default:"0.875rem",unit:"rem",min:.625,max:1.25,step:.0625},"--dm-text-base":{category:"typography",subCategory:"sizes",type:"size",label:"Base",default:"1rem",unit:"rem",min:.75,max:1.5,step:.0625},"--dm-text-lg":{category:"typography",subCategory:"sizes",type:"size",label:"Large",default:"1.125rem",unit:"rem",min:.875,max:1.75,step:.0625},"--dm-text-xl":{category:"typography",subCategory:"sizes",type:"size",label:"Extra Large",default:"1.25rem",unit:"rem",min:1,max:2,step:.0625},"--dm-text-2xl":{category:"typography",subCategory:"sizes",type:"size",label:"2XL",default:"1.5rem",unit:"rem",min:1.25,max:2.5,step:.125},"--dm-space-1":{category:"spacing",subCategory:"scale",type:"size",label:"Space 1 (4px)",default:"0.25rem",unit:"rem",min:0,max:.5,step:.0625},"--dm-space-2":{category:"spacing",subCategory:"scale",type:"size",label:"Space 2 (8px)",default:"0.5rem",unit:"rem",min:.25,max:1,step:.0625},"--dm-space-3":{category:"spacing",subCategory:"scale",type:"size",label:"Space 3 (12px)",default:"0.75rem",unit:"rem",min:.5,max:1.25,step:.0625},"--dm-space-4":{category:"spacing",subCategory:"scale",type:"size",label:"Space 4 (16px)",default:"1rem",unit:"rem",min:.5,max:2,step:.125},"--dm-space-6":{category:"spacing",subCategory:"scale",type:"size",label:"Space 6 (24px)",default:"1.5rem",unit:"rem",min:1,max:3,step:.125},"--dm-space-8":{category:"spacing",subCategory:"scale",type:"size",label:"Space 8 (32px)",default:"2rem",unit:"rem",min:1,max:4,step:.25},"--dm-radius-sm":{category:"borders",subCategory:"radius",type:"size",label:"Small Radius",default:"0.125rem",unit:"rem",min:0,max:.5,step:.0625},"--dm-radius-md":{category:"borders",subCategory:"radius",type:"size",label:"Medium Radius",default:"0.25rem",unit:"rem",min:0,max:.75,step:.0625},"--dm-radius-lg":{category:"borders",subCategory:"radius",type:"size",label:"Large Radius",default:"0.5rem",unit:"rem",min:0,max:1,step:.0625},"--dm-radius-xl":{category:"borders",subCategory:"radius",type:"size",label:"XL Radius",default:"0.75rem",unit:"rem",min:0,max:1.5,step:.125},"--dm-radius-2xl":{category:"borders",subCategory:"radius",type:"size",label:"2XL Radius",default:"1rem",unit:"rem",min:0,max:2,step:.125},"--dm-transition-fast":{category:"transitions",subCategory:"durations",type:"transition",label:"Fast",default:"150ms ease",min:50,max:300,step:25},"--dm-transition-normal":{category:"transitions",subCategory:"durations",type:"transition",label:"Normal",default:"200ms ease",min:100,max:400,step:25},"--dm-transition-slow":{category:"transitions",subCategory:"durations",type:"transition",label:"Slow",default:"300ms ease",min:200,max:600,step:25},"--dm-disabled-opacity":{category:"states",subCategory:"interactive",type:"opacity",label:"Disabled Opacity",default:"0.65",min:.3,max:1,step:.05}},c={colours:{name:"Colours",icon:"palette",subCategories:{brand:"Brand",status:"Status",backgrounds:"Backgrounds",text:"Text",borders:"Borders"}},typography:{name:"Typography",icon:"type",subCategories:{families:"Font Families",sizes:"Font Sizes"}},spacing:{name:"Spacing",icon:"maximize",subCategories:{scale:"Spacing Scale"}},borders:{name:"Borders & Radius",icon:"square",subCategories:{radius:"Border Radius"}},transitions:{name:"Transitions",icon:"zap",subCategories:{durations:"Durations"}},states:{name:"Interactive States",icon:"mouse-pointer",subCategories:{interactive:"States"}}};class p{static defaults={baseTheme:"charcoal-dark",sections:["colours","typography","spacing","borders","transitions","states"],livePreview:!0,showPreviewPanel:!0,onChange:null,onApply:null,onExport:null,onReset:null};constructor(e,t={}){this.element="string"==typeof e?document.querySelector(e):e,this.options={...p.defaults,...t},this._changes={},this._previewStyle=null,this._eventHandlers=[],this._accordionInstance=null,this.element&&this._init()}_init(){this._createPreviewStyle(),this._render(),"undefined"!=typeof Domma&&Domma.elements&&(this._accordionInstance=Domma.elements.accordion("#theme-roller-accordion",{multiExpand:!0,activeIndex:0})),this._bindEvents(),this.options.baseTheme&&o.set(this.options.baseTheme)}_createPreviewStyle(){this._previewStyle=document.getElementById("dm-theme-roller-preview"),this._previewStyle||(this._previewStyle=document.createElement("style"),this._previewStyle.id="dm-theme-roller-preview",document.head.appendChild(this._previewStyle))}_render(){const e=o.get();let t=`\n <div class="dm-theme-roller">\n <div class="dm-theme-roller-header">\n <h3 class="dm-theme-roller-title">Theme Roller</h3>\n <div class="dm-theme-roller-controls">\n <label for="theme-select" class="dm-theme-select-label">Theme:</label>\n <select id="theme-select" class="dm-theme-select form-select">\n <optgroup label="Ocean">\n <option value="ocean-light" ${"ocean-light"===e?"selected":""}>Ocean Light ☀️</option>\n <option value="ocean-dark" ${"ocean-dark"===e?"selected":""}>Ocean Dark 🌙</option>\n </optgroup>\n <optgroup label="Forest">\n <option value="forest-light" ${"forest-light"===e?"selected":""}>Forest Light ☀️</option>\n <option value="forest-dark" ${"forest-dark"===e?"selected":""}>Forest Dark 🌙</option>\n </optgroup>\n <optgroup label="Sunset">\n <option value="sunset-light" ${"sunset-light"===e?"selected":""}>Sunset Light ☀️</option>\n <option value="sunset-dark" ${"sunset-dark"===e?"selected":""}>Sunset Dark 🌙</option>\n </optgroup>\n <optgroup label="Royal">\n <option value="royal-light" ${"royal-light"===e?"selected":""}>Royal Light ☀️</option>\n <option value="royal-dark" ${"royal-dark"===e?"selected":""}>Royal Dark 🌙</option>\n </optgroup>\n <optgroup label="Lemon">\n <option value="lemon-light" ${"lemon-light"===e?"selected":""}>Lemon Light ☀️</option>\n <option value="lemon-dark" ${"lemon-dark"===e?"selected":""}>Lemon Dark 🌙</option>\n </optgroup>\n <optgroup label="Silver">\n <option value="silver-light" ${"silver-light"===e?"selected":""}>Silver Light ☀️</option>\n <option value="silver-dark" ${"silver-dark"===e?"selected":""}>Silver Dark 🌙</option>\n </optgroup>\n <optgroup label="Charcoal">\n <option value="charcoal-light" ${"charcoal-light"===e?"selected":""}>Charcoal Light ☀️</option>\n <option value="charcoal-dark" ${"charcoal-dark"===e?"selected":""}>Charcoal Dark 🌙</option>\n </optgroup>\n <optgroup label="Christmas">\n <option value="christmas-light" ${"christmas-light"===e?"selected":""}>Christmas Light ☀️</option>\n <option value="christmas-dark" ${"christmas-dark"===e?"selected":""}>Christmas Dark 🌙</option>\n </optgroup>\n <optgroup label="Unicorn">\n <option value="unicorn-light" ${"unicorn-light"===e?"selected":""}>Unicorn Light ☀️</option>\n <option value="unicorn-dark" ${"unicorn-dark"===e?"selected":""}>Unicorn Dark 🌙</option>\n </optgroup>\n <optgroup label="Dreamy">\n <option value="dreamy-light" ${"dreamy-light"===e?"selected":""}>Dreamy Light ☀️</option>\n <option value="dreamy-dark" ${"dreamy-dark"===e?"selected":""}>Dreamy Dark 🌙</option>\n </optgroup>\n <optgroup label="Grayve">\n <option value="grayve-light" ${"grayve-light"===e?"selected":""}>Grayve Light ☀️</option>\n <option value="grayve-dark" ${"grayve-dark"===e?"selected":""}>Grayve Dark 🌙</option>\n </optgroup>\n </select>\n </div>\n </div>\n `;t+='<div class="dm-theme-roller-sections accordion" id="theme-roller-accordion">';for(const e of this.options.sections){const n=c[e];n&&(t+=`\n <div class="accordion-item" data-category="${e}">\n <div class="accordion-header">\n <span data-icon="${n.icon}" data-icon-size="18"></span>\n ${n.name}\n <span class="accordion-icon">&#9660;</span>\n </div>\n <div class="accordion-body">\n <div class="accordion-content">\n ${this._renderCategoryContent(e,n)}\n </div>\n </div>\n </div>\n `)}t+="</div>",this.options.showPreviewPanel&&(t+=this._renderPreviewPanel()),t+='\n <div class="dm-theme-roller-actions">\n <button class="dm-btn dm-btn-secondary" data-action="reset">\n <span data-icon="refresh-cw" data-icon-size="16"></span>\n Reset\n </button>\n <button class="dm-btn dm-btn-secondary" data-action="save">\n <span data-icon="save" data-icon-size="16"></span>\n Save\n </button>\n <button class="dm-btn dm-btn-secondary" data-action="copy">\n <span data-icon="copy" data-icon-size="16"></span>\n Copy CSS\n </button>\n <button class="dm-btn dm-btn-primary" data-action="download">\n <span data-icon="download" data-icon-size="16"></span>\n Download\n </button>\n </div>\n ',t+="</div>",this.element.innerHTML=t,this.element.querySelectorAll('input[type="color"]').forEach(e=>{const t=e.getAttribute("value");t&&(e.value=t)}),"undefined"!=typeof Domma&&Domma.icons&&Domma.icons.scan(this.element)}_renderCategoryContent(e,t){let n="";const a=o.isDark();for(const[i,s]of Object.entries(t.subCategories)){const t=Object.entries(d).filter(([,t])=>t.category===e&&t.subCategory===i);if(0!==t.length){n+=`<div class="dm-var-group"><h4 class="dm-var-group-title">${s}</h4><div class="dm-var-grid">`;for(const[e,i]of t){const t=a?i.defaultDark||i.default:i.defaultLight||i.default;let s=getComputedStyle(document.body).getPropertyValue(e).trim();if("color"===i.type&&console.log(`[ThemeRoller] Reading ${e}: "${s}"`),"color"===i.type&&s.startsWith("rgb")){const e=s.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);if(e){s=`#${parseInt(e[1]).toString(16).padStart(2,"0")}${parseInt(e[2]).toString(16).padStart(2,"0")}${parseInt(e[3]).toString(16).padStart(2,"0")}`.toUpperCase()}}const o=this._changes[e]||s||t;n+=this._renderInput(e,i,o)}n+="</div></div>"}}return n}_renderInput(e,t,n){const a=`tr-${e.replace(/--dm-/g,"").replace(/-/g,"_")}`;let i="";switch(t.type){case"color":i=`\n <div class="dm-var-item dm-var-color">\n <label for="${a}">${t.label}</label>\n <div class="dm-color-input-wrapper">\n <input type="color" id="${a}" data-var="${e}" value="${n}">\n <input type="text" class="dm-color-text" data-var="${e}" value="${n}" maxlength="7">\n </div>\n </div>\n `;break;case"size":const s=parseFloat(n);i=`\n <div class="dm-var-item dm-var-size">\n <label for="${a}">${t.label}</label>\n <div class="dm-size-input-wrapper">\n <input type="range" id="${a}" data-var="${e}"\n min="${t.min}" max="${t.max}" step="${t.step}"\n value="${s}">\n <span class="dm-size-value">${n}</span>\n </div>\n </div>\n `;break;case"select":i=`\n <div class="dm-var-item dm-var-select">\n <label for="${a}">${t.label}</label>\n <select id="${a}" data-var="${e}">\n ${t.options.map(e=>`\n <option value="${e.value}" ${e.value===n?"selected":""}>\n ${e.label}\n </option>\n `).join("")}\n </select>\n </div>\n `;break;case"transition":const o=parseInt(n);i=`\n <div class="dm-var-item dm-var-transition">\n <label for="${a}">${t.label}</label>\n <div class="dm-size-input-wrapper">\n <input type="range" id="${a}" data-var="${e}"\n min="${t.min}" max="${t.max}" step="${t.step}"\n value="${o}">\n <span class="dm-size-value">${o}ms</span>\n </div>\n </div>\n `;break;case"opacity":const r=parseFloat(n);i=`\n <div class="dm-var-item dm-var-opacity">\n <label for="${a}">${t.label}</label>\n <div class="dm-size-input-wrapper">\n <input type="range" id="${a}" data-var="${e}"\n min="${t.min}" max="${t.max}" step="${t.step}"\n value="${r}">\n <span class="dm-size-value">${r}</span>\n </div>\n </div>\n `;break;default:i=`\n <div class="dm-var-item">\n <label for="${a}">${t.label}</label>\n <input type="text" id="${a}" data-var="${e}" value="${n}">\n </div>\n `}return i}_renderPreviewPanel(){return'\n <div class="dm-theme-roller-preview">\n <h4>Live Preview</h4>\n <div class="dm-preview-content">\n <div class="dm-preview-buttons">\n <button class="btn btn-primary">Primary</button>\n <button class="btn btn-secondary">Secondary</button>\n <button class="btn btn-success">Success</button>\n <button class="btn btn-danger">Danger</button>\n <button class="btn btn-warning">Warning</button>\n <button class="btn btn-info">Info</button>\n </div>\n <div class="dm-preview-card">\n <h5>Sample Card</h5>\n <p>This card demonstrates the current theme settings including colours, typography, and spacing.</p>\n </div>\n <div class="dm-preview-input">\n <input type="text" placeholder="Sample input field">\n </div>\n <div class="dm-preview-alerts">\n <div class="dm-alert dm-alert-success">Success message</div>\n <div class="dm-alert dm-alert-warning">Warning message</div>\n <div class="dm-alert dm-alert-danger">Error message</div>\n <div class="dm-alert dm-alert-info">Info message</div>\n </div>\n </div>\n </div>\n '}_bindEvents(){const e={"#theme-select":{on:"change",call:(e,t)=>{const n=t.value;console.log("[ThemeRoller] Theme changed to:",n),o.set(n),this._refreshInputs()}},'input[type="color"]':{on:"input",call:(e,t)=>{const n=t.dataset.var;this.set(n,t.value);const a=this.element.querySelector(`.dm-color-text[data-var="${n}"]`);a&&(a.value=t.value)}},".dm-color-text":{on:"change",call:(e,t)=>{const n=t.dataset.var,a=t.value;if(/^#[0-9A-Fa-f]{6}$/.test(a)){this.set(n,a);const e=this.element.querySelector(`input[type="color"][data-var="${n}"]`);e&&(e.value=a)}}},'input[type="range"]':{on:"input",call:(e,t)=>{const n=t.dataset.var,a=d[n];let i=t.value,s=i;"size"===a.type?(i=`${i}${a.unit}`,s=i):"transition"===a.type&&(s=`${i}ms`,i=`${i}ms ease`),this.set(n,i);const o=t.parentElement.querySelector(".dm-size-value");o&&(o.textContent=s)}},"select[data-var]":{on:"change",call:(e,t)=>{const n=t.dataset.var;this.set(n,t.value)}},"[data-action]":{on:"click",call:async(e,t)=>{switch(t.dataset.action){case"reset":this.reset();break;case"save":this.saveToStorage();break;case"copy":await this.copyToClipboard();break;case"download":this.download()}}}};console.log("[ThemeRoller] Setting up event bindings (declarative style)"),Object.keys(e).forEach(t=>{const n=this.element.querySelectorAll(t),{on:a,call:i}=e[t];n.forEach(e=>{e.addEventListener(a,t=>i(t,e))})})}_addEventListener(e,t,n){e.addEventListener(t,n),this._eventHandlers.push({element:e,event:t,handler:n})}_updateThemeDropdown(){const e=o.get(),t=this.element.querySelector("#theme-select");t&&(t.value=e)}_refreshInputs(){console.log("[ThemeRoller] _refreshInputs starting"),this._changes={},this._updatePreview(),console.log("[ThemeRoller] About to re-render"),this._render(),console.log("[ThemeRoller] Re-render completed"),"undefined"!=typeof Domma&&Domma.elements&&this._accordionInstance&&(console.log("[ThemeRoller] Destroying old accordion instance"),this._accordionInstance.destroy()),"undefined"!=typeof Domma&&Domma.elements&&(console.log("[ThemeRoller] Creating new accordion instance"),this._accordionInstance=Domma.elements.accordion("#theme-roller-accordion",{multiExpand:!0,activeIndex:0})),console.log("[ThemeRoller] About to rebind events"),this._bindEvents(),console.log("[ThemeRoller] _refreshInputs complete")}_updatePreview(){if(!this.options.livePreview||!this._previewStyle)return;if(0===Object.keys(this._changes).length)return void(this._previewStyle.textContent="");let e="body, :root {\n";for(const[t,n]of Object.entries(this._changes))e+=` ${t}: ${n} !important;\n`;e+="}",this._previewStyle.textContent=e}_capitalize(e){return e.charAt(0).toUpperCase()+e.slice(1)}get(e){return this._changes[e]||null}set(e,t){return this._changes[e]=t,this._updatePreview(),this.options.onChange&&this.options.onChange(e,t),this}getAll(){return{...this._changes}}setAll(e){for(const[t,n]of Object.entries(e))this._changes[t]=n;return this._updatePreview(),this}reset(){return this._changes={},this._updatePreview(),this._refreshInputs(),this.options.onReset&&this.options.onReset(),this._showToast("Theme reset to defaults"),this}loadPreset(e){return console.warn("[ThemeRoller] loadPreset() is deprecated. Use the theme dropdown instead."),e||(e="charcoal-dark"),o.isDisabled()&&o.enable(),o.set(e),document.body.offsetHeight,setTimeout(()=>{this._refreshInputsDelayed()},50),this._changes={},this._updatePreview(),this._updateThemeDropdown(),this}_refreshInputsDelayed(){console.log("[ThemeRoller] _refreshInputsDelayed called"),this._changes={},this._updatePreview(),this._refreshInputs(),this._showToast("Theme preset loaded")}exportCSS(e={}){const{themeName:t="custom",includeHeader:n=!0,minify:a=!1}=e;if(0===Object.keys(this._changes).length)return"";const i=a?"":"\n",s=a?"":" ";let r="";n&&(r+=`/**${i}`,r+=` * Custom Domma Theme${i}`,r+=` * Generated: ${(new Date).toISOString().split("T")[0]}${i}`,r+=` * Base: ${o.get()}${i}`,r+=` */${i}${i}`),r+=`.dm-theme-${t} {${i}`;for(const[e,t]of Object.entries(this._changes))r+=`${s}${e}: ${t};${i}`;return r+=`}${i}`,this.options.onExport&&this.options.onExport(r),r}download(e="custom-theme"){const t=this.exportCSS();if(!t)return this._showToast("No changes to export","warning"),this;const n=new Blob([t],{type:"text/css"}),a=URL.createObjectURL(n),i=document.createElement("a");return i.href=a,i.download=`${e}.css`,document.body.appendChild(i),i.click(),document.body.removeChild(i),URL.revokeObjectURL(a),this._showToast("Theme downloaded successfully"),this}async copyToClipboard(){const e=this.exportCSS();return e?(await l.copyToClipboard(e),this._showToast("CSS copied to clipboard"),this):(this._showToast("No changes to copy","warning"),this)}saveToStorage(){return r.set("theme-roller-custom",{theme:o.get(),changes:this._changes}),this._showToast("Theme saved to browser"),this}loadFromStorage(){const e=r.get("theme-roller-custom");return e&&(e.theme&&o.set(e.theme),e.changes&&this.setAll(e.changes),this._updateThemeDropdown(),this._showToast("Theme loaded from browser")),this}_showToast(e,t="success"){"undefined"!=typeof Domma&&Domma.elements&&Domma.elements.toast&&Domma.elements.toast[t](e,{position:"bottom-center",duration:2e3})}apply(){return this.options.onApply&&this.options.onApply(this._changes),this._showToast("Theme applied"),this}destroy(){for(const{element:e,event:t,handler:n}of this._eventHandlers)e.removeEventListener(t,n);this._eventHandlers=[],this._previewStyle&&this._previewStyle.remove(),this._accordionInstance&&this._accordionInstance.destroy(),this.element&&(this.element.innerHTML="")}}const m={navbar:{name:"Navigation Bar",icon:"menu",category:"headers",description:"Top navigation bar with logo and menu links",defaults:{brandText:"My Site",brandImage:"",style:"light",sticky:!1,links:[{text:"Home",url:"#"},{text:"About",url:"#"},{text:"Services",url:"#"},{text:"Contact",url:"#"}],showCta:!0,ctaText:"Get Started",ctaUrl:"#"},editableFields:[{key:"brandText",type:"text",label:"Brand Text"},{key:"brandImage",type:"text",label:"Logo URL"},{key:"style",type:"select",label:"Style",options:["light","dark","transparent"]},{key:"sticky",type:"toggle",label:"Sticky"},{key:"links",type:"links",label:"Menu Links"},{key:"showCta",type:"toggle",label:"Show CTA Button"},{key:"ctaText",type:"text",label:"CTA Text",showWhen:{showCta:!0}},{key:"ctaUrl",type:"text",label:"CTA URL",showWhen:{showCta:!0}}],template:e=>`<nav class="navbar ${"dark"===e.style?"navbar-dark":"transparent"===e.style?"navbar-transparent":"navbar-light"} ${e.sticky?"navbar-sticky":""}">\n <div class="container">\n <a href="#" class="navbar-brand">\n ${e.brandImage?`<img src="${e.brandImage}" alt="${e.brandText}" class="navbar-logo">`:`<span class="navbar-brand-text">${e.brandText}</span>`}\n </a>\n <div class="navbar-nav">\n ${e.links.map(e=>`<a href="${e.url}" class="nav-link">${e.text}</a>`).join("\n ")}\n </div>\n <div class="navbar-actions">\n ${e.showCta?`<a href="${e.ctaUrl}" class="btn btn-primary btn-sm">${e.ctaText}</a>`:""}\n </div>\n </div>\n</nav>`},hero:{name:"Hero",icon:"layout",category:"headers",description:"Large header section with title, subtitle and call-to-action",defaults:{title:"Welcome to My Site",subtitle:"A brief description of what you offer.",backgroundType:"gradient",backgroundColor:"var(--dm-primary, #6495ED)",backgroundGradient:"linear-gradient(135deg, var(--dm-primary, #6495ED) 0%, var(--dm-primary-dark, #5280d8) 100%)",backgroundImage:"",textColor:"var(--dm-white, #ffffff)",alignment:"center",size:"large",overlay:!1,buttons:[{text:"Get Started",style:"primary",url:"#"}]},editableFields:[{key:"title",type:"text",label:"Title"},{key:"subtitle",type:"textarea",label:"Subtitle"},{key:"backgroundType",type:"select",label:"Background",options:["solid","gradient","image"]},{key:"backgroundColor",type:"color",label:"Background Colour",showWhen:{backgroundType:"solid"}},{key:"backgroundGradient",type:"text",label:"Gradient CSS",showWhen:{backgroundType:"gradient"}},{key:"backgroundImage",type:"text",label:"Image URL",showWhen:{backgroundType:"image"}},{key:"textColor",type:"color",label:"Text Colour"},{key:"alignment",type:"select",label:"Alignment",options:["left","center","right"]},{key:"size",type:"select",label:"Size",options:["small","medium","large"]},{key:"overlay",type:"toggle",label:"Dark Overlay"},{key:"buttons",type:"buttons",label:"Buttons"}],template:e=>{const t="small"===e.size?"py-8":"large"===e.size?"py-16":"py-12",n="left"===e.alignment?"text-left":"right"===e.alignment?"text-right":"text-center";let a="";"solid"===e.backgroundType?a=`background-color: ${e.backgroundColor};`:"gradient"===e.backgroundType?a=`background: ${e.backgroundGradient};`:"image"===e.backgroundType&&(a=`background-image: url('${e.backgroundImage}'); background-size: cover; background-position: center;`);const i=e.overlay?'<div style="position:absolute;inset:0;background:rgba(0,0,0,0.4);"></div>':"",s=e.buttons.map(e=>{const t="primary"===e.style?"btn btn-primary":"secondary"===e.style?"btn btn-secondary":"outline"===e.style?"btn btn-outline":"btn";return`<a href="${e.url}" class="${t}">${e.text}</a>`}).join("\n ");return`<section class="hero ${t}" style="${a} color: ${e.textColor}; position: relative;">\n ${i}\n <div class="container ${n}" style="position: relative; z-index: 1;">\n <h1 class="hero-title">${e.title}</h1>\n <p class="hero-subtitle">${e.subtitle}</p>\n <div class="hero-buttons" style="margin-top: 2rem;">\n ${s}\n </div>\n </div>\n</section>`}},cardGrid:{name:"Card Grid",icon:"grid",category:"content",description:"Grid of cards for features, products, or team members",defaults:{columns:3,gap:"medium",cardStyle:"default",sectionTitle:"",sectionSubtitle:"",cards:[{title:"Feature One",text:"Description of the first feature.",image:"",icon:"star",link:"#"},{title:"Feature Two",text:"Description of the second feature.",image:"",icon:"zap",link:"#"},{title:"Feature Three",text:"Description of the third feature.",image:"",icon:"check",link:"#"}]},editableFields:[{key:"sectionTitle",type:"text",label:"Section Title"},{key:"sectionSubtitle",type:"textarea",label:"Section Subtitle"},{key:"columns",type:"number",label:"Columns",min:1,max:6},{key:"gap",type:"select",label:"Gap",options:["small","medium","large"]},{key:"cardStyle",type:"select",label:"Card Style",options:["default","bordered","shadow","minimal"]},{key:"cards",type:"cards",label:"Cards"}],template:e=>{const t="small"===e.gap?"gap-2":"large"===e.gap?"gap-6":"gap-4",n=e.sectionTitle?`\n <div class="text-center mb-8">\n <h2 class="text-3xl font-bold mb-2">${e.sectionTitle}</h2>\n ${e.sectionSubtitle?`<p class="text-muted">${e.sectionSubtitle}</p>`:""}\n </div>`:"",a=e.cards.map(t=>` <div class="${"bordered"===e.cardStyle?"card card-bordered":"shadow"===e.cardStyle?"card card-shadow":"minimal"===e.cardStyle?"card card-minimal":"card"}">\n ${t.image?`<img src="${t.image}" alt="${t.title}" class="card-img-top">`:""}\n <div class="card-body">\n ${!t.image&&t.icon?`<div class="card-icon mb-3"><span data-icon="${t.icon}" data-icon-size="32"></span></div>`:""}\n <h3 class="card-title">${t.title}</h3>\n <p class="card-text">${t.text}</p>\n ${t.link&&"#"!==t.link?`<a href="${t.link}" class="btn btn-link">Learn more</a>`:""}\n </div>\n </div>`).join("\n");return`<section class="py-12">\n <div class="container">\n ${n}\n <div class="grid grid-cols-${e.columns} ${t}">\n${a}\n </div>\n </div>\n</section>`}},content:{name:"Content Block",icon:"document-text",category:"content",description:"Rich text content section with optional image",defaults:{title:"About Us",content:"<p>Write your content here. This section supports rich text formatting.</p>",layout:"text-only",imagePosition:"right",image:"",imageAlt:""},editableFields:[{key:"title",type:"text",label:"Title"},{key:"content",type:"richtext",label:"Content"},{key:"layout",type:"select",label:"Layout",options:["text-only","text-image","image-text"]},{key:"image",type:"text",label:"Image URL",showWhen:{layout:["text-image","image-text"]}},{key:"imageAlt",type:"text",label:"Image Alt Text",showWhen:{layout:["text-image","image-text"]}}],template:e=>{if("text-only"===e.layout)return`<section class="py-12">\n <div class="container">\n <div class="max-w-3xl mx-auto">\n ${e.title?`<h2 class="text-3xl font-bold mb-4">${e.title}</h2>`:""}\n <div class="content">\n ${e.content}\n </div>\n </div>\n </div>\n</section>`;const t="image-text"===e.layout,n=`<div class="col-6">\n <img src="${e.image}" alt="${e.imageAlt}" class="img-fluid rounded">\n </div>`,a=`<div class="col-6">\n ${e.title?`<h2 class="text-3xl font-bold mb-4">${e.title}</h2>`:""}\n <div class="content">\n ${e.content}\n </div>\n </div>`;return`<section class="py-12">\n <div class="container">\n <div class="row align-center gap-6">\n ${t?n:a}\n ${t?a:n}\n </div>\n </div>\n</section>`}},form:{name:"Form Section",icon:"edit",category:"interactive",description:"Contact form, newsletter signup, or custom form",defaults:{title:"Contact Us",description:"Get in touch with us and we'll respond within 24 hours.",layout:"stacked",submitText:"Send Message",backgroundColor:"",fields:[{type:"text",name:"name",label:"Name",required:!0,placeholder:"Your name"},{type:"email",name:"email",label:"Email",required:!0,placeholder:"your@email.com"},{type:"textarea",name:"message",label:"Message",required:!1,placeholder:"Your message..."}],action:"",method:"POST"},editableFields:[{key:"title",type:"text",label:"Title"},{key:"description",type:"textarea",label:"Description"},{key:"backgroundColor",type:"color",label:"Background Colour"},{key:"layout",type:"select",label:"Layout",options:["stacked","inline","two-column"]},{key:"submitText",type:"text",label:"Submit Button Text"},{key:"fields",type:"formFields",label:"Form Fields"},{key:"action",type:"text",label:"Form Action URL"},{key:"method",type:"select",label:"Method",options:["GET","POST"]}],template:e=>{const t=e.backgroundColor?`background-color: ${e.backgroundColor};`:"",n="inline"===e.layout?"form-inline":"two-column"===e.layout?"grid grid-cols-2 gap-4":"",a=e.fields.map(e=>{const t=e.required?"required":"",n=e.required?' <span class="text-danger">*</span>':"";return"textarea"===e.type?` <div class="form-group">\n <label for="${e.name}">${e.label}${n}</label>\n <textarea class="form-input" id="${e.name}" name="${e.name}" placeholder="${e.placeholder||""}" rows="4" ${t}></textarea>\n </div>`:` <div class="form-group">\n <label for="${e.name}">${e.label}${n}</label>\n <input type="${e.type}" class="form-input" id="${e.name}" name="${e.name}" placeholder="${e.placeholder||""}" ${t}>\n </div>`}).join("\n");return`<section class="py-12" style="${t}">\n <div class="container">\n <div class="max-w-lg mx-auto">\n <div class="text-center mb-6">\n <h2 class="text-3xl font-bold mb-2">${e.title}</h2>\n ${e.description?`<p class="text-muted">${e.description}</p>`:""}\n </div>\n <form action="${e.action}" method="${e.method}" class="${n}">\n${a}\n <div class="form-group">\n <button type="submit" class="btn btn-primary btn-block">${e.submitText}</button>\n </div>\n </form>\n </div>\n </div>\n</section>`}},footer:{name:"Footer",icon:"layout",category:"footers",description:"Page footer with copyright, links, and social icons",defaults:{style:"simple",backgroundColor:"#1f2937",textColor:"#e9ecef",copyright:"© 2025 My Company. All rights reserved.",showNavLinks:!0,navLinks:[{text:"Home",url:"#"},{text:"About",url:"#"},{text:"Services",url:"#"},{text:"Contact",url:"#"}],columns:[{title:"Company",links:[{text:"About Us",url:"#"},{text:"Careers",url:"#"},{text:"Contact",url:"#"}]},{title:"Resources",links:[{text:"Blog",url:"#"},{text:"Documentation",url:"#"},{text:"Support",url:"#"}]}],showSocial:!0,socialLinks:[{platform:"twitter",url:"#"},{platform:"facebook",url:"#"},{platform:"instagram",url:"#"}]},editableFields:[{key:"style",type:"select",label:"Style",options:["simple","columns","centered"]},{key:"backgroundColor",type:"color",label:"Background"},{key:"textColor",type:"color",label:"Text Colour"},{key:"copyright",type:"text",label:"Copyright Text"},{key:"showNavLinks",type:"toggle",label:"Show Nav Links"},{key:"navLinks",type:"links",label:"Navigation Links",showWhen:{showNavLinks:!0}},{key:"columns",type:"footerColumns",label:"Columns",showWhen:{style:"columns"}},{key:"showSocial",type:"toggle",label:"Show Social Icons"},{key:"socialLinks",type:"socialLinks",label:"Social Links",showWhen:{showSocial:!0}}],template:e=>{const t=`background-color: ${e.backgroundColor}; color: ${e.textColor};`,n=e.showSocial?`\n <div class="footer-social">\n ${e.socialLinks.map(e=>`<a href="${e.url}" class="social-link" aria-label="${e.platform}"><span data-icon="${e.platform}" data-icon-size="20"></span></a>`).join("\n ")}\n </div>`:"";if("simple"===e.style){return`<footer class="footer py-6" style="${t}">\n <div class="container text-center">\n ${e.showNavLinks?`\n <nav class="footer-nav mb-3">\n ${e.navLinks.map(e=>`<a href="${e.url}">${e.text}</a>`).join(" · ")}\n </nav>`:""}\n ${n}\n <p class="footer-copyright mt-3">${e.copyright}</p>\n </div>\n</footer>`}if("columns"===e.style){const a=e.columns.map(e=>`\n <div class="footer-column">\n <h4 class="footer-column-title">${e.title}</h4>\n <ul class="footer-links">\n ${e.links.map(e=>`<li><a href="${e.url}">${e.text}</a></li>`).join("\n ")}\n </ul>\n </div>`).join("");return`<footer class="footer py-8" style="${t}">\n <div class="container">\n <div class="grid grid-cols-${e.columns.length+1} gap-6">\n <div class="footer-brand">\n <h3 class="text-xl font-bold mb-3">My Company</h3>\n <p class="text-sm opacity-75">Building amazing things.</p>\n ${n}\n </div>\n ${a}\n </div>\n <div class="footer-bottom mt-6 pt-4" style="border-top: 1px solid rgba(255,255,255,0.1);">\n <p class="text-center text-sm opacity-75">${e.copyright}</p>\n </div>\n </div>\n</footer>`}return`<footer class="footer py-8" style="${t}">\n <div class="container text-center">\n ${n}\n <p class="footer-copyright mt-4">${e.copyright}</p>\n </div>\n</footer>`}},row:{name:"Row Layout",icon:"grid",category:"layout",description:"Container for multi-column layouts with nested sections",defaults:{layout:"equal-2",columns:[{width:.5},{width:.5}],spacing:{padding:{top:3,right:3,bottom:3,left:3},margin:{top:0,right:0,bottom:0,left:0},gutter:4,locked:!1},height:{type:"auto",value:null,equalize:!1},alignment:{horizontal:"start",vertical:"start"},responsive:{mobile:"stack",tablet:"keep",desktop:"keep"},background:{type:"none",color:"",gradient:"",image:""}},editableFields:[{key:"layout",type:"layout-preset",label:"Layout Preset"},{key:"columns",type:"column-widths",label:"Column Widths"},{key:"spacing",type:"spacing-controls",label:"Spacing"},{key:"height",type:"height-controls",label:"Height"},{key:"alignment",type:"alignment-controls",label:"Alignment"},{key:"responsive",type:"responsive-controls",label:"Responsive"},{key:"background",type:"background-controls",label:"Background"}],template:e=>'<section class="pr-row" data-row-placeholder="true">Row Layout (placeholder)</section>'},carousel:{name:"Carousel",icon:"image",category:"interactive",description:"Image/content carousel with autoplay and navigation",defaults:{slides:[{image:"https://via.placeholder.com/800x400",caption:"Slide 1",content:"First slide content"},{image:"https://via.placeholder.com/800x400",caption:"Slide 2",content:"Second slide content"},{image:"https://via.placeholder.com/800x400",caption:"Slide 3",content:"Third slide content"}],autoplay:!0,interval:5e3,pauseOnHover:!0,loop:!0,animation:"slide",showArrows:!0,showIndicators:!0,height:400},editableFields:[{key:"slides",type:"slides",label:"Slides"},{key:"autoplay",type:"toggle",label:"Autoplay"},{key:"interval",type:"number",label:"Interval (ms)",showWhen:{autoplay:!0}},{key:"pauseOnHover",type:"toggle",label:"Pause on Hover"},{key:"loop",type:"toggle",label:"Loop"},{key:"animation",type:"select",label:"Animation",options:["slide","fade"]},{key:"showArrows",type:"toggle",label:"Show Arrows"},{key:"showIndicators",type:"toggle",label:"Show Indicators"},{key:"height",type:"number",label:"Height (px)"}],template:e=>{const t=e.slides.map((t,n)=>`\n <div class="carousel-slide ${0===n?"active":""}" style="height: ${e.height}px;">\n ${t.image?`<img src="${t.image}" alt="${t.caption}" style="width: 100%; height: 100%; object-fit: cover;">`:""}\n ${t.caption||t.content?`\n <div class="carousel-caption" style="position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); color: white; text-align: center; background: rgba(0,0,0,0.6); padding: 1rem 2rem; border-radius: 8px;">\n ${t.caption?`<h3 style="margin: 0 0 0.5rem 0;">${t.caption}</h3>`:""}\n ${t.content?`<p style="margin: 0;">${t.content}</p>`:""}\n </div>\n `:""}\n </div>`).join("");return`<section class="py-6" data-component="carousel">\n <div class="container">\n <div class="carousel" data-autoplay="${e.autoplay}" data-interval="${e.interval}" data-pause-hover="${e.pauseOnHover}" data-loop="${e.loop}" data-animation="${e.animation}" style="position: relative; overflow: hidden;">\n ${t}\n ${e.showArrows?'\n <button class="carousel-prev" style="position: absolute; left: 10px; top: 50%; transform: translateY(-50%); background: rgba(0,0,0,0.5); color: white; border: none; padding: 1rem; cursor: pointer; font-size: 1.5rem;">‹</button>\n <button class="carousel-next" style="position: absolute; right: 10px; top: 50%; transform: translateY(-50%); background: rgba(0,0,0,0.5); color: white; border: none; padding: 1rem; cursor: pointer; font-size: 1.5rem;">›</button>\n ':""}\n ${e.showIndicators?`\n <div class="carousel-indicators" style="position: absolute; bottom: 10px; left: 50%; transform: translateX(-50%); display: flex; gap: 0.5rem;">\n ${e.slides.map((e,t)=>`<button data-slide="${t}" class="${0===t?"active":""}" style="width: 12px; height: 12px; border-radius: 50%; border: 2px solid white; background: ${0===t?"white":"transparent"}; cursor: pointer;"></button>`).join("")}\n </div>\n `:""}\n </div>\n </div>\n</section>`}},accordion:{name:"Accordion",icon:"chevron-down",category:"interactive",description:"Expandable FAQ or content blocks",defaults:{title:"Frequently Asked Questions",items:[{title:"What is your return policy?",content:"We offer a 30-day money-back guarantee on all products."},{title:"How long does shipping take?",content:"Standard shipping takes 5-7 business days."},{title:"Do you ship internationally?",content:"Yes, we ship to most countries worldwide."}],allowMultiple:!1,activeIndex:0,animation:!0,style:"default"},editableFields:[{key:"title",type:"text",label:"Section Title"},{key:"items",type:"accordionItems",label:"Items"},{key:"allowMultiple",type:"toggle",label:"Allow Multiple Open"},{key:"activeIndex",type:"number",label:"Initially Active (index)"},{key:"animation",type:"toggle",label:"Animated"},{key:"style",type:"select",label:"Style",options:["default","bordered","minimal"]}],template:e=>{const t=e.items.map((t,n)=>`\n <div class="accordion-item ${n===e.activeIndex?"active":""}" style="border: 1px solid var(--dm-border, #dee2e6); margin-bottom: 0.5rem; border-radius: 4px;">\n <button class="accordion-header" style="width: 100%; padding: 1rem; text-align: left; background: var(--dm-surface, #fff); border: none; cursor: pointer; display: flex; justify-content: space-between; align-items: center; font-weight: 500;">\n ${t.title}\n <span class="accordion-icon" style="transition: transform 0.3s; ${n===e.activeIndex?"transform: rotate(180deg);":""}">▼</span>\n </button>\n <div class="accordion-content" style="padding: ${n===e.activeIndex?"1rem":"0 1rem"}; max-height: ${n===e.activeIndex?"500px":"0"}; overflow: hidden; transition: all 0.3s;">\n ${t.content}\n </div>\n </div>`).join("");return`<section class="py-12" data-component="accordion">\n <div class="container">\n ${e.title?`<h2 class="text-3xl font-bold mb-6 text-center">${e.title}</h2>`:""}\n <div class="accordion accordion-${e.style}" data-allow-multiple="${e.allowMultiple}" data-animation="${e.animation}" style="max-width: 800px; margin: 0 auto;">\n ${t}\n </div>\n </div>\n</section>`}},tabs:{name:"Tabs",icon:"menu",category:"interactive",description:"Tabbed content sections",defaults:{tabs:[{label:"Overview",content:"<p>Overview content goes here.</p>"},{label:"Features",content:"<p>Feature details go here.</p>"},{label:"Pricing",content:"<p>Pricing information goes here.</p>"}],activeIndex:0,style:"default",animation:"fade"},editableFields:[{key:"tabs",type:"tabItems",label:"Tabs"},{key:"activeIndex",type:"number",label:"Initially Active (index)"},{key:"style",type:"select",label:"Style",options:["default","pills","underline"]},{key:"animation",type:"select",label:"Animation",options:["none","fade","slide"]}],template:e=>{const t=e.tabs.map((t,n)=>`\n <button class="tab-button ${n===e.activeIndex?"active":""}" data-tab="${n}" style="padding: 0.75rem 1.5rem; border: 1px solid var(--dm-border, #dee2e6); background: ${n===e.activeIndex?"var(--dm-primary, #6495ED)":"transparent"}; color: ${n===e.activeIndex?"white":"inherit"}; cursor: pointer; border-radius: 4px 4px 0 0; margin-right: 0.25rem;">\n ${t.label}\n </button>`).join(""),n=e.tabs.map((t,n)=>`\n <div class="tab-panel ${n===e.activeIndex?"active":""}" data-panel="${n}" style="display: ${n===e.activeIndex?"block":"none"}; padding: 2rem; border: 1px solid var(--dm-border, #dee2e6); border-top: none;">\n ${t.content}\n </div>`).join("");return`<section class="py-12" data-component="tabs">\n <div class="container">\n <div class="tabs tabs-${e.style}" data-animation="${e.animation}">\n <div class="tabs-nav" style="display: flex; border-bottom: 2px solid var(--dm-border, #dee2e6);">\n ${t}\n </div>\n <div class="tabs-content">\n ${n}\n </div>\n </div>\n </div>\n</section>`}},modal:{name:"Modal",icon:"box",category:"interactive",description:"Popup modal trigger with content",defaults:{triggerText:"Open Modal",triggerStyle:"primary",modalTitle:"Modal Title",modalContent:"<p>Modal content goes here.</p>",backdrop:!0,keyboard:!0,animation:!0},editableFields:[{key:"triggerText",type:"text",label:"Button Text"},{key:"triggerStyle",type:"select",label:"Button Style",options:["primary","secondary","outline"]},{key:"modalTitle",type:"text",label:"Modal Title"},{key:"modalContent",type:"richtext",label:"Modal Content"},{key:"backdrop",type:"toggle",label:"Show Backdrop"},{key:"keyboard",type:"toggle",label:"Close on ESC"},{key:"animation",type:"toggle",label:"Animated"}],template:e=>`<section class="py-12 text-center" data-component="modal">\n <div class="container">\n <button class="${"primary"===e.triggerStyle?"btn btn-primary":"secondary"===e.triggerStyle?"btn btn-secondary":"btn btn-outline"}" data-modal-trigger="modal-1">${e.triggerText}</button>\n <div class="modal" id="modal-1" data-backdrop="${e.backdrop}" data-keyboard="${e.keyboard}" data-animation="${e.animation}" style="display: none; position: fixed; inset: 0; z-index: 1000; background: rgba(0,0,0,0.5); align-items: center; justify-content: center;">\n <div class="modal-content" style="background: white; padding: 2rem; border-radius: 8px; max-width: 600px; width: 90%; position: relative;">\n <button class="modal-close" style="position: absolute; top: 1rem; right: 1rem; background: none; border: none; font-size: 1.5rem; cursor: pointer;">×</button>\n <h2 class="modal-title" style="margin-bottom: 1rem;">${e.modalTitle}</h2>\n <div class="modal-body">${e.modalContent}</div>\n </div>\n </div>\n </div>\n</section>`},toast:{name:"Toast",icon:"document",category:"interactive",description:"Notification toast trigger",defaults:{triggerText:"Show Notification",toastMessage:"This is a toast notification!",toastType:"info",position:"top-right",duration:3e3},editableFields:[{key:"triggerText",type:"text",label:"Button Text"},{key:"toastMessage",type:"textarea",label:"Toast Message"},{key:"toastType",type:"select",label:"Type",options:["info","success","warning","error"]},{key:"position",type:"select",label:"Position",options:["top-left","top-center","top-right","bottom-left","bottom-center","bottom-right"]},{key:"duration",type:"number",label:"Duration (ms)"}],template:e=>`<section class="py-12 text-center" data-component="toast">\n <div class="container">\n <button class="btn btn-primary" data-toast-trigger data-toast-message="${e.toastMessage}" data-toast-type="${e.toastType}" data-toast-position="${e.position}" data-toast-duration="${e.duration}">\n ${e.triggerText}\n </button>\n </div>\n</section>`},breadcrumbs:{name:"Breadcrumbs",icon:"chevron-right",category:"interactive",description:"Navigation breadcrumb trail",defaults:{items:[{text:"Home",url:"#"},{text:"Products",url:"#"},{text:"Category",url:"#"},{text:"Current Page",url:""}],separator:"/",homeIcon:!0},editableFields:[{key:"items",type:"breadcrumbItems",label:"Items"},{key:"separator",type:"select",label:"Separator",options:["/",">","→","chevron"]},{key:"homeIcon",type:"toggle",label:"Show Home Icon"}],template:e=>`<section class="py-6" data-component="breadcrumbs">\n <div class="container">\n <nav class="breadcrumbs" style="display: flex; align-items: center; font-size: 0.875rem;">\n ${e.items.map((t,n)=>{const a=n===e.items.length-1,i="chevron"===e.separator?'<span data-icon="chevron-right" data-icon-size="14"></span>':e.separator;return`\n ${0===n&&e.homeIcon?'<span data-icon="home" data-icon-size="16" style="margin-right: 0.5rem;"></span>':""}\n ${t.url&&!a?`<a href="${t.url}" style="color: var(--dm-primary, #6495ED); text-decoration: none;">${t.text}</a>`:`<span style="color: ${a?"var(--dm-text, #212529)":"var(--dm-text-muted, #6c757d)"};">${t.text}</span>`}\n ${a?"":`<span style="margin: 0 0.5rem; color: var(--dm-text-muted, #6c757d);">${i}</span>`}`}).join("")}\n </nav>\n </div>\n</section>`},buttonGroup:{name:"Button Group",icon:"grid",category:"interactive",description:"Radio or checkbox button group",defaults:{title:"Select an Option",mode:"single",buttons:[{label:"Option 1",value:"opt1"},{label:"Option 2",value:"opt2"},{label:"Option 3",value:"opt3"}],activeIndex:0,vertical:!1},editableFields:[{key:"title",type:"text",label:"Title"},{key:"mode",type:"select",label:"Mode",options:["single","multiple"]},{key:"buttons",type:"buttonGroupItems",label:"Buttons"},{key:"activeIndex",type:"number",label:"Initially Active (index)"},{key:"vertical",type:"toggle",label:"Vertical Layout"}],template:e=>{const t=e.buttons.map((t,n)=>`\n <button class="btn-group-item ${n===e.activeIndex?"active":""}" data-value="${t.value}" style="padding: 0.75rem 1.5rem; border: 1px solid var(--dm-border, #dee2e6); background: ${n===e.activeIndex?"var(--dm-primary, #6495ED)":"transparent"}; color: ${n===e.activeIndex?"white":"inherit"}; cursor: pointer;">\n ${t.label}\n </button>`).join("");return`<section class="py-12" data-component="button-group">\n <div class="container">\n ${e.title?`<h3 class="text-xl font-semibold mb-4">${e.title}</h3>`:""}\n <div class="btn-group ${e.vertical?"btn-group-vertical":""}" data-mode="${e.mode}" style="display: flex; ${e.vertical?"flex-direction: column;":""} gap: ${e.vertical?"0.5rem":"0"};">\n ${t}\n </div>\n </div>\n</section>`}},tagCloud:{name:"Tag Cloud",icon:"star",category:"interactive",description:"Badge/tag collection display",defaults:{title:"Tags",tags:[{text:"JavaScript",color:"primary"},{text:"React",color:"success"},{text:"Node.js",color:"info"},{text:"CSS",color:"warning"},{text:"HTML",color:"danger"}],size:"medium",pill:!0},editableFields:[{key:"title",type:"text",label:"Title"},{key:"tags",type:"tagItems",label:"Tags"},{key:"size",type:"select",label:"Size",options:["small","medium","large"]},{key:"pill",type:"toggle",label:"Pill Style"}],template:e=>{const t="small"===e.size?"text-sm":"large"===e.size?"text-lg":"",n=e.tags.map(n=>{const a={primary:"var(--dm-primary, #6495ED)",success:"var(--dm-success, #28a745)",info:"var(--dm-info, #17a2b8)",warning:"var(--dm-warning, #ffc107)",danger:"var(--dm-danger, #dc3545)"},i=a[n.color]||a.primary;return`<span class="badge ${t}" style="display: inline-block; padding: 0.375rem 0.75rem; background: ${i}; color: white; border-radius: ${e.pill?"50px":"4px"}; margin: 0.25rem; font-size: ${"small"===e.size?"0.75rem":"large"===e.size?"1rem":"0.875rem"};">\n ${n.text}\n </span>`}).join("");return`<section class="py-12" data-component="tag-cloud">\n <div class="container">\n ${e.title?`<h3 class="text-xl font-semibold mb-4">${e.title}</h3>`:""}\n <div class="tag-cloud" style="display: flex; flex-wrap: wrap; gap: 0.5rem;">\n ${n}\n </div>\n </div>\n</section>`}},dropdown:{name:"Dropdown",icon:"chevron-down",category:"interactive",description:"Dropdown menu with items",defaults:{triggerText:"Menu",triggerStyle:"primary",items:[{text:"Action 1",url:"#"},{text:"Action 2",url:"#"},{text:"divider",url:""},{text:"Action 3",url:"#"}],position:"bottom"},editableFields:[{key:"triggerText",type:"text",label:"Button Text"},{key:"triggerStyle",type:"select",label:"Button Style",options:["primary","secondary","outline"]},{key:"items",type:"dropdownItems",label:"Items"},{key:"position",type:"select",label:"Position",options:["bottom","top","left","right"]}],template:e=>{const t="primary"===e.triggerStyle?"btn btn-primary":"secondary"===e.triggerStyle?"btn btn-secondary":"btn btn-outline",n=e.items.map(e=>"divider"===e.text?'<div style="height: 1px; background: var(--dm-border, #dee2e6); margin: 0.5rem 0;"></div>':`<a href="${e.url}" style="display: block; padding: 0.5rem 1rem; color: var(--dm-text, #212529); text-decoration: none; transition: background 0.2s;" onmouseover="this.style.background='var(--dm-hover-bg, #f8f9fa)'" onmouseout="this.style.background='transparent'">${e.text}</a>`).join("");return`<section class="py-12 text-center" data-component="dropdown">\n <div class="container">\n <div class="dropdown" data-position="${e.position}" style="position: relative; display: inline-block;">\n <button class="${t} dropdown-trigger" style="display: flex; align-items: center; gap: 0.5rem;">\n ${e.triggerText}\n <span data-icon="chevron-down" data-icon-size="14"></span>\n </button>\n <div class="dropdown-menu" style="display: none; position: absolute; top: 100%; left: 0; margin-top: 0.5rem; background: white; border: 1px solid var(--dm-border, #dee2e6); border-radius: 4px; box-shadow: var(--dm-shadow-md); min-width: 200px; z-index: 1000;">\n ${n}\n </div>\n </div>\n </div>\n</section>`}}},h={single:{columns:[1],label:"1 Column"},"equal-2":{columns:[.5,.5],label:"1/2 + 1/2"},"third-twothirds":{columns:[.333,.667],label:"1/3 + 2/3"},"twothirds-third":{columns:[.667,.333],label:"2/3 + 1/3"},"quarter-threequarters":{columns:[.25,.75],label:"1/4 + 3/4"},"threequarters-quarter":{columns:[.75,.25],label:"3/4 + 1/4"},"equal-3":{columns:[.333,.333,.333],label:"1/3 × 3"},"quarter-half-quarter":{columns:[.25,.5,.25],label:"1/4 + 1/2 + 1/4"},"equal-4":{columns:[.25,.25,.25,.25],label:"1/4 × 4"},"fifth-fourfifths":{columns:[.2,.8],label:"1/5 + 4/5"},"fourfifths-fifth":{columns:[.8,.2],label:"4/5 + 1/5"},"equal-5":{columns:[.2,.2,.2,.2,.2],label:"1/5 × 5"},"sixth-fivesixths":{columns:[.167,.833],label:"1/6 + 5/6"},"fivesixths-sixth":{columns:[.833,.167],label:"5/6 + 1/6"},"equal-6":{columns:[.167,.167,.167,.167,.167,.167],label:"1/6 × 6"},"sidebar-main":{columns:[.3,.7],label:"Sidebar + Main"},"main-sidebar":{columns:[.7,.3],label:"Main + Sidebar"},"narrow-wide-narrow":{columns:[.2,.6,.2],label:"1/5 + 3/5 + 1/5"},"wide-narrow-wide":{columns:[.4,.2,.4],label:"2/5 + 1/5 + 2/5"},"third-sixth-half":{columns:[.333,.167,.5],label:"1/3 + 1/6 + 1/2"},"asymmetric-3":{columns:[.5,.3,.2],label:"1/2 + 3/10 + 1/5"},"asymmetric-4":{columns:[.4,.3,.2,.1],label:"2/5 + 3/10 + 1/5 + 1/10"}};class u{static STORAGE_KEY="page-roller-templates";static ACTIVE_KEY="page-roller-active";static list(){const e=r.get(this.STORAGE_KEY,{});return Object.keys(e)}static getAll(){return r.get(this.STORAGE_KEY,{})}static load(e){return r.get(this.STORAGE_KEY,{})[e]||null}static save(e,t){const n=r.get(this.STORAGE_KEY,{});return t.name=e,t.modified=(new Date).toISOString(),t.created||(t.created=t.modified),n[e]=t,r.set(this.STORAGE_KEY,n)}static delete(e){const t=r.get(this.STORAGE_KEY,{});return!!t[e]&&(delete t[e],r.set(this.STORAGE_KEY,t))}static rename(e,t){const n=r.get(this.STORAGE_KEY,{});return!(!n[e]||n[t])&&(n[t]={...n[e],name:t,modified:(new Date).toISOString()},delete n[e],r.set(this.STORAGE_KEY,n))}static saveActive(e){r.set(this.ACTIVE_KEY,e)}static loadActive(){return r.get(this.ACTIVE_KEY,null)}static clearActive(){r.remove(this.ACTIVE_KEY)}}class g{static defaults={template:null,theme:"light",themeVariant:null,useGrid:!0,useTheme:!0,useThemeRoller:!1,splitView:!0,autoSave:!0,autoSaveInterval:3e4,onChange:null,onSave:null,onExport:null};constructor(e,t={}){this.element="string"==typeof e?document.querySelector(e):e,this.element?(this.options={...g.defaults,...t},this._sections=[],this._selectedIndex=-1,this._pageConfig={theme:this.options.theme,variant:this.options.themeVariant,useGrid:this.options.useGrid,useTheme:this.options.useTheme,customCSS:"",meta:{title:"My Page",description:"",charset:"UTF-8",viewport:"width=device-width, initial-scale=1.0"}},this._templateName="",this._isDirty=!1,this._autoSaveTimer=null,this._eventHandlers=[],this._init()):console.error("PageRoller: Container element not found")}_init(){if(this.options.template)this.loadTemplate(this.options.template);else{const e=u.loadActive();e&&this._loadPageData(e)}this._render(),this._bindEvents(),"undefined"!=typeof Domma&&Domma.icons&&Domma.icons.scan(this.element),this._refreshPreview(),this.options.autoSave&&(this._autoSaveTimer=setInterval(()=>{this.saveToStorage()},this.options.autoSaveInterval))}_render(){this.element.classList.add("qr-container"),this.element.innerHTML=this._buildHTML(),this._refs={header:this.element.querySelector(".qr-header"),templateName:this.element.querySelector(".qr-template-name"),library:this.element.querySelector(".qr-library"),canvas:this.element.querySelector(".qr-canvas"),editor:this.element.querySelector(".qr-editor"),preview:this.element.querySelector(".qr-preview-frame"),actions:this.element.querySelector(".qr-actions")}}_buildHTML(){const e=Object.entries(m).map(([e,t])=>`\n <div class="qr-library-item" data-section-type="${e}" draggable="true" title="${t.description}">\n <span class="qr-library-icon" data-icon="${t.icon}" data-icon-size="20"></span>\n <span class="qr-library-name">${t.name}</span>\n </div>\n `).join(""),t=`\n <option value="">No Theme</option>\n <option value="light" ${"light"===this._pageConfig.theme?"selected":""}>Light</option>\n <option value="dark" ${"dark"===this._pageConfig.theme?"selected":""}>Dark</option>\n `;return`\n <div class="qr-header">\n <div class="qr-header-left">\n <button class="qr-btn qr-btn-icon" data-action="new" title="New Page">\n <span data-icon="document-add" data-icon-size="18"></span>\n </button>\n <button class="qr-btn qr-btn-icon" data-action="save" title="Save Template">\n <span data-icon="save" data-icon-size="18"></span>\n </button>\n <div class="qr-dropdown">\n <button class="qr-btn qr-btn-icon" data-action="load" title="Load Template">\n <span data-icon="folder-open" data-icon-size="18"></span>\n </button>\n <div class="qr-dropdown-menu qr-templates-menu"></div>\n </div>\n <input type="text" class="qr-template-name" placeholder="Untitled Page" value="${this._escapeHtml(this._templateName)}">\n </div>\n <div class="qr-header-center">\n <span class="qr-title">Page Roller</span>\n </div>\n <div class="qr-header-right">\n <label class="qr-label">Theme:</label>\n <select class="qr-select qr-theme-select">${t}</select>\n <select class="qr-select qr-variant-select">\n <option value="">Default</option>\n <option value="ocean">Ocean</option>\n <option value="forest">Forest</option>\n <option value="sunset">Sunset</option>\n <option value="royal">Royal</option>\n <option value="lemon">Lemon</option>\n <option value="silver">Silver</option>\n <option value="charcoal">Charcoal</option>\n </select>\n <label class="qr-checkbox">\n <input type="checkbox" class="qr-grid-toggle" ${this._pageConfig.useGrid?"checked":""}>\n <span>Grid</span>\n </label>\n </div>\n </div>\n\n <div class="qr-body">\n <div class="qr-sidebar">\n <div class="qr-library">\n <div class="qr-section-title">Sections</div>\n ${e}\n </div>\n <div class="qr-editor">\n <div class="qr-section-title">Properties</div>\n <div class="qr-editor-content">\n <p class="qr-editor-placeholder">Select a section to edit its properties</p>\n </div>\n </div>\n </div>\n\n <div class="qr-main">\n <div class="qr-canvas-container">\n <div class="qr-canvas">\n <div class="qr-drop-zone qr-drop-zone-empty" data-position="0">\n <span data-icon="plus" data-icon-size="24"></span>\n <span>Click a section or drag here to add</span>\n </div>\n </div>\n </div>\n ${this.options.splitView?'\n <div class="qr-preview-container">\n <div class="qr-preview-header">\n <span>Preview</span>\n <button class="qr-btn qr-btn-sm" data-action="preview-full" title="Open in new window">\n <span data-icon="external-link" data-icon-size="14"></span>\n </button>\n </div>\n <iframe class="qr-preview-frame" sandbox="allow-same-origin"></iframe>\n </div>\n ':""}\n </div>\n </div>\n\n <div class="qr-actions">\n <button class="qr-btn" data-action="copy">\n <span data-icon="copy" data-icon-size="16"></span>\n Copy HTML\n </button>\n <button class="qr-btn" data-action="download">\n <span data-icon="download" data-icon-size="16"></span>\n Download\n </button>\n <button class="qr-btn" data-action="save-storage">\n <span data-icon="database" data-icon-size="16"></span>\n Save to Browser\n </button>\n <button class="qr-btn qr-btn-primary" data-action="preview-full">\n <span data-icon="eye" data-icon-size="16"></span>\n Full Preview\n </button>\n </div>\n `}_bindEvents(){this._on(this.element,"click","[data-action]",e=>{const t=e.target.closest("[data-action]").dataset.action;this._handleAction(t)}),this._on(this._refs.templateName,"input",null,e=>{this._templateName=e.target.value,this._markDirty()}),this._on(this.element,"change",".qr-theme-select",e=>{this._pageConfig.theme=e.target.value||null,this._markDirty(),this._refreshPreview()}),this._on(this.element,"change",".qr-variant-select",e=>{this._pageConfig.variant=e.target.value||null,this._markDirty(),this._refreshPreview()}),this._on(this.element,"change",".qr-grid-toggle",e=>{this._pageConfig.useGrid=e.target.checked,this._markDirty(),this._refreshPreview()}),this._on(this._refs.library,"click",".qr-library-item",e=>{const t=e.target.closest(".qr-library-item").dataset.sectionType;this.addSection(t)}),this._on(this._refs.library,"dragstart",".qr-library-item",e=>{e.dataTransfer.setData("section-type",e.target.closest(".qr-library-item").dataset.sectionType),e.dataTransfer.effectAllowed="copy",this._dragSource="library"}),this._on(this._refs.canvas,"click",".qr-section",e=>{if(e.target.closest(".qr-section-controls"))return;const t=parseInt(e.target.closest(".qr-section").dataset.index);this._selectSection(t)}),this._on(this._refs.canvas,"click",".qr-section-controls button",e=>{const t=e.target.closest("button"),n=t.closest(".qr-section"),a=parseInt(n.dataset.index),i=t.dataset.action;"move-up"===i?this.moveSection(a,a-1):"move-down"===i?this.moveSection(a,a+1):"delete"===i?this.removeSection(a):"duplicate"===i&&this._duplicateSection(a)}),this._on(this._refs.canvas,"dragstart",".qr-section",e=>{e.dataTransfer.setData("section-index",e.target.closest(".qr-section").dataset.index),e.dataTransfer.effectAllowed="move",this._dragSource="canvas",e.target.closest(".qr-section").classList.add("qr-dragging")}),this._on(this._refs.canvas,"dragend",".qr-section",e=>{e.target.closest(".qr-section").classList.remove("qr-dragging")}),this._on(this._refs.canvas,"dragover",".qr-drop-zone",e=>{e.preventDefault(),e.target.closest(".qr-drop-zone").classList.add("qr-drop-active")}),this._on(this._refs.canvas,"dragleave",".qr-drop-zone",e=>{e.target.closest(".qr-drop-zone").classList.remove("qr-drop-active")}),this._on(this._refs.canvas,"drop",".qr-drop-zone",e=>{e.preventDefault();const t=e.target.closest(".qr-drop-zone");t.classList.remove("qr-drop-active");const n=parseInt(t.dataset.position);if("library"===this._dragSource){const t=e.dataTransfer.getData("section-type");this.addSection(t,n)}else{const t=parseInt(e.dataTransfer.getData("section-index"));this.moveSection(t,n)}}),this._on(this._refs.editor,"input","input, textarea, select",e=>{this._handleEditorChange(e)}),this._on(this._refs.editor,"change",'input[type="checkbox"], select',e=>{this._handleEditorChange(e)}),this._on(this._refs.editor,"click","button",e=>{const t=e.target.closest("button"),n=t.dataset.action;n?this._handleEditorAction(n,t):t.dataset.field&&this._handleEditorChange(e)}),this._on(this.element,"click",".qr-templates-menu .qr-menu-item",e=>{const t=e.target.closest(".qr-menu-item").dataset.template;t&&this.loadTemplate(t)})}_on(e,t,n,a){const i=t=>{if(n){const i=t.target.closest(n);i&&e.contains(i)&&a.call(i,t)}else a.call(e,t)};e.addEventListener(t,i),this._eventHandlers.push({element:e,event:t,wrapper:i})}async _handleAction(e){switch(e){case"new":this.newPage();break;case"save":this._showSaveDialog();break;case"load":this._toggleTemplatesMenu();break;case"copy":await this.copyToClipboard();break;case"download":this.download();break;case"save-storage":this.saveToStorage();break;case"preview-full":this.openPreviewWindow()}}_handleEditorChange(e){if(this._selectedIndex<0)return;const t=e.target,n=t.closest("[data-field]");if(!n)return;const a=n.dataset.field||t.dataset.field;if(!a)return;let i;if("BUTTON"===t.tagName&&t.dataset.value)i=t.dataset.value;else if("checkbox"===t.type)i=t.checked;else if("number"===t.type)i=parseFloat(t.value)||0;else if("range"===t.type&&t.classList.contains("qr-width-slider"))i=parseFloat(t.value);else if("number"===t.type&&t.classList.contains("qr-width-number"))i=parseFloat(t.value)/100;else if("TEXTAREA"===t.tagName&&t.classList.contains("qr-json"))try{i=JSON.parse(t.value)}catch(e){return void console.warn("Invalid JSON in field:",a,e)}else i=t.value;const s=this._sections[this._selectedIndex];if(a.includes(".")){const e=a.split(".");let t=s.config;for(let n=0;n<e.length-1;n++){const a=e[n],i=e[n+1];isNaN(i)?t=t[a]:(t=t[a][parseInt(i)],n++)}t[e[e.length-1]]=i}else s.config[a]=i;if("layout"===a&&h[i]){const e=h[i];s.config.columns=e.columns.map(e=>({width:e}))}this._markDirty(),this._refreshPreview(),this._renderCanvasSections(),"background.type"!==a&&"height.type"!==a||this._renderEditor()}_handleEditorAction(e,t){if(this._selectedIndex<0)return;const n=this._sections[this._selectedIndex];if("row"===n.type){switch(e){case"add-column":const e=1/(n.config.columns.length+1);n.config.columns.push({width:e}),n.config.columns.forEach(t=>{t.width=e});break;case"remove-column":if(n.config.columns.length>1){n.config.columns.pop();const e=1/n.config.columns.length;n.config.columns.forEach(t=>{t.width=e}),n.children&&(n.children=n.children.filter(e=>e.columnIndex<n.config.columns.length))}break;case"equalize-columns":const t=1/n.config.columns.length;n.config.columns.forEach(e=>{e.width=t});break;case"toggle-lock":n.config.spacing.locked=!n.config.spacing.locked}this._markDirty(),this._renderEditor(),this._refreshPreview(),this._renderCanvasSections()}}_markDirty(){this._isDirty=!0,this.options.onChange&&this.options.onChange(this._getPageData())}_selectSection(e){this._selectedIndex=e,this._refs.canvas.querySelectorAll(".qr-section").forEach((t,n)=>{t.classList.toggle("qr-selected",n===e)}),this._renderEditor()}_renderEditor(){const e=this._refs.editor.querySelector(".qr-editor-content");if(this._selectedIndex<0||!this._sections[this._selectedIndex])return void(e.innerHTML='<p class="qr-editor-placeholder">Select a section to edit its properties</p>');const t=this._sections[this._selectedIndex],n=m[t.type];if(!n)return void(e.innerHTML='<p class="qr-editor-placeholder">Unknown section type</p>');let a="";for(const e of n.editableFields){if(e.showWhen){if(!Object.entries(e.showWhen).every(([e,n])=>Array.isArray(n)?n.includes(t.config[e]):t.config[e]===n))continue}a+=this._renderEditorField(e,t.config[e.key])}e.innerHTML=`\n <div class="qr-editor-section-header">\n <span data-icon="${n.icon}" data-icon-size="18"></span>\n <span>${n.name}</span>\n </div>\n <div class="qr-editor-fields">\n ${a}\n </div>\n `,"undefined"!=typeof Domma&&Domma.icons&&Domma.icons.scan(e)}_renderEditorField(e,t){const n=this._escapeHtml(String(t??""));switch(e.type){case"text":return`\n <div class="qr-field" data-field="${e.key}">\n <label class="qr-field-label">${e.label}</label>\n <input type="text" class="qr-input" value="${n}">\n </div>\n `;case"textarea":return`\n <div class="qr-field" data-field="${e.key}">\n <label class="qr-field-label">${e.label}</label>\n <textarea class="qr-input qr-textarea">${n}</textarea>\n </div>\n `;case"richtext":return`\n <div class="qr-field" data-field="${e.key}">\n <label class="qr-field-label">${e.label}</label>\n <textarea class="qr-input qr-textarea qr-richtext">${n}</textarea>\n </div>\n `;case"number":return`\n <div class="qr-field" data-field="${e.key}">\n <label class="qr-field-label">${e.label}</label>\n <input type="number" class="qr-input" value="${t}" min="${e.min??""}" max="${e.max??""}">\n </div>\n `;case"color":return`\n <div class="qr-field" data-field="${e.key}">\n <label class="qr-field-label">${e.label}</label>\n <div class="qr-color-wrapper">\n <input type="color" class="qr-color" value="${n}">\n <input type="text" class="qr-input qr-color-text" value="${n}">\n </div>\n </div>\n `;case"select":const a=e.options.map(e=>`<option value="${e}" ${t===e?"selected":""}>${this._capitalize(e)}</option>`).join("");return`\n <div class="qr-field" data-field="${e.key}">\n <label class="qr-field-label">${e.label}</label>\n <select class="qr-select">${a}</select>\n </div>\n `;case"toggle":return`\n <div class="qr-field" data-field="${e.key}">\n <label class="qr-checkbox">\n <input type="checkbox" ${t?"checked":""}>\n <span>${e.label}</span>\n </label>\n </div>\n `;case"buttons":case"cards":case"formFields":case"links":case"footerColumns":case"socialLinks":case"slides":case"accordionItems":case"tabItems":case"breadcrumbItems":case"buttonGroupItems":case"tagItems":case"dropdownItems":return`\n <div class="qr-field" data-field="${e.key}">\n <label class="qr-field-label">${e.label}</label>\n <p class="qr-field-note" style="font-size: 0.875rem; color: var(--dm-text-muted, #6c757d); margin: 0.25rem 0;">Edit in JSON format</p>\n <textarea class="qr-input qr-textarea qr-json" rows="12" style="font-family: monospace; font-size: 0.875rem;">${JSON.stringify(t,null,2)}</textarea>\n </div>\n `;case"layout-preset":const i=Object.entries(h).map(([e,n])=>{const a=e===t,i=n.columns.map(e=>`<div style="flex: ${e}; background: ${a?"var(--dm-primary, #6495ED)":"var(--dm-gray-300, #dee2e6)"}; height: 100%; border-radius: 2px;"></div>`).join("");return`\n <button class="qr-preset-btn ${a?"active":""}"\n type="button"\n data-preset="${e}"\n data-field="layout"\n title="${n.label}">\n <div class="qr-preset-visual" style="display: flex; gap: 2px; height: 30px; margin-bottom: 4px;">\n ${i}\n </div>\n <span class="qr-preset-label" style="font-size: 0.75rem; display: block;">${n.label}</span>\n </button>\n `}).join("");return`\n <div class="qr-field" data-field="${e.key}">\n <label class="qr-field-label">${e.label}</label>\n <div class="qr-preset-grid" style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.5rem; margin-top: 0.5rem;">\n ${i}\n </div>\n </div>\n `;case"column-widths":const s=t.map((e,t)=>`\n <div class="qr-width-control" style="margin-bottom: 0.75rem;">\n <label style="display: block; font-size: 0.875rem; margin-bottom: 0.25rem;">Column ${t+1}</label>\n <div style="display: flex; gap: 0.5rem; align-items: center;">\n <input type="range"\n min="0.1"\n max="1"\n step="0.01"\n value="${e.width}"\n data-col-idx="${t}"\n data-field="columns.${t}.width"\n class="qr-width-slider"\n style="flex: 1;">\n <input type="number"\n min="10"\n max="100"\n value="${(100*e.width).toFixed(0)}"\n data-col-idx="${t}"\n data-field="columns.${t}.width"\n class="qr-width-number"\n style="width: 60px;">\n <span>%</span>\n </div>\n </div>\n `).join("");return`\n <div class="qr-field" data-field="${e.key}">\n <label class="qr-field-label">${e.label}</label>\n ${s}\n <div class="qr-width-actions" style="display: flex; gap: 0.5rem; margin-top: 0.5rem;">\n <button type="button" class="qr-btn qr-btn-sm qr-btn-equalize" data-action="equalize-columns">Equalize</button>\n <button type="button" class="qr-btn qr-btn-sm qr-btn-add-col" data-action="add-column">+ Column</button>\n ${t.length>1?'<button type="button" class="qr-btn qr-btn-sm qr-btn-remove-col" data-action="remove-column">- Column</button>':""}\n </div>\n </div>\n `;case"spacing-controls":return`\n <div class="qr-field" data-field="${e.key}">\n <label class="qr-field-label">${e.label}</label>\n\n <div class="qr-spacing-section" style="margin-bottom: 1rem;">\n <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.5rem;">\n <h4 style="margin: 0; font-size: 0.875rem;">Padding</h4>\n <button type="button"\n class="qr-chainlink ${t.locked?"locked":""}"\n data-action="toggle-lock"\n data-field="spacing.locked"\n title="${t.locked?"Unlock":"Lock"} sides"\n style="background: none; border: 1px solid var(--dm-border, #dee2e6); border-radius: 4px; padding: 4px 8px; cursor: pointer;">\n ${t.locked?"🔒":"🔓"}\n </button>\n </div>\n <div class="qr-spacing-inputs" style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.5rem;">\n <input type="number" data-field="spacing.padding.top" value="${t.padding.top}" placeholder="Top" class="qr-input" style="width: 100%;">\n <input type="number" data-field="spacing.padding.right" value="${t.padding.right}" placeholder="Right" class="qr-input" style="width: 100%;">\n <input type="number" data-field="spacing.padding.bottom" value="${t.padding.bottom}" placeholder="Bottom" class="qr-input" style="width: 100%;">\n <input type="number" data-field="spacing.padding.left" value="${t.padding.left}" placeholder="Left" class="qr-input" style="width: 100%;">\n </div>\n </div>\n\n <div class="qr-spacing-section" style="margin-bottom: 1rem;">\n <h4 style="margin: 0 0 0.5rem 0; font-size: 0.875rem;">Margin</h4>\n <div class="qr-spacing-inputs" style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.5rem;">\n <input type="number" data-field="spacing.margin.top" value="${t.margin.top}" placeholder="Top" class="qr-input" style="width: 100%;">\n <input type="number" data-field="spacing.margin.right" value="${t.margin.right}" placeholder="Right" class="qr-input" style="width: 100%;">\n <input type="number" data-field="spacing.margin.bottom" value="${t.margin.bottom}" placeholder="Bottom" class="qr-input" style="width: 100%;">\n <input type="number" data-field="spacing.margin.left" value="${t.margin.left}" placeholder="Left" class="qr-input" style="width: 100%;">\n </div>\n </div>\n\n <div class="qr-spacing-section">\n <h4 style="margin: 0 0 0.5rem 0; font-size: 0.875rem;">Gutter (Column Gap)</h4>\n <div style="display: flex; gap: 0.5rem; align-items: center;">\n <input type="range"\n min="0"\n max="10"\n step="0.5"\n value="${t.gutter}"\n data-field="spacing.gutter"\n class="qr-gutter-slider"\n style="flex: 1;">\n <span class="qr-gutter-value" style="min-width: 50px;">${t.gutter}rem</span>\n </div>\n </div>\n </div>\n `;case"height-controls":return`\n <div class="qr-field" data-field="${e.key}">\n <label class="qr-field-label">${e.label}</label>\n <select class="qr-select" data-field="height.type" style="margin-bottom: 0.5rem;">\n <option value="auto" ${"auto"===t.type?"selected":""}>Auto</option>\n <option value="min" ${"min"===t.type?"selected":""}>Minimum</option>\n <option value="max" ${"max"===t.type?"selected":""}>Maximum</option>\n <option value="fixed" ${"fixed"===t.type?"selected":""}>Fixed</option>\n </select>\n ${"auto"!==t.type?`\n <div style="display: flex; gap: 0.5rem; align-items: center; margin-bottom: 0.5rem;">\n <input type="number"\n class="qr-input qr-height-value"\n data-field="height.value"\n value="${t.value||300}"\n min="0"\n step="10"\n style="flex: 1;">\n <span>px</span>\n </div>\n `:""}\n <label class="qr-checkbox">\n <input type="checkbox"\n ${t.equalize?"checked":""}\n data-field="height.equalize">\n <span>Equalize Column Heights</span>\n </label>\n </div>\n `;case"alignment-controls":const o=t.horizontal||"start",r=t.vertical||"start";return`\n <div class="qr-field" data-field="${e.key}">\n <label class="qr-field-label">${e.label}</label>\n\n <div class="qr-align-section" style="margin-bottom: 1rem;">\n <label style="display: block; font-size: 0.875rem; margin-bottom: 0.5rem;">Horizontal</label>\n <div class="qr-btn-group" style="display: flex; gap: 0.25rem;">\n ${["start","center","end","stretch"].map(e=>`\n <button type="button"\n class="qr-align-btn ${o===e?"active":""}"\n data-field="alignment.horizontal"\n data-value="${e}"\n style="flex: 1; padding: 0.5rem; border: 1px solid var(--dm-border, #dee2e6); background: ${o===e?"var(--dm-primary, #6495ED)":"var(--dm-surface, #fff)"}; color: ${o===e?"white":"inherit"}; border-radius: 4px; cursor: pointer;">\n ${e}\n </button>\n `).join("")}\n </div>\n </div>\n\n <div class="qr-align-section">\n <label style="display: block; font-size: 0.875rem; margin-bottom: 0.5rem;">Vertical</label>\n <div class="qr-btn-group" style="display: flex; gap: 0.25rem;">\n ${["start","center","end","stretch"].map(e=>`\n <button type="button"\n class="qr-align-btn ${r===e?"active":""}"\n data-field="alignment.vertical"\n data-value="${e}"\n style="flex: 1; padding: 0.5rem; border: 1px solid var(--dm-border, #dee2e6); background: ${r===e?"var(--dm-primary, #6495ED)":"var(--dm-surface, #fff)"}; color: ${r===e?"white":"inherit"}; border-radius: 4px; cursor: pointer;">\n ${e}\n </button>\n `).join("")}\n </div>\n </div>\n </div>\n `;case"responsive-controls":return`\n <div class="qr-field" data-field="${e.key}">\n <label class="qr-field-label">${e.label}</label>\n\n <div class="qr-responsive-section" style="margin-bottom: 0.75rem;">\n <label style="display: block; font-size: 0.875rem; margin-bottom: 0.25rem;">Mobile (<576px)</label>\n <select data-field="responsive.mobile" class="qr-select">\n <option value="stack" ${"stack"===t.mobile?"selected":""}>Stack</option>\n <option value="keep" ${"keep"===t.mobile?"selected":""}>Keep Layout</option>\n <option value="hide" ${"hide"===t.mobile?"selected":""}>Hide</option>\n </select>\n </div>\n\n <div class="qr-responsive-section" style="margin-bottom: 0.75rem;">\n <label style="display: block; font-size: 0.875rem; margin-bottom: 0.25rem;">Tablet (576-768px)</label>\n <select data-field="responsive.tablet" class="qr-select">\n <option value="stack" ${"stack"===t.tablet?"selected":""}>Stack</option>\n <option value="keep" ${"keep"===t.tablet?"selected":""}>Keep Layout</option>\n <option value="hide" ${"hide"===t.tablet?"selected":""}>Hide</option>\n </select>\n </div>\n\n <div class="qr-responsive-section">\n <label style="display: block; font-size: 0.875rem; margin-bottom: 0.25rem;">Desktop (>768px)</label>\n <select data-field="responsive.desktop" class="qr-select">\n <option value="keep" ${"keep"===t.desktop?"selected":""}>Keep Layout</option>\n <option value="hide" ${"hide"===t.desktop?"selected":""}>Hide</option>\n </select>\n </div>\n </div>\n `;case"background-controls":return`\n <div class="qr-field" data-field="${e.key}">\n <label class="qr-field-label">${e.label}</label>\n <select class="qr-select qr-bg-type" data-field="background.type" style="margin-bottom: 0.5rem;">\n <option value="none" ${"none"===t.type?"selected":""}>None</option>\n <option value="color" ${"color"===t.type?"selected":""}>Solid Colour</option>\n <option value="gradient" ${"gradient"===t.type?"selected":""}>Gradient</option>\n <option value="image" ${"image"===t.type?"selected":""}>Image</option>\n </select>\n ${"color"===t.type?`\n <input type="color"\n class="qr-bg-color"\n data-field="background.color"\n value="${t.color||"#ffffff"}"\n style="width: 100%; height: 40px;">\n `:""}\n ${"gradient"===t.type?`\n <input type="text"\n class="qr-input qr-bg-gradient"\n data-field="background.gradient"\n value="${this._escapeHtml(t.gradient||"")}"\n placeholder="linear-gradient(...)">\n `:""}\n ${"image"===t.type?`\n <input type="text"\n class="qr-input qr-bg-image"\n data-field="background.image"\n value="${this._escapeHtml(t.image||"")}"\n placeholder="Image URL">\n `:""}\n </div>\n `;default:return""}}_renderCanvasSections(){let e="";0===this._sections.length?e='\n <div class="qr-drop-zone qr-drop-zone-empty" data-position="0">\n <span data-icon="plus" data-icon-size="24"></span>\n <span>Click a section or drag here to add</span>\n </div>\n ':(e='<div class="qr-drop-zone" data-position="0"></div>',this._sections.forEach((t,n)=>{const a=m[t.type],i=n===this._selectedIndex;e+=`\n <div class="qr-section ${i?"qr-selected":""}"\n data-index="${n}"\n data-type="${t.type}"\n draggable="true">\n <div class="qr-section-header">\n <span data-icon="${a?.icon||"box"}" data-icon-size="16"></span>\n <span class="qr-section-name">${a?.name||t.type}</span>\n </div>\n <div class="qr-section-preview">\n ${this._getSectionPreviewLabel(t)}\n </div>\n <div class="qr-section-controls">\n <button data-action="move-up" title="Move Up" ${0===n?"disabled":""}>\n <span data-icon="chevron-up" data-icon-size="14"></span>\n </button>\n <button data-action="move-down" title="Move Down" ${n===this._sections.length-1?"disabled":""}>\n <span data-icon="chevron-down" data-icon-size="14"></span>\n </button>\n <button data-action="duplicate" title="Duplicate">\n <span data-icon="copy" data-icon-size="14"></span>\n </button>\n <button data-action="delete" title="Delete">\n <span data-icon="trash" data-icon-size="14"></span>\n </button>\n </div>\n </div>\n <div class="qr-drop-zone" data-position="${n+1}"></div>\n `})),this._refs.canvas.innerHTML=e,"undefined"!=typeof Domma&&Domma.icons&&Domma.icons.scan(this._refs.canvas)}_getSectionPreviewLabel(e){const t=e.config;switch(e.type){case"hero":return this._escapeHtml(t.title||"Untitled Hero");case"cardGrid":return`${t.cards?.length||0} cards, ${t.columns} columns`;case"form":return this._escapeHtml(t.title||"Untitled Form");case"footer":return`${this._capitalize(t.style)} style`;case"content":return this._escapeHtml(t.title||"Content Block");case"navbar":return this._escapeHtml(t.brandText||"Navigation");default:return e.type}}_refreshPreview(){if(!this._refs.preview)return;const e=this._generatePreviewHTML();this._refs.preview.srcdoc=e}_generatePreviewHTML(){const e=getComputedStyle(document.documentElement),t=["--dm-primary","--dm-primary-hover","--dm-secondary","--dm-success","--dm-danger","--dm-warning","--dm-info","--dm-text","--dm-text-muted","--dm-background","--dm-surface","--dm-border","--dm-hover-bg","--dm-gray-100","--dm-gray-200","--dm-gray-300","--dm-gray-400","--dm-gray-500","--dm-gray-600","--dm-gray-700","--dm-gray-800","--dm-gray-900","--dm-radius-sm","--dm-radius-md","--dm-radius-lg","--dm-radius-xl","--dm-radius-full","--dm-shadow-sm","--dm-shadow-md","--dm-shadow-lg","--dm-shadow-xl","--dm-font-sans","--dm-font-mono","--dm-text-xs","--dm-text-sm","--dm-text-base","--dm-text-lg","--dm-text-xl","--dm-text-2xl","--dm-text-3xl","--dm-transition-fast","--dm-transition-normal"];let n=":root {\n";for(const a of t){const t=e.getPropertyValue(a);t&&(n+=` ${a}: ${t.trim()};\n`)}n+="}\n";const a=`\n ${n}\n * { box-sizing: border-box; margin: 0; padding: 0; }\n body {\n font-family: var(--dm-font-sans, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);\n color: var(--dm-text, #212529);\n background: var(--dm-background, #fff);\n line-height: 1.5;\n }\n .container { max-width: 1200px; margin: 0 auto; padding: 0 1rem; }\n .text-center { text-align: center; }\n .text-left { text-align: left; }\n .text-right { text-align: right; }\n .text-muted { color: var(--dm-text-muted, #6c757d); }\n .mb-2 { margin-bottom: 0.5rem; }\n .mb-3 { margin-bottom: 0.75rem; }\n .mb-4 { margin-bottom: 1rem; }\n .mb-6 { margin-bottom: 1.5rem; }\n .mb-8 { margin-bottom: 2rem; }\n .mt-3 { margin-top: 0.75rem; }\n .mt-4 { margin-top: 1rem; }\n .mt-6 { margin-top: 1.5rem; }\n .py-6 { padding-top: 1.5rem; padding-bottom: 1.5rem; }\n .py-8 { padding-top: 2rem; padding-bottom: 2rem; }\n .py-12 { padding-top: 3rem; padding-bottom: 3rem; }\n .py-16 { padding-top: 4rem; padding-bottom: 4rem; }\n .pt-4 { padding-top: 1rem; }\n .text-sm { font-size: 0.875rem; }\n .text-xl { font-size: 1.25rem; }\n .text-2xl { font-size: 1.5rem; }\n .text-3xl { font-size: 1.875rem; }\n .font-bold { font-weight: 700; }\n .font-semibold { font-weight: 600; }\n .max-w-lg { max-width: 32rem; }\n .max-w-3xl { max-width: 48rem; }\n .mx-auto { margin-left: auto; margin-right: auto; }\n .opacity-75 { opacity: 0.75; }\n\n /* Grid */\n .grid { display: grid; }\n .grid-cols-1 { grid-template-columns: repeat(1, 1fr); }\n .grid-cols-2 { grid-template-columns: repeat(2, 1fr); }\n .grid-cols-3 { grid-template-columns: repeat(3, 1fr); }\n .grid-cols-4 { grid-template-columns: repeat(4, 1fr); }\n .grid-cols-5 { grid-template-columns: repeat(5, 1fr); }\n .grid-cols-6 { grid-template-columns: repeat(6, 1fr); }\n .gap-2 { gap: 0.5rem; }\n .gap-4 { gap: 1rem; }\n .gap-6 { gap: 1.5rem; }\n\n /* Row/Col */\n .row { display: flex; flex-wrap: wrap; gap: 1rem; }\n .col-6 { flex: 0 0 calc(50% - 0.5rem); }\n .align-center { align-items: center; }\n\n /* Hero */\n .hero { position: relative; }\n .hero-title { font-size: 2.5rem; font-weight: 700; margin-bottom: 1rem; }\n .hero-subtitle { font-size: 1.125rem; opacity: 0.9; max-width: 600px; margin: 0 auto; }\n .hero-buttons { display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap; }\n\n /* Buttons */\n .btn {\n display: inline-flex; align-items: center; justify-content: center;\n padding: 0.625rem 1.25rem; border-radius: var(--dm-radius-md, 0.375rem);\n font-weight: 500; text-decoration: none; cursor: pointer;\n border: 1px solid transparent; transition: all 0.15s ease;\n }\n .btn-primary { background: var(--dm-primary, #6495ED); color: white; }\n .btn-primary:hover { background: var(--dm-primary-hover, #5280d8); }\n .btn-secondary { background: var(--dm-secondary, #6c757d); color: white; }\n .btn-outline { background: transparent; border-color: currentColor; }\n .btn-link { background: none; border: none; color: var(--dm-primary, #6495ED); padding: 0; }\n .btn-block { width: 100%; }\n .btn-sm { padding: 0.375rem 0.75rem; font-size: 0.875rem; }\n\n /* Cards */\n .card {\n background: var(--dm-surface, #fff);\n border: 1px solid var(--dm-border, #dee2e6);\n border-radius: var(--dm-radius-lg, 0.5rem);\n overflow: hidden;\n }\n .card-bordered { border-width: 2px; }\n .card-shadow { box-shadow: var(--dm-shadow-md); }\n .card-minimal { border: none; background: transparent; }\n .card-body { padding: 1.25rem; }\n .card-title { font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; }\n .card-text { color: var(--dm-text-muted, #6c757d); font-size: 0.875rem; }\n .card-img-top { width: 100%; height: auto; }\n .card-icon { color: var(--dm-primary, #6495ED); }\n\n /* Forms */\n .form-group { margin-bottom: 1rem; }\n .form-group label { display: block; margin-bottom: 0.375rem; font-weight: 500; }\n .form-input {\n width: 100%; padding: 0.625rem 0.875rem;\n border: 1px solid var(--dm-border, #dee2e6);\n border-radius: var(--dm-radius-md, 0.375rem);\n font-size: 1rem; font-family: inherit;\n background: var(--dm-background, #fff);\n color: var(--dm-text, #212529);\n }\n .form-input:focus { outline: none; border-color: var(--dm-primary, #6495ED); }\n textarea.form-input { resize: vertical; min-height: 100px; }\n .text-danger { color: var(--dm-danger, #dc3545); }\n\n /* Footer */\n .footer { color: inherit; }\n .footer a { color: inherit; text-decoration: none; opacity: 0.8; }\n .footer a:hover { opacity: 1; }\n .footer-nav { display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap; }\n .footer-social { display: flex; gap: 1rem; justify-content: center; margin-bottom: 1rem; }\n .footer-copyright { opacity: 0.7; font-size: 0.875rem; }\n .footer-column-title { font-weight: 600; margin-bottom: 0.75rem; }\n .footer-links { list-style: none; }\n .footer-links li { margin-bottom: 0.5rem; }\n .footer-bottom { border-top: 1px solid rgba(255,255,255,0.1); }\n\n /* Navbar */\n .navbar {\n display: flex; align-items: center; padding: 1rem 0;\n background: var(--dm-surface, #fff);\n }\n .navbar-dark { background: var(--dm-gray-800, #343a40); color: white; }\n .navbar-light { background: var(--dm-surface, #fff); color: var(--dm-text, #212529); }\n .navbar-transparent { background: transparent; }\n .navbar-sticky { position: sticky; top: 0; z-index: 100; }\n .navbar .container { display: flex; align-items: center; justify-content: space-between; width: 100%; }\n .navbar-brand { font-weight: 600; font-size: 1.25rem; text-decoration: none; color: inherit; }\n .navbar-nav { display: flex; gap: 1.5rem; }\n .nav-link { text-decoration: none; color: inherit; opacity: 0.8; }\n .nav-link:hover { opacity: 1; }\n .navbar-actions { display: flex; gap: 0.5rem; }\n\n /* Images */\n .img-fluid { max-width: 100%; height: auto; }\n .rounded { border-radius: var(--dm-radius-lg, 0.5rem); }\n\n /* Content */\n .content p { margin-bottom: 1rem; }\n .content h1, .content h2, .content h3 { margin-bottom: 0.75rem; }\n `,i=[];this._pageConfig.theme&&i.push(`dm-theme-${this._pageConfig.theme}`),this._pageConfig.variant&&i.push(`dm-theme-${this._pageConfig.variant}`);let s="";for(const e of this._sections){const t=m[e.type];t?.template&&(s+=t.template(e.config),s+="\n")}s||(s='\n <div style="display: flex; align-items: center; justify-content: center; height: 100vh; color: var(--dm-text-muted);">\n <div style="text-align: center;">\n <p style="font-size: 1.25rem; margin-bottom: 0.5rem;">No sections yet</p>\n <p style="font-size: 0.875rem;">Click a section from the library to add it</p>\n </div>\n </div>\n ');let o="";const r=document.querySelector('link[href*="domma-themes"]');return r&&(o=new URL(r.getAttribute("href"),window.location.href).href),`<!DOCTYPE html>\n<html lang="en">\n<head>\n <meta charset="UTF-8">\n <meta name="viewport" content="width=device-width, initial-scale=1.0">\n <title>Preview</title>\n ${o?`<link rel="stylesheet" href="${o}">`:""}\n <style>${a}</style>\n</head>\n<body${i.length?` class="${i.join(" ")}"`:""}>\n${s}\n</body>\n</html>`}_renderSectionHTML(e){if("row"===e.type)return this._renderRowHTML(e);const t=m[e.type];return t?.template(e.config)||""}_renderRowHTML(e){const t=e.config,n=e.children||[],a=["display: grid",`grid-template-columns: ${t.columns.map(e=>`${(100*e.width).toFixed(2)}%`).join(" ")}`,`gap: ${t.spacing.gutter}rem`,`padding: ${t.spacing.padding.top}rem ${t.spacing.padding.right}rem ${t.spacing.padding.bottom}rem ${t.spacing.padding.left}rem`,`margin: ${t.spacing.margin.top}rem ${t.spacing.margin.right}rem ${t.spacing.margin.bottom}rem ${t.spacing.margin.left}rem`,"min"===t.height.type?`min-height: ${t.height.value}px`:"","max"===t.height.type?`max-height: ${t.height.value}px`:"","fixed"===t.height.type?`height: ${t.height.value}px`:"",`align-items: ${t.alignment.vertical}`,`justify-items: ${t.alignment.horizontal}`,"color"===t.background.type?`background-color: ${t.background.color}`:"","gradient"===t.background.type?`background: ${t.background.gradient}`:"","image"===t.background.type?`background-image: url('${t.background.image}'); background-size: cover; background-position: center`:""].filter(Boolean).join("; ");let i="pr-row";"stack"===t.responsive.mobile&&(i+=" pr-row-stack-mobile"),"stack"===t.responsive.tablet&&(i+=" pr-row-stack-tablet"),"hide"!==t.responsive.mobile&&"hide"!==t.responsive.tablet&&"hide"!==t.responsive.desktop||(i+=" pr-row-responsive");const s=t.columns.map((e,t)=>n.filter(e=>e.columnIndex===t)).map((e,t)=>` <div class="pr-column" data-column="${t}">\n${e.map(e=>this._renderSectionHTML(e.section)).join("\n")||" \x3c!-- Empty column --\x3e"}\n </div>`).join("\n");return`<section class="${i}" style="${a}"${"hide"===t.responsive.mobile?' data-hide-mobile="true"':""}${"hide"===t.responsive.tablet?' data-hide-tablet="true"':""}${"hide"===t.responsive.desktop?' data-hide-desktop="true"':""}>\n${s}\n</section>`}_showSaveDialog(){const e=prompt("Enter template name:",this._templateName||"My Template");e&&this.saveTemplate(e)}_toggleTemplatesMenu(){const e=this.element.querySelector(".qr-templates-menu"),t=u.list();0===t.length?e.innerHTML='<div class="qr-menu-empty">No saved templates</div>':e.innerHTML=t.map(e=>`\n <div class="qr-menu-item" data-template="${this._escapeHtml(e)}">\n <span data-icon="document" data-icon-size="14"></span>\n <span>${this._escapeHtml(e)}</span>\n </div>\n `).join(""),e.classList.toggle("qr-visible"),"undefined"!=typeof Domma&&Domma.icons&&Domma.icons.scan(e)}_duplicateSection(e){if(e<0||e>=this._sections.length)return;const t=this._sections[e],n={type:t.type,config:JSON.parse(JSON.stringify(t.config))};this._sections.splice(e+1,0,n),this._markDirty(),this._renderCanvasSections(),this._refreshPreview(),this._selectSection(e+1)}_getPageData(){return{version:2,name:this._templateName,config:{...this._pageConfig},sections:this._sections.map(e=>this._serializeSection(e))}}_serializeSection(e){const t={type:e.type,config:{...e.config}};return"row"===e.type&&e.children&&(t.children=e.children.map(e=>({columnIndex:e.columnIndex,section:this._serializeSection(e.section)}))),t}_loadPageData(e){if(!e)return;const t=this._migrateV1ToV2(e);this._templateName=t.name||"",this._pageConfig={...this._pageConfig,...t.config},this._sections=(t.sections||[]).map(e=>this._deserializeSection(e)),this._selectedIndex=-1}_migrateV1ToV2(e){return 2===e.version?e:{version:2,name:e.name||"Untitled",config:e.config||{},sections:e.sections?e.sections.map(e=>({type:"row",config:{...m.row.defaults,layout:"single",columns:[{width:1}]},children:[{columnIndex:0,section:e}]})):[]}}_deserializeSection(e){const t={type:e.type,config:{...m[e.type]?.defaults||{},...e.config}};return"row"===e.type&&e.children&&(t.children=e.children.map(e=>({columnIndex:e.columnIndex,section:this._deserializeSection(e.section)}))),t}_escapeHtml(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}_capitalize(e){return e.charAt(0).toUpperCase()+e.slice(1)}_showToast(e,t="info"){"undefined"!=typeof Domma&&Domma.elements?.toast?Domma.elements.toast[t]?.(e,{position:"bottom-center",duration:3e3})||Domma.elements.toast.show(e):console.log(`[PageRoller] ${e}`)}addSection(e,t){const n=m[e];if(!n)return console.error(`PageRoller: Unknown section type "${e}"`),this;const a={type:e,config:{...n.defaults}};return"number"==typeof t&&t>=0&&t<=this._sections.length?(this._sections.splice(t,0,a),this._selectedIndex=t):(this._sections.push(a),this._selectedIndex=this._sections.length-1),this._markDirty(),this._renderCanvasSections(),this._renderEditor(),this._refreshPreview(),this}removeSection(e){return e<0||e>=this._sections.length||(this._sections.splice(e,1),this._selectedIndex===e?this._selectedIndex=-1:this._selectedIndex>e&&this._selectedIndex--,this._markDirty(),this._renderCanvasSections(),this._renderEditor(),this._refreshPreview()),this}moveSection(e,t){if(e<0||e>=this._sections.length)return this;if(t<0||t>this._sections.length)return this;if(e===t)return this;const[n]=this._sections.splice(e,1),a=t>e?t-1:t;return this._sections.splice(a,0,n),this._selectedIndex===e&&(this._selectedIndex=a),this._markDirty(),this._renderCanvasSections(),this._refreshPreview(),this}updateSection(e,t){return e<0||e>=this._sections.length||(this._sections[e].config={...this._sections[e].config,...t},this._markDirty(),this._refreshPreview(),e===this._selectedIndex&&this._renderEditor()),this}getSection(e){return this._sections[e]||null}getSections(){return[...this._sections]}setTheme(e,t){this._pageConfig.theme=e,void 0!==t&&(this._pageConfig.variant=t);const n=this.element.querySelector(".qr-theme-select"),a=this.element.querySelector(".qr-variant-select");return n&&(n.value=e||""),a&&(a.value=t||""),this._markDirty(),this._refreshPreview(),this}setGridEnabled(e){this._pageConfig.useGrid=e;const t=this.element.querySelector(".qr-grid-toggle");return t&&(t.checked=e),this._markDirty(),this._refreshPreview(),this}getPageConfig(){return{...this._pageConfig}}exportHTML(e={}){const{minify:t=!1,includeComments:n=!0,standalone:a=!0}=e;let i="";if(a){i+="<!DOCTYPE html>\n",i+='<html lang="en">\n',i+="<head>\n",i+=` <meta charset="${this._pageConfig.meta.charset}">\n`,i+=` <meta name="viewport" content="${this._pageConfig.meta.viewport}">\n`,i+=` <title>${this._escapeHtml(this._pageConfig.meta.title)}</title>\n`,this._pageConfig.meta.description&&(i+=` <meta name="description" content="${this._escapeHtml(this._pageConfig.meta.description)}">\n`),this._pageConfig.useTheme&&(i+=' <link rel="stylesheet" href="domma-themes.css">\n'),this._pageConfig.useGrid&&(i+=' <link rel="stylesheet" href="domma.css">\n'),this._pageConfig.customCSS&&(i+=" <style>\n",i+=this._pageConfig.customCSS,i+="\n </style>\n"),i+="</head>\n";const e=[];this._pageConfig.theme&&e.push(`dm-theme-${this._pageConfig.theme}`),this._pageConfig.variant&&e.push(`dm-theme-${this._pageConfig.variant}`),i+=`<body${e.length?` class="${e.join(" ")}"`:""}>\n`}for(const e of this._sections){const t=m[e.type];t&&(n&&(i+=`\n\x3c!-- ${t.name} Section --\x3e\n`),i+=this._renderSectionHTML(e),i+="\n")}return a&&(i+='\n <script src="domma.min.js"><\/script>\n',i+=" <script>\n",i+=" // Initialise Domma\n",i+=' document.addEventListener("DOMContentLoaded", function() {\n',i+=" // Scan and initialise icons\n",i+=" if (window.Domma && Domma.icons) Domma.icons.scan();\n",i+="\n",i+=" // Auto-init carousels\n",i+=" document.querySelectorAll(\"[data-component='carousel']\").forEach(function(el) {\n",i+=' var carousel = el.querySelector(".carousel");\n',i+=" if (carousel && Domma.elements && Domma.elements.carousel) {\n",i+=" Domma.elements.carousel(carousel, {\n",i+=' autoplay: carousel.dataset.autoplay === "true",\n',i+=" interval: parseInt(carousel.dataset.interval) || 5000,\n",i+=' pauseOnHover: carousel.dataset.pauseHover === "true",\n',i+=' loop: carousel.dataset.loop === "true",\n',i+=' animation: carousel.dataset.animation || "slide"\n',i+=" });\n",i+=" }\n",i+=" });\n",i+="\n",i+=" // Auto-init accordions\n",i+=" document.querySelectorAll(\"[data-component='accordion']\").forEach(function(el) {\n",i+=' var accordion = el.querySelector(".accordion");\n',i+=" if (accordion && Domma.elements && Domma.elements.accordion) {\n",i+=" Domma.elements.accordion(accordion, {\n",i+=' allowMultiple: accordion.dataset.allowMultiple === "true",\n',i+=' animation: accordion.dataset.animation !== "false"\n',i+=" });\n",i+=" }\n",i+=" });\n",i+="\n",i+=" // Auto-init tabs\n",i+=" document.querySelectorAll(\"[data-component='tabs']\").forEach(function(el) {\n",i+=' var tabs = el.querySelector(".tabs");\n',i+=" if (tabs && Domma.elements && Domma.elements.tabs) {\n",i+=" Domma.elements.tabs(tabs, {\n",i+=' animation: tabs.dataset.animation || "fade"\n',i+=" });\n",i+=" }\n",i+=" });\n",i+="\n",i+=" // Auto-init modals\n",i+=" document.querySelectorAll(\"[data-component='modal']\").forEach(function(el) {\n",i+=' var modal = el.querySelector(".modal");\n',i+=' var trigger = el.querySelector("[data-modal-trigger]");\n',i+=" if (modal && trigger && Domma.elements && Domma.elements.modal) {\n",i+=" var instance = Domma.elements.modal(modal, {\n",i+=' backdrop: modal.dataset.backdrop !== "false",\n',i+=' keyboard: modal.dataset.keyboard !== "false",\n',i+=' animation: modal.dataset.animation !== "false"\n',i+=" });\n",i+=' trigger.addEventListener("click", function() { instance.open(); });\n',i+=" }\n",i+=" });\n",i+="\n",i+=" // Auto-init toast triggers\n",i+=' document.querySelectorAll("[data-toast-trigger]").forEach(function(trigger) {\n',i+=' trigger.addEventListener("click", function() {\n',i+=" if (Domma.elements && Domma.elements.toast) {\n",i+=" Domma.elements.toast({\n",i+=' message: trigger.dataset.toastMessage || "Notification",\n',i+=' type: trigger.dataset.toastType || "info",\n',i+=' position: trigger.dataset.toastPosition || "top-right",\n',i+=" duration: parseInt(trigger.dataset.toastDuration) || 3000\n",i+=" });\n",i+=" }\n",i+=" });\n",i+=" });\n",i+="\n",i+=" // Auto-init breadcrumbs\n",i+=" document.querySelectorAll(\"[data-component='breadcrumbs']\").forEach(function(el) {\n",i+=' var breadcrumbs = el.querySelector(".breadcrumbs");\n',i+=" if (breadcrumbs && Domma.elements && Domma.elements.breadcrumbs) {\n",i+=" Domma.elements.breadcrumbs(breadcrumbs);\n",i+=" }\n",i+=" });\n",i+="\n",i+=" // Auto-init button groups\n",i+=" document.querySelectorAll(\"[data-component='button-group']\").forEach(function(el) {\n",i+=' var btnGroup = el.querySelector(".btn-group");\n',i+=" if (btnGroup && Domma.elements && Domma.elements.buttonGroup) {\n",i+=" Domma.elements.buttonGroup(btnGroup, {\n",i+=' mode: btnGroup.dataset.mode || "single"\n',i+=" });\n",i+=" }\n",i+=" });\n",i+="\n",i+=" // Auto-init dropdowns\n",i+=" document.querySelectorAll(\"[data-component='dropdown']\").forEach(function(el) {\n",i+=' var dropdown = el.querySelector(".dropdown");\n',i+=" if (dropdown && Domma.elements && Domma.elements.dropdown) {\n",i+=" Domma.elements.dropdown(dropdown, {\n",i+=' position: dropdown.dataset.position || "bottom"\n',i+=" });\n",i+=" }\n",i+=" });\n",i+=" });\n",i+=" <\/script>\n",i+="</body>\n",i+="</html>\n"),t?i.replace(/\n\s*/g,""):i}async copyToClipboard(){const e=this.exportHTML({standalone:!0});return await l.copyToClipboard(e),this._showToast("HTML copied to clipboard","success"),this.options.onExport&&this.options.onExport({type:"copy",html:e}),this}download(e){const t=e||this._templateName||"page",n=this.exportHTML({standalone:!0}),a=new Blob([n],{type:"text/html"}),i=URL.createObjectURL(a),s=document.createElement("a");return s.href=i,s.download=`${t}.html`,document.body.appendChild(s),s.click(),document.body.removeChild(s),URL.revokeObjectURL(i),this._showToast("Page downloaded","success"),this.options.onExport&&this.options.onExport({type:"download",filename:t,html:n}),this}saveToStorage(){return u.saveActive(this._getPageData()),this._isDirty=!1,this._showToast("Page saved to browser","success"),this}saveTemplate(e){return this._templateName=e,this._refs.templateName.value=e,u.save(e,this._getPageData()),this._isDirty=!1,this._showToast(`Template "${e}" saved`,"success"),this.options.onSave&&this.options.onSave({name:e,data:this._getPageData()}),this}loadTemplate(e){const t=u.load(e);if(!t)return this._showToast(`Template "${e}" not found`,"error"),this;this._loadPageData(t),this._refs.templateName.value=this._templateName;const n=this.element.querySelector(".qr-theme-select"),a=this.element.querySelector(".qr-variant-select"),i=this.element.querySelector(".qr-grid-toggle");return n&&(n.value=this._pageConfig.theme||""),a&&(a.value=this._pageConfig.variant||""),i&&(i.checked=this._pageConfig.useGrid),this._renderCanvasSections(),this._renderEditor(),this._refreshPreview(),this._isDirty=!1,this._showToast(`Template "${e}" loaded`,"success"),this}deleteTemplate(e){return u.delete(e)&&this._showToast(`Template "${e}" deleted`,"success"),this}listTemplates(){return u.list()}newPage(){return this._isDirty&&!confirm("You have unsaved changes. Start a new page anyway?")||(this._sections=[],this._selectedIndex=-1,this._templateName="",this._pageConfig={theme:"light",variant:null,useGrid:!0,useTheme:!0,customCSS:"",meta:{title:"My Page",description:"",charset:"UTF-8",viewport:"width=device-width, initial-scale=1.0"}},this._refs.templateName.value="",this.element.querySelector(".qr-theme-select").value="light",this.element.querySelector(".qr-variant-select").value="",this.element.querySelector(".qr-grid-toggle").checked=!0,this._renderCanvasSections(),this._renderEditor(),this._refreshPreview(),this._isDirty=!1,u.clearActive()),this}openPreviewWindow(){const e=this._generatePreviewHTML(),t=window.open("","_blank");return t.document.write(e),t.document.close(),this}refreshPreview(){return this._refreshPreview(),this}destroy(){this._autoSaveTimer&&clearInterval(this._autoSaveTimer);for(const{element:e,event:t,wrapper:n}of this._eventHandlers)e.removeEventListener(t,n);this._eventHandlers=[],this.element.classList.remove("qr-container"),this.element.innerHTML=""}}class b{constructor(e,t={}){this.element="string"==typeof e?document.querySelector(e):e,this.options={...this.constructor.defaults,...t},this._eventHandlers=[],this.element&&(this.element._dommaComponent=this)}on(e,t){this.options[e]&&"function"==typeof this.options[e]&&this.options[e](t)}setOptions(e){return this.options={...this.options,...e},"function"==typeof this._applyOptions&&this._applyOptions(),this}_addEventListener(e,t,n){e.addEventListener(t,n),this._eventHandlers.push({element:e,event:t,handler:n})}destroy(){for(const{element:e,event:t,handler:n}of this._eventHandlers)e.removeEventListener(t,n);this._eventHandlers=[],this.element&&delete this.element._dommaComponent}}const y={string:{name:"Text",icon:"type",category:"basic",description:"Single-line text input",defaults:{type:"string",required:!1,minLength:null,maxLength:null,default:"",label:"",placeholder:"",help:""}},number:{name:"Number",icon:"hash",category:"basic",description:"Numeric input",defaults:{type:"number",required:!1,min:null,max:null,default:0,label:"",placeholder:"",help:""}},boolean:{name:"Checkbox",icon:"check-square",category:"basic",description:"True/false checkbox",defaults:{type:"boolean",required:!1,default:!1,label:"",help:""}},email:{name:"Email",icon:"mail",category:"inputs",description:"Email input with validation",defaults:{type:"email",required:!1,default:"",label:"",placeholder:"",help:""}},password:{name:"Password",icon:"lock",category:"inputs",description:"Password input",defaults:{type:"password",required:!1,minLength:null,default:"",label:"",placeholder:"",help:""}},textarea:{name:"Textarea",icon:"file-text",category:"basic",description:"Multi-line text",defaults:{type:"textarea",required:!1,default:"",label:"",placeholder:"",help:"",formConfig:{rows:3}}},select:{name:"Select",icon:"chevron-down",category:"selection",description:"Dropdown select",defaults:{type:"select",required:!1,options:[],default:"",label:"",help:""}},multiselect:{name:"Multi-select",icon:"list",category:"selection",description:"Multiple selection dropdown",defaults:{type:"multiselect",required:!1,options:[],default:[],label:"",help:""}},radio:{name:"Radio Group",icon:"circle",category:"selection",description:"Radio button group",defaults:{type:"radio",required:!1,options:[],default:"",label:"",help:""}},"checkbox-group":{name:"Checkbox Group",icon:"check-square",category:"selection",description:"Multiple checkboxes",defaults:{type:"checkbox-group",required:!1,options:[],default:[],label:"",help:""}},url:{name:"URL",icon:"link",category:"inputs",description:"URL input with validation",defaults:{type:"url",required:!1,default:"",label:"",placeholder:"https://example.com",help:""}},tel:{name:"Phone",icon:"phone",category:"inputs",description:"Telephone number input",defaults:{type:"tel",required:!1,default:"",label:"",placeholder:"",help:""}},color:{name:"Color",icon:"droplet",category:"inputs",description:"Color picker",defaults:{type:"color",required:!1,default:"#000000",label:"",help:""}},range:{name:"Range",icon:"sliders",category:"inputs",description:"Slider input",defaults:{type:"range",required:!1,min:0,max:100,step:1,default:50,label:"",help:""}},hidden:{name:"Hidden",icon:"eye-off",category:"inputs",description:"Hidden field",defaults:{type:"hidden",required:!1,default:"",label:"",help:""}},file:{name:"File",icon:"paperclip",category:"advanced",description:"File upload",defaults:{type:"file",required:!1,accept:"",multiple:!1,label:"",help:""}},date:{name:"Date",icon:"calendar",category:"datetime",description:"Date picker",defaults:{type:"string",required:!1,default:"",label:"",help:"",validate:e=>{if(!e||""===e)return!0;if(e instanceof Date&&!isNaN(e.getTime()))return!0;if("string"==typeof e){const t=new Date(e);if(!isNaN(t.getTime()))return!0}return"Invalid date format"}}},datetime:{name:"Date & Time",icon:"calendar",category:"datetime",description:"Date and time picker",defaults:{type:"datetime-local",required:!1,default:"",label:"",help:""}},time:{name:"Time",icon:"clock",category:"datetime",description:"Time picker",defaults:{type:"time",required:!1,default:"",label:"",help:""}},array:{name:"Array",icon:"layers",category:"advanced",description:"Array field type",defaults:{type:"array",required:!1,default:[],label:"",help:""}},object:{name:"Object",icon:"box",category:"advanced",description:"Object field type",defaults:{type:"object",required:!1,default:{},label:"",help:""}}};class f{static STORAGE_KEY="domma:schema-builder-templates";static ACTIVE_KEY="domma:schema-builder-active";static list(){try{const e=localStorage.getItem(this.STORAGE_KEY);return e?Object.keys(JSON.parse(e)):[]}catch(e){return[]}}static load(e){try{const t=localStorage.getItem(this.STORAGE_KEY);if(!t)return null;return JSON.parse(t)[e]||null}catch(e){return null}}static save(e,t){try{const n=localStorage.getItem(this.STORAGE_KEY),a=n?JSON.parse(n):{};return a[e]={...t,updatedAt:Date.now()},localStorage.setItem(this.STORAGE_KEY,JSON.stringify(a)),!0}catch(e){return!1}}static delete(e){try{const t=localStorage.getItem(this.STORAGE_KEY);if(!t)return!1;const n=JSON.parse(t);return delete n[e],localStorage.setItem(this.STORAGE_KEY,JSON.stringify(n)),!0}catch(e){return!1}}static saveActive(e){try{return localStorage.setItem(this.ACTIVE_KEY,JSON.stringify(e)),!0}catch(e){return!1}}static loadActive(){try{const e=localStorage.getItem(this.ACTIVE_KEY);return e?JSON.parse(e):null}catch(e){return null}}}"undefined"!=typeof window&&(window.SchemaTemplateManager=f);class v extends b{static defaults={theme:"light",autoSave:!0,autoSaveInterval:3e4,showPreview:!0,previewLayout:"stacked",onChange:null,onSave:null,onExport:null};constructor(e,t={}){super(e,t),this.options={...v.defaults,...t},this._fields=[],this._selectedIndex=-1,this._schemaName="",this._isDirty=!1,this._dragSource=null,this._autoSaveTimer=null,this._previewForm=null,this._refs={},this.element&&this._init()}_init(){this._render(),this._attachEvents();const e=this.element.querySelector(".sb-preview-layout");e&&(e.value=this.options.previewLayout),this.options.showPreview&&this._refs.previewPanel&&(this._refs.previewPanel.style.display="flex",this._refs.previewPanel.style.transform="translateX(0)"),this.options.autoSave&&this._startAutoSave();const t=f.loadActive();t&&t.length>0&&(this._fields=t,this._renderCanvas(),this._renderPreview())}_render(){this.element.innerHTML='\n <div class="sb-container">\n <div class="sb-header">\n <div class="sb-header-left">\n <button class="btn btn-primary btn-sm" data-action="new"><span data-icon="plus-circle"></span> New</button>\n <button class="btn btn-sm" data-action="save"><span data-icon="save"></span> Save</button>\n <button class="btn btn-sm" data-action="load"><span data-icon="folder-open"></span> Load</button>\n <input type="text" class="sb-schema-name" placeholder="Schema Name" value="">\n </div>\n <div class="sb-header-center">\n <h2 class="sb-title">Schema Builder</h2>\n </div>\n <div class="sb-header-right">\n <button class="btn btn-sm btn-primary" data-action="toggle-preview"><span data-icon="eye"></span> Preview</button>\n <button class="btn btn-sm" data-action="import"><span data-icon="upload"></span> Import</button>\n <button class="btn btn-primary btn-sm" data-action="export"><span data-icon="download"></span> Export</button>\n </div>\n </div>\n <div class="sb-body">\n <div class="sb-sidebar sb-library">\n <div class="sb-library-header">\n <h3 style="text-align: center;">Field Library</h3>\n <input type="text" class="sb-search form-input" placeholder="Search fields..." />\n </div>\n <div class="sb-library-content"></div>\n </div>\n <div class="sb-main">\n <div class="sb-canvas"></div>\n </div>\n <div class="sb-sidebar sb-editor">\n <h3 style="text-align: center;">Properties</h3>\n <div class="sb-editor-content">\n <p class="text-muted">Select a field to edit properties</p>\n </div>\n </div>\n </div>\n </div>\n\n \x3c!-- Floating Preview Panel --\x3e\n <div class="sb-preview-panel" style="display: none;">\n <div class="sb-preview-header">\n <h3>Live Preview</h3>\n <div class="sb-preview-controls">\n <label style="font-size: 0.875rem; margin-right: 0.5rem;">Layout:</label>\n <select class="sb-preview-layout form-select" style="display: inline-block; width: auto;">\n <option value="stacked">Stacked</option>\n <option value="inline">Inline</option>\n <option value="grid-2">Grid (2 cols)</option>\n <option value="grid-3">Grid (3 cols)</option>\n <option value="grid-4">Grid (4 cols)</option>\n <option value="grid-6">Grid (6 cols)</option>\n <option value="grid-12">Grid (12 cols)</option>\n </select>\n <button class="btn btn-sm" data-action="close-preview"><span data-icon="x"></span></button>\n </div>\n </div>\n <div class="sb-preview-body">\n <div class="sb-preview-split">\n <div class="sb-preview-form-container">\n <h4>Form</h4>\n <div class="sb-preview-form"></div>\n </div>\n <div class="sb-preview-state-container">\n <h4>Model State</h4>\n <pre class="sb-state-display">{}</pre>\n </div>\n </div>\n </div>\n </div>\n ',this._refs={library:this.element.querySelector(".sb-library-content"),canvas:this.element.querySelector(".sb-canvas"),editor:this.element.querySelector(".sb-editor-content"),previewPanel:this.element.querySelector(".sb-preview-panel"),previewForm:this.element.querySelector(".sb-preview-form"),previewState:this.element.querySelector(".sb-state-display"),schemaName:this.element.querySelector(".sb-schema-name")},this._renderLibrary(),"undefined"!=typeof Domma&&Domma.icons&&Domma.icons.scan(this.element)}_renderLibrary(){const e={basic:[],inputs:[],selection:[],datetime:[]};for(const[t,n]of Object.entries(y))e[n.category]&&e[n.category].push({type:t,...n});let t="";for(const[n,a]of Object.entries(e))if(0!==a.length){t+=`<div class="sb-library-category"><h4 class="sb-category-title">${this._capitalize(n)}</h4><div class="sb-category-items">`;for(const e of a)t+=`<div class="sb-library-item" draggable="true" data-type="${e.type}" title="${this._escapeHtml(e.description)}"><span data-icon="${e.icon}"></span><span class="sb-item-name">${this._escapeHtml(e.name)}</span></div>`;t+="</div></div>"}this._refs.library.innerHTML=t,"undefined"!=typeof Domma&&Domma.icons&&Domma.icons.scan(this._refs.library)}_renderCanvas(){let e='<div class="sb-drop-zone" data-position="0"></div>';this._fields.forEach((t,n)=>{const a=n===this._selectedIndex,i=y[t.type]||{name:t.type,icon:"help-circle"};e+=`\n <div class="sb-field-card ${a?"sb-selected":""}" draggable="true" data-index="${n}">\n <div class="sb-field-header">\n <span data-icon="${i.icon}"></span>\n <span class="sb-field-name">${this._escapeHtml(t.name||"field"+n)}</span>\n <span class="sb-field-type">[${i.name}]</span>\n </div>\n <div class="sb-field-meta">\n ${t.required?'<span class="sb-badge sb-badge-required">Required</span>':""}\n ${t.label?'<span class="sb-field-label">'+this._escapeHtml(t.label)+"</span>":""}\n </div>\n <div class="sb-field-actions">\n <button class="btn btn-sm" data-action="duplicate" data-index="${n}"><span data-icon="copy"></span></button>\n <button class="btn btn-danger btn-sm" data-action="remove" data-index="${n}"><span data-icon="trash"></span></button>\n </div>\n </div>\n <div class="sb-drop-zone" data-position="${n+1}"></div>\n `}),0===this._fields.length&&(e='\n <div class="sb-empty-state">\n <span data-icon="inbox" data-icon-size="48"></span>\n <h3>No fields yet</h3>\n <p>Drag fields from the library or click to add</p>\n </div>\n <div class="sb-drop-zone" data-position="0"></div>\n '),this._refs.canvas.innerHTML=e,"undefined"!=typeof Domma&&Domma.icons&&Domma.icons.scan(this._refs.canvas)}_renderEditor(e){if(e<0||e>=this._fields.length)return void(this._refs.editor.innerHTML='<p class="text-muted">Select a field to edit properties</p>');const t=this._fields[e];let n=`\n <div class="sb-editor-section">\n <h4>Core Properties</h4>\n <div class="sb-editor-field">\n <label>Field Name</label>\n <input type="text" class="form-input" data-prop="name" value="${this._escapeHtml(t.name||"")}" placeholder="fieldName" />\n </div>\n <div class="sb-editor-field">\n <label>Type</label>\n <select class="form-select" data-prop="type">\n ${Object.entries(y).map(([e,n])=>`<option value="${e}" ${t.type===e?"selected":""}>${this._escapeHtml(n.name)}</option>`).join("")}\n </select>\n </div>\n <div class="sb-editor-field">\n <label><input type="checkbox" data-prop="required" ${t.required?"checked":""} /> Required</label>\n </div>\n </div>\n <div class="sb-editor-section">\n <h4>Form Display</h4>\n <div class="sb-editor-field">\n <label>Label</label>\n <input type="text" class="form-input" data-prop="label" value="${this._escapeHtml(t.label||"")}" />\n </div>\n <div class="sb-editor-field">\n <label>Placeholder</label>\n <input type="text" class="form-input" data-prop="placeholder" value="${this._escapeHtml(t.placeholder||"")}" />\n </div>\n <div class="sb-editor-field">\n <label>Help Text</label>\n <input type="text" class="form-input" data-prop="help" value="${this._escapeHtml(t.help||"")}" />\n </div>\n <div class="sb-editor-field">\n <label>Column Span (Grid Layout)</label>\n <select class="form-select" data-prop="formConfig.columns">\n ${[1,2,3,4,5,6,7,8,9,10,11,12].map(e=>`<option value="${e}" ${(t.formConfig?.columns||12)===e?"selected":""}>${e} ${12===e?"(Full Width)":"col"+(e>1?"s":"")}</option>`).join("")}\n </select>\n <small class="text-muted">Width in grid layouts (1-12 columns)</small>\n </div>\n </div>\n `;n+=this._renderFieldSpecificProperties(t),this._refs.editor.innerHTML=n,["select","multiselect","radio","checkbox-group"].includes(t.type)&&this._attachOptionsEditor()}_renderFieldSpecificProperties(e){let t="";if(["string","textarea","email","url","tel","password"].includes(e.type)&&(t+=`\n <div class="sb-editor-section">\n <h4>Validation</h4>\n <div class="sb-editor-field">\n <label>Min Length</label>\n <input type="number" class="form-input" data-prop="minLength" value="${e.minLength||""}" min="0" />\n </div>\n <div class="sb-editor-field">\n <label>Max Length</label>\n <input type="number" class="form-input" data-prop="maxLength" value="${e.maxLength||""}" min="0" />\n </div>\n <div class="sb-editor-field">\n <label>Pattern (Regex)</label>\n <input type="text" class="form-input" data-prop="pattern" value="${this._escapeHtml(e.pattern||"")}" placeholder="^[A-Za-z]+$" />\n </div>\n </div>\n `),"number"!==e.type&&"range"!==e.type||(t+=`\n <div class="sb-editor-section">\n <h4>Validation</h4>\n <div class="sb-editor-field">\n <label>Min Value</label>\n <input type="number" class="form-input" data-prop="min" value="${void 0!==e.min?e.min:""}" />\n </div>\n <div class="sb-editor-field">\n <label>Max Value</label>\n <input type="number" class="form-input" data-prop="max" value="${void 0!==e.max?e.max:""}" />\n </div>\n <div class="sb-editor-field">\n <label>Step</label>\n <input type="number" class="form-input" data-prop="step" value="${e.step||""}" min="0" step="0.01" />\n </div>\n </div>\n `),["date","datetime","time"].includes(e.type)&&(t+=`\n <div class="sb-editor-section">\n <h4>Validation</h4>\n <div class="sb-editor-field">\n <label>Min ${"time"===e.type?"Time":"Date"}</label>\n <input type="${e.type}" class="form-input" data-prop="min" value="${e.min||""}" />\n </div>\n <div class="sb-editor-field">\n <label>Max ${"time"===e.type?"Time":"Date"}</label>\n <input type="${e.type}" class="form-input" data-prop="max" value="${e.max||""}" />\n </div>\n </div>\n `),"file"===e.type&&(t+=`\n <div class="sb-editor-section">\n <h4>File Options</h4>\n <div class="sb-editor-field">\n <label>Accept (file types)</label>\n <input type="text" class="form-input" data-prop="accept" value="${this._escapeHtml(e.accept||"")}" placeholder=".jpg,.png,.pdf" />\n </div>\n <div class="sb-editor-field">\n <label><input type="checkbox" data-prop="multiple" ${e.multiple?"checked":""} /> Allow Multiple Files</label>\n </div>\n </div>\n `),["select","multiselect","radio","checkbox-group"].includes(e.type)){const n=e.options||[];t+=`\n <div class="sb-editor-section">\n <h4>Options</h4>\n <div class="sb-options-editor">\n <div class="sb-options-list" data-options-list>\n ${n.map((e,t)=>`\n <div class="sb-option-item" data-option-index="${t}">\n <div class="sb-option-inputs">\n <input type="text" class="form-input sb-option-value" placeholder="value" value="${this._escapeHtml(e.value||"")}" data-option-part="value" />\n <input type="text" class="form-input sb-option-label" placeholder="Label" value="${this._escapeHtml(e.label||"")}" data-option-part="label" />\n </div>\n <div class="sb-option-actions">\n <button class="btn btn-sm" data-action="move-up" ${0===t?"disabled":""}><span data-icon="chevron-up"></span></button>\n <button class="btn btn-sm" data-action="move-down" ${t===n.length-1?"disabled":""}><span data-icon="chevron-down"></span></button>\n <button class="btn btn-danger btn-sm" data-action="remove-option"><span data-icon="trash"></span></button>\n </div>\n </div>\n `).join("")}\n </div>\n <button class="btn btn-primary btn-sm" data-action="add-option"><span data-icon="plus"></span> Add Option</button>\n </div>\n </div>\n `}return t}_attachOptionsEditor(){this._refs.editor.querySelector("[data-options-list]")&&(this._on(this._refs.editor,"click",'[data-action="add-option"]',()=>{const e=this._fields[this._selectedIndex];e.options||(e.options=[]),e.options.push({value:"",label:""}),this.updateField(this._selectedIndex,{options:e.options}),this._renderEditor(this._selectedIndex)}),this._on(this._refs.editor,"click",'[data-action="remove-option"]',e=>{const t=e.target.closest(".sb-option-item"),n=parseInt(t.dataset.optionIndex),a=this._fields[this._selectedIndex];a.options.splice(n,1),this.updateField(this._selectedIndex,{options:a.options}),this._renderEditor(this._selectedIndex)}),this._on(this._refs.editor,"click",'[data-action="move-up"]',e=>{const t=e.target.closest(".sb-option-item"),n=parseInt(t.dataset.optionIndex);if(0===n)return;const a=this._fields[this._selectedIndex],i=a.options[n];a.options[n]=a.options[n-1],a.options[n-1]=i,this.updateField(this._selectedIndex,{options:a.options}),this._renderEditor(this._selectedIndex)}),this._on(this._refs.editor,"click",'[data-action="move-down"]',e=>{const t=e.target.closest(".sb-option-item"),n=parseInt(t.dataset.optionIndex),a=this._fields[this._selectedIndex];if(n===a.options.length-1)return;const i=a.options[n];a.options[n]=a.options[n+1],a.options[n+1]=i,this.updateField(this._selectedIndex,{options:a.options}),this._renderEditor(this._selectedIndex)}),this._on(this._refs.editor,"input","[data-option-part]",e=>{const t=e.target.closest(".sb-option-item"),n=parseInt(t.dataset.optionIndex),a=e.target.dataset.optionPart,i=this._fields[this._selectedIndex];i.options[n][a]=e.target.value,this.updateField(this._selectedIndex,{options:i.options})}))}_renderPreview(){if(!this.options.showPreview)return;if(this._refs.previewForm&&this._refs.previewState||(this._refs.previewForm=this.element.querySelector(".sb-preview-form"),this._refs.previewState=this.element.querySelector(".sb-state-display"),this._refs.previewPanel=this.element.querySelector(".sb-preview-panel")),!this._refs.previewForm||!this._refs.previewState)return;this._previewForm&&this._previewForm.destroy&&this._previewForm.destroy();const e=this._toBlueprint();if(0===Object.keys(e).length)return this._refs.previewForm.innerHTML='<p class="text-muted">No fields to preview</p>',void(this._refs.previewState.textContent="{}");if("undefined"!=typeof Domma&&Domma.forms)try{this._refs.previewForm.innerHTML="";let t=this.options.previewLayout,n=2;"grid-2"===t?(t="grid",n=2):"grid-3"===t?(t="grid",n=3):"grid-4"===t?(t="grid",n=4):"grid-6"===t?(t="grid",n=6):"grid-12"===t&&(t="grid",n=12),this._previewForm=Domma.forms.create(e,{},{layout:t,columns:n,submitText:"Test Submit",showReset:!1,onSubmit:async e=>{const t=this._previewForm.model.validate();if(!0===t)"undefined"!=typeof Domma&&Domma.elements&&Domma.elements.alert?await Domma.elements.alert(`<strong>✓ Validation Passed!</strong><br><br>Submitted Data:<br><pre style="text-align: left; margin-top: 10px;">${JSON.stringify(e,null,2)}</pre>`,{title:"Form Submission Test"}):alert("✓ Validation passed!\n\nSubmitted data:\n"+JSON.stringify(e,null,2));else{const e=Object.entries(t).map(([e,t])=>`• ${e}: ${t}`).join("\n");"undefined"!=typeof Domma&&Domma.elements&&Domma.elements.alert?await Domma.elements.alert(`<strong>✗ Validation Failed</strong><br><br>${e.replace(/\n/g,"<br>")}`,{title:"Form Validation Errors"}):alert("✗ Validation failed:\n\n"+e)}}}),this._previewForm.renderTo(this._refs.previewForm);const a=()=>{this._previewForm&&this._previewForm.model&&(this._refs.previewState.textContent=JSON.stringify(this._previewForm.model.toJSON(),null,2))};this._refs.previewForm.querySelectorAll("input, select, textarea").forEach(e=>{e.addEventListener("input",a),e.addEventListener("change",a)}),a()}catch(e){console.error("Preview error:",e),this._refs.previewForm.innerHTML='<p class="text-danger">Preview error: '+e.message+"</p>"}}_attachEvents(){this._on(this.element,"click",'[data-action="new"]',()=>this.newSchema()),this._on(this.element,"click",'[data-action="save"]',()=>this._showSaveModal()),this._on(this.element,"click",'[data-action="load"]',()=>this._showLoadModal()),this._on(this.element,"click",'[data-action="export"]',()=>this._showExportModal()),this._on(this.element,"click",'[data-action="toggle-preview"]',()=>this._togglePreview()),this._on(this.element,"click",'[data-action="close-preview"]',()=>this._togglePreview()),this._on(this.element,"click",".sb-library-item",e=>{const t=e.target.closest(".sb-library-item").dataset.type;this.addField(t,this._fields.length)}),this._on(this.element,"dragstart",".sb-library-item",e=>{e.dataTransfer.setData("field-type",e.target.closest(".sb-library-item").dataset.type),this._dragSource="library"}),this._on(this.element,"dragover",".sb-drop-zone",e=>{e.preventDefault(),e.target.closest(".sb-drop-zone").classList.add("sb-drop-active")}),this._on(this.element,"dragleave",".sb-drop-zone",e=>{e.target.closest(".sb-drop-zone").classList.remove("sb-drop-active")}),this._on(this.element,"drop",".sb-drop-zone",e=>{e.preventDefault();const t=e.target.closest(".sb-drop-zone");t.classList.remove("sb-drop-active");const n=parseInt(t.dataset.position);if("library"===this._dragSource){const t=e.dataTransfer.getData("field-type");this.addField(t,n)}}),this._on(this.element,"click",".sb-field-card",e=>{if(e.target.closest(".sb-field-actions"))return;const t=parseInt(e.target.closest(".sb-field-card").dataset.index);this._selectField(t)}),this._on(this.element,"click",'[data-action="remove"]',e=>{e.stopPropagation(),this.removeField(parseInt(e.target.closest('[data-action="remove"]').dataset.index))}),this._on(this.element,"click",'[data-action="duplicate"]',e=>{e.stopPropagation(),this.duplicateField(parseInt(e.target.closest('[data-action="duplicate"]').dataset.index))}),this._on(this.element,"input",".sb-editor [data-prop]",e=>this._handlePropertyChange(e)),this._on(this.element,"change",".sb-editor [data-prop]",e=>this._handlePropertyChange(e)),this._on(this.element,"change",".sb-preview-layout",e=>{this.setPreviewLayout(e.target.value)}),document.addEventListener("keydown",e=>{"Escape"===e.key&&this._refs.previewPanel&&"flex"===this._refs.previewPanel.style.display&&this._togglePreview()})}_on(e,t,n,a){const i=t=>{if(n){const i=t.target.closest(n);i&&e.contains(i)&&a.call(i,t)}else a.call(e,t)};e.addEventListener(t,i),this._eventHandlers.push({element:e,event:t,handler:i})}_handlePropertyChange(e){if(this._selectedIndex<0)return;const t=e.target.dataset.prop;let n="checkbox"===e.target.type?e.target.checked:e.target.value;if("number"===e.target.type&&""!==n){const e=parseFloat(n);isNaN(e)||(n=e)}if(t.includes(".")){const e=t.split("."),a=this._fields[this._selectedIndex];a[e[0]]||(a[e[0]]={}),a[e[0]][e[1]]=n,this.updateField(this._selectedIndex,{[e[0]]:a[e[0]]})}else this.updateField(this._selectedIndex,{[t]:n})}_selectField(e){this._selectedIndex=e,this._renderCanvas(),this._renderEditor(e)}addField(e,t){const n=y[e];if(!n)return this;const a={name:`field${this._fields.length}`,...JSON.parse(JSON.stringify(n.defaults)),formConfig:{columns:12}};return this._fields.splice(t,0,a),this._markDirty(),this._renderCanvas(),this._renderPreview(),this._selectField(t),this}removeField(e){return e<0||e>=this._fields.length||(this._fields.splice(e,1),this._markDirty(),this._selectedIndex===e?this._selectedIndex=-1:this._selectedIndex>e&&this._selectedIndex--,this._renderCanvas(),this._renderEditor(this._selectedIndex),this._renderPreview()),this}duplicateField(e){if(e<0||e>=this._fields.length)return this;const t=JSON.parse(JSON.stringify(this._fields[e]));return t.name=t.name+"_copy",this._fields.splice(e+1,0,t),this._markDirty(),this._renderCanvas(),this._renderPreview(),this._selectField(e+1),this}updateField(e,t){return e<0||e>=this._fields.length||(Object.assign(this._fields[e],t),this._markDirty(),this._renderCanvas(),this._renderPreview()),this}getFields(){return JSON.parse(JSON.stringify(this._fields))}getBlueprint(){return this._toBlueprint()}exportJS(e={}){const t=this._toBlueprint();let n="const blueprint = ";return n+=JSON.stringify(t,null,4),n+=";\n",this.options.onExport&&this.options.onExport("js",n),n}exportJSON(e={}){const t=this._toBlueprint(),n=JSON.stringify(t,null,2);return this.options.onExport&&this.options.onExport("json",n),n}exportTypeScript(e={}){const{interfaceName:t="FormData"}=e,n=this._toBlueprint();let a=`interface ${t} {\n`;for(const[e,t]of Object.entries(n)){const n=this._mapToTSType(t.type);a+=` ${e}${t.required?"":"?"}: ${n};\n`}return a+="}\n",this.options.onExport&&this.options.onExport("typescript",a),a}importBlueprint(e,t){const n=[];for(const[t,a]of Object.entries(e))n.push({name:t,...a});return this._fields=n,this._schemaName=t||"",this._selectedIndex=-1,this._refs.schemaName&&(this._refs.schemaName.value=this._schemaName),this._renderCanvas(),this._renderEditor(-1),this._renderPreview(),this}saveTemplate(e){const t={name:this._schemaName,fields:this.getFields()};return f.save(e,t),this._isDirty=!1,this.options.onSave&&this.options.onSave(e,t),this}loadTemplate(e){const t=f.load(e);return t?(this._fields=t.fields||[],this._schemaName=t.name||e,this._selectedIndex=-1,this._refs.schemaName&&(this._refs.schemaName.value=this._schemaName),this._renderCanvas(),this._renderEditor(-1),this._renderPreview(),this):this}newSchema(e=""){return this._fields=[],this._schemaName=e,this._selectedIndex=-1,this._refs.schemaName&&(this._refs.schemaName.value=this._schemaName),this._renderCanvas(),this._renderEditor(-1),this._renderPreview(),this}setPreviewLayout(e){this.options.previewLayout=e;const t=this.element.querySelector(".sb-preview-layout");return t&&(t.value=e),this._renderPreview(),this}_togglePreview(){if(!this._refs.previewPanel&&(this._refs.previewPanel=this.element.querySelector(".sb-preview-panel"),!this._refs.previewPanel))return;return"none"!==this._refs.previewPanel.style.display?(this._refs.previewPanel.style.transform="translateX(100%)",setTimeout(()=>{this._refs.previewPanel.style.display="none"},300)):(this._refs.previewPanel.style.display="flex",this._refs.previewPanel.offsetHeight,setTimeout(()=>{this._refs.previewPanel.style.transform="translateX(0)",this._renderPreview()},10)),this}_toBlueprint(){const e={};for(const t of this._fields){if(!t.name)continue;const{name:n,...a}=t;e[n]=a}return e}_mapToTSType(e){return{string:"string",email:"string",password:"string",number:"number",boolean:"boolean",date:"Date | string",select:"string",textarea:"string"}[e]||"any"}_showSaveModal(){const e=this._createModal("Save Template",`\n <div class="sb-modal-body">\n <div class="form-group">\n <label class="form-label">Template Name</label>\n <input type="text" class="form-input sb-modal-input" placeholder="My Template" value="${this._escapeHtml(this._schemaName)}" />\n </div>\n </div>\n <div class="sb-modal-footer">\n <button class="btn" data-modal-action="cancel">Cancel</button>\n <button class="btn btn-primary" data-modal-action="save">Save</button>\n </div>\n `);this._on(e,"click",'[data-modal-action="save"]',()=>{const t=e.querySelector(".sb-modal-input").value.trim();t&&(this.saveTemplate(t),e.remove())}),this._on(e,"click",'[data-modal-action="cancel"]',()=>e.remove())}_showLoadModal(){const e=f.list();if(0===e.length)return void alert("No saved templates");const t=this._createModal("Load Template",`\n <div class="sb-modal-body">\n <div class="form-group">\n <label class="form-label">Select Template</label>\n <select class="form-select sb-modal-select">\n ${e.map(e=>`<option value="${this._escapeHtml(e)}">${this._escapeHtml(e)}</option>`).join("")}\n </select>\n </div>\n </div>\n <div class="sb-modal-footer">\n <button class="btn" data-modal-action="cancel">Cancel</button>\n <button class="btn btn-primary" data-modal-action="load">Load</button>\n </div>\n `);this._on(t,"click",'[data-modal-action="load"]',()=>{this.loadTemplate(t.querySelector(".sb-modal-select").value),t.remove()}),this._on(t,"click",'[data-modal-action="cancel"]',()=>t.remove())}_showExportModal(){const e=this._createModal("Export Blueprint",'\n <div class="sb-modal-body sb-export-modal">\n <div class="sb-export-tabs">\n <button class="sb-tab-btn active" data-format="js">JavaScript</button>\n <button class="sb-tab-btn" data-format="json">JSON</button>\n <button class="sb-tab-btn" data-format="typescript">TypeScript</button>\n </div>\n <div class="sb-export-content">\n <textarea class="form-input sb-export-output" readonly rows="15"></textarea>\n </div>\n </div>\n <div class="sb-modal-footer">\n <button class="btn" data-modal-action="close">Close</button>\n <button class="btn btn-primary" data-modal-action="copy">Copy</button>\n </div>\n '),t=e.querySelector(".sb-export-output");t.value=this.exportJS(),this._on(e,"click",".sb-tab-btn",n=>{const a=n.target.dataset.format;e.querySelectorAll(".sb-tab-btn").forEach(e=>e.classList.toggle("active",e.dataset.format===a)),"js"===a?t.value=this.exportJS():"json"===a?t.value=this.exportJSON():"typescript"===a&&(t.value=this.exportTypeScript())}),this._on(e,"click",'[data-modal-action="copy"]',async()=>{try{await navigator.clipboard.writeText(t.value),alert("Copied to clipboard!")}catch(e){alert("Failed to copy")}}),this._on(e,"click",'[data-modal-action="close"]',()=>e.remove())}_createModal(e,t){const n=document.createElement("div");return n.className="sb-modal-overlay",n.innerHTML=`<div class="sb-modal"><div class="sb-modal-header"><h3>${this._escapeHtml(e)}</h3></div>${t}</div>`,document.body.appendChild(n),n}_startAutoSave(){this._autoSaveTimer&&clearInterval(this._autoSaveTimer),this._autoSaveTimer=setInterval(()=>{this._isDirty&&f.saveActive(this._fields)},this.options.autoSaveInterval)}_markDirty(){this._isDirty=!0,this.options.onChange&&this.options.onChange(this.getFields())}_capitalize(e){return e.charAt(0).toUpperCase()+e.slice(1)}_escapeHtml(e){if(!e)return"";const t=document.createElement("div");return t.textContent=e.toString(),t.innerHTML}destroy(){this._autoSaveTimer&&clearInterval(this._autoSaveTimer),this._previewForm&&this._previewForm.destroy&&this._previewForm.destroy(),super.destroy()}}class _ extends b{constructor(e,t={}){super(e,t),this.mode=t.mode||"text",this.model=t.model||null,this.modelKey=t.modelKey||null,this.autosave=void 0!==t.autosave&&t.autosave,this.autosaveInterval=t.autosaveInterval||3e3,this.storageKey=t.storage||null,this.toolbar=t.toolbar||this._getDefaultToolbar(),this.showToolbar=!1!==t.showToolbar,this.imagePaste=!1!==t.imagePaste,this.imageMode=t.imageMode||"base64",this.imageUpload=t.imageUpload||null,this.language=t.language||"javascript",this.lineNumbers=!1!==t.lineNumbers,this.theme=t.theme||"light",this.placeholder=t.placeholder||"",this.minHeight=t.minHeight||200,this.maxHeight=t.maxHeight||null,this.characterCount=t.characterCount||!1,this.wordCount=t.wordCount||!1,this.onChange=t.onChange||null,this.onSave=t.onSave||null,this.onImagePaste=t.onImagePaste||null,this._content="",this._history=[],this._historyIndex=-1,this._maxHistory=50,this._autosaveTimer=null,this._container=null,this._editorEl=null,this._toolbarEl=null,this._counterEl=null,this._init()}_init(){if(!this.element)return;this.element.style.display="none",this._createContainer(),!this.showToolbar||"rich"!==this.mode&&"code"!==this.mode||this._createToolbar(),this._createEditor(),(this.characterCount||this.wordCount)&&this._createCounter(),this.storageKey&&this._loadFromStorage();const e=this.element.value||this.element.textContent||"";e&&this.setValue(e),this.autosave&&this._setupAutosave(),this.model&&"function"==typeof this.model.onChange&&(this._modelUnsubscribe=this.model.onChange((e,t)=>{e===this.modelKey&&t!==this.getValue()&&this.setValue(t)}))}_getDefaultToolbar(){return"rich"===this.mode?[["bold","italic","underline","strikethrough"],["h1","h2","h3"],["ul","ol","blockquote"],["link","image","code"],["codeblock","embed"],["undo","redo","clear"]]:"code"===this.mode?[["undo","redo"],["format","clear"]]:[]}_createContainer(){this._container=document.createElement("div"),this._container.className=`dm-editor dm-editor-${this.mode} dm-theme-${this.theme}`,this._container.style.minHeight=`${this.minHeight}px`,this.maxHeight&&(this._container.style.maxHeight=`${this.maxHeight}px`),this.element.parentNode.insertBefore(this._container,this.element.nextSibling)}_createToolbar(){this._toolbarEl=document.createElement("div"),this._toolbarEl.className="dm-editor-toolbar",this.toolbar.forEach(e=>{const t=document.createElement("div");t.className="dm-editor-toolbar-group",e.forEach(e=>{const n=this._createToolbarButton(e);n&&t.appendChild(n)}),this._toolbarEl.appendChild(t)}),this._container.appendChild(this._toolbarEl)}_createToolbarButton(e){const t=document.createElement("button");t.type="button",t.className=`dm-editor-toolbar-btn dm-editor-btn-${e}`,t.title=this._getButtonTitle(e),t.setAttribute("data-action",e);const n=this._getIconName(e);if(n&&window.Domma&&window.Domma.icons){const a=window.Domma.icons.render(n,{size:16});a?t.appendChild(a):t.innerHTML=this._getFallbackIcon(e)}else t.innerHTML=this._getFallbackIcon(e);return t.addEventListener("click",t=>{t.preventDefault(),this._executeCommand(e)}),t}_getButtonTitle(e){return{bold:"Bold (Ctrl+B)",italic:"Italic (Ctrl+I)",underline:"Underline (Ctrl+U)",strikethrough:"Strikethrough",h1:"Heading 1",h2:"Heading 2",h3:"Heading 3",ul:"Bullet List",ol:"Numbered List",blockquote:"Quote",link:"Insert Link",image:"Insert Image",code:"Inline Code",codeblock:"Code Block",embed:"Embed Media",undo:"Undo (Ctrl+Z)",redo:"Redo (Ctrl+Y)",clear:"Clear Formatting",format:"Auto Format"}[e]||e}_getIconName(e){return{bold:"bold",italic:"italic",underline:"underline",strikethrough:"strikethrough",h1:"heading-1",h2:"heading-2",h3:"heading-3",ul:"list-bullet",ol:"list-numbered",blockquote:"quote",link:"link-add",image:"image-add",code:"code-inline",codeblock:"code-block",embed:"embed",undo:"undo",redo:"redo",clear:"clear-format",format:"clear-format"}[e]}_getFallbackIcon(e){return{bold:"<strong>B</strong>",italic:"<em>I</em>",underline:"<u>U</u>",strikethrough:"<s>S</s>",h1:"H1",h2:"H2",h3:"H3",ul:"•",ol:"1.",blockquote:'"',link:"⚓",image:"🖼",code:"</>",codeblock:"{ }",embed:"▶",undo:"↶",redo:"↷",clear:"✕",format:"✨"}[e]||e}_createEditor(){"text"===this.mode?this._createTextEditor():"rich"===this.mode?this._createRichEditor():"code"===this.mode&&this._createCodeEditor()}_createTextEditor(){const e=document.createElement("div");e.className="dm-editor-body",this._editorEl=document.createElement("textarea"),this._editorEl.className="dm-editor-textarea",this._editorEl.placeholder=this.placeholder,this._editorEl.style.minHeight=`${this.minHeight}px`,this._editorEl.addEventListener("input",()=>{this._autoResize(),this._handleChange()}),e.appendChild(this._editorEl),this._container.appendChild(e)}_createRichEditor(){const e=document.createElement("div");e.className="dm-editor-body",this._editorEl=document.createElement("div"),this._editorEl.className="dm-editor-content",this._editorEl.contentEditable="true",this._editorEl.setAttribute("data-placeholder",this.placeholder),this._editorEl.addEventListener("input",()=>this._handleChange()),this._editorEl.addEventListener("keydown",e=>this._handleKeydown(e)),this.imagePaste&&this._editorEl.addEventListener("paste",e=>this._handlePaste(e)),e.appendChild(this._editorEl),this._container.appendChild(e)}_createCodeEditor(){const e=document.createElement("div");e.className="dm-editor-code-wrapper",this.lineNumbers&&(this._lineNumbersEl=document.createElement("div"),this._lineNumbersEl.className="dm-editor-line-numbers",e.appendChild(this._lineNumbersEl)),this._editorEl=document.createElement("textarea"),this._editorEl.className="dm-editor-code-textarea",this._editorEl.placeholder=this.placeholder,this._editorEl.spellcheck=!1,this._editorEl.setAttribute("data-language",this.language),this._editorEl.addEventListener("input",()=>{this._updateLineNumbers(),this._highlightSyntax(),this._handleChange()}),this._editorEl.addEventListener("scroll",()=>{this._lineNumbersEl&&(this._lineNumbersEl.scrollTop=this._editorEl.scrollTop)}),this._editorEl.addEventListener("keydown",e=>this._handleCodeKeydown(e)),e.appendChild(this._editorEl),this._container.appendChild(e)}_createCounter(){const e=document.createElement("div");e.className="dm-editor-footer";const t=document.createElement("div");t.className="dm-editor-status",(this.characterCount||this.wordCount)&&(this._counterEl=document.createElement("div"),this._counterEl.className="dm-editor-count",t.appendChild(this._counterEl)),this.autosave&&(this._autosaveIndicator=document.createElement("div"),this._autosaveIndicator.className="dm-editor-autosave",this._autosaveIndicator.textContent="Auto-save enabled",t.appendChild(this._autosaveIndicator)),e.appendChild(t);const n=document.createElement("div");n.className="dm-editor-history";const a=document.createElement("button");a.type="button",a.className="dm-editor-history-btn",a.textContent="Undo",a.disabled=!0,a.onclick=()=>this.undo(),this._undoBtn=a;const i=document.createElement("button");i.type="button",i.className="dm-editor-history-btn",i.textContent="Redo",i.disabled=!0,i.onclick=()=>this.redo(),this._redoBtn=i,n.appendChild(a),n.appendChild(i),e.appendChild(n),this._container.appendChild(e),this._updateCounter()}_autoResize(){"text"===this.mode&&(this._editorEl.style.height="auto",this._editorEl.style.height=this._editorEl.scrollHeight+"px")}_updateLineNumbers(){if(!this._lineNumbersEl)return;const e=this._editorEl.value.split("\n").length,t=Array.from({length:e},(e,t)=>t+1).join("\n");this._lineNumbersEl.textContent=t}_highlightSyntax(){this._editorEl.setAttribute("data-language",this.language)}_updateCounter(){if(!this._counterEl)return;const e=this.getValue(),t=e.length,n=e.trim().split(/\s+/).filter(e=>e.length>0).length;let a="";this.characterCount&&(a+=`${t} characters`),this.characterCount&&this.wordCount&&(a+=" • "),this.wordCount&&(a+=`${n} words`),this._counterEl.textContent=a}_handleChange(){const e=this.getValue();this._counterEl&&this._updateCounter(),this.model&&this.modelKey&&this.model.set(this.modelKey,e),this.onChange&&this.onChange(e),this._addToHistory(e),this.autosave&&this._triggerAutosave()}_handleKeydown(e){if(e.ctrlKey||e.metaKey)switch(e.key.toLowerCase()){case"b":e.preventDefault(),this._executeCommand("bold");break;case"i":e.preventDefault(),this._executeCommand("italic");break;case"u":e.preventDefault(),this._executeCommand("underline");break;case"z":e.preventDefault(),this.undo();break;case"y":e.preventDefault(),this.redo()}}_handleCodeKeydown(e){if("Tab"===e.key){e.preventDefault();const t=this._editorEl.selectionStart,n=this._editorEl.selectionEnd,a=this._editorEl.value;this._editorEl.value=a.substring(0,t)+" "+a.substring(n),this._editorEl.selectionStart=this._editorEl.selectionEnd=t+4,this._handleChange()}}_handlePaste(e){const t=e.clipboardData?.items;if(t)for(let n=0;n<t.length;n++)if(-1!==t[n].type.indexOf("image")){e.preventDefault();const a=t[n].getAsFile();return void this._insertImage(a)}}async _insertImage(e){let t;if(this.imageUpload&&"function"==typeof this.imageUpload)t=await this.imageUpload(e);else{if("base64"!==this.imageMode)return void console.warn("No image upload handler provided");t=await this._fileToBase64(e)}t&&(document.execCommand("insertImage",!1,t),this.onImagePaste&&this.onImagePaste(t,e))}_fileToBase64(e){return new Promise((t,n)=>{const a=new FileReader;a.onload=()=>t(a.result),a.onerror=n,a.readAsDataURL(e)})}_executeCommand(e){if("rich"===this.mode){switch(e){case"bold":case"italic":case"underline":case"strikethrough":document.execCommand(e);break;case"h1":case"h2":case"h3":document.execCommand("formatBlock",!1,e);break;case"ul":document.execCommand("insertUnorderedList");break;case"ol":document.execCommand("insertOrderedList");break;case"blockquote":document.execCommand("formatBlock",!1,"blockquote");break;case"link":this._insertLink();break;case"image":this._promptImage();break;case"code":document.execCommand("insertHTML",!1,"<code></code>");break;case"codeblock":this._insertCodeBlock();break;case"embed":this._insertEmbed();break;case"undo":this.undo();break;case"redo":this.redo();break;case"clear":this.clear()}this._editorEl.focus()}}_insertLink(){const e=prompt("Enter URL:");e&&document.execCommand("createLink",!1,e)}_promptImage(){const e=prompt("Enter image URL:");e&&document.execCommand("insertImage",!1,e)}_insertCodeBlock(){const e=prompt("Enter code:");if(e){const t=`<pre><code>${this._escapeHtml(e)}</code></pre>`;document.execCommand("insertHTML",!1,t)}}_insertEmbed(){const e=prompt("Enter embed URL (YouTube, Twitter, etc.):");if(!e)return;const t=this._createEmbed(e);t&&document.execCommand("insertHTML",!1,t)}_createEmbed(e){const t=e.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([^&]+)/);if(t)return`<iframe width="560" height="315" src="https://www.youtube.com/embed/${t[1]}" frameborder="0" allowfullscreen></iframe>`;if(e.match(/twitter\.com\/\w+\/status\/(\d+)/))return`<blockquote class="twitter-tweet"><a href="${e}"></a></blockquote>`;const n=e.match(/codepen\.io\/([^\/]+)\/pen\/([^\/]+)/);return n?`<iframe height="400" style="width: 100%;" src="https://codepen.io/${n[1]}/embed/${n[2]}" frameborder="0"></iframe>`:`<a href="${e}" target="_blank">${e}</a>`}_escapeHtml(e){const t=document.createElement("div");return t.textContent=e,t.innerHTML}_addToHistory(e){this._historyIndex<this._history.length-1&&(this._history=this._history.slice(0,this._historyIndex+1)),this._history.push(e),this._history.length>this._maxHistory?this._history.shift():this._historyIndex++,this._updateHistoryButtons()}_setupAutosave(){}_triggerAutosave(){this._autosaveTimer&&clearTimeout(this._autosaveTimer),this._autosaveIndicator&&(this._autosaveIndicator.textContent="Saving...",this._autosaveIndicator.className="dm-editor-autosave saving"),this._autosaveTimer=setTimeout(()=>{this._saveToStorage(),this.onSave&&this.onSave(this.getValue()),this._autosaveIndicator&&(this._autosaveIndicator.textContent="Saved",this._autosaveIndicator.className="dm-editor-autosave saved",setTimeout(()=>{this._autosaveIndicator&&(this._autosaveIndicator.textContent="Auto-save enabled",this._autosaveIndicator.className="dm-editor-autosave")},2e3))},this.autosaveInterval)}_saveToStorage(){if(!this.storageKey)return;const e=this.getValue();localStorage.setItem(`domma:editor:${this.storageKey}`,e)}_loadFromStorage(){if(!this.storageKey)return;const e=localStorage.getItem(`domma:editor:${this.storageKey}`);e&&this.setValue(e)}getValue(){return"rich"===this.mode?this._editorEl.innerHTML:this._editorEl.value}setValue(e){return"rich"===this.mode?this._editorEl.innerHTML=e:this._editorEl.value=e,"code"===this.mode&&(this._updateLineNumbers(),this._highlightSyntax()),"text"===this.mode&&this._autoResize(),this._updateCounter(),this}getText(){return"rich"===this.mode?this._editorEl.textContent:this._editorEl.value}clear(){return this.setValue(""),this._history=[],this._historyIndex=-1,this}focus(){return this._editorEl.focus(),this}blur(){return this._editorEl.blur(),this}undo(){return this._historyIndex>0&&(this._historyIndex--,this.setValue(this._history[this._historyIndex]),this._updateHistoryButtons()),this}redo(){return this._historyIndex<this._history.length-1&&(this._historyIndex++,this.setValue(this._history[this._historyIndex]),this._updateHistoryButtons()),this}_updateHistoryButtons(){this._undoBtn&&(this._undoBtn.disabled=this._historyIndex<=0),this._redoBtn&&(this._redoBtn.disabled=this._historyIndex>=this._history.length-1)}save(){return this._saveToStorage(),this.onSave&&this.onSave(this.getValue()),this}setMode(e){if(e===this.mode)return this;const t=this.getText();return this.mode=e,this._container.innerHTML="",this._container.className=`dm-editor dm-editor-${this.mode} dm-theme-${this.theme}`,!this.showToolbar||"rich"!==this.mode&&"code"!==this.mode||(this.toolbar=this._getDefaultToolbar(),this._createToolbar()),this._createEditor(),(this.characterCount||this.wordCount)&&this._createCounter(),this.setValue(t),this}getMode(){return this.mode}destroy(){this._autosaveTimer&&clearTimeout(this._autosaveTimer),this._modelUnsubscribe&&this._modelUnsubscribe(),this._container&&this._container.parentNode&&this._container.parentNode.removeChild(this._container),this.element.style.display="",super.destroy()}}class x extends b{constructor(e,t={}){super(e,{pageSize:"A4",orientation:"portrait",margins:"normal",title:null,removeBackgrounds:!1,optimizeImages:!0,header:null,footer:null,showPageNumbers:!1,preview:!1,hideElements:[],pageBreaks:"auto",onBeforePrint:null,onAfterPrint:null,onPreview:null,...t}),this._styleElement=null,this._boundBeforePrint=null,this._boundAfterPrint=null,this._hiddenElements=[],this._init()}_init(){this.element?(this._boundBeforePrint=this._handleBeforePrint.bind(this),this._boundAfterPrint=this._handleAfterPrint.bind(this)):console.error("PrintToPDF: Element not found")}_generatePrintCSS(){const{pageSize:e,orientation:t,margins:n,removeBackgrounds:a,optimizeImages:i,pageBreaks:s}=this.options,o={A4:{width:210,height:297},Letter:{width:215.9,height:279.4},Legal:{width:215.9,height:355.6}},r={none:0,narrow:12.7,normal:20,wide:25.4};let l="";if("string"==typeof e&&o[e]){const t=o[e];l=`${t.width}mm ${t.height}mm`}else l="object"==typeof e&&e.width&&e.height?`${e.width}mm ${e.height}mm`:"A4";let d="";return d="string"==typeof n&&void 0!==r[n]?`${r[n]}mm`:"object"==typeof n?`${n.top||20}mm ${n.right||20}mm ${n.bottom||20}mm ${n.left||20}mm`:"20mm",`\n@media print {\n @page {\n size: ${l} ${t};\n margin: ${d};\n }\n\n /* Hide non-printable elements */\n .no-print,\n .dm-modal,\n .dm-toast-container,\n .dm-loader-overlay {\n display: none !important;\n }\n\n /* Optimize body */\n body {\n ${a?"background: white !important;":""}\n print-color-adjust: exact;\n -webkit-print-color-adjust: exact;\n }\n\n /* Table optimizations */\n table {\n page-break-inside: ${"avoid"===s?"avoid":"auto"};\n border-collapse: collapse;\n }\n\n thead {\n display: table-header-group;\n }\n\n tfoot {\n display: table-footer-group;\n }\n\n tr {\n page-break-inside: avoid;\n }\n\n /* Image optimizations */\n ${i?"\n img {\n max-width: 100% !important;\n page-break-inside: avoid;\n }\n ":""}\n\n /* Page break helpers */\n h1, h2, h3, h4, h5, h6 {\n page-break-after: avoid;\n }\n\n .page-break-before {\n page-break-before: always;\n }\n\n .page-break-after {\n page-break-after: always;\n }\n\n .page-break-avoid {\n page-break-inside: avoid;\n }\n\n /* Code blocks */\n pre, code {\n page-break-inside: avoid;\n }\n\n /* Remove backgrounds if requested */\n ${a?"\n * {\n background: transparent !important;\n background-image: none !important;\n }\n\n body {\n background: white !important;\n }\n ":""}\n}\n`}_injectPrintStyles(){if(this._styleElement)return;const e=this._generatePrintCSS();this._styleElement=document.createElement("style"),this._styleElement.setAttribute("data-domma-print-styles","true"),this._styleElement.textContent=e,document.head.appendChild(this._styleElement)}_removePrintStyles(){this._styleElement&&this._styleElement.parentNode&&(this._styleElement.parentNode.removeChild(this._styleElement),this._styleElement=null)}_hideElements(){const{hideElements:e}=this.options;e&&0!==e.length&&e.forEach(e=>{document.querySelectorAll(e).forEach(e=>{"none"!==e.style.display&&(this._hiddenElements.push({element:e,originalDisplay:e.style.display}),e.style.display="none")})})}_restoreElements(){this._hiddenElements.forEach(({element:e,originalDisplay:t})=>{e.style.display=t}),this._hiddenElements=[]}_handleBeforePrint(){this._hideElements(),"function"==typeof this.options.onBeforePrint&&this.options.onBeforePrint()}_handleAfterPrint(){this._restoreElements(),this._removePrintStyles(),window.removeEventListener("beforeprint",this._boundBeforePrint),window.removeEventListener("afterprint",this._boundAfterPrint),"function"==typeof this.options.onAfterPrint&&this.options.onAfterPrint()}preview(){console.warn("PrintToPDF: preview() is not implemented. Configure options and call print() directly."),this.print()}print(){if(!this.element)return void console.error("PrintToPDF: No element to print");const e=document.title;this.options.title&&(document.title=this.options.title),this._injectPrintStyles(),window.addEventListener("beforeprint",this._boundBeforePrint),window.addEventListener("afterprint",this._boundAfterPrint),window.print(),this.options.title&&(document.title=e)}destroy(){this._removePrintStyles(),this._restoreElements(),this._previewContainer&&this._previewContainer.parentNode&&(this._previewContainer.parentNode.removeChild(this._previewContainer),this._previewContainer=null),this._boundBeforePrint&&window.removeEventListener("beforeprint",this._boundBeforePrint),this._boundAfterPrint&&window.removeEventListener("afterprint",this._boundAfterPrint),super.destroy()}}const w={themeRoller(e,t={}){const n=new p(e,t);return n.element&&"undefined"!=typeof Domma&&Domma.elements._instances.set(n.element,n),n},pageRoller(e,t={}){const n=new g(e,t);return n.element&&"undefined"!=typeof Domma&&Domma.elements._instances.set(n.element,n),n},schemaBuilder(e,t={}){const n=new v(e,t);return n.element&&"undefined"!=typeof Domma&&Domma.elements._instances.set(n.element,n),n},editor(e,t={}){const n=new _(e,t);return n.element&&"undefined"!=typeof Domma&&Domma.elements._instances.set(n.element,n),n},printToPDF(e,t={}){const n=new x(e,t);return n.element&&"undefined"!=typeof Domma&&Domma.elements._instances.set(n.element,n),n}};"undefined"!=typeof Domma&&Domma.elements&&(Domma.elements.themeRoller=w.themeRoller,Domma.elements.pageRoller=w.pageRoller,Domma.elements.schemaBuilder=w.schemaBuilder,Domma.elements.editor=w.editor,Domma.elements.printToPDF=w.printToPDF),"undefined"!=typeof window&&(window.DommaTools=w),e.Editor=_,e.PageRoller=g,e.PrintToPDF=x,e.SchemaBuilder=v,e.ThemeRoller=p,e.default=w,e.tools=w,Object.defineProperty(e,"__esModule",{value:!0})});