@yuneta/gobj-ui 7.23.186 → 7.23.188

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.
@@ -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],
@@ -30342,54 +30394,57 @@ function plan_graph_saves(live, saved) {
30342
30394
  * treedb_node_label.js
30343
30395
  *
30344
30396
  * Pure, testable logic behind the label of a node in the treedb
30345
- * graph: WHAT A RECORD IS CALLED, which is not always what it is
30346
- * keyed by. Kept out of the gclass so it can be unit-tested with
30347
- * no DOM and no G6.
30397
+ * graph: WHAT A RECORD IS CALLED. Kept out of the gclass so it can
30398
+ * be unit-tested with no DOM and no G6.
30348
30399
  *
30349
30400
  * Copyright (c) 2026, ArtGins.
30350
30401
  * All Rights Reserved.
30351
30402
  ***********************************************************************/
30352
- /************************************************************
30353
- * What a node is CALLED, which is not always what it is
30354
- * keyed by.
30355
- *
30356
- * A topic whose id column is flagged `rowid`, `uuid` or
30357
- * `qualified` keys its records by something that is not the
30358
- * name — a counter, a random string, or the name with every
30359
- * ancestor in front of it — and the name a human knows the
30360
- * record by lives in the secondary key the topic declares
30361
- * (`pkey2s`, carried in the desc since SDK > 7.13.0). The
30362
- * `topics` and `cols` topics of treedb_system_schema are the
30363
- * case that forced this: named in `value`, so every card in
30364
- * the graph read "181", "225", "193" while they were keyed by
30365
- * rowid, and reads the whole path now that they are
30366
- * qualified.
30367
- *
30368
- * The pkey stays reachable: it is the card's tooltip.
30369
- *
30370
- * Falls back to the id whenever the schema does not say
30371
- * otherwise — an older node whose desc carries no `pkey2s`
30372
- * included.
30403
+ var SEP = " · ";
30404
+ /************************************************************
30405
+ * What a node is CALLED: **its id, and the secondary keys it
30406
+ * carries**.
30407
+ *
30408
+ * A record is one thing and an INSTANCE of it is another: the
30409
+ * topic keys the record by `id` and the instance by the
30410
+ * columns it declares in `pkey2s`. A card that shows one
30411
+ * without the other is a card that cannot be identified --
30412
+ * and it used to show one INSTEAD of the other, so the yunos
30413
+ * of an agent read `7.23.0-1`, `7.23.0-1`, `7.23.0-1` (the
30414
+ * release, which they share) and never said which yuno each
30415
+ * one was, while a `configurations` card read `1`.
30416
+ *
30417
+ * It came from the opposite case, and that one still works:
30418
+ * the `topics` and `cols` of treedb_system_schema are keyed
30419
+ * by a rowid, so the card said "181" and the name lived in
30420
+ * the pkey2. Now it says both -- `181 · yuno_role` -- which
30421
+ * is what the record is: that name, under that key.
30422
+ *
30423
+ * Rules:
30424
+ * - the id always opens the label;
30425
+ * - every pkey2 the record carries follows it, in the
30426
+ * order the topic declares them, separated by `·`;
30427
+ * - a pkey2 whose value is empty, absent or equal to the
30428
+ * id adds nothing;
30429
+ * - a desc with no `pkey2s` (an older node's schema
30430
+ * included) gives the bare id.
30373
30431
  ************************************************************/
30374
30432
  function node_label(desc, record) {
30375
30433
  let id = record.id;
30376
- let id_col = null;
30377
- if (Array.isArray(desc.cols)) {
30378
- for (let col of desc.cols) if (col && col.id === (desc.pkey || "id")) {
30379
- id_col = col;
30380
- break;
30381
- }
30382
- }
30383
- if (!id_col || !Array.isArray(id_col.flag)) return id;
30384
- if (id_col.flag.indexOf("rowid") < 0 && id_col.flag.indexOf("uuid") < 0 && id_col.flag.indexOf("qualified") < 0) return id;
30385
- let pkey2s = desc.pkey2s;
30434
+ let pkey2s = desc ? desc.pkey2s : null;
30386
30435
  if (typeof pkey2s === "string") pkey2s = pkey2s ? [pkey2s] : [];
30387
- if (!Array.isArray(pkey2s)) return id;
30436
+ if (!Array.isArray(pkey2s) || pkey2s.length === 0) return id;
30437
+ let parts = [];
30388
30438
  for (let name of pkey2s) {
30439
+ if (!name) continue;
30389
30440
  let value = record[name];
30390
- if (typeof value === "string" && value.length > 0) return value;
30441
+ if (typeof value === "number") value = String(value);
30442
+ if (typeof value !== "string" || value.length === 0) continue;
30443
+ if (value === String(id)) continue;
30444
+ parts.push(value);
30391
30445
  }
30392
- return id;
30446
+ if (parts.length === 0) return id;
30447
+ return [id, ...parts].join(SEP);
30393
30448
  }
30394
30449
  //#endregion
30395
30450
  //#region src/treedb_fold_model.js
@@ -58815,7 +58870,7 @@ function build_graph$1(gobj) {
58815
58870
  container: priv.$container,
58816
58871
  animation: false,
58817
58872
  autoResize: false,
58818
- zoomRange: [.2, 4],
58873
+ zoomRange: [.02, 4],
58819
58874
  node: {
58820
58875
  palette: {
58821
58876
  type: "group",
@@ -60071,6 +60126,7 @@ function draw_link(gobj, child_topic, child_id, source_col, fkey, verbose) {
60071
60126
  parent_topic,
60072
60127
  parent_id,
60073
60128
  hook_name,
60129
+ child_topic,
60074
60130
  child_id,
60075
60131
  is_tree
60076
60132
  });
@@ -60577,7 +60633,7 @@ async function graph_fit_selection(gobj, ids) {
60577
60633
  let bw = Math.max(1, maxx - minx);
60578
60634
  let bh = Math.max(1, maxy - miny);
60579
60635
  let zoom = Math.min(vw / bw, vh / bh);
60580
- let range = graph.getZoomRange() || [.2, 4];
60636
+ let range = graph.getZoomRange() || [.02, 4];
60581
60637
  zoom = Math.max(range[0], Math.min(range[1], zoom));
60582
60638
  await graph.zoomTo(zoom);
60583
60639
  await graph.focusElement(ids);
@@ -63314,16 +63370,14 @@ function refresh_html_nodes_theme(gobj, theme) {
63314
63370
  function refresh_default_edges_theme(gobj, theme) {
63315
63371
  let graph = gobj.priv.graph;
63316
63372
  if (!graph) return;
63317
- let dark = theme === "dark";
63318
63373
  let edges = graph.getData().edges || [];
63319
63374
  let updates = [];
63320
63375
  for (let i = 0; i < edges.length; i++) {
63321
63376
  let d = edges[i].data || {};
63322
63377
  if (!d.themed_default) continue;
63323
- let stroke = d.is_tree ? dark ? "#22a7c2" : "#0e7490" : dark ? "#8b94a3" : "#6b7280";
63324
63378
  updates.push({
63325
63379
  id: edges[i].id,
63326
- style: { stroke }
63380
+ style: { stroke: default_edge_stroke(gobj, d, theme) }
63327
63381
  });
63328
63382
  }
63329
63383
  if (updates.length > 0) graph.updateEdgeData(updates);
@@ -63895,21 +63949,54 @@ function apply_edge_properties(gobj, edge_id, lineWidth, stroke, scope) {
63895
63949
  deselect_edge(gobj);
63896
63950
  }
63897
63951
  /************************************************************
63952
+ * The colour a link is DRAWN in: the colour of the two knobs it
63953
+ * joins.
63954
+ *
63955
+ * Both ends of a link wear the CHILD topic's colour -- the
63956
+ * parent's hook port is painted with the colour of whatever may
63957
+ * hang there, and the child's fkey port with its own (see
63958
+ * build_ports) -- so the line between them has one obvious
63959
+ * colour, and a reader follows a link by its hue instead of
63960
+ * tracing a grey thread across the canvas.
63961
+ *
63962
+ * It goes through port_rim(): the FILL of a port says which
63963
+ * topic, and a line on the canvas is a graphical object that
63964
+ * needs 3:1 against it, which that mix clears in both themes
63965
+ * (9.11 dark, 3.80 light) while keeping the hue. So a link is
63966
+ * drawn in exactly the colour of the rim of the two knobs it
63967
+ * ends on.
63968
+ *
63969
+ * The containment/tree relation (parent and child of the same
63970
+ * topic) is no longer marked by a teal of its own -- it is that
63971
+ * topic's colour like any other link, and what still tells it
63972
+ * apart is its WIDTH.
63973
+ *
63974
+ * A topic with no colour, or an edge of a graph with no schema
63975
+ * yet, keeps the neutral grey this used to give everything.
63976
+ ************************************************************/
63977
+ function default_edge_stroke(gobj, d, theme) {
63978
+ let priv = gobj.priv;
63979
+ let desc = is_object(priv.descs) ? priv.descs[d.child_topic] : null;
63980
+ if (desc && desc.color) return port_rim(desc.color, theme);
63981
+ let dark = theme === "dark";
63982
+ return d.is_tree ? dark ? "#22a7c2" : "#0e7490" : dark ? "#8b94a3" : "#6b7280";
63983
+ }
63984
+ /************************************************************
63898
63985
  * The style an edge INHERITS, before anything saved for the
63899
63986
  * edge itself: its hook's default in the parent topic, else the
63900
63987
  * parent topic's default for every edge, else the library's.
63901
63988
  *
63902
- * The library's: neutral grey, teal for the containment/tree
63903
- * relation (parent and child of the same topic, a
63904
- * self-hierarchy). `themed_default` says the colour is still the
63905
- * library's, so a theme toggle re-themes it and leaves a chosen
63906
- * one alone.
63989
+ * The library's: the colour of the ports it joins (see
63990
+ * default_edge_stroke), and a wider line for the
63991
+ * containment/tree relation. `themed_default` says the colour is
63992
+ * still the library's, so a theme toggle re-themes it and leaves
63993
+ * a chosen one alone.
63907
63994
  ************************************************************/
63908
63995
  function edge_inherited_style_of(gobj, d) {
63909
- let dark = gobj.priv.theme === "dark";
63996
+ let priv = gobj.priv;
63910
63997
  let style = {
63911
63998
  lineWidth: d.is_tree ? 2 : 1.6,
63912
- stroke: d.is_tree ? dark ? "#22a7c2" : "#0e7490" : dark ? "#8b94a3" : "#6b7280",
63999
+ stroke: default_edge_stroke(gobj, d, priv.theme),
63913
64000
  themed_default: true
63914
64001
  };
63915
64002
  let defs = topic_defaults_of(gobj, d.parent_topic);
@@ -71730,7 +71817,7 @@ function render_toolbar(gobj) {
71730
71817
  ]);
71731
71818
  let $right = [];
71732
71819
  if (priv.treedb_id && has_model) {
71733
- $right.push(toolbar_button("SCHEMA_DIAGRAM_BTN", "yi-hexagon-nodes", priv.diagram ? "topics" : "diagram", "EV_TOGGLE_DIAGRAM", false));
71820
+ $right.push(toolbar_button("SCHEMA_DIAGRAM_BTN", "yi-compass-drafting", priv.diagram ? "topics" : "diagram", "EV_TOGGLE_DIAGRAM", false));
71734
71821
  $right.push(toolbar_button("SCHEMA_VALIDATE_BTN", "yi-square-check", "check", "EV_VALIDATE", false));
71735
71822
  $right.push(toolbar_button("SCHEMA_EXPORT_BTN", "yi-download", "export", "EV_EXPORT", false));
71736
71823
  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.186",
3
+ "version": "7.23.188",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -967,7 +967,12 @@ function build_graph(gobj)
967
967
  container: priv.$container,
968
968
  animation: false,
969
969
  autoResize: false,
970
- zoomRange: [0.2, 4],
970
+ /* The floor was 0.2, and a treedb of a few hundred records does
971
+ * not fit in a screen at 20%: the reader could see the shape of
972
+ * the whole thing in the minimap and never on the canvas. Fit
973
+ * still stops at a legible zoom on purpose (graph_fit_readable);
974
+ * zooming out by hand is the reader's business. */
975
+ zoomRange: [0.02, 4],
971
976
  node: { // WARNING this affect to all nodes with prevalence over individual defines!
972
977
  palette: {
973
978
  type: 'group',
@@ -2938,6 +2943,7 @@ function draw_link(
2938
2943
  parent_topic: parent_topic,
2939
2944
  parent_id: parent_id,
2940
2945
  hook_name: hook_name,
2946
+ child_topic: child_topic,
2941
2947
  child_id: child_id,
2942
2948
  is_tree: is_tree,
2943
2949
  });
@@ -3718,7 +3724,7 @@ async function graph_fit_selection(gobj, ids)
3718
3724
  let bh = Math.max(1, maxy - miny);
3719
3725
 
3720
3726
  let zoom = Math.min(vw / bw, vh / bh);
3721
- let range = graph.getZoomRange() || [0.2, 4];
3727
+ let range = graph.getZoomRange() || [0.02, 4];
3722
3728
  zoom = Math.max(range[0], Math.min(range[1], zoom));
3723
3729
 
3724
3730
  await graph.zoomTo(zoom);
@@ -7795,7 +7801,6 @@ function refresh_default_edges_theme(gobj, theme)
7795
7801
  if(!graph) {
7796
7802
  return;
7797
7803
  }
7798
- let dark = (theme === "dark");
7799
7804
  let edges = graph.getData().edges || [];
7800
7805
  let updates = [];
7801
7806
  for(let i = 0; i < edges.length; i++) {
@@ -7803,10 +7808,7 @@ function refresh_default_edges_theme(gobj, theme)
7803
7808
  if(!d.themed_default) {
7804
7809
  continue;
7805
7810
  }
7806
- let stroke = d.is_tree
7807
- ? (dark ? '#22a7c2' : '#0e7490')
7808
- : (dark ? '#8b94a3' : '#6b7280');
7809
- updates.push({ id: edges[i].id, style: { stroke: stroke } });
7811
+ updates.push({id: edges[i].id, style: {stroke: default_edge_stroke(gobj, d, theme)}});
7810
7812
  }
7811
7813
  if(updates.length > 0) {
7812
7814
  graph.updateEdgeData(updates);
@@ -8688,26 +8690,60 @@ function apply_edge_properties(gobj, edge_id, lineWidth, stroke, scope)
8688
8690
  deselect_edge(gobj);
8689
8691
  }
8690
8692
 
8693
+ /************************************************************
8694
+ * The colour a link is DRAWN in: the colour of the two knobs it
8695
+ * joins.
8696
+ *
8697
+ * Both ends of a link wear the CHILD topic's colour -- the
8698
+ * parent's hook port is painted with the colour of whatever may
8699
+ * hang there, and the child's fkey port with its own (see
8700
+ * build_ports) -- so the line between them has one obvious
8701
+ * colour, and a reader follows a link by its hue instead of
8702
+ * tracing a grey thread across the canvas.
8703
+ *
8704
+ * It goes through port_rim(): the FILL of a port says which
8705
+ * topic, and a line on the canvas is a graphical object that
8706
+ * needs 3:1 against it, which that mix clears in both themes
8707
+ * (9.11 dark, 3.80 light) while keeping the hue. So a link is
8708
+ * drawn in exactly the colour of the rim of the two knobs it
8709
+ * ends on.
8710
+ *
8711
+ * The containment/tree relation (parent and child of the same
8712
+ * topic) is no longer marked by a teal of its own -- it is that
8713
+ * topic's colour like any other link, and what still tells it
8714
+ * apart is its WIDTH.
8715
+ *
8716
+ * A topic with no colour, or an edge of a graph with no schema
8717
+ * yet, keeps the neutral grey this used to give everything.
8718
+ ************************************************************/
8719
+ function default_edge_stroke(gobj, d, theme)
8720
+ {
8721
+ let priv = gobj.priv;
8722
+ let desc = is_object(priv.descs)? priv.descs[d.child_topic] : null;
8723
+ if(desc && desc.color) {
8724
+ return port_rim(desc.color, theme);
8725
+ }
8726
+ let dark = (theme === "dark");
8727
+ return d.is_tree? (dark? '#22a7c2' : '#0e7490') : (dark? '#8b94a3' : '#6b7280');
8728
+ }
8729
+
8691
8730
  /************************************************************
8692
8731
  * The style an edge INHERITS, before anything saved for the
8693
8732
  * edge itself: its hook's default in the parent topic, else the
8694
8733
  * parent topic's default for every edge, else the library's.
8695
8734
  *
8696
- * The library's: neutral grey, teal for the containment/tree
8697
- * relation (parent and child of the same topic, a
8698
- * self-hierarchy). `themed_default` says the colour is still the
8699
- * library's, so a theme toggle re-themes it and leaves a chosen
8700
- * one alone.
8735
+ * The library's: the colour of the ports it joins (see
8736
+ * default_edge_stroke), and a wider line for the
8737
+ * containment/tree relation. `themed_default` says the colour is
8738
+ * still the library's, so a theme toggle re-themes it and leaves
8739
+ * a chosen one alone.
8701
8740
  ************************************************************/
8702
8741
  function edge_inherited_style_of(gobj, d)
8703
8742
  {
8704
8743
  let priv = gobj.priv;
8705
- let dark = (priv.theme === "dark");
8706
8744
  let style = {
8707
8745
  lineWidth: d.is_tree ? 2 : 1.6,
8708
- stroke: d.is_tree
8709
- ? (dark ? '#22a7c2' : '#0e7490')
8710
- : (dark ? '#8b94a3' : '#6b7280'),
8746
+ stroke: default_edge_stroke(gobj, d, priv.theme),
8711
8747
  themed_default: true,
8712
8748
  };
8713
8749
 
@@ -810,7 +810,9 @@ function render_toolbar(gobj)
810
810
  *----------------------------------------------*/
811
811
  let $right = [];
812
812
  if(priv.treedb_id && has_model) {
813
- $right.push(toolbar_button("SCHEMA_DIAGRAM_BTN", "yi-hexagon-nodes",
813
+ /* The compass, like every other door to a SCHEMA: the
814
+ * `hexagon-nodes` it wore is the graph of the DATA. */
815
+ $right.push(toolbar_button("SCHEMA_DIAGRAM_BTN", "yi-compass-drafting",
814
816
  priv.diagram ? "topics" : "diagram", "EV_TOGGLE_DIAGRAM", false));
815
817
  $right.push(toolbar_button("SCHEMA_VALIDATE_BTN", "yi-square-check",
816
818
  "check", "EV_VALIDATE", false));