@customviews-js/customviews 1.1.7 → 1.1.9

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,5 +1,5 @@
1
1
  /*!
2
- * @customviews-js/customviews v1.1.7
2
+ * @customviews-js/customviews v1.1.8
3
3
  * (c) 2025 Chan Ger Teck
4
4
  * Released under the MIT License.
5
5
  */
@@ -9,7 +9,8 @@
9
9
  class PersistenceManager {
10
10
  // Storage keys for localStorage
11
11
  static STORAGE_KEYS = {
12
- STATE: 'customviews-state'
12
+ STATE: 'customviews-state',
13
+ TAB_NAV_VISIBILITY: 'cv-tab-navs-visible'
13
14
  };
14
15
  /**
15
16
  * Check if localStorage is available in the current environment
@@ -46,6 +47,35 @@ class PersistenceManager {
46
47
  if (!this.isStorageAvailable())
47
48
  return;
48
49
  localStorage.removeItem(PersistenceManager.STORAGE_KEYS.STATE);
50
+ localStorage.removeItem(PersistenceManager.STORAGE_KEYS.TAB_NAV_VISIBILITY);
51
+ }
52
+ /**
53
+ * Persist tab nav visibility preference
54
+ */
55
+ persistTabNavVisibility(visible) {
56
+ if (!this.isStorageAvailable())
57
+ return;
58
+ try {
59
+ localStorage.setItem(PersistenceManager.STORAGE_KEYS.TAB_NAV_VISIBILITY, visible ? 'true' : 'false');
60
+ }
61
+ catch (error) {
62
+ console.warn('Failed to persist tab nav visibility:', error);
63
+ }
64
+ }
65
+ /**
66
+ * Get persisted tab nav visibility preference
67
+ */
68
+ getPersistedTabNavVisibility() {
69
+ if (!this.isStorageAvailable())
70
+ return null;
71
+ try {
72
+ const raw = localStorage.getItem(PersistenceManager.STORAGE_KEYS.TAB_NAV_VISIBILITY);
73
+ return raw === null ? null : raw === 'true';
74
+ }
75
+ catch (error) {
76
+ console.warn('Failed to get persisted tab nav visibility:', error);
77
+ return null;
78
+ }
49
79
  }
50
80
  /**
51
81
  * Check if any persistence data exists
@@ -1097,14 +1127,10 @@ class CustomViewsCore {
1097
1127
  this.applyState(newState);
1098
1128
  });
1099
1129
  // Apply stored nav visibility preference on page load
1100
- try {
1101
- const navPref = localStorage.getItem('cv-tab-navs-visible');
1102
- if (navPref !== null) {
1103
- const visible = navPref === 'true';
1104
- TabManager.setNavsVisibility(this.rootEl, visible);
1105
- }
1130
+ const navPref = this.persistenceManager.getPersistedTabNavVisibility();
1131
+ if (navPref !== null) {
1132
+ TabManager.setNavsVisibility(this.rootEl, navPref);
1106
1133
  }
1107
- catch (e) { /* ignore */ }
1108
1134
  // For session history, clicks on back/forward button
1109
1135
  window.addEventListener("popstate", () => {
1110
1136
  this.loadAndCallApplyState();
@@ -1171,6 +1197,8 @@ class CustomViewsCore {
1171
1197
  else {
1172
1198
  console.warn("No configuration loaded, cannot reset to default state");
1173
1199
  }
1200
+ // Reset tab nav visibility to default (visible)
1201
+ TabManager.setNavsVisibility(this.rootEl, true);
1174
1202
  // Clear URL
1175
1203
  URLStateManager.clearURL();
1176
1204
  }
@@ -1249,6 +1277,18 @@ class CustomViewsCore {
1249
1277
  }
1250
1278
  });
1251
1279
  }
