ultimate-jekyll-manager 0.0.187 → 0.0.189

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.
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Appearance Module
3
+ * Handles theme appearance switching (dark, light, system)
4
+ */
5
+
6
+ // Constants
7
+ const STORAGE_KEY = 'appearance.preference';
8
+ const VALID_VALUES = ['dark', 'light', 'system'];
9
+
10
+ // Module state
11
+ let webManager = null;
12
+ let mediaQuery = null;
13
+
14
+ // Module
15
+ export default (Manager) => {
16
+ // Shortcuts
17
+ webManager = Manager.webManager;
18
+
19
+ // Create appearance API
20
+ const appearanceAPI = {
21
+ /**
22
+ * Get the current saved preference
23
+ * @returns {string|null} 'dark', 'light', 'system', or null if not set
24
+ */
25
+ get: () => webManager.storage().get(STORAGE_KEY) || null,
26
+
27
+ /**
28
+ * Get the resolved (actual) theme being displayed
29
+ * @returns {string} 'dark' or 'light'
30
+ */
31
+ getResolved: () => document.documentElement.getAttribute('data-bs-theme') || 'dark',
32
+
33
+ /**
34
+ * Set the appearance preference
35
+ * @param {string} value - 'dark', 'light', or 'system'
36
+ */
37
+ set: (value) => {
38
+ // Validate
39
+ if (!VALID_VALUES.includes(value)) {
40
+ console.warn(`Invalid appearance value: ${value}. Must be one of: ${VALID_VALUES.join(', ')}`);
41
+ return;
42
+ }
43
+
44
+ // Save preference
45
+ webManager.storage().set(STORAGE_KEY, value);
46
+
47
+ // Apply theme
48
+ applyTheme(value);
49
+
50
+ // Update UI elements
51
+ updateUI(value);
52
+
53
+ // Setup or teardown system preference listener
54
+ setupSystemListener(value === 'system');
55
+ },
56
+
57
+ /**
58
+ * Toggle between dark and light (skips system)
59
+ */
60
+ toggle() {
61
+ const current = this.getResolved();
62
+ const next = current === 'dark' ? 'light' : 'dark';
63
+ this.set(next);
64
+ },
65
+
66
+ /**
67
+ * Cycle through all three modes: dark → light → system → dark
68
+ */
69
+ cycle() {
70
+ const current = this.get() || this.getResolved();
71
+ const order = ['dark', 'light', 'system'];
72
+ const currentIndex = order.indexOf(current);
73
+ const nextIndex = (currentIndex + 1) % order.length;
74
+ this.set(order[nextIndex]);
75
+ },
76
+
77
+ /**
78
+ * Clear saved preference (revert to site default)
79
+ */
80
+ clear: () => {
81
+ webManager.storage().remove(STORAGE_KEY);
82
+ updateUI(null);
83
+ setupSystemListener(false);
84
+ }
85
+ };
86
+
87
+ // Register on UJ library
88
+ webManager._ujLibrary.appearance = appearanceAPI;
89
+
90
+ // Initialize UI event listeners
91
+ initializeUI();
92
+
93
+ // Setup system listener if current preference is 'system'
94
+ const currentPreference = appearanceAPI.get();
95
+ if (currentPreference === 'system') {
96
+ setupSystemListener(true);
97
+ }
98
+
99
+ // Update UI with current state
100
+ updateUI(currentPreference);
101
+
102
+ console.log('Appearance module loaded');
103
+ };
104
+
105
+ /**
106
+ * Apply theme to the document
107
+ * @param {string} preference - 'dark', 'light', or 'system'
108
+ */
109
+ const applyTheme = (preference) => {
110
+ let theme = preference;
111
+
112
+ // Resolve system preference
113
+ if (preference === 'system') {
114
+ theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
115
+ }
116
+
117
+ // Apply to document
118
+ document.documentElement.setAttribute('data-bs-theme', theme);
119
+ };
120
+
121
+ /**
122
+ * Setup or teardown the system preference change listener
123
+ * @param {boolean} enable - Whether to enable the listener
124
+ */
125
+ const setupSystemListener = (enable) => {
126
+ // Clean up existing listener
127
+ if (mediaQuery) {
128
+ mediaQuery.removeEventListener('change', handleSystemChange);
129
+ mediaQuery = null;
130
+ }
131
+
132
+ // Setup new listener if needed
133
+ if (enable) {
134
+ mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
135
+ mediaQuery.addEventListener('change', handleSystemChange);
136
+ }
137
+ };
138
+
139
+ /**
140
+ * Handle system preference change
141
+ * @param {MediaQueryListEvent} event
142
+ */
143
+ const handleSystemChange = (event) => {
144
+ const theme = event.matches ? 'dark' : 'light';
145
+ document.documentElement.setAttribute('data-bs-theme', theme);
146
+ updateUI('system');
147
+ };
148
+
149
+ /**
150
+ * Initialize UI event listeners
151
+ */
152
+ const initializeUI = () => {
153
+ // Use event delegation for appearance controls
154
+ document.addEventListener('click', (event) => {
155
+ const $target = event.target.closest('[data-appearance-set]');
156
+
157
+ if (!$target) {
158
+ return;
159
+ }
160
+
161
+ event.preventDefault();
162
+
163
+ const value = $target.getAttribute('data-appearance-set');
164
+ webManager.uj().appearance.set(value);
165
+ });
166
+ };
167
+
168
+ /**
169
+ * Update UI elements to reflect current state
170
+ * @param {string|null} preference - Current preference
171
+ */
172
+ const updateUI = (preference) => {
173
+ const resolved = document.documentElement.getAttribute('data-bs-theme');
174
+ const displayValue = preference || resolved;
175
+
176
+ // Update [data-appearance-current] elements with the preference
177
+ document.querySelectorAll('[data-appearance-current]').forEach(($el) => {
178
+ const format = $el.getAttribute('data-appearance-current') || 'preference';
179
+ $el.textContent = format === 'resolved' ? resolved : displayValue;
180
+ });
181
+
182
+ // Update [data-appearance-icon] elements - show/hide based on current mode
183
+ // Icons should have data-appearance-icon="light|dark|system" attribute
184
+ document.querySelectorAll('[data-appearance-icon]').forEach(($el) => {
185
+ const iconMode = $el.getAttribute('data-appearance-icon');
186
+ $el.hidden = iconMode !== displayValue;
187
+ });
188
+
189
+ // Update active state on [data-appearance-set] elements
190
+ document.querySelectorAll('[data-appearance-set]').forEach(($el) => {
191
+ const value = $el.getAttribute('data-appearance-set');
192
+ const isActive = value === preference || (!preference && value === resolved);
193
+
194
+ $el.classList.toggle('active', isActive);
195
+ $el.setAttribute('aria-pressed', isActive ? 'true' : 'false');
196
+ });
197
+ };
@@ -151,8 +151,8 @@ const createCustomAd = ($vertUnit, config) => {
151
151
  const iframeId = `vert-${window.__ujVertIdCounter = (window.__ujVertIdCounter || 0) + 1}`;
152
152
 
153
153
  // Build base URL for the ad content
154
- // Use local server if debug=true OR if we're in development mode
155
- const baseURL = (qsDebug || webManager.isDevelopment())
154
+ // Use local server if debug=true OR if we're in development mode AND on promo-server
155
+ const baseURL = (qsDebug || (webManager.isDevelopment() && webManager.config.brand.id === 'promo-server'))
156
156
  ? `${window.location.protocol}//${window.location.host}/verts/main`
157
157
  : 'https://promo-server.itwcreativeworks.com/verts/main';
158
158
 
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Appearance Test Page JavaScript
3
+ */
4
+
5
+ // Libraries
6
+ let webManager = null;
7
+
8
+ // Module
9
+ export default (Manager) => {
10
+ return new Promise(async function (resolve) {
11
+ // Shortcuts
12
+ webManager = Manager.webManager;
13
+
14
+ // Initialize when DOM is ready
15
+ await webManager.dom().ready();
16
+
17
+ // Initialize debug panel
18
+ initDebugPanel();
19
+
20
+ // Initialize programmatic controls
21
+ initControls();
22
+
23
+ // Initialize event logging
24
+ initEventLog();
25
+
26
+ // Resolve after initialization
27
+ return resolve();
28
+ });
29
+ };
30
+
31
+ /**
32
+ * Initialize debug panel with live values
33
+ */
34
+ function initDebugPanel() {
35
+ const $debugSaved = document.getElementById('debug-saved');
36
+ const $debugResolved = document.getElementById('debug-resolved');
37
+ const $debugAttr = document.getElementById('debug-attr');
38
+ const $debugLocalStorage = document.getElementById('debug-localstorage');
39
+ const $debugSystem = document.getElementById('debug-system');
40
+ const $btnRefresh = document.getElementById('btn-refresh');
41
+
42
+ // Update function
43
+ function updateDebug() {
44
+ const appearance = webManager.uj().appearance;
45
+
46
+ // Saved preference via API
47
+ const saved = appearance.get();
48
+ $debugSaved.textContent = saved !== null ? `"${saved}"` : 'null (not set)';
49
+
50
+ // Resolved theme via API
51
+ const resolved = appearance.getResolved();
52
+ $debugResolved.textContent = `"${resolved}"`;
53
+
54
+ // Raw HTML attribute
55
+ const attr = document.documentElement.getAttribute('data-bs-theme');
56
+ $debugAttr.textContent = `"${attr}"`;
57
+
58
+ // Raw localStorage value (stored under _manager.appearance.preference)
59
+ let rawStorage = null;
60
+ try {
61
+ const managerData = localStorage.getItem('_manager');
62
+ const parsedData = managerData ? JSON.parse(managerData) : null;
63
+ rawStorage = parsedData?.appearance?.preference || null;
64
+ } catch (e) {
65
+ rawStorage = '(error reading)';
66
+ }
67
+ $debugLocalStorage.textContent = rawStorage !== null ? `"${rawStorage}"` : 'null';
68
+
69
+ // System preference
70
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
71
+ const systemPref = prefersDark ? 'dark' : 'light';
72
+ $debugSystem.textContent = `"${systemPref}" (prefers-color-scheme: ${prefersDark ? 'dark' : 'light'})`;
73
+
74
+ console.log('[Appearance Test] Debug updated:', {
75
+ saved,
76
+ resolved,
77
+ attr,
78
+ rawStorage,
79
+ systemPref,
80
+ });
81
+ }
82
+
83
+ // Initial update
84
+ updateDebug();
85
+
86
+ // Refresh button
87
+ $btnRefresh.addEventListener('click', updateDebug);
88
+
89
+ // Auto-update when theme changes (observe attribute changes)
90
+ const observer = new MutationObserver((mutations) => {
91
+ mutations.forEach((mutation) => {
92
+ if (mutation.attributeName === 'data-bs-theme') {
93
+ updateDebug();
94
+ }
95
+ });
96
+ });
97
+ observer.observe(document.documentElement, { attributes: true });
98
+
99
+ // Also listen for system preference changes
100
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
101
+ mediaQuery.addEventListener('change', updateDebug);
102
+
103
+ // Export for console access
104
+ window.updateAppearanceDebug = updateDebug;
105
+ }
106
+
107
+ /**
108
+ * Initialize programmatic control buttons
109
+ */
110
+ function initControls() {
111
+ const appearance = webManager.uj().appearance;
112
+
113
+ // Toggle button
114
+ document.getElementById('btn-toggle').addEventListener('click', () => {
115
+ console.log('[Appearance Test] Toggle clicked');
116
+ appearance.toggle();
117
+ });
118
+
119
+ // Cycle button
120
+ document.getElementById('btn-cycle').addEventListener('click', () => {
121
+ console.log('[Appearance Test] Cycle clicked');
122
+ appearance.cycle();
123
+ });
124
+
125
+ // Clear button
126
+ document.getElementById('btn-clear').addEventListener('click', () => {
127
+ console.log('[Appearance Test] Clear clicked');
128
+ appearance.clear();
129
+ // Trigger debug update
130
+ window.updateAppearanceDebug?.();
131
+ });
132
+
133
+ // Quick set buttons
134
+ document.getElementById('btn-set-light').addEventListener('click', () => {
135
+ console.log('[Appearance Test] set("light") clicked');
136
+ appearance.set('light');
137
+ });
138
+
139
+ document.getElementById('btn-set-dark').addEventListener('click', () => {
140
+ console.log('[Appearance Test] set("dark") clicked');
141
+ appearance.set('dark');
142
+ });
143
+
144
+ document.getElementById('btn-set-system').addEventListener('click', () => {
145
+ console.log('[Appearance Test] set("system") clicked');
146
+ appearance.set('system');
147
+ });
148
+ }
149
+
150
+ /**
151
+ * Initialize event logging
152
+ */
153
+ function initEventLog() {
154
+ const $log = document.getElementById('event-log');
155
+ const $clearBtn = document.getElementById('btn-clear-log');
156
+ let logLines = [];
157
+
158
+ function addLogEntry(message) {
159
+ const timestamp = new Date().toLocaleTimeString();
160
+ const entry = `[${timestamp}] ${message}`;
161
+ logLines.push(entry);
162
+
163
+ // Keep last 50 entries
164
+ if (logLines.length > 50) {
165
+ logLines = logLines.slice(-50);
166
+ }
167
+
168
+ $log.textContent = logLines.join('\n');
169
+ $log.scrollTop = $log.scrollHeight;
170
+ }
171
+
172
+ // Clear log button
173
+ $clearBtn.addEventListener('click', () => {
174
+ logLines = [];
175
+ $log.textContent = 'Log cleared...';
176
+ });
177
+
178
+ // Observe data-bs-theme changes
179
+ const observer = new MutationObserver((mutations) => {
180
+ mutations.forEach((mutation) => {
181
+ if (mutation.attributeName === 'data-bs-theme') {
182
+ const oldValue = mutation.oldValue;
183
+ const newValue = document.documentElement.getAttribute('data-bs-theme');
184
+ addLogEntry(`Theme changed: "${oldValue}" -> "${newValue}"`);
185
+ }
186
+ });
187
+ });
188
+ observer.observe(document.documentElement, {
189
+ attributes: true,
190
+ attributeOldValue: true,
191
+ attributeFilter: ['data-bs-theme'],
192
+ });
193
+
194
+ // Log system preference changes
195
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
196
+ mediaQuery.addEventListener('change', (event) => {
197
+ const systemPref = event.matches ? 'dark' : 'light';
198
+ addLogEntry(`System preference changed: "${systemPref}"`);
199
+ });
200
+
201
+ // Log click events on appearance controls
202
+ document.addEventListener('click', (event) => {
203
+ const $target = event.target.closest('[data-appearance-set]');
204
+ if ($target) {
205
+ const value = $target.getAttribute('data-appearance-set');
206
+ addLogEntry(`User clicked: set("${value}")`);
207
+ }
208
+ });
209
+
210
+ // Initial log entry
211
+ addLogEntry('Appearance test page loaded');
212
+ addLogEntry(`Initial theme: "${document.documentElement.getAttribute('data-bs-theme')}"`);
213
+ addLogEntry(`Saved preference: ${webManager.uj().appearance.get() || '(none)'}`);
214
+ }
@@ -4,6 +4,7 @@ import authModule from '__main_assets__/js/core/auth.js';
4
4
  import lazyLoadingModule from '__main_assets__/js/core/lazy-loading.js';
5
5
  import queryStringsModule from '__main_assets__/js/core/query-strings.js';
6
6
  import serviceWorkerModule from '__main_assets__/js/core/service-worker.js';
7
+ import appearanceModule from '__main_assets__/js/core/appearance.js';
7
8
  import completeModule from '__main_assets__/js/core/complete.js';
8
9
 
9
10
  // Ultimate Jekyll Manager Module
@@ -32,6 +33,7 @@ export default async function (Manager, options) {
32
33
  lazyLoadingModule(Manager, options);
33
34
  queryStringsModule(Manager, options);
34
35
  serviceWorkerModule(Manager, options);
36
+ appearanceModule(Manager, options);
35
37
 
36
38
  // Conditionally loaded modules based on config (keep as dynamic imports)
37
39
  const conditionalModules = [
@@ -38,18 +38,54 @@
38
38
  <!-- Prerendered Icons -->
39
39
  {%- assign icons = page.resolved.prerender_icons | default: empty -%}
40
40
  {%- iftruthy icons -%}
41
- <!-- Pre-rendered Icon Templates -->
42
- <div id="prerendered-icons" class="d-none" aria-hidden="true">
43
- {%- for icon in icons -%}
44
- {%- assign icon_name = icon.name | default: icon -%}
45
- {%- assign icon_class = icon.class | default: "fa-3xl" -%}
46
- <div data-icon="{{ icon_name }}" data-class="{{ icon_class }}">
47
- {% uj_icon icon_name, icon_class %}
48
- </div>
49
- {%- endfor -%}
50
- </div>
41
+ <!-- Pre-rendered Icon Templates -->
42
+ <div id="prerendered-icons" class="d-none" aria-hidden="true">
43
+ {%- for icon in icons -%}
44
+ {%- assign icon_name = icon.name | default: icon -%}
45
+ {%- assign icon_class = icon.class | default: "fa-3xl" -%}
46
+ <div data-icon="{{ icon_name }}" data-class="{{ icon_class }}">
47
+ {% uj_icon icon_name, icon_class %}
48
+ </div>
49
+ {%- endfor -%}
50
+ </div>
51
51
  {%- endiftruthy -%}
52
52
 
53
+ <!-- Script - Set preferred appearance -->
54
+ <!-- This runs immediately to prevent flash of wrong theme -->
55
+ <!-- Priority: 1) User saved preference, 2) Site preset, 3) System preference (if preset is 'system') -->
56
+ <script type="text/javascript">
57
+ (function() {
58
+ 'use strict';
59
+ var $html = document.documentElement;
60
+
61
+ // Get the preset value from Jekyll (set at build time)
62
+ var preset = $html.getAttribute('data-bs-theme');
63
+
64
+ // Try to read user's saved preference from _manager storage
65
+ // (same format as webManager.storage() uses)
66
+ var saved = null;
67
+ try {
68
+ var managerData = localStorage.getItem('_manager');
69
+ var parsedData = managerData ? JSON.parse(managerData) : null;
70
+ saved = parsedData && parsedData.appearance && parsedData.appearance.preference;
71
+ } catch (e) {}
72
+
73
+ // Use saved preference if available, otherwise fall back to preset
74
+ var preference = saved || preset;
75
+ var theme = preference;
76
+
77
+ // If preference is 'system', resolve to actual light/dark based on OS setting
78
+ if (preference === 'system') {
79
+ theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
80
+ }
81
+
82
+ // Only update if different from preset (avoid unnecessary DOM write)
83
+ if (theme && theme !== preset) {
84
+ $html.setAttribute('data-bs-theme', theme);
85
+ }
86
+ })();
87
+ </script>
88
+
53
89
  <!-- Script to prevent clicks on disabled elements during page load -->
