@yuneta/gobj-ui 7.23.43 → 7.23.45

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.
@@ -6035,6 +6035,293 @@ function yui_watch_theme(gobj, event = "EV_THEME") {
6035
6035
  }
6036
6036
  } };
6037
6037
  }
6038
+ /************************************************************
6039
+ * JSON type discriminator (viewer vocabulary)
6040
+ ************************************************************/
6041
+ function json_type(value) {
6042
+ if (value === null) return "null";
6043
+ if (Array.isArray(value)) return "array";
6044
+ switch (typeof value) {
6045
+ case "string": return "string";
6046
+ case "number": return "number";
6047
+ case "boolean": return "boolean";
6048
+ case "object": return "object";
6049
+ default: return "unknown";
6050
+ }
6051
+ }
6052
+ /************************************************************
6053
+ * Detect a kw_collapse() sentinel.
6054
+ *
6055
+ * The kernel replaces an over-limit dict with:
6056
+ * { "__collapsed__": { "path": ..., "size": N } }
6057
+ * and an over-limit array with:
6058
+ * [ { "__collapsed__": { "path": ..., "size": N } } ]
6059
+ *
6060
+ * Returns { size, path, is_array } or null.
6061
+ ************************************************************/
6062
+ function is_collapsed(value) {
6063
+ if (value && typeof value === "object" && !Array.isArray(value)) {
6064
+ let keys = Object.keys(value);
6065
+ if (keys.length === 1 && keys[0] === "__collapsed__") {
6066
+ let c = value.__collapsed__ || {};
6067
+ return {
6068
+ size: c.size,
6069
+ path: c.path,
6070
+ is_array: false
6071
+ };
6072
+ }
6073
+ return null;
6074
+ }
6075
+ if (Array.isArray(value) && value.length === 1) {
6076
+ let e = value[0];
6077
+ if (e && typeof e === "object" && !Array.isArray(e) && Object.keys(e).length === 1 && e.__collapsed__) {
6078
+ let c = e.__collapsed__ || {};
6079
+ return {
6080
+ size: c.size,
6081
+ path: c.path,
6082
+ is_array: true
6083
+ };
6084
+ }
6085
+ }
6086
+ return null;
6087
+ }
6088
+ /************************************************************
6089
+ * Join / split absolute path segments (kernel convention)
6090
+ ************************************************************/
6091
+ function seg_join(segments) {
6092
+ return segments.map(function(s) {
6093
+ return String(s);
6094
+ }).join("`");
6095
+ }
6096
+ function seg_split(path) {
6097
+ if (path === "" || path === null || path === void 0) return [];
6098
+ return path.split("`");
6099
+ }
6100
+ /************************************************************
6101
+ * Walk a JSON tree by absolute segments; undefined if the
6102
+ * path does not resolve.
6103
+ ************************************************************/
6104
+ function get_by_segments(root, segments) {
6105
+ let v = root;
6106
+ for (let seg of segments) {
6107
+ if (v === null || v === void 0) return;
6108
+ if (Array.isArray(v)) v = v[Number(seg)];
6109
+ else if (typeof v === "object") v = v[seg];
6110
+ else return;
6111
+ }
6112
+ return v;
6113
+ }
6114
+ /************************************************************
6115
+ * Set a value at absolute segments (mutates root).
6116
+ *
6117
+ * segments == [] means "replace the whole root" — the caller
6118
+ * must use the RETURNED value in that case (a primitive/array
6119
+ * root cannot be mutated in place).
6120
+ ************************************************************/
6121
+ function set_by_segments(root, segments, new_value) {
6122
+ if (segments.length === 0) return new_value;
6123
+ let parent = get_by_segments(root, segments.slice(0, -1));
6124
+ if (parent === null || parent === void 0 || typeof parent !== "object") return root;
6125
+ let last = segments[segments.length - 1];
6126
+ if (Array.isArray(parent)) parent[Number(last)] = new_value;
6127
+ else parent[last] = new_value;
6128
+ return root;
6129
+ }
6130
+ /************************************************************
6131
+ * Does `value` (or any loaded descendant / its own key)
6132
+ * contain the lower-cased search term?
6133
+ *
6134
+ * Collapsed (not-yet-loaded) subtrees can't be searched, so
6135
+ * they never match on content — only their key can match,
6136
+ * which the renderer checks separately.
6137
+ ************************************************************/
6138
+ function subtree_matches(value, term) {
6139
+ if (!term) return true;
6140
+ if (is_collapsed(value)) return false;
6141
+ let type = json_type(value);
6142
+ if (type === "object") {
6143
+ for (let [k, v] of Object.entries(value)) {
6144
+ if (String(k).toLowerCase().includes(term)) return true;
6145
+ if (subtree_matches(v, term)) return true;
6146
+ }
6147
+ return false;
6148
+ }
6149
+ if (type === "array") {
6150
+ for (let v of value) if (subtree_matches(v, term)) return true;
6151
+ return false;
6152
+ }
6153
+ return String(value).toLowerCase().includes(term);
6154
+ }
6155
+ /************************************************************
6156
+ * Fields whose numeric value is an epoch timestamp.
6157
+ *
6158
+ * THE ONE LIST. C_YUI_FORM's jsoneditor takes its
6159
+ * `timestampTag` from here: it used to carry its own copy,
6160
+ * and a copy is how the two stopped agreeing.
6161
+ *
6162
+ * TWO SPELLINGS, AND LAS DOS SON DEL BACKEND. timeranger2
6163
+ * writes `from_t`/`from_tm` in the match_cond of a query and
6164
+ * `fr_t`/`fr_tm` in the metadata of a topic and of a file --
6165
+ * which is what a Raw JSON of a treedb actually shows. With
6166
+ * only the long pair listed, `to_t` got its date and the
6167
+ * `fr_t` right above it did not, which reads as a broken
6168
+ * annotator rather than a missing name.
6169
+ ************************************************************/
6170
+ var TIME_FIELDS = [
6171
+ "__t__",
6172
+ "__tm__",
6173
+ "t",
6174
+ "tm",
6175
+ "time",
6176
+ "from_t",
6177
+ "to_t",
6178
+ "from_tm",
6179
+ "to_tm",
6180
+ "fr_t",
6181
+ "fr_tm",
6182
+ "t_input",
6183
+ "t_output"
6184
+ ];
6185
+ function is_time_field(field) {
6186
+ return TIME_FIELDS.indexOf(field) >= 0;
6187
+ }
6188
+ /************************************************************
6189
+ * Format an epoch value as a local wall-clock string.
6190
+ * Accepts seconds or milliseconds; returns null when the
6191
+ * value is not a usable positive timestamp (0 == "unset").
6192
+ ************************************************************/
6193
+ function format_epoch(value) {
6194
+ if (typeof value !== "number" || !isFinite(value) || value <= 0) return null;
6195
+ let ms = Math.abs(value) < 0xe8d4a51000 ? value * 1e3 : value;
6196
+ let d = new Date(ms);
6197
+ if (isNaN(d.getTime())) return null;
6198
+ return d.toLocaleString();
6199
+ }
6200
+ /************************************************************
6201
+ * The text view's payload: the document as text, indented
6202
+ * four characters, cut at `max_chars`.
6203
+ *
6204
+ * Returns {text, capped}. `capped` is what the caller
6205
+ * announces under the dump — a cut that is not announced
6206
+ * reads as a document that ends there.
6207
+ ************************************************************/
6208
+ function json_text_dump(root, max_chars) {
6209
+ let text;
6210
+ try {
6211
+ text = JSON.stringify(root, null, 4);
6212
+ } catch (e) {
6213
+ return {
6214
+ text: "",
6215
+ capped: false,
6216
+ error: String(e)
6217
+ };
6218
+ }
6219
+ if (text === void 0) return {
6220
+ text: "",
6221
+ capped: false,
6222
+ error: "not representable as JSON"
6223
+ };
6224
+ if (typeof max_chars === "number" && max_chars > 0 && text.length > max_chars) return {
6225
+ text: text.slice(0, max_chars),
6226
+ capped: true
6227
+ };
6228
+ return {
6229
+ text,
6230
+ capped: false
6231
+ };
6232
+ }
6233
+ /************************************************************
6234
+ * Which view a freshly mounted viewer opens on.
6235
+ *
6236
+ * Three answers, in order of authority: what the HOST asked
6237
+ * for, what the READER chose last time, and `fallback`.
6238
+ *
6239
+ * The host wins because a viewer mounted to show a graph has
6240
+ * to show one. The memory comes next, because reopening a
6241
+ * document in the view you just left is the whole point of
6242
+ * remembering it. `fallback` is the floor.
6243
+ *
6244
+ * Anything not in `modes` is treated as unsaid, which covers
6245
+ * an absent value, an empty one, and a stored string from an
6246
+ * older release that named a view no longer offered.
6247
+ ************************************************************/
6248
+ function pick_view_mode(asked, remembered, modes, fallback) {
6249
+ let known = (m) => Array.isArray(modes) && modes.indexOf(m) >= 0;
6250
+ if (known(asked)) return asked;
6251
+ if (known(remembered)) return remembered;
6252
+ return fallback;
6253
+ }
6254
+ /************************************************************
6255
+ * The short label a container row carries beside its size.
6256
+ *
6257
+ * An array of dicts that carry an `id` is a list of RECORDS,
6258
+ * and a row that says only `2: {15}` makes you open all
6259
+ * fifteen fields of every one of them to find out WHICH
6260
+ * record it is. The id is the one field that answers that,
6261
+ * so the row says it without being opened.
6262
+ *
6263
+ * Only a SCALAR id, and only when it is really there: an `id`
6264
+ * that is itself a dict or a list is not a name, it is more
6265
+ * document, and printing `[object Object]` beside the size
6266
+ * would be worse than printing nothing.
6267
+ *
6268
+ * Cut at `max_chars`, because this rides on a row whose job
6269
+ * is to stay one line.
6270
+ ************************************************************/
6271
+ function container_label(value, max_chars) {
6272
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return "";
6273
+ if (!Object.prototype.hasOwnProperty.call(value, "id")) return "";
6274
+ let id = value.id;
6275
+ let t = typeof id;
6276
+ if (t !== "string" && t !== "number" && t !== "boolean") return "";
6277
+ let text = String(id).trim();
6278
+ if (text === "") return "";
6279
+ let cap = typeof max_chars === "number" && max_chars > 0 ? max_chars : 0;
6280
+ if (cap && text.length > cap) return text.slice(0, cap) + "…";
6281
+ return text;
6282
+ }
6283
+ /************************************************************
6284
+ * A dict, in the JSON sense: not an array and not null.
6285
+ ************************************************************/
6286
+ function is_plain_object(v) {
6287
+ return v !== null && typeof v === "object" && !Array.isArray(v);
6288
+ }
6289
+ /************************************************************
6290
+ * Does this container hold ONLY other containers?
6291
+ *
6292
+ * Such a value is not a thing, it is a LIST of things -- and a
6293
+ * list is fully described by the row that names it in its
6294
+ * parent, plus the edges to what it holds. Drawing it as a
6295
+ * card of its own put a node in the middle of the graph with no
6296
+ * data in it, saying nothing the parent's row did not already
6297
+ * say, and pushed everything below it one level down.
6298
+ *
6299
+ * An EMPTY container inside counts as content: `{0}` is
6300
+ * something the card has to show, and there is no child card
6301
+ * to show it instead.
6302
+ ************************************************************/
6303
+ function is_pure_collection(value) {
6304
+ let entries = is_plain_object(value) ? Object.entries(value) : Array.isArray(value) ? value.map((v, i) => [i, v]) : [];
6305
+ if (entries.length === 0) return false;
6306
+ for (let [, v] of entries) if (!has_branch(v)) return false;
6307
+ return true;
6308
+ }
6309
+ /************************************************************
6310
+ * A value that gets a card, or children, of its own.
6311
+ ************************************************************/
6312
+ function has_branch(value) {
6313
+ return is_plain_object(value) && Object.keys(value).length > 0 || Array.isArray(value) && value.length > 0;
6314
+ }
6315
+ /************************************************************
6316
+ * How many branches a container has, which is what the fold
6317
+ * chip counts.
6318
+ ************************************************************/
6319
+ function count_branches(value) {
6320
+ let entries = is_plain_object(value) ? Object.values(value) : Array.isArray(value) ? value : [];
6321
+ let n = 0;
6322
+ for (let v of entries) if (has_branch(v)) n++;
6323
+ return n;
6324
+ }
6038
6325
  //#endregion
