@yuneta/gobj-ui 7.23.74 → 7.23.75

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.
@@ -27365,10 +27365,10 @@ function fold_reveal(model, state, node_key) {
27365
27365
  /***********************************************************************
27366
27366
  * treedb_layout.js
27367
27367
  *
27368
- * Three layouts made for a treedb, and nothing else.
27368
+ * Two layouts made for a treedb, and nothing else.
27369
27369
  *
27370
27370
  * What a treedb graph IS, once it is folded (treedb_fold_model.js):
27371
- * a forest read left to right. A main topic is a tree (places:
27371
+ * a forest read top to bottom. A main topic is a tree (places:
27372
27372
  * country, region, site, hall), the other topics hang from its
27373
27373
  * nodes through hooks (devices, users, controllers), a record may
27374
27374
  * hang from two parents (a device from its place AND its
@@ -27389,9 +27389,8 @@ function fold_reveal(model, state, node_key) {
27389
27389
  * - the force family scatters a tree, which is the pile with
27390
27390
  * physics.
27391
27391
  *
27392
- * So: a tidy tree (`tree`), an outline (`outline`) and a radial
27393
- * tree (`radial`), all three fed by the SAME spanning tree, chosen
27394
- * deterministically:
27392
+ * So: a tidy tree (`tree`) and a radial tree (`radial`), both fed
27393
+ * by the SAME spanning tree, chosen deterministically:
27395
27394
  *
27396
27395
  * - roots are the nodes with no incoming edge, in node order;
27397
27396
  * - a node belongs to the FIRST parent that reaches it in a
@@ -27403,16 +27402,16 @@ function fold_reveal(model, state, node_key) {
27403
27402
  * and then by node order (record order, the pages' order);
27404
27403
  * - a node nothing reaches (a cycle) is a root too.
27405
27404
  *
27406
- * `tree`: the classic tidy tree, left to right. Each depth is a
27407
- * column as wide as its widest card; a node is centred on the
27408
- * block of its children; siblings stack with `nodesep` between
27409
- * them. O(n), and opening a hook moves NOTHING that is not below
27410
- * or beside it -- the cards keep their order, the eye keeps its
27411
- * place.
27412
- *
27413
- * `outline`: one node per row, indented by depth -- what a JSON
27414
- * viewer draws, and what a treedb is when read as one. Tall, but
27415
- * nothing is ever beside anything: the reader scrolls a list.
27405
+ * `tree`: the classic tidy tree, top to bottom. Each depth is a
27406
+ * row as tall as its tallest card; a node is centred over the
27407
+ * block of its children; siblings sit side by side with `nodesep`
27408
+ * between them. O(n), and opening a hook moves NOTHING that is
27409
+ * not below or beside it -- the cards keep their order, the eye
27410
+ * keeps its place. It reads DOWN because that is the direction a
27411
+ * tree with many children has room in: a hall with a hundred
27412
+ * devices is a wide row, not a tall column beside a card. (The
27413
+ * left-to-right reading is `direction: "LR"`, kept for a host
27414
+ * that wants it; `dagre` reads that way in the graph.)
27416
27415
  *
27417
27416
  * `radial`: the root in the middle and a ring per depth, each
27418
27417
  * subtree owning a sector proportional to its leaves; the radius
@@ -27420,15 +27419,15 @@ function fold_reveal(model, state, node_key) {
27420
27419
  * nothing overlaps by construction (see `layout_radial`).
27421
27420
  *
27422
27421
  * Pure: takes plain nodes and edges, returns positions. The G6
27423
- * classes in c_g6_nodes_tree.js are three thin adapters over it.
27422
+ * classes in c_g6_nodes_tree.js are two thin adapters over it.
27424
27423
  *
27425
27424
  * Copyright (c) 2026, ArtGins.
27426
27425
  * All Rights Reserved.
27427
27426
  ***********************************************************************/
27428
27427
  var DEFAULTS = {
27429
27428
  nodesep: 18,
27430
- ranksep: 110,
27431
- indent: 48
27429
+ ranksep: 90,
27430
+ direction: "TB"
27432
27431
  };
27433
27432
  /************************************************************
27434
27433
  * The spanning tree of a graph, deterministic.
@@ -27515,13 +27514,33 @@ function walk_order(t) {
27515
27514
  return order;
27516
27515
  }
27517
27516
  /************************************************************
27518
- * The tidy tree, left to right.
27517
+ * The tidy tree.
27519
27518
  *
27520
27519
  * Returns Map<id, {x, y}> with the CENTRE of each node, the way G6
27521
27520
  * positions a node.
27521
+ *
27522
+ * The algorithm is written ONCE, reading left to right (depths are
27523
+ * columns, siblings stack down). Top to bottom -- the default -- is
27524
+ * the same tree with the axes swapped: every card is fed in
27525
+ * transposed (its height as its width), and every position comes
27526
+ * back transposed. That is the whole difference, and it is the
27527
+ * reason the code is not duplicated for a second direction.
27522
27528
  ************************************************************/
27523
27529
  function layout_tree(nodes, edges, opts) {
27524
27530
  let o = Object.assign({}, DEFAULTS, opts || {});
27531
+ if (o.direction === "LR") return tidy_tree_lr(nodes, edges, o);
27532
+ let lr = tidy_tree_lr(nodes.map((n) => Object.assign({}, n, {
27533
+ w: n.h,
27534
+ h: n.w
27535
+ })), edges, o);
27536
+ let pos = /* @__PURE__ */ new Map();
27537
+ for (let [id, p] of lr) pos.set(id, {
27538
+ x: p.y,
27539
+ y: p.x
27540
+ });
27541
+ return pos;
27542
+ }
27543
+ function tidy_tree_lr(nodes, edges, o) {
27525
27544
  let t = spanning_tree(nodes, edges);
27526
27545
  let pos = /* @__PURE__ */ new Map();
27527
27546
  let col_w = [];
@@ -27687,37 +27706,6 @@ function layout_radial(nodes, edges, opts) {
27687
27706
  }
27688
27707
  return pos;
27689
27708
  }
27690
- /************************************************************
27691
- * The outline: one node per row, indented by depth, in the
27692
- * pre-order of the spanning tree. Rows are left-aligned on
27693
- * their indent, so a column of chips reads as a list.
27694
- ************************************************************/
27695
- function layout_outline(nodes, edges, opts) {
27696
- let o = Object.assign({}, DEFAULTS, opts || {});
27697
- let t = spanning_tree(nodes, edges);
27698
- let pos = /* @__PURE__ */ new Map();
27699
- let y = 0;
27700
- let visit = (root_id) => {
27701
- let stack = [root_id];
27702
- while (stack.length) {
27703
- let id = stack.pop();
27704
- let n = t.by_id.get(id);
27705
- let d = t.depth.get(id);
27706
- pos.set(id, {
27707
- x: d * o.indent + n.w / 2,
27708
- y: y + n.h / 2
27709
- });
27710
- y += n.h + o.nodesep;
27711
- let kids = t.children.get(id) || [];
27712
- for (let i = kids.length - 1; i >= 0; i--) stack.push(kids[i]);
27713
- }
27714
- };
27715
- for (let root of t.roots) {
27716
- visit(root);
27717
- y += o.nodesep;
27718
- }
27719
- return pos;
27720
- }
27721
27709
  //#endregion
27722
27710
  //#region node_modules/@babel/runtime/helpers/esm/typeof.js
27723
27711
  function _typeof(o) {
@@ -54534,6 +54522,14 @@ var GCLASS_NAME$4 = "C_G6_NODES_TREE";
54534
54522
  var HIGHLIGHT_COLOR = "#f0a020";
54535
54523
  var HIGHLIGHT_HALO = "rgba(240,160,32,0.35)";
54536
54524
  var SELECT_RING = "rgba(59,130,246,0.95)";
54525
+ var PORT_R_ENTITY = 10;
54526
+ var PORT_R_CHILD = 5;
54527
+ var PORT_LINE_WIDTH = 2;
54528
+ var COMPACT_SIZE = {
54529
+ hierarchical: 32,
54530
+ extended: 28,
54531
+ child: 22
54532
+ };
54537
54533
  /***************************************************************
54538
54534
  * Internal layout and operation mode definitions
54539
54535
  ***************************************************************/
@@ -54541,13 +54537,9 @@ var _layouts = {
54541
54537
  "manual": { type: "manual" },
54542
54538
  "treedb-tree": {
54543
54539
  type: "treedb-tree",
54540
+ direction: "TB",
54544
54541
  nodesep: 18,
54545
- ranksep: 110
54546
- },
54547
- "treedb-outline": {
54548
- type: "treedb-outline",
54549
- nodesep: 12,
54550
- indent: 48
54542
+ ranksep: 90
54551
54543
  },
54552
54544
  "dagre": {
54553
54545
  type: "dagre",
@@ -54618,6 +54610,8 @@ var attrs_table$4 = [
54618
54610
  SDATA(data_type_t.DTP_LIST, "hidden_topics", 0, "[]", "Topics left out of the tree: no card, no pill counts them, no edge reaches them"),
54619
54611
  SDATA(data_type_t.DTP_STRING, "main_topic", 0, "", "The topic the tree hangs from; empty = deduced (the one whose hooks reach the most others). Its parentless records are the roots; a parentless record of a topic linked to it is LOOSE and shown only on request"),
54620
54612
  SDATA(data_type_t.DTP_LIST, "loose_topics", 0, "[]", "Topics whose loose records (no parent, in a topic the schema hangs from the main one) are shown as roots"),
54613
+ SDATA(data_type_t.DTP_STRING, "node_mode", 0, "expanded", "How a record is drawn: `expanded` = the card with its ports (the only shape a link can be edited on); `compact` = a rounded square of the topic's colour, no ports, no text -- the topology alone. One node can be toggled against it (EV_TOGGLE_NODE_MODE)"),
54614
+ SDATA(data_type_t.DTP_BOOLEAN, "node_labels", 0, true, "A compact node says its name under the square. Off, it is a coloured square and nothing else"),
54621
54615
  SDATA(data_type_t.DTP_BOOLEAN, "confirm_delete_node", 0, true, "Ask confirmation before deleting a node"),
54622
54616
  SDATA(data_type_t.DTP_BOOLEAN, "confirm_unlink_edge", 0, true, "Ask confirmation before unlinking an edge"),
54623
54617
  SDATA(data_type_t.DTP_STRING, "wide", 0, "40px", "Height of header"),
@@ -54701,6 +54695,7 @@ var PRIVATE_DATA$4 = {
54701
54695
  _fold_state: null,
54702
54696
  _fold_listener: null,
54703
54697
  _pill_sig: null,
54698
+ _open_nodes: null,
54704
54699
  _fold_busy: false
54705
54700
  };
54706
54701
  var __gclass__$4 = null;
@@ -54846,7 +54841,6 @@ function register_layouts(gobj) {
54846
54841
  _g6_extensions_registered = true;
54847
54842
  register(ExtensionCategory.LAYOUT, "manual", ManualLayout);
54848
54843
  register(ExtensionCategory.LAYOUT, "treedb-tree", TreedbTreeLayout);
54849
- register(ExtensionCategory.LAYOUT, "treedb-outline", TreedbOutlineLayout);
54850
54844
  register(ExtensionCategory.LAYOUT, "treedb-radial", TreedbRadialLayout);
54851
54845
  register(ExtensionCategory.NODE, "light", LightNode);
54852
54846
  }
@@ -54929,6 +54923,9 @@ function configure_events(gobj) {
54929
54923
  graph.on(NodeEvent.CONTEXT_MENU, (evt) => {
54930
54924
  gobj_send_event(gobj, "EV_NODE_CONTEXT_MENU", { evt }, gobj);
54931
54925
  });
54926
+ graph.on(NodeEvent.DBLCLICK, (evt) => {
54927
+ gobj_send_event(gobj, "EV_NODE_DBLCLICK", { evt }, gobj);
54928
+ });
54932
54929
  graph.on(EdgeEvent.CLICK, (evt) => {
54933
54930
  if (consume_long_press_click(graph)) return;
54934
54931
  gobj_send_event(gobj, "EV_EDGE_CLICK", { evt }, gobj);
@@ -55353,7 +55350,7 @@ function configure_behaviour(gobj) {
55353
55350
  case "writing":
55354
55351
  case "reading":
55355
55352
  priv.edit_mode = false;
55356
- behaviors = ["drag-canvas", "zoom-canvas"];
55353
+ behaviors = ["drag-canvas", ...camera_behaviors()];
55357
55354
  break;
55358
55355
  case "edition":
55359
55356
  priv.edit_mode = true;
@@ -55367,7 +55364,7 @@ function configure_behaviour(gobj) {
55367
55364
  return true;
55368
55365
  }
55369
55366
  },
55370
- "zoom-canvas",
55367
+ ...camera_behaviors(),
55371
55368
  "drag-element",
55372
55369
  {
55373
55370
  type: BRUSH_SELECT_OWNED,
@@ -55385,7 +55382,7 @@ function configure_behaviour(gobj) {
55385
55382
  break;
55386
55383
  case "operation":
55387
55384
  priv.edit_mode = false;
55388
- behaviors = ["drag-canvas", "zoom-canvas"];
55385
+ behaviors = ["drag-canvas", ...camera_behaviors()];
55389
55386
  break;
55390
55387
  default: log_error(`operation mode unknown: ${operation_mode}`);
55391
55388
  }
@@ -55457,13 +55454,33 @@ function build_ports(gobj, desc) {
55457
55454
  * Which way the graph reads. `dagre` is the left-to-right
55458
55455
  * layout; everything else keeps the top-down reading, `manual`
55459
55456
  * included, because the saved arrangements were made with the
55460
- * ports on the top and bottom edges.
55461
- ************************************************************/
55462
- var LR_LAYOUTS = [
55463
- "dagre",
55464
- "treedb-tree",
55465
- "treedb-outline"
55466
- ];
55457
+ * ports on the top and bottom edges. The treedb tree reads down
55458
+ * too, since 7.23.75: read right it was dagre's twin.
55459
+ ************************************************************/
55460
+ var LR_LAYOUTS = ["dagre"];
55461
+ /************************************************************
55462
+ * The camera behaviors every operation mode shares.
55463
+ *
55464
+ * The wheel SCROLLS, as it does on a map and on every page: a
55465
+ * graph of many nodes is taller than the screen, and a wheel
55466
+ * that zoomed instead made it a thing to be looked at from afar
55467
+ * or read through a keyhole -- never scrolled. Zoom is Ctrl +
55468
+ * wheel (which is also what a trackpad pinch arrives as), the
55469
+ * toolbar's +/-, and the two-finger pinch of g6_touch_gestures.
55470
+ * `enable` on the scroll keeps the two apart: a Ctrl wheel is
55471
+ * the zoom's, so the scroll stands aside for it.
55472
+ ************************************************************/
55473
+ function camera_behaviors() {
55474
+ return [{
55475
+ type: "scroll-canvas",
55476
+ key: "scroll-canvas",
55477
+ enable: (event) => !(event && (event.ctrlKey || event.metaKey))
55478
+ }, {
55479
+ type: "zoom-canvas",
55480
+ key: "zoom-canvas",
55481
+ trigger: ["Control"]
55482
+ }];
55483
+ }
55467
55484
  function layout_direction(gobj) {
55468
55485
  return str_in_list(LR_LAYOUTS, gobj.priv.layout) ? "LR" : "TB";
55469
55486
  }
@@ -55531,83 +55548,101 @@ function apply_layout_direction(gobj) {
55531
55548
  * Create a topic node in the G6 graph
55532
55549
  ************************************************************/
55533
55550
  function create_topic_node(gobj, desc, record) {
55534
- let priv = gobj.priv;
55535
- const graph = priv.graph;
55551
+ const graph = gobj.priv.graph;
55536
55552
  let node_name = build_node_name(gobj, desc.topic_name, record.id);
55537
55553
  let xy = get_default_ne_xy(gobj);
55538
55554
  let geometry = get_node_graph_props(gobj, desc.topic_name, record.id, record);
55539
55555
  let x = kw_get_int(gobj, geometry, "x", xy, kw_flag_t.KW_CREATE);
55540
55556
  let y = kw_get_int(gobj, geometry, "y", xy, kw_flag_t.KW_CREATE);
55541
- let ports = build_ports(gobj, desc);
55542
- let node_graph_type = null;
55557
+ let shape = node_shape_of(gobj, node_name, desc, record, geometry, pills_html_of_key(gobj, fold_node_key(desc.topic_name, String(record.id))));
55558
+ let node_def = {
55559
+ id: node_name,
55560
+ type: shape.type,
55561
+ style: Object.assign({
55562
+ x,
55563
+ y
55564
+ }, shape.style),
55565
+ data: {
55566
+ topic_name: desc.topic_name,
55567
+ desc,
55568
+ record,
55569
+ graph_props: geometry,
55570
+ port_keys: build_ports(gobj, desc).map((p) => p.key)
55571
+ }
55572
+ };
55573
+ graph.addNodeData([node_def]);
55574
+ }
55575
+ /************************************************************
55576
+ * The shape of a record node: OPEN (the card, with its ports)
55577
+ * or CLOSED (a coloured square), by `node_mode` and the nodes
55578
+ * toggled against it.
55579
+ *
55580
+ * Both answer {type, style} WITHOUT x/y, which belong to the node
55581
+ * and not to its shape, and a node moves between the two on the
55582
+ * fly: G6 rebuilds the element when `type` changes, and the
55583
+ * style is MERGED, which is why each shape also writes the keys
55584
+ * the other one would leave behind (`labelText`, `ports`).
55585
+ ************************************************************/
55586
+ function node_shape_of(gobj, node_id, desc, record, geometry, pills_html) {
55587
+ if (node_is_compact(gobj, node_id)) return compact_shape_of(gobj, desc, record, {});
55588
+ return card_shape_of(gobj, desc, record, geometry, pills_html);
55589
+ }
55590
+ function node_is_compact(gobj, node_id) {
55591
+ let priv = gobj.priv;
55592
+ if (!priv._open_nodes) priv._open_nodes = /* @__PURE__ */ new Set();
55593
+ let compact = gobj_read_str_attr(gobj, "node_mode") === "compact";
55594
+ return priv._open_nodes.has(node_id) ? !compact : compact;
55595
+ }
55596
+ /************************************************************
55597
+ * The CARD: an html node, the tier's size, the ports, the
55598
+ * saved geometry on top. The flags (focus, selection, anchor)
55599
+ * are not painted here: repaint_cards() reads them where they
55600
+ * live and rebuilds the html.
55601
+ ************************************************************/
55602
+ function card_shape_of(gobj, desc, record, geometry, pills_html) {
55603
+ let priv = gobj.priv;
55543
55604
  let node_treedb_type = desc.node_treedb_type;
55605
+ let label = node_label(desc, record);
55606
+ let ports = build_ports(gobj, desc);
55544
55607
  let style = {
55545
- x,
55546
- y,
55547
55608
  fill: desc.color,
55548
55609
  stroke: getStrokeColor(desc.color),
55549
- lineWidth: 1
55610
+ lineWidth: 1,
55611
+ lineDash: [],
55612
+ halo: false,
55613
+ labelText: ""
55550
55614
  };
55551
- let pills_html = pills_html_of_key(gobj, fold_node_key(desc.topic_name, String(record.id)));
55552
55615
  let size = card_size_for(node_treedb_type, !!pills_html);
55553
- if (node_treedb_type === "child") {
55554
- node_graph_type = "html";
55555
- style.size = size;
55556
- style.dx = -size[0] / 2;
55557
- style.dy = -size[1] / 2;
55558
- style.innerHTML = build_chip_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), record.id);
55559
- } else if (node_treedb_type === "extended") {
55560
- node_graph_type = "html";
55561
- style.size = size;
55562
- style.dx = -size[0] / 2;
55563
- style.dy = -size[1] / 2;
55564
- style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, true, record.id, false, false, false, pills_html);
55565
- } else {
55566
- node_graph_type = "html";
55567
- style.size = size;
55568
- style.dx = -size[0] / 2;
55569
- style.dy = -size[1] / 2;
55570
- style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, node_label(desc, record), desc.topic_name, false, record.id, false, false, false, pills_html);
55571
- }
55616
+ style.size = size;
55617
+ style.dx = -size[0] / 2;
55618
+ style.dy = -size[1] / 2;
55619
+ if (node_treedb_type === "child") style.innerHTML = build_chip_innerHTML(desc.color, priv.theme, record.icon, label, record.id);
55620
+ else if (node_treedb_type === "extended") style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, label, desc.topic_name, true, record.id, false, false, false, pills_html);
55621
+ else style.innerHTML = build_node_innerHTML(desc.color, priv.theme, record.icon, label, desc.topic_name, false, record.id, false, false, false, pills_html);
55572
55622
  let topic_props = priv._graph_properties[desc.topic_name];
55573
55623
  let topic_defaults = is_object(topic_props) && is_object(topic_props.defaults) ? topic_props.defaults : null;
55574
55624
  if (topic_defaults) {
55575
55625
  let def_size = topic_defaults.size;
55576
55626
  if (!geometry.size && Array.isArray(def_size) && def_size.length > 0) {
55577
55627
  style.size = [...def_size];
55578
- if (node_graph_type === "html") {
55579
- style.dx = -def_size[0] / 2;
55580
- style.dy = -(def_size.length > 1 ? def_size[1] : def_size[0]) / 2;
55581
- }
55628
+ style.dx = -def_size[0] / 2;
55629
+ style.dy = -(def_size.length > 1 ? def_size[1] : def_size[0]) / 2;
55582
55630
  }
55583
55631
  if (!geometry.portR && topic_defaults.portR > 0) style.portR = topic_defaults.portR;
55584
55632
  }
55585
55633
  let saved_size = geometry.size;
55586
55634
  if (Array.isArray(saved_size) && saved_size.length > 0) {
55587
55635
  style.size = saved_size;
55588
- if (node_graph_type === "html") {
55589
- style.dx = -saved_size[0] / 2;
55590
- style.dy = -(saved_size.length > 1 ? saved_size[1] : saved_size[0]) / 2;
55591
- }
55636
+ style.dx = -saved_size[0] / 2;
55637
+ style.dy = -(saved_size.length > 1 ? saved_size[1] : saved_size[0]) / 2;
55592
55638
  }
55593
55639
  if (geometry.portR > 0) style.portR = geometry.portR;
55594
- let node_def = {
55595
- id: node_name,
55596
- type: node_graph_type,
55597
- style,
55598
- data: {
55599
- topic_name: desc.topic_name,
55600
- desc,
55601
- record,
55602
- graph_props: geometry
55603
- }
55604
- };
55605
55640
  if (json_size(ports)) {
55606
55641
  json_object_update_missing(style, {
55607
55642
  port: true,
55608
55643
  ports,
55609
- portR: node_treedb_type === "child" ? 2 : 6,
55610
- portLineWidth: 1
55644
+ portR: node_treedb_type === "child" ? PORT_R_CHILD : PORT_R_ENTITY,
55645
+ portLineWidth: PORT_LINE_WIDTH
55611
55646
  });
55612
55647
  let port_sizes = geometry.port_sizes;
55613
55648
  let default_port_sizes = topic_defaults ? topic_defaults.port_sizes : null;
@@ -55617,8 +55652,117 @@ function create_topic_node(gobj, desc, record) {
55617
55652
  if (r == null && is_object(default_port_sizes)) r = default_port_sizes[key];
55618
55653
  if (r != null) style.ports[i].r = r;
55619
55654
  }
55655
+ } else {
55656
+ style.port = false;
55657
+ style.ports = [];
55658
+ }
55659
+ return {
55660
+ type: "html",
55661
+ style
55662
+ };
55663
+ }
55664
+ /************************************************************
55665
+ * The SQUARE: a G6 `rect` of the topic's colour, no ports, the
55666
+ * name as a label under it when `node_labels` says so. A native
55667
+ * shape and not html on purpose: G6's own stroke and halo paint
55668
+ * on it, so the focus, the selection and the anchor are three
55669
+ * keys of the style instead of a rebuilt innerHTML.
55670
+ *
55671
+ * flags {highlight, selected, anchored}
55672
+ ************************************************************/
55673
+ function compact_shape_of(gobj, desc, record, flags) {
55674
+ let dark = gobj.priv.theme === "dark";
55675
+ let f = flags || {};
55676
+ let tier = desc.node_treedb_type;
55677
+ let side = COMPACT_SIZE[tier] || COMPACT_SIZE.hierarchical;
55678
+ let labels = gobj_read_bool_attr(gobj, "node_labels");
55679
+ let stroke = getStrokeColor(desc.color);
55680
+ let line_width = 1.5;
55681
+ if (f.selected) {
55682
+ stroke = SELECT_RING;
55683
+ line_width = 3;
55684
+ }
55685
+ if (f.highlight) {
55686
+ stroke = HIGHLIGHT_COLOR;
55687
+ line_width = 3;
55688
+ }
55689
+ let dash = [];
55690
+ if (tier === "extended") dash = [4, 3];
55691
+ if (f.anchored) {
55692
+ dash = [2, 2];
55693
+ line_width = Math.max(line_width, 2.5);
55694
+ }
55695
+ return {
55696
+ type: "rect",
55697
+ style: {
55698
+ size: [side, side],
55699
+ radius: Math.round(side / 5),
55700
+ fill: desc.color,
55701
+ stroke,
55702
+ lineWidth: line_width,
55703
+ lineDash: dash,
55704
+ halo: !!f.highlight,
55705
+ haloStroke: HIGHLIGHT_HALO,
55706
+ haloLineWidth: 8,
55707
+ port: false,
55708
+ ports: [],
55709
+ labelText: labels ? node_label(desc, record) : "",
55710
+ labelPlacement: "bottom",
55711
+ labelFontSize: 11,
55712
+ labelFill: dark ? "#e6e9ef" : "#2e333d",
55713
+ labelMaxWidth: 150,
55714
+ labelWordWrap: true,
55715
+ labelMaxLines: 1,
55716
+ labelTextOverflow: "ellipsis",
55717
+ labelBackground: true,
55718
+ labelBackgroundFill: dark ? "#1b2230" : "#ffffff",
55719
+ labelBackgroundOpacity: .85,
55720
+ labelBackgroundRadius: 3,
55721
+ labelPadding: [1, 3]
55722
+ }
55723
+ };
55724
+ }
55725
+ /************************************************************
55726
+ * Give these record nodes the shape their mode says, then
55727
+ * repaint them with the flags they carry. A `+N` chip is not a
55728
+ * record and keeps its own shape.
55729
+ ************************************************************/
55730
+ function reshape_nodes(gobj, ids) {
55731
+ let graph = gobj.priv.graph;
55732
+ if (!graph) return;
55733
+ let updates = [];
55734
+ let touched = /* @__PURE__ */ new Set();
55735
+ for (let id of ids) {
55736
+ let nd;
55737
+ try {
55738
+ nd = graph.getNodeData(id);
55739
+ } catch (e) {
55740
+ continue;
55741
+ }
55742
+ if (!nd || !nd.data || !nd.data.desc || is_more_node(nd)) continue;
55743
+ let geometry = nd.data.graph_props || {};
55744
+ let shape = node_shape_of(gobj, id, nd.data.desc, nd.data.record || {}, geometry, pills_html_of(gobj, nd));
55745
+ updates.push({
55746
+ id,
55747
+ type: shape.type,
55748
+ style: shape.style
55749
+ });
55750
+ touched.add(id);
55751
+ }
55752
+ if (!updates.length) return;
55753
+ try {
55754
+ graph.updateNodeData(updates);
55755
+ } catch (e) {
55756
+ log_error(`${gobj_short_name(gobj)}: cannot reshape the nodes: ${e}`);
55757
+ return;
55620
55758
  }
55621
- if (node_graph_type) graph.addNodeData([node_def]);
55759
+ repaint_cards(gobj, touched);
55760
+ }
55761
+ function all_record_node_ids(gobj) {
55762
+ let graph = gobj.priv.graph;
55763
+ let ids = [];
55764
+ for (let nd of graph && graph.getNodeData() || []) if (nd && nd.data && nd.data.desc && !is_more_node(nd)) ids.push(nd.id);
55765
+ return ids;
55622
55766
  }
55623
55767
  /************************************************************
55624
55768
  * Update topic node data
@@ -56465,7 +56609,7 @@ var ManualLayout = class extends BaseLayout {
56465
56609
  }
56466
56610
  };
56467
56611
  /************************************************************
56468
- * Custom G6 layouts: TreedbTreeLayout / TreedbOutlineLayout.
56612
+ * Custom G6 layouts: TreedbTreeLayout / TreedbRadialLayout.
56469
56613
  *
56470
56614
  * Thin adapters over treedb_layout.js: the nodes with their sizes,
56471
56615
  * the edges with the RANK of the hook they leave by (the position
@@ -56483,7 +56627,8 @@ function treedb_layout_input(data) {
56483
56627
  let w = is_array(size) ? size[0] : size;
56484
56628
  let h = is_array(size) ? size.length > 1 ? size[1] : size[0] : size;
56485
56629
  let ports = node.style && is_array(node.style.ports) ? node.style.ports : [];
56486
- ports_of.set(node.id, ports.map((p) => p.key));
56630
+ let keys = node.data && is_array(node.data.port_keys) ? node.data.port_keys : ports.map((p) => p.key);
56631
+ ports_of.set(node.id, keys);
56487
56632
  return {
56488
56633
  id: node.id,
56489
56634
  w,
@@ -56519,12 +56664,6 @@ var TreedbTreeLayout = class extends BaseLayout {
56519
56664
  return treedb_layout_output(layout_tree(input.nodes, input.edges, options));
56520
56665
  }
56521
56666
  };
56522
- var TreedbOutlineLayout = class extends BaseLayout {
56523
- async execute(data, options) {
56524
- let input = treedb_layout_input(data);
56525
- return treedb_layout_output(layout_outline(input.nodes, input.edges, options));
56526
- }
56527
- };
56528
56667
  var TreedbRadialLayout = class extends BaseLayout {
56529
56668
  async execute(data, options) {
56530
56669
  let input = treedb_layout_input(data);
@@ -57216,7 +57355,7 @@ function get_port_radius(gobj, node_id, port_key) {
57216
57355
  if (ports[i].r != null) return ports[i].r;
57217
57356
  break;
57218
57357
  }
57219
- return style.portR || 6;
57358
+ return style.portR || PORT_R_ENTITY;
57220
57359
  }
57221
57360
  /************************************************************
57222
57361
  * How much wider than the port itself its hit area is,
@@ -57248,7 +57387,7 @@ function detect_port_click(gobj, node_id, canvasX, canvasY) {
57248
57387
  const style = graph.getNodeData(node_id).style || {};
57249
57388
  const { w, h } = extract_size(style);
57250
57389
  const ports = style.ports || [];
57251
- const defaultR = style.portR || 6;
57390
+ const defaultR = style.portR || PORT_R_ENTITY;
57252
57391
  let best_key = null;
57253
57392
  let best_dist = Infinity;
57254
57393
  let slop = port_hit_slop_world(graph);
@@ -57405,7 +57544,7 @@ function enter_linking_mode(gobj) {
57405
57544
  p.stroke = "#52c41a";
57406
57545
  p.lineWidth = 3;
57407
57546
  if (p.r != null) p.r = p.r + 4;
57408
- else p.r = (style.portR || 6) + 4;
57547
+ else p.r = (style.portR || PORT_R_ENTITY) + 4;
57409
57548
  }
57410
57549
  graph.updateNodeData([{
57411
57550
  id: vh.node_id,
@@ -57557,7 +57696,7 @@ function find_hook_at_point(gobj, canvasX, canvasY) {
57557
57696
  let pl = p.placement || [.5, .5];
57558
57697
  let px = pos[0] + (pl[0] - .5) * w;
57559
57698
  let py = pos[1] + (pl[1] - .5) * h;
57560
- let r = p.r != null ? p.r : style.portR || 6;
57699
+ let r = p.r != null ? p.r : style.portR || PORT_R_ENTITY;
57561
57700
  let dx = canvasX - px;
57562
57701
  let dy = canvasY - py;
57563
57702
  if (Math.sqrt(dx * dx + dy * dy) <= r + 6) return {
@@ -58315,7 +58454,19 @@ function repaint_cards(gobj, ids) {
58315
58454
  } catch (e) {
58316
58455
  continue;
58317
58456
  }
58318
- let html = node_innerHTML_of(nd, priv.theme, focus.has(id), selected.has(id), priv.anchor_state === "on" && id === priv.anchor_id, pills_html_of(gobj, nd));
58457
+ let anchored = priv.anchor_state === "on" && id === priv.anchor_id;
58458
+ if (nd && nd.data && nd.data.desc && node_is_compact(gobj, id)) {
58459
+ updates.push({
58460
+ id,
58461
+ style: compact_shape_of(gobj, nd.data.desc, nd.data.record || {}, {
58462
+ highlight: focus.has(id),
58463
+ selected: selected.has(id),
58464
+ anchored
58465
+ }).style
58466
+ });
58467
+ continue;
58468
+ }
58469
+ let html = node_innerHTML_of(nd, priv.theme, focus.has(id), selected.has(id), anchored, pills_html_of(gobj, nd));
58319
58470
  if (html !== null) updates.push({
58320
58471
  id,
58321
58472
  style: { innerHTML: html }
@@ -58352,7 +58503,19 @@ function refresh_html_nodes_theme(gobj, theme) {
58352
58503
  for (let i = 0; i < nodes.length; i++) {
58353
58504
  let id = nodes[i].id;
58354
58505
  let nd = graph.getNodeData(id);
58355
- let html = node_innerHTML_of(nd, theme, highlighted.has(id), selected.has(id), priv.anchor_state === "on" && id === priv.anchor_id, pills_html_of(gobj, nd));
58506
+ let anchored = priv.anchor_state === "on" && id === priv.anchor_id;
58507
+ if (nd && nd.data && nd.data.desc && node_is_compact(gobj, id)) {
58508
+ updates.push({
58509
+ id,
58510
+ style: compact_shape_of(gobj, nd.data.desc, nd.data.record || {}, {
58511
+ highlight: highlighted.has(id),
58512
+ selected: selected.has(id),
58513
+ anchored
58514
+ }).style
58515
+ });
58516
+ continue;
58517
+ }
58518
+ let html = node_innerHTML_of(nd, theme, highlighted.has(id), selected.has(id), anchored, pills_html_of(gobj, nd));
58356
58519
  if (html === null) html = more_innerHTML_of(gobj, nd, theme);
58357
58520
  if (html !== null) updates.push({
58358
58521
  id,
@@ -58972,8 +59135,10 @@ function ctx_item(icon_id, label, value) {
58972
59135
  ************************************************************/
58973
59136
  function build_node_context_menu(gobj, node_id) {
58974
59137
  let items = [];
58975
- if (gobj.priv.edit_mode) {
58976
- inject_svg_icons();
59138
+ inject_svg_icons();
59139
+ if (node_is_compact(gobj, node_id)) items.push(ctx_item("g6-icon-fullscreen", t("open node"), "toggle_node_mode"));
59140
+ else items.push(ctx_item("g6-icon-fullscreen-exit", t("close node"), "toggle_node_mode"));
59141
+ if (gobj.priv.edit_mode && !node_is_compact(gobj, node_id)) {
58977
59142
  items.push(ctx_item("g6-icon-resize", t("resize all"), "resize_all_nodes"));
58978
59143
  items.push(ctx_item("g6-icon-resize", t("resize topic nodes"), "resize_topic_nodes"));
58979
59144
  }
@@ -59001,8 +59166,11 @@ function build_edge_context_menu(gobj) {
59001
59166
  * Handle context menu click: dispatch by value.
59002
59167
  ************************************************************/
59003
59168
  function handle_context_menu_click(gobj, value) {
59004
- gobj.priv;
59169
+ let priv = gobj.priv;
59005
59170
  switch (value) {
59171
+ case "toggle_node_mode":
59172
+ if (priv._context_node_id) gobj_send_event(gobj, "EV_TOGGLE_NODE_MODE", { id: priv._context_node_id }, gobj);
59173
+ break;
59006
59174
  case "resize_all_nodes":
59007
59175
  copy_size_to_nodes(gobj, false);
59008
59176
  break;
@@ -59533,7 +59701,7 @@ async function reconcile_fold_apply(gobj, opts, visible) {
59533
59701
  let nd = graph.getNodeData(id);
59534
59702
  let desc = nd && nd.data && nd.data.desc;
59535
59703
  let geometry = nd && nd.data && nd.data.graph_props || {};
59536
- if (desc && !is_array(geometry.size)) {
59704
+ if (desc && !is_array(geometry.size) && !node_is_compact(gobj, id)) {
59537
59705
  let size = card_size_for(desc.node_treedb_type, !!sig);
59538
59706
  resize.push({
59539
59707
  id,
@@ -59618,6 +59786,7 @@ function ac_clear_data(gobj, event, kw, src) {
59618
59786
  priv._fold_model = null;
59619
59787
  priv._fold_state = null;
59620
59788
  priv._pill_sig = null;
59789
+ priv._open_nodes = null;
59621
59790
  graph_remove_plugin(gobj, "history");
59622
59791
  update_history_buttons(gobj);
59623
59792
  graph_clear(gobj);
@@ -60116,6 +60285,79 @@ function ac_set_loose_topics(gobj, event, kw, src) {
60116
60285
  return 0;
60117
60286
  }
60118
60287
  /************************************************************
60288
+ * The shape of every record: open cards or closed squares.
60289
+ * A change of mode forgets the nodes toggled against the old
60290
+ * one -- they were exceptions to a rule that no longer holds --
60291
+ * and lays the graph out again, because a square and a card
60292
+ * do not occupy the same room.
60293
+ ************************************************************/
60294
+ function ac_set_node_mode(gobj, event, kw, src) {
60295
+ let priv = gobj.priv;
60296
+ let mode = kw && kw.node_mode === "compact" ? "compact" : "expanded";
60297
+ gobj_write_str_attr(gobj, "node_mode", mode);
60298
+ priv._open_nodes = /* @__PURE__ */ new Set();
60299
+ if (!priv.graph || !priv._fold_model) return 0;
60300
+ reshape_nodes(gobj, all_record_node_ids(gobj));
60301
+ reconcile_fold(gobj, {
60302
+ relayout: true,
60303
+ fit: true
60304
+ });
60305
+ return 0;
60306
+ }
60307
+ /************************************************************
60308
+ * The name under a closed square, on or off. Only the closed
60309
+ * nodes change, and nothing moves: the label hangs outside the
60310
+ * square and the layout measures the square.
60311
+ ************************************************************/
60312
+ function ac_set_node_labels(gobj, event, kw, src) {
60313
+ let priv = gobj.priv;
60314
+ gobj_write_bool_attr(gobj, "node_labels", !!(kw && kw.node_labels));
60315
+ if (!priv.graph) return 0;
60316
+ reshape_nodes(gobj, all_record_node_ids(gobj).filter((id) => node_is_compact(gobj, id)));
60317
+ return 0;
60318
+ }
60319
+ /************************************************************
60320
+ * ONE node opened or closed against the mode (a double click,
60321
+ * or the context menu). It holds still on screen while the
60322
+ * rest of the graph makes room, as a fold does.
60323
+ ************************************************************/
60324
+ function ac_toggle_node_mode$1(gobj, event, kw, src) {
60325
+ let priv = gobj.priv;
60326
+ let node_id = kw && kw.id || "";
60327
+ if (!priv.graph || !node_id) {
60328
+ log_error(`${gobj_short_name(gobj)}: toggle node mode without a node`);
60329
+ return -1;
60330
+ }
60331
+ if (!priv._open_nodes) priv._open_nodes = /* @__PURE__ */ new Set();
60332
+ if (priv._open_nodes.has(node_id)) priv._open_nodes.delete(node_id);
60333
+ else priv._open_nodes.add(node_id);
60334
+ let vp = yui_graph_viewport_of(priv.graph, node_id);
60335
+ reshape_nodes(gobj, [node_id]);
60336
+ if (priv._fold_model) reconcile_fold(gobj, {
60337
+ relayout: true,
60338
+ keep: node_id,
60339
+ keep_vp: vp
60340
+ });
60341
+ return 0;
60342
+ }
60343
+ /************************************************************
60344
+ * A double click on a node: open it if closed, close it if open.
60345
+ * A `+N` chip is not a record and has no shape to toggle.
60346
+ ************************************************************/
60347
+ function ac_node_dblclick(gobj, event, kw, src) {
60348
+ let priv = gobj.priv;
60349
+ let node_id = kw && kw.evt && kw.evt.target ? kw.evt.target.id : "";
60350
+ let nd = null;
60351
+ try {
60352
+ nd = priv.graph.getNodeData(node_id);
60353
+ } catch (e) {
60354
+ nd = null;
60355
+ }
60356
+ if (!nd || !nd.data || !nd.data.desc || is_more_node(nd)) return 0;
60357
+ gobj_send_event(gobj, "EV_TOGGLE_NODE_MODE", { id: node_id }, gobj);
60358
+ return 0;
60359
+ }
60360
+ /************************************************************
60119
60361
  * Tell whoever asked how many nodes the term matched. The count is
60120
60362
  * the answer to the question the box asks; without it "nothing moved"
60121
60363
  * and "nothing matched" look the same.
@@ -60605,6 +60847,26 @@ function create_gclass$4(gclass_name) {
60605
60847
  ac_set_loose_topics,
60606
60848
  null
60607
60849
  ],
60850
+ [
60851
+ "EV_SET_NODE_MODE",
60852
+ ac_set_node_mode,
60853
+ null
60854
+ ],
60855
+ [
60856
+ "EV_SET_NODE_LABELS",
60857
+ ac_set_node_labels,
60858
+ null
60859
+ ],
60860
+ [
60861
+ "EV_TOGGLE_NODE_MODE",
60862
+ ac_toggle_node_mode$1,
60863
+ null
60864
+ ],
60865
+ [
60866
+ "EV_NODE_DBLCLICK",
60867
+ ac_node_dblclick,
60868
+ null
60869
+ ],
60608
60870
  [
60609
60871
  "EV_CENTER",
60610
60872
  ac_center,
@@ -60709,6 +60971,10 @@ function create_gclass$4(gclass_name) {
60709
60971
  ["EV_SET_HIDDEN_TOPICS", 0],
60710
60972
  ["EV_SET_MAIN_TOPIC", 0],
60711
60973
  ["EV_SET_LOOSE_TOPICS", 0],
60974
+ ["EV_SET_NODE_MODE", 0],
60975
+ ["EV_SET_NODE_LABELS", 0],
60976
+ ["EV_TOGGLE_NODE_MODE", 0],
60977
+ ["EV_NODE_DBLCLICK", 0],
60712
60978
  ["EV_LEGEND_STATE", event_flag_t.EVF_OUTPUT_EVENT],
60713
60979
  ["EV_CENTER", 0],
60714
60980
  ["EV_FULLSCREEN", 0],
@@ -60774,6 +61040,8 @@ var attrs_table$3 = [
60774
61040
  SDATA(data_type_t.DTP_LIST, "hidden_topics", sdata_flag_t.SDF_PERSIST, "[]", "Topics hidden from the graph (legend). User preference, per treedb"),
60775
61041
  SDATA(data_type_t.DTP_STRING, "main_topic", sdata_flag_t.SDF_PERSIST, "", "The topic the tree hangs from; empty = deduced by the graph. User preference, per treedb"),
60776
61042
  SDATA(data_type_t.DTP_LIST, "loose_topics", sdata_flag_t.SDF_PERSIST, "[]", "Topics whose LOOSE records (no parent) are shown. User preference, per treedb"),
61043
+ SDATA(data_type_t.DTP_STRING, "node_mode", sdata_flag_t.SDF_PERSIST, "expanded", "How the records are drawn: `expanded` = the cards with their ports, `compact` = coloured squares, the topology alone. User preference"),
61044
+ SDATA(data_type_t.DTP_BOOLEAN, "node_labels", sdata_flag_t.SDF_PERSIST, true, "A compact node says its name under its square. User preference"),
60777
61045
  SDATA(data_type_t.DTP_POINTER, "gobj_remote_yuno", 0, null, "Remote Yuno to request data"),
60778
61046
  SDATA(data_type_t.DTP_STRING, "treedb_name", 0, null, "Remote service treedb name"),
60779
61047
  SDATA(data_type_t.DTP_DICT, "descs", 0, null, "Descriptions of topics obtained"),
@@ -60851,7 +61119,9 @@ function mt_create$3(gobj) {
60851
61119
  fold_page_size: gobj_read_integer_attr(gobj, "fold_page_size"),
60852
61120
  hidden_topics: gobj_read_attr(gobj, "hidden_topics") || [],
60853
61121
  main_topic: gobj_read_str_attr(gobj, "main_topic") || "",
60854
- loose_topics: gobj_read_attr(gobj, "loose_topics") || []
61122
+ loose_topics: gobj_read_attr(gobj, "loose_topics") || [],
61123
+ node_mode: gobj_read_str_attr(gobj, "node_mode") || "expanded",
61124
+ node_labels: gobj_read_bool_attr(gobj, "node_labels")
60855
61125
  }, gobj);
60856
61126
  populate_nodes_tree_options(gobj);
60857
61127
  if (priv.with_treedb_tables) {}
@@ -60902,6 +61172,7 @@ function build_ui$3(gobj) {
60902
61172
  let priv = gobj.priv;
60903
61173
  let padding = gobj_read_attr(gobj, "padding");
60904
61174
  let $toolbar = make_toolbar(gobj);
61175
+ refresh_node_shape_buttons(gobj, $toolbar);
60905
61176
  let $back = make_back_to_topics(gobj);
60906
61177
  let $container = createElement2([
60907
61178
  "div",
@@ -61043,6 +61314,57 @@ function option_label(name) {
61043
61314
  /************************************************************
61044
61315
  *
61045
61316
  ************************************************************/
61317
+ /************************************************************
61318
+ * The two toggles of the nodes' shape.
61319
+ *
61320
+ * Closed nodes: every record a rounded square of its topic's
61321
+ * colour, no ports, no text -- the topology of a graph of many
61322
+ * nodes, which the cards hide behind their own size. Labels:
61323
+ * the name under each square, when the squares alone do not say
61324
+ * enough. Both persist, both are forwarded to the G6 child, and
61325
+ * one node can be opened against the rule with a double click or
61326
+ * its context menu (the child's business).
61327
+ ************************************************************/
61328
+ function node_shape_items(gobj, wide) {
61329
+ let toggle = (cls, icon, event_name, label_key) => {
61330
+ return [
61331
+ "button",
61332
+ {
61333
+ class: `${cls} button`,
61334
+ type: "button",
61335
+ style: {
61336
+ height: wide,
61337
+ width: "2.5em"
61338
+ },
61339
+ title: t(label_key),
61340
+ "data-i18n-title": label_key,
61341
+ "aria-label": t(label_key),
61342
+ "data-i18n-aria-label": label_key,
61343
+ "aria-pressed": "false"
61344
+ },
61345
+ ["i", {
61346
+ style: "font-size:1.5em; color:inherit;",
61347
+ class: icon
61348
+ }],
61349
+ { click: (evt) => {
61350
+ evt.stopPropagation();
61351
+ gobj_send_event(gobj, event_name, {}, gobj);
61352
+ } }
61353
+ ];
61354
+ };
61355
+ return [toggle("GRAPH_NODE_MODE", "yi-hexagon-nodes", "EV_TOGGLE_NODE_MODE", "closed nodes"), toggle("GRAPH_NODE_LABELS", "yi-font", "EV_TOGGLE_NODE_LABELS", "node labels")];
61356
+ }
61357
+ function refresh_node_shape_buttons(gobj, $root) {
61358
+ if (!$root) return;
61359
+ let compact = gobj_read_str_attr(gobj, "node_mode") === "compact";
61360
+ let labels = gobj_read_bool_attr(gobj, "node_labels");
61361
+ set_pressed_state($root, ".GRAPH_NODE_MODE", compact);
61362
+ set_pressed_state($root, ".GRAPH_NODE_LABELS", labels);
61363
+ let $mode = $root.querySelector(".GRAPH_NODE_MODE");
61364
+ if ($mode) $mode.setAttribute("aria-pressed", compact ? "true" : "false");
61365
+ let $labels = $root.querySelector(".GRAPH_NODE_LABELS");
61366
+ if ($labels) $labels.setAttribute("aria-pressed", labels ? "true" : "false");
61367
+ }
61046
61368
  function make_toolbar(gobj) {
61047
61369
  let priv = gobj.priv;
61048
61370
  let modes = gobj_read_attr(gobj, "operation_modes");
@@ -61113,6 +61435,7 @@ function make_toolbar(gobj) {
61113
61435
  } }
61114
61436
  ],
61115
61437
  ...yui_graph_fold_items(gobj, gobj_read_str_attr(gobj, "wide")),
61438
+ ...node_shape_items(gobj, gobj_read_str_attr(gobj, "wide")),
61116
61439
  [
61117
61440
  "button",
61118
61441
  {
@@ -62427,6 +62750,29 @@ function ac_collapse_all(gobj, event, kw, src) {
62427
62750
  return 0;
62428
62751
  }
62429
62752
  /************************************************************
62753
+ * The shape pair of the toolbar: the attr flips, persists,
62754
+ * shows on the button, and reaches the G6 child, which owns
62755
+ * the nodes.
62756
+ ************************************************************/
62757
+ function ac_toggle_node_mode(gobj, event, kw, src) {
62758
+ let priv = gobj.priv;
62759
+ let mode = gobj_read_str_attr(gobj, "node_mode") === "compact" ? "expanded" : "compact";
62760
+ gobj_write_str_attr(gobj, "node_mode", mode);
62761
+ gobj_save_persistent_attrs(gobj, "node_mode");
62762
+ refresh_node_shape_buttons(gobj, priv.$container);
62763
+ if (priv.gobj_nodes_tree) gobj_send_event(priv.gobj_nodes_tree, "EV_SET_NODE_MODE", { node_mode: mode }, gobj);
62764
+ return 0;
62765
+ }
62766
+ function ac_toggle_node_labels(gobj, event, kw, src) {
62767
+ let priv = gobj.priv;
62768
+ let labels = !gobj_read_bool_attr(gobj, "node_labels");
62769
+ gobj_write_bool_attr(gobj, "node_labels", labels);
62770
+ gobj_save_persistent_attrs(gobj, "node_labels");
62771
+ refresh_node_shape_buttons(gobj, priv.$container);
62772
+ if (priv.gobj_nodes_tree) gobj_send_event(priv.gobj_nodes_tree, "EV_SET_NODE_LABELS", { node_labels: labels }, gobj);
62773
+ return 0;
62774
+ }
62775
+ /************************************************************
62430
62776
  * How many nodes the term matched, from the graph child. Zero with a
62431
62777
  * term typed is an ANSWER and is shown; an empty box shows nothing.
62432
62778
  ************************************************************/
@@ -62640,6 +62986,16 @@ function create_gclass$3(gclass_name) {
62640
62986
  ac_collapse_all,
62641
62987
  null
62642
62988
  ],
62989
+ [
62990
+ "EV_TOGGLE_NODE_MODE",
62991
+ ac_toggle_node_mode,
62992
+ null
62993
+ ],
62994
+ [
62995
+ "EV_TOGGLE_NODE_LABELS",
62996
+ ac_toggle_node_labels,
62997
+ null
62998
+ ],
62643
62999
  [
62644
63000
  "EV_LAYOUT_AUTOSET",
62645
63001
  ac_layout_autoset,
@@ -62704,6 +63060,8 @@ function create_gclass$3(gclass_name) {
62704
63060
  ["EV_FIND_NODES", 0],
62705
63061
  ["EV_EXPAND_ALL", 0],
62706
63062
  ["EV_COLLAPSE_ALL", 0],
63063
+ ["EV_TOGGLE_NODE_MODE", 0],
63064
+ ["EV_TOGGLE_NODE_LABELS", 0],
62707
63065
  ["EV_FIND_RESULT", 0],
62708
63066
  ["EV_LAYOUT_AUTOSET", 0],
62709
63067
  ["EV_LEGEND_TOPIC", 0],