@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
  */
@@ -15,7 +15,8 @@
15
15
  class PersistenceManager {
16
16
  // Storage keys for localStorage
17
17
  static STORAGE_KEYS = {
18
- STATE: 'customviews-state'
18
+ STATE: 'customviews-state',
19
+ TAB_NAV_VISIBILITY: 'cv-tab-navs-visible'
19
20
  };
20
21
  /**
21
22
  * Check if localStorage is available in the current environment
@@ -52,6 +53,35 @@
52
53
  if (!this.isStorageAvailable())
53
54
  return;
54
55
  localStorage.removeItem(PersistenceManager.STORAGE_KEYS.STATE);
56
+ localStorage.removeItem(PersistenceManager.STORAGE_KEYS.TAB_NAV_VISIBILITY);
57
+ }
58
+ /**
59
+ * Persist tab nav visibility preference
60
+ */
61
+ persistTabNavVisibility(visible) {
62
+ if (!this.isStorageAvailable())
63
+ return;
64
+ try {
65
+ localStorage.setItem(PersistenceManager.STORAGE_KEYS.TAB_NAV_VISIBILITY, visible ? 'true' : 'false');
66
+ }
67
+ catch (error) {
68
+ console.warn('Failed to persist tab nav visibility:', error);
69
+ }
70
+ }
71
+ /**
72
+ * Get persisted tab nav visibility preference
73
+ */
74
+ getPersistedTabNavVisibility() {
75
+ if (!this.isStorageAvailable())
76
+ return null;
77
+ try {
78
+ const raw = localStorage.getItem(PersistenceManager.STORAGE_KEYS.TAB_NAV_VISIBILITY);
79
+ return raw === null ? null : raw === 'true';
80
+ }
81
+ catch (error) {
82
+ console.warn('Failed to get persisted tab nav visibility:', error);
83
+ return null;
84
+ }
55
85
  }
56
86
  /**
57
87
  * Check if any persistence data exists
@@ -1103,14 +1133,10 @@ ${TAB_STYLES}
1103
1133
  this.applyState(newState);
1104
1134
  });
1105
1135
  // Apply stored nav visibility preference on page load
1106
- try {
1107
- const navPref = localStorage.getItem('cv-tab-navs-visible');
1108
- if (navPref !== null) {
1109
- const visible = navPref === 'true';
1110
- TabManager.setNavsVisibility(this.rootEl, visible);
1111
- }
1136
+ const navPref = this.persistenceManager.getPersistedTabNavVisibility();
1137
+ if (navPref !== null) {
1138
+ TabManager.setNavsVisibility(this.rootEl, navPref);
1112
1139
  }
1113
- catch (e) { /* ignore */ }
1114
1140
  // For session history, clicks on back/forward button
1115
1141
  window.addEventListener("popstate", () => {
1116
1142
  this.loadAndCallApplyState();
@@ -1177,6 +1203,8 @@ ${TAB_STYLES}
1177
1203
  else {
1178
1204
  console.warn("No configuration loaded, cannot reset to default state");
1179
1205
  }
1206
+ // Reset tab nav visibility to default (visible)
1207
+ TabManager.setNavsVisibility(this.rootEl, true);
1180
1208
  // Clear URL
1181
1209
  URLStateManager.clearURL();
1182
1210
  }
@@ -1255,6 +1283,18 @@ ${TAB_STYLES}
1255
1283
  }
1256
1284
  });
1257
1285
  }
1286
+ /**
1287
+ * Persist tab nav visibility preference
1288
+ */
1289
+ persistTabNavVisibility(visible) {
1290
+ this.persistenceManager.persistTabNavVisibility(visible);
1291
+ }
1292
+ /**
1293
+ * Get persisted tab nav visibility preference
1294
+ */
1295
+ getPersistedTabNavVisibility() {
1296
+ return this.persistenceManager.getPersistedTabNavVisibility();
1297
+ }
1258
1298
  cloneState(state) {
1259
1299
  if (!state)
1260
1300
  return {};
@@ -2073,6 +2113,57 @@ ${TAB_STYLES}
2073
2113
  gap: 1rem;
2074
2114
  }
2075
2115
 
