@zolomedia/bifrost-client 1.7.74

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.
Files changed (140) hide show
  1. package/L1_Foundation/L1_Foundation.js +13 -0
  2. package/L1_Foundation/bootstrap/bootstrap.js +11 -0
  3. package/L1_Foundation/bootstrap/bootstrap_hooks.js +123 -0
  4. package/L1_Foundation/bootstrap/bootstrap_index.js +15 -0
  5. package/L1_Foundation/bootstrap/bootstrap_logger.js +135 -0
  6. package/L1_Foundation/bootstrap/cdn_loader.js +217 -0
  7. package/L1_Foundation/bootstrap/module_registry.js +102 -0
  8. package/L1_Foundation/bootstrap/prism_loader.js +164 -0
  9. package/L1_Foundation/config/client_config.js +110 -0
  10. package/L1_Foundation/config/config.js +7 -0
  11. package/L1_Foundation/connection/connection.js +8 -0
  12. package/L1_Foundation/connection/websocket_connection.js +122 -0
  13. package/L1_Foundation/constants/bifrost_constants.js +284 -0
  14. package/L1_Foundation/constants/constants.js +7 -0
  15. package/L1_Foundation/logger/logger.js +10 -0
  16. package/L2_Handling/L2_Handling.js +15 -0
  17. package/L2_Handling/cache/cache.js +22 -0
  18. package/L2_Handling/cache/cache_constants.js +69 -0
  19. package/L2_Handling/cache/orchestration/cache_manager.js +299 -0
  20. package/L2_Handling/cache/orchestration/cache_orchestrator.js +260 -0
  21. package/L2_Handling/cache/orchestration/orchestration.js +12 -0
  22. package/L2_Handling/cache/storage/session_manager.js +289 -0
  23. package/L2_Handling/cache/storage/storage.js +10 -0
  24. package/L2_Handling/cache/storage/storage_manager.js +590 -0
  25. package/L2_Handling/display/composite/composite.js +13 -0
  26. package/L2_Handling/display/composite/dashboard_renderer.js +221 -0
  27. package/L2_Handling/display/composite/swiper_renderer.js +564 -0
  28. package/L2_Handling/display/composite/terminal_renderer.js +922 -0
  29. package/L2_Handling/display/composite/wizard_conditional_renderer.js +274 -0
  30. package/L2_Handling/display/display.js +30 -0
  31. package/L2_Handling/display/feedback/feedback.js +11 -0
  32. package/L2_Handling/display/feedback/progressbar_renderer.js +418 -0
  33. package/L2_Handling/display/feedback/spinner_renderer.js +246 -0
  34. package/L2_Handling/display/inputs/button_renderer.js +634 -0
  35. package/L2_Handling/display/inputs/form_renderer.js +583 -0
  36. package/L2_Handling/display/inputs/input_renderer.js +658 -0
  37. package/L2_Handling/display/inputs/inputs.js +12 -0
  38. package/L2_Handling/display/navigation/menu_renderer.js +206 -0
  39. package/L2_Handling/display/navigation/navigation.js +11 -0
  40. package/L2_Handling/display/navigation/navigation_renderer.js +703 -0
  41. package/L2_Handling/display/orchestration/orchestration.js +11 -0
  42. package/L2_Handling/display/orchestration/renderer.js +430 -0
  43. package/L2_Handling/display/orchestration/zdisplay_orchestrator.js +1759 -0
  44. package/L2_Handling/display/outputs/alert_renderer.js +161 -0
  45. package/L2_Handling/display/outputs/audio_renderer.js +94 -0
  46. package/L2_Handling/display/outputs/card_renderer.js +229 -0
  47. package/L2_Handling/display/outputs/code_renderer.js +66 -0
  48. package/L2_Handling/display/outputs/dl_renderer.js +131 -0
  49. package/L2_Handling/display/outputs/header_renderer.js +162 -0
  50. package/L2_Handling/display/outputs/icon_renderer.js +107 -0
  51. package/L2_Handling/display/outputs/image_renderer.js +145 -0
  52. package/L2_Handling/display/outputs/list_renderer.js +190 -0
  53. package/L2_Handling/display/outputs/outputs.js +19 -0
  54. package/L2_Handling/display/outputs/table_renderer.js +765 -0
  55. package/L2_Handling/display/outputs/text_renderer.js +818 -0
  56. package/L2_Handling/display/outputs/typography_renderer.js +293 -0
  57. package/L2_Handling/display/outputs/video_renderer.js +116 -0
  58. package/L2_Handling/display/primitives/document_structure_primitives.js +319 -0
  59. package/L2_Handling/display/primitives/form_primitives.js +526 -0
  60. package/L2_Handling/display/primitives/generic_containers.js +109 -0
  61. package/L2_Handling/display/primitives/interactive_primitives.js +305 -0
  62. package/L2_Handling/display/primitives/link_primitives.js +552 -0
  63. package/L2_Handling/display/primitives/lists_primitives.js +262 -0
  64. package/L2_Handling/display/primitives/media_primitives.js +383 -0
  65. package/L2_Handling/display/primitives/primitives.js +19 -0
  66. package/L2_Handling/display/primitives/semantic_element_primitive.js +226 -0
  67. package/L2_Handling/display/primitives/table_primitives.js +528 -0
  68. package/L2_Handling/display/primitives/typography_primitives.js +175 -0
  69. package/L2_Handling/display/specialized/input_request_renderer.js +467 -0
  70. package/L2_Handling/display/specialized/specialized.js +10 -0
  71. package/L2_Handling/hooks/hooks.js +9 -0
  72. package/L2_Handling/hooks/menu_integration.js +57 -0
  73. package/L2_Handling/hooks/widget_hook_manager.js +292 -0
  74. package/L2_Handling/message/message.js +8 -0
  75. package/L2_Handling/message/message_handler.js +701 -0
  76. package/L2_Handling/navigation/navigation.js +8 -0
  77. package/L2_Handling/navigation/navigation_manager.js +403 -0
  78. package/L2_Handling/zhooks/features/cache_live.js +287 -0
  79. package/L2_Handling/zhooks/features/crumbs_live.js +292 -0
  80. package/L2_Handling/zhooks/zhooks_manager.js +65 -0
  81. package/L2_Handling/zvaf/zvaf.js +8 -0
  82. package/L2_Handling/zvaf/zvaf_manager.js +334 -0
  83. package/L3_Abstraction/L3_Abstraction.js +12 -0
  84. package/L3_Abstraction/orchestrator/container_unwrapper.js +101 -0
  85. package/L3_Abstraction/orchestrator/group_renderer.js +698 -0
  86. package/L3_Abstraction/orchestrator/input_event_handler.js +797 -0
  87. package/L3_Abstraction/orchestrator/metadata_processor.js +249 -0
  88. package/L3_Abstraction/orchestrator/navbar_builder.js +201 -0
  89. package/L3_Abstraction/orchestrator/orchestrator.js +13 -0
  90. package/L3_Abstraction/orchestrator/wizard_gate_handler.js +360 -0
  91. package/L3_Abstraction/renderer/renderer.js +1 -0
  92. package/L3_Abstraction/session/session.js +1 -0
  93. package/L4_Orchestration/L4_Orchestration.js +11 -0
  94. package/L4_Orchestration/client/client.js +1 -0
  95. package/L4_Orchestration/facade/facade.js +9 -0
  96. package/L4_Orchestration/facade/manager_registry.js +118 -0
  97. package/L4_Orchestration/facade/renderer_registry.js +274 -0
  98. package/L4_Orchestration/lifecycle/asset_loader.js +255 -0
  99. package/L4_Orchestration/lifecycle/initializer.js +135 -0
  100. package/L4_Orchestration/lifecycle/lifecycle.js +8 -0
  101. package/L4_Orchestration/rendering/facade.js +94 -0
  102. package/L4_Orchestration/rendering/rendering.js +7 -0
  103. package/LICENSE +21 -0
  104. package/README.md +82 -0
  105. package/bifrost_client.js +204 -0
  106. package/bifrost_core.js +1686 -0
  107. package/docs/ARCHITECTURE.md +111 -0
  108. package/docs/PROTOCOL.md +106 -0
  109. package/docs/RENDERERS.md +101 -0
  110. package/docs/SECURITY.md +92 -0
  111. package/package.json +24 -0
  112. package/syntax/prism-zconfig.js +41 -0
  113. package/syntax/prism-zenv.js +69 -0
  114. package/syntax/prism-zolo-theme.css +288 -0
  115. package/syntax/prism-zolo.js +380 -0
  116. package/syntax/prism-zschema.js +38 -0
  117. package/syntax/prism-zspark.js +25 -0
  118. package/syntax/prism-zui.js +68 -0
  119. package/zSys/accessibility/accessibility.js +10 -0
  120. package/zSys/accessibility/emoji_accessibility.js +173 -0
  121. package/zSys/dom/block_utils.js +122 -0
  122. package/zSys/dom/container_utils.js +370 -0
  123. package/zSys/dom/dom.js +13 -0
  124. package/zSys/dom/dom_utils.js +328 -0
  125. package/zSys/dom/encoding_utils.js +117 -0
  126. package/zSys/dom/style_utils.js +71 -0
  127. package/zSys/errors/error_display.js +299 -0
  128. package/zSys/errors/errors.js +10 -0
  129. package/zSys/theme/color_utils.js +274 -0
  130. package/zSys/theme/dark_mode_utils.js +272 -0
  131. package/zSys/theme/size_utils.js +256 -0
  132. package/zSys/theme/spacing_utils.js +405 -0
  133. package/zSys/theme/theme.js +14 -0
  134. package/zSys/theme/zbase.css +1735 -0
  135. package/zSys/theme/zbase_inject.js +161 -0
  136. package/zSys/theme/ztheme_utils.js +305 -0
  137. package/zSys/validation/error_boundary.js +201 -0
  138. package/zSys/validation/validation.js +11 -0
  139. package/zSys/validation/validation_utils.js +238 -0
  140. package/zSys/zSys.js +14 -0
