jsgui3-server 0.0.143 → 0.0.145

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 (67) hide show
  1. package/docs/comprehensive-documentation.md +25 -6
  2. package/docs/configuration-reference.md +46 -11
  3. package/docs/controls-development.md +54 -26
  4. package/docs/jsgui3-html-improvement-ideas.md +162 -0
  5. package/docs/jsgui3-html-improvement-ideas.svg +151 -0
  6. package/docs/troubleshooting.md +9 -8
  7. package/examples/controls/14d) window, canvas globe/EarthGlobeRenderer.js +19 -14
  8. package/examples/controls/14d) window, canvas globe/pipeline/TransformStage.js +5 -5
  9. package/examples/jsgui3-html/01) mvvm-counter/client.js +648 -0
  10. package/examples/jsgui3-html/01) mvvm-counter/server.js +21 -0
  11. package/examples/jsgui3-html/02) date-transform/client.js +764 -0
  12. package/examples/jsgui3-html/02) date-transform/server.js +21 -0
  13. package/examples/jsgui3-html/03) form-validation/client.js +1045 -0
  14. package/examples/jsgui3-html/03) form-validation/server.js +21 -0
  15. package/examples/jsgui3-html/04) data-grid/client.js +738 -0
  16. package/examples/jsgui3-html/04) data-grid/server.js +21 -0
  17. package/examples/jsgui3-html/05) master-detail/client.js +649 -0
  18. package/examples/jsgui3-html/05) master-detail/server.js +21 -0
  19. package/examples/jsgui3-html/06) theming/client.js +514 -0
  20. package/examples/jsgui3-html/06) theming/server.js +21 -0
  21. package/examples/jsgui3-html/07) mixins/client.js +465 -0
  22. package/examples/jsgui3-html/07) mixins/server.js +21 -0
  23. package/examples/jsgui3-html/08) router/client.js +372 -0
  24. package/examples/jsgui3-html/08) router/server.js +21 -0
  25. package/examples/jsgui3-html/09) resource-transform/client.js +692 -0
  26. package/examples/jsgui3-html/09) resource-transform/server.js +21 -0
  27. package/examples/jsgui3-html/10) binding-debugger/client.js +810 -0
  28. package/examples/jsgui3-html/10) binding-debugger/server.js +21 -0
  29. package/examples/jsgui3-html/README.md +48 -0
  30. package/http/responders/static/Static_Route_HTTP_Responder.js +25 -20
  31. package/lab/README.md +19 -0
  32. package/lab/experiments/window_examples_dom_audit.js +241 -0
  33. package/lab/results/window_examples_dom_audit.json +131 -0
  34. package/lab/results/window_examples_dom_audit.md +46 -0
  35. package/package.json +8 -3
  36. package/publishers/http-webpageorsite-publisher.js +8 -4
  37. package/resources/processors/bundlers/css-bundler.js +28 -173
  38. package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +32 -20
  39. package/resources/processors/bundlers/style-bundler.js +288 -0
  40. package/resources/processors/compilers/SASS_Compiler.js +88 -0
  41. package/resources/processors/extractors/js/css_and_js/AST_Node/CSS_And_JS_From_JS_String_Using_AST_Node_Extractor.js +64 -68
  42. package/resources/website-css-resource.js +24 -20
  43. package/resources/website-javascript-resource-processor.js +17 -57
  44. package/resources/website-javascript-resource.js +17 -57
  45. package/serve-factory.js +38 -24
  46. package/server.js +116 -92
  47. package/tests/README.md +38 -3
  48. package/tests/bundlers.test.js +41 -32
  49. package/tests/content-analysis.test.js +19 -18
  50. package/tests/end-to-end.test.js +336 -365
  51. package/tests/error-handling.test.js +13 -11
  52. package/tests/examples-controls.e2e.test.js +13 -1
  53. package/tests/fixtures/end-to-end-client.js +54 -0
  54. package/tests/fixtures/jsgui3-html/binding_debugger_expectations.json +15 -0
  55. package/tests/fixtures/jsgui3-html/counter_expectations.json +31 -0
  56. package/tests/fixtures/jsgui3-html/data_grid_expectations.json +26 -0
  57. package/tests/fixtures/jsgui3-html/date_transform_expectations.json +26 -0
  58. package/tests/fixtures/jsgui3-html/form_validation_expectations.json +27 -0
  59. package/tests/fixtures/jsgui3-html/master_detail_expectations.json +15 -0
  60. package/tests/fixtures/jsgui3-html/mixins_expectations.json +10 -0
  61. package/tests/fixtures/jsgui3-html/resource_transform_expectations.json +11 -0
  62. package/tests/fixtures/jsgui3-html/router_expectations.json +10 -0
  63. package/tests/fixtures/jsgui3-html/theming_expectations.json +10 -0
  64. package/tests/jsgui3-html-examples.puppeteer.test.js +537 -0
  65. package/tests/sass-controls.e2e.test.js +327 -0
  66. package/tests/test-runner.js +4 -1
  67. package/tests/window-examples.puppeteer.test.js +455 -0
