@yuneta/gobj-ui 5.15.0 → 5.17.0

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.
@@ -15500,7 +15500,7 @@ function request_print_tranger$1(gobj, path) {
15500
15500
  * would travel, be refused by the yuno, and come back as a toast --
15501
15501
  * after the table had already drawn the row.
15502
15502
  ***************************************************************/
15503
- function refuse_if_readonly$1(gobj, event) {
15503
+ function refuse_if_readonly$2(gobj, event) {
15504
15504
  if (!gobj_read_bool_attr(gobj, "readonly")) return false;
15505
15505
  log_error(`${gobj_short_name(gobj)}: ${event} refused, treedb '${gobj_read_str_attr(gobj, "treedb_name")}' is READ-ONLY`);
15506
15506
  return true;
@@ -15572,7 +15572,8 @@ function ac_mt_command_answer$1(gobj, event, kw, src) {
15572
15572
  treedb_name: kw_get_str(gobj, kw_command, "treedb_name", "", 0),
15573
15573
  topic_name: kw_get_str(gobj, kw_command, "topic_name", "", 0),
15574
15574
  record: kw_get_dict(gobj, kw_command, "record", {}, 0),
15575
- created: command === "create-node"
15575
+ created: command === "create-node",
15576
+ command
15576
15577
  });
15577
15578
  break;
15578
15579
  case "delete-node": break;
@@ -15777,7 +15778,7 @@ function ac_treedb_node_deleted$1(gobj, event, kw, src) {
15777
15778
  * }
15778
15779
  ********************************************/
15779
15780
  function ac_create_record(gobj, event, kw, src) {
15780
- if (refuse_if_readonly$1(gobj, event)) return -1;
15781
+ if (refuse_if_readonly$2(gobj, event)) return -1;
15781
15782
  let treedb_name = gobj_read_str_attr(gobj, "treedb_name");
15782
15783
  let topic_name = gobj_read_attr(src, "topic_name");
15783
15784
  let record = kw.record;
@@ -15795,7 +15796,7 @@ function ac_create_record(gobj, event, kw, src) {
15795
15796
  * }
15796
15797
  ********************************************/
15797
15798
  function ac_update_record(gobj, event, kw, src) {
15798
- if (refuse_if_readonly$1(gobj, event)) return -1;
15799
+ if (refuse_if_readonly$2(gobj, event)) return -1;
15799
15800
  let treedb_name = gobj_read_str_attr(gobj, "treedb_name");
15800
15801
  let topic_name = gobj_read_attr(src, "topic_name");
15801
15802
  let record = kw.record;
@@ -15812,7 +15813,7 @@ function ac_update_record(gobj, event, kw, src) {
15812
15813
  * }
15813
15814
  ********************************************/
15814
15815
  function ac_delete_record(gobj, event, kw, src) {
15815
- if (refuse_if_readonly$1(gobj, event)) return -1;
15816
+ if (refuse_if_readonly$2(gobj, event)) return -1;
15816
15817
  let treedb_name = gobj_read_str_attr(gobj, "treedb_name");
15817
15818
  let topic_name = gobj_read_attr(src, "topic_name");
15818
15819
  let record = kw.record;
@@ -17633,7 +17634,7 @@ function write_plan(gobj) {
17633
17634
  * A read-only topic refuses the write EVENTS too, not only their
17634
17635
  * buttons.
17635
17636
  ***************************************************************/
17636
- function refuse_if_readonly(gobj, event) {
17637
+ function refuse_if_readonly$1(gobj, event) {
17637
17638
  if (!gobj_read_bool_attr(gobj, "readonly")) return false;
17638
17639
  log_error(`${gobj_short_name(gobj)}: ${event} refused, topic '${gobj_read_str_attr(gobj, "topic_name")}' is READ-ONLY`);
17639
17640
  return true;
@@ -17705,7 +17706,10 @@ function ac_load_node_updated(gobj, event, kw, src) {
17705
17706
  return -1;
17706
17707
  }
17707
17708
  let tabulator = gobj_read_attr(gobj, "tabulator");
17708
- if (tabulator) for (let record of data) tabulator.updateData([record]);
17709
+ if (tabulator) {
17710
+ for (let record of data) if (tabulator.getRow(record.id)) tabulator.updateData([record]);
17711
+ else if (tabulator.getDataCount() > 0) log_error(`${gobj_short_name(gobj)}: updated node is not in the loaded table: ${record.id}`);
17712
+ }
17709
17713
  return 0;
17710
17714
  }
17711
17715
  /************************************************************
@@ -17730,7 +17734,7 @@ function ac_node_deleted$1(gobj, event, kw, src) {
17730
17734
  * and the Op column is visible (with row's edit/delete icons)
17731
17735
  ************************************************************/
17732
17736
  function ac_edition_mode(gobj, event, kw, src) {
17733
- if (refuse_if_readonly(gobj, event)) return -1;
17737
+ if (refuse_if_readonly$1(gobj, event)) return -1;
17734
17738
  let tabulator = gobj_read_attr(gobj, "tabulator");
17735
17739
  let editable = gobj_read_bool_attr(gobj, "editable");
17736
17740
  editable = !editable;
@@ -17774,7 +17778,7 @@ function ac_edition_mode(gobj, event, kw, src) {
17774
17778
  * - the backend will broadcast the new record that will be added to table
17775
17779
  ************************************************************/
17776
17780
  function ac_new_row(gobj, event, kw, src) {
17777
- if (refuse_if_readonly(gobj, event)) return -1;
17781
+ if (refuse_if_readonly$1(gobj, event)) return -1;
17778
17782
  let row = {};
17779
17783
  let desc = gobj_read_attr(gobj, "desc");
17780
17784
  for (let i = 0; i < desc.cols.length; i++) {
@@ -17804,7 +17808,7 @@ function ac_new_row(gobj, event, kw, src) {
17804
17808
  * kw {index:, row:}
17805
17809
  ************************************************************/
17806
17810
  function ac_delete_rows(gobj, event, kw, src) {
17807
- if (refuse_if_readonly(gobj, event)) return -1;
17811
+ if (refuse_if_readonly$1(gobj, event)) return -1;
17808
17812
  let tabulator = gobj_read_attr(gobj, "tabulator");
17809
17813
  if (json_size(kw) === 0) {
17810
17814
  let rows = tabulator.getSelectedData();
@@ -17868,7 +17872,7 @@ function ac_copy_rows(gobj, event, kw, src) {
17868
17872
  * From internal
17869
17873
  ************************************************************/
17870
17874
  function ac_paste_rows(gobj, event, kw, src) {
17871
- if (refuse_if_readonly(gobj, event)) return -1;
17875
+ if (refuse_if_readonly$1(gobj, event)) return -1;
17872
17876
  if (kw.topic_name === gobj_read_str_attr(gobj, "topic_name") && kw.treedb_name === gobj_read_str_attr(gobj, "treedb_name")) for (let row of kw.rows) gobj_publish_event(gobj, "EV_CREATE_RECORD", {
17873
17877
  topic_name: gobj_read_str_attr(gobj, "topic_name"),
17874
17878
  record: row
@@ -17881,7 +17885,7 @@ function ac_paste_rows(gobj, event, kw, src) {
17881
17885
  * fkeys, times and numbers). Route by the form's mode.
17882
17886
  ************************************************************/
17883
17887
  function ac_form_save_record(gobj, event, kw, src) {
17884
- if (refuse_if_readonly(gobj, event)) return -1;
17888
+ if (refuse_if_readonly$1(gobj, event)) return -1;
17885
17889
  let mode = gobj_read_str_attr(src, "form_mode");
17886
17890
  gobj_publish_event(gobj, mode === "create" ? "EV_CREATE_RECORD" : "EV_UPDATE_RECORD", {
17887
17891
  topic_name: gobj_read_str_attr(gobj, "topic_name"),
@@ -18289,6 +18293,7 @@ var attrs_table$3 = [
18289
18293
  SDATA(data_type_t.DTP_STRING, "treedb_name", 0, null, "Remote service treedb name"),
18290
18294
  SDATA(data_type_t.DTP_DICT, "descs", 0, null, "Descriptions of topics obtained"),
18291
18295
  SDATA(data_type_t.DTP_BOOLEAN, "system", 0, false, "Manage system topics (true) or user topics (false)"),
18296
+ SDATA(data_type_t.DTP_BOOLEAN, "readonly", 0, false, "The whole treedb is read-only: the graph drops its `edition` mode (the only one that draws write affordances) and refuses every write event. Set it when this yuno is not the MASTER of the treedb's tranger -- only the master can write, and the yuno answers 'READ-ONLY' to a write since SDK 7.13.0 (ask `treedb-info`)"),
18292
18297
  SDATA(data_type_t.DTP_STRING, "back_route", 0, "", "Optional hash route for a '← topics' button back to the topics grid (host-supplied; empty = no button)"),
18293
18298
  SDATA(data_type_t.DTP_STRING, "base_route", 0, "", "This view's base route (host-supplied); used to declare its per-topic focus sub-routes to the site map (ROUTING.md contributor)."),
18294
18299
  SDATA(data_type_t.DTP_DICT, "records", 0, "{}", "Data of topics"),
@@ -18339,6 +18344,7 @@ function mt_create$3(gobj) {
18339
18344
  if (!subscriber) subscriber = gobj_parent(gobj);
18340
18345
  gobj_subscribe_event(gobj, null, {}, subscriber);
18341
18346
  if (!priv.treedb_name) log_error(`${gobj_name(gobj)} -> treedb_name not configured`);
18347
+ if (gobj_read_bool_attr(gobj, "readonly") && priv.operation_mode === "edition") gobj_write_str_attr(gobj, "operation_mode", "reading");
18342
18348
  let canvas_id = clean_name(gobj_name(gobj)) + "-canvas";
18343
18349
  gobj_write_attr(gobj, "canvas_id", canvas_id);
18344
18350
  let $container_canvas = build_ui$3(gobj).querySelector(`#${priv.canvas_id}`);
@@ -18493,6 +18499,8 @@ function destroy_ui(gobj) {
18493
18499
  ************************************************************/
18494
18500
  function make_toolbar(gobj) {
18495
18501
  gobj.priv;
18502
+ let modes = gobj_read_attr(gobj, "operation_modes");
18503
+ if (gobj_read_bool_attr(gobj, "readonly")) modes = modes.filter((mode) => mode !== "edition");
18496
18504
  const $toolbar_header = yui_toolbar({}, [
18497
18505
  [
18498
18506
  "div",
@@ -18531,7 +18539,7 @@ function make_toolbar(gobj) {
18531
18539
  [[
18532
18540
  "select",
18533
18541
  { class: "GRAPH_MODE_SELECT" },
18534
- gobj_read_attr(gobj, "operation_modes").map((item) => [
18542
+ modes.map((item) => [
18535
18543
  "option",
18536
18544
  {},
18537
18545
  item
@@ -19083,6 +19091,19 @@ function refresh_data(gobj) {
19083
19091
  if (priv.gobj_nodes_tree) gobj_send_event(priv.gobj_nodes_tree, "EV_CLEAR_DATA", {}, gobj);
19084
19092
  request_treedb_descs(gobj);
19085
19093
  }
19094
+ /********************************************
19095
+ * The last gate before a write leaves for the yuno. The `edition`
19096
+ * mode is gone on a replica, so no button can raise these -- but the
19097
+ * G6 child also publishes them from its undo/redo history and from
19098
+ * saving the node geometry, and a write that got this far would
19099
+ * travel, be refused by the yuno, and come back as a toast with the
19100
+ * graph already redrawn.
19101
+ ********************************************/
19102
+ function refuse_if_readonly(gobj, event) {
19103
+ if (!gobj_read_bool_attr(gobj, "readonly")) return false;
19104
+ log_error(`${gobj_short_name(gobj)}: ${event} refused, treedb '${gobj_read_str_attr(gobj, "treedb_name")}' is READ-ONLY`);
19105
+ return true;
19106
+ }
19086
19107
  /***************************
19087
19108
  * Actions
19088
19109
  ***************************/
@@ -19151,7 +19172,15 @@ function ac_mt_command_answer(gobj, event, kw, src) {
19151
19172
  case "update-node":
19152
19173
  case "delete-node":
19153
19174
  case "link-nodes":
19154
- case "unlink-nodes": break;
19175
+ case "unlink-nodes":
19176
+ if (result >= 0 && kw_get_str(gobj, kw_command, "topic_name", "", 0) !== "__graphs__") gobj_publish_event(gobj, "EV_RECORD_WRITTEN", {
19177
+ treedb_name: kw_get_str(gobj, kw_command, "treedb_name", "", 0),
19178
+ topic_name: kw_get_str(gobj, kw_command, "topic_name", "", 0),
19179
+ record: kw_get_dict(gobj, kw_command, "record", {}, 0),
19180
+ created: command === "create-node",
19181
+ command
19182
+ });
19183
+ break;
19155
19184
  default: log_error(`${gobj_short_name(gobj)} Command unknown: ${command}`);
19156
19185
  }
19157
19186
  return 0;
@@ -19370,7 +19399,9 @@ function ac_edge_clicked(gobj, event, kw, src) {
19370
19399
  * Send to backend
19371
19400
  ********************************************/
19372
19401
  function ac_create_node(gobj, event, kw, src) {
19373
- let treedb_name = gobj.priv.treedb_name;
19402
+ let priv = gobj.priv;
19403
+ if (refuse_if_readonly(gobj, event)) return -1;
19404
+ let treedb_name = priv.treedb_name;
19374
19405
  let topic_name = kw.topic_name;
19375
19406
  let record = kw.record;
19376
19407
  return treedb_create_node(gobj, treedb_name, topic_name, record, kw.options || {});
@@ -19385,7 +19416,9 @@ function ac_create_node(gobj, event, kw, src) {
19385
19416
  * Send to backend
19386
19417
  ********************************************/
19387
19418
  function ac_update_node(gobj, event, kw, src) {
19388
- let treedb_name = gobj.priv.treedb_name;
19419
+ let priv = gobj.priv;
19420
+ if (refuse_if_readonly(gobj, event)) return -1;
19421
+ let treedb_name = priv.treedb_name;
19389
19422
  let topic_name = kw.topic_name;
19390
19423
  let record = kw.record;
19391
19424
  return treedb_update_node(gobj, treedb_name, topic_name, record, kw.options || {});
@@ -19400,7 +19433,9 @@ function ac_update_node(gobj, event, kw, src) {
19400
19433
  * Send to backend
19401
19434
  ********************************************/
19402
19435
  function ac_delete_node(gobj, event, kw, src) {
19403
- let treedb_name = gobj.priv.treedb_name;
19436
+ let priv = gobj.priv;
19437
+ if (refuse_if_readonly(gobj, event)) return -1;
19438
+ let treedb_name = priv.treedb_name;
19404
19439
  let topic_name = kw.topic_name;
19405
19440
  let record = kw.record;
19406
19441
  return treedb_delete_node(gobj, treedb_name, topic_name, record, kw.options || {});
@@ -19415,7 +19450,9 @@ function ac_delete_node(gobj, event, kw, src) {
19415
19450
  * Send to backend
19416
19451
  ********************************************/
19417
19452
  function ac_link_nodes(gobj, event, kw, src) {
19418
- let treedb_name = gobj.priv.treedb_name;
19453
+ let priv = gobj.priv;
19454
+ if (refuse_if_readonly(gobj, event)) return -1;
19455
+ let treedb_name = priv.treedb_name;
19419
19456
  let parent_ref = kw.parent_ref;
19420
19457
  let child_ref = kw.child_ref;
19421
19458
  return treedb_link_nodes(gobj, treedb_name, parent_ref, child_ref, kw.options || {});
@@ -19430,7 +19467,9 @@ function ac_link_nodes(gobj, event, kw, src) {
19430
19467
  * Send to backend
19431
19468
  ********************************************/
19432
19469
  function ac_unlink_nodes(gobj, event, kw, src) {
19433
- let treedb_name = gobj.priv.treedb_name;
19470
+ let priv = gobj.priv;
19471
+ if (refuse_if_readonly(gobj, event)) return -1;
19472
+ let treedb_name = priv.treedb_name;
19434
19473
  let parent_ref = kw.parent_ref;
19435
19474
  let child_ref = kw.child_ref;
19436
19475
  return treedb_unlink_nodes(gobj, treedb_name, parent_ref, child_ref, kw.options || {});
@@ -19470,6 +19509,10 @@ function ac_set_layout$1(gobj, event, kw, src) {
19470
19509
  function ac_set_operation_mode$1(gobj, event, kw, src) {
19471
19510
  let priv = gobj.priv;
19472
19511
  let operation_mode = kw.operation_mode;
19512
+ if (operation_mode === "edition" && gobj_read_bool_attr(gobj, "readonly")) {
19513
+ log_error(`${gobj_short_name(gobj)}: edition mode refused, treedb '${gobj_read_str_attr(gobj, "treedb_name")}' is READ-ONLY`);
19514
+ return -1;
19515
+ }
19473
19516
  gobj_write_str_attr(gobj, "operation_mode", operation_mode);
19474
19517
  gobj_save_persistent_attrs(gobj, "operation_mode");
19475
19518
  let $operation_mode_select = priv.$container.querySelector(".GRAPH_MODE_SELECT");
@@ -19691,6 +19734,7 @@ function create_gclass$3(gclass_name) {
19691
19734
  ["EV_SET_OPERATION_MODE", 0],
19692
19735
  ["EV_SET_FOCUS_TOPIC", 0],
19693
19736
  ["EV_OPERATION_MODE_CHANGED", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
19737
+ ["EV_RECORD_WRITTEN", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
19694
19738
  ["EV_SHOW", 0],
19695
19739
  ["EV_HIDE", 0],
19696
19740
  ["EV_TRANSPORT_STATE", 0]
@@ -19710,11 +19754,26 @@ function register_c_yui_treedb_graph() {
19710
19754
  /***********************************************************************
19711
19755
  * c_yui_treedb_schema.js
19712
19756
  *
19713
- * Schema-graph landing (prototype): the treedb drawn as a GRAPH OF
19714
- * TOPICS one node per topic, one edge per hook/fkey relationship
19715
- * from the schema `descs` alone (no data, no backend calls). A
19716
- * node click opens that topic's table (a real hash navigation via
19717
- * the host-supplied `node_route`). An alternate landing to the
19757
+ * Schema-graph landing: the treedb drawn the way its `.c` literal
19758
+ * draws it in ASCII (treedb_schema_*.c, treedb_system_schema.c)
19759
+ * one CARD per topic listing its fields in schema order, one edge
19760
+ * per hook, from the row that declares the hook to the fkey row of
19761
+ * the child it names. Built from the schema `descs` alone: no data,
19762
+ * no backend calls.
19763
+ *
19764
+ * WHY THE CARD AND NOT A DOT. A topic is its fields; a schema is
19765
+ * read to find out what a topic holds and what links to what. A
19766
+ * graph of labelled dots answers neither, and the node graph next
19767
+ * door (C_G6_NODES_TREE) answers a different question entirely —
19768
+ * it draws the RECORDS, so on a treedb whose records are schemas
19769
+ * it draws one box per column, hundreds of them, each labelled by
19770
+ * a pkey that may be a rowid. This view draws the schema itself.
19771
+ *
19772
+ * The marks are the notation of the `.c` literals, so the drawing
19773
+ * and the source read the same — see schema_rows().
19774
+ *
19775
+ * A node click opens that topic's table (a real hash navigation
19776
+ * via the host-supplied `node_route`). An alternate landing to the
19718
19777
  * topic cards, in the spirit of "every treedb is a graph".
19719
19778
  *
19720
19779
  * Copyright (c) 2026, ArtGins.
@@ -19724,6 +19783,27 @@ function register_c_yui_treedb_graph() {
19724
19783
  * Constants
19725
19784
  ***************************************************************/
19726
19785
  var GCLASS_NAME$2 = "C_YUI_TREEDB_SCHEMA";
19786
+ /************************************************************
19787
+ * Geometry of a card, in one place: the row height is what
19788
+ * turns a field's index into the vertical position of its
19789
+ * port, so an edge leaves the very row that declares the
19790
+ * hook and lands on the row that declares the fkey.
19791
+ ************************************************************/
19792
+ var CARD_HEADER_H = 30;
19793
+ var CARD_ROW_H = 19;
19794
+ var CARD_PAD_V = 7;
19795
+ var CARD_MIN_W = 210;
19796
+ var CARD_MAX_W = 340;
19797
+ var topic_colors = [
19798
+ "rgb(237, 201, 73)",
19799
+ "rgb(118, 183, 178)",
19800
+ "rgb(255, 157, 167)",
19801
+ "rgb(175, 122, 161)",
19802
+ "rgb(89, 161, 79)",
19803
+ "rgb(186, 176, 171)",
19804
+ "rgb(66, 146, 198)"
19805
+ ];
19806
+ var SYSTEM_TOPIC_COLOR = "rgb(148, 163, 184)";
19727
19807
  /***************************************************************
19728
19808
  * Data
19729
19809
  ***************************************************************/
@@ -19785,48 +19865,254 @@ function build_ui$2(gobj) {
19785
19865
  gobj_write_attr(gobj, "$container", $container);
19786
19866
  }
19787
19867
  /************************************************************
19868
+ * The shape of a hook/fkey field, from the type it declares.
19869
+ * `dict` and `object` are the SAME thing to treedb (both build
19870
+ * a json object keyed by child id), and so are `list` and
19871
+ * `array` — see the hook/fkey switches in tr_treedb.c. A
19872
+ * `string` holds one reference, so it draws as one.
19873
+ ************************************************************/
19874
+ function hook_mark(type) {
19875
+ if (type === "dict" || type === "object") return "{}";
19876
+ if (type === "list" || type === "array") return "[]";
19877
+ return "()";
19878
+ }
19879
+ /************************************************************
19880
+ * The fields of one topic, as the schema declares them.
19881
+ *
19882
+ * The marks are the notation of the schema `.c` literals
19883
+ * (treedb_schema_*.c, treedb_system_schema.c), so the
19884
+ * drawing and the source read the same:
19885
+ * {} dict hook (N unique children)
19886
+ * [] list hook (n not-unique children)
19887
+ * () 1 child
19888
+ * (↖) 1 fkey (1 parent)
19889
+ * [↖] n fkeys (n parents)
19890
+ * {↖} N fkeys (N parents)
19891
+ * * required
19892
+ * # the primary key
19893
+ *
19894
+ * A column can be BOTH hook and fkey, so the flags are read
19895
+ * here rather than through treedb_get_field_desc's single
19896
+ * `type`, which keeps only the last flag it saw.
19897
+ ************************************************************/
19898
+ function schema_rows(desc) {
19899
+ let rows = [];
19900
+ if (!is_object(desc) || !Array.isArray(desc.cols)) return rows;
19901
+ for (let col of desc.cols) {
19902
+ if (!is_object(col) || !col.id) continue;
19903
+ let flags = Array.isArray(col.flag) ? col.flag : [];
19904
+ let is_hook = flags.indexOf("hook") >= 0;
19905
+ let is_fkey = flags.indexOf("fkey") >= 0;
19906
+ let mark = "";
19907
+ if (is_hook) mark = hook_mark(col.type);
19908
+ if (is_fkey) {
19909
+ let fmark = hook_mark(col.type).replace("}", "↖}").replace("]", "↖]").replace(")", "↖)");
19910
+ mark = mark ? mark + " " + fmark : fmark;
19911
+ }
19912
+ rows.push({
19913
+ name: col.id,
19914
+ type: col.type || "",
19915
+ mark,
19916
+ is_hook,
19917
+ is_fkey,
19918
+ is_pkey: col.id === desc.pkey,
19919
+ required: flags.indexOf("required") >= 0 || flags.indexOf("notnull") >= 0
19920
+ });
19921
+ }
19922
+ return rows;
19923
+ }
19924
+ /************************************************************
19925
+ * Size of the card that holds those rows. The width follows
19926
+ * the longest line so a long field name is not clipped into
19927
+ * an ellipsis — the point of the drawing is to read them.
19928
+ ************************************************************/
19929
+ function card_size(rows) {
19930
+ let longest = 0;
19931
+ for (let row of rows) {
19932
+ let len = row.name.length + (row.mark ? row.mark.length + 2 : 0) + 2;
19933
+ if (len > longest) longest = len;
19934
+ }
19935
+ let w = Math.round(26 + longest * 6.6);
19936
+ if (w < CARD_MIN_W) w = CARD_MIN_W;
19937
+ if (w > CARD_MAX_W) w = CARD_MAX_W;
19938
+ let h = 44 + rows.length * CARD_ROW_H;
19939
+ return [w, h];
19940
+ }
19941
+ /************************************************************
19942
+ * Vertical placement of a field's port, as a fraction of the
19943
+ * card height: the centre of its own row.
19944
+ ************************************************************/
19945
+ function row_placement(index, height) {
19946
+ return (37 + index * CARD_ROW_H + CARD_ROW_H / 2) / height;
19947
+ }
19948
+ /************************************************************
19949
+ * The card itself. Header = topic name on its topic colour;
19950
+ * body = one line per field, in schema order.
19951
+ ************************************************************/
19952
+ function build_card_innerHTML(topic, rows, color, dark) {
19953
+ let surface = dark ? "#1b2230" : "#ffffff";
19954
+ let bg = dark ? `color-mix(in srgb, ${color} 18%, #232b38)` : `color-mix(in srgb, ${color} 6%, ${surface})`;
19955
+ let border = dark ? `color-mix(in srgb, ${color} 85%, #ffffff)` : color;
19956
+ let header_bg = dark ? `color-mix(in srgb, ${color} 45%, #2c3542)` : `color-mix(in srgb, ${color} 28%, ${surface})`;
19957
+ let title_color = dark ? "#e8eaed" : "#0f172a";
19958
+ let text_color = dark ? "#d3d8de" : "#334155";
19959
+ let mute_color = dark ? "#98a2b0" : "#7c8694";
19960
+ let rule_color = dark ? "rgba(255,255,255,0.06)" : "rgba(15,23,42,0.05)";
19961
+ let shadow = dark ? "0 1px 3px rgba(0,0,0,0.45), 0 1px 2px rgba(0,0,0,0.30)" : "0 1px 3px rgba(15,23,42,0.12), 0 1px 2px rgba(15,23,42,0.06)";
19962
+ let rows_html = "";
19963
+ for (let row of rows) {
19964
+ let lead = row.is_pkey ? "#" : row.required ? "*" : "";
19965
+ let name_weight = row.is_pkey || row.required ? "600" : "400";
19966
+ let name_color = row.is_hook || row.is_fkey ? title_color : text_color;
19967
+ rows_html += ` <div class="TREEDB_SCHEMA_FIELD" style="
19968
+ display: flex; align-items: center; gap: 6px;
19969
+ height: ${CARD_ROW_H}px; line-height: ${CARD_ROW_H}px;
19970
+ padding: 0 10px; border-top: 1px solid ${rule_color};
19971
+ ">
19972
+ <span style="
19973
+ width: 8px; flex: 0 0 8px; text-align: center;
19974
+ color: ${mute_color}; font-size: 11px;
19975
+ ">${lead}</span>
19976
+ <span style="
19977
+ flex: 1 1 auto; min-width: 0; overflow: hidden;
19978
+ text-overflow: ellipsis; white-space: nowrap;
19979
+ font-size: 12px; font-weight: ${name_weight}; color: ${name_color};
19980
+ ">${escapeHtml(row.name)}</span>
19981
+ <span style="
19982
+ flex: 0 0 auto; font-size: 11px; color: ${mute_color};
19983
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
19984
+ ">${escapeHtml(row.mark)}</span>
19985
+ </div>
19986
+ `;
19987
+ }
19988
+ return `
19989
+ <div class="TREEDB_SCHEMA_CARD" title="${escapeHtml(topic)}" style="
19990
+ box-sizing: border-box;
19991
+ width: 100%;
19992
+ height: 100%;
19993
+ background: ${bg};
19994
+ border: 1.5px solid ${border};
19995
+ border-radius: 8px;
19996
+ box-shadow: ${shadow};
19997
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
19998
+ display: flex;
19999
+ flex-direction: column;
20000
+ overflow: hidden;
20001
+ cursor: pointer;
20002
+ ">
20003
+ <div class="TREEDB_SCHEMA_CARD_HEADER" style="
20004
+ height: ${CARD_HEADER_H}px; line-height: ${CARD_HEADER_H}px;
20005
+ flex: 0 0 ${CARD_HEADER_H}px;
20006
+ padding: 0 10px; background: ${header_bg}; color: ${title_color};
20007
+ font-size: 13px; font-weight: 700;
20008
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
20009
+ ">${escapeHtml(topic)}</div>
20010
+ <div class="TREEDB_SCHEMA_CARD_BODY" style="
20011
+ flex: 1 1 auto; padding: ${CARD_PAD_V}px 0; overflow: hidden;
20012
+ ">
20013
+ ${rows_html} </div>
20014
+ </div>
20015
+ `;
20016
+ }
20017
+ /************************************************************
19788
20018
  * Derive {nodes, edges} from the schema.
19789
- * Node = a topic. Edge = a hook (parent -> child) or an
19790
- * fkey (child -> parent, reversed to parent -> child), deduped.
20019
+ *
20020
+ * Node = a topic, drawn as the card the `.c` literal draws in
20021
+ * ASCII: its name and its fields. Edge = a hook, from the row
20022
+ * that declares it to the fkey row of the child it names —
20023
+ * `'hook': {'users': 'departments'}` says both ends, so the
20024
+ * arrow can land where the `.c` drawing lands it. An fkey
20025
+ * whose parent declares no hook still gets its edge, or a
20026
+ * half-declared schema would draw as disconnected.
20027
+ *
19791
20028
  * Left-to-right dagre follows the parent -> child data flow.
19792
20029
  ************************************************************/
19793
20030
  function schema_to_graph(gobj) {
19794
20031
  let descs = gobj_read_attr(gobj, "descs");
19795
20032
  let system = gobj_read_bool_attr(gobj, "system");
20033
+ let dark = yui_is_dark();
19796
20034
  let nodes = [];
19797
- let topic_set = {};
20035
+ let cards = {};
19798
20036
  if (!is_object(descs)) return {
19799
20037
  nodes,
19800
20038
  edges: []
19801
20039
  };
20040
+ let idx = 0;
19802
20041
  for (let topic of Object.keys(descs)) {
19803
- if (!system && topic.substring(0, 2) === "__") continue;
19804
- topic_set[topic] = true;
20042
+ let is_system = topic.substring(0, 2) === "__";
20043
+ if (!system && is_system) continue;
20044
+ let rows = schema_rows(descs[topic]);
20045
+ let color;
20046
+ if (is_system) color = SYSTEM_TOPIC_COLOR;
20047
+ else {
20048
+ color = topic_colors[idx % topic_colors.length];
20049
+ idx++;
20050
+ }
20051
+ cards[topic] = {
20052
+ rows,
20053
+ size: card_size(rows),
20054
+ color
20055
+ };
20056
+ }
20057
+ for (let topic of Object.keys(cards)) {
20058
+ let card = cards[topic];
20059
+ let [w, h] = card.size;
20060
+ let ports = [];
20061
+ for (let i = 0; i < card.rows.length; i++) {
20062
+ let row = card.rows[i];
20063
+ if (!row.is_hook && !row.is_fkey) continue;
20064
+ ports.push({
20065
+ key: row.name,
20066
+ placement: [row.is_hook ? 1 : 0, row_placement(i, h)],
20067
+ fill: card.color,
20068
+ stroke: getStrokeColor(card.color)
20069
+ });
20070
+ }
19805
20071
  nodes.push({
19806
20072
  id: topic,
19807
- data: { topic_name: topic }
20073
+ type: "html",
20074
+ style: {
20075
+ size: [w, h],
20076
+ dx: -w / 2,
20077
+ dy: -h / 2,
20078
+ innerHTML: build_card_innerHTML(topic, card.rows, card.color, dark),
20079
+ port: ports.length > 0,
20080
+ ports,
20081
+ portR: 3,
20082
+ portLineWidth: 1
20083
+ },
20084
+ data: {
20085
+ topic_name: topic,
20086
+ rows: card.rows,
20087
+ color: card.color
20088
+ }
19808
20089
  });
19809
20090
  }
19810
20091
  let edge_seen = {};
19811
20092
  let edges = [];
19812
- let add_edge = (source, target) => {
19813
- if (!topic_set[source] || !topic_set[target] || source === target) return;
19814
- let key = source + "" + target;
20093
+ let add_edge = (source, source_port, target, target_port) => {
20094
+ if (!cards[source] || !cards[target]) return;
20095
+ let key = `${source}|${source_port}|${target}|${target_port}`;
19815
20096
  if (edge_seen[key]) return;
19816
20097
  edge_seen[key] = true;
19817
20098
  edges.push({
19818
20099
  id: key,
20100
+ type: "cubic",
19819
20101
  source,
19820
- target
20102
+ target,
20103
+ style: {
20104
+ sourcePort: source_port,
20105
+ targetPort: target_port
20106
+ }
19821
20107
  });
19822
20108
  };
19823
- for (let topic of Object.keys(descs)) {
20109
+ for (let topic of Object.keys(cards)) {
19824
20110
  let desc = descs[topic];
19825
20111
  if (!is_object(desc) || !Array.isArray(desc.cols)) continue;
19826
20112
  for (let col of desc.cols) {
19827
- if (!col) continue;
19828
- if (is_object(col.hook)) for (let child of Object.keys(col.hook)) add_edge(topic, child);
19829
- else if (is_object(col.fkey)) for (let parent of Object.keys(col.fkey)) add_edge(parent, topic);
20113
+ if (!is_object(col) || !col.id) continue;
20114
+ if (is_object(col.hook)) for (let child of Object.keys(col.hook)) add_edge(topic, col.id, child, col.hook[child]);
20115
+ if (is_object(col.fkey)) for (let parent of Object.keys(col.fkey)) add_edge(parent, col.fkey[parent], topic, col.id);
19830
20116
  }
19831
20117
  }
19832
20118
  return {
@@ -19835,26 +20121,15 @@ function schema_to_graph(gobj) {
19835
20121
  };
19836
20122
  }
19837
20123
  /************************************************************
19838
- * The theme-dependent styles, in ONE place: they are applied
19839
- * as the graph is built and re-applied on a theme switch
20124
+ * The theme-dependent edge style, in ONE place: applied as
20125
+ * the graph is built and re-applied on a theme switch
19840
20126
  * (ac_theme), which restyles the LIVE graph — rebuilding it
19841
20127
  * would drop the user's zoom/pan and any dragged node.
19842
20128
  ************************************************************/
19843
- function node_style(dark) {
19844
- return {
19845
- size: 40,
19846
- fill: "#5B8FF9",
19847
- labelText: (d) => d.id,
19848
- labelPlacement: "bottom",
19849
- labelFill: dark ? "#e6e6e6" : "#333333",
19850
- labelBackground: true,
19851
- labelBackgroundFill: dark ? "rgba(0,0,0,0.5)" : "rgba(255,255,255,0.7)",
19852
- cursor: "pointer"
19853
- };
19854
- }
19855
20129
  function edge_style(dark) {
19856
20130
  return {
19857
- stroke: dark ? "#666666" : "#bbbbbb",
20131
+ stroke: dark ? "#7a8593" : "#9aa4b2",
20132
+ lineWidth: 1.2,
19858
20133
  endArrow: true
19859
20134
  };
19860
20135
  }
@@ -19886,13 +20161,12 @@ function build_graph$1(gobj) {
19886
20161
  container: $container,
19887
20162
  autoResize: true,
19888
20163
  data,
19889
- node: { style: node_style(dark) },
19890
20164
  edge: { style: edge_style(dark) },
19891
20165
  layout: {
19892
20166
  type: "antv-dagre",
19893
20167
  rankdir: "LR",
19894
- nodesep: 24,
19895
- ranksep: 60
20168
+ nodesep: 28,
20169
+ ranksep: 90
19896
20170
  },
19897
20171
  behaviors: [
19898
20172
  "zoom-canvas",
@@ -19977,8 +20251,17 @@ function ac_theme$1(gobj, event, kw, src) {
19977
20251
  let dark = kw && kw.theme ? kw.theme === "dark" : yui_is_dark();
19978
20252
  try {
19979
20253
  graph.setTheme(dark ? "dark" : "light");
19980
- graph.setNode({ style: node_style(dark) });
19981
20254
  graph.setEdge({ style: edge_style(dark) });
20255
+ let updates = [];
20256
+ for (let node of graph.getNodeData()) {
20257
+ let data = node.data;
20258
+ if (!is_object(data) || !Array.isArray(data.rows)) continue;
20259
+ updates.push({
20260
+ id: node.id,
20261
+ style: { innerHTML: build_card_innerHTML(data.topic_name, data.rows, data.color, dark) }
20262
+ });
20263
+ }
20264
+ if (updates.length > 0) graph.updateNodeData(updates);
19982
20265
  graph.draw().catch((e) => {
19983
20266
  log_error(`${gobj_short_name(gobj)}: schema graph redraw failed: ${e}`);
19984
20267
  });
@@ -20043,6 +20326,58 @@ function register_c_yui_treedb_schema() {
20043
20326
  return create_gclass$2(GCLASS_NAME$2);
20044
20327
  }
20045
20328
  //#endregion
20329
+ //#region src/treedb_node_label.js
20330
+ /***********************************************************************
20331
+ * treedb_node_label.js
20332
+ *
20333
+ * Pure, testable logic behind the label of a node in the treedb
20334
+ * graph: WHAT A RECORD IS CALLED, which is not always what it is
20335
+ * keyed by. Kept out of the gclass so it can be unit-tested with
20336
+ * no DOM and no G6.
20337
+ *
20338
+ * Copyright (c) 2026, ArtGins.
20339
+ * All Rights Reserved.
20340
+ ***********************************************************************/
20341
+ /************************************************************
20342
+ * What a node is CALLED, which is not always what it is
20343
+ * keyed by.
20344
+ *
20345
+ * A topic whose id column is flagged `rowid` or `uuid` keys
20346
+ * its records by a value nobody reads — that is the point of
20347
+ * those flags — and the name a human knows the record by
20348
+ * lives in the secondary key the topic declares (`pkey2s`,
20349
+ * carried in the desc since SDK > 7.13.0). The `topics` and
20350
+ * `cols` topics of treedb_system_schema are the case that
20351
+ * forced this: keyed by rowid, named in `value`, so every
20352
+ * card in the graph read "181", "225", "193".
20353
+ *
20354
+ * The pkey stays reachable: it is the card's tooltip.
20355
+ *
20356
+ * Falls back to the id whenever the schema does not say
20357
+ * otherwise — an older node whose desc carries no `pkey2s`
20358
+ * included.
20359
+ ************************************************************/
20360
+ function node_label(desc, record) {
20361
+ let id = record.id;
20362
+ let id_col = null;
20363
+ if (Array.isArray(desc.cols)) {
20364
+ for (let col of desc.cols) if (col && col.id === (desc.pkey || "id")) {
20365
+ id_col = col;
20366
+ break;
20367
+ }
20368
+ }
20369
+ if (!id_col || !Array.isArray(id_col.flag)) return id;
20370
+ if (id_col.flag.indexOf("rowid") < 0 && id_col.flag.indexOf("uuid") < 0) return id;
20371
+ let pkey2s = desc.pkey2s;
20372
+ if (typeof pkey2s === "string") pkey2s = pkey2s ? [pkey2s] : [];
20373
+ if (!Array.isArray(pkey2s)) return id;
20374
+ for (let name of pkey2s) {
20375
+ let value = record[name];
20376
+ if (typeof value === "string" && value.length > 0) return value;
20377
+ }
20378
+ return id;
20379
+ }
20380
+ //#endregion
20046
20381
  //#region node_modules/@babel/runtime/helpers/esm/typeof.js
20047
20382
  function _typeof(o) {
20048
20383
  "@babel/helpers - typeof";
@@ -47575,19 +47910,19 @@ function create_topic_node(gobj, desc, record) {
47575
47910
  style.size = [116, 40];
47576
47911
  style.dx = -58;
47577
47912
  style.dy = -20;
47578
- style.innerHTML = build_chip_innerHTML(desc.color, priv.theme, record.icon, record.id);
47913
+ style.innerHTML = build_chip_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), record.id);
47579
47914
  } else if (node_treedb_type === "extended") {
47580
47915
  node_graph_type = "html";
47581
47916
  style.size = [144, 66];
47582
47917
  style.dx = -72;
47583
47918
  style.dy = -33;
47584
- style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, record.id, desc.topic_name, true);
47919
+ style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, true, record.id);
47585
47920
  } else {
47586
47921
  node_graph_type = "html";
47587
47922
  style.size = [172, 96];
47588
47923
  style.dx = -86;
47589
47924
  style.dy = -48;
47590
- style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, record.id, desc.topic_name);
47925
+ style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, false, record.id);
47591
47926
  }
47592
47927
  let topic_props = priv._graph_properties[desc.topic_name];
47593
47928
  let topic_defaults = is_object(topic_props) && is_object(topic_props.defaults) ? topic_props.defaults : null;
@@ -47655,15 +47990,15 @@ function update_topic_node(gobj, desc, node_name, record) {
47655
47990
  nodedata.data.record = record;
47656
47991
  if (desc.node_treedb_type === "child") graph.updateNodeData([{
47657
47992
  id: node_name,
47658
- style: { innerHTML: build_chip_innerHTML(desc.color, priv.theme, record.icon, record.id) }
47993
+ style: { innerHTML: build_chip_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), record.id) }
47659
47994
  }]);
47660
47995
  else if (desc.node_treedb_type === "extended") graph.updateNodeData([{
47661
47996
  id: node_name,
47662
- style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, record.id, desc.topic_name, true) }
47997
+ style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, true, record.id) }
47663
47998
  }]);
47664
47999
  else if (desc.node_treedb_type === "hierarchical") graph.updateNodeData([{
47665
48000
  id: node_name,
47666
- style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, record.id, desc.topic_name) }
48001
+ style: { innerHTML: build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, false, record.id) }
47667
48002
  }]);
47668
48003
  } catch (e) {
47669
48004
  log_error(e.message);
@@ -49570,7 +49905,7 @@ function show_node_popover(gobj) {
49570
49905
  };
49571
49906
  if (node_graph_type === "hierarchical") {
49572
49907
  let record = nodeData.data.record || {};
49573
- updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon, record.id, nodeData.data.desc.topic_name);
49908
+ updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon, node_label(nodeData.data.desc, record), nodeData.data.desc.topic_name, false, record.id);
49574
49909
  }
49575
49910
  graph.updateNodeData([{
49576
49911
  id: node_id,
@@ -49610,7 +49945,7 @@ function show_node_popover(gobj) {
49610
49945
  };
49611
49946
  if (node_graph_type === "hierarchical") {
49612
49947
  let record = nodeData.data.record || {};
49613
- restoreStyle.innerHTML = build_node_innerHTML(origFill, priv.theme, record.icon, record.id, nodeData.data.desc.topic_name);
49948
+ restoreStyle.innerHTML = build_node_innerHTML(origFill, priv.theme, record.icon, node_label(nodeData.data.desc, record), nodeData.data.desc.topic_name, false, record.id);
49614
49949
  }
49615
49950
  graph.updateNodeData([{
49616
49951
  id: node_id,
@@ -49711,15 +50046,15 @@ function refresh_html_nodes_theme(gobj, theme) {
49711
50046
  let record = nd.data.record || {};
49712
50047
  if (tt === "hierarchical") updates.push({
49713
50048
  id: nodes[i].id,
49714
- style: { innerHTML: build_node_innerHTML(nd.data.desc.color, theme, record.icon, record.id, nd.data.desc.topic_name) }
50049
+ style: { innerHTML: build_node_innerHTML(nd.data.desc.color, theme, record.icon, node_label(nd.data.desc, record), nd.data.desc.topic_name, false, record.id) }
49715
50050
  });
49716
50051
  else if (tt === "child") updates.push({
49717
50052
  id: nodes[i].id,
49718
- style: { innerHTML: build_chip_innerHTML(nd.data.desc.color, theme, record.icon, record.id) }
50053
+ style: { innerHTML: build_chip_innerHTML(nd.data.desc.color, theme, record.icon, node_label(nd.data.desc, record), record.id) }
49719
50054
  });
49720
50055
  else if (tt === "extended") updates.push({
49721
50056
  id: nodes[i].id,
49722
- style: { innerHTML: build_node_innerHTML(nd.data.desc.color, theme, record.icon, record.id, nd.data.desc.topic_name, true) }
50057
+ style: { innerHTML: build_node_innerHTML(nd.data.desc.color, theme, record.icon, node_label(nd.data.desc, record), nd.data.desc.topic_name, true, record.id) }
49723
50058
  });
49724
50059
  }
49725
50060
  if (updates.length > 0) graph.updateNodeData(updates);
@@ -49751,7 +50086,8 @@ function refresh_default_edges_theme(gobj, theme) {
49751
50086
  * but lighter (1px border, no shadow, single line). The name is
49752
50087
  * always legible (ellipsis + native title tooltip on overflow).
49753
50088
  ************************************************************/
49754
- function build_chip_innerHTML(color, theme, icon, id) {
50089
+ function build_chip_innerHTML(color, theme, icon, label, key) {
50090
+ let title = key || label;
49755
50091
  let dark = theme === "dark";
49756
50092
  let bg = dark ? `color-mix(in srgb, ${color} 30%, #2c3542)` : `color-mix(in srgb, ${color} 10%, ${dark ? "#1b2230" : "#ffffff"})`;
49757
50093
  let border = dark ? `color-mix(in srgb, ${color} 85%, #ffffff)` : color;
@@ -49762,7 +50098,7 @@ function build_chip_innerHTML(color, theme, icon, id) {
49762
50098
  margin-right: 6px; flex: 0 0 auto;
49763
50099
  "/>`;
49764
50100
  return `
49765
- <div title="${escapeHtml(id)}" style="
50101
+ <div title="${escapeHtml(title)}" style="
49766
50102
  box-sizing: border-box;
49767
50103
  width: 100%;
49768
50104
  height: 100%;
@@ -49779,7 +50115,7 @@ function build_chip_innerHTML(color, theme, icon, id) {
49779
50115
  ">${icon_html}<span style="
49780
50116
  font-size: 12px; font-weight: 600; line-height: 1;
49781
50117
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
49782
- ">${escapeHtml(id)}</span>
50118
+ ">${escapeHtml(label)}</span>
49783
50119
  </div>
49784
50120
  `;
49785
50121
  }
@@ -49793,7 +50129,8 @@ function build_chip_innerHTML(color, theme, icon, id) {
49793
50129
  * colour is kept (per-topic differentiation) but softened via
49794
50130
  * color-mix instead of a harsh saturated fill. Theme-aware.
49795
50131
  ************************************************************/
49796
- function build_node_innerHTML(color, theme, icon, id, topic_name, structural) {
50132
+ function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key) {
50133
+ let title = key || label;
49797
50134
  let dark = theme === "dark";
49798
50135
  let surface = dark ? "#1b2230" : "#ffffff";
49799
50136
  let bg, border, border_style;
@@ -49823,7 +50160,7 @@ function build_node_innerHTML(color, theme, icon, id, topic_name, structural) {
49823
50160
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
49824
50161
  ">${escapeHtml(topic_name)}</div>`;
49825
50162
  return `
49826
- <div title="${escapeHtml(id)}" style="
50163
+ <div title="${escapeHtml(title)}" style="
49827
50164
  box-sizing: border-box;
49828
50165
  width: 100%;
49829
50166
  height: 100%;
@@ -49846,7 +50183,7 @@ function build_node_innerHTML(color, theme, icon, id, topic_name, structural) {
49846
50183
  max-width: 100%; overflow: hidden; text-overflow: ellipsis;
49847
50184
  display: -webkit-box; -webkit-line-clamp: 2;
49848
50185
  -webkit-box-orient: vertical; word-break: break-word;
49849
- ">${escapeHtml(id)}</div>${sub_html}
50186
+ ">${escapeHtml(label)}</div>${sub_html}
49850
50187
  </div>
49851
50188
  `;
49852
50189
  }
@@ -50067,7 +50404,7 @@ function apply_node_properties(gobj, node_id, fill, stroke, lineWidth, scope) {
50067
50404
  };
50068
50405
  if (nd.data.desc.node_treedb_type === "hierarchical") {
50069
50406
  let record = nd.data.record || {};
50070
- updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon, record.id, nd.data.desc.topic_name);
50407
+ updateStyle.innerHTML = build_node_innerHTML(fill, priv.theme, record.icon, node_label(nd.data.desc, record), nd.data.desc.topic_name, false, record.id);
50071
50408
  }
50072
50409
  updates.push({
50073
50410
  id: nodes[i].id,