@yuneta/gobj-ui 7.19.4 → 7.20.1

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.
@@ -10411,6 +10411,39 @@ function format_epoch(value) {
10411
10411
  if (isNaN(d.getTime())) return null;
10412
10412
  return d.toLocaleString();
10413
10413
  }
10414
+ /************************************************************
10415
+ * The text view's payload: the document as text, indented
10416
+ * four characters, cut at `max_chars`.
10417
+ *
10418
+ * Returns {text, capped}. `capped` is what the caller
10419
+ * announces under the dump — a cut that is not announced
10420
+ * reads as a document that ends there.
10421
+ ************************************************************/
10422
+ function json_text_dump(root, max_chars) {
10423
+ let text;
10424
+ try {
10425
+ text = JSON.stringify(root, null, 4);
10426
+ } catch (e) {
10427
+ return {
10428
+ text: "",
10429
+ capped: false,
10430
+ error: String(e)
10431
+ };
10432
+ }
10433
+ if (text === void 0) return {
10434
+ text: "",
10435
+ capped: false,
10436
+ error: "not representable as JSON"
10437
+ };
10438
+ if (typeof max_chars === "number" && max_chars > 0 && text.length > max_chars) return {
10439
+ text: text.slice(0, max_chars),
10440
+ capped: true
10441
+ };
10442
+ return {
10443
+ text,
10444
+ capped: false
10445
+ };
10446
+ }
10414
10447
  //#endregion
10415
10448
  //#region src/c_yui_json.js
10416
10449
  /***********************************************************************
@@ -10439,9 +10472,19 @@ function format_epoch(value) {
10439
10472
  * Only expanded containers are materialised in the DOM, so the tree
10440
10473
  * stays bounded no matter how large the source document is.
10441
10474
  *
10475
+ * TWO VIEWS over the same working document, chosen by the `view_mode`
10476
+ * attr ("tree" | "text") and switched from the toolbar: the lazy tree
10477
+ * above, and the raw `JSON.stringify(…, 4)` dump of what is currently
10478
+ * loaded. The text view is what you reach for to read a document as
10479
+ * it is written, to select a slab of it, or to search it with the
10480
+ * browser's own Ctrl+F; it shows the `__collapsed__` sentinels
10481
+ * verbatim, because that is honestly what the client holds. The
10482
+ * tree-only controls (search, expand/collapse) hide with the tree.
10483
+ *
10442
10484
  * DOM is self-describing (UPPER_SNAKE logical classes): JSON_VIEWER /
10443
- * JSON_TOOLBAR / JSON_SEARCH / JSON_TREE / JSON_ROW / JSON_KEY /
10444
- * JSON_VALUE / JSON_SUMMARY / JSON_COLLAPSED / JSON_TIME.
10485
+ * JSON_TOOLBAR / JSON_SEARCH / JSON_VIEW_MODE / JSON_TREE / JSON_TEXT /
10486
+ * JSON_TEXT_BODY / JSON_ROW / JSON_KEY / JSON_VALUE / JSON_SUMMARY /
10487
+ * JSON_COLLAPSED / JSON_TIME.
10445
10488
  *
10446
10489
  * Copyright (c) 2026, ArtGins.
10447
10490
  * All Rights Reserved.
@@ -10451,6 +10494,7 @@ function format_epoch(value) {
10451
10494
  ***************************************************************/
10452
10495
  var GCLASS_NAME$10 = "C_YUI_JSON";
10453
10496
  var MAX_RENDER_ROWS = 5e3;
10497
+ var MAX_TEXT_CHARS = 2e6;
10454
10498
  /***************************************************************
10455
10499
  * Data
10456
10500
  ***************************************************************/
@@ -10458,6 +10502,7 @@ var attrs_table$10 = [
10458
10502
  SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of output events"),
10459
10503
  SDATA(data_type_t.DTP_STRING, "title", 0, "", "Optional header title (i18n key)"),
10460
10504
  SDATA(data_type_t.DTP_JSON, "json_data", 0, null, "Initial JSON to render (usually already collapsed)"),
10505
+ SDATA(data_type_t.DTP_STRING, "view_mode", 0, "tree", "View: 'tree' (lazy tree) or 'text' (raw JSON dump)"),
10461
10506
  SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "HTMLElement root, mounted by the parent"),
10462
10507
  SDATA_END()
