@yuneta/gobj-ui 7.23.185 → 7.23.187

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.
@@ -1,4 +1,4 @@
1
- import { SDATA, SDATA_END, clean_name, createElement2, createOneHtml, create_template_record, data_type_t, debounce, delete_from_list, duplicate_objects, empty_json, empty_string, escapeHtml, event_flag_t, flat_diff, gclass_create, gclass_find_by_name, gclass_flag_t, getPositionRelativeToBody, gobj_bottom_gobj, gobj_change_state, gobj_command, gobj_create, gobj_create_pure_child, gobj_create_service, gobj_current_state, gobj_destroy, gobj_find_child, gobj_find_service, gobj_full_name, gobj_gclass_name, gobj_get_gclass_trace_level2, gobj_global_trace_level, gobj_global_trace_no_level, gobj_is_destroying, gobj_is_playing, gobj_is_pure_child, gobj_is_running, gobj_is_service, gobj_is_volatil, gobj_match_children, gobj_name, gobj_parent, gobj_post_event, gobj_publish_event, gobj_read_attr, gobj_read_bool_attr, gobj_read_integer_attr, gobj_read_pointer_attr, gobj_read_str_attr, gobj_save_persistent_attrs, gobj_send_event, gobj_set_trace_machine_format, gobj_short_name, gobj_start, gobj_start_tree, gobj_stop, gobj_stop_children, gobj_subscribe_event, gobj_unsubscribe_event, gobj_write_attr, gobj_write_bool_attr, gobj_write_integer_attr, gobj_write_str_attr, gobj_yuno, gobj_yuno_name, is_array, is_boolean, is_date, is_gobj, is_null, is_number, is_object, is_string, json2flat, json_deep_copy, json_object_size, json_object_update, json_object_update_missing, json_size, kw_clone_by_keys, kw_flag_t, kw_get_dict, kw_get_dict_value, kw_get_int, kw_get_local_storage_value, kw_get_str, kw_has_key, kw_set_dict_value, kw_set_local_storage_value, kwid_find_one_record, kwid_new_dict, log_error, log_warning, msg_iev_get_stack, parseBoolean, parseSVG, refresh_language, safeSrc, sdata_flag_t, set_console_log_enabled, set_log_callback, sprintf, str_in_list, template_get_field_desc, trace_level_t, trace_msg, treedb_decoder_fkey, treedb_encoder_fkey, treedb_get_field_desc, treedb_hook_data_size } from "@yuneta/gobj-js";
1
+ import { SDATA, SDATA_END, clean_name, createElement2, createOneHtml, create_template_record, data_type_t, debounce, delete_from_list, duplicate_objects, empty_json, empty_string, escapeHtml, event_flag_t, flat_diff, gclass_create, gclass_find_by_name, gclass_flag_t, getPositionRelativeToBody, gobj_bottom_gobj, gobj_change_state, gobj_command, gobj_create, gobj_create_pure_child, gobj_create_service, gobj_current_state, gobj_destroy, gobj_find_child, gobj_find_service, gobj_full_name, gobj_gclass_name, gobj_get_gclass_trace_level2, gobj_global_trace_level, gobj_global_trace_no_level, gobj_is_destroying, gobj_is_playing, gobj_is_pure_child, gobj_is_running, gobj_is_service, gobj_is_volatil, gobj_match_children, gobj_name, gobj_parent, gobj_post_event, gobj_publish_event, gobj_read_attr, gobj_read_bool_attr, gobj_read_integer_attr, gobj_read_pointer_attr, gobj_read_str_attr, gobj_save_persistent_attrs, gobj_send_event, gobj_set_trace_machine_format, gobj_short_name, gobj_start, gobj_start_tree, gobj_stop, gobj_stop_children, gobj_subscribe_event, gobj_unsubscribe_event, gobj_write_attr, gobj_write_bool_attr, gobj_write_integer_attr, gobj_write_str_attr, gobj_yuno, gobj_yuno_name, is_array, is_boolean, is_date, is_gobj, is_null, is_number, is_object, is_string, json2flat, json_deep_copy, json_is_identical, json_object_size, json_object_update, json_object_update_missing, json_size, kw_clone_by_keys, kw_flag_t, kw_get_dict, kw_get_dict_value, kw_get_int, kw_get_local_storage_value, kw_get_str, kw_has_key, kw_set_dict_value, kw_set_local_storage_value, kwid_find_one_record, kwid_new_dict, log_error, log_warning, msg_iev_get_stack, parseBoolean, parseSVG, refresh_language, safeSrc, sdata_flag_t, set_console_log_enabled, set_log_callback, sprintf, str_in_list, template_get_field_desc, trace_level_t, trace_msg, treedb_decoder_fkey, treedb_encoder_fkey, treedb_get_field_desc, treedb_hook_data_size } from "@yuneta/gobj-js";
2
2
  import i18next, { t } from "i18next";
