@yuneta/gobj-ui 7.23.29 → 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 +950 -179
- package/dist/gobj-ui.es.js +951 -180
- package/package.json +1 -1
- package/src/c_yui_gobj_tree_js.js +435 -245
- 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/yui_gclass_view.js +277 -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;
|
|
@@ -13200,7 +13200,7 @@ function ac_json_item_clicked(gobj, event, kw, src) {
|
|
|
13200
13200
|
/************************************************************
|
|
13201
13201
|
* EV_LANGUAGE_CHANGED — re-translate chrome + re-render
|
|
13202
13202
|
************************************************************/
|
|
13203
|
-
function ac_language_changed$
|
|
13203
|
+
function ac_language_changed$4(gobj, event, kw, src) {
|
|
13204
13204
|
let priv = gobj.priv;
|
|
13205
13205
|
let $container = gobj_read_attr(gobj, "$container");
|
|
13206
13206
|
if ($container) {
|
|
@@ -13261,7 +13261,7 @@ function create_gclass$10(gclass_name) {
|
|
|
13261
13261
|
],
|
|
13262
13262
|
[
|
|
13263
13263
|
"EV_LANGUAGE_CHANGED",
|
|
13264
|
-
ac_language_changed$
|
|
13264
|
+
ac_language_changed$4,
|
|
13265
13265
|
null
|
|
13266
13266
|
],
|
|
13267
13267
|
[
|
|
@@ -13337,7 +13337,7 @@ function create_gclass$10(gclass_name) {
|
|
|
13337
13337
|
],
|
|
13338
13338
|
[
|
|
13339
13339
|
"EV_LANGUAGE_CHANGED",
|
|
13340
|
-
ac_language_changed$
|
|
13340
|
+
ac_language_changed$4,
|
|
13341
13341
|
null
|
|
13342
13342
|
],
|
|
13343
13343
|
[
|
|
@@ -13387,6 +13387,567 @@ function register_c_yui_json() {
|
|
|
13387
13387
|
return create_gclass$10(GCLASS_NAME$10);
|
|
13388
13388
|
}
|
|
13389
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
|
|
13390
13951
|
//#region src/c_yui_gobj_tree_js.js
|
|
13391
13952
|
/***********************************************************************
|
|
13392
13953
|
* c_yui_gobj_tree_js.js
|
|
@@ -13497,6 +14058,9 @@ var PRIVATE_DATA$9 = {
|
|
|
13497
14058
|
$popover: null,
|
|
13498
14059
|
$popover_title: null,
|
|
13499
14060
|
$popover_body: null,
|
|
14061
|
+
$popover_gclass: null,
|
|
14062
|
+
popover_node: null,
|
|
14063
|
+
gclass_view: null,
|
|
13500
14064
|
node_by_id: null,
|
|
13501
14065
|
collapse_state: null,
|
|
13502
14066
|
resize_observer: null,
|
|
@@ -13511,34 +14075,92 @@ var PRIVATE_DATA$9 = {
|
|
|
13511
14075
|
var __gclass__$9 = null;
|
|
13512
14076
|
var __layout_registered__ = false;
|
|
13513
14077
|
/***************************************************************
|
|
13514
|
-
* 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.
|
|
13515
14086
|
***************************************************************/
|
|
13516
14087
|
var ROLE_COLORS = {
|
|
13517
14088
|
yuno: {
|
|
13518
|
-
|
|
13519
|
-
|
|
14089
|
+
stroke: "#0284c7",
|
|
14090
|
+
accent: "#38bdf8"
|
|
13520
14091
|
},
|
|
13521
14092
|
service: {
|
|
13522
|
-
|
|
13523
|
-
|
|
14093
|
+
stroke: "#059669",
|
|
14094
|
+
accent: "#34d399"
|
|
13524
14095
|
},
|
|
13525
14096
|
pure: {
|
|
13526
|
-
|
|
13527
|
-
|
|
14097
|
+
stroke: "#d97706",
|
|
14098
|
+
accent: "#fbbf24"
|
|
13528
14099
|
},
|
|
13529
14100
|
volatil: {
|
|
13530
|
-
|
|
13531
|
-
|
|
14101
|
+
stroke: "#db2777",
|
|
14102
|
+
accent: "#f472b6"
|
|
13532
14103
|
},
|
|
13533
|
-
|
|
13534
|
-
|
|
13535
|
-
|
|
14104
|
+
child: {
|
|
14105
|
+
stroke: "#7c3aed",
|
|
14106
|
+
accent: "#a78bfa"
|
|
13536
14107
|
}
|
|
13537
14108
|
};
|
|
13538
|
-
|
|
13539
|
-
|
|
13540
|
-
|
|
13541
|
-
|
|
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
|
+
}
|
|
13542
14164
|
};
|
|
13543
14165
|
/***************************************************************
|
|
13544
14166
|
* Fixed node sizes per role (px).
|
|
@@ -13548,16 +14170,16 @@ var STATE_COLORS = {
|
|
|
13548
14170
|
var NODE_SIZES = {
|
|
13549
14171
|
full: {
|
|
13550
14172
|
yuno: {
|
|
13551
|
-
w:
|
|
13552
|
-
h:
|
|
14173
|
+
w: 260,
|
|
14174
|
+
h: 96
|
|
13553
14175
|
},
|
|
13554
14176
|
service: {
|
|
13555
|
-
w:
|
|
13556
|
-
h:
|
|
14177
|
+
w: 232,
|
|
14178
|
+
h: 92
|
|
13557
14179
|
},
|
|
13558
14180
|
child: {
|
|
13559
|
-
w:
|
|
13560
|
-
h:
|
|
14181
|
+
w: 196,
|
|
14182
|
+
h: 86
|
|
13561
14183
|
}
|
|
13562
14184
|
},
|
|
13563
14185
|
compact: {
|
|
@@ -13580,27 +14202,47 @@ var NODE_SIZES = {
|
|
|
13580
14202
|
***************************************************************/
|
|
13581
14203
|
var GT_FONT = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif";
|
|
13582
14204
|
/***************************************************************
|
|
13583
|
-
*
|
|
13584
|
-
*
|
|
13585
|
-
*
|
|
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.
|
|
13586
14212
|
***************************************************************/
|
|
13587
|
-
function role_card_style(
|
|
14213
|
+
function role_card_style(colors, dark) {
|
|
14214
|
+
let stroke = colors.stroke;
|
|
14215
|
+
let accent = colors.accent;
|
|
13588
14216
|
return {
|
|
13589
|
-
bg: dark ? `color-mix(in srgb, ${stroke}
|
|
13590
|
-
border: dark ?
|
|
13591
|
-
|
|
13592
|
-
|
|
13593
|
-
|
|
13594
|
-
|
|
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)"
|
|
13595
14225
|
};
|
|
13596
14226
|
}
|
|
13597
14227
|
/***************************************************************
|
|
13598
|
-
*
|
|
14228
|
+
* The pill colours of a node, for the theme in force.
|
|
13599
14229
|
***************************************************************/
|
|
13600
|
-
function
|
|
13601
|
-
|
|
13602
|
-
if (
|
|
13603
|
-
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
|
+
}
|
|
13604
14246
|
}
|
|
13605
14247
|
/***************************************************************
|
|
13606
14248
|
* Layouts registry.
|
|
@@ -13713,13 +14355,21 @@ function mt_create$9(gobj) {
|
|
|
13713
14355
|
***************************************************************/
|
|
13714
14356
|
function mt_start$9(gobj) {
|
|
13715
14357
|
restore_view_shape(gobj);
|
|
14358
|
+
let shell = yui_shell_of(gobj);
|
|
14359
|
+
if (shell) gobj_subscribe_event(shell, "EV_LANGUAGE_CHANGED", {}, gobj);
|
|
13716
14360
|
build_graph$2(gobj);
|
|
13717
14361
|
load_tree(gobj);
|
|
13718
14362
|
}
|
|
13719
14363
|
/***************************************************************
|
|
13720
14364
|
* Framework Method: Stop
|
|
13721
14365
|
***************************************************************/
|
|
13722
|
-
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
|
+
}
|
|
13723
14373
|
/***************************************************************
|
|
13724
14374
|
* Framework Method: Destroy
|
|
13725
14375
|
***************************************************************/
|
|
@@ -13745,6 +14395,8 @@ function mt_destroy$9(gobj) {
|
|
|
13745
14395
|
priv.$popover = null;
|
|
13746
14396
|
priv.$popover_title = null;
|
|
13747
14397
|
priv.$popover_body = null;
|
|
14398
|
+
priv.$popover_gclass = null;
|
|
14399
|
+
priv.popover_node = null;
|
|
13748
14400
|
priv.$layout_select = null;
|
|
13749
14401
|
priv.node_by_id = null;
|
|
13750
14402
|
priv.collapse_state = null;
|
|
@@ -13848,6 +14500,30 @@ function build_popover(gobj, $container) {
|
|
|
13848
14500
|
text-overflow: ellipsis;
|
|
13849
14501
|
`;
|
|
13850
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`);
|
|
13851
14527
|
let $close = document.createElement("button");
|
|
13852
14528
|
$close.type = "button";
|
|
13853
14529
|
$close.className = "GOBJ_TREE_POPOVER_CLOSE";
|
|
@@ -14032,22 +14708,20 @@ function apply_layout(gobj) {
|
|
|
14032
14708
|
});
|
|
14033
14709
|
}
|
|
14034
14710
|
/************************************************************
|
|
14035
|
-
*
|
|
14036
|
-
|
|
14037
|
-
|
|
14038
|
-
|
|
14039
|
-
|
|
14040
|
-
if (gobj_is_service(target_gobj)) return ROLE_COLORS.service;
|
|
14041
|
-
if (gobj_is_pure_child(target_gobj)) return ROLE_COLORS.pure;
|
|
14042
|
-
return ROLE_COLORS.default;
|
|
14043
|
-
}
|
|
14044
|
-
/************************************************************
|
|
14045
|
-
* 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.
|
|
14046
14716
|
************************************************************/
|
|
14047
|
-
function
|
|
14048
|
-
|
|
14049
|
-
|
|
14050
|
-
|
|
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
|
+
}
|
|
14051
14725
|
}
|
|
14052
14726
|
/************************************************************
|
|
14053
14727
|
* Generate a unique node id
|
|
@@ -14079,72 +14753,135 @@ function is_node_collapsed(gobj, full_name, num_children) {
|
|
|
14079
14753
|
************************************************************/
|
|
14080
14754
|
function render_toggle_html(node_id, collapsed, num_children, cs) {
|
|
14081
14755
|
let label = collapsed ? "+" + num_children : "−";
|
|
14756
|
+
let title = collapsed ? t("expand children") : t("collapse children");
|
|
14082
14757
|
return `<span
|
|
14083
14758
|
data-gobj-toggle="true"
|
|
14084
14759
|
data-node-id="${node_id}"
|
|
14085
|
-
title="${
|
|
14760
|
+
title="${escapeHtml(title)}"
|
|
14086
14761
|
style="
|
|
14087
14762
|
flex: 0 0 auto;
|
|
14088
14763
|
display: inline-flex;
|
|
14089
14764
|
align-items: center;
|
|
14090
14765
|
justify-content: center;
|
|
14091
|
-
min-width:
|
|
14092
|
-
height:
|
|
14093
|
-
padding: 0
|
|
14766
|
+
min-width: 24px;
|
|
14767
|
+
height: 18px;
|
|
14768
|
+
padding: 0 6px;
|
|
14094
14769
|
margin-left: 4px;
|
|
14095
|
-
background: ${cs.
|
|
14096
|
-
color:
|
|
14097
|
-
border: 1px solid ${cs.
|
|
14098
|
-
border-radius:
|
|
14770
|
+
background: ${cs.accent};
|
|
14771
|
+
color: #ffffff;
|
|
14772
|
+
border: 1px solid ${cs.accent};
|
|
14773
|
+
border-radius: 9px;
|
|
14099
14774
|
font-size: 10px;
|
|
14100
|
-
font-weight:
|
|
14775
|
+
font-weight: 700;
|
|
14101
14776
|
line-height: 1;
|
|
14102
14777
|
cursor: pointer;
|
|
14103
14778
|
user-select: none;
|
|
14104
14779
|
">${label}</span>`;
|
|
14105
14780
|
}
|
|
14106
14781
|
/************************************************************
|
|
14107
|
-
*
|
|
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.
|
|
14108
14844
|
************************************************************/
|
|
14109
|
-
function build_gobj_nodes(gobj,
|
|
14845
|
+
function build_gobj_nodes(gobj, d, nodes, edges, parent_id, compact, labels) {
|
|
14110
14846
|
let priv = gobj.priv;
|
|
14111
14847
|
let node_id = gen_node_id(gobj);
|
|
14112
|
-
let gclass_name = gobj_gclass_name(target_gobj) || "";
|
|
14113
|
-
let instance_name = gobj_name(target_gobj) || "";
|
|
14114
|
-
let state = gobj_current_state(target_gobj) || "";
|
|
14115
|
-
let full_name = gobj_full_name(target_gobj);
|
|
14116
|
-
let colors = get_role_colors(target_gobj, is_root);
|
|
14117
14848
|
let dark = yui_is_dark();
|
|
14118
|
-
let
|
|
14119
|
-
let
|
|
14120
|
-
let
|
|
14121
|
-
let
|
|
14122
|
-
let status_text = !running ? "stopped" : playing ? "playing" : "running";
|
|
14123
|
-
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);
|
|
14124
14853
|
let size = NODE_SIZES[compact ? "compact" : "full"][category];
|
|
14125
14854
|
let width = size.w;
|
|
14126
14855
|
let height = size.h;
|
|
14127
14856
|
let border_width = category === "yuno" || category === "service" ? 2 : 1;
|
|
14128
|
-
let
|
|
14857
|
+
let border_style = d.disabled ? "dashed" : "solid";
|
|
14858
|
+
let children = d.children || [];
|
|
14129
14859
|
let num_children = children.length;
|
|
14130
14860
|
let has_children = num_children > 0;
|
|
14131
|
-
let collapsed = has_children && is_node_collapsed(gobj,
|
|
14861
|
+
let collapsed = has_children && is_node_collapsed(gobj, d.fullname, num_children);
|
|
14132
14862
|
let direct_children = [];
|
|
14133
14863
|
for (let child of children) direct_children.push({
|
|
14134
|
-
full_name:
|
|
14135
|
-
has_grandchildren: (child.
|
|
14864
|
+
full_name: child.fullname,
|
|
14865
|
+
has_grandchildren: (child.children || []).length > 0
|
|
14136
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;
|
|
14137
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 : "");
|
|
14138
14875
|
let node_html;
|
|
14139
14876
|
if (compact) {
|
|
14140
|
-
let compact_label = escapeHtml(
|
|
14877
|
+
let compact_label = escapeHtml(card_title);
|
|
14141
14878
|
node_html = `
|
|
14142
|
-
<div class="GOBJ_CARD" data-gobj-name="${escapeHtml(
|
|
14879
|
+
<div class="GOBJ_CARD" data-gobj-name="${escapeHtml(d.fullname)}" style="
|
|
14143
14880
|
width: ${width}px;
|
|
14144
14881
|
height: ${height}px;
|
|
14145
14882
|
background: ${cs.bg};
|
|
14146
|
-
border: ${border_width}px
|
|
14147
|
-
border-left:
|
|
14883
|
+
border: ${border_width}px ${border_style} ${cs.border};
|
|
14884
|
+
border-left: 6px solid ${cs.accent};
|
|
14148
14885
|
border-radius: 7px;
|
|
14149
14886
|
box-shadow: ${cs.shadow};
|
|
14150
14887
|
font-family: ${GT_FONT};
|
|
@@ -14153,34 +14890,24 @@ function build_gobj_nodes(gobj, target_gobj, nodes, edges, parent_id, is_root, c
|
|
|
14153
14890
|
padding: 0 8px;
|
|
14154
14891
|
box-sizing: border-box;
|
|
14155
14892
|
overflow: hidden;
|
|
14893
|
+
opacity: ${d.disabled ? "0.72" : "1"};
|
|
14156
14894
|
cursor: pointer;
|
|
14157
14895
|
" title="${compact_label}">
|
|
14158
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>
|
|
14159
|
-
|
|
14160
|
-
flex: 0 0 auto;
|
|
14161
|
-
width: 8px; height: 8px; border-radius: 50%;
|
|
14162
|
-
background: ${state_color}; margin-left: 6px;
|
|
14163
|
-
"></span>
|
|
14897
|
+
${render_status_html(d, ss, labels, false)}
|
|
14164
14898
|
${toggle_html}
|
|
14165
14899
|
</div>`;
|
|
14166
14900
|
} else {
|
|
14167
|
-
let header_label = escapeHtml(
|
|
14168
|
-
let name_label = escapeHtml(
|
|
14169
|
-
let state_label = escapeHtml(state);
|
|
14170
|
-
let badges = [];
|
|
14171
|
-
if (is_root) badges.push("yuno");
|
|
14172
|
-
else {
|
|
14173
|
-
if (gobj_is_service(target_gobj)) badges.push("service");
|
|
14174
|
-
if (gobj_is_pure_child(target_gobj)) badges.push("pure");
|
|
14175
|
-
if (gobj_is_volatil(target_gobj)) badges.push("volatil");
|
|
14176
|
-
}
|
|
14177
|
-
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);
|
|
14178
14904
|
node_html = `
|
|
14179
|
-
<div class="GOBJ_CARD" data-gobj-name="${escapeHtml(
|
|
14905
|
+
<div class="GOBJ_CARD" data-gobj-name="${escapeHtml(d.fullname)}" style="
|
|
14180
14906
|
width: ${width}px;
|
|
14181
14907
|
height: ${height}px;
|
|
14182
14908
|
background: ${cs.bg};
|
|
14183
|
-
border: ${border_width}px
|
|
14909
|
+
border: ${border_width}px ${border_style} ${cs.border};
|
|
14910
|
+
border-left: 6px solid ${cs.accent};
|
|
14184
14911
|
border-radius: 9px;
|
|
14185
14912
|
box-shadow: ${cs.shadow};
|
|
14186
14913
|
overflow: hidden;
|
|
@@ -14189,48 +14916,29 @@ function build_gobj_nodes(gobj, target_gobj, nodes, edges, parent_id, is_root, c
|
|
|
14189
14916
|
display: flex;
|
|
14190
14917
|
flex-direction: column;
|
|
14191
14918
|
justify-content: center;
|
|
14192
|
-
padding:
|
|
14193
|
-
gap:
|
|
14919
|
+
padding: 6px 9px;
|
|
14920
|
+
gap: 2px;
|
|
14921
|
+
opacity: ${d.disabled ? "0.72" : "1"};
|
|
14194
14922
|
cursor: pointer;
|
|
14195
|
-
" title="${escapeHtml(
|
|
14923
|
+
" title="${escapeHtml(card_title)}">
|
|
14196
14924
|
<div style="flex:0 0 auto; display:flex; align-items:center; gap:6px; overflow:hidden;">
|
|
14197
|
-
<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>
|
|
14198
14926
|
${toggle_html}
|
|
14199
14927
|
</div>
|
|
14200
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>
|
|
14201
14929
|
<div style="flex:0 0 auto; display:flex; justify-content:space-between; align-items:center; gap:6px; overflow:hidden;">
|
|
14202
|
-
<span style="flex:1 1 auto; min-width:0;
|
|
14203
|
-
|
|
14204
|
-
<span style="width:7px; height:7px; border-radius:50%; background:${state_color};"></span>${state_label || status_text}
|
|
14205
|
-
</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)}
|
|
14206
14932
|
</div>
|
|
14933
|
+
<div style="flex:0 0 auto; overflow:hidden; white-space:nowrap; text-overflow:ellipsis;">${render_badges_html(d, cs, labels)}</div>
|
|
14207
14934
|
</div>`;
|
|
14208
14935
|
}
|
|
14209
|
-
let parent_gobj = gobj_parent(target_gobj);
|
|
14210
|
-
let node_data = {
|
|
14211
|
-
gclass: gclass_name,
|
|
14212
|
-
name: instance_name,
|
|
14213
|
-
short_name: gobj_short_name(target_gobj),
|
|
14214
|
-
full_name,
|
|
14215
|
-
parent_short_name: parent_gobj ? gobj_short_name(parent_gobj) : "",
|
|
14216
|
-
state,
|
|
14217
|
-
category,
|
|
14218
|
-
is_yuno: is_root,
|
|
14219
|
-
is_service: gobj_is_service(target_gobj),
|
|
14220
|
-
is_pure_child: gobj_is_pure_child(target_gobj),
|
|
14221
|
-
is_volatil: gobj_is_volatil(target_gobj),
|
|
14222
|
-
is_running: running,
|
|
14223
|
-
is_playing: playing,
|
|
14224
|
-
num_children,
|
|
14225
|
-
is_collapsed: collapsed,
|
|
14226
|
-
direct_children
|
|
14227
|
-
};
|
|
14228
14936
|
if (!priv.node_by_id) priv.node_by_id = {};
|
|
14229
|
-
priv.node_by_id[node_id] =
|
|
14937
|
+
priv.node_by_id[node_id] = d;
|
|
14230
14938
|
let node = {
|
|
14231
14939
|
id: node_id,
|
|
14232
14940
|
type: "html",
|
|
14233
|
-
data:
|
|
14941
|
+
data: d,
|
|
14234
14942
|
style: {
|
|
14235
14943
|
innerHTML: node_html,
|
|
14236
14944
|
size: [width, height],
|
|
@@ -14243,11 +14951,11 @@ function build_gobj_nodes(gobj, target_gobj, nodes, edges, parent_id, is_root, c
|
|
|
14243
14951
|
source: parent_id,
|
|
14244
14952
|
target: node_id,
|
|
14245
14953
|
style: {
|
|
14246
|
-
stroke: colors.
|
|
14247
|
-
lineWidth: 1
|
|
14954
|
+
stroke: colors.accent,
|
|
14955
|
+
lineWidth: 1.4
|
|
14248
14956
|
}
|
|
14249
14957
|
});
|
|
14250
|
-
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);
|
|
14251
14959
|
return node_id;
|
|
14252
14960
|
}
|
|
14253
14961
|
/************************************************************
|
|
@@ -14264,9 +14972,13 @@ function load_tree(gobj) {
|
|
|
14264
14972
|
let priv = gobj.priv;
|
|
14265
14973
|
let graph = priv.graph;
|
|
14266
14974
|
if (!graph) return;
|
|
14267
|
-
let
|
|
14268
|
-
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
|
+
}
|
|
14269
14980
|
let layout_cfg = get_layout_cfg(gobj_read_str_attr(gobj, "layout"));
|
|
14981
|
+
let labels = node_labels(t);
|
|
14270
14982
|
let anchor = priv.pending_anchor || null;
|
|
14271
14983
|
let preserve_view = !!priv.pending_preserve_view;
|
|
14272
14984
|
priv.pending_anchor = null;
|
|
@@ -14275,7 +14987,7 @@ function load_tree(gobj) {
|
|
|
14275
14987
|
priv.node_by_id = {};
|
|
14276
14988
|
let nodes = [];
|
|
14277
14989
|
let edges = [];
|
|
14278
|
-
build_gobj_nodes(gobj,
|
|
14990
|
+
build_gobj_nodes(gobj, root, nodes, edges, null, layout_cfg.compact, labels);
|
|
14279
14991
|
if (nodes.length > 0) {
|
|
14280
14992
|
let root_id = nodes[0].id;
|
|
14281
14993
|
graph.setData({
|
|
@@ -14376,46 +15088,17 @@ function refresh_tree(gobj, opts) {
|
|
|
14376
15088
|
}
|
|
14377
15089
|
}
|
|
14378
15090
|
/************************************************************
|
|
14379
|
-
* Rows to show in the popover for a given node data.
|
|
14380
|
-
*
|
|
14381
|
-
* This is the extension point: to surface a new attribute,
|
|
14382
|
-
* collect it into `node_data` in build_gobj_nodes(), then
|
|
14383
|
-
* append a `[label, value]` pair here.
|
|
14384
|
-
************************************************************/
|
|
14385
|
-
function get_node_info_rows(node_data) {
|
|
14386
|
-
let rows = [];
|
|
14387
|
-
rows.push([t("gclass"), node_data.gclass || "—"]);
|
|
14388
|
-
rows.push([t("name"), node_data.name || "—"]);
|
|
14389
|
-
if (node_data.full_name && node_data.full_name !== node_data.short_name) rows.push([t("full name"), node_data.full_name]);
|
|
14390
|
-
let role;
|
|
14391
|
-
if (node_data.is_yuno) role = "yuno";
|
|
14392
|
-
else if (node_data.is_service) role = "service";
|
|
14393
|
-
else if (node_data.is_volatil) role = "volatil child";
|
|
14394
|
-
else if (node_data.is_pure_child) role = "pure child";
|
|
14395
|
-
else role = "child";
|
|
14396
|
-
rows.push([t("role"), role]);
|
|
14397
|
-
let status;
|
|
14398
|
-
if (!node_data.is_running) status = "stopped";
|
|
14399
|
-
else if (node_data.is_playing) status = "running + playing";
|
|
14400
|
-
else status = "running";
|
|
14401
|
-
rows.push([t("status"), status]);
|
|
14402
|
-
rows.push([t("state"), node_data.state || "—"]);
|
|
14403
|
-
if (node_data.parent_short_name) rows.push([t("parent"), node_data.parent_short_name]);
|
|
14404
|
-
if (typeof node_data.num_children === "number") {
|
|
14405
|
-
let kids = node_data.num_children;
|
|
14406
|
-
if (kids > 0 && node_data.is_collapsed) rows.push([t("children"), kids + " " + t("(collapsed)")]);
|
|
14407
|
-
else if (kids > 0) rows.push([t("children"), String(kids)]);
|
|
14408
|
-
}
|
|
14409
|
-
return rows;
|
|
14410
|
-
}
|
|
14411
|
-
/************************************************************
|
|
14412
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.
|
|
14413
15096
|
************************************************************/
|
|
14414
15097
|
function render_popover_body(gobj, node_data) {
|
|
14415
15098
|
let $body = gobj.priv.$popover_body;
|
|
14416
15099
|
if (!$body) return;
|
|
14417
|
-
$body.innerHTML =
|
|
14418
|
-
<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;">
|
|
14419
15102
|
<span style="color:var(--bulma-text-weak, #6B7280); font-weight:500;">${escapeHtml(String(label))}</span>
|
|
14420
15103
|
<span style="color:var(--bulma-text-strong, #1A1A1A); word-break:break-all;">${escapeHtml(String(value))}</span>
|
|
14421
15104
|
</div>
|
|
@@ -14432,6 +15115,7 @@ function show_popover(gobj, node_data, client_x, client_y) {
|
|
|
14432
15115
|
let $title = priv.$popover_title;
|
|
14433
15116
|
let $container = gobj_read_attr(gobj, "$container");
|
|
14434
15117
|
if (!$popover || !$title || !$container) return;
|
|
15118
|
+
priv.popover_node = node_data;
|
|
14435
15119
|
$title.textContent = node_data.short_name || node_data.gclass + (node_data.name ? "^" + node_data.name : "");
|
|
14436
15120
|
$title.title = $title.textContent;
|
|
14437
15121
|
render_popover_body(gobj, node_data);
|
|
@@ -14462,6 +15146,7 @@ function hide_popover(gobj) {
|
|
|
14462
15146
|
if (priv.$popover) priv.$popover.style.display = "none";
|
|
14463
15147
|
if (priv.$popover_body) priv.$popover_body.innerHTML = "";
|
|
14464
15148
|
if (priv.$popover_title) priv.$popover_title.textContent = "";
|
|
15149
|
+
priv.popover_node = null;
|
|
14465
15150
|
}
|
|
14466
15151
|
/************************************************************
|
|
14467
15152
|
* Generic tree layout engine.
|
|
@@ -15031,6 +15716,68 @@ function ac_hide$5(gobj, event, kw, src) {
|
|
|
15031
15716
|
hide_popover(gobj);
|
|
15032
15717
|
return 0;
|
|
15033
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
|
+
}
|
|
15034
15781
|
/***************************
|
|
15035
15782
|
* FSM
|
|
15036
15783
|
***************************/
|
|
@@ -15064,7 +15811,11 @@ function create_gclass$9(gclass_name) {
|
|
|
15064
15811
|
["EV_CENTER_ANCHOR", 0],
|
|
15065
15812
|
["EV_RESIZE", 0],
|
|
15066
15813
|
["EV_SHOW", 0],
|
|
15067
|
-
["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]
|
|
15068
15819
|
], [["ST_IDLE", [
|
|
15069
15820
|
[
|
|
15070
15821
|
"EV_REFRESH",
|
|
@@ -15145,6 +15896,26 @@ function create_gclass$9(gclass_name) {
|
|
|
15145
15896
|
"EV_HIDE",
|
|
15146
15897
|
ac_hide$5,
|
|
15147
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
|
|
15148
15919
|
]
|
|
15149
15920
|
]]], gmt$9, 0, attrs_table$9, PRIVATE_DATA$9, 0, 0, 0, 0);
|
|
15150
15921
|
if (!__gclass__$9) return -1;
|