@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,161 @@
1
+ /**
2
+ *
3
+ * Alert Renderer - Signal/Feedback Messages
4
+ *
5
+ *
6
+ * Renders alert/signal events from zCLI backend (zSignals subsystem).
7
+ * Provides visual feedback for success, info, warning, and error states.
8
+ *
9
+ * @module rendering/alert_renderer
10
+ * @layer 3
11
+ * @pattern Strategy (single event family)
12
+ *
13
+ * Philosophy:
14
+ * - "Terminal first" - alerts are visual feedback primitives
15
+ * - Pure rendering (no auto-dismiss, no complex animations)
16
+ * - Semantic colors (success=green, info=blue, warning=orange, error=red)
17
+ * - Uses Layer 2 utilities exclusively (no inline logic)
18
+ *
19
+ * Dependencies:
20
+ * - Layer 2: dom_utils.js, ztheme_utils.js
21
+ *
22
+ * Exports:
23
+ * - AlertRenderer: Class for rendering alert/signal events
24
+ *
25
+ * Example:
26
+ * ```javascript
27
+ * import { AlertRenderer } from './alert_renderer.js';
28
+ *
29
+ * const renderer = new AlertRenderer(logger);
30
+ * renderer.render({
31
+ * content: 'Operation successful!',
32
+ * eventType: 'success'
33
+ * }, 'zVaF');
34
+ * ```
35
+ */
36
+
37
+ // ─────────────────────────────────────────────────────────────────
38
+ // Imports
39
+ // ─────────────────────────────────────────────────────────────────
40
+
41
+ // Layer 2: Utilities
42
+ import { createElement, setAttributes } from '../../../zSys/dom/dom_utils.js';
43
+ import { getAlertColorClass } from '../../../zSys/theme/ztheme_utils.js';
44
+ import { withErrorBoundary } from '../../../zSys/validation/error_boundary.js';
45
+
46
+ //
47
+ // Alert Renderer Class
48
+ //
49
+
50
+ /**
51
+ * AlertRenderer - Renders signal/alert events
52
+ *
53
+ * Handles zCLI signal events (error, warning, success, info) which
54
+ * provide visual feedback to users. Maps to zTheme's zSignal components.
55
+ *
56
+ * Event Mapping:
57
+ * - error → zSignal-error (red)
58
+ * - warning → zSignal-warning (orange)
59
+ * - success → zSignal-success (green)
60
+ * - info → zSignal-info (blue)
61
+ */
62
+ export class AlertRenderer {
63
+ /**
64
+ * Create an AlertRenderer instance
65
+ * @param {Object} logger - Logger instance for debugging
66
+ */
67
+ constructor(logger) {
68
+ this.logger = logger || console;
69
+ this.logger.debug('[AlertRenderer] Initialized');
70
+
71
+ // Wrap render method with error boundary
72
+ const originalRender = this.render.bind(this);
73
+ this.render = withErrorBoundary(originalRender, {
74
+ component: 'AlertRenderer',
75
+ logger: this.logger
76
+ });
77
+ }
78
+
79
+ /**
80
+ * Render an alert/signal event
81
+ *
82
+ * @param {Object} data - Alert event data
83
+ * @param {string} data.content - Alert message text
84
+ * @param {string} data.eventType - Event type (error, warning, success, info)
85
+ * @param {number} [data.indent=0] - Indentation level (0 = no indent)
86
+ * @param {string} [data.class] - Custom CSS class (optional)
87
+ * @param {string} zone - Target DOM element ID
88
+ * @returns {HTMLElement|null} Created alert element or null if failed
89
+ *
90
+ * @example
91
+ * renderer.render({ content: 'Success!', eventType: 'success' }, 'zVaF');
92
+ * renderer.render({ content: 'Warning!', eventType: 'warning', indent: 1 }, 'zVaF');
93
+ * renderer.render({ content: 'Error!', eventType: 'error' }, 'zVaF');
94
+ */
95
+ render(data, zone) {
96
+ const { content, eventType, indent = 0, class: customClass } = data;
97
+
98
+ // Validate required parameters
99
+ if (!content) {
100
+ this.logger.error('[AlertRenderer] [ERROR] Missing required parameter: content');
101
+ return null;
102
+ }
103
+
104
+ if (!eventType) {
105
+ this.logger.error('[AlertRenderer] [ERROR] Missing required parameter: eventType');
106
+ return null;
107
+ }
108
+
109
+ // Get target container
110
+ const container = document.getElementById(zone);
111
+ if (!container) {
112
+ this.logger.error(`[AlertRenderer] [ERROR] Zone not found: ${zone}`);
113
+ return null;
114
+ }
115
+
116
+ // Build CSS classes array
117
+ const classes = ['zSignal'];
118
+
119
+ // Add alert color class (uses Layer 2 utility)
120
+ const alertClass = getAlertColorClass(eventType);
121
+ if (alertClass) {
122
+ classes.push(alertClass);
123
+ }
124
+
125
+ // Add custom class if provided (from YAML)
126
+ if (customClass) {
127
+ classes.push(customClass);
128
+ }
129
+
130
+ // Create alert element (using Layer 2 utility)
131
+ const alert = createElement('div', classes);
132
+ alert.textContent = content; // Use textContent for XSS safety
133
+
134
+ // Apply attributes
135
+ const attributes = {
136
+ role: 'alert' // ARIA role for accessibility
137
+ };
138
+
139
+ // Apply indent as inline style (zTheme doesn't have indent utilities)
140
+ // Each indent level = 1rem left margin
141
+ if (indent > 0) {
142
+ attributes.style = `margin-left: ${indent}rem;`;
143
+ }
144
+
145
+ setAttributes(alert, attributes);
146
+
147
+ // Append to container
148
+ container.appendChild(alert);
149
+
150
+ // Log success
151
+ this.logger.log(`[AlertRenderer] Rendered ${eventType} alert (${content.length} chars, indent: ${indent})`);
152
+
153
+ return alert;
154
+ }
155
+ }
156
+
157
+ //
158
+ // Default Export
159
+ //
160
+ export default AlertRenderer;
161
+
@@ -0,0 +1,94 @@
1
+ /**
2
+ *
3
+ * Audio Renderer - Media Display Events
4
+ *
5
+ * Terminal-First Design:
6
+ * - Backend sends src, alt_text, caption (works with URLs, served paths)
7
+ * - Terminal displays metadata + button to open (zOpen)
8
+ * - Bifrost renders a native <audio controls> element with zTheme styling
9
+ *
10
+ * Mirrors ImageRenderer / VideoRenderer: primitives-first, _zClass / _id
11
+ * passthrough, optional <figure>/<figcaption> when a caption is present.
12
+ *
13
+ * @module rendering/audio_renderer
14
+ * @layer 3
15
+ * @pattern Primitives-First
16
+ *
17
+ * Exports:
18
+ * - AudioRenderer: Class for rendering audio events
19
+ */
20
+
21
+ import { withErrorBoundary } from '../../../zSys/validation/error_boundary.js';
22
+
23
+ export class AudioRenderer {
24
+ constructor(logger) {
25
+ this.logger = logger;
26
+
27
+ const originalRender = this.render.bind(this);
28
+ this.render = withErrorBoundary(originalRender, {
29
+ component: 'AudioRenderer',
30
+ logger: this.logger
31
+ });
32
+ }
33
+
34
+ /**
35
+ * Render an <audio> element from event primitives.
36
+ *
37
+ * @param {Object} eventData - Event data from backend
38
+ * @param {string} eventData.src - Audio source (URL or served path)
39
+ * @param {string} [eventData.alt_text] - Accessibility label (→ aria-label)
40
+ * @param {string} [eventData.caption] - Optional caption (→ <figcaption>)
41
+ * @param {boolean} [eventData.loop] - Loop playback
42
+ * @param {boolean} [eventData.muted] - Start muted
43
+ * @param {string} [eventData._zClass] - Custom classes for styling
44
+ * @param {string} [eventData._id] - Custom DOM id for targeting
45
+ * @returns {HTMLElement} Audio element (or <figure> when captioned)
46
+ */
47
+ render(eventData) {
48
+ const { src, alt_text, caption, _zClass, _id, loop, muted } = eventData;
49
+
50
+ if (!src) {
51
+ this.logger.error('[AudioRenderer] Missing src parameter');
52
+ return this._createErrorElement();
53
+ }
54
+
55
+ this.logger.debug(`[AudioRenderer] Rendering audio: ${src}`);
56
+
57
+ const audio = document.createElement('audio');
58
+ audio.src = src;
59
+ audio.controls = true;
60
+ audio.preload = 'metadata';
61
+ audio.style.maxWidth = '100%';
62
+
63
+ if (_zClass) audio.className = _zClass;
64
+ if (_id) audio.setAttribute('id', _id);
65
+ if (alt_text) audio.setAttribute('aria-label', alt_text);
66
+ if (loop) audio.loop = true;
67
+ if (muted) audio.muted = true;
68
+
69
+ if (caption) {
70
+ const figure = document.createElement('figure');
71
+ figure.appendChild(audio);
72
+ const figcaption = document.createElement('figcaption');
73
+ figcaption.textContent = caption;
74
+ figcaption.className = 'zText-muted zText-center zmt-2';
75
+ figure.appendChild(figcaption);
76
+ // _zClass/_zStyle live on the inner <audio>; skip the central pass on the wrapper.
77
+ figure.__zMetaScoped = true;
78
+ this.logger.debug('[AudioRenderer] Audio with caption rendered');
79
+ return figure;
80
+ }
81
+
82
+ this.logger.debug('[AudioRenderer] Audio rendered');
83
+ return audio;
84
+ }
85
+
86
+ _createErrorElement() {
87
+ const error = document.createElement('div');
88
+ error.className = 'zAlert zAlert-danger';
89
+ error.textContent = '[WARN] Audio source missing';
90
+ return error;
91
+ }
92
+ }
93
+
94
+ export default AudioRenderer;
@@ -0,0 +1,229 @@
1
+ /**
2
+ *
3
+ * Card Renderer - zTheme Card Components
4
+ *
5
+ *
6
+ * Renders card/panel components aligned with zTheme card system:
7
+ * - zCard (basic cards with proper body wrapper)
8
+ * - zCard-header/footer (optional sections)
9
+ * - zCard-title/subtitle/text (typography)
10
+ * - Color variants (zCard-primary, zCard-success, etc.)
11
+ *
12
+ * REFACTORED: Uses Layer 0 primitives + Layer 2 utilities
13
+ *
14
+ * @module rendering/card_renderer
15
+ * @layer 3
16
+ * @pattern Strategy (card components)
17
+ *
18
+ * @see https://github.com/ZoloAi/zTheme/blob/main/src/css/zCards.css
19
+ */
20
+
21
+ // ─────────────────────────────────────────────────────────────────
22
+ // Imports
23
+ // ─────────────────────────────────────────────────────────────────
24
+
25
+ // Layer 0: Primitives
26
+ import { createDiv } from '../primitives/generic_containers.js';
27
+ import { createHeading, createParagraph } from '../primitives/typography_primitives.js';
28
+
29
+ export default class CardRenderer {
30
+ /**
31
+ * Create a CardRenderer instance
32
+ * @param {Object} logger - Logger instance for debugging
33
+ */
34
+ constructor(logger) {
35
+ this.logger = logger || console;
36
+ this.logger.debug('[CardRenderer] Initialized');
37
+ }
38
+
39
+ /**
40
+ * Enhance a container with proper zCard structure
41
+ *
42
+ * Transforms:
43
+ * <div class="zCard">
44
+ * <h2>Title</h2>
45
+ * <p>Content</p>
46
+ * </div>
47
+ *
48
+ * Into:
49
+ * <div class="zCard">
50
+ * <div class="zCard-body">
51
+ * <h2 class="zCard-title">Title</h2>
52
+ * <p class="zCard-text">Content</p>
53
+ * </div>
54
+ * </div>
55
+ *
56
+ * @param {HTMLElement} container - Container element with zCard class
57
+ * @returns {HTMLElement} Enhanced card element
58
+ */
59
+ enhanceCard(container) {
60
+ if (!container || !container.classList.contains('zCard')) {
61
+ return container;
62
+ }
63
+
64
+ // Check if zCard-body already exists
65
+ const existingBody = container.querySelector(':scope > .zCard-body');
66
+ if (existingBody) {
67
+ this.logger.log('[CardRenderer] Card already has zCard-body, skipping enhancement');
68
+ return container;
69
+ }
70
+
71
+ // Create zCard-body wrapper (using primitive)
72
+ const cardBody = createDiv({ class: 'zCard-body' });
73
+
74
+ // Move all direct children into the card body
75
+ while (container.firstChild) {
76
+ const child = container.firstChild;
77
+
78
+ // Apply zCard typography classes based on element type
79
+ if (child.nodeType === Node.ELEMENT_NODE) {
80
+ this._applyCardTypography(child);
81
+ }
82
+
83
+ cardBody.appendChild(child);
84
+ }
85
+
86
+ // Append the card body back to the container
87
+ container.appendChild(cardBody);
88
+
89
+ this.logger.log('[CardRenderer] Enhanced card with zCard-body wrapper');
90
+ return container;
91
+ }
92
+
93
+ /**
94
+ * Apply zCard typography classes to elements
95
+ * @private
96
+ * @param {HTMLElement} element - Element to enhance
97
+ */
98
+ _applyCardTypography(element) {
99
+ const tagName = element.tagName.toLowerCase();
100
+
101
+ // Apply zCard-title to first heading (h1-h6)
102
+ if (['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tagName)) {
103
+ // Check if this is the first heading in the card
104
+ const parentCard = element.closest('.zCard');
105
+ if (parentCard) {
106
+ const existingTitle = parentCard.querySelector('.zCard-title');
107
+ if (!existingTitle) {
108
+ element.classList.add('zCard-title');
109
+ this.logger.log(`[CardRenderer] Applied zCard-title to ${tagName}`);
110
+ } else {
111
+ // Subsequent headings could be subtitles
112
+ element.classList.add('zCard-subtitle');
113
+ this.logger.log(`[CardRenderer] Applied zCard-subtitle to ${tagName}`);
114
+ }
115
+ }
116
+ } else if (tagName === 'p') {
117
+ // Apply zCard-text to paragraphs
118
+ element.classList.add('zCard-text');
119
+ this.logger.log('[CardRenderer] Applied zCard-text to paragraph');
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Create a complete card from scratch
125
+ *
126
+ * @param {Object} options - Card configuration
127
+ * @param {string} [options.title] - Card title
128
+ * @param {string} [options.subtitle] - Card subtitle
129
+ * @param {string} [options.text] - Card text content
130
+ * @param {string} [options.variant] - Color variant (primary, success, info, etc.)
131
+ * @param {string} [options.header] - Header text
132
+ * @param {string} [options.footer] - Footer text
133
+ * @param {string} [options.classes] - Additional CSS classes
134
+ * @returns {HTMLElement} Complete card element
135
+ */
136
+ createCard(options = {}) {
137
+ const {
138
+ title = '',
139
+ subtitle = '',
140
+ text = '',
141
+ variant = null,
142
+ header = null,
143
+ footer = null,
144
+ classes = ''
145
+ } = options;
146
+
147
+ // Create card container (using primitive)
148
+ const card = createDiv({ class: 'zCard' });
149
+
150
+ // Apply variant class
151
+ if (variant) {
152
+ card.classList.add(`zCard-${variant}`);
153
+ }
154
+
155
+ // Apply additional classes
156
+ if (classes) {
157
+ const classList = classes.split(' ').filter(c => c.trim());
158
+ card.classList.add(...classList);
159
+ }
160
+
161
+ // Add header if provided (using primitive)
162
+ if (header) {
163
+ const headerEl = createDiv({ class: 'zCard-header' });
164
+ headerEl.textContent = header;
165
+ card.appendChild(headerEl);
166
+ }
167
+
168
+ // Create card body (using primitive)
169
+ const cardBody = createDiv({ class: 'zCard-body' });
170
+
171
+ // Add title (using primitive)
172
+ if (title) {
173
+ const titleEl = createHeading(2, { class: 'zCard-title' });
174
+ titleEl.textContent = title;
175
+ cardBody.appendChild(titleEl);
176
+ }
177
+
178
+ // Add subtitle (using primitive)
179
+ if (subtitle) {
180
+ const subtitleEl = createHeading(6, { class: 'zCard-subtitle zmb-2 zText-muted' });
181
+ subtitleEl.textContent = subtitle;
182
+ cardBody.appendChild(subtitleEl);
183
+ }
184
+
185
+ // Add text content (using primitive)
186
+ if (text) {
187
+ const textEl = createParagraph({ class: 'zCard-text' });
188
+ textEl.textContent = text;
189
+ cardBody.appendChild(textEl);
190
+ }
191
+
192
+ card.appendChild(cardBody);
193
+
194
+ // Add footer if provided (using primitive)
195
+ if (footer) {
196
+ const footerEl = createDiv({ class: 'zCard-footer zText-muted' });
197
+ footerEl.textContent = footer;
198
+ card.appendChild(footerEl);
199
+ }
200
+
201
+ this.logger.log('[CardRenderer] Created card:', {title, variant, hasHeader: !!header, hasFooter: !!footer});
202
+
203
+ return card;
204
+ }
205
+
206
+ /**
207
+ * Extract card variant from color code
208
+ * @param {string} color - Color code (PRIMARY, SUCCESS, etc.)
209
+ * @returns {string|null} Card variant class or null
210
+ */
211
+ getCardVariant(color) {
212
+ if (!color) {
213
+ return null;
214
+ }
215
+
216
+ const variantMap = {
217
+ 'PRIMARY': 'primary',
218
+ 'SECONDARY': 'secondary',
219
+ 'SUCCESS': 'success',
220
+ 'INFO': 'info',
221
+ 'WARNING': 'warning',
222
+ 'DANGER': 'danger',
223
+ 'LIGHT': 'light',
224
+ 'DARK': 'dark'
225
+ };
226
+
227
+ return variantMap[color.toUpperCase()] || null;
228
+ }
229
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Code Renderer - Renders `code` display events
3
+ *
4
+ * Handles the 'code' zDisplay event, which is the SSOT for code block
5
+ * rendering shared by:
6
+ * - zCode shorthand
7
+ * - zMD code fences (delegated from markdown_parser.py)
8
+ * - zTerminal preview (delegated from terminal_executor.py)
9
+ *
10
+ * Produces a <pre><code class="language-{lang}"> block.
11
+ * Prism.js (if loaded) will auto-highlight based on the language class.
12
+ *
13
+ * @module rendering/code_renderer
14
+ * @layer 3
15
+ * @pattern Strategy (single event type)
16
+ */
17
+
18
+ import { createLanguagePre } from '../primitives/semantic_element_primitive.js';
19
+ import { withErrorBoundary } from '../../../zSys/validation/error_boundary.js';
20
+
21
+ export class CodeRenderer {
22
+ /**
23
+ * @param {Object} logger - Logger instance
24
+ */
25
+ constructor(logger) {
26
+ this.logger = logger || console;
27
+ this.logger.debug('[CodeRenderer] Initialized');
28
+
29
+ const originalRenderCode = this.renderCode.bind(this);
30
+ this.renderCode = withErrorBoundary(originalRenderCode, {
31
+ component: 'CodeRenderer.renderCode',
32
+ logger: this.logger
33
+ });
34
+ }
35
+
36
+ /**
37
+ * Render a code block element.
38
+ *
39
+ * @param {Object} eventData
40
+ * @param {string} eventData.content - Raw code content
41
+ * @param {string} [eventData.language] - Programming language (e.g. 'python', 'js', 'zolo')
42
+ * @param {number} [eventData.indent] - Indentation level (unused in HTML; kept for symmetry)
43
+ * @returns {HTMLElement} <pre><code> element
44
+ */
45
+ renderCode(eventData) {
46
+ const content = eventData.content || '';
47
+ const language = eventData.language || null;
48
+
49
+ this.logger.debug(`[CodeRenderer] renderCode: language=${language}, length=${content.length}`);
50
+
51
+ const attrs = {};
52
+ if (eventData.zId || eventData._zId || eventData._id) {
53
+ attrs.id = eventData.zId || eventData._zId || eventData._id;
54
+ }
55
+ // _zClass is applied centrally by the orchestrator (SSOT); only legacy `class`
56
+ // is honoured here as a creation-time attribute.
57
+ if (eventData.class) {
58
+ attrs.class = eventData.class;
59
+ }
60
+
61
+ const element = createLanguagePre(language || 'text', content, attrs);
62
+ return element;
63
+ }
64
+ }
65
+
66
+ export default CodeRenderer;
@@ -0,0 +1,131 @@
1
+ /**
2
+ * DL Renderer - Description List Rendering
3
+ *
4
+ * Renders HTML description lists (<dl>, <dt>, <dd>) from zDisplay events.
5
+ *
6
+ * Extracted from zdisplay_renderer.js (Phase 9, Task 3.2)
7
+ *
8
+ * @module display/outputs/dl_renderer
9
+ * @layer L2 (Handling)
10
+ */
11
+
12
+ export class DLRenderer {
13
+ constructor(logger) {
14
+ this.logger = logger;
15
+ }
16
+
17
+ /**
18
+ * Render a description list element
19
+ * @param {Object} event - DL event data
20
+ * @param {Array} event.items - Array of {term, desc} objects
21
+ * @param {string} event._zClass - Optional CSS classes
22
+ * @param {number} event.indent - Optional indent level
23
+ * @returns {HTMLElement} - Description list element
24
+ */
25
+ render(event) {
26
+ const dl = document.createElement('dl');
27
+
28
+ // _zClass / _zStyle applied centrally by the orchestrator (SSOT) on this <dl>.
29
+
30
+ // Apply indent as left margin
31
+ if (event.indent && event.indent > 0) {
32
+ dl.style.marginLeft = `${event.indent}rem`;
33
+ }
34
+
35
+ // Render description list items
36
+ const items = event.items || [];
37
+ items.forEach(item => {
38
+ // Create <dt> for the term
39
+ const dt = document.createElement('dt');
40
+ const termContent = item.term || '';
41
+ dt.innerHTML = this._sanitizeHTML(this._decodeUnicodeEscapes(termContent));
42
+ dl.appendChild(dt);
43
+
44
+ // Create <dd> for description(s)
45
+ // desc can be a string or an array of strings
46
+ const descriptions = Array.isArray(item.desc) ? item.desc : [item.desc];
47
+ descriptions.forEach(desc => {
48
+ const dd = document.createElement('dd');
49
+ const descContent = desc || '';
50
+ dd.innerHTML = this._sanitizeHTML(this._decodeUnicodeEscapes(descContent));
51
+ dl.appendChild(dd);
52
+ });
53
+ });
54
+
55
+ return dl;
56
+ }
57
+
58
+ /**
59
+ * Decode Unicode escape sequences to actual characters
60
+ * Supports: \uXXXX (standard) and \UXXXXXXXX (extended) formats
61
+ *
62
+ * Note: Basic escape sequences (\n, \t, etc.) are handled by JSON.parse()
63
+ * automatically when receiving data from backend. We only need to decode
64
+ * custom Unicode formats that JSON doesn't handle.
65
+ *
66
+ * @param {string} text - Text containing Unicode escapes
67
+ * @returns {string} - Decoded text
68
+ * @private
69
+ */
70
+ _decodeUnicodeEscapes(text) {
71
+ if (!text || typeof text !== 'string') return text;
72
+
73
+ // Replace \uXXXX format (standard 4-digit Unicode escape)
74
+ text = text.replace(/\\u([0-9A-Fa-f]{4})/g, (match, hexCode) => {
75
+ return String.fromCodePoint(parseInt(hexCode, 16));
76
+ });
77
+
78
+ // Replace \UXXXXXXXX format (extended 4-8 digit for supplementary characters & emojis)
79
+ text = text.replace(/\\U([0-9A-Fa-f]{4,8})/g, (match, hexCode) => {
80
+ return String.fromCodePoint(parseInt(hexCode, 16));
81
+ });
82
+
83
+ return text;
84
+ }
85
+
86
+ /**
87
+ * Sanitize HTML to prevent XSS attacks
88
+ * Allows common safe tags: <strong>, <em>, <code>, <a>, <br>, <span>
89
+ *
90
+ * @param {string} html - HTML string to sanitize
91
+ * @returns {string} - Sanitized HTML
92
+ * @private
93
+ */
94
+ _sanitizeHTML(html) {
95
+ if (!html) {
96
+ return '';
97
+ }
98
+
99
+ // Allow common safe tags: <strong>, <em>, <code>, <a>, <br>
100
+ // This is a basic sanitizer - for production, consider using DOMPurify
101
+ const allowedTags = ['strong', 'em', 'code', 'a', 'br', 'span'];
102
+
103
+ // Create a temporary div to parse HTML
104
+ const temp = document.createElement('div');
105
+ temp.innerHTML = html;
106
+
107
+ // Remove script tags and event handlers
108
+ const scripts = temp.querySelectorAll('script');
109
+ scripts.forEach(script => script.remove());
110
+
111
+ // Remove event handler attributes (onclick, onerror, etc.)
112
+ const allElements = temp.querySelectorAll('*');
113
+ allElements.forEach(el => {
114
+ Array.from(el.attributes).forEach(attr => {
115
+ if (attr.name.startsWith('on')) {
116
+ el.removeAttribute(attr.name);
117
+ }
118
+ });
119
+
120
+ // Remove elements not in allowed list (except text nodes)
121
+ if (!allowedTags.includes(el.tagName.toLowerCase())) {
122
+ // Keep the text content but remove the tag
123
+ const textContent = el.textContent;
124
+ const textNode = document.createTextNode(textContent);
125
+ el.parentNode.replaceChild(textNode, el);
126
+ }
127
+ });
128
+
129
+ return temp.innerHTML;
130
+ }
131
+ }