@yuneta/gobj-ui 7.23.170 → 7.23.172

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/src/yui_dev.js CHANGED
@@ -13,6 +13,12 @@ import {
13
13
  kw_get_local_storage_value,
14
14
  kw_set_local_storage_value,
15
15
  gobj_write_attr,
16
+ gobj_command,
17
+ gobj_global_trace_level,
18
+ gobj_global_trace_no_level,
19
+ gobj_get_gclass_trace_level2,
20
+ trace_level_t,
21
+ log_error,
16
22
  gobj_create_service,
17
23
  gobj_find_service,
18
24
  gobj_start,
@@ -21,16 +27,6 @@ import {
21
27
  gobj_set_trace_machine_format,
22
28
  } from "@yuneta/gobj-js";
23
29
 
24
- /* A NAMESPACE import, deliberately, for one optional API.
25
- *
26
- * `set_console_log_filter` arrived in gobj-js 7.13.6, and a NAMED import of
27
- * an export the installed copy does not have is a hard module-load error --
28
- * it would take the whole app down instead of degrading. Read off the
29
- * namespace, a missing export is plain `undefined` and can be tested for. */
30
- import * as gobj_js from "@yuneta/gobj-js";
31
-
32
- import {log_signature, log_is_periodic} from "./dev_machine_trace.js";
33
-
34
30
  import i18next, {t} from 'i18next';
35
31
 
36
32
  /***********************************************************************
@@ -41,11 +37,8 @@ import i18next, {t} from 'i18next';
41
37
  * losing history. Reopening the window repaints the buffer.
42
38
  ***********************************************************************/
43
39
  const TRAFFIC_MAX = 600; // capped history
44
- const PERIODIC_THRESHOLD = 5; // a signature seen >= N times reads as recurring
45
- const PERIODIC_RE = /PERIODIC|TIMEOUT|HEARTBEAT|PING/i;
46
40
 
47
- let TRAFFIC_LOG = []; // [{title,event,command,sig,dir,size,ts,kw,jn,hay,$node}]
48
- let TRAFFIC_COUNTS = new Map(); // signature -> occurrences (for periodic detection)
41
+ let TRAFFIC_LOG = []; // [{title,event,command,dir,size,ts,kw,jn,hay,$node}]
49
42
  let SEARCH_TEXT = ""; // session-only free-text filter (not persisted)
50
43
 
51
44
  /* Running totals of framework errors/warnings seen since page load (or the
@@ -61,8 +54,18 @@ const TRAFFIC_TS_FIELDS = {
61
54
  "from_t": 1, "to_t": 1, "from_tm": 1, "to_tm": 1, "time": 1,
62
55
  };
63
56
 
64
- /* Trace toggles: [localStorage key, display label, handler]. */
65
- /* `data-label` carries the i18n KEY and not the text: the chips are
57
+ /* Trace toggles.
58
+ *
59
+ * A chip does ONE thing: it turns trace bits of the runtime on or off
60
+ * through the yuno's trace commands -- the C kernel's, same names -- and
61
+ * the yuno persists them (`trace_levels` / `no_trace_levels`). Whether a
62
+ * message is traced is decided by the gobj that emits it, never by this
63
+ * window reading messages. The chip's state is READ from the runtime, so
64
+ * it cannot disagree with what is traced.
65
+ *
66
+ * [key, label i18n key, title i18n key, state(), toggle()]
67
+ *
68
+ * `data-label` carries the i18n KEY and not the text: the chips are
66
69
  * repainted from it by `refresh_dev_chrome()` on every toggle, so a
67
70
  * label translated once at build time would come back in English at
68
71
  * the first click. */
@@ -70,36 +73,48 @@ const TRAFFIC_TS_FIELDS = {
70
73
  * `validate-locales`: the table below and the `grp` / `mk_view` /
71
74
  * `mk_expand` / `mk_dir` / `mk_out` helpers pass their key as a
72
75
  * VARIABLE, and `OUT_TITLES` holds three more in a lookup table --
73
- * so they, so a scan of `t("…")`
74
- * sees none of them and reports OK with the whole window in English --
75
- * which is how it shipped in gui_agent and gui_treedb. The full set a
76
- * consumer must define, kept here so it can be copied:
76
+ * so a scan of `t("…")` sees none of them and reports OK with the
77
+ * whole window in English. The full set a consumer must define,
78
+ * kept here so it can be copied:
77
79
  *
78
80
  * automata, both, browser console only, clear,
79
81
  * clear captured traffic, compact, console, copied, copy,
80
82
  * copy visible traffic to clipboard, creation, data, detailed,
81
83
  * dev window and browser console, dev window only, developer,
82
- * errors, expand, expanded, filter events / payload, find,
83
- * hide the timers and the recurring traffic, i18n, incoming, log,
84
- * machine trace shape, metadata, mute this message, muted,
85
- * name only, no poll, outgoing, output, periodic, schema, show,
84
+ * errors, expand, expanded, filter events / payload, find, i18n,
85
+ * i18next debug output, incoming, log, machine trace shape,
86
+ * metadata, name only, outgoing, output, periodic, schema, show,
86
87
  * show this section in the expanded view, simple mach,
87
- * start / stop, subscriptions, traces, traffic, unmute, view,
88
- * window, yuno monitor
88
+ * start / stop, subscriptions,
89
+ * trace every event of every automaton,
90
+ * trace subscriptions and publications,
91
+ * trace the creation and destruction of gobjs,
92
+ * trace the messages to and from the backend,
93
+ * trace the periodic timer event,
94
+ * trace the start and stop of gobjs,
95
+ * traces, traffic, view, window, yuno monitor
89
96
  *
90
97
  * wattyzer and the yunovatios GUIs carry them; check a new consumer
91
98
  * against this list, and confirm it by DUMPING the window. */
92
99
  const TRACE_DEFS = [
93
- ["trace_automata", "automata", trace_automata],
94
- ["trace_creation", "creation", trace_creation],
95
- ["trace_start_stop", "start / stop", trace_start_stop],
96
- ["trace_subscriptions", "subscriptions", trace_subscriptions],
97
- ["trace_i18n", "i18n", trace_i18n],
98
- ["trace_traffic", "traffic", trace_traffic],
99
- ["no_poll", "no poll", set_no_poll],
100
+ ["automata", "automata", "trace every event of every automaton",
101
+ automata_state, toggle_automata],
102
+ ["creation", "creation", "trace the creation and destruction of gobjs",
103
+ () => global_bit(trace_level_t.TRACE_CREATE_DELETE), () => toggle_global("create_delete")],
104
+ ["start_stop", "start / stop", "trace the start and stop of gobjs",
105
+ () => global_bit(trace_level_t.TRACE_START_STOP), () => toggle_global("start_stop")],
106
+ ["subscriptions", "subscriptions", "trace subscriptions and publications",
107
+ () => global_bit(trace_level_t.TRACE_SUBSCRIPTIONS), () => toggle_global("subscriptions")],
108
+ ["i18n", "i18n", "i18next debug output",
109
+ i18n_state, toggle_i18n],
110
+ ["traffic", "traffic", "trace the messages to and from the backend",
111
+ traffic_state, toggle_traffic],
112
+ ["periodic", "periodic", "trace the periodic timer event",
113
+ periodic_state, toggle_periodic],
100
114
  ];
101
115
 
102
116
 
117
+
103
118
  /******************************
104
119
  * Small helpers
105
120
  ******************************/
@@ -201,17 +216,6 @@ function dev_view()
201
216
  return (v === "compact" || v === "name" || v === "full") ? v : "detailed";
202
217
  }
203
218
 
204
- /* ON by default. The machine trace is the reason this window
205
- * exists, and a yuno with one timer emits two lines a second for
206
- * ever: leaving the filter off meant the first thing anybody saw
207
- * after enabling Automata was a wall of EV_TIMEOUT with their own
208
- * transitions somewhere inside it. The chip turns it back off.
209
- *
210
- * A CONSTANT, and not the literal in two places: the reader and the
211
- * toggle must agree on what "unset" means, or the first click on the
212
- * chip writes the value it already had and appears to do nothing. */
213
- const HIDE_PERIODIC_DEFAULT = 1;
214
-
215
219
  /* Which SHAPE the machine trace is written in — the two the C kernel
216
220
  * offers, and the chip that swaps them:
217
221
  *
@@ -220,7 +224,7 @@ const HIDE_PERIODIC_DEFAULT = 1;
220
224
  * change, and the `<- … ret: N` return
221
225
  *
222
226
  * 1 because that is what the C kernel defaults to, so a browser yuno and
223
- * a node read alike. Like HIDE_PERIODIC_DEFAULT, a CONSTANT and not a
227
+ * a node read alike. A CONSTANT and not a
224
228
  * literal in each of the three places that read it (the chip, the chrome
225
229
  * and start up): they have to agree on what "unset" means, or the chip
226
230
  * is born showing the wrong state and its first click does nothing. */
@@ -241,11 +245,6 @@ function dev_simple_mach()
241
245
  return dev_num(SIMPLE_MACH_KEY, SIMPLE_MACH_DEFAULT) ? 1 : 0;
242
246
  }
