@yuneta/gobj-ui 7.23.28 → 7.23.30
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.
- package/README.md +163 -2
- package/dist/gobj-ui.cjs.js +1278 -202
- package/dist/gobj-ui.es.js +1279 -203
- package/package.json +1 -1
- package/src/c_g6_nodes_tree.js +40 -10
- package/src/c_yui_gobj_tree_js.js +630 -245
- package/src/c_yui_json_graph.js +245 -4
- package/src/gclass_describe.js +234 -0
- package/src/gclass_describe.test.js +125 -0
- package/src/gobj_tree_model.js +436 -0
- package/src/gobj_tree_model.test.js +184 -0
- package/src/lib_graph.css +38 -2
- package/src/yui_gclass_view.js +277 -0
- package/src/yui_graph_camera.js +60 -0
package/dist/gobj-ui.es.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SDATA, SDATA_END, clean_name, createElement2, createOneHtml, create_template_record, data_type_t, debounce, duplicate_objects, empty_string, escapeHtml, event_flag_t, gclass_create, gclass_find_by_name, gclass_flag_t, getPositionRelativeToBody, 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_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, is_array, is_boolean, is_date, is_gobj, is_null, is_number, is_object, is_string, 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_get_ids, 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_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, duplicate_objects, empty_string, escapeHtml, event_flag_t, 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_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, 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_get_ids, 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_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";
|
|
@@ -3513,7 +3513,7 @@ function composed_title(gobj) {
|
|
|
3513
3513
|
* Below the mobile breakpoint the window is a full-screen
|
|
3514
3514
|
* sheet (no float / drag / resize).
|
|
3515
3515
|
************************************************************/
|
|
3516
|
-
function is_mobile$
|
|
3516
|
+
function is_mobile$4() {
|
|
3517
3517
|
return (window.innerWidth || document.documentElement.offsetWidth) <= MOBILE_MAX;
|
|
3518
3518
|
}
|
|
3519
3519
|
/************************************************************
|
|
@@ -3546,7 +3546,7 @@ function do_minimize(gobj) {
|
|
|
3546
3546
|
************************************************************/
|
|
3547
3547
|
function build_ui$15(gobj) {
|
|
3548
3548
|
ensure_window_style();
|
|
3549
|
-
let mobile = is_mobile$
|
|
3549
|
+
let mobile = is_mobile$4();
|
|
3550
3550
|
let header = gobj_read_attr(gobj, "header");
|
|
3551
3551
|
if (is_gobj(header)) header = gobj_read_attr(header, "$container");
|
|
3552
3552
|
if (!header) header = build_default_header(gobj);
|
|
@@ -3884,7 +3884,7 @@ function min$1(gobj) {
|
|
|
3884
3884
|
* handlers moving
|
|
3885
3885
|
************************************************************/
|
|
3886
3886
|
function mvStart(gobj, evt) {
|
|
3887
|
-
if (is_mobile$
|
|
3887
|
+
if (is_mobile$4()) return;
|
|
3888
3888
|
let $container = gobj_read_attr(gobj, "$container");
|
|
3889
3889
|
let window_rect = $container.getBoundingClientRect();
|
|
3890
3890
|
let x = evt.screenX;
|
|
@@ -4043,7 +4043,7 @@ function do_center(gobj, x, y, width, height) {
|
|
|
4043
4043
|
function handleResize(gobj) {
|
|
4044
4044
|
let $container = gobj_read_attr(gobj, "$container");
|
|
4045
4045
|
if (!$container) return;
|
|
4046
|
-
if (is_mobile$
|
|
4046
|
+
if (is_mobile$4()) {
|
|
4047
4047
|
$container.classList.add("is-mobile-sheet");
|
|
4048
4048
|
let vw = window.innerWidth || document.documentElement.offsetWidth;
|
|
4049
4049
|
let vh = window.innerHeight || document.documentElement.offsetHeight;
|
|
@@ -9706,6 +9706,42 @@ function yui_graph_update_anchor($container, state) {
|
|
|
9706
9706
|
* nothing to centre right now".
|
|
9707
9707
|
************************************************************/
|
|
9708
9708
|
var __centring__ = /* @__PURE__ */ new WeakMap();
|
|
9709
|
+
/************************************************************
|
|
9710
|
+
* Put `node_id` at a GIVEN point of the viewport.
|
|
9711
|
+
*
|
|
9712
|
+
* What "the graph does not jump when I fold something" is
|
|
9713
|
+
* made of: read where the card is, rebuild the layout, put
|
|
9714
|
+
* that card back where it was. Everything that did not move
|
|
9715
|
+
* in the layout stays put with it.
|
|
9716
|
+
************************************************************/
|
|
9717
|
+
async function yui_graph_place_at(graph, node_id, viewport_xy) {
|
|
9718
|
+
if (!graph || !node_id || !viewport_xy) return false;
|
|
9719
|
+
try {
|
|
9720
|
+
if (typeof graph.getElementPosition !== "function" || typeof graph.getCanvasCenter !== "function" || typeof graph.translateTo !== "function") return false;
|
|
9721
|
+
let z = graph.getZoom() || 1;
|
|
9722
|
+
let c = graph.getCanvasCenter();
|
|
9723
|
+
let n = graph.getElementPosition(node_id);
|
|
9724
|
+
await graph.translateTo([(c[0] - n[0]) * z + (viewport_xy[0] - c[0]), (c[1] - n[1]) * z + (viewport_xy[1] - c[1])]);
|
|
9725
|
+
return true;
|
|
9726
|
+
} catch (e) {
|
|
9727
|
+
return false;
|
|
9728
|
+
}
|
|
9729
|
+
}
|
|
9730
|
+
/************************************************************
|
|
9731
|
+
* Where `node_id` is right now, in viewport pixels.
|
|
9732
|
+
*
|
|
9733
|
+
* The pair of the call above: read it before a rebuild, hand
|
|
9734
|
+
* it back after, and the node has not moved on screen even
|
|
9735
|
+
* though the layout under it changed.
|
|
9736
|
+
************************************************************/
|
|
9737
|
+
function yui_graph_viewport_of(graph, node_id) {
|
|
9738
|
+
if (!graph || !node_id) return null;
|
|
9739
|
+
try {
|
|
9740
|
+
return graph.getViewportByCanvas(graph.getElementPosition(node_id));
|
|
9741
|
+
} catch (e) {
|
|
9742
|
+
return null;
|
|
9743
|
+
}
|
|
9744
|
+
}
|
|
9709
9745
|
async function yui_graph_center_on(graph, node_id) {
|
|
9710
9746
|
if (!graph || !node_id) return false;
|
|
9711
9747
|
let st = __centring__.get(graph);
|
|
@@ -10576,6 +10612,7 @@ var PRIVATE_DATA$11 = {
|
|
|
10576
10612
|
_resize_raf: 0,
|
|
10577
10613
|
search: "",
|
|
10578
10614
|
collapsed: null,
|
|
10615
|
+
reserved: null,
|
|
10579
10616
|
match_count: 0,
|
|
10580
10617
|
find_timer: null,
|
|
10581
10618
|
fold_listener: null,
|
|
@@ -10587,7 +10624,8 @@ var PRIVATE_DATA$11 = {
|
|
|
10587
10624
|
anchor_path: "",
|
|
10588
10625
|
anchor_state: "off",
|
|
10589
10626
|
last_zoom: 1,
|
|
10590
|
-
center_posted: false
|
|
10627
|
+
center_posted: false,
|
|
10628
|
+
pending_keep: null
|
|
10591
10629
|
};
|
|
10592
10630
|
var __gclass__$11 = null;
|
|
10593
10631
|
var __layout_registered__$1 = false;
|
|
@@ -10697,6 +10735,7 @@ function type_color(type, dark) {
|
|
|
10697
10735
|
function mt_create$11(gobj) {
|
|
10698
10736
|
let priv = gobj.priv;
|
|
10699
10737
|
priv.collapsed = /* @__PURE__ */ new Set();
|
|
10738
|
+
priv.reserved = /* @__PURE__ */ new Map();
|
|
10700
10739
|
priv.search = "";
|
|
10701
10740
|
priv.canvas_id = "json-canvas-" + clean_name(gobj_name(gobj));
|
|
10702
10741
|
gobj_write_str_attr(gobj, "canvas_id", priv.canvas_id);
|
|
@@ -10796,7 +10835,11 @@ function build_ui$10(gobj) {
|
|
|
10796
10835
|
evt.stopPropagation();
|
|
10797
10836
|
evt.preventDefault();
|
|
10798
10837
|
if (evt.type !== "click") return;
|
|
10799
|
-
|
|
10838
|
+
let $card = $handle.closest ? $handle.closest(".JSON_CARD") : null;
|
|
10839
|
+
gobj_send_event(gobj, "EV_TOGGLE_FOLD", {
|
|
10840
|
+
path: $handle.getAttribute("data-fold-path"),
|
|
10841
|
+
keep: $card ? $card.getAttribute("data-json-path") : ""
|
|
10842
|
+
}, gobj);
|
|
10800
10843
|
};
|
|
10801
10844
|
for (let type of FOLD_SWALLOWED_EVENTS) $mount.addEventListener(type, priv.fold_listener, true);
|
|
10802
10845
|
} else log_error(`${gobj_short_name(gobj)}: no canvas mount, fold handles are dead`);
|
|
@@ -11077,6 +11120,8 @@ var BEHAVIORS_READING$1 = [
|
|
|
11077
11120
|
var BEHAVIORS_PICKING$1 = ["drag-canvas", ZOOM_CANVAS$1];
|
|
11078
11121
|
var CARD_HEADER_H$1 = 24;
|
|
11079
11122
|
var CARD_ROW_H$1 = 22;
|
|
11123
|
+
var H_GAP = 60;
|
|
11124
|
+
var V_GAP = 50;
|
|
11080
11125
|
function fold_toggle_html(path, foldable, folded, branches, colors) {
|
|
11081
11126
|
if (!foldable) return FOLD_SPACER;
|
|
11082
11127
|
let label = folded ? "+" + branches : "−";
|
|
@@ -11198,7 +11243,10 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id, parent_port)
|
|
|
11198
11243
|
lines.push(build_cell_html(key, value, type, dark, matched, fold));
|
|
11199
11244
|
}
|
|
11200
11245
|
if (parent_id && is_pure_collection(kw)) {
|
|
11201
|
-
if (priv.collapsed.has(path))
|
|
11246
|
+
if (priv.collapsed.has(path)) {
|
|
11247
|
+
push_fold_phantom(gobj, path, parent_id, parent_port, nodes, edges);
|
|
11248
|
+
return parent_id;
|
|
11249
|
+
}
|
|
11202
11250
|
for (let pending of pending_complex) build_json_nodes(gobj, add_segment(path, pending.key), pending.value, nodes, edges, parent_id, parent_port);
|
|
11203
11251
|
return parent_id;
|
|
11204
11252
|
}
|
|
@@ -11284,7 +11332,10 @@ function build_json_nodes(gobj, path, kw, nodes, edges, parent_id, parent_port)
|
|
|
11284
11332
|
if (parent_port) edge.style.sourcePort = parent_port;
|
|
11285
11333
|
edges.push(edge);
|
|
11286
11334
|
}
|
|
11287
|
-
if (folded)
|
|
11335
|
+
if (folded) {
|
|
11336
|
+
push_fold_phantom(gobj, path, group_id, "", nodes, edges);
|
|
11337
|
+
return group_id;
|
|
11338
|
+
}
|
|
11288
11339
|
for (let pending of pending_complex) build_json_nodes(gobj, add_segment(path, pending.key), pending.value, nodes, edges, group_id, pending.port);
|
|
11289
11340
|
return group_id;
|
|
11290
11341
|
}
|
|
@@ -11315,8 +11366,16 @@ function load_json(gobj) {
|
|
|
11315
11366
|
});
|
|
11316
11367
|
graph.render().then(() => {
|
|
11317
11368
|
reanchor$1(gobj);
|
|
11318
|
-
paint_anchor_mark(gobj);
|
|
11369
|
+
paint_anchor_mark$1(gobj);
|
|
11319
11370
|
if (preserve_view) {
|
|
11371
|
+
let keep = priv.pending_keep;
|
|
11372
|
+
priv.pending_keep = null;
|
|
11373
|
+
if (keep) {
|
|
11374
|
+
for (let n of nodes) if (n.data && n.data.path === keep.path) {
|
|
11375
|
+
yui_graph_place_at(graph, n.id, [keep.x, keep.y]);
|
|
11376
|
+
break;
|
|
11377
|
+
}
|
|
11378
|
+
}
|
|
11320
11379
|
yui_graph_update_zoom(gobj_read_attr(gobj, "$container"), priv.graph);
|
|
11321
11380
|
return;
|
|
11322
11381
|
}
|
|
@@ -11368,6 +11427,108 @@ function update_find_result(gobj) {
|
|
|
11368
11427
|
priv.$find_result.classList.remove("is-hidden");
|
|
11369
11428
|
}
|
|
11370
11429
|
/************************************************************
|
|
11430
|
+
* The invisible child a fold leaves behind.
|
|
11431
|
+
*
|
|
11432
|
+
* It is the whole of "nothing moves when I fold": the layout
|
|
11433
|
+
* is a plain tree that centres a parent over its children, so
|
|
11434
|
+
* the only way to keep the survivors still is to leave the
|
|
11435
|
+
* space where it was, in the order it was, as a CHILD. A width
|
|
11436
|
+
* reserved as a number would still have to be placed, and
|
|
11437
|
+
* appending it moves every sibling by half of it.
|
|
11438
|
+
*
|
|
11439
|
+
* A `rect` and not an `html` node: nothing is drawn, and a
|
|
11440
|
+
* DOM element per fold would be a card nobody can see but the
|
|
11441
|
+
* Inspector would show.
|
|
11442
|
+
************************************************************/
|
|
11443
|
+
function push_fold_phantom(gobj, path, parent_id, parent_port, nodes, edges) {
|
|
11444
|
+
let width = gobj.priv.reserved.get(path) || 0;
|
|
11445
|
+
if (!(width > 0) || !parent_id) return;
|
|
11446
|
+
let id = gen_node_id$1(gobj);
|
|
11447
|
+
nodes.push({
|
|
11448
|
+
id,
|
|
11449
|
+
type: "rect",
|
|
11450
|
+
data: {
|
|
11451
|
+
path,
|
|
11452
|
+
phantom: true
|
|
11453
|
+
},
|
|
11454
|
+
style: {
|
|
11455
|
+
size: [width, 1],
|
|
11456
|
+
fill: "transparent",
|
|
11457
|
+
stroke: "transparent",
|
|
11458
|
+
opacity: 0,
|
|
11459
|
+
pointerEvents: "none"
|
|
11460
|
+
}
|
|
11461
|
+
});
|
|
11462
|
+
edges.push({
|
|
11463
|
+
source: parent_id,
|
|
11464
|
+
target: id,
|
|
11465
|
+
style: {
|
|
11466
|
+
opacity: 0,
|
|
11467
|
+
endArrow: false,
|
|
11468
|
+
sourcePort: parent_port || void 0
|
|
11469
|
+
}
|
|
11470
|
+
});
|
|
11471
|
+
}
|
|
11472
|
+
/************************************************************
|
|
11473
|
+
* The width the children a fold is about to hide are taking
|
|
11474
|
+
* RIGHT NOW, measured with the layout's own numbers.
|
|
11475
|
+
*
|
|
11476
|
+
* `card_path` is the card that will lose them; `under` limits
|
|
11477
|
+
* it to the ones hanging from one key of that card, which is
|
|
11478
|
+
* the case of a container that has no card of its own -- its
|
|
11479
|
+
* children hang from the parent, so its fold is a hole in the
|
|
11480
|
+
* PARENT.
|
|
11481
|
+
************************************************************/
|
|
11482
|
+
function group_width_of(gobj, card_path, under) {
|
|
11483
|
+
let priv = gobj.priv;
|
|
11484
|
+
if (!priv.graph) return 0;
|
|
11485
|
+
try {
|
|
11486
|
+
let by_id = {};
|
|
11487
|
+
let card_id = "";
|
|
11488
|
+
for (let node of priv.graph.getNodeData()) {
|
|
11489
|
+
if (!node || !node.data) continue;
|
|
11490
|
+
by_id[node.id] = node;
|
|
11491
|
+
if (node.data.path === card_path) card_id = node.id;
|
|
11492
|
+
}
|
|
11493
|
+
if (!card_id) return 0;
|
|
11494
|
+
let prefix = under ? under + "`" : "";
|
|
11495
|
+
let sum = 0;
|
|
11496
|
+
let n = 0;
|
|
11497
|
+
for (let edge of priv.graph.getEdgeData()) {
|
|
11498
|
+
if (!edge || edge.source !== card_id) continue;
|
|
11499
|
+
let kid = by_id[edge.target];
|
|
11500
|
+
if (!kid) continue;
|
|
11501
|
+
if (prefix && String(kid.data.path).indexOf(prefix) !== 0) continue;
|
|
11502
|
+
sum += kid.data.subtree_width || 0;
|
|
11503
|
+
n++;
|
|
11504
|
+
}
|
|
11505
|
+
return n ? sum + H_GAP * (n - 1) : 0;
|
|
11506
|
+
} catch (e) {
|
|
11507
|
+
return 0;
|
|
11508
|
+
}
|
|
11509
|
+
}
|
|
11510
|
+
/************************************************************
|
|
11511
|
+
* The screen spot of the card at `path`, to be given back
|
|
11512
|
+
* after the rebuild. Null when it has no card of its own --
|
|
11513
|
+
* a pure collection -- and then nothing anchors and the
|
|
11514
|
+
* camera simply stays where it was.
|
|
11515
|
+
************************************************************/
|
|
11516
|
+
function keep_of(gobj, path) {
|
|
11517
|
+
let priv = gobj.priv;
|
|
11518
|
+
if (empty_string(path) || !priv.graph) return null;
|
|
11519
|
+
try {
|
|
11520
|
+
for (let node of priv.graph.getNodeData()) if (node && node.data && node.data.path === path) {
|
|
11521
|
+
let vp = yui_graph_viewport_of(priv.graph, node.id);
|
|
11522
|
+
return vp ? {
|
|
11523
|
+
path,
|
|
11524
|
+
x: vp[0],
|
|
11525
|
+
y: vp[1]
|
|
11526
|
+
} : null;
|
|
11527
|
+
}
|
|
11528
|
+
} catch (e) {}
|
|
11529
|
+
return null;
|
|
11530
|
+
}
|
|
11531
|
+
/************************************************************
|
|
11371
11532
|
* Clear and reload.
|
|
11372
11533
|
* {preserve_view: true}
|
|
11373
11534
|
* keep the current zoom and translate (no fitView) —
|
|
@@ -11409,8 +11570,6 @@ var JsonTreeLayout = class extends BaseLayout {
|
|
|
11409
11570
|
h
|
|
11410
11571
|
};
|
|
11411
11572
|
}
|
|
11412
|
-
const H_GAP = 60;
|
|
11413
|
-
const V_GAP = 50;
|
|
11414
11573
|
let subtree_widths = {};
|
|
11415
11574
|
function calc_subtree_width(node_id) {
|
|
11416
11575
|
let kids = children_map[node_id] || [];
|
|
@@ -11427,6 +11586,7 @@ var JsonTreeLayout = class extends BaseLayout {
|
|
|
11427
11586
|
return subtree_widths[node_id];
|
|
11428
11587
|
}
|
|
11429
11588
|
for (let root of roots) calc_subtree_width(root.id);
|
|
11589
|
+
for (let node of nodes) if (node.data) node.data.subtree_width = subtree_widths[node.id];
|
|
11430
11590
|
let positions = {};
|
|
11431
11591
|
function position_node(node_id, x, y) {
|
|
11432
11592
|
positions[node_id] = {
|
|
@@ -11474,9 +11634,16 @@ var JsonTreeLayout = class extends BaseLayout {
|
|
|
11474
11634
|
/************************************************************
|
|
11475
11635
|
*
|
|
11476
11636
|
************************************************************/
|
|
11637
|
+
function priv_reset_folds(gobj) {
|
|
11638
|
+
gobj.priv.collapsed.clear();
|
|
11639
|
+
gobj.priv.reserved.clear();
|
|
11640
|
+
}
|
|
11477
11641
|
function ac_load_data$1(gobj, event, kw, src) {
|
|
11478
11642
|
if (kw.path !== void 0) gobj_write_str_attr(gobj, "path", kw.path);
|
|
11479
|
-
if (kw.data !== void 0)
|
|
11643
|
+
if (kw.data !== void 0) {
|
|
11644
|
+
gobj_write_attr(gobj, "json_data", kw.data);
|
|
11645
|
+
priv_reset_folds(gobj);
|
|
11646
|
+
}
|
|
11480
11647
|
refresh_json(gobj);
|
|
11481
11648
|
return 0;
|
|
11482
11649
|
}
|
|
@@ -11531,8 +11698,16 @@ function ac_toggle_fold(gobj, event, kw, src) {
|
|
|
11531
11698
|
log_error(`${gobj_short_name(gobj)}: EV_TOGGLE_FOLD with no path`);
|
|
11532
11699
|
return -1;
|
|
11533
11700
|
}
|
|
11534
|
-
|
|
11535
|
-
|
|
11701
|
+
let card = kw && kw.keep || path;
|
|
11702
|
+
if (priv.collapsed.has(path)) {
|
|
11703
|
+
priv.collapsed.delete(path);
|
|
11704
|
+
priv.reserved.delete(path);
|
|
11705
|
+
} else {
|
|
11706
|
+
let w = group_width_of(gobj, card, card === path ? "" : path);
|
|
11707
|
+
priv.collapsed.add(path);
|
|
11708
|
+
if (w > 0) priv.reserved.set(path, w);
|
|
11709
|
+
}
|
|
11710
|
+
priv.pending_keep = keep_of(gobj, kw && kw.keep || path);
|
|
11536
11711
|
refresh_json(gobj, { preserve_view: true });
|
|
11537
11712
|
update_find_result(gobj);
|
|
11538
11713
|
return 0;
|
|
@@ -11559,6 +11734,7 @@ function ac_expand_all$2(gobj, event, kw, src) {
|
|
|
11559
11734
|
let priv = gobj.priv;
|
|
11560
11735
|
if (priv.collapsed.size === 0) return 0;
|
|
11561
11736
|
priv.collapsed.clear();
|
|
11737
|
+
priv.reserved.clear();
|
|
11562
11738
|
refresh_json(gobj);
|
|
11563
11739
|
update_find_result(gobj);
|
|
11564
11740
|
return 0;
|
|
@@ -11575,6 +11751,7 @@ function ac_collapse_all$2(gobj, event, kw, src) {
|
|
|
11575
11751
|
if (empty_string(path)) path = "`";
|
|
11576
11752
|
collect_foldable_paths(json_data, path, paths, true);
|
|
11577
11753
|
priv.collapsed = new Set(paths);
|
|
11754
|
+
priv.reserved.clear();
|
|
11578
11755
|
refresh_json(gobj);
|
|
11579
11756
|
update_find_result(gobj);
|
|
11580
11757
|
return 0;
|
|
@@ -11664,7 +11841,7 @@ function set_picking_mode$1(gobj, picking) {
|
|
|
11664
11841
|
* each time and the mark would otherwise survive only until the
|
|
11665
11842
|
* next fold.
|
|
11666
11843
|
************************************************************/
|
|
11667
|
-
function paint_anchor_mark(gobj) {
|
|
11844
|
+
function paint_anchor_mark$1(gobj) {
|
|
11668
11845
|
let priv = gobj.priv;
|
|
11669
11846
|
let $container = gobj_read_attr(gobj, "$container");
|
|
11670
11847
|
if (!$container) return;
|
|
@@ -11679,7 +11856,7 @@ function paint_anchor_mark(gobj) {
|
|
|
11679
11856
|
function ac_center_anchor$2(gobj, event, kw, src) {
|
|
11680
11857
|
let priv = gobj.priv;
|
|
11681
11858
|
priv.center_posted = false;
|
|
11682
|
-
paint_anchor_mark(gobj);
|
|
11859
|
+
paint_anchor_mark$1(gobj);
|
|
11683
11860
|
if (priv.anchor_state === "on" && priv.anchor_id) yui_graph_center_on(priv.graph, priv.anchor_id);
|
|
11684
11861
|
return 0;
|
|
11685
11862
|
}
|
|
@@ -11700,7 +11877,7 @@ function ac_toggle_anchor$2(gobj, event, kw, src) {
|
|
|
11700
11877
|
priv.anchor_path = "";
|
|
11701
11878
|
}
|
|
11702
11879
|
yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
|
|
11703
|
-
paint_anchor_mark(gobj);
|
|
11880
|
+
paint_anchor_mark$1(gobj);
|
|
11704
11881
|
set_picking_mode$1(gobj, priv.anchor_state === "arming");
|
|
11705
11882
|
return 0;
|
|
11706
11883
|
}
|
|
@@ -13023,7 +13200,7 @@ function ac_json_item_clicked(gobj, event, kw, src) {
|
|
|
13023
13200
|
/************************************************************
|
|
13024
13201
|
* EV_LANGUAGE_CHANGED — re-translate chrome + re-render
|
|
13025
13202
|
************************************************************/
|
|
13026
|
-
function ac_language_changed$
|
|
13203
|
+
function ac_language_changed$4(gobj, event, kw, src) {
|
|
13027
13204
|
let priv = gobj.priv;
|
|
13028
13205
|
let $container = gobj_read_attr(gobj, "$container");
|
|
13029
13206
|
if ($container) {
|
|
@@ -13084,7 +13261,7 @@ function create_gclass$10(gclass_name) {
|
|
|
13084
13261
|
],
|
|
13085
13262
|
[
|
|
13086
13263
|
"EV_LANGUAGE_CHANGED",
|
|
13087
|
-
ac_language_changed$
|
|
13264
|
+
ac_language_changed$4,
|
|
13088
13265
|
null
|
|
13089
13266
|
],
|
|
13090
13267
|
[
|
|
@@ -13160,7 +13337,7 @@ function create_gclass$10(gclass_name) {
|
|
|
13160
13337
|
],
|
|
13161
13338
|
[
|
|
13162
13339
|
"EV_LANGUAGE_CHANGED",
|
|
13163
|
-
ac_language_changed$
|
|
13340
|
+
ac_language_changed$4,
|
|
13164
13341
|
null
|
|
13165
13342
|
],
|
|
13166
13343
|
[
|
|
@@ -13210,6 +13387,567 @@ function register_c_yui_json() {
|
|
|
13210
13387
|
return create_gclass$10(GCLASS_NAME$10);
|
|
13211
13388
|
}
|
|
13212
13389
|
//#endregion
|
|
13390
|
+
//#region src/gobj_tree_model.js
|
|
13391
|
+
/***********************************************************************
|
|
13392
|
+
* gobj_tree_model.js
|
|
13393
|
+
*
|
|
13394
|
+
* The gobj tree of a yuno, as plain data.
|
|
13395
|
+
*
|
|
13396
|
+
* The view (C_YUI_GOBJ_TREE_JS) draws NODE DESCRIPTORS, never
|
|
13397
|
+
* gobjs. There are two producers of a descriptor, because the
|
|
13398
|
+
* view has to show the gobjs of the BACKEND too:
|
|
13399
|
+
*
|
|
13400
|
+
* - describe_js_gobj() the gobj tree of THIS browser yuno
|
|
13401
|
+
* - describe_backend_tree() the answer of the backend command
|
|
13402
|
+
* `view-gobj-tree` of another yuno
|
|
13403
|
+
*
|
|
13404
|
+
* Both answer the SAME shape, and that shape is the backend's:
|
|
13405
|
+
* the field names are the ones `gobj2json()` writes in
|
|
13406
|
+
* `kernel/c/gobj-c/src/gobj.c` (fullname, shortname, gclass,
|
|
13407
|
+
* name, parent, state, running, playing, service, disabled,
|
|
13408
|
+
* volatil, bottom_gobj, gobj_flags, commands, gobj_trace_level).
|
|
13409
|
+
* Anything the browser runtime cannot answer is simply absent,
|
|
13410
|
+
* and every reader here treats absent as "not known", never as
|
|
13411
|
+
* false.
|
|
13412
|
+
*
|
|
13413
|
+
* Copyright (c) 2026, ArtGins.
|
|
13414
|
+
* All Rights Reserved.
|
|
13415
|
+
***********************************************************************/
|
|
13416
|
+
/************************************************************
|
|
13417
|
+
* The role of a node: "yuno" | "service" | "volatil" | "pure" | "child"
|
|
13418
|
+
************************************************************/
|
|
13419
|
+
function node_role(d) {
|
|
13420
|
+
if (!d) return "child";
|
|
13421
|
+
if (d.is_yuno) return "yuno";
|
|
13422
|
+
if (d.service) return "service";
|
|
13423
|
+
if (d.volatil) return "volatil";
|
|
13424
|
+
if (d.pure_child) return "pure";
|
|
13425
|
+
return "child";
|
|
13426
|
+
}
|
|
13427
|
+
/************************************************************
|
|
13428
|
+
* What the node is doing: "stopped" | "running" | "playing"
|
|
13429
|
+
************************************************************/
|
|
13430
|
+
function node_status(d) {
|
|
13431
|
+
if (!d || !d.running) return "stopped";
|
|
13432
|
+
return d.playing ? "playing" : "running";
|
|
13433
|
+
}
|
|
13434
|
+
/************************************************************
|
|
13435
|
+
* The i18n key of the status, which is NOT the status itself:
|
|
13436
|
+
* a gobj that runs and does not play is PAUSED to the reader,
|
|
13437
|
+
* and "running" alone never said which of the two it was.
|
|
13438
|
+
************************************************************/
|
|
13439
|
+
function node_status_key(d) {
|
|
13440
|
+
switch (node_status(d)) {
|
|
13441
|
+
case "playing": return "playing";
|
|
13442
|
+
case "running": return "running (paused)";
|
|
13443
|
+
default: return "stopped";
|
|
13444
|
+
}
|
|
13445
|
+
}
|
|
13446
|
+
/******************************
|
|
13447
|
+
* Producers
|
|
13448
|
+
******************************/
|
|
13449
|
+
/************************************************************
|
|
13450
|
+
* A descriptor of a LIVE gobj of this browser yuno, children
|
|
13451
|
+
* included (the whole subtree, depth-first).
|
|
13452
|
+
*
|
|
13453
|
+
* `is_root` marks the yuno itself: the JS runtime has no
|
|
13454
|
+
* gobj_flag_yuno test of its own.
|
|
13455
|
+
************************************************************/
|
|
13456
|
+
function describe_js_gobj(target_gobj, is_root) {
|
|
13457
|
+
if (!target_gobj) return null;
|
|
13458
|
+
let parent_gobj = gobj_parent(target_gobj);
|
|
13459
|
+
let bottom = gobj_bottom_gobj(target_gobj);
|
|
13460
|
+
let d = {
|
|
13461
|
+
gclass: gobj_gclass_name(target_gobj) || "",
|
|
13462
|
+
name: gobj_name(target_gobj) || "",
|
|
13463
|
+
shortname: gobj_short_name(target_gobj) || "",
|
|
13464
|
+
fullname: gobj_full_name(target_gobj) || "",
|
|
13465
|
+
parent: parent_gobj ? gobj_short_name(parent_gobj) || "" : "",
|
|
13466
|
+
state: gobj_current_state(target_gobj) || "",
|
|
13467
|
+
running: !!gobj_is_running(target_gobj),
|
|
13468
|
+
playing: !!gobj_is_playing(target_gobj),
|
|
13469
|
+
service: !!gobj_is_service(target_gobj),
|
|
13470
|
+
volatil: !!gobj_is_volatil(target_gobj),
|
|
13471
|
+
pure_child: !!gobj_is_pure_child(target_gobj),
|
|
13472
|
+
disabled: !!target_gobj.disabled,
|
|
13473
|
+
bottom_gobj: bottom ? gobj_short_name(bottom) || "" : "",
|
|
13474
|
+
commands: !!(target_gobj.gclass && target_gobj.gclass.command_table),
|
|
13475
|
+
is_yuno: !!is_root,
|
|
13476
|
+
children: []
|
|
13477
|
+
};
|
|
13478
|
+
let children = target_gobj.dl_children || [];
|
|
13479
|
+
for (let child of children) {
|
|
13480
|
+
let cd = describe_js_gobj(child, false);
|
|
13481
|
+
if (cd) d.children.push(cd);
|
|
13482
|
+
}
|
|
13483
|
+
return d;
|
|
13484
|
+
}
|
|
13485
|
+
/******************************
|
|
13486
|
+
* Readouts
|
|
13487
|
+
******************************/
|
|
13488
|
+
/************************************************************
|
|
13489
|
+
* The i18n key of a role. Two of the five roles are not the
|
|
13490
|
+
* word the reader wants ("pure" and "volatil" alone say
|
|
13491
|
+
* nothing), so the map is explicit.
|
|
13492
|
+
************************************************************/
|
|
13493
|
+
function role_i18n_key(role) {
|
|
13494
|
+
switch (role) {
|
|
13495
|
+
case "yuno": return "yuno";
|
|
13496
|
+
case "service": return "service";
|
|
13497
|
+
case "volatil": return "volatil child";
|
|
13498
|
+
case "pure": return "pure child";
|
|
13499
|
+
default: return "child";
|
|
13500
|
+
}
|
|
13501
|
+
}
|
|
13502
|
+
/************************************************************
|
|
13503
|
+
* The marks a node carries besides its role: what the reader
|
|
13504
|
+
* would otherwise have to open the popover to learn.
|
|
13505
|
+
*
|
|
13506
|
+
* Returns i18n KEYS, in a fixed order, so the card and the
|
|
13507
|
+
* popover cannot disagree about what a node is.
|
|
13508
|
+
************************************************************/
|
|
13509
|
+
function node_badge_keys(d) {
|
|
13510
|
+
let badges = [];
|
|
13511
|
+
if (!d) return badges;
|
|
13512
|
+
let role = node_role(d);
|
|
13513
|
+
if (role !== "child") badges.push(role_i18n_key(role));
|
|
13514
|
+
if (d.disabled) badges.push("disabled");
|
|
13515
|
+
if (d.bottom_gobj) badges.push("bottom");
|
|
13516
|
+
if (d.commands) badges.push("commands");
|
|
13517
|
+
return badges;
|
|
13518
|
+
}
|
|
13519
|
+
/************************************************************
|
|
13520
|
+
* The vocabulary of this view, translated.
|
|
13521
|
+
*
|
|
13522
|
+
* Written as literal t("...") calls and not as t(key): a key
|
|
13523
|
+
* reached through a variable is invisible to the locale
|
|
13524
|
+
* validator, which is how a fixed vocabulary ships in one
|
|
13525
|
+
* language and never changes it.
|
|
13526
|
+
************************************************************/
|
|
13527
|
+
function node_labels(t) {
|
|
13528
|
+
if (typeof t !== "function") t = (k) => k;
|
|
13529
|
+
return {
|
|
13530
|
+
role: {
|
|
13531
|
+
"yuno": t("yuno"),
|
|
13532
|
+
"service": t("service"),
|
|
13533
|
+
"volatil child": t("volatil child"),
|
|
13534
|
+
"pure child": t("pure child"),
|
|
13535
|
+
"child": t("child")
|
|
13536
|
+
},
|
|
13537
|
+
status: {
|
|
13538
|
+
"playing": t("playing"),
|
|
13539
|
+
"running (paused)": t("running (paused)"),
|
|
13540
|
+
"stopped": t("stopped")
|
|
13541
|
+
},
|
|
13542
|
+
status_short: {
|
|
13543
|
+
"playing": t("playing"),
|
|
13544
|
+
"running": t("running"),
|
|
13545
|
+
"stopped": t("stopped")
|
|
13546
|
+
},
|
|
13547
|
+
badge: {
|
|
13548
|
+
"yuno": t("yuno"),
|
|
13549
|
+
"service": t("service"),
|
|
13550
|
+
"volatil child": t("volatil child"),
|
|
13551
|
+
"pure child": t("pure child"),
|
|
13552
|
+
"disabled": t("disabled"),
|
|
13553
|
+
"bottom": t("bottom"),
|
|
13554
|
+
"commands": t("commands")
|
|
13555
|
+
},
|
|
13556
|
+
field: {
|
|
13557
|
+
gclass: t("gclass"),
|
|
13558
|
+
name: t("name"),
|
|
13559
|
+
full_name: t("full name"),
|
|
13560
|
+
role: t("role"),
|
|
13561
|
+
status: t("status"),
|
|
13562
|
+
state: t("state"),
|
|
13563
|
+
disabled: t("disabled"),
|
|
13564
|
+
parent: t("parent"),
|
|
13565
|
+
bottom_gobj: t("bottom gobj"),
|
|
13566
|
+
commands: t("commands"),
|
|
13567
|
+
traces: t("traces"),
|
|
13568
|
+
flags: t("flags"),
|
|
13569
|
+
children: t("children"),
|
|
13570
|
+
yes: t("yes"),
|
|
13571
|
+
collapsed: t("(collapsed)")
|
|
13572
|
+
}
|
|
13573
|
+
};
|
|
13574
|
+
}
|
|
13575
|
+
/************************************************************
|
|
13576
|
+
* The rows of the popover: [label, value], both translated.
|
|
13577
|
+
*
|
|
13578
|
+
* `t` is the app's translator -- the library has none of its
|
|
13579
|
+
* own. Values that are a fixed vocabulary (the role, the
|
|
13580
|
+
* status, yes) go through it too; free text does not.
|
|
13581
|
+
************************************************************/
|
|
13582
|
+
function node_info_rows(d, t) {
|
|
13583
|
+
let rows = [];
|
|
13584
|
+
if (!d) return rows;
|
|
13585
|
+
let L = node_labels(t);
|
|
13586
|
+
let f = L.field;
|
|
13587
|
+
rows.push([f.gclass, d.gclass || "—"]);
|
|
13588
|
+
rows.push([f.name, d.name || "—"]);
|
|
13589
|
+
if (d.fullname && d.fullname !== d.shortname) rows.push([f.full_name, d.fullname]);
|
|
13590
|
+
rows.push([f.role, L.role[role_i18n_key(node_role(d))]]);
|
|
13591
|
+
rows.push([f.status, L.status[node_status_key(d)]]);
|
|
13592
|
+
rows.push([f.state, d.state || "—"]);
|
|
13593
|
+
if (d.disabled) rows.push([f.disabled, f.yes]);
|
|
13594
|
+
if (d.parent) rows.push([f.parent, d.parent]);
|
|
13595
|
+
if (d.bottom_gobj) rows.push([f.bottom_gobj, d.bottom_gobj]);
|
|
13596
|
+
if (d.commands) rows.push([f.commands, f.yes]);
|
|
13597
|
+
if (Array.isArray(d.gobj_trace_level) && d.gobj_trace_level.length > 0) rows.push([f.traces, d.gobj_trace_level.join(", ")]);
|
|
13598
|
+
if (Array.isArray(d.gobj_flags) && d.gobj_flags.length > 0) rows.push([f.flags, d.gobj_flags.join(", ")]);
|
|
13599
|
+
let kids = Array.isArray(d.children) ? d.children.length : 0;
|
|
13600
|
+
if (kids > 0) rows.push([f.children, d.is_collapsed ? kids + " " + f.collapsed : String(kids)]);
|
|
13601
|
+
return rows;
|
|
13602
|
+
}
|
|
13603
|
+
//#endregion
|
|
13604
|
+
//#region src/gclass_describe.js
|
|
13605
|
+
/***********************************************************************
|
|
13606
|
+
* gclass_describe.js
|
|
13607
|
+
*
|
|
13608
|
+
* A gclass, as plain JSON, in the shape the BACKEND answers.
|
|
13609
|
+
*
|
|
13610
|
+
* There is no gclass viewer in the framework: the C kernel
|
|
13611
|
+
* answers `view-gclass` with `gclass2json()`
|
|
13612
|
+
* (`kernel/c/gobj-c/src/gobj.c`) and nothing draws it. This
|
|
13613
|
+
* module builds the same document for a gclass of the BROWSER
|
|
13614
|
+
* yuno, out of the gobj-js registry, so one viewer can show a
|
|
13615
|
+
* gclass of either side:
|
|
13616
|
+
*
|
|
13617
|
+
* {id, gcflag, attrs, commands, gclass_methods,
|
|
13618
|
+
* internal_methods, FSM: {events, states}}
|
|
13619
|
+
*
|
|
13620
|
+
* Two keys the backend does not carry are added at the end,
|
|
13621
|
+
* because the browser can answer them for free and the reader
|
|
13622
|
+
* asks them first: `instances` and `trace_levels`.
|
|
13623
|
+
*
|
|
13624
|
+
* Copyright (c) 2026, ArtGins.
|
|
13625
|
+
* All Rights Reserved.
|
|
13626
|
+
***********************************************************************/
|
|
13627
|
+
/***************************************************************
|
|
13628
|
+
* Bit tables
|
|
13629
|
+
***************************************************************/
|
|
13630
|
+
var DATA_TYPE_NAMES = {
|
|
13631
|
+
[data_type_t.DTP_STRING]: "DTP_STRING",
|
|
13632
|
+
[data_type_t.DTP_BOOLEAN]: "DTP_BOOLEAN",
|
|
13633
|
+
[data_type_t.DTP_INTEGER]: "DTP_INTEGER",
|
|
13634
|
+
[data_type_t.DTP_REAL]: "DTP_REAL",
|
|
13635
|
+
[data_type_t.DTP_LIST]: "DTP_LIST",
|
|
13636
|
+
[data_type_t.DTP_DICT]: "DTP_DICT",
|
|
13637
|
+
[data_type_t.DTP_JSON]: "DTP_JSON",
|
|
13638
|
+
[data_type_t.DTP_POINTER]: "DTP_POINTER"
|
|
13639
|
+
};
|
|
13640
|
+
/************************************************************
|
|
13641
|
+
* The names of the bits set in `bits`, in table order.
|
|
13642
|
+
************************************************************/
|
|
13643
|
+
function bits2names(table, bits) {
|
|
13644
|
+
let names = [];
|
|
13645
|
+
let value = Number(bits) || 0;
|
|
13646
|
+
for (let name of Object.keys(table)) if ((value & table[name]) !== 0) names.push(name);
|
|
13647
|
+
return names;
|
|
13648
|
+
}
|
|
13649
|
+
/************************************************************
|
|
13650
|
+
* The name of a data type. An unknown type is reported as
|
|
13651
|
+
* the number it is -- silently drawing "DTP_STRING" for a
|
|
13652
|
+
* type nobody registered would be worse than saying "17".
|
|
13653
|
+
************************************************************/
|
|
13654
|
+
function data_type_name(type) {
|
|
13655
|
+
return DATA_TYPE_NAMES[type] || String(type);
|
|
13656
|
+
}
|
|
13657
|
+
/************************************************************
|
|
13658
|
+
* One SDATA descriptor -> one row.
|
|
13659
|
+
*
|
|
13660
|
+
* A DTP_POINTER default is a live object (a DOM node, a
|
|
13661
|
+
* gobj): it is never printed, only named, because a viewer
|
|
13662
|
+
* that serializes it would either throw or dump the page.
|
|
13663
|
+
************************************************************/
|
|
13664
|
+
function sdatadesc2json(it) {
|
|
13665
|
+
let row = {
|
|
13666
|
+
id: it.name,
|
|
13667
|
+
type: data_type_name(it.type),
|
|
13668
|
+
flag: bits2names(sdata_flag_t, it.flag),
|
|
13669
|
+
description: it.description || ""
|
|
13670
|
+
};
|
|
13671
|
+
if (it.type === data_type_t.DTP_POINTER) row.default_value = it.default_value === null || it.default_value === void 0 ? null : "<pointer>";
|
|
13672
|
+
else row.default_value = it.default_value === void 0 ? null : it.default_value;
|
|
13673
|
+
return row;
|
|
13674
|
+
}
|
|
13675
|
+
/************************************************************
|
|
13676
|
+
* The attrs table of a gclass.
|
|
13677
|
+
************************************************************/
|
|
13678
|
+
function attrs2json(attrs_table) {
|
|
13679
|
+
let rows = [];
|
|
13680
|
+
if (!Array.isArray(attrs_table)) return rows;
|
|
13681
|
+
for (let it of attrs_table) {
|
|
13682
|
+
if (!it || !it.name) continue;
|
|
13683
|
+
rows.push(sdatadesc2json(it));
|
|
13684
|
+
}
|
|
13685
|
+
return rows;
|
|
13686
|
+
}
|
|
13687
|
+
/************************************************************
|
|
13688
|
+
* The command table of a gclass, with the parameters of
|
|
13689
|
+
* each command (the `schema` slot of SDATACM).
|
|
13690
|
+
************************************************************/
|
|
13691
|
+
function commands2json(command_table) {
|
|
13692
|
+
let rows = [];
|
|
13693
|
+
if (!Array.isArray(command_table)) return rows;
|
|
13694
|
+
for (let it of command_table) {
|
|
13695
|
+
if (!it || !it.name) continue;
|
|
13696
|
+
let row = {
|
|
13697
|
+
id: it.name,
|
|
13698
|
+
alias: it.alias || [],
|
|
13699
|
+
description: it.description || ""
|
|
13700
|
+
};
|
|
13701
|
+
if (Array.isArray(it.schema)) row.parameters = attrs2json(it.schema);
|
|
13702
|
+
rows.push(row);
|
|
13703
|
+
}
|
|
13704
|
+
return rows;
|
|
13705
|
+
}
|
|
13706
|
+
/************************************************************
|
|
13707
|
+
* The methods the gclass actually implements. A table of
|
|
13708
|
+
* every possible one, mostly empty, tells the reader nothing.
|
|
13709
|
+
************************************************************/
|
|
13710
|
+
function methods2json(mt) {
|
|
13711
|
+
let names = [];
|
|
13712
|
+
if (!mt || typeof mt !== "object") return names;
|
|
13713
|
+
for (let key of Object.keys(mt)) if (typeof mt[key] === "function") names.push(key);
|
|
13714
|
+
return names;
|
|
13715
|
+
}
|
|
13716
|
+
/************************************************************
|
|
13717
|
+
* The FSM: the events the gclass declares, and what each
|
|
13718
|
+
* state does with them.
|
|
13719
|
+
************************************************************/
|
|
13720
|
+
function fsm2json(gclass) {
|
|
13721
|
+
let events = [];
|
|
13722
|
+
for (let ev of gclass.dl_events || []) events.push({
|
|
13723
|
+
event_name: ev.event_name,
|
|
13724
|
+
event_flag: bits2names(event_flag_t, ev.event_flag)
|
|
13725
|
+
});
|
|
13726
|
+
let states = {};
|
|
13727
|
+
for (let st of gclass.dl_states || []) {
|
|
13728
|
+
let actions = [];
|
|
13729
|
+
for (let ac of st.dl_actions || []) actions.push({
|
|
13730
|
+
event_name: ac.event_name,
|
|
13731
|
+
action: ac.action ? ac.action.name || "action" : "",
|
|
13732
|
+
next_state: ac.next_state || ""
|
|
13733
|
+
});
|
|
13734
|
+
states[st.state_name] = actions;
|
|
13735
|
+
}
|
|
13736
|
+
return {
|
|
13737
|
+
events,
|
|
13738
|
+
states
|
|
13739
|
+
};
|
|
13740
|
+
}
|
|
13741
|
+
/************************************************************
|
|
13742
|
+
* The description of a gclass REGISTERED IN THIS BROWSER YUNO,
|
|
13743
|
+
* or null when the registry does not know that name.
|
|
13744
|
+
*
|
|
13745
|
+
* Null is the answer for a gclass of a BACKEND yuno too: the
|
|
13746
|
+
* browser registry carries the GUI gclasses and nothing else,
|
|
13747
|
+
* so the caller asks the backend `view-gclass` for those and
|
|
13748
|
+
* shows whatever it answers -- this same shape.
|
|
13749
|
+
************************************************************/
|
|
13750
|
+
function describe_local_gclass(gclass_name) {
|
|
13751
|
+
if (!gclass_name) return null;
|
|
13752
|
+
let gclass = gclass_find_by_name(gclass_name, false);
|
|
13753
|
+
if (!gclass) return null;
|
|
13754
|
+
return {
|
|
13755
|
+
id: gclass.gclass_name,
|
|
13756
|
+
gcflag: bits2names(gclass_flag_t, gclass.gclass_flag),
|
|
13757
|
+
attrs: attrs2json(gclass.attrs_table),
|
|
13758
|
+
commands: commands2json(gclass.command_table),
|
|
13759
|
+
gclass_methods: methods2json(gclass.gmt),
|
|
13760
|
+
internal_methods: methods2json(gclass.lmt),
|
|
13761
|
+
FSM: fsm2json(gclass),
|
|
13762
|
+
instances: Number(gclass.instances) || 0,
|
|
13763
|
+
trace_levels: Array.isArray(gclass.s_user_trace_level) ? gclass.s_user_trace_level : []
|
|
13764
|
+
};
|
|
13765
|
+
}
|
|
13766
|
+
//#endregion
|
|
13767
|
+
//#region src/yui_gclass_view.js
|
|
13768
|
+
/***********************************************************************
|
|
13769
|
+
* yui_gclass_view.js
|
|
13770
|
+
*
|
|
13771
|
+
* The gclass viewer: what a GClass IS, in a window.
|
|
13772
|
+
*
|
|
13773
|
+
* The framework had no such viewer. The C kernel answers
|
|
13774
|
+
* `view-gclass` with a full description (attrs, commands,
|
|
13775
|
+
* methods, FSM) and the only reader of it was a terminal.
|
|
13776
|
+
* This one draws that same document with C_YUI_JSON, so the
|
|
13777
|
+
* FSM of a gclass is a graph and its attrs table is a tree,
|
|
13778
|
+
* and it takes the document from either side:
|
|
13779
|
+
*
|
|
13780
|
+
* - a gclass of the browser yuno -> describe_local_gclass()
|
|
13781
|
+
* - a gclass of a backend yuno -> `opts.description`, the
|
|
13782
|
+
* answer of `view-gclass`
|
|
13783
|
+
*
|
|
13784
|
+
* One window at a time per host: the caller keeps the handle
|
|
13785
|
+
* and closes it, the way the treedb graph keeps its raw-JSON
|
|
13786
|
+
* viewer.
|
|
13787
|
+
*
|
|
13788
|
+
* Copyright (c) 2026, ArtGins.
|
|
13789
|
+
* All Rights Reserved.
|
|
13790
|
+
***********************************************************************/
|
|
13791
|
+
/************************************************************
|
|
13792
|
+
* Can this app show a gclass at all?
|
|
13793
|
+
*
|
|
13794
|
+
* The viewer is C_YUI_JSON, and an app registers only the
|
|
13795
|
+
* gclasses it mounts. The caller asks this BEFORE drawing a
|
|
13796
|
+
* button, so an app that does not carry the viewer shows no
|
|
13797
|
+
* control instead of a control that fails.
|
|
13798
|
+
************************************************************/
|
|
13799
|
+
function gclass_view_available() {
|
|
13800
|
+
return !!gclass_find_by_name("C_YUI_JSON", false);
|
|
13801
|
+
}
|
|
13802
|
+
/************************************************************
|
|
13803
|
+
* Open the viewer on `gclass_name`.
|
|
13804
|
+
*
|
|
13805
|
+
* opts:
|
|
13806
|
+
* description the document to draw. Omitted, it is
|
|
13807
|
+
* built from the browser registry.
|
|
13808
|
+
* title_prefix what the window says before the gclass
|
|
13809
|
+
* name (the yuno it belongs to).
|
|
13810
|
+
* on_close called when the reader dismisses it.
|
|
13811
|
+
*
|
|
13812
|
+
* Returns a handle for close_gclass_view(), or null (and a
|
|
13813
|
+
* logged reason) when there is nothing to show.
|
|
13814
|
+
************************************************************/
|
|
13815
|
+
function open_gclass_view(host, gclass_name, opts) {
|
|
13816
|
+
opts = opts || {};
|
|
13817
|
+
if (!host || !gclass_name) {
|
|
13818
|
+
log_error(`yui_gclass_view: open without a host or a gclass name`);
|
|
13819
|
+
return null;
|
|
13820
|
+
}
|
|
13821
|
+
let shell = yui_shell_of(host);
|
|
13822
|
+
if (!gclass_view_available()) {
|
|
13823
|
+
log_error(`${gobj_short_name(host)}: C_YUI_JSON not registered by the app`);
|
|
13824
|
+
yui_shell_show_error(shell, "gclass viewer unavailable", { t });
|
|
13825
|
+
return null;
|
|
13826
|
+
}
|
|
13827
|
+
let description = opts.description || describe_local_gclass(gclass_name);
|
|
13828
|
+
if (!description) {
|
|
13829
|
+
log_warning(`${gobj_short_name(host)}: gclass not registered here: ${gclass_name}`);
|
|
13830
|
+
yui_shell_show_error(shell, "gclass not registered in this yuno", { t });
|
|
13831
|
+
return null;
|
|
13832
|
+
}
|
|
13833
|
+
let jv = gobj_create_service(`gclass-view-${clean_name(gobj_name(host))}`, "C_YUI_JSON", {
|
|
13834
|
+
json_data: description,
|
|
13835
|
+
subscriber: null
|
|
13836
|
+
}, host);
|
|
13837
|
+
if (!jv) {
|
|
13838
|
+
log_error(`${gobj_short_name(host)}: cannot create the gclass viewer`);
|
|
13839
|
+
return null;
|
|
13840
|
+
}
|
|
13841
|
+
gobj_start(jv);
|
|
13842
|
+
let handle = {
|
|
13843
|
+
json_gobj: jv,
|
|
13844
|
+
win: null,
|
|
13845
|
+
modal: null
|
|
13846
|
+
};
|
|
13847
|
+
let $box = gobj_read_pointer_attr(jv, "$container");
|
|
13848
|
+
let on_close = () => {
|
|
13849
|
+
if (typeof opts.on_close === "function") opts.on_close();
|
|
13850
|
+
};
|
|
13851
|
+
if (is_mobile$3()) {
|
|
13852
|
+
if (!shell) {
|
|
13853
|
+
log_error(`${gobj_short_name(host)}: no shell, cannot open the gclass sheet`);
|
|
13854
|
+
close_gclass_view(handle);
|
|
13855
|
+
return null;
|
|
13856
|
+
}
|
|
13857
|
+
handle.modal = yui_shell_show_modal(shell, $box, {
|
|
13858
|
+
dialog: true,
|
|
13859
|
+
logical_class: "GCLASS_VIEW_SHEET",
|
|
13860
|
+
title_prefix: opts.title_prefix || "",
|
|
13861
|
+
title: gclass_name,
|
|
13862
|
+
t,
|
|
13863
|
+
on_close
|
|
13864
|
+
});
|
|
13865
|
+
return handle;
|
|
13866
|
+
}
|
|
13867
|
+
let $win_parent = window_layer_of(host, shell);
|
|
13868
|
+
handle.win = gobj_create_service(`gclass-win-${clean_name(gobj_name(host))}`, "C_YUI_WINDOW", {
|
|
13869
|
+
$parent: $win_parent,
|
|
13870
|
+
subscriber: null,
|
|
13871
|
+
modal: false,
|
|
13872
|
+
showMax: true,
|
|
13873
|
+
showFooter: false,
|
|
13874
|
+
resizable: true,
|
|
13875
|
+
center: true,
|
|
13876
|
+
auto_save_size_and_position: true,
|
|
13877
|
+
width: 720,
|
|
13878
|
+
height: 640,
|
|
13879
|
+
logical_class: "GCLASS_VIEW_WINDOW",
|
|
13880
|
+
title_prefix: opts.title_prefix || "",
|
|
13881
|
+
title: gclass_name,
|
|
13882
|
+
icon: "yi-code",
|
|
13883
|
+
body: $box,
|
|
13884
|
+
manager: gobj_find_service("__window_manager__", false) || null,
|
|
13885
|
+
on_close
|
|
13886
|
+
}, host);
|
|
13887
|
+
if (!handle.win) {
|
|
13888
|
+
log_error(`${gobj_short_name(host)}: cannot create the gclass window`);
|
|
13889
|
+
close_gclass_view(handle);
|
|
13890
|
+
return null;
|
|
13891
|
+
}
|
|
13892
|
+
gobj_start(handle.win);
|
|
13893
|
+
return handle;
|
|
13894
|
+
}
|
|
13895
|
+
/************************************************************
|
|
13896
|
+
* Close whatever open_gclass_view() opened. Safe on null and
|
|
13897
|
+
* on a handle whose window the reader already dismissed.
|
|
13898
|
+
************************************************************/
|
|
13899
|
+
function close_gclass_view(handle) {
|
|
13900
|
+
if (!handle) return;
|
|
13901
|
+
let jv = handle.json_gobj;
|
|
13902
|
+
let win = handle.win;
|
|
13903
|
+
let modal = handle.modal;
|
|
13904
|
+
handle.json_gobj = null;
|
|
13905
|
+
handle.win = null;
|
|
13906
|
+
handle.modal = null;
|
|
13907
|
+
if (win && is_gobj(win)) try {
|
|
13908
|
+
if (gobj_is_running(win)) gobj_stop(win);
|
|
13909
|
+
gobj_destroy(win);
|
|
13910
|
+
} catch (e) {
|
|
13911
|
+
log_warning(`yui_gclass_view: window already gone: ${e}`);
|
|
13912
|
+
}
|
|
13913
|
+
if (modal && typeof modal.close === "function") try {
|
|
13914
|
+
modal.close();
|
|
13915
|
+
} catch (e) {
|
|
13916
|
+
log_warning(`yui_gclass_view: modal already gone: ${e}`);
|
|
13917
|
+
}
|
|
13918
|
+
if (jv && is_gobj(jv)) try {
|
|
13919
|
+
if (gobj_is_running(jv)) gobj_stop(jv);
|
|
13920
|
+
gobj_destroy(jv);
|
|
13921
|
+
} catch (e) {
|
|
13922
|
+
log_warning(`yui_gclass_view: viewer already gone: ${e}`);
|
|
13923
|
+
}
|
|
13924
|
+
}
|
|
13925
|
+
/************************************************************
|
|
13926
|
+
* Where to mount the window: the SAME element the caller's
|
|
13927
|
+
* own window hangs from.
|
|
13928
|
+
*
|
|
13929
|
+
* Not a preference -- a requirement. z-index only orders
|
|
13930
|
+
* siblings of one stacking context, so a viewer opened from a
|
|
13931
|
+
* window on `body` and mounted in the shell's popup layer
|
|
13932
|
+
* lands BEHIND its opener whatever number it is given, and
|
|
13933
|
+
* the button reads as one that did nothing.
|
|
13934
|
+
************************************************************/
|
|
13935
|
+
function window_layer_of(host, shell) {
|
|
13936
|
+
let $c = gobj_read_pointer_attr(host, "$container");
|
|
13937
|
+
if ($c && typeof $c.closest === "function") {
|
|
13938
|
+
let $win = $c.closest(".C_YUI_WINDOW");
|
|
13939
|
+
if ($win && $win.parentNode) return $win.parentNode;
|
|
13940
|
+
}
|
|
13941
|
+
return typeof document !== "undefined" && document.getElementById("top-layer") || shell && yui_shell_popup_layer(shell) || null;
|
|
13942
|
+
}
|
|
13943
|
+
/************************************************************
|
|
13944
|
+
* A window is not a sheet: below this width there is no room
|
|
13945
|
+
* for a title bar the reader can drag.
|
|
13946
|
+
************************************************************/
|
|
13947
|
+
function is_mobile$3() {
|
|
13948
|
+
return typeof window !== "undefined" && window.innerWidth <= 768;
|
|
13949
|
+
}
|
|
13950
|
+
//#endregion
|
|
13213
13951
|
//#region src/c_yui_gobj_tree_js.js
|
|
13214
13952
|
/***********************************************************************
|
|
13215
13953
|
* c_yui_gobj_tree_js.js
|
|
@@ -13245,6 +13983,71 @@ var BEHAVIORS_READING = [
|
|
|
13245
13983
|
"drag-element"
|
|
13246
13984
|
];
|
|
13247
13985
|
var BEHAVIORS_PICKING = ["drag-canvas", ZOOM_CANVAS];
|
|
13986
|
+
var VIEW_STORE_PREFIX = "yui_gobj_tree_view:";
|
|
13987
|
+
function view_store_key(gobj) {
|
|
13988
|
+
return VIEW_STORE_PREFIX + gobj_name(gobj);
|
|
13989
|
+
}
|
|
13990
|
+
function restore_view_shape(gobj) {
|
|
13991
|
+
let priv = gobj.priv;
|
|
13992
|
+
let state = load_view_state(gobj);
|
|
13993
|
+
if (!state) return;
|
|
13994
|
+
if (state.layout && LAYOUTS[state.layout]) {
|
|
13995
|
+
gobj_write_str_attr(gobj, "layout", state.layout);
|
|
13996
|
+
if (priv.$layout_select) priv.$layout_select.value = state.layout;
|
|
13997
|
+
}
|
|
13998
|
+
if (state.collapsed && typeof state.collapsed === "object") priv.collapse_state = Object.assign({}, state.collapsed);
|
|
13999
|
+
}
|
|
14000
|
+
function restore_view_state(gobj) {
|
|
14001
|
+
let priv = gobj.priv;
|
|
14002
|
+
let graph = priv.graph;
|
|
14003
|
+
let state = load_view_state(gobj);
|
|
14004
|
+
if (!graph || !state) return false;
|
|
14005
|
+
let done = false;
|
|
14006
|
+
priv.restoring = true;
|
|
14007
|
+
try {
|
|
14008
|
+
if (typeof state.zoom === "number" && state.zoom > 0) {
|
|
14009
|
+
graph.zoomTo(state.zoom);
|
|
14010
|
+
priv.last_zoom = state.zoom;
|
|
14011
|
+
done = true;
|
|
14012
|
+
}
|
|
14013
|
+
if (Array.isArray(state.position) && state.position.length >= 2) {
|
|
14014
|
+
graph.translateTo([state.position[0], state.position[1]]);
|
|
14015
|
+
done = true;
|
|
14016
|
+
}
|
|
14017
|
+
if (state.anchor) {
|
|
14018
|
+
priv.anchor_name = state.anchor;
|
|
14019
|
+
priv.anchor_state = "on";
|
|
14020
|
+
reanchor(gobj);
|
|
14021
|
+
yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
|
|
14022
|
+
paint_anchor_mark(gobj);
|
|
14023
|
+
}
|
|
14024
|
+
} catch (e) {
|
|
14025
|
+
log_error(`${gobj_short_name(gobj)}: cannot restore the saved view: ${e}`);
|
|
14026
|
+
} finally {
|
|
14027
|
+
priv.restoring = false;
|
|
14028
|
+
}
|
|
14029
|
+
yui_graph_update_zoom(gobj_read_attr(gobj, "$container"), priv.graph);
|
|
14030
|
+
return done;
|
|
14031
|
+
}
|
|
14032
|
+
function save_view_state(gobj) {
|
|
14033
|
+
let priv = gobj.priv;
|
|
14034
|
+
let graph = priv.graph;
|
|
14035
|
+
if (!graph || priv.restoring) return;
|
|
14036
|
+
let state = {
|
|
14037
|
+
layout: gobj_read_str_attr(gobj, "layout"),
|
|
14038
|
+
collapsed: priv.collapse_state || {},
|
|
14039
|
+
anchor: priv.anchor_state === "on" ? priv.anchor_name : ""
|
|
14040
|
+
};
|
|
14041
|
+
try {
|
|
14042
|
+
state.zoom = graph.getZoom();
|
|
14043
|
+
state.position = graph.getPosition();
|
|
14044
|
+
} catch (e) {}
|
|
14045
|
+
kw_set_local_storage_value(view_store_key(gobj), state);
|
|
14046
|
+
}
|
|
14047
|
+
function load_view_state(gobj) {
|
|
14048
|
+
let state = kw_get_local_storage_value(view_store_key(gobj), null, false);
|
|
14049
|
+
return state && typeof state === "object" ? state : null;
|
|
14050
|
+
}
|
|
13248
14051
|
var PRIVATE_DATA$9 = {
|
|
13249
14052
|
canvas_id: "",
|
|
13250
14053
|
graph: null,
|
|
@@ -13255,6 +14058,9 @@ var PRIVATE_DATA$9 = {
|
|
|
13255
14058
|
$popover: null,
|
|
13256
14059
|
$popover_title: null,
|
|
13257
14060
|
$popover_body: null,
|
|
14061
|
+
$popover_gclass: null,
|
|
14062
|
+
popover_node: null,
|
|
14063
|
+
gclass_view: null,
|
|
13258
14064
|
node_by_id: null,
|
|
13259
14065
|
collapse_state: null,
|
|
13260
14066
|
resize_observer: null,
|
|
@@ -13262,39 +14068,99 @@ var PRIVATE_DATA$9 = {
|
|
|
13262
14068
|
anchor_id: "",
|
|
13263
14069
|
anchor_name: "",
|
|
13264
14070
|
anchor_state: "off",
|
|
14071
|
+
restoring: false,
|
|
14072
|
+
restored: false,
|
|
13265
14073
|
last_zoom: 1
|
|
13266
14074
|
};
|
|
13267
14075
|
var __gclass__$9 = null;
|
|
13268
14076
|
var __layout_registered__ = false;
|
|
13269
14077
|
/***************************************************************
|
|
13270
|
-
* Node
|
|
14078
|
+
* Node colours by role.
|
|
14079
|
+
*
|
|
14080
|
+
* Saturated hues, one family apart from the next, and the same
|
|
14081
|
+
* five everywhere the reader meets a gobj. `stroke` draws the
|
|
14082
|
+
* border and the edge; `accent` is the brighter sibling used
|
|
14083
|
+
* for the bar down the left of the card and the toggle badge,
|
|
14084
|
+
* so a card carries its colour twice at two intensities and is
|
|
14085
|
+
* readable at the zoom where the text is not.
|
|
13271
14086
|
***************************************************************/
|
|
13272
14087
|
var ROLE_COLORS = {
|
|
13273
14088
|
yuno: {
|
|
13274
|
-
|
|
13275
|
-
|
|
14089
|
+
stroke: "#0284c7",
|
|
14090
|
+
accent: "#38bdf8"
|
|
13276
14091
|
},
|
|
13277
14092
|
service: {
|
|
13278
|
-
|
|
13279
|
-
|
|
14093
|
+
stroke: "#059669",
|
|
14094
|
+
accent: "#34d399"
|
|
13280
14095
|
},
|
|
13281
14096
|
pure: {
|
|
13282
|
-
|
|
13283
|
-
|
|
14097
|
+
stroke: "#d97706",
|
|
14098
|
+
accent: "#fbbf24"
|
|
13284
14099
|
},
|
|
13285
14100
|
volatil: {
|
|
13286
|
-
|
|
13287
|
-
|
|
14101
|
+
stroke: "#db2777",
|
|
14102
|
+
accent: "#f472b6"
|
|
13288
14103
|
},
|
|
13289
|
-
|
|
13290
|
-
|
|
13291
|
-
|
|
14104
|
+
child: {
|
|
14105
|
+
stroke: "#7c3aed",
|
|
14106
|
+
accent: "#a78bfa"
|
|
13292
14107
|
}
|
|
13293
14108
|
};
|
|
13294
|
-
|
|
13295
|
-
|
|
13296
|
-
|
|
13297
|
-
|
|
14109
|
+
/***************************************************************
|
|
14110
|
+
* Status colours: what the gobj is DOING.
|
|
14111
|
+
*
|
|
14112
|
+
* A pill and not a word: three states of one gobj are told
|
|
14113
|
+
* apart across a whole tree by colour long before anybody
|
|
14114
|
+
* reads them, and the dot alone never said which was which.
|
|
14115
|
+
* `disabled` is a fourth entry and not a status -- a disabled
|
|
14116
|
+
* gobj is still stopped or running -- but it takes the pill,
|
|
14117
|
+
* because it is the fact that explains the other one.
|
|
14118
|
+
***************************************************************/
|
|
14119
|
+
var STATUS_COLORS = {
|
|
14120
|
+
light: {
|
|
14121
|
+
playing: {
|
|
14122
|
+
fg: "#065f46",
|
|
14123
|
+
bg: "#a7f3d0",
|
|
14124
|
+
dot: "#10b981"
|
|
14125
|
+
},
|
|
14126
|
+
running: {
|
|
14127
|
+
fg: "#92400e",
|
|
14128
|
+
bg: "#fde68a",
|
|
14129
|
+
dot: "#f59e0b"
|
|
14130
|
+
},
|
|
14131
|
+
stopped: {
|
|
14132
|
+
fg: "#991b1b",
|
|
14133
|
+
bg: "#fecaca",
|
|
14134
|
+
dot: "#ef4444"
|
|
14135
|
+
},
|
|
14136
|
+
disabled: {
|
|
14137
|
+
fg: "#334155",
|
|
14138
|
+
bg: "#e2e8f0",
|
|
14139
|
+
dot: "#94a3b8"
|
|
14140
|
+
}
|
|
14141
|
+
},
|
|
14142
|
+
dark: {
|
|
14143
|
+
playing: {
|
|
14144
|
+
fg: "#6ee7b7",
|
|
14145
|
+
bg: "rgba(16,185,129,0.28)",
|
|
14146
|
+
dot: "#34d399"
|
|
14147
|
+
},
|
|
14148
|
+
running: {
|
|
14149
|
+
fg: "#fcd34d",
|
|
14150
|
+
bg: "rgba(245,158,11,0.28)",
|
|
14151
|
+
dot: "#fbbf24"
|
|
14152
|
+
},
|
|
14153
|
+
stopped: {
|
|
14154
|
+
fg: "#fca5a5",
|
|
14155
|
+
bg: "rgba(239,68,68,0.28)",
|
|
14156
|
+
dot: "#f87171"
|
|
14157
|
+
},
|
|
14158
|
+
disabled: {
|
|
14159
|
+
fg: "#cbd5e1",
|
|
14160
|
+
bg: "rgba(148,163,184,0.28)",
|
|
14161
|
+
dot: "#94a3b8"
|
|
14162
|
+
}
|
|
14163
|
+
}
|
|
13298
14164
|
};
|
|
13299
14165
|
/***************************************************************
|
|
13300
14166
|
* Fixed node sizes per role (px).
|
|
@@ -13304,16 +14170,16 @@ var STATE_COLORS = {
|
|
|
13304
14170
|
var NODE_SIZES = {
|
|
13305
14171
|
full: {
|
|
13306
14172
|
yuno: {
|
|
13307
|
-
w:
|
|
13308
|
-
h:
|
|
14173
|
+
w: 260,
|
|
14174
|
+
h: 96
|
|
13309
14175
|
},
|
|
13310
14176
|
service: {
|
|
13311
|
-
w:
|
|
13312
|
-
h:
|
|
14177
|
+
w: 232,
|
|
14178
|
+
h: 92
|
|
13313
14179
|
},
|
|
13314
14180
|
child: {
|
|
13315
|
-
w:
|
|
13316
|
-
h:
|
|
14181
|
+
w: 196,
|
|
14182
|
+
h: 86
|
|
13317
14183
|
}
|
|
13318
14184
|
},
|
|
13319
14185
|
compact: {
|
|
@@ -13336,27 +14202,47 @@ var NODE_SIZES = {
|
|
|
13336
14202
|
***************************************************************/
|
|
13337
14203
|
var GT_FONT = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif";
|
|
13338
14204
|
/***************************************************************
|
|
13339
|
-
*
|
|
13340
|
-
*
|
|
13341
|
-
*
|
|
14205
|
+
* Theme-aware card palette derived from the role colour.
|
|
14206
|
+
*
|
|
14207
|
+
* Brighter than a tint: at 9% of the role colour every card
|
|
14208
|
+
* was the same near-white rectangle and the tree read as a
|
|
14209
|
+
* wireframe. The fill carries enough of the hue to be named
|
|
14210
|
+
* from across the canvas, and the border and the accent bar
|
|
14211
|
+
* carry the rest.
|
|
13342
14212
|
***************************************************************/
|
|
13343
|
-
function role_card_style(
|
|
14213
|
+
function role_card_style(colors, dark) {
|
|
14214
|
+
let stroke = colors.stroke;
|
|
14215
|
+
let accent = colors.accent;
|
|
13344
14216
|
return {
|
|
13345
|
-
bg: dark ? `color-mix(in srgb, ${stroke}
|
|
13346
|
-
border: dark ?
|
|
13347
|
-
|
|
13348
|
-
|
|
13349
|
-
|
|
13350
|
-
|
|
14217
|
+
bg: dark ? `color-mix(in srgb, ${stroke} 34%, #253044)` : `color-mix(in srgb, ${stroke} 13%, #ffffff)`,
|
|
14218
|
+
border: dark ? accent : stroke,
|
|
14219
|
+
accent: dark ? accent : stroke,
|
|
14220
|
+
title: dark ? "#f1f5f9" : "#0f172a",
|
|
14221
|
+
sub: dark ? "#a8b3c2" : "#475569",
|
|
14222
|
+
tagbg: dark ? `color-mix(in srgb, ${stroke} 48%, #253044)` : `color-mix(in srgb, ${stroke} 20%, #ffffff)`,
|
|
14223
|
+
tagfg: dark ? "#f1f5f9" : stroke,
|
|
14224
|
+
shadow: dark ? "0 2px 6px rgba(0,0,0,0.50)" : "0 2px 5px rgba(15,23,42,0.16)"
|
|
13351
14225
|
};
|
|
13352
14226
|
}
|
|
13353
14227
|
/***************************************************************
|
|
13354
|
-
*
|
|
14228
|
+
* The pill colours of a node, for the theme in force.
|
|
13355
14229
|
***************************************************************/
|
|
13356
|
-
function
|
|
13357
|
-
|
|
13358
|
-
if (
|
|
13359
|
-
return
|
|
14230
|
+
function status_card_style(d, dark) {
|
|
14231
|
+
let table = dark ? STATUS_COLORS.dark : STATUS_COLORS.light;
|
|
14232
|
+
if (d.disabled) return table.disabled;
|
|
14233
|
+
return table[node_status(d)] || table.stopped;
|
|
14234
|
+
}
|
|
14235
|
+
/***************************************************************
|
|
14236
|
+
* Map a node to its visual size class. Services are drawn
|
|
14237
|
+
* bigger than plain children so the service boundary of the
|
|
14238
|
+
* hierarchy is visible before anything is read.
|
|
14239
|
+
***************************************************************/
|
|
14240
|
+
function get_node_category(d) {
|
|
14241
|
+
switch (node_role(d)) {
|
|
14242
|
+
case "yuno": return "yuno";
|
|
14243
|
+
case "service": return "service";
|
|
14244
|
+
default: return "child";
|
|
14245
|
+
}
|
|
13360
14246
|
}
|
|
13361
14247
|
/***************************************************************
|
|
13362
14248
|
* Layouts registry.
|
|
@@ -13468,13 +14354,22 @@ function mt_create$9(gobj) {
|
|
|
13468
14354
|
* Framework Method: Start
|
|
13469
14355
|
***************************************************************/
|
|
13470
14356
|
function mt_start$9(gobj) {
|
|
14357
|
+
restore_view_shape(gobj);
|
|
14358
|
+
let shell = yui_shell_of(gobj);
|
|
14359
|
+
if (shell) gobj_subscribe_event(shell, "EV_LANGUAGE_CHANGED", {}, gobj);
|
|
13471
14360
|
build_graph$2(gobj);
|
|
13472
14361
|
load_tree(gobj);
|
|
13473
14362
|
}
|
|
13474
14363
|
/***************************************************************
|
|
13475
14364
|
* Framework Method: Stop
|
|
13476
14365
|
***************************************************************/
|
|
13477
|
-
function mt_stop$9(gobj) {
|
|
14366
|
+
function mt_stop$9(gobj) {
|
|
14367
|
+
let priv = gobj.priv;
|
|
14368
|
+
let shell = yui_shell_of(gobj);
|
|
14369
|
+
if (shell) gobj_unsubscribe_event(shell, "EV_LANGUAGE_CHANGED", {}, gobj);
|
|
14370
|
+
close_gclass_view(priv.gclass_view);
|
|
14371
|
+
priv.gclass_view = null;
|
|
14372
|
+
}
|
|
13478
14373
|
/***************************************************************
|
|
13479
14374
|
* Framework Method: Destroy
|
|
13480
14375
|
***************************************************************/
|
|
@@ -13500,6 +14395,8 @@ function mt_destroy$9(gobj) {
|
|
|
13500
14395
|
priv.$popover = null;
|
|
13501
14396
|
priv.$popover_title = null;
|
|
13502
14397
|
priv.$popover_body = null;
|
|
14398
|
+
priv.$popover_gclass = null;
|
|
14399
|
+
priv.popover_node = null;
|
|
13503
14400
|
priv.$layout_select = null;
|
|
13504
14401
|
priv.node_by_id = null;
|
|
13505
14402
|
priv.collapse_state = null;
|
|
@@ -13603,10 +14500,38 @@ function build_popover(gobj, $container) {
|
|
|
13603
14500
|
text-overflow: ellipsis;
|
|
13604
14501
|
`;
|
|
13605
14502
|
$header.appendChild($title);
|
|
14503
|
+
if (gclass_view_available()) {
|
|
14504
|
+
let $gclass = document.createElement("button");
|
|
14505
|
+
$gclass.type = "button";
|
|
14506
|
+
$gclass.className = "GOBJ_TREE_POPOVER_GCLASS button is-small";
|
|
14507
|
+
$gclass.style.cssText = `
|
|
14508
|
+
margin-right: 6px;
|
|
14509
|
+
padding: 0 8px;
|
|
14510
|
+
height: 22px;
|
|
14511
|
+
font-size: 11px;
|
|
14512
|
+
white-space: nowrap;
|
|
14513
|
+
`;
|
|
14514
|
+
$gclass.textContent = t("gclass");
|
|
14515
|
+
$gclass.setAttribute("i18n", "gclass");
|
|
14516
|
+
$gclass.title = t("view gclass");
|
|
14517
|
+
$gclass.setAttribute("data-i18n-title", "view gclass");
|
|
14518
|
+
$gclass.setAttribute("aria-label", t("view gclass"));
|
|
14519
|
+
$gclass.setAttribute("data-i18n-aria-label", "view gclass");
|
|
14520
|
+
$gclass.addEventListener("click", (evt) => {
|
|
14521
|
+
evt.stopPropagation();
|
|
14522
|
+
gobj_send_event(gobj, "EV_OPEN_GCLASS", {}, gobj);
|
|
14523
|
+
});
|
|
14524
|
+
$header.appendChild($gclass);
|
|
14525
|
+
priv.$popover_gclass = $gclass;
|
|
14526
|
+
} else log_warning(`${gobj_short_name(gobj)}: no C_YUI_JSON registered, the gclass viewer is not offered`);
|
|
13606
14527
|
let $close = document.createElement("button");
|
|
13607
14528
|
$close.type = "button";
|
|
14529
|
+
$close.className = "GOBJ_TREE_POPOVER_CLOSE";
|
|
13608
14530
|
$close.textContent = "×";
|
|
13609
14531
|
$close.title = t("close");
|
|
14532
|
+
$close.setAttribute("data-i18n-title", "close");
|
|
14533
|
+
$close.setAttribute("aria-label", t("close"));
|
|
14534
|
+
$close.setAttribute("data-i18n-aria-label", "close");
|
|
13610
14535
|
$close.style.cssText = `
|
|
13611
14536
|
background: transparent;
|
|
13612
14537
|
border: none;
|
|
@@ -13740,6 +14665,7 @@ function build_graph$2(gobj) {
|
|
|
13740
14665
|
if (z === priv.last_zoom) return;
|
|
13741
14666
|
priv.last_zoom = z;
|
|
13742
14667
|
if (priv.anchor_state === "on" && priv.anchor_id) yui_graph_center_on(priv.graph, priv.anchor_id);
|
|
14668
|
+
save_view_state(gobj);
|
|
13743
14669
|
});
|
|
13744
14670
|
graph.on(NodeEvent.CLICK, (evt) => {
|
|
13745
14671
|
gobj_send_event(gobj, "EV_NODE_CLICK", { evt }, gobj);
|
|
@@ -13782,22 +14708,20 @@ function apply_layout(gobj) {
|
|
|
13782
14708
|
});
|
|
13783
14709
|
}
|
|
13784
14710
|
/************************************************************
|
|
13785
|
-
*
|
|
13786
|
-
|
|
13787
|
-
|
|
13788
|
-
|
|
13789
|
-
|
|
13790
|
-
if (gobj_is_service(target_gobj)) return ROLE_COLORS.service;
|
|
13791
|
-
if (gobj_is_pure_child(target_gobj)) return ROLE_COLORS.pure;
|
|
13792
|
-
return ROLE_COLORS.default;
|
|
13793
|
-
}
|
|
13794
|
-
/************************************************************
|
|
13795
|
-
* Resolve state color
|
|
14711
|
+
* The role colours of a node.
|
|
14712
|
+
*
|
|
14713
|
+
* Note the ORDER: volatil before service. A volatil gobj that
|
|
14714
|
+
* is also a service is drawn volatil, because that is the fact
|
|
14715
|
+
* that decides how long it lives.
|
|
13796
14716
|
************************************************************/
|
|
13797
|
-
function
|
|
13798
|
-
|
|
13799
|
-
|
|
13800
|
-
|
|
14717
|
+
function get_role_colors(d) {
|
|
14718
|
+
switch (node_role(d)) {
|
|
14719
|
+
case "yuno": return ROLE_COLORS.yuno;
|
|
14720
|
+
case "volatil": return ROLE_COLORS.volatil;
|
|
14721
|
+
case "service": return ROLE_COLORS.service;
|
|
14722
|
+
case "pure": return ROLE_COLORS.pure;
|
|
14723
|
+
default: return ROLE_COLORS.child;
|
|
14724
|
+
}
|
|
13801
14725
|
}
|
|
13802
14726
|
/************************************************************
|
|
13803
14727
|
* Generate a unique node id
|
|
@@ -13829,72 +14753,135 @@ function is_node_collapsed(gobj, full_name, num_children) {
|
|
|
13829
14753
|
************************************************************/
|
|
13830
14754
|
function render_toggle_html(node_id, collapsed, num_children, cs) {
|
|
13831
14755
|
let label = collapsed ? "+" + num_children : "−";
|
|
14756
|
+
let title = collapsed ? t("expand children") : t("collapse children");
|
|
13832
14757
|
return `<span
|
|
13833
14758
|
data-gobj-toggle="true"
|
|
13834
14759
|
data-node-id="${node_id}"
|
|
13835
|
-
title="${
|
|
14760
|
+
title="${escapeHtml(title)}"
|
|
13836
14761
|
style="
|
|
13837
14762
|
flex: 0 0 auto;
|
|
13838
14763
|
display: inline-flex;
|
|
13839
14764
|
align-items: center;
|
|
13840
14765
|
justify-content: center;
|
|
13841
|
-
min-width:
|
|
13842
|
-
height:
|
|
13843
|
-
padding: 0
|
|
14766
|
+
min-width: 24px;
|
|
14767
|
+
height: 18px;
|
|
14768
|
+
padding: 0 6px;
|
|
13844
14769
|
margin-left: 4px;
|
|
13845
|
-
background: ${cs.
|
|
13846
|
-
color:
|
|
13847
|
-
border: 1px solid ${cs.
|
|
13848
|
-
border-radius:
|
|
14770
|
+
background: ${cs.accent};
|
|
14771
|
+
color: #ffffff;
|
|
14772
|
+
border: 1px solid ${cs.accent};
|
|
14773
|
+
border-radius: 9px;
|
|
13849
14774
|
font-size: 10px;
|
|
13850
|
-
font-weight:
|
|
14775
|
+
font-weight: 700;
|
|
13851
14776
|
line-height: 1;
|
|
13852
14777
|
cursor: pointer;
|
|
13853
14778
|
user-select: none;
|
|
13854
14779
|
">${label}</span>`;
|
|
13855
14780
|
}
|
|
13856
14781
|
/************************************************************
|
|
13857
|
-
*
|
|
14782
|
+
* HTML for the status pill: what the gobj is DOING.
|
|
14783
|
+
*
|
|
14784
|
+
* A dot plus a word, and the word is dropped in the compact
|
|
14785
|
+
* card where there is no room for it -- the colour still
|
|
14786
|
+
* carries it, and the `title` says it in full either way.
|
|
14787
|
+
************************************************************/
|
|
14788
|
+
function render_status_html(d, ss, labels, with_label) {
|
|
14789
|
+
let full = d.disabled ? labels.field.disabled : labels.status[node_status_key(d)] || node_status_key(d);
|
|
14790
|
+
let label = d.disabled ? labels.field.disabled : labels.status_short[node_status(d)] || node_status(d);
|
|
14791
|
+
if (!with_label) return `<span title="${escapeHtml(full)}" style="
|
|
14792
|
+
flex: 0 0 auto;
|
|
14793
|
+
width: 10px; height: 10px; border-radius: 50%;
|
|
14794
|
+
background: ${ss.dot};
|
|
14795
|
+
box-shadow: 0 0 0 2px ${ss.bg};
|
|
14796
|
+
margin-left: 6px;
|
|
14797
|
+
"></span>`;
|
|
14798
|
+
return `<span title="${escapeHtml(full)}" style="
|
|
14799
|
+
flex: 0 0 auto;
|
|
14800
|
+
display: inline-flex;
|
|
14801
|
+
align-items: center;
|
|
14802
|
+
gap: 4px;
|
|
14803
|
+
padding: 1px 7px 1px 5px;
|
|
14804
|
+
background: ${ss.bg};
|
|
14805
|
+
color: ${ss.fg};
|
|
14806
|
+
border-radius: 9px;
|
|
14807
|
+
font-size: 10px;
|
|
14808
|
+
font-weight: 700;
|
|
14809
|
+
line-height: 1.45;
|
|
14810
|
+
white-space: nowrap;
|
|
14811
|
+
"><span style="width:7px; height:7px; border-radius:50%; background:${ss.dot};"></span>${escapeHtml(label)}</span>`;
|
|
14812
|
+
}
|
|
14813
|
+
/************************************************************
|
|
14814
|
+
* HTML for the badges: what the gobj IS, besides its colour.
|
|
14815
|
+
************************************************************/
|
|
14816
|
+
function render_badges_html(d, cs, labels) {
|
|
14817
|
+
let html = "";
|
|
14818
|
+
for (let key of node_badge_keys(d)) {
|
|
14819
|
+
let label = labels.badge[key] || key;
|
|
14820
|
+
html += `<span style="
|
|
14821
|
+
font-size: 10px;
|
|
14822
|
+
font-weight: 600;
|
|
14823
|
+
padding: 0 6px;
|
|
14824
|
+
margin-right: 4px;
|
|
14825
|
+
background: ${cs.tagbg};
|
|
14826
|
+
color: ${cs.tagfg};
|
|
14827
|
+
border: 1px solid ${cs.accent};
|
|
14828
|
+
border-radius: 9px;
|
|
14829
|
+
white-space: nowrap;
|
|
14830
|
+
">${escapeHtml(label)}</span>`;
|
|
14831
|
+
}
|
|
14832
|
+
return html;
|
|
14833
|
+
}
|
|
14834
|
+
/************************************************************
|
|
14835
|
+
* Recursively build nodes and edges from a NODE DESCRIPTOR
|
|
14836
|
+
* tree (gobj_tree_model.js), never from gobjs: the same
|
|
14837
|
+
* builder draws the browser yuno and a backend one.
|
|
14838
|
+
*
|
|
14839
|
+
* The descriptor is also what the popover reads, so the view
|
|
14840
|
+
* fields it needs are written onto it here (`full_name`,
|
|
14841
|
+
* `short_name`, `num_children`, `is_collapsed`,
|
|
14842
|
+
* `direct_children`) instead of copied into a second object
|
|
14843
|
+
* that can then disagree with the first.
|
|
13858
14844
|
************************************************************/
|
|
13859
|
-
function build_gobj_nodes(gobj,
|
|
14845
|
+
function build_gobj_nodes(gobj, d, nodes, edges, parent_id, compact, labels) {
|
|
13860
14846
|
let priv = gobj.priv;
|
|
13861
14847
|
let node_id = gen_node_id(gobj);
|
|
13862
|
-
let gclass_name = gobj_gclass_name(target_gobj) || "";
|
|
13863
|
-
let instance_name = gobj_name(target_gobj) || "";
|
|
13864
|
-
let state = gobj_current_state(target_gobj) || "";
|
|
13865
|
-
let full_name = gobj_full_name(target_gobj);
|
|
13866
|
-
let colors = get_role_colors(target_gobj, is_root);
|
|
13867
14848
|
let dark = yui_is_dark();
|
|
13868
|
-
let
|
|
13869
|
-
let
|
|
13870
|
-
let
|
|
13871
|
-
let
|
|
13872
|
-
let status_text = !running ? "stopped" : playing ? "playing" : "running";
|
|
13873
|
-
let category = get_node_category(target_gobj, is_root);
|
|
14849
|
+
let colors = get_role_colors(d);
|
|
14850
|
+
let cs = role_card_style(colors, dark);
|
|
14851
|
+
let ss = status_card_style(d, dark);
|
|
14852
|
+
let category = get_node_category(d);
|
|
13874
14853
|
let size = NODE_SIZES[compact ? "compact" : "full"][category];
|
|
13875
14854
|
let width = size.w;
|
|
13876
14855
|
let height = size.h;
|
|
13877
14856
|
let border_width = category === "yuno" || category === "service" ? 2 : 1;
|
|
13878
|
-
let
|
|
14857
|
+
let border_style = d.disabled ? "dashed" : "solid";
|
|
14858
|
+
let children = d.children || [];
|
|
13879
14859
|
let num_children = children.length;
|
|
13880
14860
|
let has_children = num_children > 0;
|
|
13881
|
-
let collapsed = has_children && is_node_collapsed(gobj,
|
|
14861
|
+
let collapsed = has_children && is_node_collapsed(gobj, d.fullname, num_children);
|
|
13882
14862
|
let direct_children = [];
|
|
13883
14863
|
for (let child of children) direct_children.push({
|
|
13884
|
-
full_name:
|
|
13885
|
-
has_grandchildren: (child.
|
|
14864
|
+
full_name: child.fullname,
|
|
14865
|
+
has_grandchildren: (child.children || []).length > 0
|
|
13886
14866
|
});
|
|
14867
|
+
d.full_name = d.fullname;
|
|
14868
|
+
d.short_name = d.shortname;
|
|
14869
|
+
d.num_children = num_children;
|
|
14870
|
+
d.is_collapsed = collapsed;
|
|
14871
|
+
d.direct_children = direct_children;
|
|
14872
|
+
d.node_id = node_id;
|
|
13887
14873
|
let toggle_html = has_children ? render_toggle_html(node_id, collapsed, num_children, cs) : "";
|
|
14874
|
+
let card_title = d.gclass + (d.name ? "^" + d.name : "");
|
|
13888
14875
|
let node_html;
|
|
13889
14876
|
if (compact) {
|
|
13890
|
-
let compact_label = escapeHtml(
|
|
14877
|
+
let compact_label = escapeHtml(card_title);
|
|
13891
14878
|
node_html = `
|
|
13892
|
-
<div style="
|
|
14879
|
+
<div class="GOBJ_CARD" data-gobj-name="${escapeHtml(d.fullname)}" style="
|
|
13893
14880
|
width: ${width}px;
|
|
13894
14881
|
height: ${height}px;
|
|
13895
14882
|
background: ${cs.bg};
|
|
13896
|
-
border: ${border_width}px
|
|
13897
|
-
border-left:
|
|
14883
|
+
border: ${border_width}px ${border_style} ${cs.border};
|
|
14884
|
+
border-left: 6px solid ${cs.accent};
|
|
13898
14885
|
border-radius: 7px;
|
|
13899
14886
|
box-shadow: ${cs.shadow};
|
|
13900
14887
|
font-family: ${GT_FONT};
|
|
@@ -13903,34 +14890,24 @@ function build_gobj_nodes(gobj, target_gobj, nodes, edges, parent_id, is_root, c
|
|
|
13903
14890
|
padding: 0 8px;
|
|
13904
14891
|
box-sizing: border-box;
|
|
13905
14892
|
overflow: hidden;
|
|
14893
|
+
opacity: ${d.disabled ? "0.72" : "1"};
|
|
13906
14894
|
cursor: pointer;
|
|
13907
14895
|
" title="${compact_label}">
|
|
13908
14896
|
<span style="flex:1 1 auto; min-width:0; font-size:11px; font-weight:600; color:${cs.title}; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">${compact_label}</span>
|
|
13909
|
-
|
|
13910
|
-
flex: 0 0 auto;
|
|
13911
|
-
width: 8px; height: 8px; border-radius: 50%;
|
|
13912
|
-
background: ${state_color}; margin-left: 6px;
|
|
13913
|
-
"></span>
|
|
14897
|
+
${render_status_html(d, ss, labels, false)}
|
|
13914
14898
|
${toggle_html}
|
|
13915
14899
|
</div>`;
|
|
13916
14900
|
} else {
|
|
13917
|
-
let header_label = escapeHtml(
|
|
13918
|
-
let name_label = escapeHtml(
|
|
13919
|
-
let state_label = escapeHtml(state);
|
|
13920
|
-
let badges = [];
|
|
13921
|
-
if (is_root) badges.push("yuno");
|
|
13922
|
-
else {
|
|
13923
|
-
if (gobj_is_service(target_gobj)) badges.push("service");
|
|
13924
|
-
if (gobj_is_pure_child(target_gobj)) badges.push("pure");
|
|
13925
|
-
if (gobj_is_volatil(target_gobj)) badges.push("volatil");
|
|
13926
|
-
}
|
|
13927
|
-
let badges_html = badges.map((b) => `<span style="font-size:10px; padding:0 5px; margin-right:4px; background:${cs.tagbg}; color:${cs.border}; border:1px solid ${cs.border}; border-radius:6px; white-space:nowrap;">${b}</span>`).join("");
|
|
14901
|
+
let header_label = escapeHtml(d.gclass);
|
|
14902
|
+
let name_label = escapeHtml(d.name);
|
|
14903
|
+
let state_label = escapeHtml(d.state);
|
|
13928
14904
|
node_html = `
|
|
13929
|
-
<div style="
|
|
14905
|
+
<div class="GOBJ_CARD" data-gobj-name="${escapeHtml(d.fullname)}" style="
|
|
13930
14906
|
width: ${width}px;
|
|
13931
14907
|
height: ${height}px;
|
|
13932
14908
|
background: ${cs.bg};
|
|
13933
|
-
border: ${border_width}px
|
|
14909
|
+
border: ${border_width}px ${border_style} ${cs.border};
|
|
14910
|
+
border-left: 6px solid ${cs.accent};
|
|
13934
14911
|
border-radius: 9px;
|
|
13935
14912
|
box-shadow: ${cs.shadow};
|
|
13936
14913
|
overflow: hidden;
|
|
@@ -13939,48 +14916,29 @@ function build_gobj_nodes(gobj, target_gobj, nodes, edges, parent_id, is_root, c
|
|
|
13939
14916
|
display: flex;
|
|
13940
14917
|
flex-direction: column;
|
|
13941
14918
|
justify-content: center;
|
|
13942
|
-
padding:
|
|
13943
|
-
gap:
|
|
14919
|
+
padding: 6px 9px;
|
|
14920
|
+
gap: 2px;
|
|
14921
|
+
opacity: ${d.disabled ? "0.72" : "1"};
|
|
13944
14922
|
cursor: pointer;
|
|
13945
|
-
" title="${escapeHtml(
|
|
14923
|
+
" title="${escapeHtml(card_title)}">
|
|
13946
14924
|
<div style="flex:0 0 auto; display:flex; align-items:center; gap:6px; overflow:hidden;">
|
|
13947
|
-
<span style="flex:1 1 auto; min-width:0; font-size:12px; line-height:1.25; font-weight:
|
|
14925
|
+
<span style="flex:1 1 auto; min-width:0; font-size:12px; line-height:1.25; font-weight:700; color:${cs.title}; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">${header_label}</span>
|
|
13948
14926
|
${toggle_html}
|
|
13949
14927
|
</div>
|
|
13950
14928
|
<div style="flex:0 0 auto; font-size:12px; line-height:1.25; color:${cs.sub}; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">${name_label || " "}</div>
|
|
13951
14929
|
<div style="flex:0 0 auto; display:flex; justify-content:space-between; align-items:center; gap:6px; overflow:hidden;">
|
|
13952
|
-
<span style="flex:1 1 auto; min-width:0;
|
|
13953
|
-
|
|
13954
|
-
<span style="width:7px; height:7px; border-radius:50%; background:${state_color};"></span>${state_label || status_text}
|
|
13955
|
-
</span>
|
|
14930
|
+
<span style="flex:1 1 auto; min-width:0; font-size:10px; line-height:1.25; color:${cs.sub}; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">${state_label || " "}</span>
|
|
14931
|
+
${render_status_html(d, ss, labels, true)}
|
|
13956
14932
|
</div>
|
|
14933
|
+
<div style="flex:0 0 auto; overflow:hidden; white-space:nowrap; text-overflow:ellipsis;">${render_badges_html(d, cs, labels)}</div>
|
|
13957
14934
|
</div>`;
|
|
13958
14935
|
}
|
|
13959
|
-
let parent_gobj = gobj_parent(target_gobj);
|
|
13960
|
-
let node_data = {
|
|
13961
|
-
gclass: gclass_name,
|
|
13962
|
-
name: instance_name,
|
|
13963
|
-
short_name: gobj_short_name(target_gobj),
|
|
13964
|
-
full_name,
|
|
13965
|
-
parent_short_name: parent_gobj ? gobj_short_name(parent_gobj) : "",
|
|
13966
|
-
state,
|
|
13967
|
-
category,
|
|
13968
|
-
is_yuno: is_root,
|
|
13969
|
-
is_service: gobj_is_service(target_gobj),
|
|
13970
|
-
is_pure_child: gobj_is_pure_child(target_gobj),
|
|
13971
|
-
is_volatil: gobj_is_volatil(target_gobj),
|
|
13972
|
-
is_running: running,
|
|
13973
|
-
is_playing: playing,
|
|
13974
|
-
num_children,
|
|
13975
|
-
is_collapsed: collapsed,
|
|
13976
|
-
direct_children
|
|
13977
|
-
};
|
|
13978
14936
|
if (!priv.node_by_id) priv.node_by_id = {};
|
|
13979
|
-
priv.node_by_id[node_id] =
|
|
14937
|
+
priv.node_by_id[node_id] = d;
|
|
13980
14938
|
let node = {
|
|
13981
14939
|
id: node_id,
|
|
13982
14940
|
type: "html",
|
|
13983
|
-
data:
|
|
14941
|
+
data: d,
|
|
13984
14942
|
style: {
|
|
13985
14943
|
innerHTML: node_html,
|
|
13986
14944
|
size: [width, height],
|
|
@@ -13993,11 +14951,11 @@ function build_gobj_nodes(gobj, target_gobj, nodes, edges, parent_id, is_root, c
|
|
|
13993
14951
|
source: parent_id,
|
|
13994
14952
|
target: node_id,
|
|
13995
14953
|
style: {
|
|
13996
|
-
stroke: colors.
|
|
13997
|
-
lineWidth: 1
|
|
14954
|
+
stroke: colors.accent,
|
|
14955
|
+
lineWidth: 1.4
|
|
13998
14956
|
}
|
|
13999
14957
|
});
|
|
14000
|
-
if (!collapsed) for (let child of children) build_gobj_nodes(gobj, child, nodes, edges, node_id,
|
|
14958
|
+
if (!collapsed) for (let child of children) build_gobj_nodes(gobj, child, nodes, edges, node_id, compact, labels);
|
|
14001
14959
|
return node_id;
|
|
14002
14960
|
}
|
|
14003
14961
|
/************************************************************
|
|
@@ -14014,9 +14972,13 @@ function load_tree(gobj) {
|
|
|
14014
14972
|
let priv = gobj.priv;
|
|
14015
14973
|
let graph = priv.graph;
|
|
14016
14974
|
if (!graph) return;
|
|
14017
|
-
let
|
|
14018
|
-
if (!
|
|
14975
|
+
let root = describe_js_gobj(gobj_yuno(), true);
|
|
14976
|
+
if (!root) {
|
|
14977
|
+
log_error(`${gobj_short_name(gobj)}: no yuno to draw`);
|
|
14978
|
+
return;
|
|
14979
|
+
}
|
|
14019
14980
|
let layout_cfg = get_layout_cfg(gobj_read_str_attr(gobj, "layout"));
|
|
14981
|
+
let labels = node_labels(t);
|
|
14020
14982
|
let anchor = priv.pending_anchor || null;
|
|
14021
14983
|
let preserve_view = !!priv.pending_preserve_view;
|
|
14022
14984
|
priv.pending_anchor = null;
|
|
@@ -14025,7 +14987,7 @@ function load_tree(gobj) {
|
|
|
14025
14987
|
priv.node_by_id = {};
|
|
14026
14988
|
let nodes = [];
|
|
14027
14989
|
let edges = [];
|
|
14028
|
-
build_gobj_nodes(gobj,
|
|
14990
|
+
build_gobj_nodes(gobj, root, nodes, edges, null, layout_cfg.compact, labels);
|
|
14029
14991
|
if (nodes.length > 0) {
|
|
14030
14992
|
let root_id = nodes[0].id;
|
|
14031
14993
|
graph.setData({
|
|
@@ -14034,11 +14996,16 @@ function load_tree(gobj) {
|
|
|
14034
14996
|
});
|
|
14035
14997
|
graph.render().then(() => {
|
|
14036
14998
|
reanchor(gobj);
|
|
14999
|
+
paint_anchor_mark(gobj);
|
|
14037
15000
|
if (anchor) {
|
|
14038
15001
|
restore_view_anchored(gobj, anchor);
|
|
14039
15002
|
return;
|
|
14040
15003
|
}
|
|
14041
15004
|
if (preserve_view) return;
|
|
15005
|
+
if (!priv.restored) {
|
|
15006
|
+
priv.restored = true;
|
|
15007
|
+
if (restore_view_state(gobj)) return;
|
|
15008
|
+
}
|
|
14042
15009
|
Promise.resolve(graph.zoomTo(1)).then(() => {
|
|
14043
15010
|
if (!yui_graph_center_on(graph, priv.anchor_id)) yui_graph_center_on(graph, root_id);
|
|
14044
15011
|
priv.last_zoom = 1;
|
|
@@ -14121,46 +15088,17 @@ function refresh_tree(gobj, opts) {
|
|
|
14121
15088
|
}
|
|
14122
15089
|
}
|
|
14123
15090
|
/************************************************************
|
|
14124
|
-
* Rows to show in the popover for a given node data.
|
|
14125
|
-
*
|
|
14126
|
-
* This is the extension point: to surface a new attribute,
|
|
14127
|
-
* collect it into `node_data` in build_gobj_nodes(), then
|
|
14128
|
-
* append a `[label, value]` pair here.
|
|
14129
|
-
************************************************************/
|
|
14130
|
-
function get_node_info_rows(node_data) {
|
|
14131
|
-
let rows = [];
|
|
14132
|
-
rows.push([t("gclass"), node_data.gclass || "—"]);
|
|
14133
|
-
rows.push([t("name"), node_data.name || "—"]);
|
|
14134
|
-
if (node_data.full_name && node_data.full_name !== node_data.short_name) rows.push([t("full name"), node_data.full_name]);
|
|
14135
|
-
let role;
|
|
14136
|
-
if (node_data.is_yuno) role = "yuno";
|
|
14137
|
-
else if (node_data.is_service) role = "service";
|
|
14138
|
-
else if (node_data.is_volatil) role = "volatil child";
|
|
14139
|
-
else if (node_data.is_pure_child) role = "pure child";
|
|
14140
|
-
else role = "child";
|
|
14141
|
-
rows.push([t("role"), role]);
|
|
14142
|
-
let status;
|
|
14143
|
-
if (!node_data.is_running) status = "stopped";
|
|
14144
|
-
else if (node_data.is_playing) status = "running + playing";
|
|
14145
|
-
else status = "running";
|
|
14146
|
-
rows.push([t("status"), status]);
|
|
14147
|
-
rows.push([t("state"), node_data.state || "—"]);
|
|
14148
|
-
if (node_data.parent_short_name) rows.push([t("parent"), node_data.parent_short_name]);
|
|
14149
|
-
if (typeof node_data.num_children === "number") {
|
|
14150
|
-
let kids = node_data.num_children;
|
|
14151
|
-
if (kids > 0 && node_data.is_collapsed) rows.push([t("children"), kids + " " + t("(collapsed)")]);
|
|
14152
|
-
else if (kids > 0) rows.push([t("children"), String(kids)]);
|
|
14153
|
-
}
|
|
14154
|
-
return rows;
|
|
14155
|
-
}
|
|
14156
|
-
/************************************************************
|
|
14157
15091
|
* Render popover body from the info rows of a node.
|
|
15092
|
+
*
|
|
15093
|
+
* The rows come from gobj_tree_model.js, so a node of the
|
|
15094
|
+
* browser yuno and a node of a backend one are read out with
|
|
15095
|
+
* the same words in the same order.
|
|
14158
15096
|
************************************************************/
|
|
14159
15097
|
function render_popover_body(gobj, node_data) {
|
|
14160
15098
|
let $body = gobj.priv.$popover_body;
|
|
14161
15099
|
if (!$body) return;
|
|
14162
|
-
$body.innerHTML =
|
|
14163
|
-
<div style="display:grid; grid-template-columns:
|
|
15100
|
+
$body.innerHTML = node_info_rows(node_data, t).map(([label, value]) => `
|
|
15101
|
+
<div style="display:grid; grid-template-columns: 96px 1fr; column-gap:10px; padding: 2px 0;">
|
|
14164
15102
|
<span style="color:var(--bulma-text-weak, #6B7280); font-weight:500;">${escapeHtml(String(label))}</span>
|
|
14165
15103
|
<span style="color:var(--bulma-text-strong, #1A1A1A); word-break:break-all;">${escapeHtml(String(value))}</span>
|
|
14166
15104
|
</div>
|
|
@@ -14177,6 +15115,7 @@ function show_popover(gobj, node_data, client_x, client_y) {
|
|
|
14177
15115
|
let $title = priv.$popover_title;
|
|
14178
15116
|
let $container = gobj_read_attr(gobj, "$container");
|
|
14179
15117
|
if (!$popover || !$title || !$container) return;
|
|
15118
|
+
priv.popover_node = node_data;
|
|
14180
15119
|
$title.textContent = node_data.short_name || node_data.gclass + (node_data.name ? "^" + node_data.name : "");
|
|
14181
15120
|
$title.title = $title.textContent;
|
|
14182
15121
|
render_popover_body(gobj, node_data);
|
|
@@ -14207,6 +15146,7 @@ function hide_popover(gobj) {
|
|
|
14207
15146
|
if (priv.$popover) priv.$popover.style.display = "none";
|
|
14208
15147
|
if (priv.$popover_body) priv.$popover_body.innerHTML = "";
|
|
14209
15148
|
if (priv.$popover_title) priv.$popover_title.textContent = "";
|
|
15149
|
+
priv.popover_node = null;
|
|
14210
15150
|
}
|
|
14211
15151
|
/************************************************************
|
|
14212
15152
|
* Generic tree layout engine.
|
|
@@ -14469,6 +15409,7 @@ var GobjLanesHLayout = class extends BaseLayout {
|
|
|
14469
15409
|
*
|
|
14470
15410
|
************************************************************/
|
|
14471
15411
|
function ac_refresh$3(gobj, event, kw, src) {
|
|
15412
|
+
save_view_state(gobj);
|
|
14472
15413
|
refresh_tree(gobj);
|
|
14473
15414
|
return 0;
|
|
14474
15415
|
}
|
|
@@ -14496,6 +15437,8 @@ function ac_change_layout(gobj, event, kw, src) {
|
|
|
14496
15437
|
gobj_write_str_attr(gobj, "layout", new_key);
|
|
14497
15438
|
if (priv.$layout_select && priv.$layout_select.value !== new_key) priv.$layout_select.value = new_key;
|
|
14498
15439
|
apply_layout(gobj);
|
|
15440
|
+
save_view_state(gobj);
|
|
15441
|
+
save_view_state(gobj);
|
|
14499
15442
|
refresh_tree(gobj);
|
|
14500
15443
|
return 0;
|
|
14501
15444
|
}
|
|
@@ -14515,6 +15458,7 @@ function ac_toggle_collapse(gobj, event, kw, src) {
|
|
|
14515
15458
|
priv.collapse_state[node_data.full_name] = "expanded";
|
|
14516
15459
|
for (let child_info of node_data.direct_children || []) if (child_info.has_grandchildren) priv.collapse_state[child_info.full_name] = "collapsed";
|
|
14517
15460
|
} else priv.collapse_state[node_data.full_name] = "collapsed";
|
|
15461
|
+
save_view_state(gobj);
|
|
14518
15462
|
refresh_tree(gobj, { anchor });
|
|
14519
15463
|
return 0;
|
|
14520
15464
|
}
|
|
@@ -14594,10 +15538,31 @@ function reanchor(gobj) {
|
|
|
14594
15538
|
************************************************************/
|
|
14595
15539
|
function ac_center_anchor$1(gobj, event, kw, src) {
|
|
14596
15540
|
let priv = gobj.priv;
|
|
15541
|
+
paint_anchor_mark(gobj);
|
|
15542
|
+
save_view_state(gobj);
|
|
14597
15543
|
if (priv.anchor_state === "on" && priv.anchor_id) yui_graph_center_on(priv.graph, priv.anchor_id);
|
|
14598
15544
|
return 0;
|
|
14599
15545
|
}
|
|
14600
15546
|
/************************************************************
|
|
15547
|
+
* Mark the card the camera is holding.
|
|
15548
|
+
*
|
|
15549
|
+
* A class on the card's own element and not a G6 node state:
|
|
15550
|
+
* G6 paints no state style on an html node.
|
|
15551
|
+
*
|
|
15552
|
+
* Re-applied after every rebuild, because the cards are new
|
|
15553
|
+
* DOM each time and the mark would otherwise last until the
|
|
15554
|
+
* next expand.
|
|
15555
|
+
************************************************************/
|
|
15556
|
+
function paint_anchor_mark(gobj) {
|
|
15557
|
+
let priv = gobj.priv;
|
|
15558
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
15559
|
+
if (!$container) return;
|
|
15560
|
+
for (let $card of $container.querySelectorAll(".GOBJ_CARD_ANCHORED")) $card.classList.remove("GOBJ_CARD_ANCHORED");
|
|
15561
|
+
if (priv.anchor_state !== "on" || !priv.anchor_name) return;
|
|
15562
|
+
let $it = $container.querySelector(".GOBJ_CARD[data-gobj-name=\"" + CSS.escape(priv.anchor_name) + "\"]");
|
|
15563
|
+
if ($it) $it.classList.add("GOBJ_CARD_ANCHORED");
|
|
15564
|
+
}
|
|
15565
|
+
/************************************************************
|
|
14601
15566
|
* Reading, or picking the node to anchor.
|
|
14602
15567
|
************************************************************/
|
|
14603
15568
|
function set_picking_mode(gobj, picking) {
|
|
@@ -14625,6 +15590,8 @@ function ac_toggle_anchor$1(gobj, event, kw, src) {
|
|
|
14625
15590
|
}
|
|
14626
15591
|
yui_graph_update_anchor(gobj_read_attr(gobj, "$container"), priv.anchor_state);
|
|
14627
15592
|
set_picking_mode(gobj, priv.anchor_state === "arming");
|
|
15593
|
+
paint_anchor_mark(gobj);
|
|
15594
|
+
save_view_state(gobj);
|
|
14628
15595
|
return 0;
|
|
14629
15596
|
}
|
|
14630
15597
|
/************************************************************
|
|
@@ -14708,6 +15675,7 @@ function ac_node_click$3(gobj, event, kw, src) {
|
|
|
14708
15675
|
function ac_show$6(gobj, event, kw, src) {
|
|
14709
15676
|
let priv = gobj.priv;
|
|
14710
15677
|
let graph = priv.graph;
|
|
15678
|
+
save_view_state(gobj);
|
|
14711
15679
|
refresh_tree(gobj);
|
|
14712
15680
|
if (graph) {
|
|
14713
15681
|
let $canvas = document.getElementById(priv.canvas_id);
|
|
@@ -14748,6 +15716,68 @@ function ac_hide$5(gobj, event, kw, src) {
|
|
|
14748
15716
|
hide_popover(gobj);
|
|
14749
15717
|
return 0;
|
|
14750
15718
|
}
|
|
15719
|
+
/************************************************************
|
|
15720
|
+
* Open the gclass of the node the popover is showing.
|
|
15721
|
+
*
|
|
15722
|
+
* One window at a time: opening a second gclass replaces the
|
|
15723
|
+
* first rather than stacking windows nobody closes.
|
|
15724
|
+
************************************************************/
|
|
15725
|
+
function ac_open_gclass(gobj, event, kw, src) {
|
|
15726
|
+
let priv = gobj.priv;
|
|
15727
|
+
let node_data = priv.popover_node;
|
|
15728
|
+
if (!node_data || !node_data.gclass) {
|
|
15729
|
+
log_error(`${gobj_short_name(gobj)}: EV_OPEN_GCLASS with no node`);
|
|
15730
|
+
return -1;
|
|
15731
|
+
}
|
|
15732
|
+
close_gclass_view(priv.gclass_view);
|
|
15733
|
+
priv.gclass_view = null;
|
|
15734
|
+
priv.gclass_view = open_gclass_view(gobj, node_data.gclass, {
|
|
15735
|
+
title_prefix: gobj_yuno_name() || "",
|
|
15736
|
+
on_close: () => {
|
|
15737
|
+
if (gobj_is_destroying(gobj)) return;
|
|
15738
|
+
gobj_send_event(gobj, "EV_GCLASS_CLOSED", {}, gobj);
|
|
15739
|
+
}
|
|
15740
|
+
});
|
|
15741
|
+
return 0;
|
|
15742
|
+
}
|
|
15743
|
+
/************************************************************
|
|
15744
|
+
* The reader dismissed the gclass window (its own ✕).
|
|
15745
|
+
*
|
|
15746
|
+
* C_YUI_WINDOW.close_window() calls on_close and THEN destroys
|
|
15747
|
+
* itself, so here we only drop our reference -- destroying it
|
|
15748
|
+
* again would destroy it twice.
|
|
15749
|
+
************************************************************/
|
|
15750
|
+
function ac_gclass_closed(gobj, event, kw, src) {
|
|
15751
|
+
gobj.priv.gclass_view = null;
|
|
15752
|
+
return 0;
|
|
15753
|
+
}
|
|
15754
|
+
/************************************************************
|
|
15755
|
+
* The gclass viewer asked for a subtree.
|
|
15756
|
+
*
|
|
15757
|
+
* It cannot: the gclass description is a COMPLETE document,
|
|
15758
|
+
* it carries no `__collapsed__` stub, so nothing in it is
|
|
15759
|
+
* fetchable. The event is declared because the viewer is
|
|
15760
|
+
* hosted as a child and subscribes its host to everything it
|
|
15761
|
+
* publishes -- and it is logged, because arriving here means
|
|
15762
|
+
* the document was not the one we built.
|
|
15763
|
+
************************************************************/
|
|
15764
|
+
function ac_gclass_expand_path(gobj, event, kw, src) {
|
|
15765
|
+
log_error(`${gobj_short_name(gobj)}: EV_EXPAND_PATH on a complete document: ${kw && kw.path}`);
|
|
15766
|
+
return -1;
|
|
15767
|
+
}
|
|
15768
|
+
/************************************************************
|
|
15769
|
+
* The app switched language.
|
|
15770
|
+
*
|
|
15771
|
+
* A rebuild, not a re-translation: the words live inside the
|
|
15772
|
+
* innerHTML of G6 nodes, where refresh_language() cannot go.
|
|
15773
|
+
************************************************************/
|
|
15774
|
+
function ac_language_changed$3(gobj, event, kw, src) {
|
|
15775
|
+
let $container = gobj_read_attr(gobj, "$container");
|
|
15776
|
+
if ($container) refresh_language($container, t);
|
|
15777
|
+
hide_popover(gobj);
|
|
15778
|
+
refresh_tree(gobj);
|
|
15779
|
+
return 0;
|
|
15780
|
+
}
|
|
14751
15781
|
/***************************
|
|
14752
15782
|
* FSM
|
|
14753
15783
|
***************************/
|
|
@@ -14781,7 +15811,11 @@ function create_gclass$9(gclass_name) {
|
|
|
14781
15811
|
["EV_CENTER_ANCHOR", 0],
|
|
14782
15812
|
["EV_RESIZE", 0],
|
|
14783
15813
|
["EV_SHOW", 0],
|
|
14784
|
-
["EV_HIDE", 0]
|
|
15814
|
+
["EV_HIDE", 0],
|
|
15815
|
+
["EV_OPEN_GCLASS", 0],
|
|
15816
|
+
["EV_GCLASS_CLOSED", 0],
|
|
15817
|
+
["EV_EXPAND_PATH", 0],
|
|
15818
|
+
["EV_LANGUAGE_CHANGED", 0]
|
|
14785
15819
|
], [["ST_IDLE", [
|
|
14786
15820
|
[
|
|
14787
15821
|
"EV_REFRESH",
|
|
@@ -14862,6 +15896,26 @@ function create_gclass$9(gclass_name) {
|
|
|
14862
15896
|
"EV_HIDE",
|
|
14863
15897
|
ac_hide$5,
|
|
14864
15898
|
null
|
|
15899
|
+
],
|
|
15900
|
+
[
|
|
15901
|
+
"EV_OPEN_GCLASS",
|
|
15902
|
+
ac_open_gclass,
|
|
15903
|
+
null
|
|
15904
|
+
],
|
|
15905
|
+
[
|
|
15906
|
+
"EV_GCLASS_CLOSED",
|
|
15907
|
+
ac_gclass_closed,
|
|
15908
|
+
null
|
|
15909
|
+
],
|
|
15910
|
+
[
|
|
15911
|
+
"EV_EXPAND_PATH",
|
|
15912
|
+
ac_gclass_expand_path,
|
|
15913
|
+
null
|
|
15914
|
+
],
|
|
15915
|
+
[
|
|
15916
|
+
"EV_LANGUAGE_CHANGED",
|
|
15917
|
+
ac_language_changed$3,
|
|
15918
|
+
null
|
|
14865
15919
|
]
|
|
14866
15920
|
]]], gmt$9, 0, attrs_table$9, PRIVATE_DATA$9, 0, 0, 0, 0);
|
|
14867
15921
|
if (!__gclass__$9) return -1;
|
|
@@ -49026,7 +50080,7 @@ function configure_toolbar(gobj) {
|
|
|
49026
50080
|
{
|
|
49027
50081
|
id: "g6-icon-anchor",
|
|
49028
50082
|
value: "anchor",
|
|
49029
|
-
className: "EV_TOGGLE_ANCHOR" + (priv.anchor_state
|
|
50083
|
+
className: "EV_TOGGLE_ANCHOR" + (priv.anchor_state === "on" ? " pressed_state" : "") + (priv.anchor_state === "arming" ? " color_pending_state" : ""),
|
|
49030
50084
|
title: anchor_title(priv.anchor_state)
|
|
49031
50085
|
}
|
|
49032
50086
|
];
|
|
@@ -51935,15 +52989,15 @@ function refresh_minimap(gobj) {
|
|
|
51935
52989
|
* same three-way choice — it lived inline in the theme refresh and is
|
|
51936
52990
|
* shared now. Returns null for a node that carries no desc.
|
|
51937
52991
|
************************************************************/
|
|
51938
|
-
function node_innerHTML_of(nd, theme, highlight, selected) {
|
|
52992
|
+
function node_innerHTML_of(nd, theme, highlight, selected, anchored) {
|
|
51939
52993
|
if (!nd || !nd.data || !nd.data.desc) return null;
|
|
51940
52994
|
let desc = nd.data.desc;
|
|
51941
52995
|
let record = nd.data.record || {};
|
|
51942
52996
|
let label = node_label(desc, record);
|
|
51943
52997
|
switch (desc.node_treedb_type) {
|
|
51944
|
-
case "child": return build_chip_innerHTML(desc.color, theme, record.icon, label, record.id, highlight, selected);
|
|
51945
|
-
case "extended": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, true, record.id, highlight, selected);
|
|
51946
|
-
case "hierarchical": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, false, record.id, highlight, selected);
|
|
52998
|
+
case "child": return build_chip_innerHTML(desc.color, theme, record.icon, label, record.id, highlight, selected, anchored);
|
|
52999
|
+
case "extended": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, true, record.id, highlight, selected, anchored);
|
|
53000
|
+
case "hierarchical": return build_node_innerHTML(desc.color, theme, record.icon, label, desc.topic_name, false, record.id, highlight, selected, anchored);
|
|
51947
53001
|
}
|
|
51948
53002
|
return null;
|
|
51949
53003
|
}
|
|
@@ -51964,7 +53018,7 @@ function repaint_cards(gobj, ids) {
|
|
|
51964
53018
|
let selected = new Set(priv._selected_paint_ids || []);
|
|
51965
53019
|
let updates = [];
|
|
51966
53020
|
for (let id of ids) {
|
|
51967
|
-
let html = node_innerHTML_of(graph.getNodeData(id), priv.theme, focus.has(id), selected.has(id));
|
|
53021
|
+
let html = node_innerHTML_of(graph.getNodeData(id), priv.theme, focus.has(id), selected.has(id), priv.anchor_state === "on" && id === priv.anchor_id);
|
|
51968
53022
|
if (html !== null) updates.push({
|
|
51969
53023
|
id,
|
|
51970
53024
|
style: { innerHTML: html }
|
|
@@ -52038,6 +53092,23 @@ function refresh_default_edges_theme(gobj, theme) {
|
|
|
52038
53092
|
* it, which is also the order that reads correctly when only one
|
|
52039
53093
|
* of them is on.
|
|
52040
53094
|
************************************************************/
|
|
53095
|
+
/************************************************************
|
|
53096
|
+
* The mark of the card the CAMERA is holding.
|
|
53097
|
+
*
|
|
53098
|
+
* An outline and not another ring, and DASHED: the two rings
|
|
53099
|
+
* already mean things here -- amber is a find match or a
|
|
53100
|
+
* focused topic, blue is the selection -- and a third solid
|
|
53101
|
+
* ring would be a third thing to learn in the same channel.
|
|
53102
|
+
* A dash is a different channel, so it reads beside either of
|
|
53103
|
+
* them, and it says "provisional" the way a camera state is.
|
|
53104
|
+
*
|
|
53105
|
+
* The same dash the other two viewers draw: this is one
|
|
53106
|
+
* feature with one control, and it should not be three
|
|
53107
|
+
* drawings.
|
|
53108
|
+
************************************************************/
|
|
53109
|
+
function anchor_outline(anchored) {
|
|
53110
|
+
return anchored ? "outline: 3px dashed #fa8c16; outline-offset: 2px;" : "";
|
|
53111
|
+
}
|
|
52041
53112
|
function ring_shadow(highlight, selected, base_shadow) {
|
|
52042
53113
|
let rings = [];
|
|
52043
53114
|
if (highlight) rings.push(`0 0 0 4px ${HIGHLIGHT_HALO}`);
|
|
@@ -52052,7 +53123,7 @@ function ring_shadow(highlight, selected, base_shadow) {
|
|
|
52052
53123
|
* but lighter (1px border, no shadow, single line). The name is
|
|
52053
53124
|
* always legible (ellipsis + native title tooltip on overflow).
|
|
52054
53125
|
************************************************************/
|
|
52055
|
-
function build_chip_innerHTML(color, theme, icon, label, key, highlight, selected) {
|
|
53126
|
+
function build_chip_innerHTML(color, theme, icon, label, key, highlight, selected, anchored) {
|
|
52056
53127
|
let title = key || label;
|
|
52057
53128
|
let dark = theme === "dark";
|
|
52058
53129
|
let bg = dark ? `color-mix(in srgb, ${color} 30%, #2c3542)` : `color-mix(in srgb, ${color} 10%, ${dark ? "#1b2230" : "#ffffff"})`;
|
|
@@ -52065,13 +53136,14 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight, selecte
|
|
|
52065
53136
|
margin-right: 6px; flex: 0 0 auto;
|
|
52066
53137
|
"/>`;
|
|
52067
53138
|
return `
|
|
52068
|
-
<div title="${escapeHtml(title)}" style="
|
|
53139
|
+
<div class="TREEDB_CARD" title="${escapeHtml(title)}" style="
|
|
52069
53140
|
box-sizing: border-box;
|
|
52070
53141
|
width: 100%;
|
|
52071
53142
|
height: 100%;
|
|
52072
53143
|
background: ${bg};
|
|
52073
53144
|
border: ${highlight ? "3px" : "1px"} solid ${border};
|
|
52074
53145
|
${ring_shadow(highlight, selected, "")}
|
|
53146
|
+
${anchor_outline(anchored)}
|
|
52075
53147
|
border-radius: 8px;
|
|
52076
53148
|
color: ${text_color};
|
|
52077
53149
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
@@ -52097,7 +53169,7 @@ function build_chip_innerHTML(color, theme, icon, label, key, highlight, selecte
|
|
|
52097
53169
|
* colour is kept (per-topic differentiation) but softened via
|
|
52098
53170
|
* color-mix instead of a harsh saturated fill. Theme-aware.
|
|
52099
53171
|
************************************************************/
|
|
52100
|
-
function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key, highlight, selected) {
|
|
53172
|
+
function build_node_innerHTML(color, theme, icon, label, topic_name, structural, key, highlight, selected, anchored) {
|
|
52101
53173
|
let title = key || label;
|
|
52102
53174
|
let dark = theme === "dark";
|
|
52103
53175
|
let surface = dark ? "#1b2230" : "#ffffff";
|
|
@@ -52132,7 +53204,7 @@ function build_node_innerHTML(color, theme, icon, label, topic_name, structural,
|
|
|
52132
53204
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
52133
53205
|
">${escapeHtml(topic_name)}</div>`;
|
|
52134
53206
|
return `
|
|
52135
|
-
<div title="${escapeHtml(title)}" style="
|
|
53207
|
+
<div class="TREEDB_CARD" title="${escapeHtml(title)}" style="
|
|
52136
53208
|
box-sizing: border-box;
|
|
52137
53209
|
width: 100%;
|
|
52138
53210
|
height: 100%;
|
|
@@ -52140,6 +53212,7 @@ function build_node_innerHTML(color, theme, icon, label, topic_name, structural,
|
|
|
52140
53212
|
border: ${highlight ? "3px" : "1.5px"} ${border_style} ${border};
|
|
52141
53213
|
border-radius: 10px;
|
|
52142
53214
|
${ring_shadow(highlight, selected, shadow)}
|
|
53215
|
+
${anchor_outline(anchored)}
|
|
52143
53216
|
color: ${title_color};
|
|
52144
53217
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
52145
53218
|
display: flex;
|
|
@@ -53123,12 +54196,14 @@ function after_camera(gobj, moved) {
|
|
|
53123
54196
|
************************************************************/
|
|
53124
54197
|
function ac_toggle_anchor(gobj, event, kw, src) {
|
|
53125
54198
|
let priv = gobj.priv;
|
|
54199
|
+
let was = priv.anchor_id;
|
|
53126
54200
|
if (priv.anchor_state === "off") priv.anchor_state = "arming";
|
|
53127
54201
|
else {
|
|
53128
54202
|
priv.anchor_state = "off";
|
|
53129
54203
|
priv.anchor_id = "";
|
|
53130
54204
|
}
|
|
53131
54205
|
update_toolbar(gobj);
|
|
54206
|
+
if (was) repaint_cards(gobj, /* @__PURE__ */ new Set([was]));
|
|
53132
54207
|
return 0;
|
|
53133
54208
|
}
|
|
53134
54209
|
/************************************************************
|
|
@@ -53306,6 +54381,7 @@ function ac_node_click$2(gobj, event, kw, src) {
|
|
|
53306
54381
|
priv.anchor_id = node_id;
|
|
53307
54382
|
priv.anchor_state = "on";
|
|
53308
54383
|
update_toolbar(gobj);
|
|
54384
|
+
repaint_cards(gobj, /* @__PURE__ */ new Set([node_id]));
|
|
53309
54385
|
gobj_post_event(gobj, "EV_CENTER_ANCHOR", {}, gobj);
|
|
53310
54386
|
return 0;
|
|
53311
54387
|
}
|