10463
10508
  ];
@@ -10468,7 +10513,13 @@ var PRIVATE_DATA$10 = {
10468
10513
  errors: null,
10469
10514
  search: "",
10470
10515
  $tree: null,
10471
- $search: null
10516
+ $text: null,
10517
+ $text_body: null,
10518
+ $search: null,
10519
+ $search_ctl: null,
10520
+ $expand_btn: null,
10521
+ $collapse_btn: null,
10522
+ $mode_btn: null
10472
10523
  };
10473
10524
  var __gclass__$10 = null;
10474
10525
  /******************************
@@ -10486,6 +10537,7 @@ function mt_create$10(gobj) {
10486
10537
  let json_data = gobj_read_attr(gobj, "json_data");
10487
10538
  if (json_data !== null && json_data !== void 0) priv.root = json_deep_copy(json_data);
10488
10539
  build_ui$9(gobj);
10540
+ apply_view_mode(gobj);
10489
10541
  let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
10490
10542
  if (!subscriber) subscriber = gobj_parent(gobj);
10491
10543
  gobj_subscribe_event(gobj, null, {}, subscriber);
@@ -10494,7 +10546,7 @@ function mt_create$10(gobj) {
10494
10546
  * Framework Method: Start
10495
10547
  ***************************************************************/
10496
10548
  function mt_start$10(gobj) {
10497
- render_tree(gobj);
10549
+ render_view(gobj);
10498
10550
  }
10499
10551
  /***************************************************************
10500
10552
  * Framework Method: Stop
@@ -10521,22 +10573,38 @@ function build_ui$9(gobj) {
10521
10573
  class: "C_YUI_JSON JSON_VIEWER view-card",
10522
10574
  style: "height:100%; display:flex; flex-direction:column;"
10523
10575
  },
10524
- [[
10525
- "div",
10526
- { class: "JSON_TOOLBAR is-flex-grow-0" },
10527
- [$toolbar]
10528
- ], [
10529
- "div",
10530
- {
10531
- class: "JSON_TREE is-flex-grow-1",
10532
- style: "flex:1 1 auto; min-height:0; overflow:auto;"
10533
- },
10534
- []
10535
- ]]
10576
+ [
10577
+ [
10578
+ "div",
10579
+ { class: "JSON_TOOLBAR is-flex-grow-0" },
10580
+ [$toolbar]
10581
+ ],
10582
+ [
10583
+ "div",
10584
+ {
10585
+ class: "JSON_TREE is-flex-grow-1",
10586
+ style: "flex:1 1 auto; min-height:0; overflow:auto;"
10587
+ },
10588
+ []
10589
+ ],
10590
+ [
10591
+ "div",
10592
+ {
10593
+ class: "JSON_TEXT is-flex-grow-1 is-hidden",
10594
+ style: "flex:1 1 auto; min-height:0; overflow:auto;"
10595
+ },
10596
+ []
10597
+ ]
10598
+ ]
10536
10599
  ]);
10537
10600
  gobj_write_attr(gobj, "$container", $container);
10538
10601
  priv.$tree = $container.querySelector(".JSON_TREE");
10602
+ priv.$text = $container.querySelector(".JSON_TEXT");
10539
10603
  priv.$search = $container.querySelector(".JSON_SEARCH");
10604
+ priv.$search_ctl = $container.querySelector(".JSON_SEARCH_CONTROL");
10605
+ priv.$expand_btn = $container.querySelector(".EV_EXPAND_ALL");
10606
+ priv.$collapse_btn = $container.querySelector(".EV_COLLAPSE_ALL");
10607
+ priv.$mode_btn = $container.querySelector(".JSON_VIEW_MODE");
10540
10608
  refresh_language($container, t);
10541
10609
  }
10542
10610
  /************************************************************
@@ -10550,10 +10618,17 @@ function destroy_ui$5(gobj) {
10550
10618
  gobj_write_attr(gobj, "$container", null);
10551
10619
  }
10552
10620
  priv.$tree = null;
10621
+ priv.$text = null;
10622
+ priv.$text_body = null;
10553
10623
  priv.$search = null;
10624
+ priv.$search_ctl = null;
10625
+ priv.$expand_btn = null;
10626
+ priv.$collapse_btn = null;
10627
+ priv.$mode_btn = null;
10554
10628
  }
10555
10629
  /************************************************************
10556
- * Toolbar: search + expand-all / collapse-all / copy
10630
+ * Toolbar: search + tree/text switch + expand-all /
10631
+ * collapse-all / copy
10557
10632
  ************************************************************/