243
247
 
244
- function dev_hide_periodic()
245
- {
246
- return dev_num("dev_hide_periodic", HIDE_PERIODIC_DEFAULT) ? true : false;
247
- }
248
-
249
248
  /* Where dev output (traffic + all logs + automata) is routed:
250
249
  * "window" → dev window only (browser console silenced framework-wide)
251
250
  * "console" → browser console only (nothing appended to the window)
@@ -259,46 +258,12 @@ function dev_route()
259
258
  }
260
259
 
261
260
  /* Push the current route's console decision down to gobj-js. Called from
262
- * apply_dev_traces (startup), whenever the Output selector changes, and
263
- * whenever a filter that the console also obeys is toggled. */
261
+ * apply_dev_traces (startup) and whenever the Output selector changes. */
264
262
  function apply_console_route()
265
263
  {
266
264
  set_console_log_enabled(dev_route() !== "window");
267
-
268
- /* ONE rule, TWO sinks.
269
- *
270
- * The window's Periodic filter used to end at the window, so with the
271
- * Output on "Both" the timers were gone from the panel and still
272
- * arriving in the browser console, two lines a second — the same flood,
273
- * one pane over. It cannot be fixed from this side alone: gobj-js writes
274
- * the console line BEFORE it calls the log sink, so nothing here can
275
- * un-print it. It hands out a say instead (`set_console_log_filter`,
276
- * gobj-js >= 7.13.6), and this is the same predicate `entry_hidden()`
277
- * uses, so the two panes cannot disagree about what is noise.
278
- *
279
- * Feature-detected rather than required: against an older gobj-js the
280
- * window still filters and the console still floods, exactly as before,
281
- * and the reason is logged once instead of failing. */
282
- if(typeof gobj_js.set_console_log_filter === "function") {
283
- gobj_js.set_console_log_filter((level, msg) => {
284
- if(!dev_hide_periodic()) {
285
- return true;
286
- }
287
- return !log_is_periodic(level, is_string(msg) ? msg : String(msg));
288
- });
289
- } else if(!__console_filter_warned__) {
290
- __console_filter_warned__ = true;
291
- console.warn(
292
- "yui_dev: gobj-js has no set_console_log_filter (needs >= 7.13.6): " +
293
- "the Periodic filter applies to this window only, not to the console"
294
- );
295
- }
296
265
  }
297
266
 
298
- /* The warning above is worth saying, and worth saying ONCE: this runs on
299
- * every Output change and every filter toggle. */
300
- let __console_filter_warned__ = false;
301
-
302
267
  /* Emit one inter-event traffic line to the browser console (routes
303
268
  * "console" and "both"). gobj-js only knows about framework logs, not this
304
269
  * traffic feed, so the console mirror is produced here. */