3
3
  import TomSelect from "tom-select";
4
4
  import { createJSONEditor } from "vanilla-jsoneditor";
@@ -23709,7 +23709,7 @@ function build_ui$6(gobj) {
23709
23709
  [[
23710
23710
  "span",
23711
23711
  { class: "icon" },
23712
- [yui_toolbar_icon("yi-hexagon-nodes")]
23712
+ [yui_toolbar_icon("yi-compass-drafting")]
23713
23713
  ], [
23714
23714
  "span",
23715
23715
  { i18n: "schema" },
@@ -23720,6 +23720,29 @@ function build_ui$6(gobj) {
23720
23720
  gobj_send_event(gobj, "EV_TOGGLE_LANDING_VIEW", {}, gobj);
23721
23721
  } }
23722
23722
  ],
23723
+ [
23724
+ "button",
23725
+ {
23726
+ class: "button TREEDB_SCHEMA_JSON_BTN is-hidden",
23727
+ title: t("schema json"),
23728
+ "aria-label": t("schema json"),
23729
+ "data-i18n-title": "schema json",
23730
+ "data-i18n-aria-label": "schema json"
23731
+ },
23732
+ [[
23733
+ "span",
23734
+ { class: "icon" },
23735
+ [yui_toolbar_icon("yi-code")]
23736
+ ], [
23737
+ "span",
23738
+ { i18n: "schema json" },
23739
+ "schema json"
23740
+ ]],
23741
+ { click: (evt) => {
23742
+ evt.stopPropagation();
23743
+ gobj_send_event(gobj, "EV_OPEN_SCHEMA_JSON", {}, gobj);
23744
+ } }
23745
+ ],
23723
23746
  [
23724
23747
  "div",
23725
23748
  {
@@ -24163,6 +24186,7 @@ function show_topics_landing(gobj) {
24163
24186
  let $info = $container.querySelector(".TREEDB_TOPIC_INFO");
24164
24187
  let $back = $container.querySelector(".TREEDB_TOPICS_BACK");
24165
24188
  let $toggle = $container.querySelector(".TREEDB_LANDING_TOGGLE");
24189
+ let $schema_json = $container.querySelector(".TREEDB_SCHEMA_JSON_BTN");
24166
24190
  if ($tabs) $tabs.classList.add("is-hidden");
24167
24191
  if ($sub) $sub.classList.add("is-hidden");
24168
24192
  if ($info) $info.classList.add("is-hidden");
@@ -24172,6 +24196,7 @@ function show_topics_landing(gobj) {
24172
24196
  $toggle.classList.remove("is-hidden");
24173
24197
  $toggle.classList.toggle("is-primary", schema_mode);
24174
24198
  }
24199
+ if ($schema_json) $schema_json.classList.toggle("is-hidden", !schema_mode);
24175
24200
  if (schema_mode) {
24176
24201
  if ($landing) $landing.classList.add("is-hidden");
24177
24202
  if ($schema) $schema.classList.remove("is-hidden");
@@ -24241,9 +24266,11 @@ function show_topic_detail(gobj) {
24241
24266
  let $info = $container.querySelector(".TREEDB_TOPIC_INFO");
24242
24267
  let $back = $container.querySelector(".TREEDB_TOPICS_BACK");
24243
24268
  let $toggle = $container.querySelector(".TREEDB_LANDING_TOGGLE");
24269
+ let $schema_json = $container.querySelector(".TREEDB_SCHEMA_JSON_BTN");
24244
24270
  if ($landing) $landing.classList.add("is-hidden");
24245
24271
  if ($schema) $schema.classList.add("is-hidden");
24246
24272
  if ($info) $info.classList.add("is-hidden");
24273
+ if ($schema_json) $schema_json.classList.add("is-hidden");
24247
24274
  if ($toggle) $toggle.classList.add("is-hidden");
24248
24275
  if ($tabs) $tabs.classList.remove("is-hidden");
24249
24276
  if ($sub) $sub.classList.remove("is-hidden");
@@ -24284,8 +24311,10 @@ function show_topic_info(gobj, topic) {
24284
24311
  let $schema = $container.querySelector(".TREEDB_TOPICS_SCHEMA");
24285
24312
  let $back = $container.querySelector(".TREEDB_TOPICS_BACK");
24286
24313
  let $toggle = $container.querySelector(".TREEDB_LANDING_TOGGLE");
24314
+ let $schema_json = $container.querySelector(".TREEDB_SCHEMA_JSON_BTN");
24287
24315
  if ($landing) $landing.classList.add("is-hidden");
24288
24316
  if ($schema) $schema.classList.add("is-hidden");
24317
+ if ($schema_json) $schema_json.classList.add("is-hidden");
24289
24318
  if ($toggle) $toggle.classList.add("is-hidden");
24290
24319
  if ($tabs) $tabs.classList.add("is-hidden");
24291
24320
  if ($sub) $sub.classList.add("is-hidden");
@@ -24560,7 +24589,7 @@ function register_sub_routes$1(gobj) {
24560
24589
  if (is_object(gobj_read_attr(gobj, "landing_routes"))) nodes.push({
24561
24590
  route: base + "/schema",
24562
24591
  label: "schema",
24563
- icon: "yi-hexagon-nodes",
24592
+ icon: "yi-compass-drafting",
24564
24593
  gclass: "C_YUI_TREEDB_SCHEMA"
24565
24594
  });
24566
24595
  if (is_object(descs)) for (let topic of Object.keys(descs)) {
@@ -24703,10 +24732,11 @@ function is_mobile$2() {
24703
24732
  * whole service (lazy drill). CHILD model: the viewer publishes
24704
24733
  * EV_EXPAND_PATH to us.
24705
24734
  ************************************************************/
24706
- function open_json_viewer$1(gobj) {
24735
+ function open_json_viewer$1(gobj, opts) {
24707
24736
  let priv = gobj.priv;
24737
+ let schema = !!(opts && opts.schema);
24708
24738
  if (priv.json_win || priv.json_modal) {
24709
- request_print_tranger$1(gobj, "");
24739
+ if (!schema) request_print_tranger$1(gobj, "");
24710
24740
  return;
24711
24741
  }
24712
24742
  if (!gclass_find_by_name("C_YUI_JSON")) {
@@ -24716,7 +24746,10 @@ function open_json_viewer$1(gobj) {
24716
24746
  }
24717
24747
  let mobile = is_mobile$2();
24718
24748
  let shell = yui_shell_of(gobj);
24719
- let jv = gobj_create_service(`treedb-topics-json-${clean_name(gobj_name(gobj))}`, "C_YUI_JSON", { subscriber: gobj }, gobj);
24749
+ let jv = gobj_create_service(`treedb-topics-json-${clean_name(gobj_name(gobj))}`, "C_YUI_JSON", {
24750
+ subscriber: gobj,
24751
+ json_data: schema ? gobj_read_attr(gobj, "descs") : null
24752
+ }, gobj);
24720
24753
  if (!jv) {
24721
24754
  log_error(`${gobj_short_name(gobj)}: cannot create the JSON viewer`);
24722
24755
  return;
@@ -24734,7 +24767,7 @@ function open_json_viewer$1(gobj) {
24734
24767
  dialog: true,
24735
24768
  logical_class: "TREEDB_JSON_SHEET",
24736
24769
  title_prefix: priv.treedb_name,
24737
- title: "raw json",
24770
+ title: schema ? "schema json" : "raw json",
24738
24771
  t,
24739
24772
  on_close: () => {
24740
24773
  if (gobj_is_destroying(gobj)) return;
@@ -24756,8 +24789,8 @@ function open_json_viewer$1(gobj) {
24756
24789
  height: 620,
24757
24790
  logical_class: "TREEDB_JSON_WINDOW",
24758
24791
  title_prefix: priv.treedb_name,
24759
- title: "raw json",
24760
- icon: "yi-eye",
24792
+ title: schema ? "schema json" : "raw json",
24793
+ icon: schema ? "yi-code" : "yi-eye",
24761
24794
  body: $box,
24762
24795
  manager: null,
24763
24796
  on_close: () => {
@@ -24772,7 +24805,7 @@ function open_json_viewer$1(gobj) {
24772
24805
  }
24773
24806
  gobj_start(priv.json_win);
24774
24807
  }
24775
- request_print_tranger$1(gobj, "");
24808
+ if (!schema) request_print_tranger$1(gobj, "");
24776
24809
  }
24777
24810
  /************************************************************
24778
24811
  * Close the JSON viewer (user dismiss / teardown).
@@ -25366,6 +25399,19 @@ function ac_open_json$1(gobj, event, kw, src) {
25366
25399
  return 0;
25367
25400
  }
25368
25401
  /********************************************
25402
+ * The schema, as the json it is: the `descs` the schema graph is
25403
+ * drawing. Same viewer and same host as the tranger's raw json --
25404
+ * one at a time, so it closes whatever was open first.
25405
+ ********************************************/
25406
+ function ac_open_schema_json(gobj, event, kw, src) {
25407
+ if (!is_object(gobj_read_attr(gobj, "descs"))) {
25408
+ log_error(`${gobj_short_name(gobj)}: schema json with no descs`);
25409
+ return -1;
25410
+ }
25411
+ open_json_viewer$1(gobj, { schema: true });
25412
+ return 0;
25413
+ }
25414
+ /********************************************
25369
25415
  * Open the graph where the reader left it: its last visited
25370
25416
  * route under the graph's base -- `<graph>/<topic>` when a topic
25371
25417
  * was focused there -- or the bare base when the graph was never
@@ -25499,6 +25545,11 @@ function create_gclass$6(gclass_name) {
25499
25545
  ac_open_json$1,
25500
25546
  null
25501
25547
  ],
25548
+ [
25549
+ "EV_OPEN_SCHEMA_JSON",
25550
+ ac_open_schema_json,
25551
+ null
25552
+ ],
25502
25553
  [
25503
25554
  "EV_OPEN_GRAPH",
25504
25555
  ac_open_graph,
@@ -25571,6 +25622,7 @@ function create_gclass$6(gclass_name) {
25571
25622
  ["EV_RECORD_WRITTEN", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
25572
25623
  ["EV_REFRESH_TOPIC", 0],
25573
25624
  ["EV_OPEN_JSON", 0],
25625
+ ["EV_OPEN_SCHEMA_JSON", 0],
25574
25626
  ["EV_OPEN_GRAPH", 0],
25575
25627
  ["EV_EXPAND_PATH", 0],
25576
25628
  ["EV_JSON_CLOSED", 0],
@@ -30255,6 +30307,88 @@ function register_c_yui_treedb_topic_with_form() {
30255
30307
  return create_gclass$5(GCLASS_NAME$5);
30256
30308
  }
30257
30309
  //#endregion
30310
+ //#region src/graph_save_plan.js
30311
+ /***********************************************************************
30312
+ * graph_save_plan.js
30313
+ *
30314
+ * Which topics a Save of the graph has to write, and which it
30315
+ * must leave alone.
30316
+ *
30317
+ * The arrangement of a treedb graph lives in `__graphs__`, ONE
30318
+ * record per topic. The Save collected every topic the view had
30319
+ * loaded and wrote all of them, whatever had moved: on a treedb
30320
+ * of five topics, dragging one card appended five records, four
30321
+ * of them saying exactly what the record under them said. The
30322
+ * store is append-only, so those four are there for ever.
30323
+ *
30324
+ * This module is the decision, and nothing else: no G6, no DOM,
30325
+ * no gobj. Give it what the view is holding and what the backend
30326
+ * already has, and it answers which topics differ.
30327
+ *
30328
+ * Two things it deliberately does NOT do:
30329
+ *
30330
+ * - It does not ask the history plugin. The Save button lights
30331
+ * from two places -- `history.canUndo()` for what G6 records
30332
+ * (a move, a resize) and `mark_graph_dirty()` for what it does
30333
+ * not (a colour, a pill, a per-topic default) -- so the undo
30334
+ * stack is not the list of what changed, and reading it as one
30335
+ * would silently drop half the changes. A value comparison
30336
+ * sees both, and keeps working through undo, redo and a
30337
+ * history that was just cleared.
30338
+ *
30339
+ * - It does not compare `__origin__`. That key is the node_uuid
30340
+ * of whoever wrote the record, bookkeeping about the write and
30341
+ * not about the arrangement; comparing it would make every
30342
+ * topic differ the first time a second browser saved, and the
30343
+ * record it forces would say nothing new.
30344
+ *
30345
+ * Copyright (c) 2026, ArtGins.
30346
+ * All Rights Reserved.
30347
+ ***********************************************************************/
30348
+ /***************************************************************
30349
+ * The properties as they are worth comparing: everything
30350
+ * except who wrote them.
30351
+ ***************************************************************/
30352
+ function arrangement_of(properties) {
30353
+ if (!is_object(properties)) return {};
30354
+ let out = {};
30355
+ for (const [key, value] of Object.entries(properties)) {
30356
+ if (key === "__origin__") continue;
30357
+ out[key] = value;
30358
+ }
30359
+ return out;
30360
+ }
30361
+ /***************************************************************
30362
+ * TRUE when the arrangement the view holds for `topic_name`
30363
+ * is not the one the backend already has.
30364
+ *
30365
+ * A topic with no saved record has never been written, so
30366
+ * anything it holds is a change -- and an EMPTY arrangement
30367
+ * is not: a topic the view loaded and nobody ever arranged
30368
+ * has nothing to say, and saying it would create a record
30369
+ * that means "no layout".
30370
+ ***************************************************************/
30371
+ function topic_arrangement_changed(live, saved) {
30372
+ let a = arrangement_of(live);
30373
+ if (!saved) return Object.keys(a).length > 0;
30374
+ return !json_is_identical(a, arrangement_of(saved));
30375
+ }
30376
+ /***************************************************************
30377
+ * The topics a Save has to write.
30378
+ *
30379
+ * live {topic_name: properties} what the view holds
30380
+ * saved {topic_name: properties} what the backend has
30381
+ *
30382
+ * Returns their names, in the order the view holds them.
30383
+ ***************************************************************/
30384
+ function plan_graph_saves(live, saved) {
30385
+ let plan = [];
30386
+ if (!is_object(live)) return plan;
30387
+ let have = is_object(saved) ? saved : {};
30388
+ for (const [topic_name, properties] of Object.entries(live)) if (topic_arrangement_changed(properties, have[topic_name])) plan.push(topic_name);
30389
+ return plan;
30390
+ }
30391
+ //#endregion
30258
30392
  //#region src/treedb_node_label.js
30259
30393
  /***********************************************************************
30260
30394
  * treedb_node_label.js
@@ -58482,6 +58616,7 @@ var PRIVATE_DATA$4 = {
58482
58616
  graph: null,
58483
58617
  __graphs__: [],
58484
58618
  _graph_properties: {},
58619
+ _saved_graph_properties: {},
58485
58620
  yet_showed: false,
58486
58621
  edit_mode: false,
58487
58622
  operation_mode: null,
@@ -58732,7 +58867,7 @@ function build_graph$1(gobj) {
58732
58867
  container: priv.$container,
58733
58868
  animation: false,
58734
58869
  autoResize: false,
58735
- zoomRange: [.2, 4],
58870
+ zoomRange: [.02, 4],
58736
58871
  node: {
58737
58872
  palette: {
58738
58873
  type: "group",
@@ -59988,6 +60123,7 @@ function draw_link(gobj, child_topic, child_id, source_col, fkey, verbose) {
59988
60123
  parent_topic,
59989
60124
  parent_id,
59990
60125
  hook_name,
60126
+ child_topic,
59991
60127
  child_id,
59992
60128
  is_tree
59993
60129
  });
@@ -60067,11 +60203,15 @@ function clear_link(gobj, child_topic, child_id, source_col, fkey, verbose) {
60067
60203
  function build_graph_properties(gobj) {
60068
60204
  let priv = gobj.priv;
60069
60205
  priv._graph_properties = {};
60206
+ priv._saved_graph_properties = {};
60070
60207
  for (let i = 0; i < priv.__graphs__.length; i++) {
60071
60208
  let rec = priv.__graphs__[i];
60072
60209
  if (!rec.active || !rec.topic) continue;
60073
60210
  let props = rec.properties;
60074
- if (is_object(props)) priv._graph_properties[rec.topic] = props;
60211
+ if (is_object(props)) {
60212
+ priv._graph_properties[rec.topic] = props;
60213
+ priv._saved_graph_properties[rec.topic] = json_deep_copy(props);
60214
+ }
60075
60215
  }
60076
60216
  }
60077
60217
  /************************************************************
@@ -60156,35 +60296,22 @@ function save_geometry(gobj) {
60156
60296
  }
60157
60297
  const edges = graph.getData().edges;
60158
60298
  for (let i = 0; i < edges.length; i++) update_edge_geometry(gobj, edges[i].id);
60159
- let origin = gobj_read_str_attr(gobj_yuno(), "node_uuid");
60160
- for (const [topic_name, properties] of Object.entries(priv._graph_properties)) {
60161
- properties.__origin__ = origin;
60162
- let kw_update = {
60163
- treedb_name: priv.treedb_name,
60164
- topic_name: "__graphs__",
60165
- record: {
60166
- id: topic_name,
60167
- topic: topic_name,
60168
- active: true,
60169
- properties
60170
- },
60171
- options: {
60172
- list_dict: true,
60173
- autolink: false,
60174
- create: true
60175
- }
60176
- };
60177
- gobj_publish_event(gobj, "EV_UPDATE_NODE", kw_update);
60178
- }
60299
+ let plan = plan_graph_saves(priv._graph_properties, priv._saved_graph_properties);
60300
+ for (let i = 0; i < plan.length; i++) save_topic_graph_properties(gobj, plan[i]);
60179
60301
  }
60180
60302
  /************************************************************
60181
60303
  * Save __graphs__ properties for a single topic to backend.
60304
+ *
60305
+ * The ONE door to `__graphs__`: a caller that knows a topic moved
60306
+ * says so, and this decides whether the backend needs telling.
60182
60307
  ************************************************************/
60183
60308
  function save_topic_graph_properties(gobj, topic_name) {
60184
60309
  let priv = gobj.priv;
60185
60310
  let properties = priv._graph_properties[topic_name];
60186
60311
  if (!properties) return;
60312
+ if (!topic_arrangement_changed(properties, priv._saved_graph_properties[topic_name])) return;
60187
60313
  properties.__origin__ = gobj_read_str_attr(gobj_yuno(), "node_uuid");
60314
+ priv._saved_graph_properties[topic_name] = json_deep_copy(properties);
60188
60315
  let kw_update = {
60189
60316
  treedb_name: priv.treedb_name,
60190
60317
  topic_name: "__graphs__",
@@ -60503,7 +60630,7 @@ async function graph_fit_selection(gobj, ids) {
60503
60630
  let bw = Math.max(1, maxx - minx);
60504
60631
  let bh = Math.max(1, maxy - miny);
60505
60632
  let zoom = Math.min(vw / bw, vh / bh);
60506
- let range = graph.getZoomRange() || [.2, 4];
60633
+ let range = graph.getZoomRange() || [.02, 4];
60507
60634
  zoom = Math.max(range[0], Math.min(range[1], zoom));
60508
60635
  await graph.zoomTo(zoom);
60509
60636
  await graph.focusElement(ids);
@@ -63240,16 +63367,14 @@ function refresh_html_nodes_theme(gobj, theme) {
63240
63367
  function refresh_default_edges_theme(gobj, theme) {
63241
63368
  let graph = gobj.priv.graph;
63242
63369
  if (!graph) return;
63243
- let dark = theme === "dark";
63244
63370
  let edges = graph.getData().edges || [];
63245
63371
  let updates = [];
63246
63372
  for (let i = 0; i < edges.length; i++) {
63247
63373
  let d = edges[i].data || {};
63248
63374
  if (!d.themed_default) continue;
63249
- let stroke = d.is_tree ? dark ? "#22a7c2" : "#0e7490" : dark ? "#8b94a3" : "#6b7280";
63250
63375
  updates.push({
63251
63376
  id: edges[i].id,
63252
- style: { stroke }
63377
+ style: { stroke: default_edge_stroke(gobj, d, theme) }
63253
63378
  });
63254
63379
  }
63255
63380
  if (updates.length > 0) graph.updateEdgeData(updates);
@@ -63821,21 +63946,54 @@ function apply_edge_properties(gobj, edge_id, lineWidth, stroke, scope) {
63821
63946
  deselect_edge(gobj);
63822
63947
  }
63823
63948
  /************************************************************
63949
+ * The colour a link is DRAWN in: the colour of the two knobs it
63950
+ * joins.
63951
+ *
63952
+ * Both ends of a link wear the CHILD topic's colour -- the
63953
+ * parent's hook port is painted with the colour of whatever may
63954
+ * hang there, and the child's fkey port with its own (see
63955
+ * build_ports) -- so the line between them has one obvious
63956
+ * colour, and a reader follows a link by its hue instead of
63957
+ * tracing a grey thread across the canvas.
63958
+ *
63959
+ * It goes through port_rim(): the FILL of a port says which
63960
+ * topic, and a line on the canvas is a graphical object that
63961
+ * needs 3:1 against it, which that mix clears in both themes
63962
+ * (9.11 dark, 3.80 light) while keeping the hue. So a link is
63963
+ * drawn in exactly the colour of the rim of the two knobs it
63964
+ * ends on.
63965
+ *
63966
+ * The containment/tree relation (parent and child of the same
63967
+ * topic) is no longer marked by a teal of its own -- it is that
63968
+ * topic's colour like any other link, and what still tells it
63969
+ * apart is its WIDTH.
63970
+ *
63971
+ * A topic with no colour, or an edge of a graph with no schema
63972
+ * yet, keeps the neutral grey this used to give everything.
63973
+ ************************************************************/
63974
+ function default_edge_stroke(gobj, d, theme) {
63975
+ let priv = gobj.priv;
63976
+ let desc = is_object(priv.descs) ? priv.descs[d.child_topic] : null;
63977
+ if (desc && desc.color) return port_rim(desc.color, theme);
63978
+ let dark = theme === "dark";
63979
+ return d.is_tree ? dark ? "#22a7c2" : "#0e7490" : dark ? "#8b94a3" : "#6b7280";
63980
+ }
63981
+ /************************************************************
63824
63982
  * The style an edge INHERITS, before anything saved for the
63825
63983
  * edge itself: its hook's default in the parent topic, else the
63826
63984
  * parent topic's default for every edge, else the library's.
63827
63985
  *
63828
- * The library's: neutral grey, teal for the containment/tree
63829
- * relation (parent and child of the same topic, a
63830
- * self-hierarchy). `themed_default` says the colour is still the
63831
- * library's, so a theme toggle re-themes it and leaves a chosen
63832
- * one alone.
63986
+ * The library's: the colour of the ports it joins (see
63987
+ * default_edge_stroke), and a wider line for the
63988
+ * containment/tree relation. `themed_default` says the colour is
63989
+ * still the library's, so a theme toggle re-themes it and leaves
63990
+ * a chosen one alone.
63833
63991
  ************************************************************/
63834
63992
  function edge_inherited_style_of(gobj, d) {
63835
- let dark = gobj.priv.theme === "dark";
63993
+ let priv = gobj.priv;
63836
63994
  let style = {
63837
63995
  lineWidth: d.is_tree ? 2 : 1.6,
63838
- stroke: d.is_tree ? dark ? "#22a7c2" : "#0e7490" : dark ? "#8b94a3" : "#6b7280",
63996
+ stroke: default_edge_stroke(gobj, d, priv.theme),
63839
63997
  themed_default: true
63840
63998
  };
63841
63999
  let defs = topic_defaults_of(gobj, d.parent_topic);
@@ -71656,7 +71814,7 @@ function render_toolbar(gobj) {
71656
71814
  ]);
71657
71815
  let $right = [];
71658
71816
  if (priv.treedb_id && has_model) {
71659
- $right.push(toolbar_button("SCHEMA_DIAGRAM_BTN", "yi-hexagon-nodes", priv.diagram ? "topics" : "diagram", "EV_TOGGLE_DIAGRAM", false));
71817
+ $right.push(toolbar_button("SCHEMA_DIAGRAM_BTN", "yi-compass-drafting", priv.diagram ? "topics" : "diagram", "EV_TOGGLE_DIAGRAM", false));
71660
71818
  $right.push(toolbar_button("SCHEMA_VALIDATE_BTN", "yi-square-check", "check", "EV_VALIDATE", false));
71661
71819
  $right.push(toolbar_button("SCHEMA_EXPORT_BTN", "yi-download", "export", "EV_EXPORT", false));
71662
71820
  if (!readonly) $right.push(toolbar_button("SCHEMA_IMPORT_BTN", "yi-upload", "import", "EV_IMPORT", false));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.23.185",
3
+ "version": "7.23.187",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",