2116
+ /* Navigation toggle icon container */
2117
+ .cv-nav-toggle-container {
2118
+ display: flex;
2119
+ align-items: center;
2120
+ gap: 0.5rem;
2121
+ }
2122
+
2123
+ .cv-nav-icon {
2124
+ width: 2rem;
2125
+ height: 2rem;
2126
+ color: rgba(0, 0, 0, 0.8);
2127
+ display: flex;
2128
+ align-items: center;
2129
+ justify-content: center;
2130
+ flex-shrink: 0;
2131
+ transition: color 0.2s ease;
2132
+ }
2133
+
2134
+ /* Logo box - centered grey box on its own row */
2135
+ .cv-tabgroup-logo-box {
2136
+ width: 3.5rem;
2137
+ height: 3.5rem;
2138
+ background: rgba(0, 0, 0, 0.08);
2139
+ border-radius: 0.5rem;
2140
+ display: flex;
2141
+ align-items: center;
2142
+ justify-content: center;
2143
+ flex-shrink: 0;
2144
+ margin-bottom: 0.5rem;
2145
+ }
2146
+
2147
+ /* Title container for title alignment (without icon) */
2148
+ .cv-tabgroup-title-container {
2149
+ display: flex;
2150
+ align-items: center;
2151
+ gap: 0.5rem;
2152
+ }
2153
+
2154
+ /* Hover state for icon - apply to the entire tabgroup-row */
2155
+ .cv-tabgroup-card.cv-tabgroup-header:hover .cv-nav-icon {
2156
+ color: #3e84f4;
2157
+ }
2158
+
2159
+ .cv-widget-theme-dark .cv-nav-icon {
2160
+ color: rgba(255, 255, 255, 0.8);
2161
+ }
2162
+
2163
+ .cv-widget-theme-dark .cv-tabgroup-card.cv-tabgroup-header:hover .cv-nav-icon {
2164
+ color: #60a5fa;
2165
+ }
2166
+
2076
2167
  /* Tab Group Card - Items */