6039
6326
  //#region src/c_yui_form.js
6040
6327
  /***********************************************************************
@@ -7163,7 +7450,7 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
7163
7450
  gobj_send_event(gobj, "EV_RECORD_CHANGED", {}, gobj);
7164
7451
  },
7165
7452
  timestampTag: function({ field, value, path }) {
7166
- return field === "__t__" || field === "__tm__" || field === "tm" || field === "t" || field === "time" || field === "from_t" || field === "to_t" || field === "t_input" || field === "t_output" || field === "from_tm" || field === "to_tm";
7453
+ return is_time_field(field);
7167
7454
  }
7168
7455
  }
7169
7456
  });
@@ -10325,268 +10612,6 @@ function ensure_drag_canvas_patch() {
10325
10612
  register(ExtensionCategory.BEHAVIOR, "drag-canvas", DragCanvasTouch);
10326
10613
  __drag_canvas_patched__ = true;
10327
10614
  }
10328
- /************************************************************
10329
- * JSON type discriminator (viewer vocabulary)
10330
- ************************************************************/
10331
- function json_type(value) {
10332
- if (value === null) return "null";
10333
- if (Array.isArray(value)) return "array";
10334
- switch (typeof value) {
10335
- case "string": return "string";
10336
- case "number": return "number";
10337
- case "boolean": return "boolean";
10338
- case "object": return "object";
10339
- default: return "unknown";
10340
- }
10341
- }
10342
- /************************************************************
10343
- * Detect a kw_collapse() sentinel.
10344
- *
10345
- * The kernel replaces an over-limit dict with:
10346
- * { "__collapsed__": { "path": ..., "size": N } }
10347
- * and an over-limit array with:
10348
- * [ { "__collapsed__": { "path": ..., "size": N } } ]
10349
- *
10350
- * Returns { size, path, is_array } or null.
10351
- ************************************************************/
10352
- function is_collapsed(value) {
10353
- if (value && typeof value === "object" && !Array.isArray(value)) {
10354
- let keys = Object.keys(value);
10355
- if (keys.length === 1 && keys[0] === "__collapsed__") {
10356
- let c = value.__collapsed__ || {};
10357
- return {
10358
- size: c.size,
10359
- path: c.path,
10360
- is_array: false
10361
- };
10362
- }
10363
- return null;
10364
- }
10365
- if (Array.isArray(value) && value.length === 1) {
10366
- let e = value[0];
10367
- if (e && typeof e === "object" && !Array.isArray(e) && Object.keys(e).length === 1 && e.__collapsed__) {
10368
- let c = e.__collapsed__ || {};
10369
- return {
10370
- size: c.size,
10371
- path: c.path,
10372
- is_array: true
10373
- };
10374
- }
10375
- }
10376
- return null;
10377
- }
10378
- /************************************************************
10379
- * Join / split absolute path segments (kernel convention)
10380
- ************************************************************/
10381
- function seg_join(segments) {
10382
- return segments.map(function(s) {
10383
- return String(s);
10384
- }).join("`");
10385
- }
10386
- function seg_split(path) {
10387
- if (path === "" || path === null || path === void 0) return [];
10388
- return path.split("`");
10389
- }
10390
- /************************************************************
10391
- * Walk a JSON tree by absolute segments; undefined if the
10392
- * path does not resolve.
10393
- ************************************************************/
10394
- function get_by_segments(root, segments) {
10395
- let v = root;
10396
- for (let seg of segments) {
10397
- if (v === null || v === void 0) return;
10398
- if (Array.isArray(v)) v = v[Number(seg)];
10399
- else if (typeof v === "object") v = v[seg];
10400
- else return;
10401
- }
10402
- return v;
10403
- }
10404
- /************************************************************
10405
- * Set a value at absolute segments (mutates root).
10406
- *
10407
- * segments == [] means "replace the whole root" — the caller
10408
- * must use the RETURNED value in that case (a primitive/array
10409
- * root cannot be mutated in place).
10410
- ************************************************************/
10411
- function set_by_segments(root, segments, new_value) {
10412
- if (segments.length === 0) return new_value;
10413
- let parent = get_by_segments(root, segments.slice(0, -1));
10414
- if (parent === null || parent === void 0 || typeof parent !== "object") return root;
10415
- let last = segments[segments.length - 1];
10416
- if (Array.isArray(parent)) parent[Number(last)] = new_value;
10417
- else parent[last] = new_value;
10418
- return root;
10419
- }
10420
- /************************************************************
10421
- * Does `value` (or any loaded descendant / its own key)
10422
- * contain the lower-cased search term?
10423
- *
10424
- * Collapsed (not-yet-loaded) subtrees can't be searched, so
10425
- * they never match on content — only their key can match,
10426
- * which the renderer checks separately.
10427
- ************************************************************/
10428
- function subtree_matches(value, term) {
10429
- if (!term) return true;
10430
- if (is_collapsed(value)) return false;
10431
- let type = json_type(value);
10432
- if (type === "object") {
10433
- for (let [k, v] of Object.entries(value)) {
10434
- if (String(k).toLowerCase().includes(term)) return true;
10435
- if (subtree_matches(v, term)) return true;
10436
- }
10437
- return false;
10438
- }
10439
- if (type === "array") {
10440
- for (let v of value) if (subtree_matches(v, term)) return true;
10441
- return false;
10442
- }
10443
- return String(value).toLowerCase().includes(term);
10444
- }
10445
- /************************************************************
10446
- * Fields whose numeric value is an epoch timestamp.
10447
- * Mirrors the timestampTag list wired into C_YUI_FORM's
10448
- * jsoneditor control, so the two viewers agree.
10449
- ************************************************************/
10450
- function is_time_field(field) {
10451
- return field === "__t__" || field === "__tm__" || field === "tm" || field === "t" || field === "time" || field === "from_t" || field === "to_t" || field === "t_input" || field === "t_output" || field === "from_tm" || field === "to_tm";
10452
- }
10453
- /************************************************************
10454
- * Format an epoch value as a local wall-clock string.
10455
- * Accepts seconds or milliseconds; returns null when the
10456
- * value is not a usable positive timestamp (0 == "unset").
10457
- ************************************************************/
10458
- function format_epoch(value) {
10459
- if (typeof value !== "number" || !isFinite(value) || value <= 0) return null;
10460
- let ms = Math.abs(value) < 0xe8d4a51000 ? value * 1e3 : value;
10461
- let d = new Date(ms);
10462
- if (isNaN(d.getTime())) return null;
10463
- return d.toLocaleString();
10464
- }
10465
- /************************************************************
10466
- * The text view's payload: the document as text, indented
10467
- * four characters, cut at `max_chars`.
10468
- *
10469
- * Returns {text, capped}. `capped` is what the caller
10470
- * announces under the dump — a cut that is not announced
10471
- * reads as a document that ends there.
10472
- ************************************************************/
10473
- function json_text_dump(root, max_chars) {
10474
- let text;
10475
- try {
10476
- text = JSON.stringify(root, null, 4);
10477
- } catch (e) {
10478
- return {
10479
- text: "",
10480
- capped: false,
10481
- error: String(e)
10482
- };
10483
- }
10484
- if (text === void 0) return {
10485
- text: "",
10486
- capped: false,
10487
- error: "not representable as JSON"
10488
- };
10489
- if (typeof max_chars === "number" && max_chars > 0 && text.length > max_chars) return {
10490
- text: text.slice(0, max_chars),
10491
- capped: true
10492
- };
10493
- return {
10494
- text,
10495
- capped: false
10496
- };
10497
- }
10498
- /************************************************************
10499
- * Which view a freshly mounted viewer opens on.
10500
- *
10501
- * Three answers, in order of authority: what the HOST asked
10502
- * for, what the READER chose last time, and `fallback`.
10503
- *
10504
- * The host wins because a viewer mounted to show a graph has
10505
- * to show one. The memory comes next, because reopening a
10506
- * document in the view you just left is the whole point of
10507
- * remembering it. `fallback` is the floor.
10508
- *
10509
- * Anything not in `modes` is treated as unsaid, which covers
10510
- * an absent value, an empty one, and a stored string from an
10511
- * older release that named a view no longer offered.
10512
- ************************************************************/
10513
- function pick_view_mode(asked, remembered, modes, fallback) {
10514
- let known = (m) => Array.isArray(modes) && modes.indexOf(m) >= 0;
10515
- if (known(asked)) return asked;
10516
- if (known(remembered)) return remembered;
10517
- return fallback;
10518
- }
10519
- /************************************************************
10520
- * The short label a container row carries beside its size.
10521
- *
10522
- * An array of dicts that carry an `id` is a list of RECORDS,
10523
- * and a row that says only `2: {15}` makes you open all
10524
- * fifteen fields of every one of them to find out WHICH
10525
- * record it is. The id is the one field that answers that,
10526
- * so the row says it without being opened.
10527
- *
10528
- * Only a SCALAR id, and only when it is really there: an `id`
10529
- * that is itself a dict or a list is not a name, it is more
10530
- * document, and printing `[object Object]` beside the size
10531
- * would be worse than printing nothing.
10532
- *
10533
- * Cut at `max_chars`, because this rides on a row whose job
10534
- * is to stay one line.
10535
- ************************************************************/
10536
- function container_label(value, max_chars) {
10537
- if (value === null || typeof value !== "object" || Array.isArray(value)) return "";
10538
- if (!Object.prototype.hasOwnProperty.call(value, "id")) return "";
10539
- let id = value.id;
10540
- let t = typeof id;
10541
- if (t !== "string" && t !== "number" && t !== "boolean") return "";
10542
- let text = String(id).trim();
10543
- if (text === "") return "";
10544
- let cap = typeof max_chars === "number" && max_chars > 0 ? max_chars : 0;
10545
- if (cap && text.length > cap) return text.slice(0, cap) + "…";
10546
- return text;
10547
- }
10548
- /************************************************************
10549
- * A dict, in the JSON sense: not an array and not null.
10550
- ************************************************************/
10551
- function is_plain_object(v) {
10552
- return v !== null && typeof v === "object" && !Array.isArray(v);
10553
- }
10554
- /************************************************************
10555
- * Does this container hold ONLY other containers?
10556
- *
10557
- * Such a value is not a thing, it is a LIST of things -- and a
10558
- * list is fully described by the row that names it in its
10559
- * parent, plus the edges to what it holds. Drawing it as a
10560
- * card of its own put a node in the middle of the graph with no
10561
- * data in it, saying nothing the parent's row did not already
10562
- * say, and pushed everything below it one level down.
10563
- *
10564
- * An EMPTY container inside counts as content: `{0}` is
10565
- * something the card has to show, and there is no child card
10566
- * to show it instead.
10567
- ************************************************************/
10568
- function is_pure_collection(value) {
10569
- let entries = is_plain_object(value) ? Object.entries(value) : Array.isArray(value) ? value.map((v, i) => [i, v]) : [];
10570
- if (entries.length === 0) return false;
10571
- for (let [, v] of entries) if (!has_branch(v)) return false;
10572
- return true;
10573
- }
10574
- /************************************************************
10575
- * A value that gets a card, or children, of its own.
10576
- ************************************************************/
10577
- function has_branch(value) {
10578
- return is_plain_object(value) && Object.keys(value).length > 0 || Array.isArray(value) && value.length > 0;
10579
- }
10580
- /************************************************************
10581
- * How many branches a container has, which is what the fold
10582
- * chip counts.
10583
- ************************************************************/
10584
- function count_branches(value) {
10585
- let entries = is_plain_object(value) ? Object.values(value) : Array.isArray(value) ? value : [];
10586
- let n = 0;
10587
- for (let v of entries) if (has_branch(v)) n++;
10588
- return n;
10589
- }
10590
10615
  //#endregion
