@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.
- package/L1_Foundation/L1_Foundation.js +13 -0
- package/L1_Foundation/bootstrap/bootstrap.js +11 -0
- package/L1_Foundation/bootstrap/bootstrap_hooks.js +123 -0
- package/L1_Foundation/bootstrap/bootstrap_index.js +15 -0
- package/L1_Foundation/bootstrap/bootstrap_logger.js +135 -0
- package/L1_Foundation/bootstrap/cdn_loader.js +217 -0
- package/L1_Foundation/bootstrap/module_registry.js +102 -0
- package/L1_Foundation/bootstrap/prism_loader.js +164 -0
- package/L1_Foundation/config/client_config.js +110 -0
- package/L1_Foundation/config/config.js +7 -0
- package/L1_Foundation/connection/connection.js +8 -0
- package/L1_Foundation/connection/websocket_connection.js +122 -0
- package/L1_Foundation/constants/bifrost_constants.js +284 -0
- package/L1_Foundation/constants/constants.js +7 -0
- package/L1_Foundation/logger/logger.js +10 -0
- package/L2_Handling/L2_Handling.js +15 -0
- package/L2_Handling/cache/cache.js +22 -0
- package/L2_Handling/cache/cache_constants.js +69 -0
- package/L2_Handling/cache/orchestration/cache_manager.js +299 -0
- package/L2_Handling/cache/orchestration/cache_orchestrator.js +260 -0
- package/L2_Handling/cache/orchestration/orchestration.js +12 -0
- package/L2_Handling/cache/storage/session_manager.js +289 -0
- package/L2_Handling/cache/storage/storage.js +10 -0
- package/L2_Handling/cache/storage/storage_manager.js +590 -0
- package/L2_Handling/display/composite/composite.js +13 -0
- package/L2_Handling/display/composite/dashboard_renderer.js +221 -0
- package/L2_Handling/display/composite/swiper_renderer.js +564 -0
- package/L2_Handling/display/composite/terminal_renderer.js +922 -0
- package/L2_Handling/display/composite/wizard_conditional_renderer.js +274 -0
- package/L2_Handling/display/display.js +30 -0
- package/L2_Handling/display/feedback/feedback.js +11 -0
- package/L2_Handling/display/feedback/progressbar_renderer.js +418 -0
- package/L2_Handling/display/feedback/spinner_renderer.js +246 -0
- package/L2_Handling/display/inputs/button_renderer.js +634 -0
- package/L2_Handling/display/inputs/form_renderer.js +583 -0
- package/L2_Handling/display/inputs/input_renderer.js +658 -0
- package/L2_Handling/display/inputs/inputs.js +12 -0
- package/L2_Handling/display/navigation/menu_renderer.js +206 -0
- package/L2_Handling/display/navigation/navigation.js +11 -0
- package/L2_Handling/display/navigation/navigation_renderer.js +703 -0
- package/L2_Handling/display/orchestration/orchestration.js +11 -0
- package/L2_Handling/display/orchestration/renderer.js +430 -0
- package/L2_Handling/display/orchestration/zdisplay_orchestrator.js +1759 -0
- package/L2_Handling/display/outputs/alert_renderer.js +161 -0
- package/L2_Handling/display/outputs/audio_renderer.js +94 -0
- package/L2_Handling/display/outputs/card_renderer.js +229 -0
- package/L2_Handling/display/outputs/code_renderer.js +66 -0
- package/L2_Handling/display/outputs/dl_renderer.js +131 -0
- package/L2_Handling/display/outputs/header_renderer.js +162 -0
- package/L2_Handling/display/outputs/icon_renderer.js +107 -0
- package/L2_Handling/display/outputs/image_renderer.js +145 -0
- package/L2_Handling/display/outputs/list_renderer.js +190 -0
- package/L2_Handling/display/outputs/outputs.js +19 -0
- package/L2_Handling/display/outputs/table_renderer.js +765 -0
- package/L2_Handling/display/outputs/text_renderer.js +818 -0
- package/L2_Handling/display/outputs/typography_renderer.js +293 -0
- package/L2_Handling/display/outputs/video_renderer.js +116 -0
- package/L2_Handling/display/primitives/document_structure_primitives.js +319 -0
- package/L2_Handling/display/primitives/form_primitives.js +526 -0
- package/L2_Handling/display/primitives/generic_containers.js +109 -0
- package/L2_Handling/display/primitives/interactive_primitives.js +305 -0
- package/L2_Handling/display/primitives/link_primitives.js +552 -0
- package/L2_Handling/display/primitives/lists_primitives.js +262 -0
- package/L2_Handling/display/primitives/media_primitives.js +383 -0
- package/L2_Handling/display/primitives/primitives.js +19 -0
- package/L2_Handling/display/primitives/semantic_element_primitive.js +226 -0
- package/L2_Handling/display/primitives/table_primitives.js +528 -0
- package/L2_Handling/display/primitives/typography_primitives.js +175 -0
- package/L2_Handling/display/specialized/input_request_renderer.js +467 -0
- package/L2_Handling/display/specialized/specialized.js +10 -0
- package/L2_Handling/hooks/hooks.js +9 -0
- package/L2_Handling/hooks/menu_integration.js +57 -0
- package/L2_Handling/hooks/widget_hook_manager.js +292 -0
- package/L2_Handling/message/message.js +8 -0
- package/L2_Handling/message/message_handler.js +701 -0
- package/L2_Handling/navigation/navigation.js +8 -0
- package/L2_Handling/navigation/navigation_manager.js +403 -0
- package/L2_Handling/zhooks/features/cache_live.js +287 -0
- package/L2_Handling/zhooks/features/crumbs_live.js +292 -0
- package/L2_Handling/zhooks/zhooks_manager.js +65 -0
- package/L2_Handling/zvaf/zvaf.js +8 -0
- package/L2_Handling/zvaf/zvaf_manager.js +334 -0
- package/L3_Abstraction/L3_Abstraction.js +12 -0
- package/L3_Abstraction/orchestrator/container_unwrapper.js +101 -0
- package/L3_Abstraction/orchestrator/group_renderer.js +698 -0
- package/L3_Abstraction/orchestrator/input_event_handler.js +797 -0
- package/L3_Abstraction/orchestrator/metadata_processor.js +249 -0
- package/L3_Abstraction/orchestrator/navbar_builder.js +201 -0
- package/L3_Abstraction/orchestrator/orchestrator.js +13 -0
- package/L3_Abstraction/orchestrator/wizard_gate_handler.js +360 -0
- package/L3_Abstraction/renderer/renderer.js +1 -0
- package/L3_Abstraction/session/session.js +1 -0
- package/L4_Orchestration/L4_Orchestration.js +11 -0
- package/L4_Orchestration/client/client.js +1 -0
- package/L4_Orchestration/facade/facade.js +9 -0
- package/L4_Orchestration/facade/manager_registry.js +118 -0
- package/L4_Orchestration/facade/renderer_registry.js +274 -0
- package/L4_Orchestration/lifecycle/asset_loader.js +255 -0
- package/L4_Orchestration/lifecycle/initializer.js +135 -0
- package/L4_Orchestration/lifecycle/lifecycle.js +8 -0
- package/L4_Orchestration/rendering/facade.js +94 -0
- package/L4_Orchestration/rendering/rendering.js +7 -0
- package/LICENSE +21 -0
- package/README.md +82 -0
- package/bifrost_client.js +204 -0
- package/bifrost_core.js +1686 -0
- package/docs/ARCHITECTURE.md +111 -0
- package/docs/PROTOCOL.md +106 -0
- package/docs/RENDERERS.md +101 -0
- package/docs/SECURITY.md +92 -0
- package/package.json +24 -0
- package/syntax/prism-zconfig.js +41 -0
- package/syntax/prism-zenv.js +69 -0
- package/syntax/prism-zolo-theme.css +288 -0
- package/syntax/prism-zolo.js +380 -0
- package/syntax/prism-zschema.js +38 -0
- package/syntax/prism-zspark.js +25 -0
- package/syntax/prism-zui.js +68 -0
- package/zSys/accessibility/accessibility.js +10 -0
- package/zSys/accessibility/emoji_accessibility.js +173 -0
- package/zSys/dom/block_utils.js +122 -0
- package/zSys/dom/container_utils.js +370 -0
- package/zSys/dom/dom.js +13 -0
- package/zSys/dom/dom_utils.js +328 -0
- package/zSys/dom/encoding_utils.js +117 -0
- package/zSys/dom/style_utils.js +71 -0
- package/zSys/errors/error_display.js +299 -0
- package/zSys/errors/errors.js +10 -0
- package/zSys/theme/color_utils.js +274 -0
- package/zSys/theme/dark_mode_utils.js +272 -0
- package/zSys/theme/size_utils.js +256 -0
- package/zSys/theme/spacing_utils.js +405 -0
- package/zSys/theme/theme.js +14 -0
- package/zSys/theme/zbase.css +1735 -0
- package/zSys/theme/zbase_inject.js +161 -0
- package/zSys/theme/ztheme_utils.js +305 -0
- package/zSys/validation/error_boundary.js +201 -0
- package/zSys/validation/validation.js +11 -0
- package/zSys/validation/validation_utils.js +238 -0
- 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
|
+
}
|