@yuneta/gobj-ui 7.2.5 → 7.4.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.
@@ -18581,6 +18581,8 @@ var PRIVATE_DATA$4 = {
18581
18581
  topics: [],
18582
18582
  records: {},
18583
18583
  gobj_nodes_tree: null,
18584
+ find_timer: null,
18585
+ focus_topic: "",
18584
18586
  gobj_treedb_tables: null,
18585
18587
  hook_data_viewer: null,
18586
18588
  json_gobj: null,
@@ -18655,6 +18657,11 @@ function mt_stop$4(gobj) {
18655
18657
  * Framework Method: Destroy
18656
18658
  ***************************************************************/
18657
18659
  function mt_destroy$4(gobj) {
18660
+ let priv = gobj.priv;
18661
+ if (priv.find_timer) {
18662
+ clearTimeout(priv.find_timer);
18663
+ priv.find_timer = null;
18664
+ }
18658
18665
  destroy_ui$1(gobj);
18659
18666
  }
18660
18667
  /***************************
@@ -18674,26 +18681,37 @@ function build_ui$4(gobj) {
18674
18681
  class: "C_YUI_TREEDB_GRAPH",
18675
18682
  style: `height:100%; display:flex; flex-direction:column;`
18676
18683
  },
18677
- [[
18678
- "div",
18679
- { class: "GRAPH_TOOLBAR_ROW is-flex-grow-0 is-flex is-align-items-center" },
18680
- $back ? [$back, $toolbar] : [$toolbar]
18681
- ], [
18682
- "div",
18683
- {
18684
- class: `GRAPH_BODY is-flex-grow-1 ${padding}`,
18685
- style: "height:100%; min-height:0; overflow:hidden;"
18686
- },
18687
- [[
18684
+ [
18685
+ [
18686
+ "div",
18687
+ { class: "GRAPH_TOOLBAR_ROW is-flex-grow-0 is-flex is-align-items-center" },
18688
+ $back ? [$back, $toolbar] : [$toolbar]
18689
+ ],
18690
+ [
18688
18691
  "div",
18689
18692
  {
18690
- id: priv.canvas_id,
18691
- class: `GRAPH_CANVAS graph-container`,
18692
- style: "height:100%; min-height:0;border: 1px solid var(--bulma-border-weak);border-radius:0.2rem;"
18693
+ class: "GRAPH_LEGEND is-hidden is-flex-grow-0",
18694
+ style: "display:flex; flex-wrap:wrap; align-items:center; gap:.4rem; padding:.35rem .5rem;"
18693
18695
  },
18694
18696
  []
18695
- ]]
18696
- ]]
18697
+ ],
18698
+ [
18699
+ "div",
18700
+ {
18701
+ class: `GRAPH_BODY is-flex-grow-1 ${padding}`,
18702
+ style: "height:100%; min-height:0; overflow:hidden;"
18703
+ },
18704
+ [[
18705
+ "div",
18706
+ {
18707
+ id: priv.canvas_id,
18708
+ class: `GRAPH_CANVAS graph-container`,
18709
+ style: "height:100%; min-height:0;border: 1px solid var(--bulma-border-weak);border-radius:0.2rem;"
18710
+ },
18711
+ []
18712
+ ]]
18713
+ ]
18714
+ ]
18697
18715
  ]);
18698
18716
  gobj_write_attr(gobj, "$container", $container);
18699
18717
  refresh_language($container, t);
@@ -18761,7 +18779,7 @@ function destroy_ui$1(gobj) {
18761
18779
  *
18762
18780
  ************************************************************/
18763
18781
  function make_toolbar(gobj) {
18764
- gobj.priv;
18782
+ let priv = gobj.priv;
18765
18783
  let modes = gobj_read_attr(gobj, "operation_modes");
18766
18784
  if (gobj_read_bool_attr(gobj, "readonly")) modes = modes.filter((mode) => mode !== "edition");
18767
18785
  let left_items = [
@@ -18826,6 +18844,29 @@ function make_toolbar(gobj) {
18826
18844
  gobj_send_event(gobj, "EV_REFRESH_TREEDB", { evt }, gobj);
18827
18845
  } }
18828
18846
  ],
18847
+ [
18848
+ "button",
18849
+ {
18850
+ class: "GRAPH_LEGEND_BTN button ml-2",
18851
+ title: t("legend"),
18852
+ "aria-label": t("legend"),
18853
+ "data-i18n-title": "legend",
18854
+ "data-i18n-aria-label": "legend"
18855
+ },
18856
+ [["i", { class: "yi-square" }], [
18857
+ "span",
18858
+ {
18859
+ class: "is-hidden-mobile",
18860
+ style: "padding-left:5px;",
18861
+ i18n: "legend"
18862
+ },
18863
+ "legend"
18864
+ ]],
18865
+ { click: (evt) => {
18866
+ evt.stopPropagation();
18867
+ gobj_send_event(gobj, "EV_TOGGLE_LEGEND", {}, gobj);
18868
+ } }
18869
+ ],
18829
18870
  [
18830
18871
  "button",
18831
18872
  {
@@ -18870,7 +18911,12 @@ function make_toolbar(gobj) {
18870
18911
  ]],
18871
18912
  { input: (evt) => {
18872
18913
  evt.stopPropagation();
18873
- gobj_send_event(gobj, "EV_FIND_NODES", { text: evt.target.value.trim() }, gobj);
18914
+ let text = evt.target.value.trim();
18915
+ if (priv.find_timer) clearTimeout(priv.find_timer);
18916
+ priv.find_timer = setTimeout(function() {
18917
+ priv.find_timer = null;
18918
+ gobj_send_event(gobj, "EV_FIND_NODES", { text }, gobj);
18919
+ }, 250);
18874
18920
  } }
18875
18921
  ], [
18876
18922
  "div",
@@ -19830,6 +19876,86 @@ function ac_set_operation_mode$1(gobj, event, kw, src) {
19830
19876
  * until its data is loaded).
19831
19877
  ************************************************************/
19832
19878
  /************************************************************
19879
+ * Fill the legend strip with one entry per topic: a swatch in the
19880
+ * topic's colour and its name. The colours belong to the graph child —
19881
+ * it assigns them from its palette when the schema arrives — so they
19882
+ * are read from there and never guessed twice.
19883
+ *
19884
+ * An entry is a BUTTON: clicking it focuses that topic, clicking the
19885
+ * focused one clears the focus. The focus travels the same way a topic
19886
+ * card's graph icon travels — as EV_TOPIC_SELECTED, which the host turns
19887
+ * into the URL, so what you are looking at stays linkable.
19888
+ ************************************************************/
19889
+ function refresh_legend(gobj) {
19890
+ let priv = gobj.priv;
19891
+ let $container = gobj_read_attr(gobj, "$container");
19892
+ if (!$container) return;
19893
+ let $legend = $container.querySelector(".GRAPH_LEGEND");
19894
+ if (!$legend) return;
19895
+ let descs = priv.gobj_nodes_tree ? gobj_read_attr(priv.gobj_nodes_tree, "descs") : null;
19896
+ $legend.textContent = "";
19897
+ if (!descs) return;
19898
+ for (const [topic_name, desc] of Object.entries(descs)) {
19899
+ if (topic_name.substring(0, 2) === "__") continue;
19900
+ if (!desc || !desc.color) continue;
19901
+ let focused = priv.focus_topic === topic_name;
19902
+ let $item = createElement2([
19903
+ "button",
19904
+ {
19905
+ class: "GRAPH_LEGEND_ITEM button is-small" + (focused ? " is-primary" : ""),
19906
+ title: topic_name,
19907
+ "aria-label": topic_name,
19908
+ "aria-pressed": focused ? "true" : "false"
19909
+ },
19910
+ [["span", {
19911
+ class: "GRAPH_LEGEND_SWATCH",
19912
+ style: `display:inline-block; width:.8rem; height:.8rem; border-radius:3px; margin-right:.4rem; background:${desc.color}; border:1px solid rgba(0,0,0,.25);`
19913
+ }], [
19914
+ "span",
19915
+ {},
19916
+ topic_name
19917
+ ]],
19918
+ { click: (evt) => {
19919
+ evt.stopPropagation();
19920
+ gobj_send_event(gobj, "EV_LEGEND_TOPIC", { topic: topic_name }, gobj);
19921
+ } }
19922
+ ]);
19923
+ $legend.appendChild($item);
19924
+ }
19925
+ }
19926
+ /************************************************************
19927
+ *
19928
+ ************************************************************/
19929
+ function ac_toggle_legend(gobj, event, kw, src) {
19930
+ let $container = gobj_read_attr(gobj, "$container");
19931
+ if (!$container) return 0;
19932
+ let $legend = $container.querySelector(".GRAPH_LEGEND");
19933
+ if (!$legend) {
19934
+ log_error(`${gobj_short_name(gobj)}: no legend strip to toggle`);
19935
+ return -1;
19936
+ }
19937
+ if ($legend.classList.contains("is-hidden")) {
19938
+ refresh_legend(gobj);
19939
+ $legend.classList.remove("is-hidden");
19940
+ } else $legend.classList.add("is-hidden");
19941
+ return 0;
19942
+ }
19943
+ /************************************************************
19944
+ * A legend entry was clicked: focus that topic, or clear the focus if
19945
+ * it was already the focused one. Published UP rather than applied
19946
+ * here, so the URL follows — the host owns the route.
19947
+ ************************************************************/
19948
+ function ac_legend_topic(gobj, event, kw, src) {
19949
+ let priv = gobj.priv;
19950
+ let topic = kw && kw.topic || "";
19951
+ if (!topic) {
19952
+ log_error(`${gobj_short_name(gobj)}: legend click with no topic`);
19953
+ return -1;
19954
+ }
19955
+ gobj_publish_event(gobj, "EV_TOPIC_SELECTED", { topic: priv.focus_topic === topic ? "" : topic });
19956
+ return 0;
19957
+ }
19958
+ /************************************************************
19833
19959
  * Forward the find down to the graph child.
19834
19960
  ************************************************************/
19835
19961
  function ac_find_nodes$1(gobj, event, kw, src) {
@@ -19858,7 +19984,11 @@ function ac_find_result(gobj, event, kw, src) {
19858
19984
  }
19859
19985
  function ac_set_focus_topic(gobj, event, kw, src) {
19860
19986
  let priv = gobj.priv;
19861
- if (priv.gobj_nodes_tree) gobj_send_event(priv.gobj_nodes_tree, "EV_FOCUS_TOPIC", { topic: kw && kw.topic }, gobj);
19987
+ priv.focus_topic = kw && kw.topic || "";
19988
+ if (priv.gobj_nodes_tree) gobj_send_event(priv.gobj_nodes_tree, "EV_FOCUS_TOPIC", { topic: priv.focus_topic }, gobj);
19989
+ let $container = gobj_read_attr(gobj, "$container");
19990
+ let $legend = $container ? $container.querySelector(".GRAPH_LEGEND") : null;
19991
+ if ($legend && !$legend.classList.contains("is-hidden")) refresh_legend(gobj);
19862
19992
  return 0;
19863
19993
  }
19864
19994
  /************************************************************
@@ -20026,6 +20156,16 @@ function create_gclass$4(gclass_name) {
20026
20156
  ac_find_nodes$1,
20027
20157
  null
20028
20158
  ],
20159
+ [
20160
+ "EV_TOGGLE_LEGEND",
20161
+ ac_toggle_legend,
20162
+ null
20163
+ ],
20164
+ [
20165
+ "EV_LEGEND_TOPIC",
20166
+ ac_legend_topic,
20167
+ null
20168
+ ],
20029
20169
  [
20030
20170
  "EV_FIND_RESULT",
20031
20171
  ac_find_result,
@@ -20074,6 +20214,9 @@ function create_gclass$4(gclass_name) {
20074
20214
  ["EV_SET_FOCUS_TOPIC", 0],
20075
20215
  ["EV_FIND_NODES", 0],
20076
20216
  ["EV_FIND_RESULT", 0],
20217
+ ["EV_TOGGLE_LEGEND", 0],
20218
+ ["EV_LEGEND_TOPIC", 0],
20219
+ ["EV_TOPIC_SELECTED", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
20077
20220
  ["EV_OPERATION_MODE_CHANGED", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
20078
20221
  ["EV_RECORD_WRITTEN", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
20079
20222
  ["EV_SHOW", 0],
@@ -52629,6 +52772,8 @@ ensure_drag_canvas_patch();
52629
52772
  * Constants
52630
52773
  ***************************************************************/
52631
52774
  var GCLASS_NAME$1 = "C_G6_NODES_TREE";
52775
+ var HIGHLIGHT_COLOR = "#f0a020";
52776
+ var HIGHLIGHT_HALO = "rgba(240,160,32,0.35)";
52632
52777
  /***************************************************************
52633
52778
  * Internal layout and operation mode definitions
52634
52779
  ***************************************************************/
@@ -52678,6 +52823,7 @@ var attrs_table$1 = [
52678
52823
  SDATA(data_type_t.DTP_BOOLEAN, "with_gridline", 0, true, "Use gridline plugin"),
52679
52824
  SDATA(data_type_t.DTP_BOOLEAN, "with_fullscreen", 0, true, "Use fullscreen plugin"),
52680
52825
  SDATA(data_type_t.DTP_BOOLEAN, "with_toolbar", 0, true, "Use toolbar plugin"),
52826
+ SDATA(data_type_t.DTP_INTEGER, "minimap_min_nodes", 0, 30, "Show the minimap from this many nodes on (0 = never). A minimap of a graph that fits on screen is decoration; one of two hundred cards is the only way to know where you are"),
52681
52827
  SDATA(data_type_t.DTP_STRING, "toolbar_position", 0, "right-top", "Toolbar position: top-left, top-right, bottom-left, bottom-right, left-top, right-top"),
52682
52828
  SDATA(data_type_t.DTP_LIST, "layout_names", sdata_flag_t.SDF_RD, JSON.stringify(Object.keys(_layouts)), "Available layout names (read-only, for parent to query)"),
52683
52829
  SDATA(data_type_t.DTP_STRING, "hook_port_position", 0, "bottom", "Hook port position"),
@@ -53905,10 +54051,12 @@ function graph_focus_topic(gobj, topic) {
53905
54051
  if (ids.length === 0) log_warning(`${gobj_short_name(gobj)}: focus topic '${topic}' has no nodes`);
53906
54052
  for (let id of ids) states[id] = ["active"];
53907
54053
  }
54054
+ let prev_ids = priv._focus_ids || [];
53908
54055
  priv._focus_ids = ids;
53909
54056
  priv._focus_topic = topic || null;
53910
54057
  try {
53911
54058
  if (typeof graph.setElementState === "function") graph.setElementState(states);
54059
+ apply_node_highlight(gobj, prev_ids, ids);
53912
54060
  if (ids.length && typeof graph.focusElement === "function") graph.focusElement(ids);
53913
54061
  } catch (e) {
53914
54062
  log_error(`${gobj_short_name(gobj)}: focus topic apply failed: ${e}`);
@@ -53965,10 +54113,12 @@ function graph_find_nodes(gobj, term) {
53965
54113
  }
53966
54114
  for (let id of ids) states[id] = ["active"];
53967
54115
  }
54116
+ let prev_ids = priv._focus_ids || [];
53968
54117
  priv._focus_ids = ids;
53969
54118
  priv._focus_topic = null;
53970
54119
  try {
53971
54120
  if (typeof graph.setElementState === "function") graph.setElementState(states);
54121
+ apply_node_highlight(gobj, prev_ids, ids);
53972
54122
  if (ids.length && typeof graph.focusElement === "function") graph.focusElement(ids);
53973
54123
  } catch (e) {
53974
54124
  log_error(`${gobj_short_name(gobj)}: find apply failed: ${e}`);
@@ -55512,27 +55662,121 @@ function show_node_detail_popover(gobj, node_id) {
55512
55662
  * not re-themed by setTheme()) are regenerated; structural
55513
55663
  * junction diamonds get their neutral fill/stroke re-themed.
55514
55664
  ************************************************************/
55515
- function refresh_html_nodes_theme(gobj, theme) {
55665
+ /************************************************************
55666
+ * Show or hide the minimap, deciding by the SIZE of the graph.
55667
+ *
55668
+ * A minimap of a graph that already fits on screen is decoration; one
55669
+ * of two hundred cards is the only way to know where you are. So it is
55670
+ * not a preference the reader has to find and set — it appears when
55671
+ * there is something to be lost in, from `minimap_min_nodes` on.
55672
+ *
55673
+ * Its shapes are drawn by hand. G6's minimap clones each element's key
55674
+ * shape into its own canvas, and every node here is an `html` node
55675
+ * whose key shape is a DOM element — the same reason the `active`
55676
+ * state never painted (see HIGHLIGHT_COLOR). A block in the topic's
55677
+ * colour is also what a minimap of cards SHOULD show: at that scale a
55678
+ * card is a rectangle anyway.
55679
+ ************************************************************/
55680
+ function refresh_minimap(gobj) {
55516
55681
  let graph = gobj.priv.graph;
55517
55682
  if (!graph) return;
55683
+ let threshold = gobj_read_integer_attr(gobj, "minimap_min_nodes");
55684
+ let n = 0;
55685
+ try {
55686
+ let data = graph.getData() || {};
55687
+ n = is_array(data.nodes) ? data.nodes.length : 0;
55688
+ } catch (e) {
55689
+ n = 0;
55690
+ }
55691
+ if (!threshold || n < threshold) {
55692
+ graph_remove_plugin(gobj, "minimap");
55693
+ return;
55694
+ }
55695
+ if (graph_get_plugin(gobj, "minimap")) return;
55696
+ graph_add_plugin(gobj, "minimap", {
55697
+ size: [200, 140],
55698
+ position: "bottom-left",
55699
+ shape: (id, element_type, element) => {
55700
+ if (element_type !== "node") return element;
55701
+ try {
55702
+ let nd = graph.getNodeData(id);
55703
+ let size = nd && nd.style && nd.style.size || [120, 60];
55704
+ let color = nd && nd.data && nd.data.desc && nd.data.desc.color || "#94a3b8";
55705
+ return new Rect({ style: {
55706
+ x: -size[0] / 2,
55707
+ y: -size[1] / 2,
55708
+ width: size[0],
55709
+ height: size[1],
55710
+ fill: color,
55711
+ radius: 4
55712
+ } });
55713
+ } catch (e) {
55714
+ log_error(`${gobj_short_name(gobj)}: minimap shape failed: ${e}`);
55715
+ return element;
55716
+ }
55717
+ }
55718
+ });
55719
+ }
55720
+ /************************************************************
55721
+ * The innerHTML of ONE node, in the given theme and highlight state.
55722
+ * The three treedb tiers (hierarchical / extended / child) each have
55723
+ * their own card, and both the theme refresh and the highlight need the
55724
+ * same three-way choice — it lived inline in the theme refresh and is
55725
+ * shared now. Returns null for a node that carries no desc.
55726
+ ************************************************************/
55727
+ function node_innerHTML_of(nd, theme, highlight) {
55728
+ if (!nd || !nd.data || !nd.data.desc) return null;
55729
+ let desc = nd.data.desc;
55730
+ let record = nd.data.record || {};
55731
+ let label = node_label(desc, record);
55732
+ switch (desc.node_treedb_type) {
55733
+ case "child": return build_chip_innerHTML(desc.color, theme, record.icon, label, record.id, highlight);
55734
+ case "extended": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, true, record.id, highlight);
55735
+ case "hierarchical": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, false, record.id, highlight);
55736
+ }
55737
+ return null;
55738
+ }
55739
+ /************************************************************
55740
+ * Repaint the cards whose highlight state CHANGES: the ones that had it
55741
+ * and lose it, plus the ones that gain it. Only those — a treedb graph
55742
+ * is redrawn per keystroke of the find box otherwise.
55743
+ ************************************************************/
55744
+ function apply_node_highlight(gobj, prev_ids, next_ids) {
55745
+ let priv = gobj.priv;
55746
+ let graph = priv.graph;
55747
+ if (!graph) return;
55748
+ let next = new Set(next_ids || []);
55749
+ let touched = /* @__PURE__ */ new Set([...prev_ids || [], ...next]);
55750
+ if (touched.size === 0) return;
55751
+ let updates = [];
55752
+ for (let id of touched) {
55753
+ let html = node_innerHTML_of(graph.getNodeData(id), priv.theme, next.has(id));
55754
+ if (html !== null) updates.push({
55755
+ id,
55756
+ style: { innerHTML: html }
55757
+ });
55758
+ }
55759
+ if (!updates.length) return;
55760
+ try {
55761
+ graph.updateNodeData(updates);
55762
+ graph.draw();
55763
+ } catch (e) {
55764
+ log_error(`${gobj_short_name(gobj)}: cannot repaint the highlight: ${e}`);
55765
+ }
55766
+ }
55767
+ function refresh_html_nodes_theme(gobj, theme) {
55768
+ let priv = gobj.priv;
55769
+ let graph = priv.graph;
55770
+ if (!graph) return;
55518
55771
  let nodes = graph.getData().nodes || [];
55772
+ let highlighted = new Set(priv._focus_ids || []);
55519
55773
  let updates = [];
55520
55774
  for (let i = 0; i < nodes.length; i++) {
55521
- let nd = graph.getNodeData(nodes[i].id);
55522
- if (!nd || !nd.data || !nd.data.desc) continue;
55523
- let tt = nd.data.desc.node_treedb_type;
55524
- let record = nd.data.record || {};
55525
- if (tt === "hierarchical") updates.push({
55526
- id: nodes[i].id,
55527
- 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) }
55528
- });
55529
- else if (tt === "child") updates.push({
55530
- id: nodes[i].id,
55531
- style: { innerHTML: build_chip_innerHTML(nd.data.desc.color, theme, record.icon, node_label(nd.data.desc, record), record.id) }
55532
- });
55533
- else if (tt === "extended") updates.push({
55534
- id: nodes[i].id,
55535
- 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) }
55775
+ let id = nodes[i].id;
55776
+ let html = node_innerHTML_of(graph.getNodeData(id), theme, highlighted.has(id));
55777
+ if (html !== null) updates.push({
55778
+ id,
55779
+ style: { innerHTML: html }
55536
55780
  });
55537
55781
  }
55538
55782
  if (updates.length > 0) graph.updateNodeData(updates);
@@ -55564,12 +55808,13 @@ function refresh_default_edges_theme(gobj, theme) {
55564
55808
  * but lighter (1px border, no shadow, single line). The name is
55565
55809
  * always legible (ellipsis + native title tooltip on overflow).
55566
55810
  ************************************************************/
55567
- function build_chip_innerHTML(color, theme, icon, label, key) {
55811
+ function build_chip_innerHTML(color, theme, icon, label, key, highlight) {
55568
55812
  let title = key || label;
55569
55813
  let dark = theme === "dark";
55570
55814
  let bg = dark ? `color-mix(in srgb, ${color} 30%, #2c3542)` : `color-mix(in srgb, ${color} 10%, ${dark ? "#1b2230" : "#ffffff"})`;
55571
55815
  let border = dark ? `color-mix(in srgb, ${color} 85%, #ffffff)` : color;
55572
55816
  let text_color = dark ? "#e8eaed" : "#0f172a";
55817
+ if (highlight) border = HIGHLIGHT_COLOR;
55573
55818
  let icon_html = "";
55574
55819
  if (icon) icon_html = `<img src="${safeSrc(icon)}" alt="" style="
55575
55820
  width: 16px; height: 16px; object-fit: contain;
@@ -55581,7 +55826,8 @@ function build_chip_innerHTML(color, theme, icon, label, key) {
55581
55826
  width: 100%;
55582
55827
  height: 100%;
55583
55828
  background: ${bg};
55584
- border: 1px solid ${border};
55829
+ border: ${highlight ? "3px" : "1px"} solid ${border};
55830
+ ${highlight ? `box-shadow: 0 0 0 4px ${HIGHLIGHT_HALO};` : ""}
55585
55831
  border-radius: 8px;
55586
55832
  color: ${text_color};
55587
55833
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
@@ -55607,7 +55853,7 @@ function build_chip_innerHTML(color, theme, icon, label, key) {
55607
55853
  * colour is kept (per-topic differentiation) but softened via
55608
55854
  * color-mix instead of a harsh saturated fill. Theme-aware.
55609
55855
  ************************************************************/
55610
- function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key) {
55856
+ function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key, highlight) {
55611
55857
  let title = key || label;
55612
55858
  let dark = theme === "dark";
55613
55859
  let surface = dark ? "#1b2230" : "#ffffff";
@@ -55621,6 +55867,10 @@ function build_node_innerHTML(color, theme, icon, label, topic_name, structural,
55621
55867
  border = dark ? `color-mix(in srgb, ${color} 85%, #ffffff)` : color;
55622
55868
  border_style = "solid";
55623
55869
  }
55870
+ if (highlight) {
55871
+ border = HIGHLIGHT_COLOR;
55872
+ border_style = "solid";
55873
+ }
55624
55874
  let title_color = dark ? "#e8eaed" : "#0f172a";
55625
55875
  let sub_color = dark ? "#9aa4b2" : "#64748b";
55626
55876
  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)";
@@ -55643,9 +55893,9 @@ function build_node_innerHTML(color, theme, icon, label, topic_name, structural,
55643
55893
  width: 100%;
55644
55894
  height: 100%;
55645
55895
  background: ${bg};
55646
- border: 1.5px ${border_style} ${border};
55896
+ border: ${highlight ? "3px" : "1.5px"} ${border_style} ${border};
55647
55897
  border-radius: 10px;
55648
- box-shadow: ${shadow};
55898
+ box-shadow: ${highlight ? `0 0 0 4px ${HIGHLIGHT_HALO}, ${shadow}` : shadow};
55649
55899
  color: ${title_color};
55650
55900
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
55651
55901
  display: flex;
@@ -56264,6 +56514,7 @@ function ac_load_data(gobj, event, kw, src) {
56264
56514
  graph_layout(gobj).then(() => {
56265
56515
  if (priv.edit_mode) graph_add_plugin(gobj, "history");
56266
56516
  else graph_remove_plugin(gobj, "history");
56517
+ refresh_minimap(gobj);
56267
56518
  if (priv._pending_focus_topic !== null) {
56268
56519
  let ft = priv._pending_focus_topic;
56269
56520
  priv._pending_focus_topic = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.2.5",
3
+ "version": "7.4.0",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",