10591
10616
  //#region src/c_yui_json_graph.js
10592
10617
  /***********************************************************************
@@ -54322,6 +54347,7 @@ function create_popover_base(left, top, className, borderColor, minWidth) {
54322
54347
  popover.style.cssText = "position:absolute;left:" + left + "px;top:" + top + "px;background:var(--bulma-scheme-main, #fff);color:var(--bulma-text-strong, #1A1A1A);border:1px solid " + borderColor + ";border-radius:6px;padding:12px;z-index:100;pointer-events:all;box-shadow:0 4px 12px rgba(0,0,0,0.15);min-width:" + minWidth + "px;font-size:13px;";
54323
54348
  popover.addEventListener("click", (e) => e.stopPropagation());
54324
54349
  popover.addEventListener("pointerdown", (e) => e.stopPropagation());
54350
+ popover.addEventListener("wheel", (e) => e.stopPropagation());
54325
54351
  return popover;
54326
54352
  }
54327
54353
  /************************************************************
@@ -55771,6 +55797,7 @@ function show_node_detail_popover(gobj, node_id) {
55771
55797
  popover.style.maxWidth = "320px";
55772
55798
  popover.style.maxHeight = "60%";
55773
55799
  popover.style.overflow = "auto";
55800
+ popover.style.padding = "0 12px 12px";
55774
55801
  popover.style.fontFamily = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif";
55775
55802
  let rows = "";
55776
55803
  for (let key of Object.keys(record)) {
@@ -55781,9 +55808,9 @@ function show_node_detail_popover(gobj, node_id) {
55781
55808
  else if (typeof v === "object") vs = JSON.stringify(v);
55782
55809
  else vs = String(v);
55783
55810
  if (vs.length > 240) vs = vs.slice(0, 240) + "…";
55784
- rows += `<div style="display:flex;gap:10px;padding:5px 0;border-top:1px solid ${rowbd};"><div style="flex:0 0 38%;color:${sub};font-size:12px;word-break:break-word;">${escapeHtml(key)}</div><div style="flex:1 1 auto;font-size:12px;word-break:break-word;">${escapeHtml(vs)}</div></div>`;
55811
+ rows += `<div class="g6-detail-row" style="border-top:1px solid ${rowbd};"><div class="g6-detail-key" style="color:${sub};">${escapeHtml(key)}</div><div class="g6-detail-val">${escapeHtml(vs)}</div></div>`;
55785
55812
  }
55786
- popover.innerHTML = `<div style="display:flex;align-items:flex-start;gap:8px;margin-bottom:6px;"><div style="flex:1 1 auto;min-width:0;"><div style="font-size:14px;font-weight:600;word-break:break-word;">${escapeHtml(id)}</div><div style="font-size:11px;color:${sub};margin-top:2px;">${escapeHtml(topic)}</div></div><div class="g6-detail-close" style="flex:0 0 auto;cursor:pointer;font-size:16px;line-height:1;color:${sub};padding:0 2px;">×</div></div>` + (rows || `<div style="font-size:12px;color:${sub};">${escapeHtml(t("no fields"))}</div>`);
55813
+ popover.innerHTML = `<div class="g6-detail-head" style="background:${bg};"><div style="flex:1 1 auto;min-width:0;"><div class="g6-detail-id">${escapeHtml(id)}</div><div class="g6-detail-topic" style="color:${sub};">${escapeHtml(topic)}</div></div><div class="g6-detail-close" role="button" tabindex="0" aria-label="${escapeHtml(t("close"))}" title="${escapeHtml(t("close"))}" style="color:${sub};">×</div></div>` + (rows || `<div class="g6-detail-empty" style="color:${sub};">${escapeHtml(t("no fields"))}</div>`);
55787
55814
  let close_el = popover.querySelector(".g6-detail-close");
55788
55815
  if (close_el) close_el.addEventListener("click", (e) => {
55789
55816
  e.stopPropagation();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.23.43",
3
+ "version": "7.23.45",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -29,7 +29,7 @@
29
29
  "devDependencies": {
30
30
  "@rollup/plugin-terser": "^1.0.0",
31
31
  "@vitest/coverage-v8": "^4.1.10",
32
- "@yuneta/gobj-js": "^7.13.2",
32
+ "@yuneta/gobj-js": "^7.16.1",
33
33
  "vite": "^8.2.1",
34
34
  "vitest": "^4.1.10"
35
35
  },