54
90
  <script type="text/javascript">
55
91
  (function() {
@@ -0,0 +1,271 @@
1
+ ---
2
+ ### ALL PAGES ###
3
+ layout: themes/[ site.theme.id ]/frontend/core/base
4
+ permalink: /test/libraries/appearance
5
+
6
+ ### REGULAR PAGES ###
7
+ sitemap:
8
+ include: false
9
+ meta:
10
+ title: "Appearance Test Page"
11
+ description: "Testing the appearance switching system (dark, light, system modes)."
12
+ breadcrumb: "Appearance Test"
13
+ index: false
14
+
15
+ ---
16
+
17
+ <div class="container py-5">
18
+ <h1 class="mb-4">Appearance Test Page</h1>
19
+ <p class="text-muted mb-5">Testing the appearance switching system with detailed debugging information.</p>
20
+
21
+ <div class="row g-4">
22
+
23
+ <!-- Theme Switcher -->
24
+ <div class="col-lg-6">
25
+ <div class="card">
26
+ <div class="card-header">
27
+ <h5 class="mb-0">Theme Switcher</h5>
28
+ <small class="text-muted">Click to change appearance mode</small>
29
+ </div>
30
+ <div class="card-body">
31
+ <!-- Dropdown Switcher -->
32
+ <div class="mb-4">
33
+ <label class="form-label">Dropdown Switcher</label>
34
+ <div class="dropdown">
35
+ <button class="btn btn-outline-adaptive dropdown-toggle w-100" type="button" data-bs-toggle="dropdown" aria-expanded="false">
36
+ <span data-appearance-icon="light" hidden>{% uj_icon "sun", "fa-md me-2" %}</span>
37
+ <span data-appearance-icon="dark" hidden>{% uj_icon "moon-stars", "fa-md me-2" %}</span>
38
+ <span data-appearance-icon="system" hidden>{% uj_icon "circle-half-stroke", "fa-md me-2" %}</span>
39
+ <span data-appearance-current></span>
40
+ </button>
41
+ <ul class="dropdown-menu w-100">
42
+ <li><a class="dropdown-item" href="#" data-appearance-set="light">{% uj_icon "sun", "fa-md me-2" %} Light</a></li>
43
+ <li><a class="dropdown-item" href="#" data-appearance-set="dark">{% uj_icon "moon-stars", "fa-md me-2" %} Dark</a></li>
44
+ <li><a class="dropdown-item" href="#" data-appearance-set="system">{% uj_icon "circle-half-stroke", "fa-md me-2" %} System</a></li>
45
+ </ul>
46
+ </div>
47
+ </div>
48
+
49
+ <!-- Button Group Switcher -->
50
+ <div class="mb-4">
51
+ <label class="form-label">Button Group Switcher</label>
52
+ <div class="btn-group w-100" role="group">
53
+ <button type="button" class="btn btn-outline-adaptive" data-appearance-set="light">
54
+ {% uj_icon "sun", "fa-md me-1" %} Light
55
+ </button>
56
+ <button type="button" class="btn btn-outline-adaptive" data-appearance-set="dark">
57
+ {% uj_icon "moon-stars", "fa-md me-1" %} Dark
58
+ </button>
59
+ <button type="button" class="btn btn-outline-adaptive" data-appearance-set="system">
60
+ {% uj_icon "circle-half-stroke", "fa-md me-1" %} System
61
+ </button>
62
+ </div>
63
+ </div>
64
+
65
+ <!-- Programmatic Controls -->
66
+ <div class="mb-4">
67
+ <label class="form-label">Programmatic Controls</label>
68
+ <div class="d-flex gap-2 flex-wrap">
69
+ <button type="button" class="btn btn-primary btn-sm" id="btn-toggle">
70
+ {% uj_icon "repeat", "fa-md me-1" %} Toggle (dark/light)
71
+ </button>
72
+ <button type="button" class="btn btn-secondary btn-sm" id="btn-cycle">
73
+ {% uj_icon "arrows-spin", "fa-md me-1" %} Cycle (all 3)
74
+ </button>
75
+ <button type="button" class="btn btn-warning btn-sm" id="btn-clear">
76
+ {% uj_icon "trash", "fa-md me-1" %} Clear Preference
77
+ </button>
78
+ </div>
79
+ </div>
80
+
81
+ <!-- Quick Set -->
82
+ <div>
83
+ <label class="form-label">Quick Set via API</label>
84
+ <div class="d-flex gap-2 flex-wrap">
85
+ <button type="button" class="btn btn-outline-warning btn-sm" id="btn-set-light">set('light')</button>
86
+ <button type="button" class="btn btn-outline-info btn-sm" id="btn-set-dark">set('dark')</button>
87
+ <button type="button" class="btn btn-outline-success btn-sm" id="btn-set-system">set('system')</button>
88
+ </div>
89
+ </div>
90
+ </div>
91
+ </div>
92
+ </div>
93
+
94
+ <!-- Debug Panel -->
95
+ <div class="col-lg-6">
96
+ <div class="card">
97
+ <div class="card-header d-flex justify-content-between align-items-center">
98
+ <div>
99
+ <h5 class="mb-0">Debug Panel</h5>
100
+ <small class="text-muted">Live values (updates automatically)</small>
101
+ </div>
102
+ <button type="button" class="btn btn-outline-secondary btn-sm" id="btn-refresh">
103
+ {% uj_icon "arrows-rotate", "fa-md" %}
104
+ </button>
105
+ </div>
106
+ <div class="card-body">
107
+ <table class="table table-sm mb-0">
108
+ <tbody>
109
+ <tr>
110
+ <th scope="row" class="text-muted" style="width: 50%;">Saved Preference</th>
111
+ <td><code id="debug-saved">-</code></td>
112
+ </tr>
113
+ <tr>
114
+ <th scope="row" class="text-muted">Resolved Theme</th>
115
+ <td><code id="debug-resolved">-</code></td>
116
+ </tr>
117
+ <tr>
118
+ <th scope="row" class="text-muted">data-bs-theme attr</th>
119
+ <td><code id="debug-attr">-</code></td>
120
+ </tr>
121
+ <tr>
122
+ <th scope="row" class="text-muted">localStorage raw</th>
123
+ <td><code id="debug-localstorage">-</code></td>
124
+ </tr>
125
+ <tr>
126
+ <th scope="row" class="text-muted">System Preference</th>
127
+ <td><code id="debug-system">-</code></td>
128
+ </tr>
129
+ <tr>
130
+ <th scope="row" class="text-muted">Site Preset (Jekyll)</th>
131
+ <td><code id="debug-preset">{{ page.resolved.theme.appearance }}</code></td>
132
+ </tr>
133
+ </tbody>
134
+ </table>
135
+ </div>
136
+ </div>
137
+
138
+ <!-- Event Log -->
139
+ <div class="card mt-4">
140
+ <div class="card-header d-flex justify-content-between align-items-center">
141
+ <div>
142
+ <h5 class="mb-0">Event Log</h5>
143
+ <small class="text-muted">Theme change events</small>
144
+ </div>
145
+ <button type="button" class="btn btn-outline-secondary btn-sm" id="btn-clear-log">
146
+ {% uj_icon "trash", "fa-md" %}
147
+ </button>
148
+ </div>
149
+ <div class="card-body p-0">
150
+ <pre id="event-log" class="mb-0 p-3 small" style="max-height: 200px; overflow: auto; background: var(--bs-tertiary-bg);">Waiting for events...</pre>
151
+ </div>
152
+ </div>
153
+ </div>
154
+
155
+ <!-- Visual Test -->
156
+ <div class="col-12">
157
+ <div class="card">
158
+ <div class="card-header">
159
+ <h5 class="mb-0">Visual Theme Test</h5>
160
+ <small class="text-muted">These elements should adapt to the current theme</small>
161
+ </div>
162
+ <div class="card-body">
163
+ <div class="row g-3">
164
+ <!-- Background colors -->
165
+ <div class="col-md-4">
166
+ <h6>Backgrounds</h6>
167
+ <div class="p-2 mb-2 bg-body border rounded">bg-body</div>
168
+ <div class="p-2 mb-2 bg-body-secondary border rounded">bg-body-secondary</div>
169
+ <div class="p-2 mb-2 bg-body-tertiary border rounded">bg-body-tertiary</div>
170
+ </div>
171
+
172
+ <!-- Text colors -->
173
+ <div class="col-md-4">
174
+ <h6>Text</h6>
175
+ <p class="text-body mb-2">text-body (primary text)</p>
176
+ <p class="text-body-secondary mb-2">text-body-secondary</p>
177
+ <p class="text-body-tertiary mb-2">text-body-tertiary</p>
178
+ <p class="text-muted mb-2">text-muted</p>
179
+ </div>
180
+
181
+ <!-- Buttons -->
182
+ <div class="col-md-4">
183
+ <h6>Adaptive Buttons</h6>
184
+ <div class="d-flex gap-2 flex-wrap">
185
+ <button class="btn btn-adaptive">btn-adaptive</button>
186
+ <button class="btn btn-outline-adaptive">btn-outline-adaptive</button>
187
+ </div>
188
+ </div>
189
+
190
+ <!-- Alerts -->
191
+ <div class="col-md-6">
192
+ <h6>Alerts</h6>
193
+ <div class="alert alert-primary py-2 mb-2">Primary alert</div>
194
+ <div class="alert alert-secondary py-2 mb-2">Secondary alert</div>
195
+ <div class="alert alert-success py-2 mb-0">Success alert</div>
196
+ </div>
197
+
198
+ <!-- Form -->
199
+ <div class="col-md-6">
200
+ <h6>Form Elements</h6>
201
+ <input type="text" class="form-control mb-2" placeholder="Text input">
202
+ <select class="form-select mb-2">
203
+ <option>Select option</option>
204
+ </select>
205
+ <div class="form-check">
206
+ <input class="form-check-input" type="checkbox" id="test-check" checked>
207
+ <label class="form-check-label" for="test-check">Checkbox</label>
208
+ </div>
209
+ </div>
210
+ </div>
211
+ </div>
212
+ </div>
213
+ </div>
214
+
215
+ <!-- API Reference -->
216
+ <div class="col-12">
217
+ <div class="card">
218
+ <div class="card-header">
219
+ <h5 class="mb-0">API Reference</h5>
220
+ <small class="text-muted">webManager.uj().appearance methods</small>
221
+ </div>
222
+ <div class="card-body">
223
+ <div class="table-responsive">
224
+ <table class="table table-sm mb-0">
225
+ <thead>
226
+ <tr>
227
+ <th>Method</th>
228
+ <th>Returns</th>
229
+ <th>Description</th>
230
+ </tr>
231
+ </thead>
232
+ <tbody>
233
+ <tr>
234
+ <td><code>.get()</code></td>
235
+ <td><code>'dark' | 'light' | 'system' | null</code></td>
236
+ <td>Get saved preference (null if not set)</td>
237
+ </tr>
238
+ <tr>
239
+ <td><code>.getResolved()</code></td>
240
+ <td><code>'dark' | 'light'</code></td>
241
+ <td>Get actual displayed theme</td>
242
+ </tr>
243
+ <tr>
244
+ <td><code>.set(value)</code></td>
245
+ <td><code>void</code></td>
246
+ <td>Save and apply preference ('dark', 'light', or 'system')</td>
247
+ </tr>
248
+ <tr>
249
+ <td><code>.toggle()</code></td>
250
+ <td><code>void</code></td>
251
+ <td>Toggle between dark and light</td>
252
+ </tr>
253
+ <tr>
254
+ <td><code>.cycle()</code></td>
255
+ <td><code>void</code></td>
256
+ <td>Cycle: dark -> light -> system -> dark</td>
257
+ </tr>
258
+ <tr>
259
+ <td><code>.clear()</code></td>
260
+ <td><code>void</code></td>
261
+ <td>Clear saved preference (revert to site default)</td>
262
+ </tr>
263
+ </tbody>
264
+ </table>
265
+ </div>
266
+ </div>
267
+ </div>
268
+ </div>
269
+
270
+ </div>
271
+ </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate-jekyll-manager",
3
- "version": "0.0.187",
3
+ "version": "0.0.189",
4
4
  "description": "Ultimate Jekyll dependency manager",
5
5
  "main": "dist/index.js",
6
6
  "exports": {