@customviews-js/customviews 1.1.3 → 1.1.4

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.
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * @customviews-js/customviews v1.1.3
2
+ * @customviews-js/customviews v1.1.4
3
3
  * (c) 2025 Chan Ger Teck
4
4
  * Released under the MIT License.
5
5
  */
6
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).CustomViews={})}(this,function(t){"use strict";class e{static STORAGE_KEYS={STATE:"customviews-state"};isStorageAvailable(){return"undefined"!=typeof window&&void 0!==window.localStorage}persistState(t){if(this.isStorageAvailable())try{localStorage.setItem(e.STORAGE_KEYS.STATE,JSON.stringify(t))}catch(t){console.warn("Failed to persist state:",t)}}getPersistedState(){if(!this.isStorageAvailable())return null;try{const t=localStorage.getItem(e.STORAGE_KEYS.STATE);return t?JSON.parse(t):null}catch(t){return console.warn("Failed to parse persisted state:",t),null}}clearAll(){this.isStorageAvailable()&&localStorage.removeItem(e.STORAGE_KEYS.STATE)}hasPersistedData(){return!!this.isStorageAvailable()&&!!this.getPersistedState()}}class n{static parseURL(){const t=new URLSearchParams(window.location.search).get("view");let e=null;if(t)try{e=this.decodeState(t)}catch(t){console.warn("Failed to decode view state from URL:",t)}return e}static updateURL(t){if("undefined"==typeof window||!window.history)return;const e=new URL(window.location.href);if(e.searchParams.delete("view"),t){const n=this.encodeState(t);n&&e.searchParams.set("view",n)}const n=e.pathname+(e.search||"")+(e.hash||"");window.history.replaceState({},"",n)}static clearURL(){this.updateURL(null)}static generateShareableURL(t){const e=new URL(window.location.href);if(e.searchParams.delete("view"),t){const n=this.encodeState(t);n&&e.searchParams.set("view",n)}return e.toString()}static encodeState(t){try{const e={};t.toggles&&t.toggles.length>0&&(e.t=t.toggles),t.tabs&&Object.keys(t.tabs).length>0&&(e.g=Object.entries(t.tabs));const n=JSON.stringify(e);let o;o="function"==typeof btoa?btoa(n):Buffer.from(n,"utf-8").toString("base64");return o.replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}catch(t){return console.warn("Failed to encode state:",t),null}}static decodeState(t){try{let e,n=t.replace(/-/g,"+").replace(/_/g,"/");for(;n.length%4;)n+="=";e="function"==typeof atob?atob(n):Buffer.from(n,"base64").toString("utf-8");const o=JSON.parse(e);if(!o||"object"!=typeof o)throw new Error("Invalid compact state structure");const s={toggles:Array.isArray(o.t)?o.t:[]};if(Array.isArray(o.g)){s.tabs={};for(const[t,e]of o.g)"string"==typeof t&&"string"==typeof e&&(s.tabs[t]=e)}return s}catch(t){return console.warn("Failed to decode view state:",t),null}}}class o{hiddenToggles=new Set;setToggleVisibility(t,e){const n=this.hiddenToggles.has(t),o=!e;return o&&!n?(this.hiddenToggles.add(t),!0):!(o||!n)&&(this.hiddenToggles.delete(t),!0)}hideAll(t){for(const e of t)this.setToggleVisibility(e,!1)}showAll(t){for(const e of t)this.setToggleVisibility(e,!0)}getHiddenToggles(){return Array.from(this.hiddenToggles)}filterVisibleToggles(t){return t.filter(t=>!this.hiddenToggles.has(t))}applyElementVisibility(t,e){e?(t.classList.remove("cv-hidden"),t.classList.add("cv-visible")):(t.classList.add("cv-hidden"),t.classList.remove("cv-visible"))}}function s(t,e,n){const o=n.get(e);if(!o)return;const s=o.type||function(t){return t.src?"image":t.content&&/<[^>]+>/.test(t.content)?"html":"text"}(o);switch(s){case"image":!function(t,e){if(!e.src)return;t.innerHTML="";const n=document.createElement("img");n.src=e.src,n.alt=e.alt||"",e.className&&(n.className=e.className),e.style&&n.setAttribute("style",e.style),n.style.maxWidth=n.style.maxWidth||"100%",n.style.height=n.style.height||"auto",n.style.display=n.style.display||"block",t.appendChild(n)}(t,o);break;case"text":!function(t,e){null!=e.content&&(t.textContent=e.content),e.className&&(t.className=e.className),e.style&&t.setAttribute("style",e.style)}(t,o);break;case"html":!function(t,e){null!=e.content&&(t.innerHTML=e.content),e.className&&(t.className=e.className),e.style&&t.setAttribute("style",e.style)}(t,o);break;default:t.innerHTML=o.content||String(o),console.warn("[CustomViews] Unknown asset type:",s)}}const i="cv-tabgroup",a="cv-tab",r="cv-tabs-nav";class c{static applySelections(t,e,n){t.querySelectorAll(i).forEach(t=>{const o=t.getAttribute("id");if(!o)return;const s=this.resolveActiveTab(o,e,n,t);Array.from(t.children).filter(t=>t.tagName.toLowerCase()===a).forEach(t=>{const e=t.getAttribute("id");if(!e)return;const n=e===s;this.applyTabVisibility(t,n)})})}static resolveActiveTab(t,e,n,o){if(e[t])return e[t];if(n){const e=n.find(e=>e.id===t);if(e){if(e.default)return e.default;const t=e.tabs[0];if(t)return t.id}}const s=Array.from(o.children).find(t=>t.tagName.toLowerCase()===a);return s?s.getAttribute("id"):null}static applyTabVisibility(t,e){e?(t.classList.remove("cv-hidden"),t.classList.add("cv-visible")):(t.classList.add("cv-hidden"),t.classList.remove("cv-visible"))}static buildNavs(t,e,n){const o=t.querySelectorAll('cv-tabgroup[nav="auto"], cv-tabgroup:not([nav])');let s=!1;o.forEach(t=>{const n=t.getAttribute("id");if(!n)return;Array.from(t.children).filter(t=>"cv-tab"===t.tagName.toLowerCase()).forEach(t=>{const o=t.getAttribute("id");if(!o)return;const i=t.getAttribute("header")||this.getTabLabel(o,n,e)||o;/:fa-[\w-]+:/.test(i)&&(s=!0)})}),s&&function(){if(Array.from(document.styleSheets).some(t=>t.href&&(t.href.includes("font-awesome")||t.href.includes("fontawesome"))))return;const t=document.createElement("link");t.rel="stylesheet",t.href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css",t.setAttribute("data-customviews-fontawesome","true"),document.head.appendChild(t)}(),o.forEach(t=>{const o=t.getAttribute("id");if(!o)return;let s=t.querySelector(`.${r}`);if(s)return;const i=Array.from(t.children).filter(t=>t.tagName.toLowerCase()===a);if(0===i.length)return;s=document.createElement("ul"),s.className=`${r} nav-tabs`,s.setAttribute("role","tablist"),t.insertBefore(s,t.firstChild),i.forEach(t=>{const i=t.getAttribute("id");if(!i)return;const a=t.getAttribute("header")||this.getTabLabel(i,o,e)||i,r=document.createElement("li");r.className="nav-item";const c=document.createElement("a");c.className="nav-link",c.innerHTML=a.replace(/:fa-([\w-]+):/g,(t,e)=>`<i class="fa fa-${e}"></i>`),c.href="#",c.setAttribute("data-tab-id",i),c.setAttribute("data-group-id",o),c.setAttribute("role","tab");t.classList.contains("cv-visible")?(c.classList.add("active"),c.setAttribute("aria-selected","true")):c.setAttribute("aria-selected","false"),n&&c.addEventListener("click",t=>{t.preventDefault(),n(o,i)}),r.appendChild(c),s.appendChild(r)});const c=document.createElement("div");c.className="cv-tabgroup-bottom-border",t.appendChild(c)})}static getTabLabel(t,e,n){if(!n)return null;const o=n.find(t=>t.id===e);if(!o)return null;const s=o.tabs.find(e=>e.id===t);return s?.label||null}static updateNavActiveState(t,e,n){t.querySelectorAll(`${i}[id="${e}"]`).forEach(t=>{t.querySelectorAll(".nav-link").forEach(t=>{t.getAttribute("data-tab-id")===n?(t.classList.add("active"),t.setAttribute("aria-selected","true")):(t.classList.remove("active"),t.setAttribute("aria-selected","false"))})})}static updateAllNavActiveStates(t,e,n){t.querySelectorAll(i).forEach(t=>{const o=t.getAttribute("id");if(!o)return;const s=this.resolveActiveTab(o,e,n,t);if(!s)return;t.querySelectorAll(".nav-link").forEach(t=>{t.getAttribute("data-tab-id")===s?(t.classList.add("active"),t.setAttribute("aria-selected","true")):(t.classList.remove("active"),t.setAttribute("aria-selected","false"))})})}}class l{assets;baseURL;constructor(t,e=""){this.assets=t,this.baseURL=e,this.validate()||console.warn("Invalid assets:",this.assets)}validate(){return Object.values(this.assets).every(t=>t.src||t.content)}get(t){const e=this.assets[t];if(e)return this.baseURL&&e.src?{...e,src:this.prependBaseURL(e.src)}:e}prependBaseURL(t){if(t.startsWith("http://")||t.startsWith("https://"))return t;return(this.baseURL.endsWith("/")?this.baseURL.slice(0,-1):this.baseURL)+(t.startsWith("/")?t:"/"+t)}loadFromJSON(t){this.assets=t}loadAdditionalAssets(t){this.assets={...this.assets,...t}}}const d="[data-cv-toggle], [data-customviews-toggle], cv-toggle";class g{static applyToggles(t,e){t.querySelectorAll(d).forEach(t=>{const n=this.getToggleCategories(t).some(t=>e.includes(t));this.applyToggleVisibility(t,n)})}static renderAssets(t,e,n){t.querySelectorAll(d).forEach(t=>{const o=this.getToggleCategories(t),i=this.getToggleId(t);i&&o.some(t=>e.includes(t))&&s(t,i,n)})}static getToggleCategories(t){if("cv-toggle"===t.tagName.toLowerCase()){return(t.getAttribute("category")||"").split(/\s+/).filter(Boolean)}return(t.dataset.cvToggle||t.dataset.customviewsToggle||"").split(/\s+/).filter(Boolean)}static getToggleId(t){return t.dataset.cvId||t.dataset.customviewsId||t.getAttribute("data-cv-id")||t.getAttribute("data-customviews-id")||void 0}static applyToggleVisibility(t,e){e?(t.classList.remove("cv-hidden"),t.classList.add("cv-visible")):(t.classList.add("cv-hidden"),t.classList.remove("cv-visible"))}}class h{rootEl;assetsManager;persistenceManager;visibilityManager;config;stateChangeListeners=[];showUrlEnabled;lastAppliedState=null;constructor(t){this.assetsManager=t.assetsManager,this.config=t.config,this.rootEl=t.rootEl||document.body,this.persistenceManager=new e,this.visibilityManager=new o,this.showUrlEnabled=t.showUrl??!1,this.lastAppliedState=this.cloneState(this.config?.defaultState)}getConfig(){return this.config}getTabGroups(){return this.config.tabGroups}getCurrentActiveTabs(){if(this.lastAppliedState?.tabs)return{...this.lastAppliedState.tabs};const t=this.persistenceManager.getPersistedState();return t?.tabs?{...t.tabs}:this.config?.defaultState?.tabs?{...this.config.defaultState.tabs}:{}}setActiveTab(t,e){const n={toggles:this.getCurrentActiveToggles(),tabs:{...this.getCurrentActiveTabs(),[t]:e}};this.applyState(n);const o=new CustomEvent("customviews:tab-change",{detail:{groupId:t,tabId:e},bubbles:!0});document.dispatchEvent(o)}async init(){!function(){if("undefined"==typeof document)return;if(document.querySelector("#cv-core-styles"))return;const t=document.createElement("style");t.id="cv-core-styles",t.textContent="\n\n/* Core toggle visibility transitions */\n[data-cv-toggle], [data-customviews-toggle], cv-toggle {\n transition: opacity 150ms ease,\n transform 150ms ease,\n max-height 200ms ease,\n margin 150ms ease;\n will-change: opacity, transform, max-height, margin;\n}\n\n.cv-visible {\n opacity: 1 !important;\n transform: translateY(0) !important;\n max-height: var(--cv-max-height, 9999px) !important;\n}\n\n.cv-hidden {\n opacity: 0 !important;\n transform: translateY(-4px) !important;\n pointer-events: none !important;\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n border-top-width: 0 !important;\n border-bottom-width: 0 !important;\n max-height: 0 !important;\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n overflow: hidden !important;\n}\n\n\n\n/* Tab navigation styles - Bootstrap-style tabs matching MarkBind */\n.cv-tabs-nav {\n display: flex;\n flex-wrap: wrap;\n padding-left: 0;\n margin-top: 0.5rem;\n margin-bottom: 1rem;\n list-style: none;\n border-bottom: 1px solid #dee2e6;\n}\n\n.cv-tabs-nav .nav-item {\n margin-bottom: -1px;\n list-style: none;\n display: inline-block;\n}\n\n.cv-tabs-nav .nav-link {\n display: block;\n padding: 0.5rem 1rem;\n color: #495057;\n text-decoration: none;\n background-color: transparent;\n border: 1px solid transparent;\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;\n cursor: pointer;\n}\n\n.cv-tabs-nav .nav-link:hover,\n.cv-tabs-nav .nav-link:focus {\n border-color: #e9ecef #e9ecef #dee2e6;\n isolation: isolate;\n}\n\n.cv-tabs-nav .nav-link.active {\n color: #495057;\n background-color: #fff;\n border-color: #dee2e6 #dee2e6 #fff;\n}\n\n.cv-tabs-nav .nav-link:focus {\n outline: 0;\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n\n/* Legacy button-based nav (deprecated, kept for compatibility) */\n.cv-tabs-nav-item {\n background: none;\n border: none;\n border-bottom: 2px solid transparent;\n padding: 0.5rem 1rem;\n cursor: pointer;\n font-size: 1rem;\n color: #6c757d;\n transition: color 150ms ease, border-color 150ms ease;\n}\n\n.cv-tabs-nav-item:hover {\n color: #495057;\n border-bottom-color: #dee2e6;\n}\n\n.cv-tabs-nav-item.active {\n color: #007bff;\n border-bottom-color: #007bff;\n font-weight: 500;\n}\n\n.cv-tabs-nav-item:focus {\n outline: 2px solid #007bff;\n outline-offset: 2px;\n}\n\n/* Tab panel base styles */\ncv-tab {\n display: block;\n}\n\n/* Override visibility for tab panels - use display instead of collapse animation */\ncv-tab.cv-hidden {\n display: none !important;\n}\n\ncv-tab.cv-visible {\n display: block !important;\n}\n\ncv-tabgroup {\n display: block;\n margin-bottom: 1.5rem;\n}\n\n/* Bottom border line for tab groups */\n.cv-tabgroup-bottom-border {\n border-bottom: 1px solid #dee2e6;\n margin-top: 1rem;\n}\n\n/* Tab content wrapper */\n.cv-tab-content {\n padding: 1rem 0;\n}\n\n",document.head.appendChild(t)}(),c.buildNavs(this.rootEl,this.config.tabGroups,(t,e)=>{this.setActiveTab(t,e)}),window.addEventListener("popstate",()=>{this.loadAndCallApplyState()}),this.loadAndCallApplyState()}async loadAndCallApplyState(){const t=n.parseURL();if(t)return void this.applyState(t);const e=this.persistenceManager.getPersistedState();e?this.applyState(e):this.renderState(this.config.defaultState)}applyState(t){const e=this.cloneState(t);this.renderState(e),this.persistenceManager.persistState(e),this.showUrlEnabled?n.updateURL(e):n.clearURL()}renderState(t){this.lastAppliedState=this.cloneState(t);const e=t.toggles||[],n=this.visibilityManager.filterVisibleToggles(e);g.applyToggles(this.rootEl,n),g.renderAssets(this.rootEl,n,this.assetsManager),c.applySelections(this.rootEl,t.tabs||{},this.config.tabGroups),c.updateAllNavActiveStates(this.rootEl,t.tabs||{},this.config.tabGroups),this.notifyStateChangeListeners()}resetToDefault(){this.persistenceManager.clearAll(),this.config?this.renderState(this.config.defaultState):console.warn("No configuration loaded, cannot reset to default state"),n.clearURL()}getCurrentActiveToggles(){return this.lastAppliedState?this.lastAppliedState.toggles||[]:this.config&&this.config.defaultState.toggles||[]}clearPersistence(){this.persistenceManager.clearAll(),this.config?this.renderState(this.config.defaultState):console.warn("No configuration loaded, cannot reset to default state"),n.clearURL()}setOption(t,e){switch(t){case"showUrl":{const t=Boolean(e);if(this.showUrlEnabled===t)return;if(this.showUrlEnabled=t,t){const t=this.getTrackedStateSnapshot();n.updateURL(t)}else n.clearURL();break}default:console.warn(`[CustomViews] Unknown option '${t}' passed to setOption`)}}addStateChangeListener(t){this.stateChangeListeners.push(t)}removeStateChangeListener(t){const e=this.stateChangeListeners.indexOf(t);e>-1&&this.stateChangeListeners.splice(e,1)}notifyStateChangeListeners(){this.stateChangeListeners.forEach(t=>{try{t()}catch(t){console.warn("Error in state change listener:",t)}})}cloneState(t){return t?JSON.parse(JSON.stringify(t)):{}}getTrackedStateSnapshot(){return this.lastAppliedState?this.cloneState(this.lastAppliedState):this.config?this.cloneState(this.config.defaultState):{}}}function u(t,e){if(!e)return t;if(t.startsWith("http://")||t.startsWith("https://"))return t;return(e.endsWith("/")?e.slice(0,-1):e)+(t.startsWith("/")?t:"/"+t)}class p extends HTMLElement{connectedCallback(){}}class m extends HTMLElement{connectedCallback(){setTimeout(()=>{const t=new CustomEvent("cv:tabgroup-ready",{bubbles:!0,detail:{groupId:this.getAttribute("id")}});this.dispatchEvent(t)},0)}}class v extends HTMLElement{connectedCallback(){}}class b{static async init(t){let e;customElements.get("cv-tab")||customElements.define("cv-tab",p),customElements.get("cv-tabgroup")||customElements.define("cv-tabgroup",m),customElements.get("cv-toggle")||customElements.define("cv-toggle",v);const n=t.baseURL||"";if(t.assetsJsonPath){const o=u(t.assetsJsonPath,n);try{const t=await(await fetch(o)).json();e=new l(t,n)}catch(t){console.error(`[CustomViews] Failed to load assets JSON from ${o}:`,t),e=new l({},n)}}else e=new l({},n);let o;t.config?o=t.config:(console.error("No config provided, using minimal default config"),o={allToggles:[],defaultState:{}});const s={assetsManager:e,config:o,rootEl:t.rootEl};void 0!==t.showUrl&&(s.showUrl=t.showUrl);const i=new h(s);return i.init(),i}}class f{core;container;widgetIcon=null;options;modal=null;constructor(t){this.core=t.core,this.container=t.container||document.body,this.options={core:t.core,container:this.container,position:t.position||"middle-left",theme:t.theme||"light",showReset:t.showReset??!0,title:t.title||"Customize View",description:t.description||"Toggle different content sections to customize your view. Changes are applied instantly and the URL will be updated for sharing.",showWelcome:t.showWelcome??!1,welcomeTitle:t.welcomeTitle||"Site Customization",welcomeMessage:t.welcomeMessage||'This site is powered by Custom Views. Use the widget on the side (⚙) to customize your experience. Your preferences will be saved and can be shared via URL.<br><br>Learn more at <a href="https://github.com/customviews-js/customviews" target="_blank">customviews GitHub</a>.',showTabGroups:t.showTabGroups??!0}}render(){return this.widgetIcon=this.createWidgetIcon(),this.attachEventListeners(),document.body.appendChild(this.widgetIcon),this.options.showWelcome&&this.showWelcomeModalIfFirstVisit(),this.widgetIcon}createWidgetIcon(){const t=document.createElement("div");return t.className=`cv-widget-icon cv-widget-${this.options.position}`,t.innerHTML="⚙",t.title=this.options.title,t.setAttribute("aria-label","Open Custom Views"),function(){if(document.querySelector("#cv-widget-styles"))return;const t=document.createElement("style");t.id="cv-widget-styles",t.textContent="\n/* Rounded rectangle widget icon styles */\n.cv-widget-icon {\n position: fixed;\n /* Slightly transparent by default so the widget is subtle at the page edge */\n background: rgba(255, 255, 255, 0.92);\n color: rgba(0, 0, 0, 0.9);\n opacity: 0.6;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n font-weight: bold;\n cursor: pointer;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n z-index: 9998;\n transition: all 0.3s ease;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.cv-widget-icon:hover {\n /* Become fully opaque on hover to improve readability */\n background: rgba(255, 255, 255, 1);\n color: rgba(0, 0, 0, 1);\n opacity: 1;\n}\n\n/* Top-right: rounded end on left, sticks out leftward on hover */\n.cv-widget-top-right {\n top: 20px;\n right: 0;\n border-radius: 18px 0 0 18px;\n padding-left: 8px;\n justify-content: flex-start;\n}\n\n/* Top-left: rounded end on right, sticks out rightward on hover */\n.cv-widget-top-left {\n top: 20px;\n left: 0;\n border-radius: 0 18px 18px 0;\n padding-right: 8px;\n justify-content: flex-end;\n}\n\n/* Bottom-right: rounded end on left, sticks out leftward on hover */\n.cv-widget-bottom-right {\n bottom: 20px;\n right: 0;\n border-radius: 18px 0 0 18px;\n padding-left: 8px;\n justify-content: flex-start;\n}\n\n/* Bottom-left: rounded end on right, sticks out rightward on hover */\n.cv-widget-bottom-left {\n bottom: 20px;\n left: 0;\n border-radius: 0 18px 18px 0;\n padding-right: 8px;\n justify-content: flex-end;\n}\n\n/* Middle-left: rounded end on right, sticks out rightward on hover */\n.cv-widget-middle-left {\n top: 50%;\n left: 0;\n transform: translateY(-50%);\n border-radius: 0 18px 18px 0;\n padding-right: 8px;\n justify-content: flex-end;\n}\n\n/* Middle-right: rounded end on left, sticks out leftward on hover */\n.cv-widget-middle-right {\n top: 50%;\n right: 0;\n transform: translateY(-50%);\n border-radius: 18px 0 0 18px;\n padding-left: 8px;\n justify-content: flex-start;\n}\n\n.cv-widget-top-right,\n.cv-widget-middle-right,\n.cv-widget-bottom-right,\n.cv-widget-top-left,\n.cv-widget-middle-left,\n.cv-widget-bottom-left {\n height: 36px;\n width: 36px;\n}\n\n.cv-widget-middle-right:hover,\n.cv-widget-top-right:hover,\n.cv-widget-bottom-right:hover,\n.cv-widget-top-left:hover,\n.cv-widget-middle-left:hover,\n.cv-widget-bottom-left:hover {\n width: 55px;\n}\n\n/* Modal content styles */\n.cv-widget-section {\n margin-bottom: 16px;\n}\n\n.cv-widget-section:last-child {\n margin-bottom: 0;\n}\n\n.cv-widget-section label {\n display: block;\n margin-bottom: 4px;\n font-weight: 500;\n color: #555;\n}\n\n.cv-widget-profile-select,\n.cv-widget-state-select {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 4px;\n background: white;\n font-size: 14px;\n}\n\n.cv-widget-profile-select:focus,\n.cv-widget-state-select:focus {\n outline: none;\n border-color: #007bff;\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.cv-widget-profile-select:disabled,\n.cv-widget-state-select:disabled {\n background: #f8f9fa;\n color: #6c757d;\n cursor: not-allowed;\n}\n\n.cv-widget-current {\n margin: 16px 0;\n padding: 12px;\n background: #f8f9fa;\n border-radius: 4px;\n border-left: 4px solid #007bff;\n}\n\n.cv-widget-current label {\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: #666;\n margin-bottom: 4px;\n}\n\n.cv-widget-current-view {\n font-weight: 500;\n color: #333;\n}\n\n.cv-widget-reset {\n width: 100%;\n padding: 8px 16px;\n background: #dc3545;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n}\n\n.cv-widget-reset:hover {\n background: #c82333;\n}\n\n.cv-widget-reset:active {\n background: #bd2130;\n}\n\n/* Responsive design for mobile */\n@media (max-width: 768px) {\n .cv-widget-top-right,\n .cv-widget-top-left {\n top: 10px;\n }\n\n .cv-widget-bottom-right,\n .cv-widget-bottom-left {\n bottom: 10px;\n }\n\n /* All widgets stay flush with screen edges */\n .cv-widget-top-right,\n .cv-widget-bottom-right,\n .cv-widget-middle-right {\n right: 0;\n }\n\n .cv-widget-top-left,\n .cv-widget-bottom-left,\n .cv-widget-middle-left {\n left: 0;\n }\n\n /* Slightly smaller on mobile */\n .cv-widget-icon {\n width: 60px;\n height: 32px;\n }\n\n .cv-widget-icon:hover {\n width: 75px;\n }\n}\n\n/* Modal styles */\n.cv-widget-modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10002;\n animation: fadeIn 0.2s ease;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.cv-widget-modal {\n background: white;\n border-radius: 8px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n max-width: 400px;\n width: 90vw;\n max-height: 80vh;\n overflow-y: auto;\n animation: slideIn 0.2s ease;\n}\n\n@keyframes slideIn {\n from { \n opacity: 0;\n transform: scale(0.9) translateY(-20px);\n }\n to { \n opacity: 1;\n transform: scale(1) translateY(0);\n }\n}\n\n.cv-widget-modal-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid #e9ecef;\n background: #f8f9fa;\n border-radius: 8px 8px 0 0;\n}\n\n.cv-widget-modal-header h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: #333;\n}\n\n.cv-widget-modal-close {\n background: none;\n border: none;\n font-size: 20px;\n cursor: pointer;\n padding: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n color: #666;\n line-height: 1;\n transition: all 0.2s ease;\n}\n\n.cv-widget-modal-close:hover {\n background: #e9ecef;\n color: #333;\n}\n\n.cv-widget-modal-content {\n padding: 20px;\n}\n\n.cv-widget-modal-actions {\n margin-top: 20px;\n padding-top: 16px;\n border-top: 1px solid #e9ecef;\n}\n\n.cv-widget-restore {\n width: 100%;\n padding: 10px 16px;\n background: #28a745;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n}\n\n.cv-widget-restore:hover {\n background: #218838;\n}\n\n.cv-widget-create-state {\n width: 100%;\n padding: 10px 16px;\n background: #007bff;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n margin-bottom: 10px;\n}\n\n.cv-widget-create-state:hover {\n background: #0056b3;\n}\n\n/* Dark theme modal styles */\n.cv-widget-theme-dark .cv-widget-modal {\n background: #2d3748;\n color: #e2e8f0;\n}\n\n.cv-widget-theme-dark .cv-widget-modal-header {\n background: #1a202c;\n border-color: #4a5568;\n}\n\n.cv-widget-theme-dark .cv-widget-modal-header h3 {\n color: #e2e8f0;\n}\n\n.cv-widget-theme-dark .cv-widget-modal-close {\n color: #a0aec0;\n}\n\n.cv-widget-theme-dark .cv-widget-modal-close:hover {\n background: #4a5568;\n color: #e2e8f0;\n}\n\n.cv-widget-theme-dark .cv-widget-modal-actions {\n border-color: #4a5568;\n}\n\n/* Custom state creator styles */\n.cv-custom-state-modal {\n max-width: 500px;\n}\n\n.cv-custom-state-form h4 {\n margin: 20px 0 10px 0;\n font-size: 16px;\n font-weight: 600;\n color: #333;\n border-bottom: 1px solid #e9ecef;\n padding-bottom: 5px;\n}\n\n.cv-custom-state-form p {\n font-size: 15px;\n line-height: 1.6;\n color: #555;\n margin-bottom: 24px;\n text-align: justify;\n}\n\n.cv-custom-state-section {\n margin-bottom: 16px;\n}\n\n.cv-custom-state-section label {\n display: block;\n margin-bottom: 4px;\n font-weight: 500;\n color: #555;\n}\n\n.cv-custom-state-input {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 4px;\n background: white;\n font-size: 14px;\n}\n\n.cv-custom-state-input:focus {\n outline: none;\n border-color: #007bff;\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.cv-custom-toggles {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));\n gap: 10px;\n}\n\n.cv-custom-state-toggle {\n display: flex;\n align-items: center;\n}\n\n.cv-custom-state-toggle label {\n display: flex;\n align-items: center;\n cursor: pointer;\n font-weight: normal;\n margin: 0;\n}\n\n.cv-toggle-switch {\n position: relative;\n width: 44px;\n height: 24px;\n background: #ccc;\n border-radius: 12px;\n margin-right: 12px;\n cursor: pointer;\n transition: background-color 0.3s ease;\n flex-shrink: 0;\n}\n\n.cv-toggle-switch:hover {\n background: #bbb;\n}\n\n.cv-toggle-switch.cv-toggle-active {\n background: #007bff;\n}\n\n.cv-toggle-switch.cv-toggle-active:hover {\n background: #0056b3;\n}\n\n.cv-toggle-handle {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 20px;\n height: 20px;\n background: white;\n border-radius: 50%;\n transition: transform 0.3s ease;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n}\n\n.cv-toggle-switch.cv-toggle-active .cv-toggle-handle {\n transform: translateX(20px);\n}\n\n/* Dark theme toggle switch styles */\n.cv-widget-theme-dark .cv-toggle-switch {\n background: #4a5568;\n}\n\n.cv-widget-theme-dark .cv-toggle-switch:hover {\n background: #5a6578;\n}\n\n.cv-widget-theme-dark .cv-toggle-switch.cv-toggle-active {\n background: #63b3ed;\n}\n\n.cv-widget-theme-dark .cv-toggle-switch.cv-toggle-active:hover {\n background: #4299e1;\n}\n\n.cv-tab-groups {\n margin-top: 20px;\n}\n\n.cv-tab-group-control {\n margin-bottom: 15px;\n}\n\n.cv-tab-group-control label {\n display: block;\n margin-bottom: 5px;\n font-weight: 500;\n font-size: 14px;\n}\n\n.cv-tab-group-select {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid #ced4da;\n border-radius: 4px;\n font-size: 14px;\n background-color: white;\n cursor: pointer;\n}\n\n.cv-tab-group-select:focus {\n outline: none;\n border-color: #007bff;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.cv-widget-theme-dark .cv-tab-group-select {\n background-color: #2d3748;\n border-color: #4a5568;\n color: #e2e8f0;\n}\n\n.cv-custom-state-actions {\n display: flex;\n gap: 10px;\n margin-top: 20px;\n padding-top: 16px;\n border-top: 1px solid #e9ecef;\n}\n\n.cv-custom-state-cancel,\n.cv-custom-state-copy-url {\n flex: 1;\n padding: 10px 16px;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n}\n\n.cv-custom-state-reset {\n flex: 1;\n padding: 10px 16px;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n background: #dc3545;\n color: white;\n}\n\n.cv-custom-state-reset:hover {\n background: #c82333;\n}\n\n.cv-custom-state-cancel {\n background: #6c757d;\n color: white;\n}\n\n.cv-custom-state-cancel:hover {\n background: #5a6268;\n}\n\n.cv-custom-state-copy-url {\n background: #28a745;\n color: white;\n}\n\n.cv-custom-state-copy-url:hover {\n background: #218838;\n}\n\n/* Dark theme custom state styles */\n.cv-widget-theme-dark .cv-custom-state-form h4 {\n color: #e2e8f0;\n border-color: #4a5568;\n}\n\n.cv-widget-theme-dark .cv-custom-state-form p {\n color: #cbd5e0;\n}\n\n.cv-widget-theme-dark .cv-custom-state-section label {\n color: #a0aec0;\n}\n\n.cv-widget-theme-dark .cv-custom-state-input {\n background: #1a202c;\n border-color: #4a5568;\n color: #e2e8f0;\n}\n\n.cv-widget-theme-dark .cv-custom-state-actions {\n border-color: #4a5568;\n}\n\n/* Welcome modal styles */\n.cv-welcome-modal {\n max-width: 500px;\n}\n\n.cv-welcome-content {\n text-align: center;\n}\n\n.cv-welcome-content p {\n font-size: 15px;\n line-height: 1.6;\n color: #555;\n margin-bottom: 24px;\n text-align: justify;\n}\n\n.cv-welcome-widget-preview {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 20px;\n background: #f8f9fa;\n border-radius: 8px;\n margin-bottom: 24px;\n}\n\n.cv-welcome-widget-icon {\n width: 36px;\n height: 36px;\n background: white;\n color: black;\n border-radius: 0 18px 18px 0;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n}\n\n.cv-welcome-widget-label {\n font-size: 14px;\n color: #666;\n margin: 0;\n font-weight: 500;\n}\n\n.cv-welcome-got-it {\n width: 100%;\n padding: 12px 24px;\n background: #007bff;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 16px;\n font-weight: 600;\n transition: background 0.2s ease;\n}\n\n.cv-welcome-got-it:hover {\n background: #0056b3;\n}\n\n.cv-welcome-got-it:active {\n background: #004494;\n}\n\n/* Dark theme welcome modal styles */\n.cv-widget-theme-dark .cv-welcome-content p {\n color: #cbd5e0;\n}\n\n.cv-widget-theme-dark .cv-welcome-widget-preview {\n background: #1a202c;\n}\n\n.cv-widget-theme-dark .cv-welcome-widget-label {\n color: #a0aec0;\n}\n",document.head.appendChild(t)}(),t}destroy(){this.widgetIcon&&(this.widgetIcon.remove(),this.widgetIcon=null),this.modal&&(this.modal.remove(),this.modal=null)}attachEventListeners(){this.widgetIcon&&this.widgetIcon.addEventListener("click",()=>this.openStateModal())}closeModal(){this.modal&&(this.modal.remove(),this.modal=null)}openStateModal(){const t=this.core.getConfig(),e=t?.allToggles||[];this.createCustomStateModal(e)}createCustomStateModal(t){this.closeModal(),this.modal=document.createElement("div"),this.modal.className="cv-widget-modal-overlay",this.applyThemeToModal();const e=t.length?t.map(t=>`\n <div class="cv-custom-state-toggle">\n <label>\n <div class="cv-toggle-switch" data-toggle="${t}">\n <div class="cv-toggle-handle"></div>\n </div>\n ${this.formatToggleName(t)}\n </label>\n </div>\n `).join(""):'<p class="cv-no-toggles">No configurable sections available.</p>',n=this.core.getTabGroups();let o="";if(this.options.showTabGroups&&n&&n.length>0){o=`\n <h4>Tab Groups</h4>\n <div class="cv-tab-groups">\n ${n.map(t=>{const e=t.tabs.map(t=>`<option value="${t.id}">${t.label||t.id}</option>`).join("");return`\n <div class="cv-tab-group-control">\n <label for="tab-group-${t.id}">${t.label||t.id}</label>\n <select id="tab-group-${t.id}" class="cv-tab-group-select" data-group-id="${t.id}">\n ${e}\n </select>\n </div>\n `}).join("")}\n </div>\n `}this.modal.innerHTML=`\n <div class="cv-widget-modal cv-custom-state-modal">\n <div class="cv-widget-modal-header">\n <h3>${this.options.title}</h3>\n <button class="cv-widget-modal-close" aria-label="Close modal">×</button>\n </div>\n <div class="cv-widget-modal-content">\n <div class="cv-custom-state-form">\n <p>${this.options.description}</p>\n \n <h4>Content Sections</h4>\n <div class="cv-custom-toggles">\n ${e}\n </div>\n \n ${o}\n \n <div class="cv-custom-state-actions">\n ${this.options.showReset?'<button class="cv-custom-state-reset">Reset to Default</button>':""}\n <button class="cv-custom-state-copy-url">Copy Shareable URL</button>\n </div>\n </div>\n </div>\n </div>\n `,document.body.appendChild(this.modal),this.attachStateModalEventListeners(),this.loadCurrentStateIntoForm()}attachStateModalEventListeners(){if(!this.modal)return;const t=this.modal.querySelector(".cv-widget-modal-close");t&&t.addEventListener("click",()=>{this.closeModal()});const e=this.modal.querySelector(".cv-custom-state-copy-url");e&&e.addEventListener("click",()=>{this.copyShareableURL()});const n=this.modal.querySelector(".cv-custom-state-reset");n&&n.addEventListener("click",()=>{this.core.resetToDefault(),this.loadCurrentStateIntoForm()});this.modal.querySelectorAll(".cv-toggle-switch").forEach(t=>{t.addEventListener("click",()=>{t.classList.toggle("cv-toggle-active");const e=this.getCurrentCustomStateFromModal();this.core.applyState(e)})});this.modal.querySelectorAll(".cv-tab-group-select").forEach(t=>{t.addEventListener("change",()=>{const e=t.dataset.groupId,n=t.value;e&&n&&this.core.setActiveTab(e,n)})}),this.modal.addEventListener("click",t=>{t.target===this.modal&&this.closeModal()});const o=t=>{"Escape"===t.key&&(this.closeModal(),document.removeEventListener("keydown",o))};document.addEventListener("keydown",o)}applyThemeToModal(){this.modal&&("dark"===this.options.theme?this.modal.classList.add("cv-widget-theme-dark"):this.modal.classList.remove("cv-widget-theme-dark"))}getCurrentCustomStateFromModal(){if(!this.modal)return{};const t=[];this.modal.querySelectorAll(".cv-toggle-switch").forEach(e=>{const n=e.dataset.toggle;n&&e.classList.contains("cv-toggle-active")&&t.push(n)});const e=this.modal.querySelectorAll(".cv-tab-group-select"),n={};e.forEach(t=>{const e=t.dataset.groupId;e&&(n[e]=t.value)});const o={toggles:t};return Object.keys(n).length>0&&(o.tabs=n),o}copyShareableURL(){const t=this.getCurrentCustomStateFromModal(),e=n.generateShareableURL(t);navigator.clipboard.writeText(e).then(()=>{console.log("Shareable URL copied to clipboard!")}).catch(()=>{console.error("Failed to copy URL!")})}loadCurrentStateIntoForm(){if(!this.modal)return;const t=this.core.getCurrentActiveToggles();this.modal.querySelectorAll(".cv-toggle-switch").forEach(t=>{t.classList.remove("cv-toggle-active")}),t.forEach(t=>{const e=this.modal?.querySelector(`[data-toggle="${t}"]`);e&&e.classList.add("cv-toggle-active")});const e=this.core.getCurrentActiveTabs();this.modal.querySelectorAll(".cv-tab-group-select").forEach(t=>{const n=t.dataset.groupId;n&&e[n]&&(t.value=e[n])})}formatToggleName(t){return t.charAt(0).toUpperCase()+t.slice(1)}showWelcomeModalIfFirstVisit(){const t="cv-welcome-shown";localStorage.getItem(t)||(setTimeout(()=>{this.createWelcomeModal()},500),localStorage.setItem(t,"true"))}createWelcomeModal(){this.modal||(this.modal=document.createElement("div"),this.modal.className="cv-widget-modal-overlay cv-welcome-modal-overlay",this.applyThemeToModal(),this.modal.innerHTML=`\n <div class="cv-widget-modal cv-welcome-modal">\n <div class="cv-widget-modal-header">\n <h3>${this.options.welcomeTitle}</h3>\n <button class="cv-widget-modal-close" aria-label="Close modal">×</button>\n </div>\n <div class="cv-widget-modal-content">\n <div class="cv-welcome-content">\n <p style="text-align: justify;">${this.options.welcomeMessage}</p>\n \n <div class="cv-welcome-widget-preview">\n <div class="cv-welcome-widget-icon">⚙</div>\n <p class="cv-welcome-widget-label">Look for this widget on the side of the screen</p>\n </div>\n \n <button class="cv-welcome-got-it">Got it!</button>\n </div>\n </div>\n </div>\n `,document.body.appendChild(this.modal),this.attachWelcomeModalEventListeners())}attachWelcomeModalEventListeners(){if(!this.modal)return;const t=this.modal.querySelector(".cv-widget-modal-close");t&&t.addEventListener("click",()=>{this.closeModal()});const e=this.modal.querySelector(".cv-welcome-got-it");e&&e.addEventListener("click",()=>{this.closeModal()}),this.modal.addEventListener("click",t=>{t.target===this.modal&&this.closeModal()});const n=t=>{"Escape"===t.key&&(this.closeModal(),document.removeEventListener("keydown",n))};document.addEventListener("keydown",n)}}"undefined"!=typeof window&&(window.CustomViews=b,window.CustomViewsWidget=f,"undefined"!=typeof window&&(window.__customViewsInitialized?console.info("[CustomViews] Auto-init skipped: already initialized."):document.addEventListener("DOMContentLoaded",async function(){if(!window.__customViewsInitInProgress&&!window.__customViewsInitialized){window.__customViewsInitInProgress=!0;try{let t=document.currentScript;if(!t){const e=document.querySelectorAll('script[src*="@customviews-js"]');if(e.length>0){for(let n=0;n<e.length;n++){const o=e[n],s=o.getAttribute("src")||"";if(s.match(/@customviews-js\/customviews(\.min)?\.js($|\?)/)||s.includes("@customviews-js/customviews")){t=o;break}}t||(t=e[0])}}let e,n="",o="/customviews.config.json";t&&(n=t.getAttribute("data-base-url")||"",o=t.getAttribute("data-config-path")||o);try{const t=u(o,n);console.log(`[CustomViews] Loading config from: ${t}`);const s=await fetch(t);s.ok?(e=await s.json(),console.log("[CustomViews] Config loaded successfully")):(console.warn(`[CustomViews] Config file not found at ${t}. Using defaults.`),e={config:{allToggles:[],defaultState:{}},widget:{enabled:!0}})}catch(t){return void console.error("[CustomViews] Error loading config file:",t)}const s=n||e.baseURL||"",i={config:e.config,assetsJsonPath:e.assetsJsonPath,baseURL:s};void 0!==e.showUrl&&(i.showUrl=e.showUrl);const a=await b.init(i);if(!a)return void console.error("[CustomViews] Failed to initialize core.");let r;window.customViewsInstance={core:a},!1!==e.widget?.enabled?(r=new f({core:a,...e.widget}),r.render(),window.customViewsInstance.widget=r,console.log("[CustomViews] Widget initialized and rendered")):console.log("[CustomViews] Widget disabled in config - skipping initialization");const c=new CustomEvent("customviews:ready",{detail:{core:a,widget:r}});document.dispatchEvent(c),window.__customViewsInitialized=!0,window.__customViewsInitInProgress=!1}catch(t){window.__customViewsInitInProgress=!1,console.error("[CustomViews] Auto-initialization error:",t)}}}))),t.AssetsManager=l,t.CustomViews=b,t.CustomViewsCore=h,t.CustomViewsWidget=f,t.PersistenceManager=e,t.URLStateManager=n});
6
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).CustomViews={})}(this,function(t){"use strict";class e{static STORAGE_KEYS={STATE:"customviews-state"};isStorageAvailable(){return"undefined"!=typeof window&&void 0!==window.localStorage}persistState(t){if(this.isStorageAvailable())try{localStorage.setItem(e.STORAGE_KEYS.STATE,JSON.stringify(t))}catch(t){console.warn("Failed to persist state:",t)}}getPersistedState(){if(!this.isStorageAvailable())return null;try{const t=localStorage.getItem(e.STORAGE_KEYS.STATE);return t?JSON.parse(t):null}catch(t){return console.warn("Failed to parse persisted state:",t),null}}clearAll(){this.isStorageAvailable()&&localStorage.removeItem(e.STORAGE_KEYS.STATE)}hasPersistedData(){return!!this.isStorageAvailable()&&!!this.getPersistedState()}}class n{static parseURL(){const t=new URLSearchParams(window.location.search).get("view");let e=null;if(t)try{e=this.decodeState(t)}catch(t){console.warn("Failed to decode view state from URL:",t)}return e}static updateURL(t){if("undefined"==typeof window||!window.history)return;const e=new URL(window.location.href);if(e.searchParams.delete("view"),t){const n=this.encodeState(t);n&&e.searchParams.set("view",n)}const n=e.pathname+(e.search||"")+(e.hash||"");window.history.replaceState({},"",n)}static clearURL(){this.updateURL(null)}static generateShareableURL(t){const e=new URL(window.location.href);if(e.searchParams.delete("view"),t){const n=this.encodeState(t);n&&e.searchParams.set("view",n)}return e.toString()}static encodeState(t){try{const e={};t.toggles&&t.toggles.length>0&&(e.t=t.toggles),t.tabs&&Object.keys(t.tabs).length>0&&(e.g=Object.entries(t.tabs));const n=JSON.stringify(e);let o;o="function"==typeof btoa?btoa(n):Buffer.from(n,"utf-8").toString("base64");return o.replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}catch(t){return console.warn("Failed to encode state:",t),null}}static decodeState(t){try{let e,n=t.replace(/-/g,"+").replace(/_/g,"/");for(;n.length%4;)n+="=";e="function"==typeof atob?atob(n):Buffer.from(n,"base64").toString("utf-8");const o=JSON.parse(e);if(!o||"object"!=typeof o)throw new Error("Invalid compact state structure");const s={toggles:Array.isArray(o.t)?o.t:[]};if(Array.isArray(o.g)){s.tabs={};for(const[t,e]of o.g)"string"==typeof t&&"string"==typeof e&&(s.tabs[t]=e)}return s}catch(t){return console.warn("Failed to decode view state:",t),null}}}class o{hiddenToggles=new Set;setToggleVisibility(t,e){const n=this.hiddenToggles.has(t),o=!e;return o&&!n?(this.hiddenToggles.add(t),!0):!(o||!n)&&(this.hiddenToggles.delete(t),!0)}hideAll(t){for(const e of t)this.setToggleVisibility(e,!1)}showAll(t){for(const e of t)this.setToggleVisibility(e,!0)}getHiddenToggles(){return Array.from(this.hiddenToggles)}filterVisibleToggles(t){return t.filter(t=>!this.hiddenToggles.has(t))}applyElementVisibility(t,e){e?(t.classList.remove("cv-hidden"),t.classList.add("cv-visible")):(t.classList.add("cv-hidden"),t.classList.remove("cv-visible"))}}function s(){if(Array.from(document.styleSheets).some(t=>t.href&&(t.href.includes("font-awesome")||t.href.includes("fontawesome"))))return;const t=document.createElement("link");t.rel="stylesheet",t.href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css",t.setAttribute("data-customviews-fontawesome","true"),document.head.appendChild(t)}function i(t){return t.replace(/:(fa[b|s|r]?)-([\w-]+):/g,(t,e,n)=>`<i class="${e} fa-${n}"></i>`)}function a(t,e,n){const o=n.get(e);if(!o)return;const s=o.type||function(t){return t.src?"image":t.content&&/<[^>]+>/.test(t.content)?"html":"text"}(o);switch(s){case"image":!function(t,e){if(!e.src)return;t.innerHTML="";const n=document.createElement("img");n.src=e.src,n.alt=e.alt||"",e.className&&(n.className=e.className),e.style&&n.setAttribute("style",e.style),n.style.maxWidth=n.style.maxWidth||"100%",n.style.height=n.style.height||"auto",n.style.display=n.style.display||"block",t.appendChild(n)}(t,o);break;case"text":!function(t,e){null!=e.content&&(t.textContent=e.content),e.className&&(t.className=e.className),e.style&&t.setAttribute("style",e.style)}(t,o);break;case"html":!function(t,e){null!=e.content&&(t.innerHTML=e.content),e.className&&(t.className=e.className),e.style&&t.setAttribute("style",e.style)}(t,o);break;default:t.innerHTML=o.content||String(o),console.warn("[CustomViews] Unknown asset type:",s)}}const r="cv-tabgroup",c="cv-tab",l="cv-tabs-nav";class d{static applySelections(t,e,n){t.querySelectorAll(r).forEach(t=>{const o=t.getAttribute("id");if(!o)return;const s=this.resolveActiveTab(o,e,n,t);Array.from(t.children).filter(t=>t.tagName.toLowerCase()===c).forEach(t=>{const e=t.getAttribute("id");if(!e)return;const n=e===s;this.applyTabVisibility(t,n)})})}static resolveActiveTab(t,e,n,o){if(e[t])return e[t];if(n){const e=n.find(e=>e.id===t);if(e){if(e.default)return e.default;const t=e.tabs[0];if(t)return t.id}}const s=Array.from(o.children).find(t=>t.tagName.toLowerCase()===c);return s?s.getAttribute("id"):null}static applyTabVisibility(t,e){e?(t.classList.remove("cv-hidden"),t.classList.add("cv-visible")):(t.classList.add("cv-hidden"),t.classList.remove("cv-visible"))}static buildNavs(t,e,n){const o=t.querySelectorAll('cv-tabgroup[nav="auto"], cv-tabgroup:not([nav])');let a=!1;o.forEach(t=>{const n=t.getAttribute("id");if(!n)return;Array.from(t.children).filter(t=>"cv-tab"===t.tagName.toLowerCase()).forEach(t=>{const o=t.getAttribute("id");if(!o)return;const s=t.getAttribute("header")||this.getTabLabel(o,n,e)||o;/:fa-[\w-]+:/.test(s)&&(a=!0)})}),a&&s(),o.forEach(t=>{const o=t.getAttribute("id");if(!o)return;let s=t.querySelector(`.${l}`);if(s)return;const a=Array.from(t.children).filter(t=>t.tagName.toLowerCase()===c);if(0===a.length)return;s=document.createElement("ul"),s.className=`${l} nav-tabs`,s.setAttribute("role","tablist"),t.insertBefore(s,t.firstChild),a.forEach(t=>{const a=t.getAttribute("id");if(!a)return;const r=t.getAttribute("header")||this.getTabLabel(a,o,e)||a,c=document.createElement("li");c.className="nav-item";const l=document.createElement("a");l.className="nav-link",l.innerHTML=i(r),l.href="#",l.setAttribute("data-tab-id",a),l.setAttribute("data-group-id",o),l.setAttribute("role","tab");t.classList.contains("cv-visible")?(l.classList.add("active"),l.setAttribute("aria-selected","true")):l.setAttribute("aria-selected","false"),n&&l.addEventListener("click",t=>{t.preventDefault(),n(o,a)}),c.appendChild(l),s.appendChild(c)});const r=document.createElement("div");r.className="cv-tabgroup-bottom-border",t.appendChild(r)})}static getTabLabel(t,e,n){if(!n)return null;const o=n.find(t=>t.id===e);if(!o)return null;const s=o.tabs.find(e=>e.id===t);return s?.label||null}static updateNavActiveState(t,e,n){t.querySelectorAll(`${r}[id="${e}"]`).forEach(t=>{t.querySelectorAll(".nav-link").forEach(t=>{t.getAttribute("data-tab-id")===n?(t.classList.add("active"),t.setAttribute("aria-selected","true")):(t.classList.remove("active"),t.setAttribute("aria-selected","false"))})})}static updateAllNavActiveStates(t,e,n){t.querySelectorAll(r).forEach(t=>{const o=t.getAttribute("id");if(!o)return;const s=this.resolveActiveTab(o,e,n,t);if(!s)return;t.querySelectorAll(".nav-link").forEach(t=>{t.getAttribute("data-tab-id")===s?(t.classList.add("active"),t.setAttribute("aria-selected","true")):(t.classList.remove("active"),t.setAttribute("aria-selected","false"))})})}}class g{assets;baseURL;constructor(t,e=""){this.assets=t,this.baseURL=e,this.validate()||console.warn("Invalid assets:",this.assets)}validate(){return Object.values(this.assets).every(t=>t.src||t.content)}get(t){const e=this.assets[t];if(e)return this.baseURL&&e.src?{...e,src:this.prependBaseURL(e.src)}:e}prependBaseURL(t){if(t.startsWith("http://")||t.startsWith("https://"))return t;return(this.baseURL.endsWith("/")?this.baseURL.slice(0,-1):this.baseURL)+(t.startsWith("/")?t:"/"+t)}loadFromJSON(t){this.assets=t}loadAdditionalAssets(t){this.assets={...this.assets,...t}}}const h="[data-cv-toggle], [data-customviews-toggle], cv-toggle";class u{static applyToggles(t,e){t.querySelectorAll(h).forEach(t=>{const n=this.getToggleCategories(t).some(t=>e.includes(t));this.applyToggleVisibility(t,n)})}static renderAssets(t,e,n){t.querySelectorAll(h).forEach(t=>{const o=this.getToggleCategories(t),s=this.getToggleId(t);s&&o.some(t=>e.includes(t))&&a(t,s,n)})}static getToggleCategories(t){if("cv-toggle"===t.tagName.toLowerCase()){return(t.getAttribute("category")||"").split(/\s+/).filter(Boolean)}return(t.dataset.cvToggle||t.dataset.customviewsToggle||"").split(/\s+/).filter(Boolean)}static getToggleId(t){return t.dataset.cvId||t.dataset.customviewsId||t.getAttribute("data-cv-id")||t.getAttribute("data-customviews-id")||void 0}static applyToggleVisibility(t,e){e?(t.classList.remove("cv-hidden"),t.classList.add("cv-visible")):(t.classList.add("cv-hidden"),t.classList.remove("cv-visible"))}}class p{rootEl;assetsManager;persistenceManager;visibilityManager;config;stateChangeListeners=[];showUrlEnabled;lastAppliedState=null;constructor(t){this.assetsManager=t.assetsManager,this.config=t.config,this.rootEl=t.rootEl||document.body,this.persistenceManager=new e,this.visibilityManager=new o,this.showUrlEnabled=t.showUrl??!1,this.lastAppliedState=this.cloneState(this.config?.defaultState)}getConfig(){return this.config}getTabGroups(){return this.config.tabGroups}getCurrentActiveTabs(){if(this.lastAppliedState?.tabs)return{...this.lastAppliedState.tabs};const t=this.persistenceManager.getPersistedState();return t?.tabs?{...t.tabs}:this.config?.defaultState?.tabs?{...this.config.defaultState.tabs}:{}}setActiveTab(t,e){const n={toggles:this.getCurrentActiveToggles(),tabs:{...this.getCurrentActiveTabs(),[t]:e}};this.applyState(n);const o=new CustomEvent("customviews:tab-change",{detail:{groupId:t,tabId:e},bubbles:!0});document.dispatchEvent(o)}async init(){!function(){if("undefined"==typeof document)return;if(document.querySelector("#cv-core-styles"))return;const t=document.createElement("style");t.id="cv-core-styles",t.textContent="\n\n/* Core toggle visibility transitions */\n[data-cv-toggle], [data-customviews-toggle], cv-toggle {\n transition: opacity 150ms ease,\n transform 150ms ease,\n max-height 200ms ease,\n margin 150ms ease;\n will-change: opacity, transform, max-height, margin;\n}\n\n.cv-visible {\n opacity: 1 !important;\n transform: translateY(0) !important;\n max-height: var(--cv-max-height, 9999px) !important;\n}\n\n.cv-hidden {\n opacity: 0 !important;\n transform: translateY(-4px) !important;\n pointer-events: none !important;\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n border-top-width: 0 !important;\n border-bottom-width: 0 !important;\n max-height: 0 !important;\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n overflow: hidden !important;\n}\n\n\n\n/* Tab navigation styles - Bootstrap-style tabs matching MarkBind */\n.cv-tabs-nav {\n display: flex;\n flex-wrap: wrap;\n padding-left: 0;\n margin-top: 0.5rem;\n margin-bottom: 1rem;\n list-style: none;\n border-bottom: 1px solid #dee2e6;\n}\n\n.cv-tabs-nav .nav-item {\n margin-bottom: -1px;\n list-style: none;\n display: inline-block;\n}\n\n.cv-tabs-nav .nav-link {\n display: block;\n padding: 0.5rem 1rem;\n color: #495057;\n text-decoration: none;\n background-color: transparent;\n border: 1px solid transparent;\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;\n cursor: pointer;\n}\n\n.cv-tabs-nav .nav-link:hover,\n.cv-tabs-nav .nav-link:focus {\n border-color: #e9ecef #e9ecef #dee2e6;\n isolation: isolate;\n}\n\n.cv-tabs-nav .nav-link.active {\n color: #495057;\n background-color: #fff;\n border-color: #dee2e6 #dee2e6 #fff;\n}\n\n.cv-tabs-nav .nav-link:focus {\n outline: 0;\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n\n/* Legacy button-based nav (deprecated, kept for compatibility) */\n.cv-tabs-nav-item {\n background: none;\n border: none;\n border-bottom: 2px solid transparent;\n padding: 0.5rem 1rem;\n cursor: pointer;\n font-size: 1rem;\n color: #6c757d;\n transition: color 150ms ease, border-color 150ms ease;\n}\n\n.cv-tabs-nav-item:hover {\n color: #495057;\n border-bottom-color: #dee2e6;\n}\n\n.cv-tabs-nav-item.active {\n color: #007bff;\n border-bottom-color: #007bff;\n font-weight: 500;\n}\n\n.cv-tabs-nav-item:focus {\n outline: 2px solid #007bff;\n outline-offset: 2px;\n}\n\n/* Tab panel base styles */\ncv-tab {\n display: block;\n}\n\n/* Override visibility for tab panels - use display instead of collapse animation */\ncv-tab.cv-hidden {\n display: none !important;\n}\n\ncv-tab.cv-visible {\n display: block !important;\n}\n\ncv-tabgroup {\n display: block;\n margin-bottom: 1.5rem;\n}\n\n/* Bottom border line for tab groups */\n.cv-tabgroup-bottom-border {\n border-bottom: 1px solid #dee2e6;\n margin-top: 1rem;\n}\n\n/* Tab content wrapper */\n.cv-tab-content {\n padding: 1rem 0;\n}\n\n",document.head.appendChild(t)}(),d.buildNavs(this.rootEl,this.config.tabGroups,(t,e)=>{this.setActiveTab(t,e)}),window.addEventListener("popstate",()=>{this.loadAndCallApplyState()}),this.loadAndCallApplyState()}async loadAndCallApplyState(){const t=n.parseURL();if(t)return void this.applyState(t);const e=this.persistenceManager.getPersistedState();e?this.applyState(e):this.renderState(this.config.defaultState)}applyState(t){const e=this.cloneState(t);this.renderState(e),this.persistenceManager.persistState(e),this.showUrlEnabled?n.updateURL(e):n.clearURL()}renderState(t){this.lastAppliedState=this.cloneState(t);const e=t.toggles||[],n=this.visibilityManager.filterVisibleToggles(e);u.applyToggles(this.rootEl,n),u.renderAssets(this.rootEl,n,this.assetsManager),d.applySelections(this.rootEl,t.tabs||{},this.config.tabGroups),d.updateAllNavActiveStates(this.rootEl,t.tabs||{},this.config.tabGroups),this.notifyStateChangeListeners()}resetToDefault(){this.persistenceManager.clearAll(),this.config?this.renderState(this.config.defaultState):console.warn("No configuration loaded, cannot reset to default state"),n.clearURL()}getCurrentActiveToggles(){return this.lastAppliedState?this.lastAppliedState.toggles||[]:this.config&&this.config.defaultState.toggles||[]}clearPersistence(){this.persistenceManager.clearAll(),this.config?this.renderState(this.config.defaultState):console.warn("No configuration loaded, cannot reset to default state"),n.clearURL()}setOption(t,e){switch(t){case"showUrl":{const t=Boolean(e);if(this.showUrlEnabled===t)return;if(this.showUrlEnabled=t,t){const t=this.getTrackedStateSnapshot();n.updateURL(t)}else n.clearURL();break}default:console.warn(`[CustomViews] Unknown option '${t}' passed to setOption`)}}addStateChangeListener(t){this.stateChangeListeners.push(t)}removeStateChangeListener(t){const e=this.stateChangeListeners.indexOf(t);e>-1&&this.stateChangeListeners.splice(e,1)}notifyStateChangeListeners(){this.stateChangeListeners.forEach(t=>{try{t()}catch(t){console.warn("Error in state change listener:",t)}})}cloneState(t){return t?JSON.parse(JSON.stringify(t)):{}}getTrackedStateSnapshot(){return this.lastAppliedState?this.cloneState(this.lastAppliedState):this.config?this.cloneState(this.config.defaultState):{}}}function m(t,e){if(!e)return t;if(t.startsWith("http://")||t.startsWith("https://"))return t;return(e.endsWith("/")?e.slice(0,-1):e)+(t.startsWith("/")?t:"/"+t)}class v extends HTMLElement{connectedCallback(){}}class b extends HTMLElement{connectedCallback(){setTimeout(()=>{const t=new CustomEvent("cv:tabgroup-ready",{bubbles:!0,detail:{groupId:this.getAttribute("id")}});this.dispatchEvent(t)},0)}}class f extends HTMLElement{connectedCallback(){}}class w{static async init(t){let e;customElements.get("cv-tab")||customElements.define("cv-tab",v),customElements.get("cv-tabgroup")||customElements.define("cv-tabgroup",b),customElements.get("cv-toggle")||customElements.define("cv-toggle",f);const n=t.baseURL||"";if(t.assetsJsonPath){const o=m(t.assetsJsonPath,n);try{const t=await(await fetch(o)).json();e=new g(t,n)}catch(t){console.error(`[CustomViews] Failed to load assets JSON from ${o}:`,t),e=new g({},n)}}else e=new g({},n);let o;t.config?o=t.config:(console.error("No config provided, using minimal default config"),o={allToggles:[],defaultState:{}});const s={assetsManager:e,config:o,rootEl:t.rootEl};void 0!==t.showUrl&&(s.showUrl=t.showUrl);const i=new p(s);return i.init(),i}}class y{core;container;widgetIcon=null;options;modal=null;constructor(t){this.core=t.core,this.container=t.container||document.body,this.options={core:t.core,container:this.container,position:t.position||"middle-left",theme:t.theme||"light",showReset:t.showReset??!0,title:t.title||"Customize View",description:t.description||"Toggle different content sections to customize your view. Changes are applied instantly and the URL will be updated for sharing.",showWelcome:t.showWelcome??!1,welcomeTitle:t.welcomeTitle||"Site Customization",welcomeMessage:t.welcomeMessage||'This site is powered by Custom Views. Use the widget on the side (⚙) to customize your experience. Your preferences will be saved and can be shared via URL.<br><br>Learn more at <a href="https://github.com/customviews-js/customviews" target="_blank">customviews GitHub</a>.',showTabGroups:t.showTabGroups??!0}}render(){return this.widgetIcon=this.createWidgetIcon(),this.attachEventListeners(),document.body.appendChild(this.widgetIcon),this.options.showWelcome&&this.showWelcomeModalIfFirstVisit(),this.widgetIcon}createWidgetIcon(){const t=document.createElement("div");return t.className=`cv-widget-icon cv-widget-${this.options.position}`,t.innerHTML="⚙",t.title=this.options.title,t.setAttribute("aria-label","Open Custom Views"),function(){if(document.querySelector("#cv-widget-styles"))return;const t=document.createElement("style");t.id="cv-widget-styles",t.textContent="\n/* Rounded rectangle widget icon styles */\n.cv-widget-icon {\n position: fixed;\n /* Slightly transparent by default so the widget is subtle at the page edge */\n background: rgba(255, 255, 255, 0.92);\n color: rgba(0, 0, 0, 0.9);\n opacity: 0.6;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n font-weight: bold;\n cursor: pointer;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n z-index: 9998;\n transition: all 0.3s ease;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.cv-widget-icon:hover {\n /* Become fully opaque on hover to improve readability */\n background: rgba(255, 255, 255, 1);\n color: rgba(0, 0, 0, 1);\n opacity: 1;\n}\n\n/* Top-right: rounded end on left, sticks out leftward on hover */\n.cv-widget-top-right {\n top: 20px;\n right: 0;\n border-radius: 18px 0 0 18px;\n padding-left: 8px;\n justify-content: flex-start;\n}\n\n/* Top-left: rounded end on right, sticks out rightward on hover */\n.cv-widget-top-left {\n top: 20px;\n left: 0;\n border-radius: 0 18px 18px 0;\n padding-right: 8px;\n justify-content: flex-end;\n}\n\n/* Bottom-right: rounded end on left, sticks out leftward on hover */\n.cv-widget-bottom-right {\n bottom: 20px;\n right: 0;\n border-radius: 18px 0 0 18px;\n padding-left: 8px;\n justify-content: flex-start;\n}\n\n/* Bottom-left: rounded end on right, sticks out rightward on hover */\n.cv-widget-bottom-left {\n bottom: 20px;\n left: 0;\n border-radius: 0 18px 18px 0;\n padding-right: 8px;\n justify-content: flex-end;\n}\n\n/* Middle-left: rounded end on right, sticks out rightward on hover */\n.cv-widget-middle-left {\n top: 50%;\n left: 0;\n transform: translateY(-50%);\n border-radius: 0 18px 18px 0;\n padding-right: 8px;\n justify-content: flex-end;\n}\n\n/* Middle-right: rounded end on left, sticks out leftward on hover */\n.cv-widget-middle-right {\n top: 50%;\n right: 0;\n transform: translateY(-50%);\n border-radius: 18px 0 0 18px;\n padding-left: 8px;\n justify-content: flex-start;\n}\n\n.cv-widget-top-right,\n.cv-widget-middle-right,\n.cv-widget-bottom-right,\n.cv-widget-top-left,\n.cv-widget-middle-left,\n.cv-widget-bottom-left {\n height: 36px;\n width: 36px;\n}\n\n.cv-widget-middle-right:hover,\n.cv-widget-top-right:hover,\n.cv-widget-bottom-right:hover,\n.cv-widget-top-left:hover,\n.cv-widget-middle-left:hover,\n.cv-widget-bottom-left:hover {\n width: 55px;\n}\n\n/* Modal content styles */\n.cv-widget-section {\n margin-bottom: 16px;\n}\n\n.cv-widget-section:last-child {\n margin-bottom: 0;\n}\n\n.cv-widget-section label {\n display: block;\n margin-bottom: 4px;\n font-weight: 500;\n color: #555;\n}\n\n.cv-widget-profile-select,\n.cv-widget-state-select {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 4px;\n background: white;\n font-size: 14px;\n}\n\n.cv-widget-profile-select:focus,\n.cv-widget-state-select:focus {\n outline: none;\n border-color: #007bff;\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.cv-widget-profile-select:disabled,\n.cv-widget-state-select:disabled {\n background: #f8f9fa;\n color: #6c757d;\n cursor: not-allowed;\n}\n\n.cv-widget-current {\n margin: 16px 0;\n padding: 12px;\n background: #f8f9fa;\n border-radius: 4px;\n border-left: 4px solid #007bff;\n}\n\n.cv-widget-current label {\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: #666;\n margin-bottom: 4px;\n}\n\n.cv-widget-current-view {\n font-weight: 500;\n color: #333;\n}\n\n.cv-widget-reset {\n width: 100%;\n padding: 8px 16px;\n background: #dc3545;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n}\n\n.cv-widget-reset:hover {\n background: #c82333;\n}\n\n.cv-widget-reset:active {\n background: #bd2130;\n}\n\n/* Responsive design for mobile */\n@media (max-width: 768px) {\n .cv-widget-top-right,\n .cv-widget-top-left {\n top: 10px;\n }\n\n .cv-widget-bottom-right,\n .cv-widget-bottom-left {\n bottom: 10px;\n }\n\n /* All widgets stay flush with screen edges */\n .cv-widget-top-right,\n .cv-widget-bottom-right,\n .cv-widget-middle-right {\n right: 0;\n }\n\n .cv-widget-top-left,\n .cv-widget-bottom-left,\n .cv-widget-middle-left {\n left: 0;\n }\n\n /* Slightly smaller on mobile */\n .cv-widget-icon {\n width: 60px;\n height: 32px;\n }\n\n .cv-widget-icon:hover {\n width: 75px;\n }\n}\n\n/* Modal styles */\n.cv-widget-modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10002;\n animation: fadeIn 0.2s ease;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.cv-widget-modal {\n background: white;\n border-radius: 8px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n max-width: 400px;\n width: 90vw;\n max-height: 80vh;\n overflow-y: auto;\n animation: slideIn 0.2s ease;\n}\n\n@keyframes slideIn {\n from { \n opacity: 0;\n transform: scale(0.9) translateY(-20px);\n }\n to { \n opacity: 1;\n transform: scale(1) translateY(0);\n }\n}\n\n.cv-widget-modal-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid #e9ecef;\n background: #f8f9fa;\n border-radius: 8px 8px 0 0;\n}\n\n.cv-widget-modal-header h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: #333;\n}\n\n.cv-widget-modal-close {\n background: none;\n border: none;\n font-size: 20px;\n cursor: pointer;\n padding: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n color: #666;\n line-height: 1;\n transition: all 0.2s ease;\n}\n\n.cv-widget-modal-close:hover {\n background: #e9ecef;\n color: #333;\n}\n\n.cv-widget-modal-content {\n padding: 20px;\n}\n\n.cv-widget-modal-actions {\n margin-top: 20px;\n padding-top: 16px;\n border-top: 1px solid #e9ecef;\n}\n\n.cv-widget-restore {\n width: 100%;\n padding: 10px 16px;\n background: #28a745;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n}\n\n.cv-widget-restore:hover {\n background: #218838;\n}\n\n.cv-widget-create-state {\n width: 100%;\n padding: 10px 16px;\n background: #007bff;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n margin-bottom: 10px;\n}\n\n.cv-widget-create-state:hover {\n background: #0056b3;\n}\n\n/* Dark theme modal styles */\n.cv-widget-theme-dark .cv-widget-modal {\n background: #2d3748;\n color: #e2e8f0;\n}\n\n.cv-widget-theme-dark .cv-widget-modal-header {\n background: #1a202c;\n border-color: #4a5568;\n}\n\n.cv-widget-theme-dark .cv-widget-modal-header h3 {\n color: #e2e8f0;\n}\n\n.cv-widget-theme-dark .cv-widget-modal-close {\n color: #a0aec0;\n}\n\n.cv-widget-theme-dark .cv-widget-modal-close:hover {\n background: #4a5568;\n color: #e2e8f0;\n}\n\n.cv-widget-theme-dark .cv-widget-modal-actions {\n border-color: #4a5568;\n}\n\n/* Custom state creator styles */\n.cv-custom-state-modal {\n max-width: 500px;\n}\n\n.cv-custom-state-form h4 {\n margin: 20px 0 10px 0;\n font-size: 16px;\n font-weight: 600;\n color: #333;\n border-bottom: 1px solid #e9ecef;\n padding-bottom: 5px;\n}\n\n.cv-custom-state-form p {\n font-size: 15px;\n line-height: 1.6;\n color: #555;\n margin-bottom: 24px;\n text-align: justify;\n}\n\n.cv-custom-state-section {\n margin-bottom: 16px;\n}\n\n.cv-custom-state-section label {\n display: block;\n margin-bottom: 4px;\n font-weight: 500;\n color: #555;\n}\n\n.cv-custom-state-input {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 4px;\n background: white;\n font-size: 14px;\n}\n\n.cv-custom-state-input:focus {\n outline: none;\n border-color: #007bff;\n box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);\n}\n\n.cv-custom-toggles {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));\n gap: 10px;\n}\n\n.cv-custom-state-toggle {\n display: flex;\n align-items: center;\n}\n\n.cv-custom-state-toggle label {\n display: flex;\n align-items: center;\n cursor: pointer;\n font-weight: normal;\n margin: 0;\n}\n\n.cv-toggle-switch {\n position: relative;\n width: 44px;\n height: 24px;\n background: #ccc;\n border-radius: 12px;\n margin-right: 12px;\n cursor: pointer;\n transition: background-color 0.3s ease;\n flex-shrink: 0;\n}\n\n.cv-toggle-switch:hover {\n background: #bbb;\n}\n\n.cv-toggle-switch.cv-toggle-active {\n background: #007bff;\n}\n\n.cv-toggle-switch.cv-toggle-active:hover {\n background: #0056b3;\n}\n\n.cv-toggle-handle {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 20px;\n height: 20px;\n background: white;\n border-radius: 50%;\n transition: transform 0.3s ease;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n}\n\n.cv-toggle-switch.cv-toggle-active .cv-toggle-handle {\n transform: translateX(20px);\n}\n\n/* Dark theme toggle switch styles */\n.cv-widget-theme-dark .cv-toggle-switch {\n background: #4a5568;\n}\n\n.cv-widget-theme-dark .cv-toggle-switch:hover {\n background: #5a6578;\n}\n\n.cv-widget-theme-dark .cv-toggle-switch.cv-toggle-active {\n background: #63b3ed;\n}\n\n.cv-widget-theme-dark .cv-toggle-switch.cv-toggle-active:hover {\n background: #4299e1;\n}\n\n.cv-tab-groups {\n margin-top: 20px;\n}\n\n.cv-tab-group-control {\n margin-bottom: 15px;\n}\n\n.cv-tab-group-control label {\n display: block;\n margin-bottom: 5px;\n font-weight: 500;\n font-size: 14px;\n}\n\n.cv-tab-group-select {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid #ced4da;\n border-radius: 4px;\n font-size: 14px;\n background-color: white;\n cursor: pointer;\n}\n\n.cv-tab-group-select:focus {\n outline: none;\n border-color: #007bff;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.cv-widget-theme-dark .cv-tab-group-select {\n background-color: #2d3748;\n border-color: #4a5568;\n color: #e2e8f0;\n}\n\n.cv-custom-state-actions {\n display: flex;\n gap: 10px;\n margin-top: 20px;\n padding-top: 16px;\n border-top: 1px solid #e9ecef;\n}\n\n.cv-custom-state-cancel,\n.cv-custom-state-copy-url {\n flex: 1;\n padding: 10px 16px;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n}\n\n.cv-custom-state-reset {\n flex: 1;\n padding: 10px 16px;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 14px;\n font-weight: 500;\n background: #dc3545;\n color: white;\n}\n\n.cv-custom-state-reset:hover {\n background: #c82333;\n}\n\n.cv-custom-state-cancel {\n background: #6c757d;\n color: white;\n}\n\n.cv-custom-state-cancel:hover {\n background: #5a6268;\n}\n\n.cv-custom-state-copy-url {\n background: #28a745;\n color: white;\n}\n\n.cv-custom-state-copy-url:hover {\n background: #218838;\n}\n\n/* Dark theme custom state styles */\n.cv-widget-theme-dark .cv-custom-state-form h4 {\n color: #e2e8f0;\n border-color: #4a5568;\n}\n\n.cv-widget-theme-dark .cv-custom-state-form p {\n color: #cbd5e0;\n}\n\n.cv-widget-theme-dark .cv-custom-state-section label {\n color: #a0aec0;\n}\n\n.cv-widget-theme-dark .cv-custom-state-input {\n background: #1a202c;\n border-color: #4a5568;\n color: #e2e8f0;\n}\n\n.cv-widget-theme-dark .cv-custom-state-actions {\n border-color: #4a5568;\n}\n\n/* Welcome modal styles */\n.cv-welcome-modal {\n max-width: 500px;\n}\n\n.cv-welcome-content {\n text-align: center;\n}\n\n.cv-welcome-content p {\n font-size: 15px;\n line-height: 1.6;\n color: #555;\n margin-bottom: 24px;\n text-align: justify;\n}\n\n.cv-welcome-widget-preview {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n padding: 20px;\n background: #f8f9fa;\n border-radius: 8px;\n margin-bottom: 24px;\n}\n\n.cv-welcome-widget-icon {\n width: 36px;\n height: 36px;\n background: white;\n color: black;\n border-radius: 0 18px 18px 0;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n}\n\n.cv-welcome-widget-label {\n font-size: 14px;\n color: #666;\n margin: 0;\n font-weight: 500;\n}\n\n.cv-welcome-got-it {\n width: 100%;\n padding: 12px 24px;\n background: #007bff;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 16px;\n font-weight: 600;\n transition: background 0.2s ease;\n}\n\n.cv-welcome-got-it:hover {\n background: #0056b3;\n}\n\n.cv-welcome-got-it:active {\n background: #004494;\n}\n\n/* Dark theme welcome modal styles */\n.cv-widget-theme-dark .cv-welcome-content p {\n color: #cbd5e0;\n}\n\n.cv-widget-theme-dark .cv-welcome-widget-preview {\n background: #1a202c;\n}\n\n.cv-widget-theme-dark .cv-welcome-widget-label {\n color: #a0aec0;\n}\n",document.head.appendChild(t)}(),t}destroy(){this.widgetIcon&&(this.widgetIcon.remove(),this.widgetIcon=null),this.modal&&(this.modal.remove(),this.modal=null)}attachEventListeners(){this.widgetIcon&&this.widgetIcon.addEventListener("click",()=>this.openStateModal())}closeModal(){this.modal&&(this.modal.remove(),this.modal=null)}openStateModal(){const t=this.core.getConfig(),e=t?.allToggles||[];this.createCustomStateModal(e)}createCustomStateModal(t){this.closeModal(),this.modal=document.createElement("div"),this.modal.className="cv-widget-modal-overlay",this.applyThemeToModal();const e=t.length?t.map(t=>`\n <div class="cv-custom-state-toggle">\n <label>\n <div class="cv-toggle-switch" data-toggle="${t}">\n <div class="cv-toggle-handle"></div>\n </div>\n ${this.formatToggleName(t)}\n </label>\n </div>\n `).join(""):'<p class="cv-no-toggles">No configurable sections available.</p>',n=this.core.getTabGroups();let o="",a=!1;if(this.options.showTabGroups&&n&&n.length>0)for(const t of n){if(t.label&&/:fa-[\w-]+:/.test(t.label)){a=!0;break}for(const e of t.tabs)if(e.label&&/:fa-[\w-]+:/.test(e.label)){a=!0;break}if(a)break}if(a&&s(),this.options.showTabGroups&&n&&n.length>0){o=`\n <h4>Tab Groups</h4>\n <div class="cv-tab-groups">\n ${n.map(t=>{const e=t.tabs.map(t=>`<option value="${t.id}">${i(t.label||t.id)}</option>`).join("");return`\n <div class="cv-tab-group-control">\n <label for="tab-group-${t.id}">${i(t.label||t.id)}</label>\n <select id="tab-group-${t.id}" class="cv-tab-group-select" data-group-id="${t.id}">\n ${e}\n </select>\n </div>\n `}).join("")}\n </div>\n `}this.modal.innerHTML=`\n <div class="cv-widget-modal cv-custom-state-modal">\n <div class="cv-widget-modal-header">\n <h3>${this.options.title}</h3>\n <button class="cv-widget-modal-close" aria-label="Close modal">×</button>\n </div>\n <div class="cv-widget-modal-content">\n <div class="cv-custom-state-form">\n <p>${this.options.description}</p>\n \n <h4>Content Sections</h4>\n <div class="cv-custom-toggles">\n ${e}\n </div>\n \n ${o}\n \n <div class="cv-custom-state-actions">\n ${this.options.showReset?'<button class="cv-custom-state-reset">Reset to Default</button>':""}\n <button class="cv-custom-state-copy-url">Copy Shareable URL</button>\n </div>\n </div>\n </div>\n </div>\n `,document.body.appendChild(this.modal),this.attachStateModalEventListeners(),this.loadCurrentStateIntoForm()}attachStateModalEventListeners(){if(!this.modal)return;const t=this.modal.querySelector(".cv-widget-modal-close");t&&t.addEventListener("click",()=>{this.closeModal()});const e=this.modal.querySelector(".cv-custom-state-copy-url");e&&e.addEventListener("click",()=>{this.copyShareableURL()});const n=this.modal.querySelector(".cv-custom-state-reset");n&&n.addEventListener("click",()=>{this.core.resetToDefault(),this.loadCurrentStateIntoForm()});this.modal.querySelectorAll(".cv-toggle-switch").forEach(t=>{t.addEventListener("click",()=>{t.classList.toggle("cv-toggle-active");const e=this.getCurrentCustomStateFromModal();this.core.applyState(e)})});this.modal.querySelectorAll(".cv-tab-group-select").forEach(t=>{t.addEventListener("change",()=>{const e=t.dataset.groupId,n=t.value;e&&n&&this.core.setActiveTab(e,n)})}),this.modal.addEventListener("click",t=>{t.target===this.modal&&this.closeModal()});const o=t=>{"Escape"===t.key&&(this.closeModal(),document.removeEventListener("keydown",o))};document.addEventListener("keydown",o)}applyThemeToModal(){this.modal&&("dark"===this.options.theme?this.modal.classList.add("cv-widget-theme-dark"):this.modal.classList.remove("cv-widget-theme-dark"))}getCurrentCustomStateFromModal(){if(!this.modal)return{};const t=[];this.modal.querySelectorAll(".cv-toggle-switch").forEach(e=>{const n=e.dataset.toggle;n&&e.classList.contains("cv-toggle-active")&&t.push(n)});const e=this.modal.querySelectorAll(".cv-tab-group-select"),n={};e.forEach(t=>{const e=t.dataset.groupId;e&&(n[e]=t.value)});const o={toggles:t};return Object.keys(n).length>0&&(o.tabs=n),o}copyShareableURL(){const t=this.getCurrentCustomStateFromModal(),e=n.generateShareableURL(t);navigator.clipboard.writeText(e).then(()=>{console.log("Shareable URL copied to clipboard!")}).catch(()=>{console.error("Failed to copy URL!")})}loadCurrentStateIntoForm(){if(!this.modal)return;const t=this.core.getCurrentActiveToggles();this.modal.querySelectorAll(".cv-toggle-switch").forEach(t=>{t.classList.remove("cv-toggle-active")}),t.forEach(t=>{const e=this.modal?.querySelector(`[data-toggle="${t}"]`);e&&e.classList.add("cv-toggle-active")});const e=this.core.getCurrentActiveTabs();this.modal.querySelectorAll(".cv-tab-group-select").forEach(t=>{const n=t.dataset.groupId;n&&e[n]&&(t.value=e[n])})}formatToggleName(t){return t.charAt(0).toUpperCase()+t.slice(1)}showWelcomeModalIfFirstVisit(){const t="cv-welcome-shown";localStorage.getItem(t)||(setTimeout(()=>{this.createWelcomeModal()},500),localStorage.setItem(t,"true"))}createWelcomeModal(){this.modal||(this.modal=document.createElement("div"),this.modal.className="cv-widget-modal-overlay cv-welcome-modal-overlay",this.applyThemeToModal(),this.modal.innerHTML=`\n <div class="cv-widget-modal cv-welcome-modal">\n <div class="cv-widget-modal-header">\n <h3>${this.options.welcomeTitle}</h3>\n <button class="cv-widget-modal-close" aria-label="Close modal">×</button>\n </div>\n <div class="cv-widget-modal-content">\n <div class="cv-welcome-content">\n <p style="text-align: justify;">${this.options.welcomeMessage}</p>\n \n <div class="cv-welcome-widget-preview">\n <div class="cv-welcome-widget-icon">⚙</div>\n <p class="cv-welcome-widget-label">Look for this widget on the side of the screen</p>\n </div>\n \n <button class="cv-welcome-got-it">Got it!</button>\n </div>\n </div>\n </div>\n `,document.body.appendChild(this.modal),this.attachWelcomeModalEventListeners())}attachWelcomeModalEventListeners(){if(!this.modal)return;const t=this.modal.querySelector(".cv-widget-modal-close");t&&t.addEventListener("click",()=>{this.closeModal()});const e=this.modal.querySelector(".cv-welcome-got-it");e&&e.addEventListener("click",()=>{this.closeModal()}),this.modal.addEventListener("click",t=>{t.target===this.modal&&this.closeModal()});const n=t=>{"Escape"===t.key&&(this.closeModal(),document.removeEventListener("keydown",n))};document.addEventListener("keydown",n)}}"undefined"!=typeof window&&(window.CustomViews=w,window.CustomViewsWidget=y,"undefined"!=typeof window&&(window.__customViewsInitialized?console.info("[CustomViews] Auto-init skipped: already initialized."):document.addEventListener("DOMContentLoaded",async function(){if(!window.__customViewsInitInProgress&&!window.__customViewsInitialized){window.__customViewsInitInProgress=!0;try{let t=document.currentScript;if(!t){const e=document.querySelectorAll('script[src*="@customviews-js"]');if(e.length>0){for(let n=0;n<e.length;n++){const o=e[n],s=o.getAttribute("src")||"";if(s.match(/@customviews-js\/customviews(\.min)?\.js($|\?)/)||s.includes("@customviews-js/customviews")){t=o;break}}t||(t=e[0])}}let e,n="",o="/customviews.config.json";t&&(n=t.getAttribute("data-base-url")||"",o=t.getAttribute("data-config-path")||o);try{const t=m(o,n);console.log(`[CustomViews] Loading config from: ${t}`);const s=await fetch(t);s.ok?(e=await s.json(),console.log("[CustomViews] Config loaded successfully")):(console.warn(`[CustomViews] Config file not found at ${t}. Using defaults.`),e={config:{allToggles:[],defaultState:{}},widget:{enabled:!0}})}catch(t){return void console.error("[CustomViews] Error loading config file:",t)}const s=n||e.baseURL||"",i={config:e.config,assetsJsonPath:e.assetsJsonPath,baseURL:s};void 0!==e.showUrl&&(i.showUrl=e.showUrl);const a=await w.init(i);if(!a)return void console.error("[CustomViews] Failed to initialize core.");let r;window.customViewsInstance={core:a},!1!==e.widget?.enabled?(r=new y({core:a,...e.widget}),r.render(),window.customViewsInstance.widget=r,console.log("[CustomViews] Widget initialized and rendered")):console.log("[CustomViews] Widget disabled in config - skipping initialization");const c=new CustomEvent("customviews:ready",{detail:{core:a,widget:r}});document.dispatchEvent(c),window.__customViewsInitialized=!0,window.__customViewsInitInProgress=!1}catch(t){window.__customViewsInitInProgress=!1,console.error("[CustomViews] Auto-initialization error:",t)}}}))),t.AssetsManager=g,t.CustomViews=w,t.CustomViewsCore=p,t.CustomViewsWidget=y,t.PersistenceManager=e,t.URLStateManager=n});
7
7
  //# sourceMappingURL=custom-views.min.js.map