@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,360 @@
1
+ /**
2
+ * L3_Abstraction/orchestrator/wizard_gate_handler.js
3
+ *
4
+ * Wizard Gate Handling for Progressive Disclosure
5
+ *
6
+ * Manages gated wizard steps that require user input before revealing
7
+ * subsequent steps. Handles:
8
+ * - Gate detection (keys with '!' modifier)
9
+ * - Pre-gate/post-gate rendering
10
+ * - Submit button creation and delegation
11
+ * - Gate result handling
12
+ * - Wizard restart functionality
13
+ *
14
+ * Extracted from zdisplay_orchestrator.js (Phase 4.2)
15
+ */
16
+
17
+ /**
18
+ * WizardGateHandler - Manages gated wizard step rendering and interaction
19
+ */
20
+ export class WizardGateHandler {
21
+ constructor(client, logger, orchestrator) {
22
+ this.client = client;
23
+ this.logger = logger;
24
+ this.orchestrator = orchestrator; // Reference to parent orchestrator for renderItems
25
+ }
26
+
27
+ /**
28
+ * Check if data contains a gated wizard step
29
+ * @param {Object} data - YAML data object
30
+ * @returns {string|null} Gate step key (with '!') or null
31
+ */
32
+ detectGateStep(data) {
33
+ if (!data || typeof data !== 'object') {
34
+ return null;
35
+ }
36
+ // 2C-a: Server sends gate_key explicitly — no need to scan for '!' convention
37
+ const serverGateKey = this.orchestrator._pendingGateKey;
38
+ if (serverGateKey) {
39
+ return Object.keys(data).find(k => k.replace('!', '') === serverGateKey && !k.startsWith('_'))
40
+ || (serverGateKey + '!');
41
+ }
42
+ // Fallback: scan for '!' in keys (safety net for non-chunk paths)
43
+ return Object.keys(data).find(k => k.includes('!') && !k.startsWith('_')) || null;
44
+ }
45
+
46
+ /**
47
+ * Render a gated wizard step collection with submit button and hidden post-gate steps.
48
+ * @param {Object} data - Dict of wizard steps, at least one key has '!' suffix
49
+ * @param {HTMLElement} parentElement - Element to render into
50
+ * @param {string} gateKey - The step key that has '!' (e.g., "one!")
51
+ * @param {string} wizardPath - Dot-path from file root to this wizard dict
52
+ */
53
+ async renderWizardGated(data, parentElement, gateKey, wizardPath) {
54
+ const cleanGateKey = gateKey.replace('!', '');
55
+ // zProgress is wizard CHROME (a progress readout), not a step. Exclude it from
56
+ // the step list so it never renders inline as a mis-positioned step, then draw
57
+ // it once at the top with the wizard's own step math injected: total = real
58
+ // steps, current = the step we're on (the gate). Placement is the wiring.
59
+ const allStepKeys = Object.keys(data).filter(k => !k.startsWith('_') && k !== 'zProgress');
60
+ const gateIdx = allStepKeys.indexOf(gateKey);
61
+ const preGateKeys = allStepKeys.slice(0, gateIdx);
62
+ const postGateKeys = allStepKeys.slice(gateIdx + 1);
63
+
64
+ if (data.zProgress) {
65
+ const inner = (data.zProgress && data.zProgress.zDisplay) ? data.zProgress.zDisplay : data.zProgress;
66
+ const progEvent = {
67
+ event: 'progress_bar',
68
+ ...inner,
69
+ current: gateIdx >= 0 ? gateIdx + 1 : 1,
70
+ total: allStepKeys.length
71
+ };
72
+ await this.orchestrator.renderItems({ zProgress: { zDisplay: progEvent } }, parentElement, wizardPath);
73
+ }
74
+
75
+ // Render pre-gate steps
76
+ for (const key of preGateKeys) {
77
+ const cleanKey = key.replace('!', '');
78
+ await this.orchestrator.renderItems({ [cleanKey]: data[key] }, parentElement, wizardPath);
79
+ }
80
+
81
+ // Extract _zDelegate from the gate step (GUI-only metadata, ignored in zCLI).
82
+ // After backend expand+unwrap, the zInput wrapper is stripped:
83
+ // {zInput: {prompt, _zDelegate}} → {event:'read_string', prompt, _zDelegate}
84
+ // so _zDelegate lands directly on the step value. Fallback covers raw-YAML edge cases.
85
+ const gateStepValue = data[gateKey];
86
+ this.logger.log('[WizardGate] gateStepValue keys:', gateStepValue ? Object.keys(gateStepValue) : 'null/undefined', '| _zDelegate:', gateStepValue?._zDelegate, '| zInput._zDelegate:', gateStepValue?.zInput?._zDelegate);
87
+ const delegatePath = gateStepValue?._zDelegate
88
+ ?? gateStepValue?.zInput?._zDelegate
89
+ ?? null;
90
+
91
+ // CRITICAL: zDialog forms need _dialogId for proper context management
92
+ // When wizard gate renders zDialog, inject _dialogId if missing (backend may not have sent it yet)
93
+ let gateStepValueToRender = gateStepValue;
94
+ if (gateStepValue && typeof gateStepValue === 'object' && gateStepValue.zDialog) {
95
+ // Extract _dialogId from parent context if present, or generate one
96
+ const dialogId = gateStepValue._dialogId || this._generateDialogId();
97
+ this.logger.log('[WizardGate] Injecting _dialogId into zDialog:', dialogId);
98
+
99
+ // Create a new object with _dialogId injected into the zDialog object
100
+ gateStepValueToRender = {
101
+ ...gateStepValue,
102
+ zDialog: {
103
+ ...gateStepValue.zDialog,
104
+ _dialogId: dialogId
105
+ }
106
+ };
107
+ }
108
+
109
+ // Gate step wrapper with submit button
110
+ const gateWrapper = document.createElement('div');
111
+ gateWrapper.setAttribute('data-wizard-gate', cleanGateKey);
112
+ if (delegatePath) {
113
+ gateWrapper.setAttribute('data-zdelegate', delegatePath);
114
+ }
115
+
116
+ await this.orchestrator.renderItems({ [cleanGateKey]: gateStepValueToRender }, gateWrapper, wizardPath);
117
+
118
+ // Collect pre-gate input values keyed by their step name, so the stateless
119
+ // gate-submit re-execution can seed zHat with everything answered before the
120
+ // gate (e.g. Ask_Name, Ask_Age) — not just the gate's own value.
121
+ const collectPreGateValues = () => {
122
+ const map = {};
123
+ for (const key of preGateKeys) {
124
+ const clean = key.replace('!', '');
125
+ const scope = parentElement.querySelector(`[data-zkey="${clean}"]`);
126
+ const el = scope ? scope.querySelector('input, textarea, select') : null;
127
+ if (el) map[clean] = (el.value || '').trim();
128
+ }
129
+ return map;
130
+ };
131
+
132
+ // Shared submit action — drives the gate forward via wizard_gate_submit.
133
+ // Works for both input gates (gate holds the input) and button gates
134
+ // (gate holds a zBtn like "Continue", inputs live in pre-gate steps).
135
+ const doGateSubmit = async (triggerBtn, isButtonGate) => {
136
+ const gateInput = gateWrapper.querySelector('input, textarea, select');
137
+ let value;
138
+ if (gateInput) {
139
+ value = gateInput.value.trim();
140
+ if (!value) return; // input gate: require a value
141
+ } else {
142
+ // Button gate: no input to read — use the button label as a truthy
143
+ // zHat[gateKey] marker so post-gate `if: zHat[Gate]` conditions pass.
144
+ value = (triggerBtn && triggerBtn.textContent.trim()) || 'true';
145
+ }
146
+
147
+ const values = collectPreGateValues();
148
+
149
+ if (gateInput) gateInput.disabled = true;
150
+ let restoreLabel = 'Submit';
151
+ if (triggerBtn) {
152
+ restoreLabel = triggerBtn.textContent;
153
+ triggerBtn.disabled = true;
154
+ if (!isButtonGate) triggerBtn.textContent = '...';
155
+ }
156
+
157
+ try {
158
+ this.client.connection.send(JSON.stringify({
159
+ event: 'wizard_gate_submit',
160
+ wizardPath,
161
+ gateKey: cleanGateKey,
162
+ value,
163
+ values,
164
+ }));
165
+ } catch (e) {
166
+ this.logger.error('[WizardGate] Submit error:', e);
167
+ if (gateInput) gateInput.disabled = false;
168
+ if (triggerBtn) { triggerBtn.disabled = false; triggerBtn.textContent = restoreLabel; }
169
+ }
170
+ };
171
+
172
+ // Check if the rendered content already has its own submit mechanism
173
+ // (e.g., zDialog forms have their own submit button inside the form)
174
+ const hasOwnSubmit = gateWrapper.querySelector('button[type="submit"]');
175
+
176
+ // An authored gate button (zBtn → <button class="zBtn">) acts as the gate's
177
+ // own advance control. Repurpose its click to drive wizard_gate_submit instead
178
+ // of the orphan input_response that button_renderer wires by default.
179
+ const authoredBtn = hasOwnSubmit ? null : gateWrapper.querySelector('button.zBtn');
180
+
181
+ if (hasOwnSubmit) {
182
+ // zDialog form owns its submit — nothing to inject.
183
+ } else if (authoredBtn) {
184
+ // Strip button_renderer's input_response click handler (cloneNode drops
185
+ // listeners) and own the click so it advances the gate.
186
+ const freshBtn = authoredBtn.cloneNode(true);
187
+ authoredBtn.replaceWith(freshBtn);
188
+ freshBtn.addEventListener('click', (e) => {
189
+ e.preventDefault();
190
+ doGateSubmit(freshBtn, true);
191
+ });
192
+ } else {
193
+ // Input-only gate (no authored button): inject the default Submit control.
194
+ const submitBtn = document.createElement('button');
195
+ submitBtn.textContent = 'Submit';
196
+ submitBtn.className = 'zBtn zBtn-primary mt-2';
197
+ submitBtn.style.marginTop = '0.5rem';
198
+ submitBtn.addEventListener('click', () => doGateSubmit(submitBtn, false));
199
+
200
+ // When _zDelegate is set, hide the default Submit button (delegate takes over)
201
+ // Keep it in the DOM so restartWizardFromGate can find/reset it
202
+ if (delegatePath) {
203
+ submitBtn.style.display = 'none';
204
+ submitBtn.setAttribute('aria-hidden', 'true');
205
+ }
206
+
207
+ gateWrapper.appendChild(submitBtn);
208
+ }
209
+
210
+ parentElement.appendChild(gateWrapper);
211
+
212
+ // Post-gate container (hidden until wizard_gate_result arrives)
213
+ if (postGateKeys.length > 0) {
214
+ const postGateContainer = document.createElement('div');
215
+ postGateContainer.setAttribute('data-wizard-post-gate', cleanGateKey);
216
+ postGateContainer.setAttribute('data-wizard-path', wizardPath);
217
+ postGateContainer.style.display = 'none';
218
+ parentElement.appendChild(postGateContainer);
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Handle wizard_gate_result: populate and show the post-gate container.
224
+ * @param {Object} message - {gateKey, wizardPath, data}
225
+ */
226
+ async handleWizardGateResult(message) {
227
+ const { gateKey, data } = message;
228
+ const postGateContainer = document.querySelector(`[data-wizard-post-gate="${gateKey}"]`);
229
+ if (!postGateContainer) {
230
+ this.logger.error('[WizardGate] Post-gate container not found for key:', gateKey);
231
+ return;
232
+ }
233
+
234
+ const wizardPath = postGateContainer.getAttribute('data-wizard-path') || '';
235
+ postGateContainer.innerHTML = '';
236
+ await this.orchestrator.renderItems(data, postGateContainer, wizardPath);
237
+ postGateContainer.style.display = '';
238
+
239
+ const gateWrapper = document.querySelector(`[data-wizard-gate="${gateKey}"]`);
240
+ if (gateWrapper) {
241
+ const btn = gateWrapper.querySelector('button');
242
+ if (btn) {
243
+ btn.textContent = 'Submitted';
244
+ btn.disabled = true;
245
+ }
246
+ // Also disable delegate button if one was wired for this gate
247
+ const delegatePath = gateWrapper.getAttribute('data-zdelegate');
248
+ if (delegatePath) {
249
+ const scope = gateWrapper.closest('[data-zblock]') || document;
250
+ const delegateContainer = this._resolveZDelegatePath(delegatePath, scope);
251
+ const delegateBtn = delegateContainer?.querySelector('button') || delegateContainer;
252
+ if (delegateBtn) delegateBtn.disabled = true;
253
+ }
254
+ }
255
+
256
+ // Wire restart handlers for any buttons with data-wizard-action inside the post-gate content
257
+ // (data-wizard-gate path). Mark them wired so the orchestrator's inline-gate
258
+ // loop-back pass (Pass 0b) skips them — this path owns its own restart.
259
+ const restartBtns = postGateContainer.querySelectorAll('[data-wizard-action]:not([data-wizard-action-wired])');
260
+ for (const btn of restartBtns) {
261
+ btn.dataset.wizardActionWired = 'true';
262
+ btn.addEventListener('click', () => {
263
+ this.restartWizardFromGate(btn.dataset.wizardAction);
264
+ });
265
+ }
266
+
267
+ // Wire delegates for any hidden delegated buttons in post-gate content
268
+ this.orchestrator._wireDelegates();
269
+ }
270
+
271
+ /**
272
+ * Restart a wizard from its gate step: re-enable the gate input and clear post-gate content.
273
+ * Called when a button with data-wizard-action matching the gate key is clicked.
274
+ * @param {string} targetGateKey - The gate step key to restart from (e.g. "one")
275
+ */
276
+ restartWizardFromGate(targetGateKey) {
277
+ const gateWrapper = document.querySelector(`[data-wizard-gate="${targetGateKey}"]`);
278
+ const postGateContainer = document.querySelector(`[data-wizard-post-gate="${targetGateKey}"]`);
279
+
280
+ if (!gateWrapper) {
281
+ this.logger.warn('[WizardGate] Restart: gate wrapper not found for key:', targetGateKey);
282
+ return;
283
+ }
284
+
285
+ // Re-enable gate input and reset submit button
286
+ const input = gateWrapper.querySelector('input, textarea, select');
287
+ const submitBtn = gateWrapper.querySelector('button');
288
+ if (input) {
289
+ input.disabled = false;
290
+ input.value = '';
291
+ }
292
+ if (submitBtn) {
293
+ submitBtn.disabled = false;
294
+ submitBtn.textContent = 'Submit';
295
+ }
296
+
297
+ // Re-enable delegate button if one is wired for this gate
298
+ const delegatePath = gateWrapper.getAttribute('data-zdelegate');
299
+ if (delegatePath) {
300
+ const scope = gateWrapper.closest('[data-zblock]') || document;
301
+ const delegateContainer = this._resolveZDelegatePath(delegatePath, scope);
302
+ const delegateBtn = delegateContainer?.querySelector('button') || delegateContainer;
303
+ if (delegateBtn) delegateBtn.disabled = false;
304
+ // Clear wired flag so _wireDelegates re-attaches a fresh listener
305
+ delete gateWrapper.dataset.zdelegateWired;
306
+ }
307
+
308
+ // Clear and hide post-gate content
309
+ if (postGateContainer) {
310
+ postGateContainer.style.display = 'none';
311
+ postGateContainer.innerHTML = '';
312
+ }
313
+
314
+ this.logger.log(`[WizardGate] Restarted wizard from gate: ${targetGateKey}`);
315
+
316
+ // Re-wire delegate (deferred so DOM settles after restart)
317
+ if (delegatePath) {
318
+ setTimeout(() => this.orchestrator._wireDelegates(), 50);
319
+ } else if (input) {
320
+ setTimeout(() => input.focus(), 50);
321
+ }
322
+ }
323
+
324
+ /**
325
+ * Resolve a _zDelegate path (e.g. "_GUI.Btn_Eq") to the target button element.
326
+ * Walks data-zkey attributes in sequence within the given scope (defaults to document).
327
+ * Scoping to the nearest [data-zblock] prevents cross-block collisions.
328
+ * @param {string} path - Dot-separated key path
329
+ * @param {Element|Document} [scope=document] - Root to search within
330
+ * @returns {HTMLElement|null}
331
+ * @private
332
+ */
333
+ _resolveZDelegatePath(path, scope = document) {
334
+ const parts = path.split('.');
335
+ let el = scope;
336
+ for (const part of parts) {
337
+ el = el.querySelector(`[data-zkey="${part}"]`);
338
+ if (!el) return null;
339
+ }
340
+ return el;
341
+ }
342
+
343
+ /**
344
+ * Generate a unique dialog ID for client-side form context management.
345
+ * Uses crypto.randomUUID() if available, falls back to timestamp-based UUID.
346
+ * @returns {string} UUID string
347
+ * @private
348
+ */
349
+ _generateDialogId() {
350
+ if (typeof crypto !== 'undefined' && crypto.randomUUID) {
351
+ return crypto.randomUUID();
352
+ }
353
+ // Fallback: timestamp-based UUID v4-like format
354
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
355
+ const r = (Math.random() * 16) | 0;
356
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
357
+ return v.toString(16);
358
+ });
359
+ }
360
+ }
@@ -0,0 +1 @@
1
+ // Placeholder - will be populated during migration
@@ -0,0 +1 @@
1
+ // Placeholder - will be populated during migration
@@ -0,0 +1,11 @@
1
+ /**
2
+ * L4_Orchestration - Top-Level Coordination Layer
3
+ *
4
+ * Facade, lifecycle management, module loading.
5
+ * Depends on: L1_Foundation, L2_Handling, L3_Abstraction
6
+ * Provides: BifrostClient public API
7
+ */
8
+
9
+ // Export subdirectories (will be populated in Step 5)
10
+ export * from './client/index.js';
11
+ export * from './lifecycle/lifecycle.js';
@@ -0,0 +1 @@
1
+ // Placeholder - will be populated during migration
@@ -0,0 +1,9 @@
1
+ /**
2
+ * L4_Orchestration/facade - Client Facade Components
3
+ *
4
+ * High-level facade components for BifrostClient.
5
+ * Provides registries and helpers for lazy loading and module management.
6
+ */
7
+
8
+ export { RendererRegistry, RENDERER_REGISTRY } from './renderer_registry.js';
9
+ export { ManagerRegistry, MANAGER_REGISTRY } from './manager_registry.js';
@@ -0,0 +1,118 @@
1
+ /**
2
+ * L4_Orchestration/facade/manager_registry.js
3
+ *
4
+ * Manager Registry - Centralized Lazy Loading for All Managers
5
+ *
6
+ * Consolidates 4 individual _ensure*Manager() methods from bifrost_client.js
7
+ * into a single registry-based loader.
8
+ *
9
+ * Extracted from bifrost_client.js (Task 0, Step 1.3)
10
+ *
11
+ * @module facade/manager_registry
12
+ * @layer L4 (Orchestration)
13
+ */
14
+
15
+ /**
16
+ * Manager Registry - Maps manager types to their module paths and classes
17
+ */
18
+ export const MANAGER_REGISTRY = {
19
+ cache: {
20
+ path: 'L2_Handling/cache/orchestration/cache_manager.js',
21
+ className: 'CacheManager',
22
+ isDefault: false,
23
+ passClient: true
24
+ },
25
+ zvaf: {
26
+ path: 'L2_Handling/zvaf/zvaf_manager.js',
27
+ className: 'ZVaFManager',
28
+ isDefault: false,
29
+ passClient: true
30
+ },
31
+ navigation: {
32
+ path: 'L2_Handling/navigation/navigation_manager.js',
33
+ className: 'NavigationManager',
34
+ isDefault: false,
35
+ passClient: true
36
+ },
37
+ widgetHook: {
38
+ path: 'L2_Handling/hooks/widget_hook_manager.js',
39
+ className: 'WidgetHookManager',
40
+ isDefault: false,
41
+ passClient: true
42
+ }
43
+ };
44
+
45
+ /**
46
+ * ManagerRegistry - Centralized manager loading and caching
47
+ */
48
+ export class ManagerRegistry {
49
+ constructor(client) {
50
+ this.client = client;
51
+ this.logger = client.logger;
52
+ this.baseUrl = client._baseUrl;
53
+ this.managers = {}; // Cache for loaded managers
54
+ }
55
+
56
+ /**
57
+ * Ensure a manager is loaded and cached
58
+ * @param {string} type - Manager type (e.g., 'cache', 'zvaf', 'navigation', 'widgetHook')
59
+ * @returns {Promise<Object>} Manager instance
60
+ */
61
+ async ensureManager(type) {
62
+ // Return cached manager if already loaded
63
+ if (this.managers[type]) {
64
+ return this.managers[type];
65
+ }
66
+
67
+ // Get manager config from registry
68
+ const config = MANAGER_REGISTRY[type];
69
+ if (!config) {
70
+ throw new Error(`Unknown manager type: ${type}`);
71
+ }
72
+
73
+ // Load manager module
74
+ const fullPath = `${this.baseUrl}${config.path}`;
75
+ const module = await import(fullPath);
76
+ const ManagerClass = config.isDefault ? module.default : module[config.className];
77
+
78
+ // Instantiate manager
79
+ const args = config.passClient ? [this.client] : [this.logger];
80
+ const manager = new ManagerClass(...args);
81
+
82
+ // Cache and return
83
+ this.managers[type] = manager;
84
+ this.logger.debug(`${config.className} loaded via registry`);
85
+ return manager;
86
+ }
87
+
88
+ /**
89
+ * Get a cached manager (throws if not loaded)
90
+ * @param {string} type - Manager type
91
+ * @returns {Object} Manager instance
92
+ */
93
+ getManager(type) {
94
+ const manager = this.managers[type];
95
+ if (!manager) {
96
+ throw new Error(`Manager not loaded: ${type}`);
97
+ }
98
+ return manager;
99
+ }
100
+
101
+ /**
102
+ * Check if a manager is loaded
103
+ * @param {string} type - Manager type
104
+ * @returns {boolean}
105
+ */
106
+ hasManager(type) {
107
+ return !!this.managers[type];
108
+ }
109
+
110
+ /**
111
+ * Preload multiple managers in parallel
112
+ * @param {string[]} types - Array of manager types
113
+ * @returns {Promise<void>}
114
+ */
115
+ async preloadManagers(types) {
116
+ await Promise.all(types.map(type => this.ensureManager(type)));
117
+ }
118
+ }