@@ -0,0 +1,810 @@
1
+ const jsgui = require('jsgui3-client');
2
+ const Active_HTML_Document = require('../../../controls/Active_HTML_Document');
3
+ const { BindingDebugTools } = require('jsgui3-html/html-core/BindingDebugger');
4
+ const { Data_Object } = jsgui;
5
+
6
+ class Binding_Debugger_Control extends jsgui.Control {
7
+ constructor(spec = {}) {
8
+ spec.__type_name = spec.__type_name || 'binding_debugger_control';
9
+ super(spec);
10
+
11
+ this.log_counter = 0;
12
+ this.snapshot_primary = null;
13
+
14
+ this.data.model = new Data_Object({
15
+ count: 2,
16
+ step: 1,
17
+ label: 'Signal'
18
+ });
19
+
20
+ this.view.data.model = new Data_Object({
21
+ display_text: '',
22
+ step_text: '',
23
+ status_text: '',
24
+ progress_text: '',
25
+ summary_line: '',
26
+ summary_details: '',
27
+ diff_lines: [],
28
+ activity_log: [],
29
+ debug_enabled: false
30
+ });
31
+
32
+ this.setup_bindings();
33
+ this.setup_computed();
34
+ this.setup_watchers();
35
+ this.init_debugger();
36
+
37
+ if (!spec.el) {
38
+ this.compose();
39
+ }
40
+ }
41
+
42
+ setup_bindings() {
43
+ this.bind({
44
+ count: {
45
+ to: 'display_text',
46
+ transform: (count) => `Count: ${count}`
47
+ },
48
+ step: {
49
+ to: 'step_text',
50
+ transform: (step) => `Step: ${step}`
51
+ }
52
+ });
53
+ }
54
+
55
+ setup_computed() {
56
+ this.computed(
57
+ this.data.model,
58
+ ['count', 'label'],
59
+ (count, label) => {
60
+ const safe_label = label || 'Signal';
61
+ return `${safe_label} status is ${count >= 0 ? 'steady' : 'negative'}.`;
62
+ },
63
+ { propertyName: 'status_text', target: this.view.data.model }
64
+ );
65
+
66
+ this.computed(
67
+ this.data.model,
68
+ ['count', 'step'],
69
+ (count, step) => {
70
+ return `Next move: ${count + step}`;
71
+ },
72
+ { propertyName: 'progress_text', target: this.view.data.model }
73
+ );
74
+ }
75
+
76
+ setup_watchers() {
77
+ this.watch(
78
+ this.data.model,
79
+ 'count',
80
+ (count, old_value) => {
81
+ this.append_log(`count ${old_value} -> ${count}`);
82
+ }
83
+ );
84
+
85
+ this.watch(
86
+ this.data.model,
87
+ 'step',
88
+ (step, old_value) => {
89
+ this.append_log(`step ${old_value} -> ${step}`);
90
+ }
91
+ );
92
+ }
93
+
94
+ init_debugger() {
95
+ this.binding_debugger = BindingDebugTools.getDebugger(this);
96
+ this.refresh_debug_summary();
97
+ }
98
+
99
+ refresh_debug_summary() {
100
+ const summary = this.binding_debugger.getBindingSummary();
101
+ this.view.data.model.summary_line = `Binders: ${summary.totalBinders} | Computed: ${summary.totalComputed} | Watchers: ${summary.totalWatchers}`;
102
+ this.view.data.model.summary_details = JSON.stringify(summary.details, null, 2);
103
+ }
104
+
105
+ set_debug_enabled(enabled) {
106
+ this.view.data.model.debug_enabled = !!enabled;
107
+ if (enabled) {
108
+ this.binding_debugger.enable();
109
+ this.append_log('debugger enabled');
110
+ } else {
111
+ this.binding_debugger.disable();
112
+ this.append_log('debugger disabled');
113
+ }
114
+ }
115
+
116
+ adjust_count(delta) {
117
+ const base_count = Number(this.data.model.count) || 0;
118
+ const step = Number(this.data.model.step) || 1;
119
+ this.data.model.count = base_count + delta * step;
120
+ }
121
+
122
+ append_log(message) {
123
+ this.log_counter += 1;
124
+ const entry = `${this.log_counter}. ${message}`;
125
+ const existing = Array.isArray(this.view.data.model.activity_log)
126
+ ? this.view.data.model.activity_log
127
+ : [];
128
+ this.view.data.model.activity_log = [entry, ...existing].slice(0, 8);
129
+ this.binding_debugger.log(message);
130
+ }
131
+
132
+ take_snapshot() {
133
+ this.snapshot_primary = this.binding_debugger.snapshotModels();
134
+ this.append_log('snapshot captured');
135
+ }
136
+
137
+ compare_snapshot() {
138
+ if (!this.snapshot_primary) {
139
+ this.view.data.model.diff_lines = ['Take a snapshot before comparing.'];
140
+ return;
141
+ }
142
+ const current_snapshot = this.binding_debugger.snapshotModels();
143
+ const diff_items = this.binding_debugger.compareSnapshots(this.snapshot_primary, current_snapshot);
144
+ if (!diff_items.length) {
145
+ this.view.data.model.diff_lines = ['No differences detected.'];
146
+ return;
147
+ }
148
+ this.view.data.model.diff_lines = diff_items.map((diff) => {
149
+ return `${diff.path}: ${diff.oldValue} -> ${diff.newValue}`;
150
+ });
151
+ }
152
+
153
+ render_log_list(list_container, log_entries) {
154
+ list_container.clear();
155
+ log_entries.forEach((entry, index) => {
156
+ const item = new jsgui.Control({
157
+ context: this.context,
158
+ tagName: 'li',
159
+ class: 'log-item',
160
+ content: entry
161
+ });
162
+ item.dom.attributes['data-test'] = `log-entry-${index}`;
163
+ list_container.add(item);
164
+ });
165
+ }
166
+
167
+ render_list_dom(list_el, entries, test_prefix) {
168
+ if (!list_el) return;
169
+ list_el.innerHTML = '';
170
+ entries.forEach((entry, index) => {
171
+ const item_el = document.createElement('li');
172
+ item_el.className = test_prefix === 'diff-entry' ? 'diff-item' : 'log-item';
173
+ item_el.textContent = entry;
174
+ item_el.setAttribute('data-test', `${test_prefix}-${index}`);
175
+ list_el.appendChild(item_el);
176
+ });
177
+ }
178
+
179
+ activate() {
180
+ if (this.__active) return;
181
+ super.activate();
182
+
183
+ if (this._dom_bound) return;
184
+ const root_el = this.dom.el;
185
+ if (!root_el) return;
186
+ this._dom_bound = true;
187
+
188
+ const display_el = root_el.querySelector('[data-test="count-display"]');
189
+ const status_el = root_el.querySelector('[data-test="status-text"]');
190
+ const progress_el = root_el.querySelector('[data-test="progress-text"]');
191
+ const step_input_el = root_el.querySelector('[data-test="step-input"]');
192
+ const label_input_el = root_el.querySelector('[data-test="label-input"]');
193
+ const increment_button_el = root_el.querySelector('[data-test="increment-button"]');
194
+ const decrement_button_el = root_el.querySelector('[data-test="decrement-button"]');
195
+ const summary_line_el = root_el.querySelector('[data-test="summary-line"]');
196
+ const summary_details_el = root_el.querySelector('[data-test="summary-details"]');
197
+ const debug_status_el = root_el.querySelector('[data-test="debug-status"]');
198
+ const enable_button_el = root_el.querySelector('[data-test="enable-debug"]');
199
+ const disable_button_el = root_el.querySelector('[data-test="disable-debug"]');
200
+ const refresh_button_el = root_el.querySelector('[data-test="refresh-summary"]');
201
+ const snapshot_button_el = root_el.querySelector('[data-test="take-snapshot"]');
202
+ const compare_button_el = root_el.querySelector('[data-test="compare-snapshot"]');
203
+ const log_list_el = root_el.querySelector('[data-test="log-list"]');
204
+ const diff_list_el = root_el.querySelector('[data-test="diff-list"]');
205
+
206
+ if (increment_button_el) {
207
+ increment_button_el.addEventListener('click', () => this.adjust_count(1));
208
+ }
209
+
210
+ if (decrement_button_el) {
211
+ decrement_button_el.addEventListener('click', () => this.adjust_count(-1));
212
+ }
213
+
214
+ if (step_input_el) {
215
+ step_input_el.addEventListener('input', (event) => {
216
+ const raw_value = event && event.target ? event.target.value : '1';
217
+ this.data.model.step = Number(raw_value) || 1;
218
+ });
219
+ }
220
+
221
+ if (label_input_el) {
222
+ label_input_el.addEventListener('input', (event) => {
223
+ const raw_value = event && event.target ? event.target.value : '';
224
+ this.data.model.label = raw_value;
225
+ });
226
+ }
227
+
228
+ if (enable_button_el) {
229
+ enable_button_el.addEventListener('click', () => this.set_debug_enabled(true));
230
+ }
231
+
232
+ if (disable_button_el) {
233
+ disable_button_el.addEventListener('click', () => this.set_debug_enabled(false));
234
+ }
235
+
236
+ if (refresh_button_el) {
237
+ refresh_button_el.addEventListener('click', () => this.refresh_debug_summary());
238
+ }
239
+
240
+ if (snapshot_button_el) {
241
+ snapshot_button_el.addEventListener('click', () => this.take_snapshot());
242
+ }
243
+
244
+ if (compare_button_el) {
245
+ compare_button_el.addEventListener('click', () => this.compare_snapshot());
246
+ }
247
+
248
+ this.watch(
249
+ this.view.data.model,
250
+ 'display_text',
251
+ (display_text) => {
252
+ if (display_el) display_el.textContent = display_text || '';
253
+ },
254
+ { immediate: true }
255
+ );
256
+
257
+ this.watch(
258
+ this.view.data.model,
259
+ 'status_text',
260
+ (status_text) => {
261
+ if (status_el) status_el.textContent = status_text || '';
262
+ },
263
+ { immediate: true }
264
+ );
265
+
266
+ this.watch(
267
+ this.view.data.model,
268
+ 'progress_text',
269
+ (progress_text) => {
270
+ if (progress_el) progress_el.textContent = progress_text || '';
271
+ },
272
+ { immediate: true }
273
+ );
274
+
275
+ this.watch(
276
+ this.data.model,
277
+ 'step',
278
+ (step) => {
279
+ if (step_input_el) {
280
+ step_input_el.value = String(Number(step) || 1);
281
+ }
282
+ },
283
+ { immediate: true }
284
+ );
285
+
286
+ this.watch(
287
+ this.data.model,
288
+ 'label',
289
+ (label) => {
290
+ if (label_input_el) {
291
+ label_input_el.value = label || '';
292
+ }
293
+ },
294
+ { immediate: true }
295
+ );
296
+
297
+ this.watch(
298
+ this.view.data.model,
299
+ 'summary_line',
300
+ (summary_line) => {
301
+ if (summary_line_el) summary_line_el.textContent = summary_line || '';
302
+ },
303
+ { immediate: true }
304
+ );
305
+
306
+ this.watch(
307
+ this.view.data.model,
308
+ 'summary_details',
309
+ (summary_details) => {
310
+ if (summary_details_el) summary_details_el.textContent = summary_details || '';
311
+ },
312
+ { immediate: true }
313
+ );
314
+
315
+ this.watch(
316
+ this.view.data.model,
317
+ 'debug_enabled',
318
+ (debug_enabled) => {
319
+ if (debug_status_el) {
320
+ debug_status_el.textContent = debug_enabled ? 'Debugger active' : 'Debugger paused';
321
+ debug_status_el.classList.toggle('is-active', debug_enabled);
322
+ }
323
+ },
324
+ { immediate: true }
325
+ );
326
+
327
+ this.watch(
328
+ this.view.data.model,
329
+ 'activity_log',
330
+ (activity_log) => {
331
+ this.render_list_dom(log_list_el, Array.isArray(activity_log) ? activity_log : [], 'log-entry');
332
+ },
333
+ { immediate: true }
334
+ );
335
+
336
+ this.watch(
337
+ this.view.data.model,
338
+ 'diff_lines',
339
+ (diff_lines) => {
340
+ this.render_list_dom(diff_list_el, Array.isArray(diff_lines) ? diff_lines : [], 'diff-entry');
341
+ },
342
+ { immediate: true }
343
+ );
344
+ }
345
+
346
+ compose() {
347
+ // Framework expects the method name `compose`.
348
+ const page_context = this.context;
349
+
350
+ this.add_class('binding-debugger-control');
351
+ this.dom.attributes['data-test'] = 'binding-debugger-control';
352
+
353
+ const header = new jsgui.Control({
354
+ context: page_context,
355
+ tagName: 'header',
356
+ class: 'bd-header'
357
+ });
358
+
359
+ header.add(new jsgui.Control({
360
+ context: page_context,
361
+ tagName: 'h1',
362
+ class: 'bd-title',
363
+ content: 'Binding Debugger Console'
364
+ }));
365
+ header.add(new jsgui.Control({
366
+ context: page_context,
367
+ tagName: 'p',
368
+ class: 'bd-subtitle',
369
+ content: 'Inspect bindings, computed values, and watch activity in real time.'
370
+ }));
371
+
372
+ const live_panel = new jsgui.Control({
373
+ context: page_context,
374
+ tagName: 'section',
375
+ class: 'bd-panel'
376
+ });
377
+
378
+ const count_display = new jsgui.Control({
379
+ context: page_context,
380
+ tagName: 'div',
381
+ class: 'bd-display',
382
+ content: this.view.data.model.display_text
383
+ });
384
+ count_display.dom.attributes['data-test'] = 'count-display';
385
+
386
+ const status_text = new jsgui.Control({
387
+ context: page_context,
388
+ tagName: 'div',
389
+ class: 'bd-status',
390
+ content: this.view.data.model.status_text
391
+ });
392
+ status_text.dom.attributes['data-test'] = 'status-text';
393
+
394
+ const progress_text = new jsgui.Control({
395
+ context: page_context,
396
+ tagName: 'div',
397
+ class: 'bd-progress',
398
+ content: this.view.data.model.progress_text
399
+ });
400
+ progress_text.dom.attributes['data-test'] = 'progress-text';
401
+
402
+ const controls = new jsgui.Control({
403
+ context: page_context,
404
+ tagName: 'div',
405
+ class: 'bd-controls'
406
+ });
407
+
408
+ const decrement_button = new jsgui.Control({
409
+ context: page_context,
410
+ tagName: 'button',
411
+ class: 'bd-button',
412
+ content: 'Decrease'
413
+ });
414
+ decrement_button.dom.attributes['data-test'] = 'decrement-button';
415
+
416
+ const increment_button = new jsgui.Control({
417
+ context: page_context,
418
+ tagName: 'button',
419
+ class: 'bd-button',
420
+ content: 'Increase'
421
+ });
422
+ increment_button.dom.attributes['data-test'] = 'increment-button';
423
+
424
+ controls.add(decrement_button);
425
+ controls.add(increment_button);
426
+
427
+ const field_grid = new jsgui.Control({
428
+ context: page_context,
429
+ tagName: 'div',
430
+ class: 'bd-fields'
431
+ });
432
+
433
+ const step_field = new jsgui.Control({
434
+ context: page_context,
435
+ tagName: 'label',
436
+ class: 'bd-field'
437
+ });
438
+ step_field.add('Step size');
439
+ const step_input = new jsgui.Control({
440
+ context: page_context,
441
+ tagName: 'input',
442
+ class: 'bd-input'
443
+ });
444
+ step_input.dom.attributes.type = 'number';
445
+ step_input.dom.attributes.min = '1';
446
+ step_input.dom.attributes['data-test'] = 'step-input';
447
+ step_field.add(step_input);
448
+
449
+ const label_field = new jsgui.Control({
450
+ context: page_context,
451
+ tagName: 'label',
452
+ class: 'bd-field'
453
+ });
454
+ label_field.add('Signal label');
455
+ const label_input = new jsgui.Control({
456
+ context: page_context,
457
+ tagName: 'input',
458
+ class: 'bd-input'
459
+ });
460
+ label_input.dom.attributes.type = 'text';
461
+ label_input.dom.attributes['data-test'] = 'label-input';
462
+ label_field.add(label_input);
463
+
464
+ field_grid.add(step_field);
465
+ field_grid.add(label_field);
466
+
467
+ live_panel.add(count_display);
468
+ live_panel.add(status_text);
469
+ live_panel.add(progress_text);
470
+ live_panel.add(controls);
471
+ live_panel.add(field_grid);
472
+
473
+ const debug_panel = new jsgui.Control({
474
+ context: page_context,
475
+ tagName: 'section',
476
+ class: 'bd-panel'
477
+ });
478
+
479
+ const debug_status = new jsgui.Control({
480
+ context: page_context,
481
+ tagName: 'div',
482
+ class: 'bd-debug-status',
483
+ content: 'Debugger paused'
484
+ });
485
+ debug_status.dom.attributes['data-test'] = 'debug-status';
486
+
487
+ const summary_line = new jsgui.Control({
488
+ context: page_context,
489
+ tagName: 'div',
490
+ class: 'bd-summary-line',
491
+ content: this.view.data.model.summary_line
492
+ });
493
+ summary_line.dom.attributes['data-test'] = 'summary-line';
494
+
495
+ const summary_details = new jsgui.Control({
496
+ context: page_context,
497
+ tagName: 'pre',
498
+ class: 'bd-summary-details',
499
+ content: this.view.data.model.summary_details
500
+ });
501
+ summary_details.dom.attributes['data-test'] = 'summary-details';
502
+
503
+ const debug_actions = new jsgui.Control({
504
+ context: page_context,
505
+ tagName: 'div',
506
+ class: 'bd-actions'
507
+ });
508
+
509
+ const enable_button = new jsgui.Control({
510
+ context: page_context,
511
+ tagName: 'button',
512
+ class: 'bd-button is-secondary',
513
+ content: 'Enable'
514
+ });
515
+ enable_button.dom.attributes['data-test'] = 'enable-debug';
516
+
517
+ const disable_button = new jsgui.Control({
518
+ context: page_context,
519
+ tagName: 'button',
520
+ class: 'bd-button is-secondary',
521
+ content: 'Disable'
522
+ });
523
+ disable_button.dom.attributes['data-test'] = 'disable-debug';
524
+
525
+ const refresh_button = new jsgui.Control({
526
+ context: page_context,
527
+ tagName: 'button',
528
+ class: 'bd-button is-secondary',
529
+ content: 'Refresh summary'
530
+ });
531
+ refresh_button.dom.attributes['data-test'] = 'refresh-summary';
532
+
533
+ const snapshot_button = new jsgui.Control({
534
+ context: page_context,
535
+ tagName: 'button',
536
+ class: 'bd-button is-secondary',
537
+ content: 'Take snapshot'
538
+ });
539
+ snapshot_button.dom.attributes['data-test'] = 'take-snapshot';
540
+
541
+ const compare_button = new jsgui.Control({
542
+ context: page_context,
543
+ tagName: 'button',
544
+ class: 'bd-button is-secondary',
545
+ content: 'Compare'
546
+ });
547
+ compare_button.dom.attributes['data-test'] = 'compare-snapshot';
548
+
549
+ debug_actions.add(enable_button);
550
+ debug_actions.add(disable_button);
551
+ debug_actions.add(refresh_button);
552
+ debug_actions.add(snapshot_button);
553
+ debug_actions.add(compare_button);
554
+
555
+ const diff_list = new jsgui.Control({
556
+ context: page_context,
557
+ tagName: 'ul',
558
+ class: 'bd-diff-list'
559
+ });
560
+ diff_list.dom.attributes['data-test'] = 'diff-list';
561
+
562
+ const log_title = new jsgui.Control({
563
+ context: page_context,
564
+ tagName: 'div',
565
+ class: 'bd-log-title',
566
+ content: 'Recent activity'
567
+ });
568
+
569
+ const log_list = new jsgui.Control({
570
+ context: page_context,
571
+ tagName: 'ul',
572
+ class: 'bd-log-list'
573
+ });
574
+ log_list.dom.attributes['data-test'] = 'log-list';
575
+
576
+ this.render_log_list(log_list, this.view.data.model.activity_log);
577
+
578
+ debug_panel.add(debug_status);
579
+ debug_panel.add(summary_line);
580
+ debug_panel.add(summary_details);
581
+ debug_panel.add(debug_actions);
582
+ debug_panel.add(diff_list);
583
+ debug_panel.add(log_title);
584
+ debug_panel.add(log_list);
585
+
586
+ const layout = new jsgui.Control({
587
+ context: page_context,
588
+ tagName: 'div',
589
+ class: 'bd-layout'
590
+ });
591
+
592
+ layout.add(live_panel);
593
+ layout.add(debug_panel);
594
+
595
+ this.add(header);
596
+ this.add(layout);
597
+ }
598
+ }
599
+
600
+ class Demo_UI extends Active_HTML_Document {
601
+ constructor(spec = {}) {
602
+ spec.__type_name = spec.__type_name || 'binding_debugger_demo_ui';
603
+ super(spec);
604
+
605
+ if (!spec.el) {
606
+ this.compose();
607
+ }
608
+ }
609
+
610
+ compose() {
611
+ // Framework expects the method name `compose`.
612
+ const page_context = this.context;
613
+ this.body.add_class('binding-debugger-demo');
614
+
615
+ const main_control = new Binding_Debugger_Control({
616
+ context: page_context
617
+ });
618
+
619
+ this.body.add(main_control);
620
+ }
621
+ }
622
+
623
+ Demo_UI.css = `
624
+ :root {
625
+ --bd-ink: #121217;
626
+ --bd-muted: #5a5a66;
627
+ --bd-accent: #2c6e63;
628
+ --bd-accent-soft: #e0efe9;
629
+ --bd-panel: #ffffff;
630
+ --bd-border: #d8d8de;
631
+ --bd-bg: #f0f1f6;
632
+ }
633
+
634
+ * {
635
+ box-sizing: border-box;
636
+ }
637
+
638
+ body {
639
+ margin: 0;
640
+ padding: 0;
641
+ background: linear-gradient(160deg, #e8f0f5 0%, #eef4f0 45%, #f7f2ea 100%);
642
+ color: var(--bd-ink);
643
+ font-family: "Space Grotesk", "Segoe UI", sans-serif;
644
+ }
645
+
646
+ .binding-debugger-demo {
647
+ min-height: 100vh;
648
+ display: flex;
649
+ align-items: center;
650
+ justify-content: center;
651
+ padding: 40px 24px;
652
+ }
653
+
654
+ .binding-debugger-control {
655
+ width: min(1000px, 100%);
656
+ background: var(--bd-panel);
657
+ border-radius: 26px;
658
+ padding: 32px 36px 40px;
659
+ border: 1px solid var(--bd-border);
660
+ box-shadow: 0 28px 70px rgba(18, 18, 28, 0.16);
661
+ }
662
+
663
+ .bd-header {
664
+ margin-bottom: 24px;
665
+ }
666
+
667
+ .bd-title {
668
+ margin: 0 0 8px;
669
+ font-size: 30px;
670
+ }
671
+
672
+ .bd-subtitle {
673
+ margin: 0;
674
+ color: var(--bd-muted);
675
+ font-size: 15px;
676
+ }
677
+
678
+ .bd-layout {
679
+ display: grid;
680
+ grid-template-columns: 1fr 1fr;
681
+ gap: 20px;
682
+ }
683
+
684
+ .bd-panel {
685
+ background: #fdfdfc;
686
+ border-radius: 18px;
687
+ border: 1px solid #e6e7ec;
688
+ padding: 18px 20px;
689
+ display: grid;
690
+ gap: 12px;
691
+ }
692
+
693
+ .bd-display {
694
+ font-size: 22px;
695
+ font-weight: 600;
696
+ }
697
+
698
+ .bd-status,
699
+ .bd-progress {
700
+ font-size: 14px;
701
+ color: var(--bd-muted);
702
+ }
703
+
704
+ .bd-controls {
705
+ display: flex;
706
+ gap: 10px;
707
+ }
708
+
709
+ .bd-button {
710
+ background: var(--bd-accent);
711
+ color: #fff;
712
+ border: none;
713
+ border-radius: 999px;
714
+ padding: 10px 16px;
715
+ cursor: pointer;
716
+ font-size: 13px;
717
+ font-family: "Space Grotesk", "Segoe UI", sans-serif;
718
+ }
719
+
720
+ .bd-button.is-secondary {
721
+ background: var(--bd-accent-soft);
722
+ color: #1f3d36;
723
+ }
724
+
725
+ .bd-fields {
726
+ display: grid;
727
+ gap: 12px;
728
+ }
729
+
730
+ .bd-field {
731
+ display: grid;
732
+ gap: 6px;
733
+ font-size: 12px;
734
+ text-transform: uppercase;
735
+ letter-spacing: 0.08em;
736
+ }
737
+
738
+ .bd-input {
739
+ padding: 9px 12px;
740
+ border-radius: 10px;
741
+ border: 1px solid #cfd3da;
742
+ font-size: 14px;
743
+ }
744
+
745
+ .bd-debug-status {
746
+ font-size: 13px;
747
+ font-weight: 600;
748
+ color: var(--bd-muted);
749
+ }
750
+
751
+ .bd-debug-status.is-active {
752
+ color: var(--bd-accent);
753
+ }
754
+
755
+ .bd-summary-line {
756
+ font-size: 14px;
757
+ font-weight: 600;
758
+ }
759
+
760
+ .bd-summary-details {
761
+ background: #f4f5f8;
762
+ padding: 12px;
763
+ border-radius: 12px;
764
+ font-size: 12px;
765
+ max-height: 160px;
766
+ overflow: auto;
767
+ }
768
+
769
+ .bd-actions {
770
+ display: flex;
771
+ flex-wrap: wrap;
772
+ gap: 8px;
773
+ }
774
+
775
+ .bd-diff-list,
776
+ .bd-log-list {
777
+ list-style: none;
778
+ padding: 0;
779
+ margin: 0;
780
+ display: grid;
781
+ gap: 6px;
782
+ }
783
+
784
+ .diff-item,
785
+ .log-item {
786
+ background: #f7f8fb;
787
+ border-radius: 10px;
788
+ padding: 8px 10px;
789
+ font-size: 12px;
790
+ }
791
+
792
+ .bd-log-title {
793
+ font-size: 12px;
794
+ text-transform: uppercase;
795
+ letter-spacing: 0.08em;
796
+ color: var(--bd-muted);
797
+ }
798
+
799
+ @media (max-width: 900px) {
800
+ .bd-layout {
801
+ grid-template-columns: 1fr;
802
+ }
803
+ }
804
+ `;
805
+
806
+ jsgui.controls.Binding_Debugger_Control = Binding_Debugger_Control;
807
+ jsgui.controls.Demo_UI = Demo_UI;
808
+ jsgui.controls.binding_debugger_demo_ui = Demo_UI;
809
+
810
+ module.exports = jsgui;