@@ -0,0 +1,274 @@
1
+ /**
2
+ *
3
+ * Color Utilities - Visual Identity Primitives
4
+ *
5
+ *
6
+ * Pure functions for generating and applying color classes.
7
+ * These are foundational primitives for consistent visual identity.
8
+ *
9
+ * @module rendering/color_utils
10
+ * @layer 0 (Foundation - below Layer 2 utilities)
11
+ * @pattern Pure Functions (no state, no side effects)
12
+ *
13
+ * Philosophy:
14
+ * - "Color first" - Visual identity and semantics for all content
15
+ * - Pure functions (input → output, no side effects)
16
+ * - Uses zTheme color system exclusively (semantic colors)
17
+ * - Generates class names programmatically (no hardcoded strings)
18
+ *
19
+ * zTheme Semantic Colors:
20
+ * - primary: Brand color (green in zCLI)
21
+ * - secondary: Supporting brand
22
+ * - success: Green - positive actions/states
23
+ * - danger/error: Red - errors/critical
24
+ * - warning: Yellow - caution
25
+ * - info: Blue - informational
26
+ * - light: Light gray
27
+ * - dark: Dark gray
28
+ * - white: Pure white
29
+ * - black: Pure black (text only)
30
+ * - body: Default text color
31
+ * - muted: Secondary/subdued text
32
+ * - transparent: Transparent background
33
+ *
34
+ * Dependencies:
35
+ * - None (pure utility, no imports needed)
36
+ *
37
+ * Exports:
38
+ * - getBackgroundClass()
39
+ * - getTextColorClass()
40
+ * - getBorderColorClass()
41
+ * - applyColorScheme()
42
+ *
43
+ * Example:
44
+ * ```javascript
45
+ * import { getBackgroundClass, getTextColorClass, applyColorScheme } from './color_utils.js';
46
+ *
47
+ * const bg = getBackgroundClass('primary'); // 'zBg-primary'
48
+ * const text = getTextColorClass('white'); // 'zText-white'
49
+ *
50
+ * applyColorScheme(element, { background: 'light', text: 'dark', border: 'primary' });
51
+ * ```
52
+ */
53
+
54
+ //
55
+ // Color Constants (verified against zTheme CSS)
56
+ //
57
+
58
+ /**
59
+ * Valid background colors in zTheme
60
+ */
61
+ const VALID_BG_COLORS = [
62
+ 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'error',
63
+ 'light', 'dark', 'white', 'body', 'transparent'
64
+ ];
65
+
66
+ /**
67
+ * Valid text colors in zTheme
68
+ */
69
+ const VALID_TEXT_COLORS = [
70
+ 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'error',
71
+ 'light', 'dark', 'body', 'muted', 'white', 'black',
72
+ 'black-50', 'white-50' // Opacity variants
73
+ ];
74
+
75
+ /**
76
+ * Valid border colors in zTheme
77
+ */
78
+ const VALID_BORDER_COLORS = [
79
+ 'primary', 'secondary', 'success', 'info', 'warning', 'danger',
80
+ 'light', 'dark', 'white'
81
+ ];
82
+
83
+ //
84
+ // Background Color Functions
85
+ //
86
+
87
+ /**
88
+ * Generate background color class (zTheme: zBg-)
89
+ *
90
+ * @param {string|null} color - Color name (semantic)
91
+ * @returns {string|null} Class name or null if invalid
92
+ *
93
+ * @example
94
+ * getBackgroundClass('primary'); // 'zBg-primary' (brand color)
95
+ * getBackgroundClass('light'); // 'zBg-light' (light gray)
96
+ * getBackgroundClass('transparent'); // 'zBg-transparent'
97
+ */
98
+ export function getBackgroundClass(color) {
99
+ if (!color) {
100
+ return null;
101
+ }
102
+
103
+ const normalized = color.toLowerCase();
104
+
105
+ if (VALID_BG_COLORS.includes(normalized)) {
106
+ return `zBg-${normalized}`;
107
+ }
108
+
109
+ return null;
110
+ }
111
+
112
+ //
113
+ // Text Color Functions
114
+ //
115
+
116
+ /**
117
+ * Generate text color class (zTheme: zText-)
118
+ *
119
+ * @param {string|null} color - Color name (semantic)
120
+ * @returns {string|null} Class name or null if invalid
121
+ *
122
+ * @example
123
+ * getTextColorClass('primary'); // 'zText-primary' (brand color)
124
+ * getTextColorClass('muted'); // 'zText-muted' (subdued text)
125
+ * getTextColorClass('white'); // 'zText-white'
126
+ * getTextColorClass('black-50'); // 'zText-black-50' (50% opacity)
127
+ */
128
+ export function getTextColorClass(color) {
129
+ if (!color) {
130
+ return null;
131
+ }
132
+
133
+ const normalized = color.toLowerCase();
134
+
135
+ if (VALID_TEXT_COLORS.includes(normalized)) {
136
+ return `zText-${normalized}`;
137
+ }
138
+
139
+ return null;
140
+ }
141
+
142
+ //
143
+ // Border Color Functions
144
+ //
145
+
146
+ /**
147
+ * Generate border color class (zTheme: zBorder-)
148
+ *
149
+ * Note: This sets the border COLOR only, not the border itself.
150
+ * You still need to add the `zBorder` class to show the border.
151
+ *
152
+ * @param {string|null} color - Color name (semantic)
153
+ * @returns {string|null} Class name or null if invalid
154
+ *
155
+ * @example
156
+ * // Must combine with zBorder to show border:
157
+ * element.className = 'zBorder ' + getBorderColorClass('primary');
158
+ * // Result: 'zBorder zBorder-primary'
159
+ *
160
+ * getBorderColorClass('primary'); // 'zBorder-primary'
161
+ * getBorderColorClass('danger'); // 'zBorder-danger'
162
+ */
163
+ export function getBorderColorClass(color) {
164
+ if (!color) {
165
+ return null;
166
+ }
167
+
168
+ const normalized = color.toLowerCase();
169
+
170
+ if (VALID_BORDER_COLORS.includes(normalized)) {
171
+ return `zBorder-${normalized}`;
172
+ }
173
+
174
+ return null;
175
+ }
176
+
177
+ //
178
+ // Apply Color Scheme Function
179
+ //
180
+
181
+ /**
182
+ * Apply color scheme to an element via config object
183
+ *
184
+ * This is a higher-level function for applying multiple color concerns at once.
185
+ *
186
+ * @param {HTMLElement} element - Element to apply colors to
187
+ * @param {Object} config - Color configuration
188
+ * @param {string} [config.background] - Background color
189
+ * @param {string} [config.text] - Text color
190
+ * @param {string} [config.border] - Border color (requires zBorder class to be visible)
191
+ * @returns {HTMLElement} Element with color classes applied
192
+ *
193
+ * @example
194
+ * // Create a primary-colored card
195
+ * applyColorScheme(element, {
196
+ * background: 'primary',
197
+ * text: 'white',
198
+ * border: 'primary'
199
+ * });
200
+ *
201
+ * @example
202
+ * // Create a light info box
203
+ * applyColorScheme(element, {
204
+ * background: 'light',
205
+ * text: 'dark'
206
+ * });
207
+ */
208
+ export function applyColorScheme(element, config = {}) {
209
+ const classes = [];
210
+
211
+ // Apply background color
212
+ if (config.background) {
213
+ const bgClass = getBackgroundClass(config.background);
214
+ if (bgClass) {
215
+ classes.push(bgClass);
216
+ }
217
+ }
218
+
219
+ // Apply text color
220
+ if (config.text) {
221
+ const textClass = getTextColorClass(config.text);
222
+ if (textClass) {
223
+ classes.push(textClass);
224
+ }
225
+ }
226
+
227
+ // Apply border color
228
+ if (config.border) {
229
+ const borderClass = getBorderColorClass(config.border);
230
+ if (borderClass) {
231
+ classes.push(borderClass);
232
+ }
233
+ }
234
+
235
+ // Apply all classes at once
236
+ if (classes.length > 0) {
237
+ element.classList.add(...classes);
238
+ }
239
+
240
+ return element;
241
+ }
242
+
243
+ //
244
+ // Helper: Get All Available Colors
245
+ //
246
+
247
+ /**
248
+ * Get lists of all available colors (useful for debugging/documentation)
249
+ *
250
+ * @returns {Object} Object with arrays of valid colors for each category
251
+ *
252
+ * @example
253
+ * const colors = getAvailableColors();
254
+ * this.logger.log(colors.backgrounds); // ['primary', 'secondary', ...]
255
+ */
256
+ export function getAvailableColors() {
257
+ return {
258
+ backgrounds: [...VALID_BG_COLORS],
259
+ text: [...VALID_TEXT_COLORS],
260
+ borders: [...VALID_BORDER_COLORS]
261
+ };
262
+ }
263
+
264
+ //
265
+ // Default Export (for convenience)
266
+ //
267
+ export default {
268
+ getBackgroundClass,
269
+ getTextColorClass,
270
+ getBorderColorClass,
271
+ applyColorScheme,
272
+ getAvailableColors
273
+ };
274
+
@@ -0,0 +1,272 @@
1
+ /**
2
+ * ═══════════════════════════════════════════════════════════════
3
+ * Dark Mode Utilities - Theme Management (Layer 2)
4
+ * ═══════════════════════════════════════════════════════════════
5
+ *
6
+ * Pure utility functions for managing dark/light theme modes.
7
+ * Extracted from duplicate implementations in hooks.js and bifrost_client.js.
8
+ *
9
+ * @module utils/dark_mode_utils
10
+ * @layer 2 (Utilities - Pure functions)
11
+ *
12
+ * Dependencies: None (Layer 0 - Browser APIs only)
13
+ *
14
+ * Exports:
15
+ * - applyDarkModeClasses(isDark, options): Apply/remove dark mode classes
16
+ * - toggleDarkMode(currentIsDark): Toggle between dark/light and persist
17
+ * - getDarkModeFromStorage(): Get saved theme preference
18
+ * - saveDarkModeToStorage(isDark): Persist theme preference
19
+ *
20
+ * Example:
21
+ * ```javascript
22
+ * import { applyDarkModeClasses, toggleDarkMode } from '../../zSys/theme/dark_mode_utils.js';
23
+ * applyDarkModeClasses(true); // Enable dark mode
24
+ * const newTheme = toggleDarkMode(false); // Returns 'dark'
25
+ * ```
26
+ */
27
+
28
+ // ─────────────────────────────────────────────────────────────────
29
+ // Constants
30
+ // ─────────────────────────────────────────────────────────────────
31
+ const STORAGE_KEY = 'zTheme-mode';
32
+ const THEME_DARK = 'dark';
33
+ const THEME_LIGHT = 'light';
34
+ const COLOR_WHITE = '#ffffff';
35
+
36
+ const SELECTORS = {
37
+ NAVBAR: '.zNavbar',
38
+ NAVBAR_TOGGLER: '.zNavbar-toggler',
39
+ THEME_TOGGLE: '.zTheme-toggle',
40
+ BOOTSTRAP_ICON: 'i.bi',
41
+ CARD: '.zCard',
42
+ HEADERS: 'h1, h2, h3, h4, h5, h6',
43
+ PARAGRAPHS: 'p',
44
+ };
45
+
46
+ const CLASSES = {
47
+ BG_DARK: 'zBg-dark',
48
+ TEXT_LIGHT: 'zText-light',
49
+ NAVBAR_LIGHT: 'zNavbar-light',
50
+ NAVBAR_DARK: 'zNavbar-dark',
51
+ NAVBAR_TOGGLER_DARK: 'zNavbar-toggler-dark',
52
+ };
53
+
54
+ // ─────────────────────────────────────────────────────────────────
55
+ // Storage Functions
56
+ // ─────────────────────────────────────────────────────────────────
57
+
58
+ /**
59
+ * Get dark mode preference from localStorage
60
+ * @returns {boolean} True if dark mode is enabled
61
+ */
62
+ export function getDarkModeFromStorage() {
63
+ return localStorage.getItem(STORAGE_KEY) === THEME_DARK;
64
+ }
65
+
66
+ /**
67
+ * Save dark mode preference to localStorage
68
+ * @param {boolean} isDark - Whether dark mode is enabled
69
+ */
70
+ export function saveDarkModeToStorage(isDark) {
71
+ localStorage.setItem(STORAGE_KEY, isDark ? THEME_DARK : THEME_LIGHT);
72
+ }
73
+
74
+ // ─────────────────────────────────────────────────────────────────
75
+ // Theme Application Functions
76
+ // ─────────────────────────────────────────────────────────────────
77
+
78
+ /**
79
+ * Apply or remove dark mode classes to page elements
80
+ * @param {boolean} isDark - Whether to apply dark mode
81
+ * @param {Object} options - Configuration options
82
+ * @param {HTMLElement} [options.contentArea] - Content area element (defaults to body)
83
+ * @param {Function} [options.logger] - Optional logger for debugging
84
+ * @returns {void}
85
+ */
86
+ export function applyDarkModeClasses(isDark, options = {}) {
87
+ const { contentArea = document.body, logger = null } = options;
88
+
89
+ const body = document.body;
90
+ const navbars = document.querySelectorAll(SELECTORS.NAVBAR);
91
+ const togglers = document.querySelectorAll(SELECTORS.NAVBAR_TOGGLER);
92
+
93
+ if (logger && logger.log) {
94
+ logger.log(`[DarkMode] Applying ${isDark ? 'DARK' : 'LIGHT'} mode`);
95
+ logger.log(`[DarkMode] Found ${navbars.length} navbar(s), ${togglers.length} toggler(s)`);
96
+ }
97
+
98
+ if (isDark) {
99
+ // Apply dark mode
100
+ _applyDarkMode(body, navbars, togglers, contentArea, logger);
101
+ } else {
102
+ // Remove dark mode
103
+ _removeDarkMode(body, navbars, togglers, contentArea, logger);
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Apply dark mode classes and styles
109
+ * @private
110
+ */
111
+ function _applyDarkMode(body, navbars, togglers, contentArea, logger) {
112
+ // Apply dark background to body
113
+ body.classList.add(CLASSES.BG_DARK);
114
+ body.style.backgroundColor = 'var(--color-dark)';
115
+
116
+ // Apply white text to headers/paragraphs outside cards
117
+ _applyLightTextToElements(contentArea, logger);
118
+
119
+ // Update navbars
120
+ navbars.forEach(nav => {
121
+ nav.classList.remove(CLASSES.NAVBAR_LIGHT);
122
+ nav.classList.add(CLASSES.NAVBAR_DARK);
123
+ if (logger && logger.log) {
124
+ logger.log('[DarkMode] Navbar classes:', nav.className);
125
+ }
126
+ });
127
+
128
+ // Update navbar togglers (hamburger icons)
129
+ togglers.forEach((toggler, idx) => {
130
+ toggler.classList.add(CLASSES.NAVBAR_TOGGLER_DARK);
131
+ const icon = toggler.querySelector(SELECTORS.BOOTSTRAP_ICON);
132
+ if (icon) {
133
+ icon.style.color = COLOR_WHITE;
134
+ }
135
+ if (logger && logger.log) {
136
+ const computedColor = icon ? window.getComputedStyle(icon).color : 'not found';
137
+ logger.log(`[DarkMode] Toggler ${idx} classes:`, toggler.className);
138
+ logger.log(`[DarkMode] Toggler ${idx} icon color:`, computedColor);
139
+ }
140
+ });
141
+
142
+ // Update theme toggle button icon
143
+ const themeToggleBtn = document.querySelector(SELECTORS.THEME_TOGGLE);
144
+ if (themeToggleBtn) {
145
+ const icon = themeToggleBtn.querySelector(SELECTORS.BOOTSTRAP_ICON);
146
+ if (icon) {
147
+ icon.style.color = COLOR_WHITE;
148
+ if (logger && logger.log) {
149
+ logger.log('[DarkMode] Theme toggle icon color set to white');
150
+ }
151
+ }
152
+ } else if (logger && logger.log) {
153
+ logger.log('[DarkMode] Theme toggle button not found yet');
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Remove dark mode classes and styles
159
+ * @private
160
+ */
161
+ function _removeDarkMode(body, navbars, togglers, contentArea, logger) {
162
+ // Remove dark background from body
163
+ body.classList.remove(CLASSES.BG_DARK);
164
+ body.style.backgroundColor = '';
165
+ body.style.color = '';
166
+
167
+ // Remove white text from elements
168
+ _removeLightTextFromElements(contentArea, logger);
169
+
170
+ // Update navbars
171
+ navbars.forEach(nav => {
172
+ nav.classList.remove(CLASSES.NAVBAR_DARK);
173
+ nav.classList.add(CLASSES.NAVBAR_LIGHT);
174
+ if (logger && logger.log) {
175
+ logger.log('[DarkMode] Navbar classes:', nav.className);
176
+ }
177
+ });
178
+
179
+ // Update navbar togglers
180
+ togglers.forEach((toggler, idx) => {
181
+ toggler.classList.remove(CLASSES.NAVBAR_TOGGLER_DARK);
182
+ const icon = toggler.querySelector(SELECTORS.BOOTSTRAP_ICON);
183
+ if (icon) {
184
+ icon.style.color = '';
185
+ }
186
+ if (logger && logger.log) {
187
+ const computedColor = icon ? window.getComputedStyle(icon).color : 'not found';
188
+ logger.log(`[DarkMode] Toggler ${idx} classes:`, toggler.className);
189
+ logger.log(`[DarkMode] Toggler ${idx} icon color:`, computedColor);
190
+ }
191
+ });
192
+
193
+ // Clear theme toggle button icon color
194
+ const themeToggleBtn = document.querySelector(SELECTORS.THEME_TOGGLE);
195
+ if (themeToggleBtn) {
196
+ const icon = themeToggleBtn.querySelector(SELECTORS.BOOTSTRAP_ICON);
197
+ if (icon) {
198
+ icon.style.color = '';
199
+ if (logger && logger.log) {
200
+ logger.log('[DarkMode] Theme toggle icon color cleared');
201
+ }
202
+ }
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Apply light text to headers and paragraphs outside cards
208
+ * @private
209
+ */
210
+ function _applyLightTextToElements(contentArea, logger) {
211
+ if (!contentArea) return;
212
+
213
+ // Apply white text to headers outside cards
214
+ contentArea.querySelectorAll(SELECTORS.HEADERS).forEach(header => {
215
+ if (!header.closest(SELECTORS.CARD)) {
216
+ header.classList.add(CLASSES.TEXT_LIGHT);
217
+ }
218
+ });
219
+
220
+ // Apply white text to paragraphs outside cards
221
+ contentArea.querySelectorAll(SELECTORS.PARAGRAPHS).forEach(p => {
222
+ if (!p.closest(SELECTORS.CARD)) {
223
+ p.classList.add(CLASSES.TEXT_LIGHT);
224
+ }
225
+ });
226
+ }
227
+
228
+ /**
229
+ * Remove light text from all elements
230
+ * @private
231
+ */
232
+ function _removeLightTextFromElements(contentArea, logger) {
233
+ if (!contentArea) return;
234
+
235
+ contentArea.querySelectorAll(`.${CLASSES.TEXT_LIGHT}`).forEach(el => {
236
+ el.classList.remove(CLASSES.TEXT_LIGHT);
237
+ });
238
+ }
239
+
240
+ // ─────────────────────────────────────────────────────────────────
241
+ // Toggle Function
242
+ // ─────────────────────────────────────────────────────────────────
243
+
244
+ /**
245
+ * Toggle between dark and light mode
246
+ * @param {boolean} currentIsDark - Current dark mode state
247
+ * @param {Object} options - Configuration options (passed to applyDarkModeClasses)
248
+ * @returns {string} New theme ('dark' or 'light')
249
+ */
250
+ export function toggleDarkMode(currentIsDark, options = {}) {
251
+ const newIsDark = !currentIsDark;
252
+ saveDarkModeToStorage(newIsDark);
253
+ applyDarkModeClasses(newIsDark, options);
254
+ return newIsDark ? THEME_DARK : THEME_LIGHT;
255
+ }
256
+
257
+ // ─────────────────────────────────────────────────────────────────
258
+ // Initialization
259
+ // ─────────────────────────────────────────────────────────────────
260
+
261
+ /**
262
+ * Initialize dark mode from localStorage on page load
263
+ * @param {Object} options - Configuration options (passed to applyDarkModeClasses)
264
+ * @returns {boolean} Whether dark mode is active
265
+ */
266
+ export function initializeDarkMode(options = {}) {
267
+ const isDark = getDarkModeFromStorage();
268
+ if (isDark) {
269
+ applyDarkModeClasses(true, options);
270
+ }
271
+ return isDark;
272
+ }