@@ -315,20 +280,6 @@ function console_traffic(title, jn, direction, size)
315
280
  );
316
281
  }
317
282
 
318
- function dev_muted()
319
- {
320
- let a = kw_get_local_storage_value("dev_muted_events", [], false);
321
- if(!Array.isArray(a)) {
322
- a = [];
323
- }
324
- return new Set(a);
325
- }
326
-
327
- function dev_set_muted(set)
328
- {
329
- kw_set_local_storage_value("dev_muted_events", Array.from(set));
330
- }
331
-
332
283
  function set_view(v)
333
284
  {
334
285
  kw_set_local_storage_value("dev_view_mode", v);
@@ -351,64 +302,21 @@ function toggle_pref(key, def)
351
302
  {
352
303
  let v = dev_num(key, def) ? 0 : 1;
353
304
  kw_set_local_storage_value(key, v);
354
- /* The console filter closes over dev_hide_periodic(), which reads the
355
- * stored value on each call, so it follows this on its own. Re-pushed
356
- * anyway: a filter installed before this module reached apply_dev_traces
357
- * is the difference between the console obeying the chip and ignoring
358
- * it, and one assignment is cheaper than that class of bug. */
359
- apply_console_route();
360
- rerender_all();
361
- refresh_dev_chrome();
362
- }
363
-
364
- function mute_signature(sig)
365
- {
366
- let set = dev_muted();
367
- set.add(sig);
368
- dev_set_muted(set);
369
- rerender_all();
370
- refresh_dev_chrome();
371
- }
372
-
373
- function unmute_signature(sig)
374
- {
375
- let set = dev_muted();
376
- set.delete(sig);
377
- dev_set_muted(set);
378
305
  rerender_all();
379
306
  refresh_dev_chrome();
380
307
  }
381
308
 
382
-
383
309
  /******************************
384
310
  * Filtering
385
311
  ******************************/
386
312
 
387
313
 
388
- /* Signature identifies a "kind" of message. Command answers share
389
- * the generic EV_MT_COMMAND event, so fold the command in to tell
390
- * a get-stats poll apart from a user action. */
391
- function traffic_signature(event, command)
392
- {
393
- return command ? (event + " · " + command) : event;
394
- }
395
-
396
- function traffic_is_periodic(sig)
397
- {
398
- if(PERIODIC_RE.test(sig)) {
399
- return true;
400
- }
401
- return (TRAFFIC_COUNTS.get(sig) || 0) >= PERIODIC_THRESHOLD;
402
- }
403
-
404
314
  function build_filter_ctx()
405
315
  {
406
316
  return {
407
317
  out: dev_num("dev_filter_out", 1),
408
318
  inc: dev_num("dev_filter_in", 1),
409
319
  err: dev_num("dev_filter_err", 1),
410
- muted: dev_muted(),
411
- hide_periodic: dev_hide_periodic(),
412
320
  search: SEARCH_TEXT,
413
321
  };
414
322
  }
@@ -418,20 +326,9 @@ function entry_hidden(e, ctx)
418
326
  if(e.kind === "log") {
419
327
  /* Mirrored console logs respect the search box only — not the
420
328
  * in/out/err traffic filters, which are about DIRECTION and a
421
- * log has none.
422
- *
423
- * The PERIODIC filter does reach them, and it has to: once
424
- * Automata is on, the machine trace is what floods this
425
- * window, and every one of those lines arrives here as a log.
426
- * It applies to a machine TRANSITION only, and never above
427
- * debug level — see log_is_periodic(). By NAME and not by
428
- * count, unlike the traffic half below: a busy FSM crosses
429
- * the recurrence threshold on almost every event within
430
- * seconds, so counting here would empty the window of the
431
- * very transitions somebody turned Automata on to read. */
432
- if(ctx.hide_periodic && e.periodic) {
433
- return true;
434
- }
329
+ * log has none. What is traced at all is the runtime's trace
330
+ * levels, set by the chips: this window never reads a message
331
+ * to decide whether to show it. */
435
332
  return !!(ctx.search && e.hay.indexOf(ctx.search) < 0);
436
333
  }
437
334
  if(e.dir === 1 && !ctx.out) {
@@ -443,12 +340,6 @@ function entry_hidden(e, ctx)
443
340
  if(e.dir === 3 && !ctx.err) {
444
341
  return true;
445
342
  }
446
- if(ctx.muted.has(e.sig)) {
447
- return true;
448
- }
449
- if(ctx.hide_periodic && traffic_is_periodic(e.sig)) {
450
- return true;
451
- }
452
343
  if(ctx.search && e.hay.indexOf(ctx.search) < 0) {
453
344
  return true;
454
345
  }
@@ -481,11 +372,6 @@ function ensure_dev_style()
481
372
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
482
373
  }
483
374
  .YDEV_LOG { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 6px 10px; }
484
- .YDEV_MUTED {
485
- display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
486
- padding: 4px 10px; border-bottom: 1px solid rgba(0,0,0,0.08); font-size: 12px;
487
- }
488
- .YDEV_MUTED:empty { display: none; }
489
375
  .YDEV_STATS {
490
376
  flex: 0 0 auto; display: flex; flex-wrap: wrap; gap: 14px;
491
377
  padding: 6px 10px; border-top: 1px solid rgba(0,0,0,0.1);
@@ -517,7 +403,6 @@ function ensure_dev_style()
517
403
  .YDEV_CHIP.s-in.is-active { background: rgba(5,150,105,0.16); border-color: #059669; color: #059669; }
518
404
  .YDEV_CHIP.s-err.is-active { background: rgba(220,38,38,0.16); border-color: #dc2626; color: #dc2626; }
519
405
  .YDEV_CHIP[data-dir]:not(.is-active) { opacity: 0.4; text-decoration: line-through; }
520
- .YDEV_CHIP[data-toggle="periodic"].is-active { background: rgba(217,119,6,0.16); border-color: #d97706; color: #b45309; font-weight: 600; }
521
406
  .YDEV_SEG { display: inline-flex; border: 1px solid rgba(0,0,0,0.18); border-radius: 7px; overflow: hidden; }
522
407
  .YDEV_SEG_BTN {
523
408
  font: inherit; font-size: 12px; padding: 4px 10px; border: 0;
@@ -531,12 +416,6 @@ function ensure_dev_style()
531
416
  border: 1px solid rgba(0,0,0,0.18); border-radius: 7px;
532
417
  background: transparent; color: inherit;
533
418
  }
534
- .YDEV_MUTED_CHIP {
535
- font: inherit; font-family: "DejaVu Sans Mono", monospace; font-size: 12px;
536
- display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px;
537
- border: 1px solid rgba(217,119,6,0.5); border-radius: 999px;
538
- background: rgba(217,119,6,0.12); color: #b45309; cursor: pointer;
539
- }
540
419
  .YDEV_STAT.s-out { color: #2563eb; } .YDEV_STAT.s-in { color: #059669; } .YDEV_STAT.s-err { color: #dc2626; }
541
420
  .YDEV_STAT.s-warn { color: #d97706; }
542
421
  .YDEV_STAT.is-strong { font-weight: 700; }
@@ -564,10 +443,7 @@ function ensure_dev_style()
564
443
  .dir-err .TRAFFIC_ARROW, .dir-err .TRAFFIC_EVENT { color: #dc2626; }
565
444
  .TRAFFIC_SUMMARY { opacity: 0.6; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1 1 auto; min-width: 0; }
566
445
  .TRAFFIC_META { margin-left: auto; opacity: 0.6; font-size: 11px; white-space: nowrap; }
567
- .TRAFFIC_MUTE { border: 0; background: transparent; color: inherit; cursor: pointer; opacity: 0; font-size: 12px; padding: 0 2px; flex: 0 0 auto; }
568
- .TRAFFIC_ENTRY:hover .TRAFFIC_MUTE, .TRAFFIC_LINE:hover .TRAFFIC_MUTE, .TRAFFIC_NAME:hover .TRAFFIC_MUTE { opacity: 0.5; }
569
- .TRAFFIC_MUTE:hover { opacity: 1 !important; color: #d97706; }
570
- .TRAFFIC_KW { margin: 2px 0 0 16px; }
446
+ .TRAFFIC_ENTRY:hover .TRAFFIC_KW { margin: 2px 0 0 16px; }
571
447
  .TRAFFIC_FULL { margin: 4px 0 0 16px; padding: 6px 8px; font-family: monospace; font-size: 11px; line-height: 1.4; white-space: pre-wrap; word-break: break-word; background: rgba(0,0,0,0.04); border-radius: 4px; overflow-x: auto; }
572
448
  .TRAFFIC_ROW { display: flex; gap: 6px; align-items: baseline; }
573
449
  .TRAFFIC_BULLET { opacity: 0.45; flex: 0 0 auto; }
@@ -611,9 +487,7 @@ details.TRAFFIC_NEST > summary::-webkit-details-marker { display: none; }
611
487
  :root[data-theme="dark"] .TRAFFIC_VAL.t-bool, :root[data-theme="dark"] .TRAFFIC_VAL.t-null { color: #c084fc; }
612
488
  :root[data-theme="dark"] .YDEV_CHIP.is-active { background: rgba(96,165,250,0.2); border-color: #60a5fa; color: #93c5fd; }
613
489
  :root[data-theme="dark"] .YDEV_SEG_BTN.is-active { background: #2563eb; color: #fff; }
614
- :root[data-theme="dark"] .YDEV_CHIP[data-toggle="periodic"].is-active { background: rgba(217,119,6,0.24); border-color: #f59e0b; color: #fbbf24; }
615
- :root[data-theme="dark"] .YDEV_MUTED_CHIP { border-color: rgba(245,158,11,0.5); background: rgba(245,158,11,0.16); color: #fbbf24; }
616
- `;
490
+ :root[data-theme="dark"] `;
617
491
  let $style = document.createElement('style');
618
492
  $style.id = 'yui-dev-style';
619
493
  $style.textContent = css;
@@ -727,21 +601,6 @@ function traffic_bullets(obj)
727
601
  ******************************/
728
602
 
729
603
 
730
- /* A small mute affordance that silences this signature (persistent). */
731
- function mute_button(sig)
732
- {
733
- /* `t('mute this message')` and not `'Mute ' + sig`: a composed
734
- * title is no i18n key, and the row already says which message. */
735
- return ['button', {class: 'TRAFFIC_MUTE', type: 'button',
736
- title: t('mute this message'), 'data-i18n-title': 'mute this message'}, '⊘', {
737
- click: (ev) => {
738
- ev.stopPropagation();
739
- ev.preventDefault();
740
- mute_signature(sig);
741
- }
742
- }];
743
- }
744
-
745
604
  function event_spans(e)
746
605
  {
747
606
  let spans = [['span', {class: 'TRAFFIC_ARROW'}, dir_arrow(e.dir)],
@@ -786,7 +645,6 @@ function compact_summary(kw)
786
645
  function render_detailed(e)
787
646
  {
788
647
  let head = event_spans(e);
789
- head.push(mute_button(e.sig));
790
648
  head.push(['span', {class: 'TRAFFIC_META'}, `${traffic_size(e.size)} · ${e.ts}`]);
791
649
 
792
650
  let children = [['div', {class: 'TRAFFIC_HEADER'}, head]];
@@ -843,7 +701,6 @@ function full_sections(payload)
843
701
  function render_full(e)
844
702
  {
845
703
  let head = event_spans(e);
846
- head.push(mute_button(e.sig));
847
704
  head.push(['span', {class: 'TRAFFIC_META'}, `${traffic_size(e.size)} · ${e.ts}`]);
848
705
 
849
706
  let payload = full_sections(e.kw ? e.kw : e.jn);
@@ -864,7 +721,6 @@ function render_compact(e)
864
721
  {
865
722
  let kids = event_spans(e);
866
723
  kids.push(['span', {class: 'TRAFFIC_SUMMARY'}, compact_summary(e.kw)]);
867
- kids.push(mute_button(e.sig));
868
724
  kids.push(['span', {class: 'TRAFFIC_META'}, `${traffic_size(e.size)} · ${e.ts}`]);
869
725
  return createElement2(['div', {class: 'TRAFFIC_LINE ' + dir_class(e.dir), title: e.title}, kids]);
870
726
  }
@@ -872,7 +728,6 @@ function render_compact(e)
872
728
  function render_name(e)
873
729
  {
874
730
  let kids = event_spans(e);
875
- kids.push(mute_button(e.sig));
876
731
  kids.push(['span', {class: 'TRAFFIC_META'}, e.ts]);
877
732
  return createElement2(['div', {class: 'TRAFFIC_NAME ' + dir_class(e.dir), title: e.title}, kids]);
878
733
  }
@@ -932,7 +787,6 @@ function render_entry(e)
932
787
  function clear_traffic()
933
788
  {
934
789
  TRAFFIC_LOG.length = 0;
935
- TRAFFIC_COUNTS.clear();
936
790
  LOG_ERR_COUNT = 0;
937
791
  LOG_WARN_COUNT = 0;
938
792
  let logger = document.getElementById('developer-traffic-logger');
@@ -1116,7 +970,6 @@ function info_traffic(title, msg, direction, size)
1116
970
  let event = (jn && jn.event) ? String(jn.event) : "(no event)";
1117
971
  let kw = (jn && jn.kw && typeof jn.kw === "object") ? jn.kw : null;
1118
972
  let command = (kw && typeof kw.command === "string") ? kw.command : "";
1119
- let sig = traffic_signature(event, command);
1120
973
 
1121
974
  let hay = "";
1122
975
  try {
@@ -1126,35 +979,21 @@ function info_traffic(title, msg, direction, size)
1126
979
  }
1127
980
 
1128
981
  let entry = {
1129
- title: title || "", event: event, command: command, sig: sig,
982
+ title: title || "", event: event, command: command,
1130
983
  dir: direction, size: size, ts: traffic_now(),
1131
984
  kw: kw, jn: jn, hay: hay, $node: null,
1132
985
  };
1133
986
 
1134
987
  TRAFFIC_LOG.push(entry);
1135
- TRAFFIC_COUNTS.set(sig, (TRAFFIC_COUNTS.get(sig) || 0) + 1);
1136
-
1137
- /* When a signature just crosses the "recurring" threshold and
1138
- * the periodic filter is on, its earlier entries must disappear
1139
- * too — a full repaint is the correct, simple answer. */
1140
- let crossed = dev_hide_periodic() && (TRAFFIC_COUNTS.get(sig) === PERIODIC_THRESHOLD);
1141
988
 
1142
989
  if(TRAFFIC_LOG.length > TRAFFIC_MAX) {
1143
990
  let old = TRAFFIC_LOG.shift();
1144
- let c = (TRAFFIC_COUNTS.get(old.sig) || 0) - 1;
1145
- if(c <= 0) {
1146
- TRAFFIC_COUNTS.delete(old.sig);
1147
- } else {
1148
- TRAFFIC_COUNTS.set(old.sig, c);
1149
- }
1150
991
  if(old.$node && old.$node.parentNode) {
1151
992
  old.$node.parentNode.removeChild(old.$node);
1152
993
  }
1153
994
  }
1154
995
 
1155
- if(crossed) {
1156
- rerender_all();
1157
- } else if(!entry_hidden(entry, build_filter_ctx())) {
996
+ if(!entry_hidden(entry, build_filter_ctx())) {
1158
997
  let node = render_entry(entry);
1159
998
  entry.$node = node;
1160
999
  /* Drop the "no traffic yet" placeholder before the first row. */
@@ -1221,28 +1060,14 @@ function info_log(level, msg, hora)
1221
1060
  } else {
1222
1061
  text = is_string(msg) ? msg : String(msg);
1223
1062
  }
1224
- /* A machine line is signed by its EVENT, not by "log:debug":
1225
- * that is what makes a hundred EV_TIMEOUTs ONE recurring
1226
- * thing the filter and the counter can see, instead of a
1227
- * hundred anonymous debug lines they cannot tell apart. */
1228
1063
  let entry = {
1229
1064
  kind: "log", level: lvl, text: text,
1230
1065
  dir: 0, size: 0, ts: traffic_now(),
1231
- sig: log_signature(lvl, text),
1232
- periodic: log_is_periodic(lvl, text),
1233
1066
  hay: (lvl + " " + text).toLowerCase(), $node: null,
1234
1067
  };
1235
1068
  TRAFFIC_LOG.push(entry);
1236
1069
  if(TRAFFIC_LOG.length > TRAFFIC_MAX) {
1237
1070
  let old = TRAFFIC_LOG.shift();
1238
- if(old.kind !== "log") {
1239
- let c = (TRAFFIC_COUNTS.get(old.sig) || 0) - 1;
1240
- if(c <= 0) {
1241
- TRAFFIC_COUNTS.delete(old.sig);
1242
- } else {
1243
- TRAFFIC_COUNTS.set(old.sig, c);
1244
- }
1245
- }
1246
1071
  if(old.$node && old.$node.parentNode) {
1247
1072
  old.$node.parentNode.removeChild(old.$node);
1248
1073
  }
@@ -1270,78 +1095,99 @@ function info_log(level, msg, hora)
1270
1095
  ******************************/
1271
1096
 
1272
1097
 
1273
- function trace_traffic()
1098
+ /* Send one of the yuno's trace commands; a refusal is said. */
1099
+ function yuno_trace_command(command, kw)
1274
1100
  {
1275
- let v = Number(kw_get_local_storage_value("trace_traffic"));
1276
- if(v) {
1277
- gobj_write_attr(gobj_yuno(), "trace_inter_event", false);
1278
- v = 0;
1279
- } else {
1280
- gobj_write_attr(gobj_yuno(), "trace_inter_event", true);
1281
- gobj_write_attr(gobj_yuno(), "trace_ievent_callback", info_traffic);
1282
- v = 1;
1101
+ let r = gobj_command(gobj_yuno(), command, kw, gobj_yuno());
1102
+ if(!r || r.result < 0) {
1103
+ log_error(`yui_dev: ${command} ${JSON.stringify(kw)}: ${r ? r.comment : "no answer"}`);
1283
1104
  }
1284
- kw_set_local_storage_value("trace_traffic", v);
1285
1105
  refresh_dev_chrome();
1286
1106
  }
1287
1107
 
1288
- function trace_automata()
1108
+ function global_bit(bit)
1289
1109
  {
1290
- let v = Number(kw_get_local_storage_value("trace_automata"));
1110
+ return (gobj_global_trace_level() & bit) ? 1 : 0;
1111
+ }
1112
+
1113
+ const GLOBAL_TRACE_BITS = {
1114
+ "machine": trace_level_t.TRACE_MACHINE,
1115
+ "ev_kw": trace_level_t.TRACE_EV_KW,
1116
+ "create_delete": trace_level_t.TRACE_CREATE_DELETE,
1117
+ "start_stop": trace_level_t.TRACE_START_STOP,
1118
+ "subscriptions": trace_level_t.TRACE_SUBSCRIPTIONS,
1119
+ };
1120
+
1121
+ function toggle_global(level)
1122
+ {
1123
+ yuno_trace_command("set-global-trace",
1124
+ {level: level, set: global_bit(GLOBAL_TRACE_BITS[level]) ? 0 : 1});
1125
+ }
1126
+
1127
+ /* Automata cycles 0 → 1 (machine) → 2 (machine + the kw of each event) → 0. */
1128
+ function automata_state()
1129
+ {
1130
+ if(!global_bit(trace_level_t.TRACE_MACHINE)) {
1131
+ return 0;
1132
+ }
1133
+ return global_bit(trace_level_t.TRACE_EV_KW) ? 2 : 1;
1134
+ }
1135
+
1136
+ function toggle_automata()
1137
+ {
1138
+ let v = automata_state();
1291
1139
  if(v === 0) {
1292
- v = 1;
1140
+ yuno_trace_command("set-global-trace", {level: "machine", set: 1});
1293
1141
  } else if(v === 1) {
1294
- v = 2;
1142
+ yuno_trace_command("set-global-trace", {level: "ev_kw", set: 1});
1295
1143
  } else {
1296
- v = 0;
1144
+ yuno_trace_command("set-global-trace", {level: "ev_kw", set: 0});
1145
+ yuno_trace_command("set-global-trace", {level: "machine", set: 0});
1297
1146
  }
1298
- gobj_write_attr(gobj_yuno(), "tracing", v);
1299
- kw_set_local_storage_value("trace_automata", v);
1300
- refresh_dev_chrome();
1301
1147
  }
1302
1148
 
1303
- function trace_creation()
1149
+ /* Traffic is C_IEVENT_CLI's own level, as `set-gclass-trace
1150
+ * gclass=C_IEVENT_CLI level=ievents` on a node. */
1151
+ function traffic_state()
1304
1152
  {
1305
- let v = Number(kw_get_local_storage_value("trace_creation"));
1306
- v = v === 0 ? 1 : 0;
1307
- gobj_write_attr(gobj_yuno(), "trace_creation", v);
1308
- kw_set_local_storage_value("trace_creation", v);
1309
- refresh_dev_chrome();
1153
+ return gobj_get_gclass_trace_level2("C_IEVENT_CLI").includes("ievents") ? 1 : 0;
1310
1154
  }
1311
1155
 
1312
- function trace_start_stop()
1156
+ function toggle_traffic()
1313
1157
  {
1314
- let v = Number(kw_get_local_storage_value("trace_start_stop"));
1315
- v = v === 0 ? 1 : 0;
1316
- gobj_write_attr(gobj_yuno(), "trace_start_stop", v);
1317
- kw_set_local_storage_value("trace_start_stop", v);
1318
- refresh_dev_chrome();
1158
+ yuno_trace_command("set-gclass-trace",
1159
+ {gclass_name: "C_IEVENT_CLI", level: "ievents", set: traffic_state() ? 0 : 1});
1319
1160
  }
1320
1161
 
1321
- function trace_subscriptions()
1162
+ /* Periodic is EV_TIMEOUT_PERIODIC and nothing else: the global level
1163
+ * `timer_periodic`. main() silences it with the NO-trace of the same
1164
+ * level, which wins over the trace, so the chip moves both. */
1165
+ function periodic_state()
1322
1166
  {
1323
- let v = Number(kw_get_local_storage_value("trace_subscriptions"));
1324
- v = v === 0 ? 1 : 0;
1325
- gobj_write_attr(gobj_yuno(), "trace_subscriptions", v);
1326
- kw_set_local_storage_value("trace_subscriptions", v);
1327
- refresh_dev_chrome();
1167
+ let on = gobj_global_trace_level() & trace_level_t.TRACE_TIMER_PERIODIC;
1168
+ let off = gobj_global_trace_no_level() & trace_level_t.TRACE_TIMER_PERIODIC;
1169
+ return (on && !off) ? 1 : 0;
1328
1170
  }
1329
1171
 
1330
- function trace_i18n()
1172
+ function toggle_periodic()
1331
1173
  {
1332
- let v = Number(kw_get_local_storage_value("trace_i18n"));
1333
- v = v === 0 ? 1 : 0;
1334
- i18next.options.debug = v ? true : false;
1335
- kw_set_local_storage_value("trace_i18n", v);
1336
- refresh_dev_chrome();
1174
+ let v = periodic_state() ? 0 : 1;
1175
+ yuno_trace_command("set-global-trace", {level: "timer_periodic", set: v});
1176
+ yuno_trace_command("set-global-no-trace", {level: "timer_periodic", set: v ? 0 : 1});
1337
1177
  }
1338
1178
 
1339
- function set_no_poll()
1179
+ /* I18n is no trace of the runtime: i18next's own debug switch, kept in
1180
+ * the browser as it always was. */
1181
+ function i18n_state()
1340
1182
  {
1341
- let v = Number(kw_get_local_storage_value("no_poll"));
1342
- v = v ? 0 : 1;
1343
- gobj_write_attr(gobj_yuno(), "no_poll", v);
1344
- kw_set_local_storage_value("no_poll", v);
1183
+ return Number(kw_get_local_storage_value("trace_i18n", 0, false)) ? 1 : 0;
1184
+ }
1185
+
1186
+ function toggle_i18n()
1187
+ {
1188
+ let v = i18n_state() ? 0 : 1;
1189
+ i18next.options.debug = v ? true : false;
1190
+ kw_set_local_storage_value("trace_i18n", v);
1345
1191
  refresh_dev_chrome();
1346
1192
  }
1347
1193
 
@@ -1351,16 +1197,17 @@ function set_no_poll()
1351
1197
  ******************************/
1352
1198
 
1353
1199
 
1354
- /* Sync every control's visual state from persisted prefs, plus the
1355
- * muted-events row and the stats strip. Idempotent; null-guarded so
1200
+ /* Sync every control's visual state -- the trace chips from the
1201
+ * runtime, the rest from persisted prefs -- plus the stats strip. Idempotent; null-guarded so
1356
1202
  * it is safe to call whether or not the window is mounted. */
1357
1203
  function refresh_dev_chrome()
1358
1204
  {
1359
1205
  document.querySelectorAll('.YDEV_CHIP[data-trace]').forEach(($b) => {
1360
1206
  let key = $b.getAttribute('data-trace');
1207
+ let def = TRACE_DEFS.find((d) => d[0] === key);
1361
1208
  let label = t($b.getAttribute('data-label') || '');
1362
- let v = dev_num(key, 0);
1363
- $b.textContent = (key === "trace_automata" && v > 0) ? (label + " " + v) : label;
1209
+ let v = def ? def[3]() : 0;
1210
+ $b.textContent = (key === "automata" && v > 0) ? (label + " " + v) : label;
1364
1211
  $b.classList.toggle('is-active', v > 0);
1365
1212
  });
1366
1213
 
@@ -1388,39 +1235,10 @@ function refresh_dev_chrome()
1388
1235
  $b.classList.toggle('is-active', !!dev_num($b.getAttribute('data-dir'), 1));
1389
1236
  });
1390
1237
 
1391
- document.querySelectorAll('.YDEV_CHIP[data-toggle="periodic"]').forEach(($b) => {
1392
- $b.classList.toggle('is-active', dev_hide_periodic());
1393
- });
1394
-
1395
1238
  document.querySelectorAll('.YDEV_CHIP[data-toggle="automata-simple"]').forEach(($b) => {
1396
1239
  $b.classList.toggle('is-active', !!dev_simple_mach());
1397
1240
  });
1398
1241
 
1399
- let $m = document.getElementById('ydev-muted');
1400
- if($m) {
1401
- $m.replaceChildren();
1402
- let set = dev_muted();
1403
- if(set.size) {
1404
- let $lbl = document.createElement('span');
1405
- $lbl.className = 'YDEV_LABEL';
1406
- $lbl.textContent = t('muted');
1407
- $lbl.setAttribute('data-i18n', 'muted');
1408
- $m.appendChild($lbl);
1409
- set.forEach((sig) => {
1410
- $m.appendChild(createElement2(
1411
- ['button', {class: 'YDEV_MUTED_CHIP', type: 'button',
1412
- title: t('unmute'), 'data-i18n-title': 'unmute'},
1413
- '⊘ ' + sig + ' ✕', {
1414
- click: (ev) => {
1415
- ev.stopPropagation();
1416
- unmute_signature(sig);
1417
- }
1418
- }]
1419
- ));
1420
- });
1421
- }
1422
- }
1423
-
1424
1242
  update_stats();
1425
1243
  }
1426
1244
 
@@ -1485,17 +1303,19 @@ function dev_fallback_copy(text)
1485
1303
  document.body.removeChild(ta);
1486
1304
  }
1487
1305
 
1488
- /* The control bar: trace toggles, view selector, direction /
1489
- * periodic filters, free-text search, copy, clear. Returns an element. */
1306
+ /* The control bar: trace toggles, view selector, direction
1307
+ * filters, free-text search, copy, clear. Returns an element. */
1490
1308
  function build_control_bar()
1491
1309
  {
1492
- let trace_chips = TRACE_DEFS.map(([key, label_key, fn]) => ['button', {
1310
+ let trace_chips = TRACE_DEFS.map(([key, label_key, title_key, state, toggle]) => ['button', {
1493
1311
  class: 'YDEV_CHIP', 'data-trace': key, 'data-label': label_key,
1494
1312
  'data-i18n': label_key, type: 'button',
1313
+ title: t(title_key), 'data-i18n-title': title_key,
1314
+ 'aria-label': t(label_key), 'data-i18n-aria-label': label_key,
1495
1315
  }, t(label_key), {
1496
1316
  click: (ev) => {
1497
1317
  ev.stopPropagation();
1498
- fn();
1318
+ toggle();
1499
1319
  }
1500
1320
  }]);
1501
1321
 
@@ -1594,24 +1414,6 @@ function build_control_bar()
1594
1414
  mk_dir('err', '⚠', 'dev_filter_err', 'errors'),
1595
1415
  ];
1596
1416
 
1597
- let periodic_chip = ['button', {
1598
- class: 'YDEV_CHIP', 'data-toggle': 'periodic', type: 'button',
1599
- title: t('hide the timers and the recurring traffic'),
1600
- 'data-i18n-title': 'hide the timers and the recurring traffic',
1601
- }, [
1602
- /* The glyph is its own element so the label beside it can carry
1603
- * its key: `refresh_language()` replaces the FIRST text node of
1604
- * the element that carries `data-i18n`, and that would have
1605
- * eaten the glyph with the word. */
1606
- ['span', {class: 'YDEV_GLYPH'}, '⊘'],
1607
- ['span', {'data-i18n': 'periodic'}, t('periodic')]
1608
- ], {
1609
- click: (ev) => {
1610
- ev.stopPropagation();
1611
- toggle_pref('dev_hide_periodic', HIDE_PERIODIC_DEFAULT);
1612
- }
1613
- }];
1614
-
1615
1417
  let search = ['input', {
1616
1418
  class: 'YDEV_SEARCH', type: 'search', 'data-role': 'search',
1617
1419
  placeholder: t('filter events / payload'),
@@ -1656,7 +1458,7 @@ function build_control_bar()
1656
1458
  grp('traces', [...trace_chips, simple_mach]), sep(),
1657
1459
  grp('output', [output_seg]), sep(),
1658
1460
  grp('view', [view_seg]), expand_grp, sep(),
1659
- grp('show', [...dir_chips, periodic_chip]), sep(),
1461
+ grp('show', dir_chips), sep(),
1660
1462
  grp('find', [search]), sep(),
1661
1463
  grp('log', [copy, clear]),
1662
1464
  ]]);
@@ -1672,12 +1474,11 @@ function build_title_header()
1672
1474
  ]]);
1673
1475
  }
1674
1476
 
1675
- /* The monitor body: control bar + muted row + log + stats strip. */
1477
+ /* The monitor body: control bar + log + stats strip. */
1676
1478
  function build_dev_body()
1677
1479
  {
1678
1480
  return createElement2(['div', {class: 'YDEV_BODY'}, [
1679
1481
  build_control_bar(),
1680
- ['div', {class: 'YDEV_MUTED', id: 'ydev-muted'}, []],
1681
1482
  ['div', {class: 'YDEV_LOG', id: 'developer-traffic-logger'}, []],
1682
1483
  ['div', {class: 'YDEV_STATS', id: 'ydev-stats'}, []],
1683
1484
  ]]);
@@ -1702,32 +1503,19 @@ function dev_window_was_open()
1702
1503
  }
1703
1504
 
1704
1505
  /************************************************************
1705
- * Apply ALL persisted developer-trace flags to the running
1706
- * yuno. Independent of the dev window — call it once at app
1707
- * startup so a refresh keeps logging whatever was enabled.
1506
+ * Wire the developer window's sinks and the browser-side prefs.
1507
+ * Call it once at app startup.
1508
+ *
1509
+ * The TRACE LEVELS are not here: the yuno restores the ones the user
1510
+ * persisted in its own mt_create (`trace_levels` / `no_trace_levels`),
1511
+ * as a C yuno does. What is left is where the output goes.
1708
1512
  ************************************************************/
1709
1513
  function apply_dev_traces()
1710
1514
  {
1711
- let traffic = Number(kw_get_local_storage_value("trace_traffic", 0, false));
1712
- let trace = Number(kw_get_local_storage_value("trace_automata", 0, false));
1713
- let creation = Number(kw_get_local_storage_value("trace_creation", 0, false));
1714
- let start_stop = Number(kw_get_local_storage_value("trace_start_stop", 0, false));
1715
- let subscriptions = Number(kw_get_local_storage_value("trace_subscriptions", 0, false));
1716
- let i18n = Number(kw_get_local_storage_value("trace_i18n", 0, false));
1717
- let no_poll = Number(kw_get_local_storage_value("no_poll", 0, false));
1718
-
1719
- if(traffic) {
1720
- gobj_write_attr(gobj_yuno(), "trace_inter_event", true);
1721
- gobj_write_attr(gobj_yuno(), "trace_ievent_callback", info_traffic);
1722
- } else {
1723
- gobj_write_attr(gobj_yuno(), "trace_inter_event", false);
1724
- }
1725
- gobj_write_attr(gobj_yuno(), "tracing", trace);
1726
- gobj_write_attr(gobj_yuno(), "trace_creation", creation);
1727
- gobj_write_attr(gobj_yuno(), "trace_start_stop", start_stop);
1728
- gobj_write_attr(gobj_yuno(), "trace_subscriptions", subscriptions);
1729
- gobj_write_attr(gobj_yuno(), "no_poll", no_poll);
1730
- i18next.options.debug = i18n ? true : false;
1515
+ /* The traffic trace of C_IEVENT_CLI lands in this window. */
1516
+ gobj_write_attr(gobj_yuno(), "trace_ievent_callback", info_traffic);
1517
+
1518
+ i18next.options.debug = i18n_state() ? true : false;
1731
1519
 
1732
1520
  /* Which shape the machine trace is written in (persisted). */
1733
1521
  gobj_set_trace_machine_format(dev_simple_mach());
@@ -1812,8 +1600,8 @@ function setup_dev(self, show)
1812
1600
  *
1813
1601
  * Returns { $el, dispose }:
1814
1602
  * - $el: the panel element (control bar + log + stats).
1815
- * - dispose: stops the inter-event traffic trace; call it from
1816
- * the modal's on_close.
1603
+ * - dispose: call it from the modal's on_close (kept for the
1604
+ * contract; the traces outlive the panel).
1817
1605
  ************************************************************/
1818
1606
  function build_dev_panel()
1819
1607
  {
@@ -1844,8 +1632,9 @@ function build_dev_panel()
1844
1632
  rerender_all();
1845
1633
  }, 0);
1846
1634
 
1635
+ /* Nothing to undo: a trace level is the yuno's state, persisted,
1636
+ * and it is not tied to this panel being open. */
1847
1637
  let dispose = function() {
1848
- gobj_write_attr(gobj_yuno(), "trace_inter_event", false);
1849
1638
  };
1850
1639
 
1851
1640
  return {$el: $el, dispose: dispose};