1280
+ /**
1281
+ * Persist tab nav visibility preference
1282
+ */
1283
+ persistTabNavVisibility(visible) {
1284
+ this.persistenceManager.persistTabNavVisibility(visible);
1285
+ }
1286
+ /**
1287
+ * Get persisted tab nav visibility preference
1288
+ */
1289
+ getPersistedTabNavVisibility() {
1290
+ return this.persistenceManager.getPersistedTabNavVisibility();
1291
+ }
1252
1292
  cloneState(state) {
1253
1293
  if (!state)
1254
1294
  return {};
@@ -2067,6 +2107,57 @@ const WIDGET_STYLES = `
2067
2107
  gap: 1rem;
2068
2108
  }
2069
2109
 
2110
+ /* Navigation toggle icon container */
2111
+ .cv-nav-toggle-container {
2112
+ display: flex;
2113
+ align-items: center;
2114
+ gap: 0.5rem;
2115
+ }
2116
+
2117
+ .cv-nav-icon {
2118
+ width: 2rem;
2119
+ height: 2rem;
2120
+ color: rgba(0, 0, 0, 0.8);
2121
+ display: flex;
2122
+ align-items: center;
2123
+ justify-content: center;
2124
+ flex-shrink: 0;
2125
+ transition: color 0.2s ease;
2126
+ }
2127
+
2128
+ /* Logo box - centered grey box on its own row */
2129
+ .cv-tabgroup-logo-box {
2130
+ width: 3.5rem;
2131
+ height: 3.5rem;
2132
+ background: rgba(0, 0, 0, 0.08);
2133
+ border-radius: 0.5rem;
2134
+ display: flex;
2135
+ align-items: center;
2136
+ justify-content: center;
2137
+ flex-shrink: 0;
2138
+ margin-bottom: 0.5rem;
2139
+ }
2140
+
2141
+ /* Title container for title alignment (without icon) */
2142
+ .cv-tabgroup-title-container {
2143
+ display: flex;
2144
+ align-items: center;
2145
+ gap: 0.5rem;
2146
+ }
2147
+
2148
+ /* Hover state for icon - apply to the entire tabgroup-row */
2149
+ .cv-tabgroup-card.cv-tabgroup-header:hover .cv-nav-icon {
2150
+ color: #3e84f4;
2151
+ }
2152
+
2153
+ .cv-widget-theme-dark .cv-nav-icon {
2154
+ color: rgba(255, 255, 255, 0.8);
2155
+ }
2156
+
2157
+ .cv-widget-theme-dark .cv-tabgroup-card.cv-tabgroup-header:hover .cv-nav-icon {
2158
+ color: #60a5fa;
2159
+ }
2160
+
2070
2161
  /* Tab Group Card - Items */
2071
2162
  .cv-tabgroup-card.cv-tabgroup-item {
2072
2163
  display: flex;
@@ -2084,13 +2175,16 @@ const WIDGET_STYLES = `
2084
2175
  /* Tab Group Info */
2085
2176
  .cv-tabgroup-info {
2086
2177
  flex: 1;
2178
+ display: flex;
2179
+ flex-direction: column;
2180
+ gap: 0.25rem;
2087
2181
  }
2088
2182
 
2089
2183
  .cv-tabgroup-title {
2090
2184
  font-weight: 500;
2091
2185
  font-size: 0.875rem;
2092
2186
  color: rgba(0, 0, 0, 0.9);
2093
- margin: 0 0 0.25rem 0;
2187
+ margin: 0 0 0 0;
2094
2188
  }
2095
2189
 
2096
2190
  .cv-tabgroup-description {
@@ -2281,6 +2375,10 @@ const WIDGET_STYLES = `
2281
2375
  .cv-btn-icon {
2282
2376
  width: 1rem;
2283
2377
  height: 1rem;
2378
+ display: flex;
2379
+ align-items: center;
2380
+ justify-content: center;
2381
+ transition: transform 0.2s ease;
2284
2382
  }
2285
2383
 
2286
2384
  /* Dark theme custom state styles */
@@ -2406,6 +2504,32 @@ const WIDGET_STYLES = `
2406
2504
  .cv-widget-theme-dark .cv-welcome-widget-label {
2407
2505
  color: #e2e8f0;
2408
2506
  }
2507
+
2508
+ /* Dark theme logo box */
2509
+ .cv-widget-theme-dark .cv-tabgroup-logo-box {
2510
+ background: rgba(255, 255, 255, 0.1);
2511
+ }
2512
+
2513
+ /* Spinning animation for reset button icon */
2514
+ @keyframes cv-spin {
2515
+ from {
2516
+ transform: rotate(0deg);
2517
+ }
2518
+ to {
2519
+ transform: rotate(-360deg);
2520
+ }
2521
+ }
2522
+
2523
+ .cv-spinning {
2524
+ animation: cv-spin 0.6s ease-in-out;
2525
+ }
2526
+
2527
+ /* Hide widget icon in print view */
2528
+ @media print {
2529
+ .cv-widget-icon {
2530
+ display: none !important;
2531
+ }
2532
+ }
2409
2533
  `;
2410
2534
  /**
2411
2535
  * Inject widget styles into the document head
@@ -2450,13 +2574,80 @@ function getResetIcon() {
2450
2574
  </svg>`;
2451
2575
  }
2452
2576
  /**
2453
- * Share icon for share button
2577
+ * Copy icon for sharing URL button
2454
2578
  */
2455
- function getShareIcon() {
2456
- return `<svg class="cv-btn-icon" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
2457
- <path xmlns="http://www.w3.org/2000/svg" d="M6 11C6 8.17157 6 6.75736 6.87868 5.87868C7.75736 5 9.17157 5 12 5H15C17.8284 5 19.2426 5 20.1213 5.87868C21 6.75736 21 8.17157 21 11V16C21 18.8284 21 20.2426 20.1213 21.1213C19.2426 22 17.8284 22 15 22H12C9.17157 22 7.75736 22 6.87868 21.1213C6 20.2426 6 18.8284 6 16V11Z" stroke="#1C274C" stroke-width="1.5"/>
2458
- <path xmlns="http://www.w3.org/2000/svg" opacity="0.5" d="M6 19C4.34315 19 3 17.6569 3 16V10C3 6.22876 3 4.34315 4.17157 3.17157C5.34315 2 7.22876 2 11 2H15C16.6569 2 18 3.34315 18 5" stroke="#1C274C" stroke-width="1.5"/>
2459
- </svg>`;
2579
+ function getCopyIcon() {
2580
+ return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18" height="18" viewBox="0 0 18 18" version="1.1" fill="currentColor">
2581
+ <g id="surface1">
2582
+ <path d="M 11.273438 0 L 2.546875 0 C 1.746094 0 1.089844 0.613281 1.089844
2583
+ 1.363281 L 1.089844 10.910156 L 2.546875 10.910156 L 2.546875 1.363281 L 11.273438
2584
+ 1.363281 Z M 13.453125 2.726562 L 5.453125 2.726562 C 4.65625 2.726562 4 3.339844 4
2585
+ 4.089844 L 4 13.636719 C 4 14.386719 4.65625 15 5.453125 15 L 13.453125 15 C 14.253906
2586
+ 15 14.910156 14.386719 14.910156 13.636719 L 14.910156 4.089844 C 14.910156 3.339844
2587
+ 14.253906 2.726562 13.453125 2.726562 Z M 13.453125 13.636719 L 5.453125 13.636719 L
2588
+ 5.453125 4.089844 L 13.453125 4.089844 Z M 13.453125 13.636719 "></path>
2589
+ </g>
2590
+ </svg>`;
2591
+ }
2592
+ function getTickIcon() {
2593
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="2 2 22 22" fill="currentColor">
2594
+ <path d="M 19.28125 5.28125 L 9 15.5625 L 4.71875 11.28125 L 3.28125 12.71875 L 8.28125 17.71875
2595
+ L 9 18.40625 L 9.71875 17.71875 L 20.71875 6.71875 Z"></path>
2596
+ </svg>`;
2597
+ }
2598
+ function getNavHeadingOnIcon() {
2599
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="250" height="181" viewBox="0 0 250 181">
2600
+ <rect y="34.5001" width="250" height="146" rx="4" stroke="currentColor" stroke-width="10" fill="none"/>
2601
+ <line x1="27" y1="62.0001" x2="77" y2="62.0001" stroke="currentColor" stroke-width="5"/>
2602
+ <line x1="27" y1="77.8888" x2="77" y2="77.8888" stroke="currentColor" stroke-width="5"/>
2603
+ <line x1="27" y1="97.4454" x2="221" y2="97.4454" stroke="currentColor" stroke-width="5"/>
2604
+ <line x1="27" y1="114.555" x2="221" y2="114.555" stroke="currentColor" stroke-width="5"/>
2605
+ <line x1="27" y1="132.889" x2="221" y2="132.889" stroke="currentColor" stroke-width="5"/>
2606
+ <line x1="27" y1="150" x2="221" y2="150" stroke="currentColor" stroke-width="5"/>
2607
+ <line x1="247.5" y1="43.0001" x2="247.5" y2="13.0001" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2608
+ <path d="M185 12.5001L247 12.5001" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2609
+ <line x1="204.09" y1="36.6095" x2="181.698" y2="10.0228" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2610
+ <path d="M125 9.50012L181 9.50012" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2611
+ <path d="M144.305 35.2579L120.095 6.56679" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2612
+ <path d="M120 6.50037L64 6.50037" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2613
+ <path d="M87.1957 36.1024L59 2.50008" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2614
+ <path d="M59 2.50037L3 2.50037" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2615
+ <path d="M2.5 38.5001L2.5 3.00012" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2616
+ </svg>`;
2617
+ }
2618
+ function getNavHeadingOffIcon() {
2619
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="250" height="181" viewBox="0 0 250 181" fill="currentColor">
2620
+ <rect y="34.5001" width="250" height="146" rx="4" stroke="currentColor" stroke-width="10" fill="none"/>
2621
+ <line x1="27" y1="62" x2="77" y2="62" stroke="currentColor" stroke-width="5"/>
2622
+ <line x1="27" y1="77.8887" x2="77" y2="77.8887" stroke="currentColor" stroke-width="5"/>
2623
+ <line x1="27" y1="97.4453" x2="221" y2="97.4453" stroke="currentColor" stroke-width="5"/>
2624
+ <line x1="27" y1="114.555" x2="221" y2="114.555" stroke="currentColor" stroke-width="5"/>
2625
+ <line x1="27" y1="132.889" x2="221" y2="132.889" stroke="currentColor" stroke-width="5"/>
2626
+ <line x1="27" y1="150" x2="221" y2="150" stroke="currentColor" stroke-width="5"/>
2627
+ </svg>`;
2628
+ }
2629
+ /**
2630
+ * Transition Icon for Navigation Headings
2631
+ */
2632
+ function getNavDashed() {
2633
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="250" height="181" viewBox="0 0 250 181">
2634
+ <rect y="34.5001" width="250" height="146" rx="4" stroke="currentColor" stroke-width="10" fill="none"/>
2635
+ <line x1="27" y1="62.0001" x2="77" y2="62.0001" stroke="currentColor" stroke-width="5"/>
2636
+ <line x1="27" y1="77.8888" x2="77" y2="77.8888" stroke="currentColor" stroke-width="5"/>
2637
+ <line x1="27" y1="97.4454" x2="221" y2="97.4454" stroke="currentColor" stroke-width="5"/>
2638
+ <line x1="27" y1="114.555" x2="221" y2="114.555" stroke="currentColor" stroke-width="5"/>
2639
+ <line x1="27" y1="132.889" x2="221" y2="132.889" stroke="currentColor" stroke-width="5"/>
2640
+ <line x1="27" y1="150" x2="221" y2="150" stroke="currentColor" stroke-width="5"/>
2641
+ <path d="M245 37.0001V39.5001H250V37.0001H247.5H245ZM250 13.0001C250 11.6194 248.881 10.5001 247.5 10.5001C246.119 10.5001 245 11.6194 245 13.0001H247.5H250ZM250 31.0001C250 29.6194 248.881 28.5001 247.5 28.5001C246.119 28.5001 245 29.6194 245 31.0001H247.5H250ZM245 19.0001C245 20.3808 246.119 21.5001 247.5 21.5001C248.881 21.5001 250 20.3808 250 19.0001H247.5H245ZM247.5 37.0001H250V31.0001H247.5H245V37.0001H247.5ZM247.5 19.0001H250V13.0001H247.5H245V19.0001H247.5Z" fill="currentColor"/>
2642
+ <line x1="204.09" y1="36.6095" x2="181.698" y2="10.0228" stroke="currentColor" stroke-width="5" stroke-linecap="round" stroke-dasharray="10 10"/>
2643
+ <path d="M125 9.50012L181 9.50012" stroke="currentColor" stroke-width="5" stroke-linecap="round" stroke-dasharray="10 10"/>
2644
+ <path d="M144.305 35.2579L120.095 6.56679" stroke="currentColor" stroke-width="5" stroke-linecap="round" stroke-dasharray="10 10"/>
2645
+ <path d="M120 6.50037L64 6.50037" stroke="currentColor" stroke-width="5" stroke-linecap="round" stroke-dasharray="10 10"/>
2646
+ <path d="M87.1957 36.1024L59 2.50008" stroke="currentColor" stroke-width="5" stroke-linecap="round" stroke-dasharray="10 10"/>
2647
+ <path d="M59 2.50037L3 2.50037" stroke="currentColor" stroke-width="5" stroke-linecap="round" stroke-dasharray="10 10"/>
2648
+ <path d="M2.5 38.5001L2.5 3.00012" stroke="currentColor" stroke-width="5" stroke-linecap="round" stroke-dasharray="10 10"/>
2649
+ <path d="M185 12.5001L247 12.5001" stroke="currentColor" stroke-width="5" stroke-linecap="round" stroke-dasharray="10 10"/>
2650
+ </svg>`;
2460
2651
  }
2461
2652
 
2462
2653
  class CustomViewsWidget {
@@ -2578,15 +2769,22 @@ class CustomViewsWidget {
2578
2769
  const tabGroups = this.core.getTabGroups();
2579
2770
  let tabGroupControlsHTML = '';
2580
2771
  if (this.options.showTabGroups && tabGroups && tabGroups.length > 0) {
2772
+ // Determine initial nav visibility state
2773
+ const initialNavsVisible = TabManager.areNavsVisible(document.body);
2581
2774
  tabGroupControlsHTML = `
2582
2775
  <div class="cv-tabgroup-card cv-tabgroup-header">
2583
2776
  <div class="cv-tabgroup-row">
2777
+ <div class="cv-tabgroup-logo-box" id="cv-nav-icon-box">
2778
+ <div class="cv-nav-icon" id="cv-nav-icon">${initialNavsVisible ? getNavHeadingOnIcon() : getNavHeadingOffIcon()}</div>
2779
+ </div>
2584
2780
  <div class="cv-tabgroup-info">
2585
- <p class="cv-tabgroup-title">Navigation Headers</p>
2781
+ <div class="cv-tabgroup-title-container">
2782
+ <p class="cv-tabgroup-title">Navigation Headers</p>
2783
+ </div>
2586
2784
  <p class="cv-tabgroup-description">Show or hide navigation headers</p>
2587
2785
  </div>
2588
2786
  <label class="cv-toggle-switch cv-nav-toggle">
2589
- <input class="cv-nav-pref-input" type="checkbox" aria-label="Show or hide navigation headers" />
2787
+ <input class="cv-nav-pref-input" type="checkbox" ${initialNavsVisible ? 'checked' : ''} aria-label="Show or hide navigation headers" />
2590
2788
  <span class="cv-switch-bg"></span>
2591
2789
  <span class="cv-switch-knob"></span>
2592
2790
  </label>
@@ -2644,13 +2842,13 @@ class CustomViewsWidget {
2644
2842
  <footer class="cv-modal-footer">
2645
2843
  ${this.options.showReset ? `
2646
2844
  <button class="cv-reset-btn">
2647
- ${getResetIcon()}
2845
+ <span class="cv-reset-btn-icon">${getResetIcon()}</span>
2648
2846
  <span>Reset to Default</span>
2649
2847
  </button>
2650
2848
  ` : ''}
2651
2849
  <button class="cv-share-btn">
2652
- ${getShareIcon()}
2653
2850
  <span>Copy Shareable URL</span>
2851
+ <span class="cv-share-btn-icon">${getCopyIcon()}</span>
2654
2852
  </button>
2655
2853
  </footer>
2656
2854
  </div>
@@ -2678,14 +2876,35 @@ class CustomViewsWidget {
2678
2876
  if (copyUrlBtn) {
2679
2877
  copyUrlBtn.addEventListener('click', () => {
2680
2878
  this.copyShareableURL();
2879
+ // Visual feedback: change icon to tick for 3 seconds
2880
+ const iconContainer = copyUrlBtn.querySelector('.cv-share-btn-icon');
2881
+ if (iconContainer) {
2882
+ const originalIcon = iconContainer.innerHTML;
2883
+ iconContainer.innerHTML = getTickIcon();
2884
+ // Revert after 3 seconds
2885
+ setTimeout(() => {
2886
+ iconContainer.innerHTML = originalIcon;
2887
+ }, 3000);
2888
+ }
2681
2889
  });
2682
2890
  }
2683
2891
  // Reset to default button
2684
2892
  const resetBtn = this.modal.querySelector('.cv-reset-btn');
2685
2893
  if (resetBtn) {
2686
2894
  resetBtn.addEventListener('click', () => {
2895
+ // Add spinning animation to icon
2896
+ const resetIcon = resetBtn.querySelector('.cv-reset-btn-icon');
2897
+ if (resetIcon) {
2898
+ resetIcon.classList.add('cv-spinning');
2899
+ }
2687
2900
  this.core.resetToDefault();
2688
2901
  this.loadCurrentStateIntoForm();
2902
+ // Remove spinning animation after it completes
2903
+ setTimeout(() => {
2904
+ if (resetIcon) {
2905
+ resetIcon.classList.remove('cv-spinning');
2906
+ }
2907
+ }, 600); // 600ms matches the animation duration
2689
2908
  });
2690
2909
  }
2691
2910
  // Listen to toggle switches
@@ -2719,14 +2938,34 @@ class CustomViewsWidget {
2719
2938
  });
2720
2939
  // Listener for show/hide tab navs
2721
2940
  const tabNavToggle = this.modal.querySelector('.cv-nav-pref-input');
2722
- if (tabNavToggle) {
2941
+ const navIcon = this.modal?.querySelector('#cv-nav-icon');
2942
+ const navHeaderCard = this.modal?.querySelector('.cv-tabgroup-card.cv-tabgroup-header');
2943
+ if (tabNavToggle && navIcon && navHeaderCard) {
2944
+ // Helper to update icon based on state
2945
+ const updateIcon = (isVisible, isHovering = false) => {
2946
+ if (isHovering) {
2947
+ // On hover, show the transition icon
2948
+ navIcon.innerHTML = getNavDashed();
2949
+ }
2950
+ else {
2951
+ // Normal state, show the status icon (on if visible, off if hidden)
2952
+ navIcon.innerHTML = isVisible ? getNavHeadingOnIcon() : getNavHeadingOffIcon();
2953
+ }
2954
+ };
2955
+ // Add hover listeners to entire header card
2956
+ navHeaderCard.addEventListener('mouseenter', () => {
2957
+ updateIcon(tabNavToggle.checked, true);
2958
+ });
2959
+ navHeaderCard.addEventListener('mouseleave', () => {
2960
+ updateIcon(tabNavToggle.checked, false);
2961
+ });
2962
+ // Add change listener
2723
2963
  tabNavToggle.addEventListener('change', () => {
2724
2964
  const visible = tabNavToggle.checked;
2725
- // Persist preference
2726
- try {
2727
- localStorage.setItem('cv-tab-navs-visible', visible ? 'true' : 'false');
2728
- }
2729
- catch (e) { /* ignore */ }
2965
+ // Update the icon based on new state (not hovering)
2966
+ updateIcon(visible, false);
2967
+ // Persist preference via core
2968
+ this.core.persistTabNavVisibility(visible);
2730
2969
  // Apply to DOM using TabManager via core
2731
2970
  try {
2732
2971
  const rootEl = document.body;
@@ -2838,21 +3077,22 @@ class CustomViewsWidget {
2838
3077
  });
2839
3078
  // Load tab nav visibility preference
2840
3079
  const navPref = (() => {
2841
- try {
2842
- const raw = localStorage.getItem('cv-tab-navs-visible');
2843
- if (raw === null)
2844
- return TabManager.areNavsVisible(document.body);
2845
- return raw === 'true';
2846
- }
2847
- catch (e) {
2848
- return TabManager.areNavsVisible(document.body);
3080
+ const persisted = this.core.getPersistedTabNavVisibility();
3081
+ if (persisted !== null) {
3082
+ return persisted;
2849
3083
  }
3084
+ return TabManager.areNavsVisible(document.body);
2850
3085
  })();
2851
3086
  const tabNavToggle = this.modal.querySelector('.cv-nav-pref-input');
3087
+ const navIcon = this.modal?.querySelector('#cv-nav-icon');
2852
3088
  if (tabNavToggle) {
2853
3089
  tabNavToggle.checked = navPref;
2854
3090
  // Ensure UI matches actual visibility
2855
3091
  TabManager.setNavsVisibility(document.body, navPref);
3092
+ // Update the nav icon to reflect the current state
3093
+ if (navIcon) {
3094
+ navIcon.innerHTML = navPref ? getNavHeadingOnIcon() : getNavHeadingOffIcon();
3095
+ }
2856
3096
  }
2857
3097
  }
2858
3098
  /**
@@ -2869,12 +3109,19 @@ class CustomViewsWidget {
2869
3109
  // Check if welcome has been shown before
2870
3110
  const hasSeenWelcome = localStorage.getItem(STORAGE_KEY);
2871
3111
  if (!hasSeenWelcome) {
2872
- // Show welcome modal after a short delay to let the page settle
2873
- setTimeout(() => {
2874
- this.createWelcomeModal();
2875
- }, 500);
2876
- // Mark as shown
2877
- localStorage.setItem(STORAGE_KEY, 'true');
3112
+ // Check if this page has any custom views elements
3113
+ const hasCustomViewElements = document.querySelector('cv-tabgroup') !== null ||
3114
+ document.querySelector('cv-tab') !== null ||
3115
+ document.querySelector('cv-toggle') !== null ||
3116
+ document.querySelector('[data-cv-toggle]') !== null;
3117
+ if (hasCustomViewElements) {
3118
+ // Show welcome modal after a short delay to let the page settle
3119
+ setTimeout(() => {
3120
+ this.createWelcomeModal();
3121
+ }, 500);
3122
+ // Mark as shown
3123
+ localStorage.setItem(STORAGE_KEY, 'true');
3124
+ }
2878
3125
  }
2879
3126
  }
2880
3127
  /**
@@ -2973,17 +3220,20 @@ function initializeFromScript() {
2973
3220
  }
2974
3221
  window.__customViewsInitInProgress = true;
2975
3222
  try {
2976
- // Find the script tag
3223
+ // Find the script tag that loaded this script
2977
3224
  let scriptTag = document.currentScript;
2978
- // Fallback if currentScript is not available (executed after page load)
2979
- if (!scriptTag) {
2980
- // Match the actual CustomViews bundle files (e.g., custom-views.min.js, custom-views.js)
2981
- for (const script of document.scripts) {
2982
- const src = script.src || '';
2983
- // Match filenames like: custom-views.min.js, custom-views.js, custom-views.esm.js
2984
- if (/custom-views(?:\.min)?\.(?:esm\.)?js($|\?)/i.test(src)) {
2985
- scriptTag = script;
2986
- break;
3225
+ if (!scriptTag || !scriptTag.hasAttribute('data-base-url')) {
3226
+ const dataAttrMatch = document.querySelector('script[data-base-url]');
3227
+ if (dataAttrMatch) {
3228
+ scriptTag = dataAttrMatch;
3229
+ }
3230
+ else {
3231
+ for (const script of document.scripts) {
3232
+ const src = script.src || '';
3233
+ if (/(?:custom[-_]views|@customviews-js\/customviews)(?:\.min)?\.(?:esm\.)?js($|\?)/i.test(src)) {
3234
+ scriptTag = script;
3235
+ break;
3236
+ }
2987
3237
  }
2988
3238
  }
2989
3239
  }