10558
10633
  function make_toolbar$2(gobj) {
10559
10634
  let title = gobj_read_str_attr(gobj, "title");
@@ -10582,7 +10657,7 @@ function make_toolbar$2(gobj) {
10582
10657
  let $search_control = createElement2([
10583
10658
  "div",
10584
10659
  {
10585
- class: "control has-icons-left",
10660
+ class: "control has-icons-left JSON_SEARCH_CONTROL",
10586
10661
  style: "max-width:22em;"
10587
10662
  },
10588
10663
  [$search_input, [
@@ -10594,6 +10669,7 @@ function make_toolbar$2(gobj) {
10594
10669
  attach_clear($search_control, $search_input);
10595
10670
  left_items.push($search_control);
10596
10671
  let right_items = [
10672
+ view_mode_button(gobj),
10597
10673
  icon_button(gobj, "yi-chevron-right", "EV_EXPAND_ALL", "expand loaded"),
10598
10674
  icon_button(gobj, "yi-chevron-right", "EV_COLLAPSE_ALL", "collapse all"),
10599
10675
  icon_button(gobj, "yi-copy", "EV_COPY_ALL", "copy json")
@@ -10650,6 +10726,123 @@ function icon_button(gobj, icon, event_name, label_key) {
10650
10726
  ];
10651
10727
  }
10652
10728
  /************************************************************
10729
+ * The tree/text switch.
10730
+ *
10731
+ * It shows the view it takes you TO — the `code` glyph while the
10732
+ * tree is on screen, the `sitemap` glyph while the text is — and
10733
+ * sends EV_SET_VIEW_MODE with no mode, which the action reads as
10734
+ * "toggle". apply_view_mode() keeps the icon and its i18n keys in
10735
+ * step with the attr.
10736
+ ************************************************************/
10737
+ function view_mode_button(gobj) {
10738
+ return [
10739
+ "button",
10740
+ {
10741
+ class: "button JSON_VIEW_MODE",
10742
+ style: "width:2.5em;",
10743
+ title: t("text view"),
10744
+ "data-i18n-title": "text view",
10745
+ "aria-label": t("text view"),
10746
+ "data-i18n-aria-label": "text view"
10747
+ },
10748
+ [[
10749
+ "span",
10750
+ { class: "icon" },
10751
+ [["i", { class: "yi-code" }]]
10752
+ ]],
10753
+ { click: function(evt) {
10754
+ evt.stopPropagation();
10755
+ gobj_send_event(gobj, "EV_SET_VIEW_MODE", {}, gobj);
10756
+ } }
10757
+ ];
10758
+ }
10759
+ /************************************************************
10760
+ * The current view, normalised. Anything but "text" is the tree.
10761
+ ************************************************************/
10762
+ function current_view_mode(gobj) {
10763
+ return gobj_read_str_attr(gobj, "view_mode") === "text" ? "text" : "tree";
10764
+ }
10765
+ /************************************************************
10766
+ * Show the chrome of the current view and hide the other's.
10767
+ ************************************************************/
10768
+ function apply_view_mode(gobj) {
10769
+ let priv = gobj.priv;
10770
+ let text_mode = current_view_mode(gobj) === "text";
10771
+ if (priv.$tree) priv.$tree.classList.toggle("is-hidden", text_mode);
10772
+ if (priv.$text) priv.$text.classList.toggle("is-hidden", !text_mode);
10773
+ [
10774
+ priv.$search_ctl,
10775
+ priv.$expand_btn,
10776
+ priv.$collapse_btn
10777
+ ].forEach(function($el) {
10778
+ if ($el) $el.classList.toggle("is-hidden", text_mode);
10779
+ });
10780
+ if (priv.$mode_btn) {
10781
+ let icon = text_mode ? "yi-sitemap" : "yi-code";
10782
+ let key = text_mode ? "tree view" : "text view";
10783
+ let label = text_mode ? t("tree view") : t("text view");
10784
+ let $i = priv.$mode_btn.querySelector("i");
10785
+ if ($i) $i.className = icon;
10786
+ priv.$mode_btn.setAttribute("data-i18n-title", key);
10787
+ priv.$mode_btn.setAttribute("data-i18n-aria-label", key);
10788
+ priv.$mode_btn.setAttribute("title", label);
10789
+ priv.$mode_btn.setAttribute("aria-label", label);
10790
+ }
10791
+ }
10792
+ /************************************************************
10793
+ * Render whichever view is current.
10794
+ ************************************************************/
10795
+ function render_view(gobj) {
10796
+ if (current_view_mode(gobj) === "text") render_text(gobj);
10797
+ else render_tree(gobj);
10798
+ }
10799
+ /************************************************************
10800
+ * Render the text view: the whole loaded document, verbatim.
10801
+ *
10802
+ * Unlike the tree, nothing here is lazy — what the client holds is
10803
+ * what it prints, `__collapsed__` sentinels included. Over
10804
+ * MAX_TEXT_CHARS the dump is cut and the cut is announced.
10805
+ ************************************************************/
10806
+ function render_text(gobj) {
10807
+ let priv = gobj.priv;
10808
+ let $text = priv.$text;
10809
+ if (!$text) return;
10810
+ let scroll_top = $text.scrollTop;
10811
+ $text.textContent = "";
10812
+ priv.$text_body = null;
10813
+ if (priv.root === null || priv.root === void 0) {
10814
+ $text.appendChild(createElement2([
10815
+ "div",
10816
+ {
10817
+ class: "JSON_EMPTY has-text-grey p-3",
10818
+ "data-i18n": "no data"
10819
+ },
10820
+ "no data"
10821
+ ]));
10822
+ return;
10823
+ }
10824
+ let dump = json_text_dump(priv.root, MAX_TEXT_CHARS);
10825
+ if (dump.error) log_error(`${GCLASS_NAME$10}: cannot dump the document as text: ${dump.error}`);
10826
+ let $pre = createElement2([
10827
+ "pre",
10828
+ { class: "JSON_TEXT_BODY" },
10829
+ []
10830
+ ]);
10831
+ $pre.textContent = dump.text;
10832
+ $text.appendChild($pre);
10833
+ priv.$text_body = $pre;
10834
+ if (dump.capped) $text.appendChild(createElement2([
10835
+ "div",
10836
+ {
10837
+ class: "JSON_CAPPED has-text-warning p-2",
10838
+ "data-i18n": "text truncated; collapse some branches"
10839
+ },
10840
+ "text truncated; collapse some branches"
10841
+ ]));
10842
+ refresh_language($text, t);
10843
+ $text.scrollTop = scroll_top;
10844
+ }
10845
+ /************************************************************
10653
10846
  * Re-render the whole tree from priv.root + priv.expanded.
10654
10847
  *
10655
10848
  * Only expanded containers are walked, so the DOM size is
@@ -10980,7 +11173,7 @@ function ac_set_json(gobj, event, kw, src) {
10980
11173
  priv.expanded.clear();
10981
11174
  priv.pending.clear();
10982
11175
  priv.errors.clear();
10983
- render_tree(gobj);
11176
+ render_view(gobj);
10984
11177
  return 0;
10985
11178
  }
10986
11179
  /************************************************************
@@ -10995,7 +11188,7 @@ function ac_subtree_loaded(gobj, event, kw, src) {
10995
11188
  priv.pending.delete(path);
10996
11189
  priv.errors.delete(path);
10997
11190
  if (path) priv.expanded.add(path);
10998
- render_tree(gobj);
11191
+ render_view(gobj);
10999
11192
  return 0;
11000
11193
  }
11001
11194
  /************************************************************
@@ -11007,7 +11200,7 @@ function ac_subtree_error(gobj, event, kw, src) {
11007
11200
  priv.pending.delete(path);
11008
11201
  priv.errors.set(path, kw.error || "error");
11009
11202
  log_error(`${GCLASS_NAME$10}: subtree load failed at '${path}': ${kw.error || ""}`);
11010
- render_tree(gobj);
11203
+ render_view(gobj);
11011
11204
  return 0;
11012
11205
  }
11013
11206
  /************************************************************
@@ -11018,7 +11211,7 @@ function ac_toggle_node(gobj, event, kw, src) {
11018
11211
  let path = kw.path || "";
11019
11212
  if (priv.expanded.has(path)) priv.expanded.delete(path);
11020
11213
  else priv.expanded.add(path);
11021
- render_tree(gobj);
11214
+ render_view(gobj);
11022
11215
  return 0;
11023
11216
  }
11024
11217
  /************************************************************
@@ -11031,7 +11224,7 @@ function ac_expand_collapsed(gobj, event, kw, src) {
11031
11224
  if (priv.pending.has(path)) return 0;
11032
11225
  priv.pending.add(path);
11033
11226
  priv.errors.delete(path);
11034
- render_tree(gobj);
11227
+ render_view(gobj);
11035
11228
  gobj_publish_event(gobj, "EV_EXPAND_PATH", {
11036
11229
  path,
11037
11230
  size: kw.size
@@ -11044,7 +11237,7 @@ function ac_expand_collapsed(gobj, event, kw, src) {
11044
11237
  function ac_search$1(gobj, event, kw, src) {
11045
11238
  let priv = gobj.priv;
11046
11239
  priv.search = (kw.text || "").trim().toLowerCase();
11047
- render_tree(gobj);
11240
+ render_view(gobj);
11048
11241
  return 0;
11049
11242
  }
11050
11243
  /************************************************************
@@ -11062,7 +11255,7 @@ function ac_expand_all$1(gobj, event, kw, src) {
11062
11255
  paths.forEach(function(p) {
11063
11256
  priv.expanded.add(p);
11064
11257
  });
11065
- render_tree(gobj);
11258
+ render_view(gobj);
11066
11259
  return 0;
11067
11260
  }
11068
11261
  /************************************************************
@@ -11070,7 +11263,7 @@ function ac_expand_all$1(gobj, event, kw, src) {
11070
11263
  ************************************************************/
11071
11264
  function ac_collapse_all$1(gobj, event, kw, src) {
11072
11265
  gobj.priv.expanded.clear();
11073
- render_tree(gobj);
11266
+ render_view(gobj);
11074
11267
  return 0;
11075
11268
  }
11076
11269
  /************************************************************
@@ -11079,7 +11272,12 @@ function ac_collapse_all$1(gobj, event, kw, src) {
11079
11272
  function ac_copy_all(gobj, event, kw, src) {
11080
11273
  let priv = gobj.priv;
11081
11274
  if (priv.root === null || priv.root === void 0) return 0;
11082
- let text = JSON.stringify(priv.root, null, 4);
11275
+ let dump = json_text_dump(priv.root, 0);
11276
+ if (dump.error) {
11277
+ log_error(`${GCLASS_NAME$10}: cannot copy the document: ${dump.error}`);
11278
+ return -1;
11279
+ }
11280
+ let text = dump.text;
11083
11281
  if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(text).catch(function(e) {
11084
11282
  log_error(`${GCLASS_NAME$10}: clipboard write failed: ${e}`);
11085
11283
  });
@@ -11087,6 +11285,23 @@ function ac_copy_all(gobj, event, kw, src) {
11087
11285
  return 0;
11088
11286
  }
11089
11287
  /************************************************************
11288
+ * EV_SET_VIEW_MODE { mode } — "tree" | "text".
11289
+ *
11290
+ * With no mode it toggles, which is what the toolbar button sends.
11291
+ ************************************************************/
11292
+ function ac_set_view_mode(gobj, event, kw, src) {
11293
+ let mode = kw.mode;
11294
+ if (mode === void 0 || mode === null || mode === "") mode = current_view_mode(gobj) === "text" ? "tree" : "text";
11295
+ else if (mode !== "tree" && mode !== "text") {
11296
+ log_error(`${GCLASS_NAME$10}: unknown view_mode '${mode}'`);
11297
+ return -1;
11298
+ }
11299
+ gobj_write_attr(gobj, "view_mode", mode);
11300
+ apply_view_mode(gobj);
11301
+ render_view(gobj);
11302
+ return 0;
11303
+ }
11304
+ /************************************************************
11090
11305
  * EV_LANGUAGE_CHANGED — re-translate chrome + re-render
11091
11306
  ************************************************************/
11092
11307
  function ac_language_changed$3(gobj, event, kw, src) {
@@ -11095,8 +11310,9 @@ function ac_language_changed$3(gobj, event, kw, src) {
11095
11310
  if ($container) {
11096
11311
  refresh_language($container, t);
11097
11312
  if (priv.$search) priv.$search.setAttribute("placeholder", t("search"));
11313
+ apply_view_mode(gobj);
11098
11314
  }
11099
- render_tree(gobj);
11315
+ render_view(gobj);
11100
11316
  return 0;
11101
11317
  }
11102
11318
  /************************************************************
@@ -11116,7 +11332,7 @@ function ac_hide$6(gobj, event, kw, src) {
11116
11332
  * EV_REFRESH
11117
11333
  ************************************************************/
11118
11334
  function ac_refresh$4(gobj, event, kw, src) {
11119
- render_tree(gobj);
11335
+ render_view(gobj);
11120
11336
  return 0;
11121
11337
  }
11122
11338
  /***************************
@@ -11142,6 +11358,11 @@ function create_gclass$10(gclass_name) {
11142
11358
  ac_set_json,
11143
11359
  "ST_READY"
11144
11360
  ],
11361
+ [
11362
+ "EV_SET_VIEW_MODE",
11363
+ ac_set_view_mode,
11364
+ null
11365
+ ],
11145
11366
  [
11146
11367
  "EV_LANGUAGE_CHANGED",
11147
11368
  ac_language_changed$3,
@@ -11208,6 +11429,11 @@ function create_gclass$10(gclass_name) {
11208
11429
  ac_copy_all,
11209
11430
  null
11210
11431
  ],
11432
+ [
11433
+ "EV_SET_VIEW_MODE",
11434
+ ac_set_view_mode,
11435
+ null
11436
+ ],
11211
11437
  [
11212
11438
  "EV_LANGUAGE_CHANGED",
11213
11439
  ac_language_changed$3,
@@ -11239,6 +11465,7 @@ function create_gclass$10(gclass_name) {
11239
11465
  ["EV_EXPAND_ALL", 0],
11240
11466
  ["EV_COLLAPSE_ALL", 0],
11241
11467
  ["EV_COPY_ALL", 0],
11468
+ ["EV_SET_VIEW_MODE", 0],
11242
11469
  ["EV_LANGUAGE_CHANGED", 0],
11243
11470
  ["EV_REFRESH", 0],
11244
11471
  ["EV_SHOW", 0],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.19.4",
3
+ "version": "7.20.1",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -1,7 +1,7 @@
1
1
  /***********************************************************************
2
2
  * c_yui_json.css
3
3
  *
4
- * Styling for C_YUI_JSON (lazy JSON tree viewer).
4
+ * Styling for C_YUI_JSON (lazy JSON tree viewer + raw text view).
5
5
  * Monospace tree, type-coloured leaves, dark-aware.
6
6
  * No transitions/animations by design (lib-yui convention).
7
7
  *
@@ -151,6 +151,43 @@
151
151
  opacity: 0.7;
152
152
  }
153
153
 
154
+ /*
155
+ * Text view: the same document as a raw dump, monospace like the tree.
156
+ */
157
+ .C_YUI_JSON .JSON_TEXT {
158
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
159
+ font-size: 1em;
160
+ line-height: 1.5;
161
+ padding: 0.25rem 0.5rem;
162
+ }
163
+
164
+ .C_YUI_JSON .JSON_TEXT_BODY {
165
+ margin: 0;
166
+ padding: 0;
167
+ background: none;
168
+ color: inherit;
169
+ font-family: inherit;
170
+ font-size: inherit;
171
+ line-height: inherit;
172
+ /* `pre`, NOT `pre-wrap`: the indentation IS the structure here, and
173
+ * a wrapped continuation line restarts at column 0 and lies about
174
+ * the depth of everything under it — on a phone that is most of the
175
+ * document. Long lines scroll sideways inside .JSON_TEXT (which
176
+ * owns the overflow), never on the page body. */
177
+ white-space: pre;
178
+ /* And the <pre> must GROW to its longest line: as a plain block it
179
+ * takes the container's width, its text spills out of a box that
180
+ * reports no overflow, and .JSON_TEXT gets no scroll range to
181
+ * offer — the tail of every long line is simply unreachable.
182
+ * `min-width: 100%` keeps it filling the container when the
183
+ * document is narrower. */
184
+ width: max-content;
185
+ min-width: 100%;
186
+ /* The dump is there to be read AND taken: a plain <pre> in some
187
+ * host stylesheets is unselectable chrome. */
188
+ user-select: text;
189
+ }
190
+
154
191
  /*
155
192
  * Disable any transition a host stylesheet might inject.
156
193
  */