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