@yuneta/gobj-ui 7.23.178 → 7.23.180

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.
@@ -76430,6 +76430,41 @@ function ensure_dev_style() {
76430
76430
  .YDEV_TITLE { display: flex; align-items: baseline; gap: 8px; }
76431
76431
  .YDEV_TITLE_MAIN { font-weight: 700; }
76432
76432
  .YDEV_TITLE_SUB { opacity: 0.7; font-size: 12px; }
76433
+ /* The ink of the log.
76434
+ *
76435
+ * It used to be one grey dimmed with opacity per role -- 0.85 for a key,
76436
+ * 0.5 for a preview, 0.45 for the source -- which is what washed the window
76437
+ * out: opacity blends text TOWARDS the background, so the more a line
76438
+ * mattered the less of it was left. Measured on the dark scheme the preview
76439
+ * came out at 4.39:1 and the source at 3.78:1, both under the 4.5 floor,
76440
+ * and everything that was legible was the same grey as everything else.
76441
+ *
76442
+ * Explicit colours now, one per ROLE and measured against the entry's own
76443
+ * background (#1b1e24 dark, #fafafa light): a key, a string, a number and a
76444
+ * boolean read apart from each other the way they do in a browser console,
76445
+ * which is the point of colouring a payload at all.
76446
+ * Ratios: dark 11.20 / 6.32 / 9.83 / 6.00 / 6.77 / 6.49 / 4.82,
76447
+ * light 7.38 / 7.52 / 5.16 / 8.35 / 5.59 / 7.21 / 4.35. */
76448
+ :root {
76449
+ --ydev-ink: #1f2937;
76450
+ --ydev-key: #0451a5;
76451
+ --ydev-str: #a31515;
76452
+ --ydev-num: #067a4e;
76453
+ --ydev-bool: #6b21a8;
76454
+ --ydev-dim: #5b6675;
76455
+ --ydev-src: #4a5568;
76456
+ --ydev-bullet: #6b7789;
76457
+ }
76458
+ :root[data-theme="dark"] {
76459
+ --ydev-ink: #e5e7eb;
76460
+ --ydev-key: #9cdcfe;
76461
+ --ydev-str: #ce9178;
76462
+ --ydev-num: #b5cea8;
76463
+ --ydev-bool: #c586c0;
76464
+ --ydev-dim: #9aa6b8;
76465
+ --ydev-src: #8fa3bf;
76466
+ --ydev-bullet: #7b8ba3;
76467
+ }
76433
76468
  /* -------- entries (shared) -------- */
76434
76469
  .TRAFFIC_ENTRY {
76435
76470
  border-left: 3px solid #94a3b8; border-radius: 3px;
@@ -76443,11 +76478,15 @@ function ensure_dev_style() {
76443
76478
  .TRAFFIC_HEADER { display: flex; align-items: baseline; gap: 8px; }
76444
76479
  .TRAFFIC_ARROW { font-weight: 700; }
76445
76480
  .TRAFFIC_EVENT { font-weight: 700; }
76446
- .TRAFFIC_CMD { opacity: 0.75; font-weight: 600; }
76481
+ .TRAFFIC_CMD { color: var(--ydev-dim); font-weight: 600; }
76482
+ /* Where the message went through. min-width 0 and flex 0 1 auto so it is the
76483
+ part that gives way when the row runs out of width -- the event name and
76484
+ the size/time must not be the ones to go. */
76485
+ .TRAFFIC_SRC { color: var(--ydev-src); font-size: 11px; flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
76447
76486
  .dir-out .TRAFFIC_ARROW, .dir-out .TRAFFIC_EVENT { color: #2563eb; }
76448
76487
  .dir-in .TRAFFIC_ARROW, .dir-in .TRAFFIC_EVENT { color: #059669; }
76449
76488
  .dir-err .TRAFFIC_ARROW, .dir-err .TRAFFIC_EVENT { color: #dc2626; }
76450
- .TRAFFIC_META { margin-left: auto; opacity: 0.6; font-size: 11px; white-space: nowrap; }
76489
+ .TRAFFIC_META { margin-left: auto; color: var(--ydev-dim); font-size: 11px; white-space: nowrap; }
76451
76490
  /* The indent of a nested block is NOT a hover effect: it used to be given
76452
76491
  only on .TRAFFIC_ENTRY:hover, so moving the cursor across the log made
76453
76492
  every payload under it jump 16px sideways and reflow. A payload being
@@ -76456,29 +76495,40 @@ function ensure_dev_style() {
76456
76495
  .TRAFFIC_KW { margin: 2px 0 0 16px; }
76457
76496
  .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; }
76458
76497
  .TRAFFIC_ROW { display: flex; gap: 6px; align-items: baseline; }
76459
- .TRAFFIC_BULLET { opacity: 0.45; flex: 0 0 auto; }
76460
- .TRAFFIC_KEY { opacity: 0.85; flex: 0 0 auto; }
76461
- .TRAFFIC_VAL { word-break: break-word; }
76462
- .TRAFFIC_VAL.t-num { color: #0891b2; }
76463
- .TRAFFIC_VAL.t-bool { color: #9333ea; }
76464
- .TRAFFIC_VAL.t-null { color: #9333ea; font-style: italic; }
76465
- .TRAFFIC_VAL.t-empty { opacity: 0.5; }
76466
- .TRAFFIC_TS { opacity: 0.6; margin-left: 8px; }
76498
+ .TRAFFIC_BULLET { color: var(--ydev-bullet); flex: 0 0 auto; }
76499
+ .TRAFFIC_KEY { color: var(--ydev-key); flex: 0 0 auto; }
76500
+ .TRAFFIC_VAL { color: var(--ydev-ink); word-break: break-word; }
76501
+ .TRAFFIC_VAL.t-str { color: var(--ydev-str); }
76502
+ .TRAFFIC_VAL.t-num { color: var(--ydev-num); }
76503
+ .TRAFFIC_VAL.t-bool { color: var(--ydev-bool); }
76504
+ .TRAFFIC_VAL.t-null { color: var(--ydev-bool); font-style: italic; }
76505
+ .TRAFFIC_VAL.t-empty { color: var(--ydev-dim); }
76506
+ .TRAFFIC_TS { color: var(--ydev-dim); margin-left: 8px; }
76467
76507
  details.TRAFFIC_NEST > summary { cursor: pointer; list-style: none; display: flex; gap: 6px; align-items: baseline; }
76468
76508
  details.TRAFFIC_NEST > summary::-webkit-details-marker { display: none; }
76469
- .TRAFFIC_NEST_KEY { opacity: 0.85; }
76470
- .TRAFFIC_NEST_HINT { opacity: 0.5; margin-left: 4px; }
76471
- .YDEV_EMPTY { opacity: 0.5; font-size: 12px; padding: 18px 10px; text-align: center; }
76509
+ .TRAFFIC_NEST_KEY { color: var(--ydev-key); }
76510
+ .TRAFFIC_NEST_HINT { color: var(--ydev-dim); margin-left: 4px; }
76511
+ /* The folded preview, painted with the same ink as the expanded rows: a key
76512
+ is a key and a string is a string wherever it is read. */
76513
+ .TRAFFIC_NEST_HINT_WRAP { margin-left: 4px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
76514
+ .TRAFFIC_PREV.p-punct { color: var(--ydev-dim); }
76515
+ .TRAFFIC_PREV.p-key { color: var(--ydev-key); }
76516
+ .TRAFFIC_PREV.t-str { color: var(--ydev-str); }
76517
+ .TRAFFIC_PREV.t-num { color: var(--ydev-num); }
76518
+ .TRAFFIC_PREV.t-bool { color: var(--ydev-bool); }
76519
+ .TRAFFIC_PREV.t-null { color: var(--ydev-bool); font-style: italic; }
76520
+ .TRAFFIC_PREV.t-empty { color: var(--ydev-dim); }
76521
+ .YDEV_EMPTY { color: var(--ydev-dim); font-size: 12px; padding: 18px 10px; text-align: center; }
76472
76522
  /* -------- mirrored console logs (error/warning/info/debug/msg; the automata trace shows as debug) -------- */
76473
76523
  .YDEV_LOGROW { display: flex; align-items: baseline; gap: 8px; margin: 1px 0; padding: 2px 8px; border-left: 3px solid #94a3b8; border-radius: 3px; font-family: "DejaVu Sans Mono", monospace, consolas, monaco; font-size: 12px; background: rgba(0,0,0,0.015); }
76474
- .YDEV_LOG_LVL { flex: 0 0 auto; text-transform: uppercase; font-size: 9px; font-weight: 700; letter-spacing: 0.04em; opacity: 0.8; min-width: 48px; }
76475
- .YDEV_LOG_TXT { flex: 1 1 auto; min-width: 0; white-space: pre-wrap; word-break: break-word; opacity: 0.9; }
76524
+ .YDEV_LOG_LVL { flex: 0 0 auto; text-transform: uppercase; font-size: 9px; font-weight: 700; letter-spacing: 0.04em; color: var(--ydev-dim); min-width: 48px; }
76525
+ .YDEV_LOG_TXT { flex: 1 1 auto; min-width: 0; white-space: pre-wrap; word-break: break-word; color: var(--ydev-ink); }
76476
76526
  .YDEV_LOG_error { border-left-color: #dc2626; } .YDEV_LOG_error .YDEV_LOG_LVL { color: #dc2626; }
76477
76527
  .YDEV_LOG_warning { border-left-color: #d97706; } .YDEV_LOG_warning .YDEV_LOG_LVL { color: #d97706; }
76478
76528
  .YDEV_LOG_info { border-left-color: #2563eb; } .YDEV_LOG_info .YDEV_LOG_LVL { color: #2563eb; }
76479
76529
  .YDEV_LOG_msg { border-left-color: #0891b2; } .YDEV_LOG_msg .YDEV_LOG_LVL { color: #0891b2; }
76480
- .YDEV_LOG_debug { border-left-color: #94a3b8; } .YDEV_LOG_debug .YDEV_LOG_LVL { color: #94a3b8; } .YDEV_LOG_debug .YDEV_LOG_TXT { opacity: 0.72; }
76481
- .YDEV_LOG_json { border-left-color: #9333ea; align-items: flex-start; } .YDEV_LOG_json .YDEV_LOG_LVL { color: #9333ea; } .YDEV_LOG_json .YDEV_LOG_TXT { font-size: 11px; line-height: 1.35; opacity: 0.8; }
76530
+ .YDEV_LOG_debug { border-left-color: #94a3b8; } .YDEV_LOG_debug .YDEV_LOG_LVL { color: #94a3b8; } .YDEV_LOG_debug .YDEV_LOG_TXT { color: var(--ydev-dim); }
76531
+ .YDEV_LOG_json { border-left-color: #9333ea; align-items: flex-start; } .YDEV_LOG_json .YDEV_LOG_LVL { color: #9333ea; } .YDEV_LOG_json .YDEV_LOG_TXT { font-size: 11px; line-height: 1.35; color: var(--ydev-dim); }
76482
76532
  /* -------- dark theme -------- */
76483
76533
  :root[data-theme="dark"] .TRAFFIC_FULL { background: rgba(255,255,255,0.05); }
76484
76534
  :root[data-theme="dark"] .YDEV_BAR, :root[data-theme="dark"] .YDEV_STATS { background: rgba(255,255,255,0.04); }
@@ -76493,7 +76543,7 @@ details.TRAFFIC_NEST > summary::-webkit-details-marker { display: none; }
76493
76543
  :root[data-theme="dark"] .dir-err .TRAFFIC_ARROW, :root[data-theme="dark"] .dir-err .TRAFFIC_EVENT { color: #f87171; }
76494
76544
  :root[data-theme="dark"] .YDEV_STAT.s-out { color: #60a5fa; } :root[data-theme="dark"] .YDEV_STAT.s-in { color: #34d399; } :root[data-theme="dark"] .YDEV_STAT.s-err { color: #f87171; }
76495
76545
  :root[data-theme="dark"] .YDEV_STAT.s-warn { color: #fbbf24; }
76496
- :root[data-theme="dark"] .TRAFFIC_VAL.t-num { color: #22d3ee; }
76546
+
76497
76547
  :root[data-theme="dark"] .TRAFFIC_VAL.t-bool, :root[data-theme="dark"] .TRAFFIC_VAL.t-null { color: #c084fc; }
76498
76548
  :root[data-theme="dark"] .YDEV_CHIP.is-active { background: rgba(96,165,250,0.2); border-color: #60a5fa; color: #93c5fd; }
76499
76549
  :root[data-theme="dark"] .YDEV_SEG_BTN.is-active { background: #2563eb; color: #fff; }
@@ -76567,24 +76617,65 @@ var PREVIEW_MAX_KEYS = 4;
76567
76617
  var PREVIEW_MAX_CHARS = 90;
76568
76618
  var PREVIEW_MAX_VALUE = 28;
76569
76619
  function preview_scalar(v) {
76570
- if (v === null) return "null";
76571
- if (typeof v === "string") return "\"" + (v.length > PREVIEW_MAX_VALUE ? v.slice(0, PREVIEW_MAX_VALUE) + "…" : v) + "\"";
76572
- if (typeof v === "object") return Array.isArray(v) ? "[…]" : "{…}";
76573
- return String(v);
76620
+ if (v === null) return {
76621
+ text: "null",
76622
+ cls: "t-null"
76623
+ };
76624
+ if (typeof v === "string") return {
76625
+ text: "\"" + (v.length > PREVIEW_MAX_VALUE ? v.slice(0, PREVIEW_MAX_VALUE) + "…" : v) + "\"",
76626
+ cls: "t-str"
76627
+ };
76628
+ if (typeof v === "object") return {
76629
+ text: Array.isArray(v) ? "[…]" : "{…}",
76630
+ cls: "t-empty"
76631
+ };
76632
+ if (typeof v === "boolean") return {
76633
+ text: String(v),
76634
+ cls: "t-bool"
76635
+ };
76636
+ return {
76637
+ text: String(v),
76638
+ cls: "t-num"
76639
+ };
76574
76640
  }
76575
- function object_preview(obj, count) {
76641
+ function object_preview_parts(obj, count) {
76576
76642
  let keys = Object.keys(obj);
76577
- let parts = [];
76643
+ let out = [{
76644
+ text: "{",
76645
+ cls: "p-punct"
76646
+ }];
76647
+ let fields = 0;
76578
76648
  let used = 0;
76579
76649
  for (let k of keys) {
76580
- if (parts.length >= PREVIEW_MAX_KEYS) break;
76581
- let part = k + ": " + preview_scalar(obj[k]);
76582
- if (parts.length > 0 && used + part.length > PREVIEW_MAX_CHARS) break;
76583
- parts.push(part);
76584
- used += part.length + 2;
76650
+ if (fields >= PREVIEW_MAX_KEYS) break;
76651
+ let val = preview_scalar(obj[k]);
76652
+ let width = k.length + 2 + val.text.length;
76653
+ if (fields > 0 && used + width > PREVIEW_MAX_CHARS) break;
76654
+ if (fields > 0) out.push({
76655
+ text: ", ",
76656
+ cls: "p-punct"
76657
+ });
76658
+ out.push({
76659
+ text: k,
76660
+ cls: "p-key"
76661
+ });
76662
+ out.push({
76663
+ text: ": ",
76664
+ cls: "p-punct"
76665
+ });
76666
+ out.push(val);
76667
+ fields++;
76668
+ used += width + 2;
76585
76669
  }
76586
- if (parts.length < count) parts.push("…");
76587
- return "{" + parts.join(", ") + "}";
76670
+ if (fields < count) out.push({
76671
+ text: fields > 0 ? ", …" : "…",
76672
+ cls: "p-punct"
76673
+ });
76674
+ out.push({
76675
+ text: "}",
76676
+ cls: "p-punct"
76677
+ });
76678
+ return out;
76588
76679
  }
76589
76680
  /************************************************************
76590
76681
  * One field of any type. Scalars → a bullet row; objects and
@@ -76616,7 +76707,15 @@ function traffic_value_node(key, value) {
76616
76707
  ]
76617
76708
  ]
76618
76709
  ];
76619
- let hint = is_arr ? `[${count}]` : object_preview(value, count);
76710
+ let hint = is_arr ? [[
76711
+ "span",
76712
+ { class: "TRAFFIC_NEST_HINT" },
76713
+ `[${count}]`
76714
+ ]] : object_preview_parts(value, count).map((t) => [
76715
+ "span",
76716
+ { class: "TRAFFIC_PREV " + t.cls },
76717
+ t.text
76718
+ ]);
76620
76719
  return [
76621
76720
  "details",
76622
76721
  { class: "TRAFFIC_NEST" },
@@ -76636,7 +76735,7 @@ function traffic_value_node(key, value) {
76636
76735
  ],
76637
76736
  [
76638
76737
  "span",
76639
- { class: "TRAFFIC_NEST_HINT" },
76738
+ { class: "TRAFFIC_NEST_HINT_WRAP" },
76640
76739
  hint
76641
76740
  ]
76642
76741
  ]
@@ -76674,6 +76773,11 @@ function event_spans(e) {
76674
76773
  { class: "TRAFFIC_CMD" },
76675
76774
  e.command
76676
76775
  ]);
76776
+ if (e.title) spans.push([
76777
+ "span",
76778
+ { class: "TRAFFIC_SRC" },
76779
+ e.title
76780
+ ]);
76677
76781
  return spans;
76678
76782
  }
76679
76783
  function render_detailed(e) {
@@ -76425,6 +76425,41 @@ function ensure_dev_style() {
76425
76425
  .YDEV_TITLE { display: flex; align-items: baseline; gap: 8px; }
76426
76426
  .YDEV_TITLE_MAIN { font-weight: 700; }
76427
76427
  .YDEV_TITLE_SUB { opacity: 0.7; font-size: 12px; }
76428
+ /* The ink of the log.
76429
+ *
76430
+ * It used to be one grey dimmed with opacity per role -- 0.85 for a key,
76431
+ * 0.5 for a preview, 0.45 for the source -- which is what washed the window
76432
+ * out: opacity blends text TOWARDS the background, so the more a line
76433
+ * mattered the less of it was left. Measured on the dark scheme the preview
76434
+ * came out at 4.39:1 and the source at 3.78:1, both under the 4.5 floor,
76435
+ * and everything that was legible was the same grey as everything else.
76436
+ *
76437
+ * Explicit colours now, one per ROLE and measured against the entry's own
76438
+ * background (#1b1e24 dark, #fafafa light): a key, a string, a number and a
76439
+ * boolean read apart from each other the way they do in a browser console,
76440
+ * which is the point of colouring a payload at all.
76441
+ * Ratios: dark 11.20 / 6.32 / 9.83 / 6.00 / 6.77 / 6.49 / 4.82,
76442
+ * light 7.38 / 7.52 / 5.16 / 8.35 / 5.59 / 7.21 / 4.35. */
76443
+ :root {
76444
+ --ydev-ink: #1f2937;
76445
+ --ydev-key: #0451a5;
76446
+ --ydev-str: #a31515;
76447
+ --ydev-num: #067a4e;
76448
+ --ydev-bool: #6b21a8;
76449
+ --ydev-dim: #5b6675;
76450
+ --ydev-src: #4a5568;
76451
+ --ydev-bullet: #6b7789;
76452
+ }
76453
+ :root[data-theme="dark"] {
76454
+ --ydev-ink: #e5e7eb;
76455
+ --ydev-key: #9cdcfe;
76456
+ --ydev-str: #ce9178;
76457
+ --ydev-num: #b5cea8;
76458
+ --ydev-bool: #c586c0;
76459
+ --ydev-dim: #9aa6b8;
76460
+ --ydev-src: #8fa3bf;
76461
+ --ydev-bullet: #7b8ba3;
76462
+ }
76428
76463
  /* -------- entries (shared) -------- */
76429
76464
  .TRAFFIC_ENTRY {
76430
76465
  border-left: 3px solid #94a3b8; border-radius: 3px;
@@ -76438,11 +76473,15 @@ function ensure_dev_style() {
76438
76473
  .TRAFFIC_HEADER { display: flex; align-items: baseline; gap: 8px; }
76439
76474
  .TRAFFIC_ARROW { font-weight: 700; }
76440
76475
  .TRAFFIC_EVENT { font-weight: 700; }
76441
- .TRAFFIC_CMD { opacity: 0.75; font-weight: 600; }
76476
+ .TRAFFIC_CMD { color: var(--ydev-dim); font-weight: 600; }
76477
+ /* Where the message went through. min-width 0 and flex 0 1 auto so it is the
76478
+ part that gives way when the row runs out of width -- the event name and
76479
+ the size/time must not be the ones to go. */
76480
+ .TRAFFIC_SRC { color: var(--ydev-src); font-size: 11px; flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
76442
76481
  .dir-out .TRAFFIC_ARROW, .dir-out .TRAFFIC_EVENT { color: #2563eb; }
76443
76482
  .dir-in .TRAFFIC_ARROW, .dir-in .TRAFFIC_EVENT { color: #059669; }
76444
76483
  .dir-err .TRAFFIC_ARROW, .dir-err .TRAFFIC_EVENT { color: #dc2626; }
76445
- .TRAFFIC_META { margin-left: auto; opacity: 0.6; font-size: 11px; white-space: nowrap; }
76484
+ .TRAFFIC_META { margin-left: auto; color: var(--ydev-dim); font-size: 11px; white-space: nowrap; }
76446
76485
  /* The indent of a nested block is NOT a hover effect: it used to be given
76447
76486
  only on .TRAFFIC_ENTRY:hover, so moving the cursor across the log made
76448
76487
  every payload under it jump 16px sideways and reflow. A payload being
@@ -76451,29 +76490,40 @@ function ensure_dev_style() {
76451
76490
  .TRAFFIC_KW { margin: 2px 0 0 16px; }
76452
76491
  .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; }
76453
76492
  .TRAFFIC_ROW { display: flex; gap: 6px; align-items: baseline; }
76454
- .TRAFFIC_BULLET { opacity: 0.45; flex: 0 0 auto; }
76455
- .TRAFFIC_KEY { opacity: 0.85; flex: 0 0 auto; }
76456
- .TRAFFIC_VAL { word-break: break-word; }
76457
- .TRAFFIC_VAL.t-num { color: #0891b2; }
76458
- .TRAFFIC_VAL.t-bool { color: #9333ea; }
76459
- .TRAFFIC_VAL.t-null { color: #9333ea; font-style: italic; }
76460
- .TRAFFIC_VAL.t-empty { opacity: 0.5; }
76461
- .TRAFFIC_TS { opacity: 0.6; margin-left: 8px; }
76493
+ .TRAFFIC_BULLET { color: var(--ydev-bullet); flex: 0 0 auto; }
76494
+ .TRAFFIC_KEY { color: var(--ydev-key); flex: 0 0 auto; }
76495
+ .TRAFFIC_VAL { color: var(--ydev-ink); word-break: break-word; }
76496
+ .TRAFFIC_VAL.t-str { color: var(--ydev-str); }
76497
+ .TRAFFIC_VAL.t-num { color: var(--ydev-num); }
76498
+ .TRAFFIC_VAL.t-bool { color: var(--ydev-bool); }
76499
+ .TRAFFIC_VAL.t-null { color: var(--ydev-bool); font-style: italic; }
76500
+ .TRAFFIC_VAL.t-empty { color: var(--ydev-dim); }
76501
+ .TRAFFIC_TS { color: var(--ydev-dim); margin-left: 8px; }
76462
76502
  details.TRAFFIC_NEST > summary { cursor: pointer; list-style: none; display: flex; gap: 6px; align-items: baseline; }
76463
76503
  details.TRAFFIC_NEST > summary::-webkit-details-marker { display: none; }
76464
- .TRAFFIC_NEST_KEY { opacity: 0.85; }
76465
- .TRAFFIC_NEST_HINT { opacity: 0.5; margin-left: 4px; }
76466
- .YDEV_EMPTY { opacity: 0.5; font-size: 12px; padding: 18px 10px; text-align: center; }
76504
+ .TRAFFIC_NEST_KEY { color: var(--ydev-key); }
76505
+ .TRAFFIC_NEST_HINT { color: var(--ydev-dim); margin-left: 4px; }
76506
+ /* The folded preview, painted with the same ink as the expanded rows: a key
76507
+ is a key and a string is a string wherever it is read. */
76508
+ .TRAFFIC_NEST_HINT_WRAP { margin-left: 4px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
76509
+ .TRAFFIC_PREV.p-punct { color: var(--ydev-dim); }
76510
+ .TRAFFIC_PREV.p-key { color: var(--ydev-key); }
76511
+ .TRAFFIC_PREV.t-str { color: var(--ydev-str); }
76512
+ .TRAFFIC_PREV.t-num { color: var(--ydev-num); }
76513
+ .TRAFFIC_PREV.t-bool { color: var(--ydev-bool); }
76514
+ .TRAFFIC_PREV.t-null { color: var(--ydev-bool); font-style: italic; }
76515
+ .TRAFFIC_PREV.t-empty { color: var(--ydev-dim); }
76516
+ .YDEV_EMPTY { color: var(--ydev-dim); font-size: 12px; padding: 18px 10px; text-align: center; }
76467
76517
  /* -------- mirrored console logs (error/warning/info/debug/msg; the automata trace shows as debug) -------- */
76468
76518
  .YDEV_LOGROW { display: flex; align-items: baseline; gap: 8px; margin: 1px 0; padding: 2px 8px; border-left: 3px solid #94a3b8; border-radius: 3px; font-family: "DejaVu Sans Mono", monospace, consolas, monaco; font-size: 12px; background: rgba(0,0,0,0.015); }
76469
- .YDEV_LOG_LVL { flex: 0 0 auto; text-transform: uppercase; font-size: 9px; font-weight: 700; letter-spacing: 0.04em; opacity: 0.8; min-width: 48px; }
76470
- .YDEV_LOG_TXT { flex: 1 1 auto; min-width: 0; white-space: pre-wrap; word-break: break-word; opacity: 0.9; }
76519
+ .YDEV_LOG_LVL { flex: 0 0 auto; text-transform: uppercase; font-size: 9px; font-weight: 700; letter-spacing: 0.04em; color: var(--ydev-dim); min-width: 48px; }
76520
+ .YDEV_LOG_TXT { flex: 1 1 auto; min-width: 0; white-space: pre-wrap; word-break: break-word; color: var(--ydev-ink); }
76471
76521
  .YDEV_LOG_error { border-left-color: #dc2626; } .YDEV_LOG_error .YDEV_LOG_LVL { color: #dc2626; }
76472
76522
  .YDEV_LOG_warning { border-left-color: #d97706; } .YDEV_LOG_warning .YDEV_LOG_LVL { color: #d97706; }
76473
76523
  .YDEV_LOG_info { border-left-color: #2563eb; } .YDEV_LOG_info .YDEV_LOG_LVL { color: #2563eb; }
76474
76524
  .YDEV_LOG_msg { border-left-color: #0891b2; } .YDEV_LOG_msg .YDEV_LOG_LVL { color: #0891b2; }
76475
- .YDEV_LOG_debug { border-left-color: #94a3b8; } .YDEV_LOG_debug .YDEV_LOG_LVL { color: #94a3b8; } .YDEV_LOG_debug .YDEV_LOG_TXT { opacity: 0.72; }
76476
- .YDEV_LOG_json { border-left-color: #9333ea; align-items: flex-start; } .YDEV_LOG_json .YDEV_LOG_LVL { color: #9333ea; } .YDEV_LOG_json .YDEV_LOG_TXT { font-size: 11px; line-height: 1.35; opacity: 0.8; }
76525
+ .YDEV_LOG_debug { border-left-color: #94a3b8; } .YDEV_LOG_debug .YDEV_LOG_LVL { color: #94a3b8; } .YDEV_LOG_debug .YDEV_LOG_TXT { color: var(--ydev-dim); }
76526
+ .YDEV_LOG_json { border-left-color: #9333ea; align-items: flex-start; } .YDEV_LOG_json .YDEV_LOG_LVL { color: #9333ea; } .YDEV_LOG_json .YDEV_LOG_TXT { font-size: 11px; line-height: 1.35; color: var(--ydev-dim); }
76477
76527
  /* -------- dark theme -------- */
76478
76528
  :root[data-theme="dark"] .TRAFFIC_FULL { background: rgba(255,255,255,0.05); }
76479
76529
  :root[data-theme="dark"] .YDEV_BAR, :root[data-theme="dark"] .YDEV_STATS { background: rgba(255,255,255,0.04); }
@@ -76488,7 +76538,7 @@ details.TRAFFIC_NEST > summary::-webkit-details-marker { display: none; }
76488
76538
  :root[data-theme="dark"] .dir-err .TRAFFIC_ARROW, :root[data-theme="dark"] .dir-err .TRAFFIC_EVENT { color: #f87171; }
76489
76539
  :root[data-theme="dark"] .YDEV_STAT.s-out { color: #60a5fa; } :root[data-theme="dark"] .YDEV_STAT.s-in { color: #34d399; } :root[data-theme="dark"] .YDEV_STAT.s-err { color: #f87171; }
76490
76540
  :root[data-theme="dark"] .YDEV_STAT.s-warn { color: #fbbf24; }
76491
- :root[data-theme="dark"] .TRAFFIC_VAL.t-num { color: #22d3ee; }
76541
+
76492
76542
  :root[data-theme="dark"] .TRAFFIC_VAL.t-bool, :root[data-theme="dark"] .TRAFFIC_VAL.t-null { color: #c084fc; }
76493
76543
  :root[data-theme="dark"] .YDEV_CHIP.is-active { background: rgba(96,165,250,0.2); border-color: #60a5fa; color: #93c5fd; }
76494
76544
  :root[data-theme="dark"] .YDEV_SEG_BTN.is-active { background: #2563eb; color: #fff; }
@@ -76562,24 +76612,65 @@ var PREVIEW_MAX_KEYS = 4;
76562
76612
  var PREVIEW_MAX_CHARS = 90;
76563
76613
  var PREVIEW_MAX_VALUE = 28;
76564
76614
  function preview_scalar(v) {
76565
- if (v === null) return "null";
76566
- if (typeof v === "string") return "\"" + (v.length > PREVIEW_MAX_VALUE ? v.slice(0, PREVIEW_MAX_VALUE) + "…" : v) + "\"";
76567
- if (typeof v === "object") return Array.isArray(v) ? "[…]" : "{…}";
76568
- return String(v);
76615
+ if (v === null) return {
76616
+ text: "null",
76617
+ cls: "t-null"
76618
+ };
76619
+ if (typeof v === "string") return {
76620
+ text: "\"" + (v.length > PREVIEW_MAX_VALUE ? v.slice(0, PREVIEW_MAX_VALUE) + "…" : v) + "\"",
76621
+ cls: "t-str"
76622
+ };
76623
+ if (typeof v === "object") return {
76624
+ text: Array.isArray(v) ? "[…]" : "{…}",
76625
+ cls: "t-empty"
76626
+ };
76627
+ if (typeof v === "boolean") return {
76628
+ text: String(v),
76629
+ cls: "t-bool"
76630
+ };
76631
+ return {
76632
+ text: String(v),
76633
+ cls: "t-num"
76634
+ };
76569
76635
  }
76570
- function object_preview(obj, count) {
76636
+ function object_preview_parts(obj, count) {
76571
76637
  let keys = Object.keys(obj);
76572
- let parts = [];
76638
+ let out = [{
76639
+ text: "{",
76640
+ cls: "p-punct"
76641
+ }];
76642
+ let fields = 0;
76573
76643
  let used = 0;
76574
76644
  for (let k of keys) {
76575
- if (parts.length >= PREVIEW_MAX_KEYS) break;
76576
- let part = k + ": " + preview_scalar(obj[k]);
76577
- if (parts.length > 0 && used + part.length > PREVIEW_MAX_CHARS) break;
76578
- parts.push(part);
76579
- used += part.length + 2;
76645
+ if (fields >= PREVIEW_MAX_KEYS) break;
76646
+ let val = preview_scalar(obj[k]);
76647
+ let width = k.length + 2 + val.text.length;
76648
+ if (fields > 0 && used + width > PREVIEW_MAX_CHARS) break;
76649
+ if (fields > 0) out.push({
76650
+ text: ", ",
76651
+ cls: "p-punct"
76652
+ });
76653
+ out.push({
76654
+ text: k,
76655
+ cls: "p-key"
76656
+ });
76657
+ out.push({
76658
+ text: ": ",
76659
+ cls: "p-punct"
76660
+ });
76661
+ out.push(val);
76662
+ fields++;
76663
+ used += width + 2;
76580
76664
  }
76581
- if (parts.length < count) parts.push("…");
76582
- return "{" + parts.join(", ") + "}";
76665
+ if (fields < count) out.push({
76666
+ text: fields > 0 ? ", …" : "…",
76667
+ cls: "p-punct"
76668
+ });
76669
+ out.push({
76670
+ text: "}",
76671
+ cls: "p-punct"
76672
+ });
76673
+ return out;
76583
76674
  }
76584
76675
  /************************************************************
76585
76676
  * One field of any type. Scalars → a bullet row; objects and
@@ -76611,7 +76702,15 @@ function traffic_value_node(key, value) {
76611
76702
  ]
76612
76703
  ]
76613
76704
  ];
76614
- let hint = is_arr ? `[${count}]` : object_preview(value, count);
76705
+ let hint = is_arr ? [[
76706
+ "span",
76707
+ { class: "TRAFFIC_NEST_HINT" },
76708
+ `[${count}]`
76709
+ ]] : object_preview_parts(value, count).map((t) => [
76710
+ "span",
76711
+ { class: "TRAFFIC_PREV " + t.cls },
76712
+ t.text
76713
+ ]);
76615
76714
  return [
76616
76715
  "details",
76617
76716
  { class: "TRAFFIC_NEST" },
@@ -76631,7 +76730,7 @@ function traffic_value_node(key, value) {
76631
76730
  ],
76632
76731
  [
76633
76732
  "span",
76634
- { class: "TRAFFIC_NEST_HINT" },
76733
+ { class: "TRAFFIC_NEST_HINT_WRAP" },
76635
76734
  hint
76636
76735
  ]
76637
76736
  ]
@@ -76669,6 +76768,11 @@ function event_spans(e) {
76669
76768
  { class: "TRAFFIC_CMD" },
76670
76769
  e.command
76671
76770
  ]);
76771
+ if (e.title) spans.push([
76772
+ "span",
76773
+ { class: "TRAFFIC_SRC" },
76774
+ e.title
76775
+ ]);
76672
76776
  return spans;
76673
76777
  }
76674
76778
  function render_detailed(e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.23.178",
3
+ "version": "7.23.180",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
package/src/yui_dev.js CHANGED
@@ -450,6 +450,41 @@ function ensure_dev_style()
450
450
  .YDEV_TITLE { display: flex; align-items: baseline; gap: 8px; }
451
451
  .YDEV_TITLE_MAIN { font-weight: 700; }
452
452
  .YDEV_TITLE_SUB { opacity: 0.7; font-size: 12px; }
453
+ /* The ink of the log.
454
+ *
455
+ * It used to be one grey dimmed with opacity per role -- 0.85 for a key,
456
+ * 0.5 for a preview, 0.45 for the source -- which is what washed the window
457
+ * out: opacity blends text TOWARDS the background, so the more a line
458
+ * mattered the less of it was left. Measured on the dark scheme the preview
459
+ * came out at 4.39:1 and the source at 3.78:1, both under the 4.5 floor,
460
+ * and everything that was legible was the same grey as everything else.
461
+ *
462
+ * Explicit colours now, one per ROLE and measured against the entry's own
463
+ * background (#1b1e24 dark, #fafafa light): a key, a string, a number and a
464
+ * boolean read apart from each other the way they do in a browser console,
465
+ * which is the point of colouring a payload at all.
466
+ * Ratios: dark 11.20 / 6.32 / 9.83 / 6.00 / 6.77 / 6.49 / 4.82,
467
+ * light 7.38 / 7.52 / 5.16 / 8.35 / 5.59 / 7.21 / 4.35. */
468
+ :root {
469
+ --ydev-ink: #1f2937;
470
+ --ydev-key: #0451a5;
471
+ --ydev-str: #a31515;
472
+ --ydev-num: #067a4e;
473
+ --ydev-bool: #6b21a8;
474
+ --ydev-dim: #5b6675;
475
+ --ydev-src: #4a5568;
476
+ --ydev-bullet: #6b7789;
477
+ }
478
+ :root[data-theme="dark"] {
479
+ --ydev-ink: #e5e7eb;
480
+ --ydev-key: #9cdcfe;
481
+ --ydev-str: #ce9178;
482
+ --ydev-num: #b5cea8;
483
+ --ydev-bool: #c586c0;
484
+ --ydev-dim: #9aa6b8;
485
+ --ydev-src: #8fa3bf;
486
+ --ydev-bullet: #7b8ba3;
487
+ }
453
488
  /* -------- entries (shared) -------- */
454
489
  .TRAFFIC_ENTRY {
455
490
  border-left: 3px solid #94a3b8; border-radius: 3px;
@@ -463,11 +498,15 @@ function ensure_dev_style()
463
498
  .TRAFFIC_HEADER { display: flex; align-items: baseline; gap: 8px; }
464
499
  .TRAFFIC_ARROW { font-weight: 700; }
465
500
  .TRAFFIC_EVENT { font-weight: 700; }
466
- .TRAFFIC_CMD { opacity: 0.75; font-weight: 600; }
501
+ .TRAFFIC_CMD { color: var(--ydev-dim); font-weight: 600; }
502
+ /* Where the message went through. min-width 0 and flex 0 1 auto so it is the
503
+ part that gives way when the row runs out of width -- the event name and
504
+ the size/time must not be the ones to go. */
505
+ .TRAFFIC_SRC { color: var(--ydev-src); font-size: 11px; flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
467
506
  .dir-out .TRAFFIC_ARROW, .dir-out .TRAFFIC_EVENT { color: #2563eb; }
468
507
  .dir-in .TRAFFIC_ARROW, .dir-in .TRAFFIC_EVENT { color: #059669; }
469
508
  .dir-err .TRAFFIC_ARROW, .dir-err .TRAFFIC_EVENT { color: #dc2626; }
470
- .TRAFFIC_META { margin-left: auto; opacity: 0.6; font-size: 11px; white-space: nowrap; }
509
+ .TRAFFIC_META { margin-left: auto; color: var(--ydev-dim); font-size: 11px; white-space: nowrap; }
471
510
  /* The indent of a nested block is NOT a hover effect: it used to be given
472
511
  only on .TRAFFIC_ENTRY:hover, so moving the cursor across the log made
473
512
  every payload under it jump 16px sideways and reflow. A payload being
@@ -476,29 +515,40 @@ function ensure_dev_style()
476
515
  .TRAFFIC_KW { margin: 2px 0 0 16px; }
477
516
  .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; }
478
517
  .TRAFFIC_ROW { display: flex; gap: 6px; align-items: baseline; }
479
- .TRAFFIC_BULLET { opacity: 0.45; flex: 0 0 auto; }
480
- .TRAFFIC_KEY { opacity: 0.85; flex: 0 0 auto; }
481
- .TRAFFIC_VAL { word-break: break-word; }
482
- .TRAFFIC_VAL.t-num { color: #0891b2; }
483
- .TRAFFIC_VAL.t-bool { color: #9333ea; }
484
- .TRAFFIC_VAL.t-null { color: #9333ea; font-style: italic; }
485
- .TRAFFIC_VAL.t-empty { opacity: 0.5; }
486
- .TRAFFIC_TS { opacity: 0.6; margin-left: 8px; }
518
+ .TRAFFIC_BULLET { color: var(--ydev-bullet); flex: 0 0 auto; }
519
+ .TRAFFIC_KEY { color: var(--ydev-key); flex: 0 0 auto; }
520
+ .TRAFFIC_VAL { color: var(--ydev-ink); word-break: break-word; }
521
+ .TRAFFIC_VAL.t-str { color: var(--ydev-str); }
522
+ .TRAFFIC_VAL.t-num { color: var(--ydev-num); }
523
+ .TRAFFIC_VAL.t-bool { color: var(--ydev-bool); }
524
+ .TRAFFIC_VAL.t-null { color: var(--ydev-bool); font-style: italic; }
525
+ .TRAFFIC_VAL.t-empty { color: var(--ydev-dim); }
526
+ .TRAFFIC_TS { color: var(--ydev-dim); margin-left: 8px; }
487
527
  details.TRAFFIC_NEST > summary { cursor: pointer; list-style: none; display: flex; gap: 6px; align-items: baseline; }
488
528
  details.TRAFFIC_NEST > summary::-webkit-details-marker { display: none; }
489
- .TRAFFIC_NEST_KEY { opacity: 0.85; }
490
- .TRAFFIC_NEST_HINT { opacity: 0.5; margin-left: 4px; }
491
- .YDEV_EMPTY { opacity: 0.5; font-size: 12px; padding: 18px 10px; text-align: center; }
529
+ .TRAFFIC_NEST_KEY { color: var(--ydev-key); }
530
+ .TRAFFIC_NEST_HINT { color: var(--ydev-dim); margin-left: 4px; }
531
+ /* The folded preview, painted with the same ink as the expanded rows: a key
532
+ is a key and a string is a string wherever it is read. */
533
+ .TRAFFIC_NEST_HINT_WRAP { margin-left: 4px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
534
+ .TRAFFIC_PREV.p-punct { color: var(--ydev-dim); }
535
+ .TRAFFIC_PREV.p-key { color: var(--ydev-key); }
536
+ .TRAFFIC_PREV.t-str { color: var(--ydev-str); }
537
+ .TRAFFIC_PREV.t-num { color: var(--ydev-num); }
538
+ .TRAFFIC_PREV.t-bool { color: var(--ydev-bool); }
539
+ .TRAFFIC_PREV.t-null { color: var(--ydev-bool); font-style: italic; }
540
+ .TRAFFIC_PREV.t-empty { color: var(--ydev-dim); }
541
+ .YDEV_EMPTY { color: var(--ydev-dim); font-size: 12px; padding: 18px 10px; text-align: center; }
492
542
  /* -------- mirrored console logs (error/warning/info/debug/msg; the automata trace shows as debug) -------- */
493
543
  .YDEV_LOGROW { display: flex; align-items: baseline; gap: 8px; margin: 1px 0; padding: 2px 8px; border-left: 3px solid #94a3b8; border-radius: 3px; font-family: "DejaVu Sans Mono", monospace, consolas, monaco; font-size: 12px; background: rgba(0,0,0,0.015); }
494
- .YDEV_LOG_LVL { flex: 0 0 auto; text-transform: uppercase; font-size: 9px; font-weight: 700; letter-spacing: 0.04em; opacity: 0.8; min-width: 48px; }
495
- .YDEV_LOG_TXT { flex: 1 1 auto; min-width: 0; white-space: pre-wrap; word-break: break-word; opacity: 0.9; }
544
+ .YDEV_LOG_LVL { flex: 0 0 auto; text-transform: uppercase; font-size: 9px; font-weight: 700; letter-spacing: 0.04em; color: var(--ydev-dim); min-width: 48px; }
545
+ .YDEV_LOG_TXT { flex: 1 1 auto; min-width: 0; white-space: pre-wrap; word-break: break-word; color: var(--ydev-ink); }
496
546
  .YDEV_LOG_error { border-left-color: #dc2626; } .YDEV_LOG_error .YDEV_LOG_LVL { color: #dc2626; }
497
547
  .YDEV_LOG_warning { border-left-color: #d97706; } .YDEV_LOG_warning .YDEV_LOG_LVL { color: #d97706; }
498
548
  .YDEV_LOG_info { border-left-color: #2563eb; } .YDEV_LOG_info .YDEV_LOG_LVL { color: #2563eb; }
499
549
  .YDEV_LOG_msg { border-left-color: #0891b2; } .YDEV_LOG_msg .YDEV_LOG_LVL { color: #0891b2; }
500
- .YDEV_LOG_debug { border-left-color: #94a3b8; } .YDEV_LOG_debug .YDEV_LOG_LVL { color: #94a3b8; } .YDEV_LOG_debug .YDEV_LOG_TXT { opacity: 0.72; }
501
- .YDEV_LOG_json { border-left-color: #9333ea; align-items: flex-start; } .YDEV_LOG_json .YDEV_LOG_LVL { color: #9333ea; } .YDEV_LOG_json .YDEV_LOG_TXT { font-size: 11px; line-height: 1.35; opacity: 0.8; }
550
+ .YDEV_LOG_debug { border-left-color: #94a3b8; } .YDEV_LOG_debug .YDEV_LOG_LVL { color: #94a3b8; } .YDEV_LOG_debug .YDEV_LOG_TXT { color: var(--ydev-dim); }
551
+ .YDEV_LOG_json { border-left-color: #9333ea; align-items: flex-start; } .YDEV_LOG_json .YDEV_LOG_LVL { color: #9333ea; } .YDEV_LOG_json .YDEV_LOG_TXT { font-size: 11px; line-height: 1.35; color: var(--ydev-dim); }
502
552
  /* -------- dark theme -------- */
503
553
  :root[data-theme="dark"] .TRAFFIC_FULL { background: rgba(255,255,255,0.05); }
504
554
  :root[data-theme="dark"] .YDEV_BAR, :root[data-theme="dark"] .YDEV_STATS { background: rgba(255,255,255,0.04); }
@@ -513,7 +563,7 @@ details.TRAFFIC_NEST > summary::-webkit-details-marker { display: none; }
513
563
  :root[data-theme="dark"] .dir-err .TRAFFIC_ARROW, :root[data-theme="dark"] .dir-err .TRAFFIC_EVENT { color: #f87171; }
514
564
  :root[data-theme="dark"] .YDEV_STAT.s-out { color: #60a5fa; } :root[data-theme="dark"] .YDEV_STAT.s-in { color: #34d399; } :root[data-theme="dark"] .YDEV_STAT.s-err { color: #f87171; }
515
565
  :root[data-theme="dark"] .YDEV_STAT.s-warn { color: #fbbf24; }
516
- :root[data-theme="dark"] .TRAFFIC_VAL.t-num { color: #22d3ee; }
566
+
517
567
  :root[data-theme="dark"] .TRAFFIC_VAL.t-bool, :root[data-theme="dark"] .TRAFFIC_VAL.t-null { color: #c084fc; }
518
568
  :root[data-theme="dark"] .YDEV_CHIP.is-active { background: rgba(96,165,250,0.2); border-color: #60a5fa; color: #93c5fd; }
519
569
  :root[data-theme="dark"] .YDEV_SEG_BTN.is-active { background: #2563eb; color: #fff; }
@@ -599,44 +649,66 @@ const PREVIEW_MAX_VALUE = 28;
599
649
  function preview_scalar(v)
600
650
  {
601
651
  if(v === null) {
602
- return "null";
652
+ return {text: "null", cls: "t-null"};
603
653
  }
604
654
  if(typeof v === "string") {
605
655
  let text = (v.length > PREVIEW_MAX_VALUE) ?
606
656
  v.slice(0, PREVIEW_MAX_VALUE) + "…" : v;
607
- return '"' + text + '"';
657
+ return {text: '"' + text + '"', cls: "t-str"};
608
658
  }
609
659
  if(typeof v === "object") {
610
- return Array.isArray(v) ? "[…]" : "{…}";
660
+ return {text: Array.isArray(v) ? "[…]" : "{…}", cls: "t-empty"};
661
+ }
662
+ if(typeof v === "boolean") {
663
+ return {text: String(v), cls: "t-bool"};
611
664
  }
612
- return String(v);
665
+ return {text: String(v), cls: "t-num"};
613
666
  }
614
667
 
615
- function object_preview(obj, count)
668
+ /* The preview as TOKENS, so the renderer can paint a key, a string and a
669
+ number the way the expanded rows paint them. A preview in one flat grey
670
+ is the thing this window was asked to stop doing. */
671
+ function object_preview_parts(obj, count)
616
672
  {
617
673
  let keys = Object.keys(obj);
618
- let parts = [];
674
+ let out = [{text: "{", cls: "p-punct"}];
675
+ let fields = 0;
619
676
  let used = 0;
620
677
 
621
678
  for(let k of keys) {
622
- if(parts.length >= PREVIEW_MAX_KEYS) {
679
+ if(fields >= PREVIEW_MAX_KEYS) {
623
680
  break;
624
681
  }
625
- let part = k + ": " + preview_scalar(obj[k]);
682
+ let val = preview_scalar(obj[k]);
683
+ let width = k.length + 2 + val.text.length;
626
684
  /* Stop at the width, but never with an empty preview: one field
627
685
  too wide still says more than a number. */
628
- if(parts.length > 0 && used + part.length > PREVIEW_MAX_CHARS) {
686
+ if(fields > 0 && used + width > PREVIEW_MAX_CHARS) {
629
687
  break;
630
688
  }
631
- parts.push(part);
632
- used += part.length + 2;
689
+ if(fields > 0) {
690
+ out.push({text: ", ", cls: "p-punct"});
691
+ }
692
+ out.push({text: k, cls: "p-key"});
693
+ out.push({text: ": ", cls: "p-punct"});
694
+ out.push(val);
695
+ fields++;
696
+ used += width + 2;
633
697
  }
634
698
 
635
- if(parts.length < count) {
636
- parts.push("…");
699
+ if(fields < count) {
700
+ out.push({text: fields > 0 ? ", …" : "…", cls: "p-punct"});
637
701
  }
702
+ out.push({text: "}", cls: "p-punct"});
638
703
 
639
- return "{" + parts.join(", ") + "}";
704
+ return out;
705
+ }
706
+
707
+ /* The same preview as one string. What the tests assert on, and what any
708
+ caller wanting a plain line would use. */
709
+ function object_preview(obj, count)
710
+ {
711
+ return object_preview_parts(obj, count).map((t) => t.text).join("");
640
712
  }
641
713
 
642
714
  /************************************************************
@@ -660,12 +732,16 @@ function traffic_value_node(key, value)
660
732
  ]];
661
733
  }
662
734
 
663
- let hint = is_arr ? `[${count}]` : object_preview(value, count);
735
+ let hint = is_arr ?
736
+ [['span', {class: 'TRAFFIC_NEST_HINT'}, `[${count}]`]] :
737
+ object_preview_parts(value, count).map(
738
+ (t) => ['span', {class: 'TRAFFIC_PREV ' + t.cls}, t.text]
739
+ );
664
740
  return ['details', {class: 'TRAFFIC_NEST'}, [
665
741
  ['summary', {}, [
666
742
  ['span', {class: 'TRAFFIC_BULLET'}, '▸'],
667
743
  ['span', {class: 'TRAFFIC_NEST_KEY'}, key],
668
- ['span', {class: 'TRAFFIC_NEST_HINT'}, hint],
744
+ ['span', {class: 'TRAFFIC_NEST_HINT_WRAP'}, hint],
669
745
  ]],
670
746
  ['div', {class: 'TRAFFIC_KW'}, traffic_bullets(value)],
671
747
  ]];
@@ -695,6 +771,20 @@ function traffic_bullets(obj)
695
771
  ******************************/
696
772
 
697
773
 
774
+ /* The head of a traffic entry: direction, event, command, and WHERE the
775
+ * message went through -- the gobj and the url, which gobj-js composes
776
+ * (`c_ievent_cli`) and the browser console prints in its prefix.
777
+ *
778
+ * It is written as TEXT, dim, and not as a `title`: as a tooltip it popped a
779
+ * box over the log while it was being read, and it is the one thing in that
780
+ * tooltip that was written nowhere else -- an app browsing several backends
781
+ * cannot tell its messages apart without it. It shrinks before anything
782
+ * else in the row.
783
+ *
784
+ * The string carries its own `==>` / `<==`, which says the direction the
785
+ * arrow already says. It is left as it comes: taking it out means parsing a
786
+ * string built in another package, and it would go wrong the day that one
787
+ * writes it differently. */
698
788
  function event_spans(e)
699
789
  {
700
790
  let spans = [['span', {class: 'TRAFFIC_ARROW'}, dir_arrow(e.dir)],
@@ -702,6 +792,9 @@ function event_spans(e)
702
792
  if(e.command) {
703
793
  spans.push(['span', {class: 'TRAFFIC_CMD'}, e.command]);
704
794
  }
795
+ if(e.title) {
796
+ spans.push(['span', {class: 'TRAFFIC_SRC'}, e.title]);
797
+ }
705
798
  return spans;
706
799
  }
707
800
 
@@ -1760,4 +1853,11 @@ function build_dev_panel()
1760
1853
  return {$el: $el, dispose: dispose};
1761
1854
  }
1762
1855
 
1763
- export {info_traffic, setup_dev, build_dev_panel, apply_dev_traces, dev_window_was_open};
1856
+ export {
1857
+ info_traffic, setup_dev, build_dev_panel, apply_dev_traces, dev_window_was_open,
1858
+ /* Exported for its test: a pure formatter, and the import is also what
1859
+ guards this file from a syntax error nothing else would catch --
1860
+ the stylesheet below is a template literal, so one backtick in a CSS
1861
+ comment stops the whole module from parsing. */
1862
+ object_preview, object_preview_parts,
1863
+ };
@@ -0,0 +1,78 @@
1
+ /***********************************************************************
2
+ * yui_dev_preview.test.js
3
+ *
4
+ * What a FOLDED object says about itself in the Developer
5
+ * window: its first fields, the way a browser console says it.
6
+ *
7
+ * Importing the module at all is half of this file's job: the
8
+ * monitor's stylesheet is a template literal, so a single
9
+ * backtick in a CSS comment stops yui_dev.js from parsing --
10
+ * twice now -- and nothing else in the suite imports it.
11
+ *
12
+ * Copyright (c) 2026, ArtGins.
13
+ * All Rights Reserved.
14
+ ***********************************************************************/
15
+ import {describe, it, expect} from "vitest";
16
+ import {object_preview, object_preview_parts} from "./yui_dev.js";
17
+
18
+ describe("object_preview", () => {
19
+ it("names the first fields instead of counting them", () => {
20
+ const col = {
21
+ header: "id", fillspace: 18, type: "string",
22
+ flag: ["persistent"], hidden: false,
23
+ };
24
+ expect(object_preview(col, Object.keys(col).length)).toBe(
25
+ '{header: "id", fillspace: 18, type: "string", flag: […], …}'
26
+ );
27
+ });
28
+
29
+ it("quotes strings, so a value does not run into the next key", () => {
30
+ expect(object_preview({result: 0, comment: "ok"}, 2)).toBe(
31
+ '{result: 0, comment: "ok"}'
32
+ );
33
+ });
34
+
35
+ it("does not enter a nested container", () => {
36
+ const o = {a: {x: 1}, b: [1, 2]};
37
+ expect(object_preview(o, 2)).toBe("{a: {…}, b: […]}");
38
+ });
39
+
40
+ it("says there is more with an ellipsis", () => {
41
+ const o = {a: 1, b: 2, c: 3, d: 4, e: 5};
42
+ expect(object_preview(o, 5)).toBe("{a: 1, b: 2, c: 3, d: 4, …}");
43
+ });
44
+
45
+ it("clips a long string at 28 characters", () => {
46
+ const o = {comment: "x".repeat(60)};
47
+ expect(object_preview(o, 1)).toBe('{comment: "' + "x".repeat(28) + '…"}');
48
+ });
49
+
50
+ it("keeps one field even when it alone is too wide", () => {
51
+ const o = {only: "y".repeat(200), other: 1};
52
+ expect(object_preview(o, 2).startsWith('{only: "yyy')).toBe(true);
53
+ });
54
+
55
+ it("renders null and booleans as themselves", () => {
56
+ expect(object_preview({a: null, b: true}, 2)).toBe("{a: null, b: true}");
57
+ });
58
+ });
59
+
60
+ describe("object_preview_parts", () => {
61
+ it("gives every token the class its ink is chosen by", () => {
62
+ const parts = object_preview_parts({a: "s", b: 1, c: true, d: null}, 4);
63
+ const seen = parts.filter((t) => t.cls !== "p-punct")
64
+ .map((t) => [t.text, t.cls]);
65
+ expect(seen).toEqual([
66
+ ["a", "p-key"], ['"s"', "t-str"],
67
+ ["b", "p-key"], ["1", "t-num"],
68
+ ["c", "p-key"], ["true", "t-bool"],
69
+ ["d", "p-key"], ["null", "t-null"],
70
+ ]);
71
+ });
72
+
73
+ it("joins back into exactly what object_preview says", () => {
74
+ const o = {header: "id", fillspace: 18, flag: ["x"], hidden: false, more: 1};
75
+ const joined = object_preview_parts(o, 5).map((t) => t.text).join("");
76
+ expect(joined).toBe(object_preview(o, 5));
77
+ });
78
+ });