2077
2168
  .cv-tabgroup-card.cv-tabgroup-item {
2078
2169
  display: flex;
@@ -2090,13 +2181,16 @@ ${TAB_STYLES}
2090
2181
  /* Tab Group Info */
2091
2182
  .cv-tabgroup-info {
2092
2183
  flex: 1;
2184
+ display: flex;
2185
+ flex-direction: column;
2186
+ gap: 0.25rem;
2093
2187
  }
2094
2188
 
2095
2189
  .cv-tabgroup-title {
2096
2190
  font-weight: 500;
2097
2191
  font-size: 0.875rem;
2098
2192
  color: rgba(0, 0, 0, 0.9);
2099
- margin: 0 0 0.25rem 0;
2193
+ margin: 0 0 0 0;
2100
2194
  }
2101
2195
 
2102
2196
  .cv-tabgroup-description {
@@ -2287,6 +2381,10 @@ ${TAB_STYLES}
2287
2381
  .cv-btn-icon {
2288
2382
  width: 1rem;
2289
2383
  height: 1rem;
2384
+ display: flex;
2385
+ align-items: center;
2386
+ justify-content: center;
2387
+ transition: transform 0.2s ease;
2290
2388
  }
2291
2389
 
2292
2390
  /* Dark theme custom state styles */
@@ -2412,6 +2510,32 @@ ${TAB_STYLES}
2412
2510
  .cv-widget-theme-dark .cv-welcome-widget-label {
2413
2511
  color: #e2e8f0;
2414
2512
  }
2513
+
2514
+ /* Dark theme logo box */
2515
+ .cv-widget-theme-dark .cv-tabgroup-logo-box {
2516
+ background: rgba(255, 255, 255, 0.1);
2517
+ }
2518
+
2519
+ /* Spinning animation for reset button icon */
2520
+ @keyframes cv-spin {
2521
+ from {
2522
+ transform: rotate(0deg);
2523
+ }
2524
+ to {
2525
+ transform: rotate(-360deg);
2526
+ }
2527
+ }
2528
+
2529
+ .cv-spinning {
2530
+ animation: cv-spin 0.6s ease-in-out;
2531
+ }
2532
+
2533
+ /* Hide widget icon in print view */
2534
+ @media print {
2535
+ .cv-widget-icon {
2536
+ display: none !important;
2537
+ }
2538
+ }
2415
2539
  `;
2416
2540
  /**
2417
2541
  * Inject widget styles into the document head
@@ -2456,13 +2580,80 @@ ${TAB_STYLES}
2456
2580
  </svg>`;
2457
2581
  }
2458
2582
  /**
2459
- * Share icon for share button
2583
+ * Copy icon for sharing URL button
2460
2584
  */
2461
- function getShareIcon() {
2462
- return `<svg class="cv-btn-icon" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
2463
- <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"/>
2464
- <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"/>
2465
- </svg>`;
2585
+ function getCopyIcon() {
2586
+ 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">
2587
+ <g id="surface1">
2588
+ <path d="M 11.273438 0 L 2.546875 0 C 1.746094 0 1.089844 0.613281 1.089844
2589
+ 1.363281 L 1.089844 10.910156 L 2.546875 10.910156 L 2.546875 1.363281 L 11.273438
2590
+ 1.363281 Z M 13.453125 2.726562 L 5.453125 2.726562 C 4.65625 2.726562 4 3.339844 4
2591
+ 4.089844 L 4 13.636719 C 4 14.386719 4.65625 15 5.453125 15 L 13.453125 15 C 14.253906
2592
+ 15 14.910156 14.386719 14.910156 13.636719 L 14.910156 4.089844 C 14.910156 3.339844
2593
+ 14.253906 2.726562 13.453125 2.726562 Z M 13.453125 13.636719 L 5.453125 13.636719 L
2594
+ 5.453125 4.089844 L 13.453125 4.089844 Z M 13.453125 13.636719 "></path>
2595
+ </g>
2596
+ </svg>`;
2597
+ }
2598
+ function getTickIcon() {
2599
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="2 2 22 22" fill="currentColor">
2600
+ <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
2601
+ L 9 18.40625 L 9.71875 17.71875 L 20.71875 6.71875 Z"></path>
2602
+ </svg>`;
2603
+ }
2604
+ function getNavHeadingOnIcon() {
2605
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="250" height="181" viewBox="0 0 250 181">
2606
+ <rect y="34.5001" width="250" height="146" rx="4" stroke="currentColor" stroke-width="10" fill="none"/>
2607
+ <line x1="27" y1="62.0001" x2="77" y2="62.0001" stroke="currentColor" stroke-width="5"/>
2608
+ <line x1="27" y1="77.8888" x2="77" y2="77.8888" stroke="currentColor" stroke-width="5"/>
2609
+ <line x1="27" y1="97.4454" x2="221" y2="97.4454" stroke="currentColor" stroke-width="5"/>
2610
+ <line x1="27" y1="114.555" x2="221" y2="114.555" stroke="currentColor" stroke-width="5"/>
2611
+ <line x1="27" y1="132.889" x2="221" y2="132.889" stroke="currentColor" stroke-width="5"/>
2612
+ <line x1="27" y1="150" x2="221" y2="150" stroke="currentColor" stroke-width="5"/>
2613
+ <line x1="247.5" y1="43.0001" x2="247.5" y2="13.0001" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2614
+ <path d="M185 12.5001L247 12.5001" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2615
+ <line x1="204.09" y1="36.6095" x2="181.698" y2="10.0228" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2616
+ <path d="M125 9.50012L181 9.50012" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2617
+ <path d="M144.305 35.2579L120.095 6.56679" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2618
+ <path d="M120 6.50037L64 6.50037" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2619
+ <path d="M87.1957 36.1024L59 2.50008" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2620
+ <path d="M59 2.50037L3 2.50037" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2621
+ <path d="M2.5 38.5001L2.5 3.00012" stroke="currentColor" stroke-width="5" stroke-linecap="round"/>
2622
+ </svg>`;
2623
+ }
2624
+ function getNavHeadingOffIcon() {
2625
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="250" height="181" viewBox="0 0 250 181" fill="currentColor">
2626
+ <rect y="34.5001" width="250" height="146" rx="4" stroke="currentColor" stroke-width="10" fill="none"/>
2627
+ <line x1="27" y1="62" x2="77" y2="62" stroke="currentColor" stroke-width="5"/>
2628
+ <line x1="27" y1="77.8887" x2="77" y2="77.8887" stroke="currentColor" stroke-width="5"/>
2629
+ <line x1="27" y1="97.4453" x2="221" y2="97.4453" stroke="currentColor" stroke-width="5"/>
2630
+ <line x1="27" y1="114.555" x2="221" y2="114.555" stroke="currentColor" stroke-width="5"/>
2631
+ <line x1="27" y1="132.889" x2="221" y2="132.889" stroke="currentColor" stroke-width="5"/>
2632
+ <line x1="27" y1="150" x2="221" y2="150" stroke="currentColor" stroke-width="5"/>
2633
+ </svg>`;
2634
+ }
2635
+ /**
2636
+ * Transition Icon for Navigation Headings
2637
+ */
2638
+ function getNavDashed() {
2639
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="250" height="181" viewBox="0 0 250 181">
2640
+ <rect y="34.5001" width="250" height="146" rx="4" stroke="currentColor" stroke-width="10" fill="none"/>
2641
+ <line x1="27" y1="62.0001" x2="77" y2="62.0001" stroke="currentColor" stroke-width="5"/>
2642
+ <line x1="27" y1="77.8888" x2="77" y2="77.8888" stroke="currentColor" stroke-width="5"/>
2643
+ <line x1="27" y1="97.4454" x2="221" y2="97.4454" stroke="currentColor" stroke-width="5"/>
2644
+ <line x1="27" y1="114.555" x2="221" y2="114.555" stroke="currentColor" stroke-width="5"/>
2645
+ <line x1="27" y1="132.889" x2="221" y2="132.889" stroke="currentColor" stroke-width="5"/>
2646
+ <line x1="27" y1="150" x2="221" y2="150" stroke="currentColor" stroke-width="5"/>
2647
+ <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"/>
2648
+ <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"/>
2649
+ <path d="M125 9.50012L181 9.50012" stroke="currentColor" stroke-width="5" stroke-linecap="round" stroke-dasharray="10 10"/>
2650
+ <path d="M144.305 35.2579L120.095 6.56679" stroke="currentColor" stroke-width="5" stroke-linecap="round" stroke-dasharray="10 10"/>
2651
+ <path d="M120 6.50037L64 6.50037" stroke="currentColor" stroke-width="5" stroke-linecap="round" stroke-dasharray="10 10"/>
2652
+ <path d="M87.1957 36.1024L59 2.50008" stroke="currentColor" stroke-width="5" stroke-linecap="round" stroke-dasharray="10 10"/>
2653
+ <path d="M59 2.50037L3 2.50037" stroke="currentColor" stroke-width="5" stroke-linecap="round" stroke-dasharray="10 10"/>
2654
+ <path d="M2.5 38.5001L2.5 3.00012" stroke="currentColor" stroke-width="5" stroke-linecap="round" stroke-dasharray="10 10"/>
2655
+ <path d="M185 12.5001L247 12.5001" stroke="currentColor" stroke-width="5" stroke-linecap="round" stroke-dasharray="10 10"/>
2656
+ </svg>`;
2466
2657
  }
2467
2658
 
2468
2659
  class CustomViewsWidget {
@@ -2584,15 +2775,22 @@ ${TAB_STYLES}
2584
2775
  const tabGroups = this.core.getTabGroups();
2585
2776
  let tabGroupControlsHTML = '';
2586
2777
  if (this.options.showTabGroups && tabGroups && tabGroups.length > 0) {
2778
+ // Determine initial nav visibility state
2779
+ const initialNavsVisible = TabManager.areNavsVisible(document.body);
2587
2780
  tabGroupControlsHTML = `
2588
2781
  <div class="cv-tabgroup-card cv-tabgroup-header">
2589
2782
  <div class="cv-tabgroup-row">
2783
+ <div class="cv-tabgroup-logo-box" id="cv-nav-icon-box">
2784
+ <div class="cv-nav-icon" id="cv-nav-icon">${initialNavsVisible ? getNavHeadingOnIcon() : getNavHeadingOffIcon()}</div>
2785
+ </div>
2590
2786
  <div class="cv-tabgroup-info">
2591
- <p class="cv-tabgroup-title">Navigation Headers</p>
2787
+ <div class="cv-tabgroup-title-container">
2788
+ <p class="cv-tabgroup-title">Navigation Headers</p>
2789
+ </div>
2592
2790
  <p class="cv-tabgroup-description">Show or hide navigation headers</p>
2593
2791
  </div>
2594
2792
  <label class="cv-toggle-switch cv-nav-toggle">
2595
- <input class="cv-nav-pref-input" type="checkbox" aria-label="Show or hide navigation headers" />
2793
+ <input class="cv-nav-pref-input" type="checkbox" ${initialNavsVisible ? 'checked' : ''} aria-label="Show or hide navigation headers" />
2596
2794
  <span class="cv-switch-bg"></span>
2597
2795
  <span class="cv-switch-knob"></span>
2598
2796
  </label>
@@ -2650,13 +2848,13 @@ ${TAB_STYLES}
2650
2848
  <footer class="cv-modal-footer">
2651
2849
  ${this.options.showReset ? `
2652
2850
  <button class="cv-reset-btn">
2653
- ${getResetIcon()}
2851
+ <span class="cv-reset-btn-icon">${getResetIcon()}</span>
2654
2852
  <span>Reset to Default</span>
2655
2853
  </button>
2656
2854
  ` : ''}
2657
2855
  <button class="cv-share-btn">
2658
- ${getShareIcon()}
2659
2856
  <span>Copy Shareable URL</span>
2857
+ <span class="cv-share-btn-icon">${getCopyIcon()}</span>
2660
2858
  </button>
2661
2859
  </footer>
2662
2860
  </div>
@@ -2684,14 +2882,35 @@ ${TAB_STYLES}
2684
2882
  if (copyUrlBtn) {
2685
2883
  copyUrlBtn.addEventListener('click', () => {
2686
2884
  this.copyShareableURL();
2885
+ // Visual feedback: change icon to tick for 3 seconds
2886
+ const iconContainer = copyUrlBtn.querySelector('.cv-share-btn-icon');
2887
+ if (iconContainer) {
2888
+ const originalIcon = iconContainer.innerHTML;
2889
+ iconContainer.innerHTML = getTickIcon();
2890
+ // Revert after 3 seconds
2891
+ setTimeout(() => {
2892
+ iconContainer.innerHTML = originalIcon;
2893
+ }, 3000);
2894
+ }
2687
2895
  });
2688
2896
  }
2689
2897
  // Reset to default button
2690
2898
  const resetBtn = this.modal.querySelector('.cv-reset-btn');
2691
2899
  if (resetBtn) {
2692
2900
  resetBtn.addEventListener('click', () => {
2901
+ // Add spinning animation to icon
2902
+ const resetIcon = resetBtn.querySelector('.cv-reset-btn-icon');
2903
+ if (resetIcon) {
2904
+ resetIcon.classList.add('cv-spinning');
2905
+ }
2693
2906
  this.core.resetToDefault();
2694
2907
  this.loadCurrentStateIntoForm();
2908
+ // Remove spinning animation after it completes
2909
+ setTimeout(() => {
2910
+ if (resetIcon) {
2911
+ resetIcon.classList.remove('cv-spinning');
2912
+ }
2913
+ }, 600); // 600ms matches the animation duration
2695
2914
  });
2696
2915
  }
2697
2916
  // Listen to toggle switches
@@ -2725,14 +2944,34 @@ ${TAB_STYLES}
2725
2944
  });
2726
2945
  // Listener for show/hide tab navs
2727
2946
  const tabNavToggle = this.modal.querySelector('.cv-nav-pref-input');
2728
- if (tabNavToggle) {
2947
+ const navIcon = this.modal?.querySelector('#cv-nav-icon');
2948
+ const navHeaderCard = this.modal?.querySelector('.cv-tabgroup-card.cv-tabgroup-header');
2949
+ if (tabNavToggle && navIcon && navHeaderCard) {
2950
+ // Helper to update icon based on state
2951
+ const updateIcon = (isVisible, isHovering = false) => {
2952
+ if (isHovering) {
2953
+ // On hover, show the transition icon
2954
+ navIcon.innerHTML = getNavDashed();
2955
+ }
2956
+ else {
2957
+ // Normal state, show the status icon (on if visible, off if hidden)
2958
+ navIcon.innerHTML = isVisible ? getNavHeadingOnIcon() : getNavHeadingOffIcon();
2959
+ }
2960
+ };
2961
+ // Add hover listeners to entire header card
2962
+ navHeaderCard.addEventListener('mouseenter', () => {
2963
+ updateIcon(tabNavToggle.checked, true);
2964
+ });
2965
+ navHeaderCard.addEventListener('mouseleave', () => {
2966
+ updateIcon(tabNavToggle.checked, false);
2967
+ });
2968
+ // Add change listener
2729
2969
  tabNavToggle.addEventListener('change', () => {
2730
2970
  const visible = tabNavToggle.checked;
2731
- // Persist preference
2732
- try {
2733
- localStorage.setItem('cv-tab-navs-visible', visible ? 'true' : 'false');
2734
- }
2735
- catch (e) { /* ignore */ }
2971
+ // Update the icon based on new state (not hovering)
2972
+ updateIcon(visible, false);
2973
+ // Persist preference via core
2974
+ this.core.persistTabNavVisibility(visible);
2736
2975
  // Apply to DOM using TabManager via core
2737
2976
  try {
2738
2977
  const rootEl = document.body;
@@ -2844,21 +3083,22 @@ ${TAB_STYLES}
2844
3083
  });
2845
3084
  // Load tab nav visibility preference
2846
3085
  const navPref = (() => {
2847
- try {
2848
- const raw = localStorage.getItem('cv-tab-navs-visible');
2849
- if (raw === null)
2850
- return TabManager.areNavsVisible(document.body);
2851
- return raw === 'true';
2852
- }
2853
- catch (e) {
2854
- return TabManager.areNavsVisible(document.body);
3086
+ const persisted = this.core.getPersistedTabNavVisibility();
3087
+ if (persisted !== null) {
3088
+ return persisted;
2855
3089
  }
3090
+ return TabManager.areNavsVisible(document.body);
2856
3091
  })();
2857
3092
  const tabNavToggle = this.modal.querySelector('.cv-nav-pref-input');
3093
+ const navIcon = this.modal?.querySelector('#cv-nav-icon');
2858
3094
  if (tabNavToggle) {
2859
3095
  tabNavToggle.checked = navPref;
2860
3096
  // Ensure UI matches actual visibility
2861
3097
  TabManager.setNavsVisibility(document.body, navPref);
3098
+ // Update the nav icon to reflect the current state
3099
+ if (navIcon) {
3100
+ navIcon.innerHTML = navPref ? getNavHeadingOnIcon() : getNavHeadingOffIcon();
3101
+ }
2862
3102
  }
2863
3103
  }
2864
3104
  /**
@@ -2875,12 +3115,19 @@ ${TAB_STYLES}
2875
3115
  // Check if welcome has been shown before
2876
3116
  const hasSeenWelcome = localStorage.getItem(STORAGE_KEY);
2877
3117
  if (!hasSeenWelcome) {
2878
- // Show welcome modal after a short delay to let the page settle
2879
- setTimeout(() => {
2880
- this.createWelcomeModal();
2881
- }, 500);
2882
- // Mark as shown
2883
- localStorage.setItem(STORAGE_KEY, 'true');
3118
+ // Check if this page has any custom views elements
3119
+ const hasCustomViewElements = document.querySelector('cv-tabgroup') !== null ||
3120
+ document.querySelector('cv-tab') !== null ||
3121
+ document.querySelector('cv-toggle') !== null ||
3122
+ document.querySelector('[data-cv-toggle]') !== null;
3123
+ if (hasCustomViewElements) {
3124
+ // Show welcome modal after a short delay to let the page settle
3125
+ setTimeout(() => {
3126
+ this.createWelcomeModal();
3127
+ }, 500);
3128
+ // Mark as shown
3129
+ localStorage.setItem(STORAGE_KEY, 'true');
3130
+ }
2884
3131
  }
2885
3132
  }
2886
3133
  /**
@@ -2979,17 +3226,20 @@ ${TAB_STYLES}
2979
3226
  }
2980
3227
  window.__customViewsInitInProgress = true;
2981
3228
  try {
2982
- // Find the script tag
3229
+ // Find the script tag that loaded this script
2983
3230
  let scriptTag = document.currentScript;
2984
- // Fallback if currentScript is not available (executed after page load)
2985
- if (!scriptTag) {
2986
- // Match the actual CustomViews bundle files (e.g., custom-views.min.js, custom-views.js)
2987
- for (const script of document.scripts) {
2988
- const src = script.src || '';
2989
- // Match filenames like: custom-views.min.js, custom-views.js, custom-views.esm.js
2990
- if (/custom-views(?:\.min)?\.(?:esm\.)?js($|\?)/i.test(src)) {
2991
- scriptTag = script;
2992
- break;
3231
+ if (!scriptTag || !scriptTag.hasAttribute('data-base-url')) {
3232
+ const dataAttrMatch = document.querySelector('script[data-base-url]');
3233
+ if (dataAttrMatch) {
3234
+ scriptTag = dataAttrMatch;
3235
+ }
3236
+ else {
3237
+ for (const script of document.scripts) {
3238
+ const src = script.src || '';
3239
+ if (/(?:custom[-_]views|@customviews-js\/customviews)(?:\.min)?\.(?:esm\.)?js($|\?)/i.test(src)) {
3240
+ scriptTag = script;
3241
+ break;
3242
+ }
2993
3243
  }
2994
3244
  }
2995
3245
  }