@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,249 @@
1
+ /**
2
+ * L3_Abstraction/orchestrator/metadata_processor.js
3
+ *
4
+ * Metadata Processing for Declarative Rendering
5
+ *
6
+ * Handles extraction and application of metadata from YAML data:
7
+ * - _zClass: CSS classes
8
+ * - _zStyle: Inline styles
9
+ * - _zHTML: Element type (div, section, article, etc.)
10
+ * - _zId / zId: Element ID
11
+ * - _zGroup: Grouped rendering context
12
+ * - _zGroupLabel: Label for input groups
13
+ * - _zScripts: Client-side scripts
14
+ *
15
+ * Extracted from zdisplay_orchestrator.js (Phase 4.4a)
16
+ *
17
+ * ════════════════════════════════════════════════════════════════════════════
18
+ * _zClass / _zStyle CONTRACT — SINGLE SOURCE OF TRUTH (do not re-implement)
19
+ * ════════════════════════════════════════════════════════════════════════════
20
+ * applyMetadata() is the ONE place _zClass/_zStyle are read and applied across
21
+ * all three tiers. Renderers must NOT hand-roll className/style parsing.
22
+ *
23
+ * THREE TIERS, ONE FUNCTION:
24
+ * • zBlock — renderBlock / renderChunkProgressive call applyMetadata (overwrite).
25
+ * • zKey — createContainer calls applyMetadata (overwrite).
26
+ * • zEvent — renderZDisplayEvent runs a single APPEND-mode applyMetadata pass on
27
+ * the returned element AFTER the switch. Leaf renderers own only their
28
+ * CONTEXTUAL class (color → zText- / zBtn- / zSignal- variants); they
29
+ * do NOT touch _zClass or _zStyle.
30
+ *
31
+ * CLASS MODES:
32
+ * • overwrite (default): _zClass OWNS className — for freshly-created wrappers.
33
+ * • append (options.append): _zClass LAYERS via classList.add — for event nodes
34
+ * that already carry intrinsic classes (zText, bi-*, zTable…). Dedupes.
35
+ *
36
+ * _zStyle: MERGES onto any existing inline style (indent margin, media max-width),
37
+ * never clobbers it. It is an additive escape hatch.
38
+ *
39
+ * COMPOSITE OPT-OUT (`element.__zMetaScoped = true`):
40
+ * A renderer that returns a WRAPPER but places _zClass on an INNER node sets
41
+ * __zMetaScoped on the wrapper so the central event pass skips it (no mis-target,
42
+ * no double-apply). Current composites: media-with-caption (<figure> → inner
43
+ * <img>/<video>/<audio>) and zTable (.zTable-container → inner <table>).
44
+ * Cases that return null (zDash, in-place table replace) skip the pass entirely
45
+ * and therefore keep their own _zClass handling.
46
+ * ════════════════════════════════════════════════════════════════════════════
47
+ */
48
+
49
+ // Layer 0: Primitives
50
+ import { createSemanticElement } from '../../L2_Handling/display/primitives/semantic_element_primitive.js';
51
+ import { convertStyleToString } from '../../zSys/dom/style_utils.js';
52
+
53
+ /**
54
+ * MetadataProcessor - Extracts and applies metadata to DOM elements
55
+ */
56
+ export class MetadataProcessor {
57
+ constructor(logger) {
58
+ this.logger = logger;
59
+
60
+ // Define metadata keys that should be skipped during iteration
61
+ this.METADATA_KEYS = [
62
+ '_zClass',
63
+ '_zStyle',
64
+ '_zHTML',
65
+ '_zId',
66
+ 'zScripts',
67
+ '_zScripts',
68
+ '_zGroup',
69
+ '_zGroupLabel',
70
+ 'zId'
71
+ ];
72
+ }
73
+
74
+ /**
75
+ * Extract metadata from data object (underscore-prefixed keys)
76
+ * @param {Object} data - YAML data object
77
+ * @returns {Object} Metadata object with extracted keys
78
+ */
79
+ extractMetadata(data) {
80
+ const metadata = {};
81
+
82
+ if (!data || typeof data !== 'object') {
83
+ return metadata;
84
+ }
85
+
86
+ for (const [key, value] of Object.entries(data)) {
87
+ if (key.startsWith('_') || key === 'zId') {
88
+ metadata[key] = value;
89
+ }
90
+ }
91
+
92
+ return metadata;
93
+ }
94
+
95
+ /**
96
+ * Check if a key is a metadata key (should be skipped during iteration)
97
+ * @param {string} key - Key to check
98
+ * @returns {boolean} True if key is metadata
99
+ */
100
+ isMetadataKey(key) {
101
+ return this.METADATA_KEYS.includes(key) || key.startsWith('~');
102
+ }
103
+
104
+ /**
105
+ * Create a container element with metadata applied
106
+ * @param {string} zKey - Key for data-zkey attribute
107
+ * @param {Object} metadata - Metadata object
108
+ * @param {Object} logger - Logger instance
109
+ * @returns {HTMLElement} Container element with metadata applied
110
+ */
111
+ createContainer(zKey, metadata, logger = this.logger) {
112
+ // Use centralized semantic element primitive (SSOT for _zHTML)
113
+ const elementType = metadata._zHTML || 'div';
114
+ const container = createSemanticElement(elementType, {}, logger);
115
+
116
+ // Apply metadata
117
+ this.applyMetadata(container, metadata, zKey, logger);
118
+
119
+ // Add form submit logging for debugging
120
+ if (container.tagName.toLowerCase() === 'form') {
121
+ container.addEventListener('submit', (event) => {
122
+ logger.log(`[MetadataProcessor] Form submitted: ${zKey}`);
123
+ logger.log('[MetadataProcessor] Form data:', new FormData(container));
124
+ logger.log('[MetadataProcessor] Form validity:', container.checkValidity());
125
+ });
126
+ }
127
+
128
+ return container;
129
+ }
130
+
131
+ /**
132
+ * Normalize a _zClass value (string with comma/space separators, or array)
133
+ * into a clean array of class tokens. SSOT for the "how do I read _zClass"
134
+ * question that every tier (block / key / event) used to answer differently.
135
+ * @param {string|Array} zClass
136
+ * @returns {string[]}
137
+ */
138
+ normalizeClasses(zClass) {
139
+ if (!zClass) return [];
140
+ const parts = Array.isArray(zClass)
141
+ ? zClass
142
+ : (zClass.includes(',') ? zClass.split(',') : zClass.split(' '));
143
+ return parts.map(c => String(c).trim()).filter(Boolean);
144
+ }
145
+
146
+ /**
147
+ * Apply metadata to an existing element.
148
+ *
149
+ * Two class modes:
150
+ * - overwrite (default): _zClass OWNS className — used by zBlock/zKey wrappers
151
+ * that are freshly created and have no intrinsic classes.
152
+ * - append (options.append): _zClass LAYERS on top via classList.add — used by
153
+ * zEvent elements that already carry renderer-intrinsic classes (zText,
154
+ * bi-*, zTable …) which must survive. Empty/absent _zClass is a no-op.
155
+ *
156
+ * @param {HTMLElement} element - Element to apply metadata to
157
+ * @param {Object} metadata - Metadata object (_zClass, _zStyle, zId)
158
+ * @param {string} [zKey] - Optional key for data-zkey attribute
159
+ * @param {Object} [logger] - Optional logger instance
160
+ * @param {Object} [options] - { append?: boolean }
161
+ */
162
+ applyMetadata(element, metadata, zKey = null, logger = this.logger, options = {}) {
163
+ const append = options.append === true;
164
+
165
+ // Apply custom classes
166
+ if (append) {
167
+ // Event tier: layer on top of intrinsic classes, never clear.
168
+ const extra = this.normalizeClasses(metadata._zClass);
169
+ if (extra.length) element.classList.add(...extra);
170
+ } else if (metadata._zClass !== undefined) {
171
+ if (metadata._zClass === '' || metadata._zClass === null) {
172
+ // Empty string or null = no container classes
173
+ element.className = '';
174
+ } else {
175
+ element.className = this.normalizeClasses(metadata._zClass).join(' ');
176
+ }
177
+ } else if (!element.className) {
178
+ // Default: NO classes (bare element, following HTML/CSS convention)
179
+ element.className = '';
180
+ }
181
+
182
+ // Apply inline styles if provided (supports string or nested object).
183
+ // MERGE onto any existing inline style (e.g. a renderer's indent margin or a
184
+ // media element's max-width) instead of clobbering it — _zStyle is an additive
185
+ // escape hatch, not a replacement for intrinsic layout styles.
186
+ if (metadata._zStyle !== undefined && metadata._zStyle !== '' && metadata._zStyle !== null) {
187
+ const cssString = convertStyleToString(metadata._zStyle, logger);
188
+ if (cssString) {
189
+ const existing = (element.getAttribute('style') || '').trim();
190
+ if (existing) {
191
+ const sep = existing.endsWith(';') ? ' ' : '; ';
192
+ element.setAttribute('style', existing + sep + cssString);
193
+ } else {
194
+ element.setAttribute('style', cssString);
195
+ }
196
+ }
197
+ }
198
+
199
+ // Apply custom ID if provided (no underscore = passed to both Bifrost & Terminal)
200
+ if (metadata.zId !== undefined && metadata.zId !== '' && metadata.zId !== null) {
201
+ element.setAttribute('id', metadata.zId);
202
+ }
203
+
204
+ // Add data attribute for debugging/testing
205
+ if (zKey) {
206
+ element.setAttribute('data-zkey', zKey);
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Apply classes to an element (helper for adding classes without replacing)
212
+ * @param {HTMLElement} element - Element to add classes to
213
+ * @param {string|Array} classes - Classes to add (string or array)
214
+ */
215
+ applyClasses(element, classes) {
216
+ if (!classes) return;
217
+
218
+ if (Array.isArray(classes)) {
219
+ element.classList.add(...classes.filter(c => c.trim()));
220
+ } else if (typeof classes === 'string') {
221
+ const classList = classes.includes(',')
222
+ ? classes.split(',').map(c => c.trim())
223
+ : classes.split(' ').filter(c => c.trim());
224
+ element.classList.add(...classList);
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Check if data has block-level metadata
230
+ * @param {Object} data - Data object to check
231
+ * @returns {boolean} True if data has metadata keys
232
+ */
233
+ hasBlockMetadata(data) {
234
+ if (!data || typeof data !== 'object') {
235
+ return false;
236
+ }
237
+ return Object.keys(data).some(k => k.startsWith('_'));
238
+ }
239
+
240
+ /**
241
+ * Detect if metadata indicates input-group context
242
+ * @param {Object} metadata - Metadata object
243
+ * @returns {boolean} True if input-group context
244
+ */
245
+ isInputGroupContext(metadata) {
246
+ return metadata._zGroup === 'input-group' ||
247
+ (metadata._zClass && metadata._zClass.includes('zInputGroup'));
248
+ }
249
+ }
@@ -0,0 +1,201 @@
1
+ /**
2
+ * L3_Abstraction/orchestrator/navbar_builder.js
3
+ *
4
+ * NavBar Building and Rendering
5
+ *
6
+ * Handles navigation bar construction for both:
7
+ * - Meta NavBar (from zSession config, rendered in <zNavBar> element)
8
+ * - Content NavBar (from ~zNavBar* metadata in YAML content)
9
+ *
10
+ * Delegates actual rendering to NavigationRenderer (L2).
11
+ *
12
+ * Extracted from zdisplay_orchestrator.js (Phase 4.3)
13
+ */
14
+
15
+ /**
16
+ * NavBarBuilder - Constructs and renders navigation bars
17
+ */
18
+ export class NavBarBuilder {
19
+ constructor(client, logger) {
20
+ this.client = client;
21
+ this.logger = logger;
22
+ }
23
+
24
+ /**
25
+ * 3A: Wire event delegation onto a server-built navbar element.
26
+ *
27
+ * Python emits data-nav-action="navigate|dropdown-toggle|hamburger" so the
28
+ * client needs zero construction logic — only generic interaction handling.
29
+ *
30
+ * @param {HTMLElement} navEl - The <nav> element injected from nav_html
31
+ * @param {Object} client - BifrostClient (for navigationManager access)
32
+ * @param {Object} logger - Logger instance
33
+ */
34
+ static wireNavBarEvents(navEl, client, logger) {
35
+ if (!navEl) return;
36
+
37
+ // ── Hamburger (mobile toggle) ────────────────────────────────────
38
+ const toggler = navEl.querySelector('[data-nav-action="hamburger"]');
39
+ if (toggler) {
40
+ const targetId = toggler.dataset.navTarget;
41
+ const collapse = navEl.querySelector(`#${targetId}`);
42
+ toggler.addEventListener('click', (e) => {
43
+ e.preventDefault();
44
+ const expanded = toggler.getAttribute('aria-expanded') === 'true';
45
+ toggler.setAttribute('aria-expanded', String(!expanded));
46
+ collapse?.classList.toggle('show', !expanded);
47
+ collapse?.classList.toggle('zShow', !expanded);
48
+ });
49
+ }
50
+
51
+ // ── Dropdown toggles ─────────────────────────────────────────────
52
+ navEl.querySelectorAll('[data-nav-action="dropdown-toggle"]').forEach(toggle => {
53
+ const menu = toggle.nextElementSibling;
54
+ toggle.addEventListener('click', (e) => {
55
+ e.preventDefault();
56
+ const isOpen = menu?.classList.contains('zShow');
57
+
58
+ // Second click on an already-open dropdown → navigate to the parent page
59
+ if (isOpen) {
60
+ const href = toggle.getAttribute('href') || toggle.dataset.navHref;
61
+ if (href && href !== '#') {
62
+ menu.classList.remove('zShow');
63
+ toggle.setAttribute('aria-expanded', 'false');
64
+ if (client?.navigationManager) {
65
+ // navbar:true → server RESETs the crumb trail (SSOT with zCLI:
66
+ // a navbar pick becomes the new root). Browser Back/popstate does
67
+ // NOT pass this, so it still pops the stack normally.
68
+ client.navigationManager.navigateToRoute(href, { navbar: true });
69
+ } else {
70
+ window.location.href = href;
71
+ }
72
+ }
73
+ return;
74
+ }
75
+
76
+ // Close all sibling dropdowns
77
+ navEl.querySelectorAll('.zDropdown-menu.zShow').forEach(m => {
78
+ if (m !== menu) {
79
+ m.classList.remove('zShow');
80
+ m.previousElementSibling?.setAttribute('aria-expanded', 'false');
81
+ }
82
+ });
83
+ menu?.classList.toggle('zShow', !isOpen);
84
+ toggle.setAttribute('aria-expanded', String(!isOpen));
85
+ });
86
+ });
87
+
88
+ // ── Navigation links ─────────────────────────────────────────────
89
+ navEl.querySelectorAll('[data-nav-action="navigate"]').forEach(link => {
90
+ link.addEventListener('click', (e) => {
91
+ e.preventDefault();
92
+ const href = link.dataset.navHref || link.getAttribute('href');
93
+ // Close enclosing dropdown (if any)
94
+ const menu = link.closest('.zDropdown-menu');
95
+ if (menu) {
96
+ menu.classList.remove('zShow');
97
+ menu.previousElementSibling?.setAttribute('aria-expanded', 'false');
98
+ }
99
+ if (client?.navigationManager) {
100
+ // navbar:true → server RESETs the crumb trail (SSOT with zCLI navbar).
101
+ client.navigationManager.navigateToRoute(href, { navbar: true });
102
+ } else {
103
+ window.location.href = href;
104
+ }
105
+ });
106
+ });
107
+
108
+ // ── Close dropdowns on outside click ─────────────────────────────
109
+ setTimeout(() => {
110
+ document.addEventListener('click', (e) => {
111
+ if (!navEl.contains(e.target)) {
112
+ navEl.querySelectorAll('.zDropdown-menu.zShow').forEach(menu => {
113
+ menu.classList.remove('zShow');
114
+ menu.previousElementSibling?.setAttribute('aria-expanded', 'false');
115
+ });
116
+ }
117
+ });
118
+ }, 100);
119
+
120
+ if (logger) logger.debug('[NavBarBuilder] wireNavBarEvents attached');
121
+ }
122
+
123
+ /**
124
+ * Render navbar DOM element (v1.6.1: Returns DOM element to preserve event listeners)
125
+ * Used for meta navbar (from zSession config) rendered in <zNavBar> element
126
+ * @param {Array} items - Navbar items (e.g., ['zVaF', 'zAbout', '^zLogin'])
127
+ * @param {Object} options - Client options (for title/brand)
128
+ * @returns {Promise<HTMLElement|null>} Navbar DOM element
129
+ */
130
+ async renderMetaNavBarHTML(items, options) {
131
+ this.logger.debug('[NavBarBuilder] renderMetaNavBarHTML called:', items.length);
132
+
133
+ if (!Array.isArray(items) || items.length === 0) {
134
+ this.logger.warn('[NavBarBuilder] [WARN] No navbar items provided');
135
+ return null;
136
+ }
137
+
138
+ try {
139
+ // Load navigation renderer
140
+ const navRenderer = await this.client._ensureNavigationRenderer();
141
+
142
+ // Render navbar element
143
+ const navElement = navRenderer.renderNavBar(items, {
144
+ className: 'zcli-navbar-meta',
145
+ theme: 'light',
146
+ href: (item) => {
147
+ // Strip modifiers (^ for bounce-back, ~ for anchor) from URL
148
+ const cleanItem = item.replace(/^[\^~]+/, '');
149
+ return `/${cleanItem}`;
150
+ },
151
+ brand: options.brand || options.title // Use dedicated brand field (always zSpark)
152
+ });
153
+
154
+ // FIX v1.6.1: Return DOM element directly (NOT outerHTML!)
155
+ // This preserves event listeners attached by link_primitives.js
156
+ // The caller (zvaf_manager.js) will append the element instead of setting innerHTML
157
+ this.logger.log('[NavBarBuilder] Returning navbar DOM element (preserves event listeners)');
158
+ return navElement;
159
+ } catch (error) {
160
+ this.logger.error('[NavBarBuilder] Failed to render navbar element:', error);
161
+ return null;
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Render navigation bar from metadata (~zNavBar* in content)
167
+ * Used for content-embedded navbars in YAML
168
+ * @param {Array} items - Navbar items
169
+ * @param {HTMLElement} parentElement - Parent element to append to
170
+ */
171
+ async renderNavBar(items, parentElement) {
172
+ if (!Array.isArray(items) || items.length === 0) {
173
+ this.logger.warn('[NavBarBuilder] ~zNavBar* has no items or is not an array');
174
+ return;
175
+ }
176
+
177
+ try {
178
+ // Load navigation renderer
179
+ const navRenderer = await this.client._ensureNavigationRenderer();
180
+
181
+ // Render navbar with zTheme zNavbar component
182
+ const navElement = navRenderer.renderNavBar(items, {
183
+ theme: 'light'
184
+ });
185
+
186
+ if (navElement) {
187
+ parentElement.appendChild(navElement);
188
+
189
+ // Re-initialize zTheme collapse now that navbar is in DOM
190
+ if (window.zTheme && typeof window.zTheme.initCollapse === 'function') {
191
+ window.zTheme.initCollapse();
192
+ this.logger.log('[NavBarBuilder] Re-initialized zTheme collapse for navbar');
193
+ }
194
+
195
+ this.logger.log('[NavBarBuilder] Rendered navigation bar with items:', items);
196
+ }
197
+ } catch (error) {
198
+ this.logger.error('[NavBarBuilder] Failed to render navigation bar:', error);
199
+ }
200
+ }
201
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * L3_Abstraction/orchestrator - Display Orchestration Components
3
+ *
4
+ * High-level orchestration for declarative rendering pipeline.
5
+ * Coordinates L2 display handlers and manages rendering flow.
6
+ */
7
+
8
+ export { WizardGateHandler } from './wizard_gate_handler.js';
9
+ export { NavBarBuilder } from './navbar_builder.js';
10
+ export { MetadataProcessor } from './metadata_processor.js';
11
+ export { GroupRenderer } from './group_renderer.js';
12
+ export { ContainerUnwrapper } from './container_unwrapper.js';
13
+ export { InputEventHandler } from './input_event_handler.js';