@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.cjs.js
CHANGED
|
@@ -3518,7 +3518,7 @@ function composed_title(gobj) {
|
|
|
3518
3518
|
* Below the mobile breakpoint the window is a full-screen
|
|
3519
3519
|
* sheet (no float / drag / resize).
|
|
3520
3520
|
************************************************************/
|
|
3521
|
-
function is_mobile$
|
|
3521
|
+
function is_mobile$4() {
|
|
3522
3522
|
return (window.innerWidth || document.documentElement.offsetWidth) <= MOBILE_MAX;
|
|
3523
3523
|
}
|
|
3524
3524
|
/************************************************************
|
|
@@ -3551,7 +3551,7 @@ function do_minimize(gobj) {
|
|
|
3551
3551
|
************************************************************/
|
|
3552
3552
|
function build_ui$15(gobj) {
|
|
3553
3553
|
ensure_window_style();
|
|
3554
|
-
let mobile = is_mobile$
|
|
3554
|
+
let mobile = is_mobile$4();
|
|
3555
3555
|
let header = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "header");
|
|
3556
3556
|
if ((0, _yuneta_gobj_js.is_gobj)(header)) header = (0, _yuneta_gobj_js.gobj_read_attr)(header, "$container");
|
|
3557
3557
|
if (!header) header = build_default_header(gobj);
|
|
@@ -3889,7 +3889,7 @@ function min$1(gobj) {
|
|
|
3889
3889
|
* handlers moving
|
|
3890
3890
|
************************************************************/
|
|
3891
3891
|
function mvStart(gobj, evt) {
|
|
3892
|
-
if (is_mobile$
|
|
3892
|
+
if (is_mobile$4()) return;
|
|
3893
3893
|
let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
|
|
3894
3894
|
let window_rect = $container.getBoundingClientRect();
|
|
3895
3895
|
let x = evt.screenX;
|
|
@@ -4048,7 +4048,7 @@ function do_center(gobj, x, y, width, height) {
|
|
|
4048
4048
|
function handleResize(gobj) {
|
|
4049
4049
|
let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
|
|
4050
4050
|
if (!$container) return;
|
|
4051
|
-
if (is_mobile$
|
|
4051
|
+
if (is_mobile$4()) {
|
|
4052
4052
|
$container.classList.add("is-mobile-sheet");
|
|
4053
4053
|
let vw = window.innerWidth || document.documentElement.offsetWidth;
|
|
4054
4054
|
let vh = window.innerHeight || document.documentElement.offsetHeight;
|
|
@@ -13205,7 +13205,7 @@ function ac_json_item_clicked(gobj, event, kw, src) {
|
|
|
13205
13205
|
/************************************************************
|
|
13206
13206
|
* EV_LANGUAGE_CHANGED — re-translate chrome + re-render
|
|
13207
13207
|
************************************************************/
|
|
13208
|
-
function ac_language_changed$
|
|
13208
|
+
function ac_language_changed$4(gobj, event, kw, src) {
|
|
13209
13209
|
let priv = gobj.priv;
|
|
13210
13210
|
let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
|
|
13211
13211
|
if ($container) {
|
|
@@ -13266,7 +13266,7 @@ function create_gclass$10(gclass_name) {
|
|
|
13266
13266
|
],
|
|
13267
13267
|
[
|
|
13268
13268
|
"EV_LANGUAGE_CHANGED",
|
|
13269
|
-
ac_language_changed$
|
|
13269
|
+
ac_language_changed$4,
|
|
13270
13270
|
null
|
|
13271
13271
|
],
|
|
13272
13272
|
[
|
|
@@ -13342,7 +13342,7 @@ function create_gclass$10(gclass_name) {
|
|
|
13342
13342
|
],
|
|
13343
13343
|
[
|
|
13344
13344
|
"EV_LANGUAGE_CHANGED",
|
|
13345
|
-
ac_language_changed$
|
|
13345
|
+
ac_language_changed$4,
|
|
13346
13346
|
null
|
|
13347
13347
|
],
|
|
13348
13348
|
[
|
|
@@ -13392,6 +13392,567 @@ function register_c_yui_json() {
|
|
|
13392
13392
|
return create_gclass$10(GCLASS_NAME$10);
|
|
13393
13393
|
}
|
|
13394
13394
|
//#endregion
|
|
13395
|
+
//#region src/gobj_tree_model.js
|
|
13396
|
+
/***********************************************************************
|
|
13397
|
+
* gobj_tree_model.js
|
|
13398
|
+
*
|
|
13399
|
+
* The gobj tree of a yuno, as plain data.
|
|
13400
|
+
*
|
|
13401
|
+
* The view (C_YUI_GOBJ_TREE_JS) draws NODE DESCRIPTORS, never
|
|
13402
|
+
* gobjs. There are two producers of a descriptor, because the
|
|
13403
|
+
* view has to show the gobjs of the BACKEND too:
|
|
13404
|
+
*
|
|
13405
|
+
* - describe_js_gobj() the gobj tree of THIS browser yuno
|
|
13406
|
+
* - describe_backend_tree() the answer of the backend command
|
|
13407
|
+
* `view-gobj-tree` of another yuno
|
|
13408
|
+
*
|
|
13409
|
+
* Both answer the SAME shape, and that shape is the backend's:
|
|
13410
|
+
* the field names are the ones `gobj2json()` writes in
|
|
13411
|
+
* `kernel/c/gobj-c/src/gobj.c` (fullname, shortname, gclass,
|
|
13412
|
+
* name, parent, state, running, playing, service, disabled,
|
|
13413
|
+
* volatil, bottom_gobj, gobj_flags, commands, gobj_trace_level).
|
|
13414
|
+
* Anything the browser runtime cannot answer is simply absent,
|
|
13415
|
+
* and every reader here treats absent as "not known", never as
|
|
13416
|
+
* false.
|
|
13417
|
+
*
|
|
13418
|
+
* Copyright (c) 2026, ArtGins.
|
|
13419
|
+
* All Rights Reserved.
|
|
13420
|
+
***********************************************************************/
|
|
13421
|
+
/************************************************************
|
|
13422
|
+
* The role of a node: "yuno" | "service" | "volatil" | "pure" | "child"
|
|
13423
|
+
************************************************************/
|
|
13424
|
+
function node_role(d) {
|
|
13425
|
+
if (!d) return "child";
|
|
13426
|
+
if (d.is_yuno) return "yuno";
|
|
13427
|
+
if (d.service) return "service";
|
|
13428
|
+
if (d.volatil) return "volatil";
|
|
13429
|
+
if (d.pure_child) return "pure";
|
|
13430
|
+
return "child";
|
|
13431
|
+
}
|
|
13432
|
+
/************************************************************
|
|
13433
|
+
* What the node is doing: "stopped" | "running" | "playing"
|
|
13434
|
+
************************************************************/
|
|
13435
|
+
function node_status(d) {
|
|
13436
|
+
if (!d || !d.running) return "stopped";
|
|
13437
|
+
return d.playing ? "playing" : "running";
|
|
13438
|
+
}
|
|
13439
|
+
/************************************************************
|
|
13440
|
+
* The i18n key of the status, which is NOT the status itself:
|
|
13441
|
+
* a gobj that runs and does not play is PAUSED to the reader,
|
|
13442
|
+
* and "running" alone never said which of the two it was.
|
|
13443
|
+
************************************************************/
|
|
13444
|
+
function node_status_key(d) {
|
|
13445
|
+
switch (node_status(d)) {
|
|
13446
|
+
case "playing": return "playing";
|
|
13447
|
+
case "running": return "running (paused)";
|
|
13448
|
+
default: return "stopped";
|
|
13449
|
+
}
|
|
13450
|
+
}
|
|
13451
|
+
/******************************
|
|
13452
|
+
* Producers
|
|
13453
|
+
******************************/
|
|
13454
|
+
/************************************************************
|
|
13455
|
+
* A descriptor of a LIVE gobj of this browser yuno, children
|
|
13456
|
+
* included (the whole subtree, depth-first).
|
|
13457
|
+
*
|
|
13458
|
+
* `is_root` marks the yuno itself: the JS runtime has no
|
|
13459
|
+
* gobj_flag_yuno test of its own.
|
|
13460
|
+
************************************************************/
|
|
13461
|
+
function describe_js_gobj(target_gobj, is_root) {
|
|
13462
|
+
if (!target_gobj) return null;
|
|
13463
|
+
let parent_gobj = (0, _yuneta_gobj_js.gobj_parent)(target_gobj);
|
|
13464
|
+
let bottom = (0, _yuneta_gobj_js.gobj_bottom_gobj)(target_gobj);
|
|
13465
|
+
let d = {
|
|
13466
|
+
gclass: (0, _yuneta_gobj_js.gobj_gclass_name)(target_gobj) || "",
|
|
13467
|
+
name: (0, _yuneta_gobj_js.gobj_name)(target_gobj) || "",
|
|
13468
|
+
shortname: (0, _yuneta_gobj_js.gobj_short_name)(target_gobj) || "",
|
|
13469
|
+
fullname: (0, _yuneta_gobj_js.gobj_full_name)(target_gobj) || "",
|
|
13470
|
+
parent: parent_gobj ? (0, _yuneta_gobj_js.gobj_short_name)(parent_gobj) || "" : "",
|
|
13471
|
+
state: (0, _yuneta_gobj_js.gobj_current_state)(target_gobj) || "",
|
|
13472
|
+
running: !!(0, _yuneta_gobj_js.gobj_is_running)(target_gobj),
|
|
13473
|
+
playing: !!(0, _yuneta_gobj_js.gobj_is_playing)(target_gobj),
|
|
13474
|
+
service: !!(0, _yuneta_gobj_js.gobj_is_service)(target_gobj),
|
|
13475
|
+
volatil: !!(0, _yuneta_gobj_js.gobj_is_volatil)(target_gobj),
|
|
13476
|
+
pure_child: !!(0, _yuneta_gobj_js.gobj_is_pure_child)(target_gobj),
|
|
13477
|
+
disabled: !!target_gobj.disabled,
|
|
13478
|
+
bottom_gobj: bottom ? (0, _yuneta_gobj_js.gobj_short_name)(bottom) || "" : "",
|
|
13479
|
+
commands: !!(target_gobj.gclass && target_gobj.gclass.command_table),
|
|
13480
|
+
is_yuno: !!is_root,
|
|
13481
|
+
children: []
|
|
13482
|
+
};
|
|
13483
|
+
let children = target_gobj.dl_children || [];
|
|
13484
|
+
for (let child of children) {
|
|
13485
|
+
let cd = describe_js_gobj(child, false);
|
|
13486
|
+
if (cd) d.children.push(cd);
|
|
13487
|
+
}
|
|
13488
|
+
return d;
|
|
13489
|
+
}
|
|
13490
|
+
/******************************
|
|
13491
|
+
* Readouts
|
|
13492
|
+
******************************/
|
|
13493
|
+
/************************************************************
|
|
13494
|
+
* The i18n key of a role. Two of the five roles are not the
|
|
13495
|
+
* word the reader wants ("pure" and "volatil" alone say
|
|
13496
|
+
* nothing), so the map is explicit.
|
|
13497
|
+
************************************************************/
|
|
13498
|
+
function role_i18n_key(role) {
|
|
13499
|
+
switch (role) {
|
|
13500
|
+
case "yuno": return "yuno";
|
|
13501
|
+
case "service": return "service";
|
|
13502
|
+
case "volatil": return "volatil child";
|
|
13503
|
+
case "pure": return "pure child";
|
|
13504
|
+
default: return "child";
|
|
13505
|
+
}
|
|
13506
|
+
}
|
|
13507
|
+
/************************************************************
|
|
13508
|
+
* The marks a node carries besides its role: what the reader
|
|
13509
|
+
* would otherwise have to open the popover to learn.
|
|
13510
|
+
*
|
|
13511
|
+
* Returns i18n KEYS, in a fixed order, so the card and the
|
|
13512
|
+
* popover cannot disagree about what a node is.
|
|
13513
|
+
************************************************************/
|
|
13514
|
+
function node_badge_keys(d) {
|
|
13515
|
+
let badges = [];
|
|
13516
|
+
if (!d) return badges;
|
|
13517
|
+
let role = node_role(d);
|
|
13518
|
+
if (role !== "child") badges.push(role_i18n_key(role));
|
|
13519
|
+
if (d.disabled) badges.push("disabled");
|
|
13520
|
+
if (d.bottom_gobj) badges.push("bottom");
|
|
13521
|
+
if (d.commands) badges.push("commands");
|
|
13522
|
+
return badges;
|
|
13523
|
+
}
|
|
13524
|
+
/************************************************************
|
|
13525
|
+
* The vocabulary of this view, translated.
|
|
13526
|
+
*
|
|
13527
|
+
* Written as literal t("...") calls and not as t(key): a key
|
|
13528
|
+
* reached through a variable is invisible to the locale
|
|
13529
|
+
* validator, which is how a fixed vocabulary ships in one
|
|
13530
|
+
* language and never changes it.
|
|
13531
|
+
************************************************************/
|
|
13532
|
+
function node_labels(t) {
|
|
13533
|
+
if (typeof t !== "function") t = (k) => k;
|
|
13534
|
+
return {
|
|
13535
|
+
role: {
|
|
13536
|
+
"yuno": t("yuno"),
|
|
13537
|
+
"service": t("service"),
|
|
13538
|
+
"volatil child": t("volatil child"),
|
|
13539
|
+
"pure child": t("pure child"),
|
|
13540
|
+
"child": t("child")
|
|
13541
|
+
},
|
|
13542
|
+
status: {
|
|
13543
|
+
"playing": t("playing"),
|
|
13544
|
+
"running (paused)": t("running (paused)"),
|
|
13545
|
+
"stopped": t("stopped")
|
|
13546
|
+
},
|
|
13547
|
+
status_short: {
|
|
13548
|
+
"playing": t("playing"),
|
|
13549
|
+
"running": t("running"),
|
|
13550
|
+
"stopped": t("stopped")
|
|
13551
|
+
},
|
|
13552
|
+
badge: {
|
|
13553
|
+
"yuno": t("yuno"),
|
|
13554
|
+
"service": t("service"),
|
|
13555
|
+
"volatil child": t("volatil child"),
|
|
13556
|
+
"pure child": t("pure child"),
|
|
13557
|
+
"disabled": t("disabled"),
|
|
13558
|
+
"bottom": t("bottom"),
|
|
13559
|
+
"commands": t("commands")
|
|
13560
|
+
},
|
|
13561
|
+
field: {
|
|
13562
|
+
gclass: t("gclass"),
|
|
13563
|
+
name: t("name"),
|
|
13564
|
+
full_name: t("full name"),
|
|
13565
|
+
role: t("role"),
|
|
13566
|
+
status: t("status"),
|
|
13567
|
+
state: t("state"),
|
|
13568
|
+
disabled: t("disabled"),
|
|
13569
|
+
parent: t("parent"),
|
|
13570
|
+
bottom_gobj: t("bottom gobj"),
|
|
13571
|
+
commands: t("commands"),
|
|
13572
|
+
traces: t("traces"),
|
|
13573
|
+
flags: t("flags"),
|
|
13574
|
+
children: t("children"),
|
|
13575
|
+
yes: t("yes"),
|
|
13576
|
+
collapsed: t("(collapsed)")
|
|
13577
|
+
}
|
|
13578
|
+
};
|
|
13579
|
+
}
|
|
13580
|
+
/************************************************************
|
|
13581
|
+
* The rows of the popover: [label, value], both translated.
|
|
13582
|
+
*
|
|
13583
|
+
* `t` is the app's translator -- the library has none of its
|
|
13584
|
+
* own. Values that are a fixed vocabulary (the role, the
|
|
13585
|
+
* status, yes) go through it too; free text does not.
|
|
13586
|
+
************************************************************/
|
|
13587
|
+
function node_info_rows(d, t) {
|
|
13588
|
+
let rows = [];
|
|
13589
|
+
if (!d) return rows;
|
|
13590
|
+
let L = node_labels(t);
|
|
13591
|
+
let f = L.field;
|
|
13592
|
+
rows.push([f.gclass, d.gclass || "—"]);
|
|
13593
|
+
rows.push([f.name, d.name || "—"]);
|
|
13594
|
+
if (d.fullname && d.fullname !== d.shortname) rows.push([f.full_name, d.fullname]);
|
|
13595
|
+
rows.push([f.role, L.role[role_i18n_key(node_role(d))]]);
|
|
13596
|
+
rows.push([f.status, L.status[node_status_key(d)]]);
|
|
13597
|
+
rows.push([f.state, d.state || "—"]);
|
|
13598
|
+
if (d.disabled) rows.push([f.disabled, f.yes]);
|
|
13599
|
+
if (d.parent) rows.push([f.parent, d.parent]);
|
|
13600
|
+
if (d.bottom_gobj) rows.push([f.bottom_gobj, d.bottom_gobj]);
|
|
13601
|
+
if (d.commands) rows.push([f.commands, f.yes]);
|
|
13602
|
+
if (Array.isArray(d.gobj_trace_level) && d.gobj_trace_level.length > 0) rows.push([f.traces, d.gobj_trace_level.join(", ")]);
|
|
13603
|
+
if (Array.isArray(d.gobj_flags) && d.gobj_flags.length > 0) rows.push([f.flags, d.gobj_flags.join(", ")]);
|
|
13604
|
+
let kids = Array.isArray(d.children) ? d.children.length : 0;
|
|
13605
|
+
if (kids > 0) rows.push([f.children, d.is_collapsed ? kids + " " + f.collapsed : String(kids)]);
|
|
13606
|
+
return rows;
|
|
13607
|
+
}
|
|
13608
|
+
//#endregion
|
|
13609
|
+
//#region src/gclass_describe.js
|
|
13610
|
+
/***********************************************************************
|
|
13611
|
+
* gclass_describe.js
|
|
13612
|
+
*
|
|
13613
|
+
* A gclass, as plain JSON, in the shape the BACKEND answers.
|
|
13614
|
+
*
|
|
13615
|
+
* There is no gclass viewer in the framework: the C kernel
|
|
13616
|
+
* answers `view-gclass` with `gclass2json()`
|
|
13617
|
+
* (`kernel/c/gobj-c/src/gobj.c`) and nothing draws it. This
|
|
13618
|
+
* module builds the same document for a gclass of the BROWSER
|
|
13619
|
+
* yuno, out of the gobj-js registry, so one viewer can show a
|
|
13620
|
+
* gclass of either side:
|
|
13621
|
+
*
|
|
13622
|
+
* {id, gcflag, attrs, commands, gclass_methods,
|
|
13623
|
+
* internal_methods, FSM: {events, states}}
|
|
13624
|
+
*
|
|
13625
|
+
* Two keys the backend does not carry are added at the end,
|
|
13626
|
+
* because the browser can answer them for free and the reader
|
|
13627
|
+
* asks them first: `instances` and `trace_levels`.
|
|
13628
|
+
*
|
|
13629
|
+
* Copyright (c) 2026, ArtGins.
|
|
13630
|
+
* All Rights Reserved.
|
|
13631
|
+
***********************************************************************/
|
|
13632
|
+
/***************************************************************
|
|
13633
|
+
* Bit tables
|
|
13634
|
+
***************************************************************/
|
|
13635
|
+
var DATA_TYPE_NAMES = {
|
|
13636
|
+
[_yuneta_gobj_js.data_type_t.DTP_STRING]: "DTP_STRING",
|
|
13637
|
+
[_yuneta_gobj_js.data_type_t.DTP_BOOLEAN]: "DTP_BOOLEAN",
|
|
13638
|
+
[_yuneta_gobj_js.data_type_t.DTP_INTEGER]: "DTP_INTEGER",
|
|
13639
|
+
[_yuneta_gobj_js.data_type_t.DTP_REAL]: "DTP_REAL",
|
|
13640
|
+
[_yuneta_gobj_js.data_type_t.DTP_LIST]: "DTP_LIST",
|
|
13641
|
+
[_yuneta_gobj_js.data_type_t.DTP_DICT]: "DTP_DICT",
|
|
13642
|
+
[_yuneta_gobj_js.data_type_t.DTP_JSON]: "DTP_JSON",
|
|
13643
|
+
[_yuneta_gobj_js.data_type_t.DTP_POINTER]: "DTP_POINTER"
|
|
13644
|
+
};
|
|
13645
|
+
/************************************************************
|
|
13646
|
+
* The names of the bits set in `bits`, in table order.
|
|
13647
|
+
************************************************************/
|
|
13648
|
+
function bits2names(table, bits) {
|
|
13649
|
+
let names = [];
|
|
13650
|
+
let value = Number(bits) || 0;
|
|
13651
|
+
for (let name of Object.keys(table)) if ((value & table[name]) !== 0) names.push(name);
|
|
13652
|
+
return names;
|
|
13653
|
+
}
|
|
13654
|
+
/************************************************************
|
|
13655
|
+
* The name of a data type. An unknown type is reported as
|
|
13656
|
+
* the number it is -- silently drawing "DTP_STRING" for a
|
|
13657
|
+
* type nobody registered would be worse than saying "17".
|
|
13658
|
+
************************************************************/
|
|
13659
|
+
function data_type_name(type) {
|
|
13660
|
+
return DATA_TYPE_NAMES[type] || String(type);
|
|
13661
|
+
}
|
|
13662
|
+
/************************************************************
|
|
13663
|
+
* One SDATA descriptor -> one row.
|
|
13664
|
+
*
|
|
13665
|
+
* A DTP_POINTER default is a live object (a DOM node, a
|
|
13666
|
+
* gobj): it is never printed, only named, because a viewer
|
|
13667
|
+
* that serializes it would either throw or dump the page.
|
|
13668
|
+
************************************************************/
|
|
13669
|
+
function sdatadesc2json(it) {
|
|
13670
|
+
let row = {
|
|
13671
|
+
id: it.name,
|
|
13672
|
+
type: data_type_name(it.type),
|
|
13673
|
+
flag: bits2names(_yuneta_gobj_js.sdata_flag_t, it.flag),
|
|
13674
|
+
description: it.description || ""
|
|
13675
|
+
};
|
|
13676
|
+
if (it.type === _yuneta_gobj_js.data_type_t.DTP_POINTER) row.default_value = it.default_value === null || it.default_value === void 0 ? null : "<pointer>";
|
|
13677
|
+
else row.default_value = it.default_value === void 0 ? null : it.default_value;
|
|
13678
|
+
return row;
|
|
13679
|
+
}
|
|
13680
|
+
/************************************************************
|
|
13681
|
+
* The attrs table of a gclass.
|
|
13682
|
+
************************************************************/
|
|
13683
|
+
function attrs2json(attrs_table) {
|
|
13684
|
+
let rows = [];
|
|
13685
|
+
if (!Array.isArray(attrs_table)) return rows;
|
|
13686
|
+
for (let it of attrs_table) {
|
|
13687
|
+
if (!it || !it.name) continue;
|
|
13688
|
+
rows.push(sdatadesc2json(it));
|
|
13689
|
+
}
|
|
13690
|
+
return rows;
|
|
13691
|
+
}
|
|
13692
|
+
/************************************************************
|
|
13693
|
+
* The command table of a gclass, with the parameters of
|
|
13694
|
+
* each command (the `schema` slot of SDATACM).
|
|
13695
|
+
************************************************************/
|
|
13696
|
+
function commands2json(command_table) {
|
|
13697
|
+
let rows = [];
|
|
13698
|
+
if (!Array.isArray(command_table)) return rows;
|
|
13699
|
+
for (let it of command_table) {
|
|
13700
|
+
if (!it || !it.name) continue;
|
|
13701
|
+
let row = {
|
|
13702
|
+
id: it.name,
|
|
13703
|
+
alias: it.alias || [],
|
|
13704
|
+
description: it.description || ""
|
|
13705
|
+
};
|
|
13706
|
+
if (Array.isArray(it.schema)) row.parameters = attrs2json(it.schema);
|
|
13707
|
+
rows.push(row);
|
|
13708
|
+
}
|
|
13709
|
+
return rows;
|
|
13710
|
+
}
|
|
13711
|
+
/************************************************************
|
|
13712
|
+
* The methods the gclass actually implements. A table of
|
|
13713
|
+
* every possible one, mostly empty, tells the reader nothing.
|
|
13714
|
+
************************************************************/
|
|
13715
|
+
function methods2json(mt) {
|
|
13716
|
+
let names = [];
|
|
13717
|
+
if (!mt || typeof mt !== "object") return names;
|
|
13718
|
+
for (let key of Object.keys(mt)) if (typeof mt[key] === "function") names.push(key);
|
|
13719
|
+
return names;
|
|
13720
|
+
}
|
|
13721
|
+
/************************************************************
|
|
13722
|
+
* The FSM: the events the gclass declares, and what each
|
|
13723
|
+
* state does with them.
|
|
13724
|
+
************************************************************/
|
|
13725
|
+
function fsm2json(gclass) {
|
|
13726
|
+
let events = [];
|
|
13727
|
+
for (let ev of gclass.dl_events || []) events.push({
|
|
13728
|
+
event_name: ev.event_name,
|
|
13729
|
+
event_flag: bits2names(_yuneta_gobj_js.event_flag_t, ev.event_flag)
|
|
13730
|
+
});
|
|
13731
|
+
let states = {};
|
|
13732
|
+
for (let st of gclass.dl_states || []) {
|
|
13733
|
+
let actions = [];
|
|
13734
|
+
for (let ac of st.dl_actions || []) actions.push({
|
|
13735
|
+
event_name: ac.event_name,
|
|
13736
|
+
action: ac.action ? ac.action.name || "action" : "",
|
|
13737
|
+
next_state: ac.next_state || ""
|
|
13738
|
+
});
|
|
13739
|
+
states[st.state_name] = actions;
|
|
13740
|
+
}
|
|
13741
|
+
return {
|
|
13742
|
+
events,
|
|
13743
|
+
states
|
|
13744
|
+
};
|
|
13745
|
+
}
|
|
13746
|
+
/************************************************************
|
|
13747
|
+
* The description of a gclass REGISTERED IN THIS BROWSER YUNO,
|
|
13748
|
+
* or null when the registry does not know that name.
|
|
13749
|
+
*
|
|
13750
|
+
* Null is the answer for a gclass of a BACKEND yuno too: the
|
|
13751
|
+
* browser registry carries the GUI gclasses and nothing else,
|
|
13752
|
+
* so the caller asks the backend `view-gclass` for those and
|
|
13753
|
+
* shows whatever it answers -- this same shape.
|
|
13754
|
+
************************************************************/
|
|
13755
|
+
function describe_local_gclass(gclass_name) {
|
|
13756
|
+
if (!gclass_name) return null;
|
|
13757
|
+
let gclass = (0, _yuneta_gobj_js.gclass_find_by_name)(gclass_name, false);
|
|
13758
|
+
if (!gclass) return null;
|
|
13759
|
+
return {
|
|
13760
|
+
id: gclass.gclass_name,
|
|
13761
|
+
gcflag: bits2names(_yuneta_gobj_js.gclass_flag_t, gclass.gclass_flag),
|
|
13762
|
+
attrs: attrs2json(gclass.attrs_table),
|
|
13763
|
+
commands: commands2json(gclass.command_table),
|
|
13764
|
+
gclass_methods: methods2json(gclass.gmt),
|
|
13765
|
+
internal_methods: methods2json(gclass.lmt),
|
|
13766
|
+
FSM: fsm2json(gclass),
|
|
13767
|
+
instances: Number(gclass.instances) || 0,
|
|
13768
|
+
trace_levels: Array.isArray(gclass.s_user_trace_level) ? gclass.s_user_trace_level : []
|
|
13769
|
+
};
|
|
13770
|
+
}
|
|
13771
|
+
//#endregion
|
|
13772
|
+
//#region src/yui_gclass_view.js
|
|
13773
|
+
/***********************************************************************
|
|
13774
|
+
* yui_gclass_view.js
|
|
13775
|
+
*
|
|
13776
|
+
* The gclass viewer: what a GClass IS, in a window.
|
|
13777
|
+
*
|
|
13778
|
+
* The framework had no such viewer. The C kernel answers
|
|
13779
|
+
* `view-gclass` with a full description (attrs, commands,
|
|
13780
|
+
* methods, FSM) and the only reader of it was a terminal.
|
|
13781
|
+
* This one draws that same document with C_YUI_JSON, so the
|
|
13782
|
+
* FSM of a gclass is a graph and its attrs table is a tree,
|
|
13783
|
+
* and it takes the document from either side:
|
|
13784
|
+
*
|
|
13785
|
+
* - a gclass of the browser yuno -> describe_local_gclass()
|
|
13786
|
+
* - a gclass of a backend yuno -> `opts.description`, the
|
|
13787
|
+
* answer of `view-gclass`
|
|
13788
|
+
*
|
|
13789
|
+
* One window at a time per host: the caller keeps the handle
|
|
13790
|
+
* and closes it, the way the treedb graph keeps its raw-JSON
|
|
13791
|
+
* viewer.
|
|
13792
|
+
*
|
|
13793
|
+
* Copyright (c) 2026, ArtGins.
|
|
13794
|
+
* All Rights Reserved.
|
|
13795
|
+
***********************************************************************/
|
|
13796
|
+
/************************************************************
|
|
13797
|
+
* Can this app show a gclass at all?
|
|
13798
|
+
*
|
|
13799
|
+
* The viewer is C_YUI_JSON, and an app registers only the
|
|
13800
|
+
* gclasses it mounts. The caller asks this BEFORE drawing a
|
|
13801
|
+
* button, so an app that does not carry the viewer shows no
|
|
13802
|
+
* control instead of a control that fails.
|
|
13803
|
+
************************************************************/
|
|
13804
|
+
function gclass_view_available() {
|
|
13805
|
+
return !!(0, _yuneta_gobj_js.gclass_find_by_name)("C_YUI_JSON", false);
|
|
13806
|
+
}
|
|
13807
|
+
/************************************************************
|
|
13808
|
+
* Open the viewer on `gclass_name`.
|
|
13809
|
+
*
|
|
13810
|
+
* opts:
|
|
13811
|
+
* description the document to draw. Omitted, it is
|
|
13812
|
+
* built from the browser registry.
|
|
13813
|
+
* title_prefix what the window says before the gclass
|
|
13814
|
+
* name (the yuno it belongs to).
|
|
13815
|
+
* on_close called when the reader dismisses it.
|
|
13816
|
+
*
|
|
13817
|
+
* Returns a handle for close_gclass_view(), or null (and a
|
|
13818
|
+
* logged reason) when there is nothing to show.
|
|
13819
|
+
************************************************************/
|
|
13820
|
+
function open_gclass_view(host, gclass_name, opts) {
|
|
13821
|
+
opts = opts || {};
|
|
13822
|
+
if (!host || !gclass_name) {
|
|
13823
|
+
(0, _yuneta_gobj_js.log_error)(`yui_gclass_view: open without a host or a gclass name`);
|
|
13824
|
+
return null;
|
|
13825
|
+
}
|
|
13826
|
+
let shell = yui_shell_of(host);
|
|
13827
|
+
if (!gclass_view_available()) {
|
|
13828
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(host)}: C_YUI_JSON not registered by the app`);
|
|
13829
|
+
yui_shell_show_error(shell, "gclass viewer unavailable", { t: i18next.t });
|
|
13830
|
+
return null;
|
|
13831
|
+
}
|
|
13832
|
+
let description = opts.description || describe_local_gclass(gclass_name);
|
|
13833
|
+
if (!description) {
|
|
13834
|
+
(0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(host)}: gclass not registered here: ${gclass_name}`);
|
|
13835
|
+
yui_shell_show_error(shell, "gclass not registered in this yuno", { t: i18next.t });
|
|
13836
|
+
return null;
|
|
13837
|
+
}
|
|
13838
|
+
let jv = (0, _yuneta_gobj_js.gobj_create_service)(`gclass-view-${(0, _yuneta_gobj_js.clean_name)((0, _yuneta_gobj_js.gobj_name)(host))}`, "C_YUI_JSON", {
|
|
13839
|
+
json_data: description,
|
|
13840
|
+
subscriber: null
|
|
13841
|
+
}, host);
|
|
13842
|
+
if (!jv) {
|
|
13843
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(host)}: cannot create the gclass viewer`);
|
|
13844
|
+
return null;
|
|
13845
|
+
}
|
|
13846
|
+
(0, _yuneta_gobj_js.gobj_start)(jv);
|
|
13847
|
+
let handle = {
|
|
13848
|
+
json_gobj: jv,
|
|
13849
|
+
win: null,
|
|
13850
|
+
modal: null
|
|
13851
|
+
};
|
|
13852
|
+
let $box = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(jv, "$container");
|
|
13853
|
+
let on_close = () => {
|
|
13854
|
+
if (typeof opts.on_close === "function") opts.on_close();
|
|
13855
|
+
};
|
|
13856
|
+
if (is_mobile$3()) {
|
|
13857
|
+
if (!shell) {
|
|
13858
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(host)}: no shell, cannot open the gclass sheet`);
|
|
13859
|
+
close_gclass_view(handle);
|
|
13860
|
+
return null;
|
|
13861
|
+
}
|
|
13862
|
+
handle.modal = yui_shell_show_modal(shell, $box, {
|
|
13863
|
+
dialog: true,
|
|
13864
|
+
logical_class: "GCLASS_VIEW_SHEET",
|
|
13865
|
+
title_prefix: opts.title_prefix || "",
|
|
13866
|
+
title: gclass_name,
|
|
13867
|
+
t: i18next.t,
|
|
13868
|
+
on_close
|
|
13869
|
+
});
|
|
13870
|
+
return handle;
|
|
13871
|
+
}
|
|
13872
|
+
let $win_parent = window_layer_of(host, shell);
|
|
13873
|
+
handle.win = (0, _yuneta_gobj_js.gobj_create_service)(`gclass-win-${(0, _yuneta_gobj_js.clean_name)((0, _yuneta_gobj_js.gobj_name)(host))}`, "C_YUI_WINDOW", {
|
|
13874
|
+
$parent: $win_parent,
|
|
13875
|
+
subscriber: null,
|
|
13876
|
+
modal: false,
|
|
13877
|
+
showMax: true,
|
|
13878
|
+
showFooter: false,
|
|
13879
|
+
resizable: true,
|
|
13880
|
+
center: true,
|
|
13881
|
+
auto_save_size_and_position: true,
|
|
13882
|
+
width: 720,
|
|
13883
|
+
height: 640,
|
|
13884
|
+
logical_class: "GCLASS_VIEW_WINDOW",
|
|
13885
|
+
title_prefix: opts.title_prefix || "",
|
|
13886
|
+
title: gclass_name,
|
|
13887
|
+
icon: "yi-code",
|
|
13888
|
+
body: $box,
|
|
13889
|
+
manager: (0, _yuneta_gobj_js.gobj_find_service)("__window_manager__", false) || null,
|
|
13890
|
+
on_close
|
|
13891
|
+
}, host);
|
|
13892
|
+
if (!handle.win) {
|
|
13893
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(host)}: cannot create the gclass window`);
|
|
13894
|
+
close_gclass_view(handle);
|
|
13895
|
+
return null;
|
|
13896
|
+
}
|
|
13897
|
+
(0, _yuneta_gobj_js.gobj_start)(handle.win);
|
|
13898
|
+
return handle;
|
|
13899
|
+
}
|
|
13900
|
+
/************************************************************
|
|
13901
|
+
* Close whatever open_gclass_view() opened. Safe on null and
|
|
13902
|
+
* on a handle whose window the reader already dismissed.
|
|
13903
|
+
************************************************************/
|
|
13904
|
+
function close_gclass_view(handle) {
|
|
13905
|
+
if (!handle) return;
|
|
13906
|
+
let jv = handle.json_gobj;
|
|
13907
|
+
let win = handle.win;
|
|
13908
|
+
let modal = handle.modal;
|
|
13909
|
+
handle.json_gobj = null;
|
|
13910
|
+
handle.win = null;
|
|
13911
|
+
handle.modal = null;
|
|
13912
|
+
if (win && (0, _yuneta_gobj_js.is_gobj)(win)) try {
|
|
13913
|
+
if ((0, _yuneta_gobj_js.gobj_is_running)(win)) (0, _yuneta_gobj_js.gobj_stop)(win);
|
|
13914
|
+
(0, _yuneta_gobj_js.gobj_destroy)(win);
|
|
13915
|
+
} catch (e) {
|
|
13916
|
+
(0, _yuneta_gobj_js.log_warning)(`yui_gclass_view: window already gone: ${e}`);
|
|
13917
|
+
}
|
|
13918
|
+
if (modal && typeof modal.close === "function") try {
|
|
13919
|
+
modal.close();
|
|
13920
|
+
} catch (e) {
|
|
13921
|
+
(0, _yuneta_gobj_js.log_warning)(`yui_gclass_view: modal already gone: ${e}`);
|
|
13922
|
+
}
|
|
13923
|
+
if (jv && (0, _yuneta_gobj_js.is_gobj)(jv)) try {
|
|
13924
|
+
if ((0, _yuneta_gobj_js.gobj_is_running)(jv)) (0, _yuneta_gobj_js.gobj_stop)(jv);
|
|
13925
|
+
(0, _yuneta_gobj_js.gobj_destroy)(jv);
|
|
13926
|
+
} catch (e) {
|
|
13927
|
+
(0, _yuneta_gobj_js.log_warning)(`yui_gclass_view: viewer already gone: ${e}`);
|
|
13928
|
+
}
|
|
13929
|
+
}
|
|
13930
|
+
/************************************************************
|
|
13931
|
+
* Where to mount the window: the SAME element the caller's
|
|
13932
|
+
* own window hangs from.
|
|
13933
|
+
*
|
|
13934
|
+
* Not a preference -- a requirement. z-index only orders
|
|
13935
|
+
* siblings of one stacking context, so a viewer opened from a
|
|
13936
|
+
* window on `body` and mounted in the shell's popup layer
|
|
13937
|
+
* lands BEHIND its opener whatever number it is given, and
|
|
13938
|
+
* the button reads as one that did nothing.
|
|
13939
|
+
************************************************************/
|
|
13940
|
+
function window_layer_of(host, shell) {
|
|
13941
|
+
let $c = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(host, "$container");
|
|
13942
|
+
if ($c && typeof $c.closest === "function") {
|
|
13943
|
+
let $win = $c.closest(".C_YUI_WINDOW");
|
|
13944
|
+
if ($win && $win.parentNode) return $win.parentNode;
|
|
13945
|
+
}
|
|
13946
|
+
return typeof document !== "undefined" && document.getElementById("top-layer") || shell && yui_shell_popup_layer(shell) || null;
|
|
13947
|
+
}
|
|
13948
|
+
/************************************************************
|
|
13949
|
+
* A window is not a sheet: below this width there is no room
|
|
13950
|
+
* for a title bar the reader can drag.
|
|
13951
|
+
************************************************************/
|
|
13952
|
+
function is_mobile$3() {
|
|
13953
|
+
return typeof window !== "undefined" && window.innerWidth <= 768;
|
|
13954
|
+
}
|
|
13955
|
+
//#endregion
|
|
13395
13956
|
//#region src/c_yui_gobj_tree_js.js
|
|
13396
13957
|
/***********************************************************************
|
|
13397
13958
|
* c_yui_gobj_tree_js.js
|
|
@@ -13502,6 +14063,9 @@ var PRIVATE_DATA$9 = {
|
|
|
13502
14063
|
$popover: null,
|
|
13503
14064
|
$popover_title: null,
|
|
13504
14065
|
$popover_body: null,
|
|
14066
|
+
$popover_gclass: null,
|
|
14067
|
+
popover_node: null,
|
|
14068
|
+
gclass_view: null,
|
|
13505
14069
|
node_by_id: null,
|
|
13506
14070
|
collapse_state: null,
|
|
13507
14071
|
resize_observer: null,
|
|
@@ -13516,34 +14080,92 @@ var PRIVATE_DATA$9 = {
|
|
|
13516
14080
|
var __gclass__$9 = null;
|
|
13517
14081
|
var __layout_registered__ = false;
|
|
13518
14082
|
/***************************************************************
|
|
13519
|
-
* Node
|
|
14083
|
+
* Node colours by role.
|
|
14084
|
+
*
|
|
14085
|
+
* Saturated hues, one family apart from the next, and the same
|
|
14086
|
+
* five everywhere the reader meets a gobj. `stroke` draws the
|
|
14087
|
+
* border and the edge; `accent` is the brighter sibling used
|
|
14088
|
+
* for the bar down the left of the card and the toggle badge,
|
|
14089
|
+
* so a card carries its colour twice at two intensities and is
|
|
14090
|
+
* readable at the zoom where the text is not.
|
|
13520
14091
|
***************************************************************/
|
|
13521
14092
|
var ROLE_COLORS = {
|
|
13522
14093
|
yuno: {
|
|
13523
|
-
|
|
13524
|
-
|
|
14094
|
+
stroke: "#0284c7",
|
|
14095
|
+
accent: "#38bdf8"
|
|
13525
14096
|
},
|
|
13526
14097
|
service: {
|
|
13527
|
-
|
|
13528
|
-
|
|
14098
|
+
stroke: "#059669",
|
|
14099
|
+
accent: "#34d399"
|
|
13529
14100
|
},
|
|
13530
14101
|
pure: {
|
|
13531
|
-
|
|
13532
|
-
|
|
14102
|
+
stroke: "#d97706",
|
|
14103
|
+
accent: "#fbbf24"
|
|
13533
14104
|
},
|
|
13534
14105
|
volatil: {
|
|
13535
|
-
|
|
13536
|
-
|
|
14106
|
+
stroke: "#db2777",
|
|
14107
|
+
accent: "#f472b6"
|
|
13537
14108
|
},
|
|
13538
|
-
|
|
13539
|
-
|
|
13540
|
-
|
|
14109
|
+
child: {
|
|
14110
|
+
stroke: "#7c3aed",
|
|
14111
|
+
accent: "#a78bfa"
|
|
13541
14112
|
}
|
|
13542
14113
|
};
|
|
13543
|
-
|
|
13544
|
-
|
|
13545
|
-
|
|
13546
|
-
|
|
14114
|
+
/***************************************************************
|
|
14115
|
+
* Status colours: what the gobj is DOING.
|
|
14116
|
+
*
|
|
14117
|
+
* A pill and not a word: three states of one gobj are told
|
|
14118
|
+
* apart across a whole tree by colour long before anybody
|
|
14119
|
+
* reads them, and the dot alone never said which was which.
|
|
14120
|
+
* `disabled` is a fourth entry and not a status -- a disabled
|
|
14121
|
+
* gobj is still stopped or running -- but it takes the pill,
|
|
14122
|
+
* because it is the fact that explains the other one.
|
|
14123
|
+
***************************************************************/
|
|
14124
|
+
var STATUS_COLORS = {
|
|
14125
|
+
light: {
|
|
14126
|
+
playing: {
|
|
14127
|
+
fg: "#065f46",
|
|
14128
|
+
bg: "#a7f3d0",
|
|
14129
|
+
dot: "#10b981"
|
|
14130
|
+
},
|
|
14131
|
+
running: {
|
|
14132
|
+
fg: "#92400e",
|
|
14133
|
+
bg: "#fde68a",
|
|
14134
|
+
dot: "#f59e0b"
|
|
14135
|
+
},
|
|
14136
|
+
stopped: {
|
|
14137
|
+
fg: "#991b1b",
|
|
14138
|
+
bg: "#fecaca",
|
|
14139
|
+
dot: "#ef4444"
|
|
14140
|
+
},
|
|
14141
|
+
disabled: {
|
|
14142
|
+
fg: "#334155",
|
|
14143
|
+
bg: "#e2e8f0",
|
|
14144
|
+
dot: "#94a3b8"
|
|
14145
|
+
}
|
|
14146
|
+
},
|
|
14147
|
+
dark: {
|
|
14148
|
+
playing: {
|
|
14149
|
+
fg: "#6ee7b7",
|
|
14150
|
+
bg: "rgba(16,185,129,0.28)",
|
|
14151
|
+
dot: "#34d399"
|
|
14152
|
+
},
|
|
14153
|
+
running: {
|
|
14154
|
+
fg: "#fcd34d",
|
|
14155
|
+
bg: "rgba(245,158,11,0.28)",
|
|
14156
|
+
dot: "#fbbf24"
|
|
14157
|
+
},
|
|
14158
|
+
stopped: {
|
|
14159
|
+
fg: "#fca5a5",
|
|
14160
|
+
bg: "rgba(239,68,68,0.28)",
|
|
14161
|
+
dot: "#f87171"
|
|
14162
|
+
},
|
|
14163
|
+
disabled: {
|
|
14164
|
+
fg: "#cbd5e1",
|
|
14165
|
+
bg: "rgba(148,163,184,0.28)",
|
|
14166
|
+
dot: "#94a3b8"
|
|
14167
|
+
}
|
|
14168
|
+
}
|
|
13547
14169
|
};
|
|
13548
14170
|
/***************************************************************
|
|
13549
14171
|
* Fixed node sizes per role (px).
|
|
@@ -13553,16 +14175,16 @@ var STATE_COLORS = {
|
|
|
13553
14175
|
var NODE_SIZES = {
|
|
13554
14176
|
full: {
|
|
13555
14177
|
yuno: {
|
|
13556
|
-
w:
|
|
13557
|
-
h:
|
|
14178
|
+
w: 260,
|
|
14179
|
+
h: 96
|
|
13558
14180
|
},
|
|
13559
14181
|
service: {
|
|
13560
|
-
w:
|
|
13561
|
-
h:
|
|
14182
|
+
w: 232,
|
|
14183
|
+
h: 92
|
|
13562
14184
|
},
|
|
13563
14185
|
child: {
|
|
13564
|
-
w:
|
|
13565
|
-
h:
|
|
14186
|
+
w: 196,
|
|
14187
|
+
h: 86
|
|
13566
14188
|
}
|
|
13567
14189
|
},
|
|
13568
14190
|
compact: {
|
|
@@ -13585,27 +14207,47 @@ var NODE_SIZES = {
|
|
|
13585
14207
|
***************************************************************/
|
|
13586
14208
|
var GT_FONT = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif";
|
|
13587
14209
|
/***************************************************************
|
|
13588
|
-
*
|
|
13589
|
-
*
|
|
13590
|
-
*
|
|
14210
|
+
* Theme-aware card palette derived from the role colour.
|
|
14211
|
+
*
|
|
14212
|
+
* Brighter than a tint: at 9% of the role colour every card
|
|
14213
|
+
* was the same near-white rectangle and the tree read as a
|
|
14214
|
+
* wireframe. The fill carries enough of the hue to be named
|
|
14215
|
+
* from across the canvas, and the border and the accent bar
|
|
14216
|
+
* carry the rest.
|
|
13591
14217
|
***************************************************************/
|
|
13592
|
-
function role_card_style(
|
|
14218
|
+
function role_card_style(colors, dark) {
|
|
14219
|
+
let stroke = colors.stroke;
|
|
14220
|
+
let accent = colors.accent;
|
|
13593
14221
|
return {
|
|
13594
|
-
bg: dark ? `color-mix(in srgb, ${stroke}
|
|
13595
|
-
border: dark ?
|
|
13596
|
-
|
|
13597
|
-
|
|
13598
|
-
|
|
13599
|
-
|
|
14222
|
+
bg: dark ? `color-mix(in srgb, ${stroke} 34%, #253044)` : `color-mix(in srgb, ${stroke} 13%, #ffffff)`,
|
|
14223
|
+
border: dark ? accent : stroke,
|
|
14224
|
+
accent: dark ? accent : stroke,
|
|
14225
|
+
title: dark ? "#f1f5f9" : "#0f172a",
|
|
14226
|
+
sub: dark ? "#a8b3c2" : "#475569",
|
|
14227
|
+
tagbg: dark ? `color-mix(in srgb, ${stroke} 48%, #253044)` : `color-mix(in srgb, ${stroke} 20%, #ffffff)`,
|
|
14228
|
+
tagfg: dark ? "#f1f5f9" : stroke,
|
|
14229
|
+
shadow: dark ? "0 2px 6px rgba(0,0,0,0.50)" : "0 2px 5px rgba(15,23,42,0.16)"
|
|
13600
14230
|
};
|
|
13601
14231
|
}
|
|
13602
14232
|
/***************************************************************
|
|
13603
|
-
*
|
|
14233
|
+
* The pill colours of a node, for the theme in force.
|
|
13604
14234
|
***************************************************************/
|
|
13605
|
-
function
|
|
13606
|
-
|
|
13607
|
-
if (
|
|
13608
|
-
return
|
|
14235
|
+
function status_card_style(d, dark) {
|
|
14236
|
+
let table = dark ? STATUS_COLORS.dark : STATUS_COLORS.light;
|
|
14237
|
+
if (d.disabled) return table.disabled;
|
|
14238
|
+
return table[node_status(d)] || table.stopped;
|
|
14239
|
+
}
|
|
14240
|
+
/***************************************************************
|
|
14241
|
+
* Map a node to its visual size class. Services are drawn
|
|
14242
|
+
* bigger than plain children so the service boundary of the
|
|
14243
|
+
* hierarchy is visible before anything is read.
|
|
14244
|
+
***************************************************************/
|
|
14245
|
+
function get_node_category(d) {
|
|
14246
|
+
switch (node_role(d)) {
|
|
14247
|
+
case "yuno": return "yuno";
|
|
14248
|
+
case "service": return "service";
|
|
14249
|
+
default: return "child";
|
|
14250
|
+
}
|
|
13609
14251
|
}
|
|
13610
14252
|
/***************************************************************
|
|
13611
14253
|
* Layouts registry.
|
|
@@ -13718,13 +14360,21 @@ function mt_create$9(gobj) {
|
|
|
13718
14360
|
***************************************************************/
|
|
13719
14361
|
function mt_start$9(gobj) {
|
|
13720
14362
|
restore_view_shape(gobj);
|
|
14363
|
+
let shell = yui_shell_of(gobj);
|
|
14364
|
+
if (shell) (0, _yuneta_gobj_js.gobj_subscribe_event)(shell, "EV_LANGUAGE_CHANGED", {}, gobj);
|
|
13721
14365
|
build_graph$2(gobj);
|
|
13722
14366
|
load_tree(gobj);
|
|
13723
14367
|
}
|
|
13724
14368
|
/***************************************************************
|
|
13725
14369
|
* Framework Method: Stop
|
|
13726
14370
|
***************************************************************/
|
|
13727
|
-
function mt_stop$9(gobj) {
|
|
14371
|
+
function mt_stop$9(gobj) {
|
|
14372
|
+
let priv = gobj.priv;
|
|
14373
|
+
let shell = yui_shell_of(gobj);
|
|
14374
|
+
if (shell) (0, _yuneta_gobj_js.gobj_unsubscribe_event)(shell, "EV_LANGUAGE_CHANGED", {}, gobj);
|
|
14375
|
+
close_gclass_view(priv.gclass_view);
|
|
14376
|
+
priv.gclass_view = null;
|
|
14377
|
+
}
|
|
13728
14378
|
/***************************************************************
|
|
13729
14379
|
* Framework Method: Destroy
|
|
13730
14380
|
***************************************************************/
|
|
@@ -13750,6 +14400,8 @@ function mt_destroy$9(gobj) {
|
|
|
13750
14400
|
priv.$popover = null;
|
|
13751
14401
|
priv.$popover_title = null;
|
|
13752
14402
|
priv.$popover_body = null;
|
|
14403
|
+
priv.$popover_gclass = null;
|
|
14404
|
+
priv.popover_node = null;
|
|
13753
14405
|
priv.$layout_select = null;
|
|
13754
14406
|
priv.node_by_id = null;
|
|
13755
14407
|
priv.collapse_state = null;
|
|
@@ -13853,6 +14505,30 @@ function build_popover(gobj, $container) {
|
|
|
13853
14505
|
text-overflow: ellipsis;
|
|
13854
14506
|
`;
|
|
13855
14507
|
$header.appendChild($title);
|
|
14508
|
+
if (gclass_view_available()) {
|
|
14509
|
+
let $gclass = document.createElement("button");
|
|
14510
|
+
$gclass.type = "button";
|
|
14511
|
+
$gclass.className = "GOBJ_TREE_POPOVER_GCLASS button is-small";
|
|
14512
|
+
$gclass.style.cssText = `
|
|
14513
|
+
margin-right: 6px;
|
|
14514
|
+
padding: 0 8px;
|
|
14515
|
+
height: 22px;
|
|
14516
|
+
font-size: 11px;
|
|
14517
|
+
white-space: nowrap;
|
|
14518
|
+
`;
|
|
14519
|
+
$gclass.textContent = (0, i18next.t)("gclass");
|
|
14520
|
+
$gclass.setAttribute("i18n", "gclass");
|
|
14521
|
+
$gclass.title = (0, i18next.t)("view gclass");
|
|
14522
|
+
$gclass.setAttribute("data-i18n-title", "view gclass");
|
|
14523
|
+
$gclass.setAttribute("aria-label", (0, i18next.t)("view gclass"));
|
|
14524
|
+
$gclass.setAttribute("data-i18n-aria-label", "view gclass");
|
|
14525
|
+
$gclass.addEventListener("click", (evt) => {
|
|
14526
|
+
evt.stopPropagation();
|
|
14527
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_OPEN_GCLASS", {}, gobj);
|
|
14528
|
+
});
|
|
14529
|
+
$header.appendChild($gclass);
|
|
14530
|
+
priv.$popover_gclass = $gclass;
|
|
14531
|
+
} else (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no C_YUI_JSON registered, the gclass viewer is not offered`);
|
|
13856
14532
|
let $close = document.createElement("button");
|
|
13857
14533
|
$close.type = "button";
|
|
13858
14534
|
$close.className = "GOBJ_TREE_POPOVER_CLOSE";
|
|
@@ -14037,22 +14713,20 @@ function apply_layout(gobj) {
|
|
|
14037
14713
|
});
|
|
14038
14714
|
}
|
|
14039
14715
|
/************************************************************
|
|
14040
|
-
*
|
|
14041
|
-
|
|
14042
|
-
|
|
14043
|
-
|
|
14044
|
-
|
|
14045
|
-
if ((0, _yuneta_gobj_js.gobj_is_service)(target_gobj)) return ROLE_COLORS.service;
|
|
14046
|
-
if ((0, _yuneta_gobj_js.gobj_is_pure_child)(target_gobj)) return ROLE_COLORS.pure;
|
|
14047
|
-
return ROLE_COLORS.default;
|
|
14048
|
-
}
|
|
14049
|
-
/************************************************************
|
|
14050
|
-
* Resolve state color
|
|
14716
|
+
* The role colours of a node.
|
|
14717
|
+
*
|
|
14718
|
+
* Note the ORDER: volatil before service. A volatil gobj that
|
|
14719
|
+
* is also a service is drawn volatil, because that is the fact
|
|
14720
|
+
* that decides how long it lives.
|
|
14051
14721
|
************************************************************/
|
|
14052
|
-
function
|
|
14053
|
-
|
|
14054
|
-
|
|
14055
|
-
|
|
14722
|
+
function get_role_colors(d) {
|
|
14723
|
+
switch (node_role(d)) {
|
|
14724
|
+
case "yuno": return ROLE_COLORS.yuno;
|
|
14725
|
+
case "volatil": return ROLE_COLORS.volatil;
|
|
14726
|
+
case "service": return ROLE_COLORS.service;
|
|
14727
|
+
case "pure": return ROLE_COLORS.pure;
|
|
14728
|
+
default: return ROLE_COLORS.child;
|
|
14729
|
+
}
|
|
14056
14730
|
}
|
|
14057
14731
|
/************************************************************
|
|
14058
14732
|
* Generate a unique node id
|
|
@@ -14084,72 +14758,135 @@ function is_node_collapsed(gobj, full_name, num_children) {
|
|
|
14084
14758
|
************************************************************/
|
|
14085
14759
|
function render_toggle_html(node_id, collapsed, num_children, cs) {
|
|
14086
14760
|
let label = collapsed ? "+" + num_children : "−";
|
|
14761
|
+
let title = collapsed ? (0, i18next.t)("expand children") : (0, i18next.t)("collapse children");
|
|
14087
14762
|
return `<span
|
|
14088
14763
|
data-gobj-toggle="true"
|
|
14089
14764
|
data-node-id="${node_id}"
|
|
14090
|
-
title="${
|
|
14765
|
+
title="${(0, _yuneta_gobj_js.escapeHtml)(title)}"
|
|
14091
14766
|
style="
|
|
14092
14767
|
flex: 0 0 auto;
|
|
14093
14768
|
display: inline-flex;
|
|
14094
14769
|
align-items: center;
|
|
14095
14770
|
justify-content: center;
|
|
14096
|
-
min-width:
|
|
14097
|
-
height:
|
|
14098
|
-
padding: 0
|
|
14771
|
+
min-width: 24px;
|
|
14772
|
+
height: 18px;
|
|
14773
|
+
padding: 0 6px;
|
|
14099
14774
|
margin-left: 4px;
|
|
14100
|
-
background: ${cs.
|
|
14101
|
-
color:
|
|
14102
|
-
border: 1px solid ${cs.
|
|
14103
|
-
border-radius:
|
|
14775
|
+
background: ${cs.accent};
|
|
14776
|
+
color: #ffffff;
|
|
14777
|
+
border: 1px solid ${cs.accent};
|
|
14778
|
+
border-radius: 9px;
|
|
14104
14779
|
font-size: 10px;
|
|
14105
|
-
font-weight:
|
|
14780
|
+
font-weight: 700;
|
|
14106
14781
|
line-height: 1;
|
|
14107
14782
|
cursor: pointer;
|
|
14108
14783
|
user-select: none;
|
|
14109
14784
|
">${label}</span>`;
|
|
14110
14785
|
}
|
|
14111
14786
|
/************************************************************
|
|
14112
|
-
*
|
|
14787
|
+
* HTML for the status pill: what the gobj is DOING.
|
|
14788
|
+
*
|
|
14789
|
+
* A dot plus a word, and the word is dropped in the compact
|
|
14790
|
+
* card where there is no room for it -- the colour still
|
|
14791
|
+
* carries it, and the `title` says it in full either way.
|
|
14792
|
+
************************************************************/
|
|
14793
|
+
function render_status_html(d, ss, labels, with_label) {
|
|
14794
|
+
let full = d.disabled ? labels.field.disabled : labels.status[node_status_key(d)] || node_status_key(d);
|
|
14795
|
+
let label = d.disabled ? labels.field.disabled : labels.status_short[node_status(d)] || node_status(d);
|
|
14796
|
+
if (!with_label) return `<span title="${(0, _yuneta_gobj_js.escapeHtml)(full)}" style="
|
|
14797
|
+
flex: 0 0 auto;
|
|
14798
|
+
width: 10px; height: 10px; border-radius: 50%;
|
|
14799
|
+
background: ${ss.dot};
|
|
14800
|
+
box-shadow: 0 0 0 2px ${ss.bg};
|
|
14801
|
+
margin-left: 6px;
|
|
14802
|
+
"></span>`;
|
|
14803
|
+
return `<span title="${(0, _yuneta_gobj_js.escapeHtml)(full)}" style="
|
|
14804
|
+
flex: 0 0 auto;
|
|
14805
|
+
display: inline-flex;
|
|
14806
|
+
align-items: center;
|
|
14807
|
+
gap: 4px;
|
|
14808
|
+
padding: 1px 7px 1px 5px;
|
|
14809
|
+
background: ${ss.bg};
|
|
14810
|
+
color: ${ss.fg};
|
|
14811
|
+
border-radius: 9px;
|
|
14812
|
+
font-size: 10px;
|
|
14813
|
+
font-weight: 700;
|
|
14814
|
+
line-height: 1.45;
|
|
14815
|
+
white-space: nowrap;
|
|
14816
|
+
"><span style="width:7px; height:7px; border-radius:50%; background:${ss.dot};"></span>${(0, _yuneta_gobj_js.escapeHtml)(label)}</span>`;
|
|
14817
|
+
}
|
|
14818
|
+
/************************************************************
|
|
14819
|
+
* HTML for the badges: what the gobj IS, besides its colour.
|
|
14820
|
+
************************************************************/
|
|
14821
|
+
function render_badges_html(d, cs, labels) {
|
|
14822
|
+
let html = "";
|
|
14823
|
+
for (let key of node_badge_keys(d)) {
|
|
14824
|
+
let label = labels.badge[key] || key;
|
|
14825
|
+
html += `<span style="
|
|
14826
|
+
font-size: 10px;
|
|
14827
|
+
font-weight: 600;
|
|
14828
|
+
padding: 0 6px;
|
|
14829
|
+
margin-right: 4px;
|
|
14830
|
+
background: ${cs.tagbg};
|
|
14831
|
+
color: ${cs.tagfg};
|
|
14832
|
+
border: 1px solid ${cs.accent};
|
|
14833
|
+
border-radius: 9px;
|
|
14834
|
+
white-space: nowrap;
|
|
14835
|
+
">${(0, _yuneta_gobj_js.escapeHtml)(label)}</span>`;
|
|
14836
|
+
}
|
|
14837
|
+
return html;
|
|
14838
|
+
}
|
|
14839
|
+
/************************************************************
|
|
14840
|
+
* Recursively build nodes and edges from a NODE DESCRIPTOR
|
|
14841
|
+
* tree (gobj_tree_model.js), never from gobjs: the same
|
|
14842
|
+
* builder draws the browser yuno and a backend one.
|
|
14843
|
+
*
|
|
14844
|
+
* The descriptor is also what the popover reads, so the view
|
|
14845
|
+
* fields it needs are written onto it here (`full_name`,
|
|
14846
|
+
* `short_name`, `num_children`, `is_collapsed`,
|
|
14847
|
+
* `direct_children`) instead of copied into a second object
|
|
14848
|
+
* that can then disagree with the first.
|
|
14113
14849
|
************************************************************/
|
|
14114
|
-
function build_gobj_nodes(gobj,
|
|
14850
|
+
function build_gobj_nodes(gobj, d, nodes, edges, parent_id, compact, labels) {
|
|
14115
14851
|
let priv = gobj.priv;
|
|
14116
14852
|
let node_id = gen_node_id(gobj);
|
|
14117
|
-
let gclass_name = (0, _yuneta_gobj_js.gobj_gclass_name)(target_gobj) || "";
|
|
14118
|
-
let instance_name = (0, _yuneta_gobj_js.gobj_name)(target_gobj) || "";
|
|
14119
|
-
let state = (0, _yuneta_gobj_js.gobj_current_state)(target_gobj) || "";
|
|
14120
|
-
let full_name = (0, _yuneta_gobj_js.gobj_full_name)(target_gobj);
|
|
14121
|
-
let colors = get_role_colors(target_gobj, is_root);
|
|
14122
14853
|
let dark = yui_is_dark();
|
|
14123
|
-
let
|
|
14124
|
-
let
|
|
14125
|
-
let
|
|
14126
|
-
let
|
|
14127
|
-
let status_text = !running ? "stopped" : playing ? "playing" : "running";
|
|
14128
|
-
let category = get_node_category(target_gobj, is_root);
|
|
14854
|
+
let colors = get_role_colors(d);
|
|
14855
|
+
let cs = role_card_style(colors, dark);
|
|
14856
|
+
let ss = status_card_style(d, dark);
|
|
14857
|
+
let category = get_node_category(d);
|
|
14129
14858
|
let size = NODE_SIZES[compact ? "compact" : "full"][category];
|
|
14130
14859
|
let width = size.w;
|
|
14131
14860
|
let height = size.h;
|
|
14132
14861
|
let border_width = category === "yuno" || category === "service" ? 2 : 1;
|
|
14133
|
-
let
|
|
14862
|
+
let border_style = d.disabled ? "dashed" : "solid";
|
|
14863
|
+
let children = d.children || [];
|
|
14134
14864
|
let num_children = children.length;
|
|
14135
14865
|
let has_children = num_children > 0;
|
|
14136
|
-
let collapsed = has_children && is_node_collapsed(gobj,
|
|
14866
|
+
let collapsed = has_children && is_node_collapsed(gobj, d.fullname, num_children);
|
|
14137
14867
|
let direct_children = [];
|
|
14138
14868
|
for (let child of children) direct_children.push({
|
|
14139
|
-
full_name:
|
|
14140
|
-
has_grandchildren: (child.
|
|
14869
|
+
full_name: child.fullname,
|
|
14870
|
+
has_grandchildren: (child.children || []).length > 0
|
|
14141
14871
|
});
|
|
14872
|
+
d.full_name = d.fullname;
|
|
14873
|
+
d.short_name = d.shortname;
|
|
14874
|
+
d.num_children = num_children;
|
|
14875
|
+
d.is_collapsed = collapsed;
|
|
14876
|
+
d.direct_children = direct_children;
|
|
14877
|
+
d.node_id = node_id;
|
|
14142
14878
|
let toggle_html = has_children ? render_toggle_html(node_id, collapsed, num_children, cs) : "";
|
|
14879
|
+
let card_title = d.gclass + (d.name ? "^" + d.name : "");
|
|
14143
14880
|
let node_html;
|
|
14144
14881
|
if (compact) {
|
|
14145
|
-
let compact_label = (0, _yuneta_gobj_js.escapeHtml)(
|
|
14882
|
+
let compact_label = (0, _yuneta_gobj_js.escapeHtml)(card_title);
|
|
14146
14883
|
node_html = `
|
|
14147
|
-
<div class="GOBJ_CARD" data-gobj-name="${(0, _yuneta_gobj_js.escapeHtml)(
|
|
14884
|
+
<div class="GOBJ_CARD" data-gobj-name="${(0, _yuneta_gobj_js.escapeHtml)(d.fullname)}" style="
|
|
14148
14885
|
width: ${width}px;
|
|
14149
14886
|
height: ${height}px;
|
|
14150
14887
|
background: ${cs.bg};
|
|
14151
|
-
border: ${border_width}px
|
|
14152
|
-
border-left:
|
|
14888
|
+
border: ${border_width}px ${border_style} ${cs.border};
|
|
14889
|
+
border-left: 6px solid ${cs.accent};
|
|
14153
14890
|
border-radius: 7px;
|
|
14154
14891
|
box-shadow: ${cs.shadow};
|
|
14155
14892
|
font-family: ${GT_FONT};
|
|
@@ -14158,34 +14895,24 @@ function build_gobj_nodes(gobj, target_gobj, nodes, edges, parent_id, is_root, c
|
|
|
14158
14895
|
padding: 0 8px;
|
|
14159
14896
|
box-sizing: border-box;
|
|
14160
14897
|
overflow: hidden;
|
|
14898
|
+
opacity: ${d.disabled ? "0.72" : "1"};
|
|
14161
14899
|
cursor: pointer;
|
|
14162
14900
|
" title="${compact_label}">
|
|
14163
14901
|
<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>
|
|
14164
|
-
|
|
14165
|
-
flex: 0 0 auto;
|
|
14166
|
-
width: 8px; height: 8px; border-radius: 50%;
|
|
14167
|
-
background: ${state_color}; margin-left: 6px;
|
|
14168
|
-
"></span>
|
|
14902
|
+
${render_status_html(d, ss, labels, false)}
|
|
14169
14903
|
${toggle_html}
|
|
14170
14904
|
</div>`;
|
|
14171
14905
|
} else {
|
|
14172
|
-
let header_label = (0, _yuneta_gobj_js.escapeHtml)(
|
|
14173
|
-
let name_label = (0, _yuneta_gobj_js.escapeHtml)(
|
|
14174
|
-
let state_label = (0, _yuneta_gobj_js.escapeHtml)(state);
|
|
14175
|
-
let badges = [];
|
|
14176
|
-
if (is_root) badges.push("yuno");
|
|
14177
|
-
else {
|
|
14178
|
-
if ((0, _yuneta_gobj_js.gobj_is_service)(target_gobj)) badges.push("service");
|
|
14179
|
-
if ((0, _yuneta_gobj_js.gobj_is_pure_child)(target_gobj)) badges.push("pure");
|
|
14180
|
-
if ((0, _yuneta_gobj_js.gobj_is_volatil)(target_gobj)) badges.push("volatil");
|
|
14181
|
-
}
|
|
14182
|
-
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("");
|
|
14906
|
+
let header_label = (0, _yuneta_gobj_js.escapeHtml)(d.gclass);
|
|
14907
|
+
let name_label = (0, _yuneta_gobj_js.escapeHtml)(d.name);
|
|
14908
|
+
let state_label = (0, _yuneta_gobj_js.escapeHtml)(d.state);
|
|
14183
14909
|
node_html = `
|
|
14184
|
-
<div class="GOBJ_CARD" data-gobj-name="${(0, _yuneta_gobj_js.escapeHtml)(
|
|
14910
|
+
<div class="GOBJ_CARD" data-gobj-name="${(0, _yuneta_gobj_js.escapeHtml)(d.fullname)}" style="
|
|
14185
14911
|
width: ${width}px;
|
|
14186
14912
|
height: ${height}px;
|
|
14187
14913
|
background: ${cs.bg};
|
|
14188
|
-
border: ${border_width}px
|
|
14914
|
+
border: ${border_width}px ${border_style} ${cs.border};
|
|
14915
|
+
border-left: 6px solid ${cs.accent};
|
|
14189
14916
|
border-radius: 9px;
|
|
14190
14917
|
box-shadow: ${cs.shadow};
|
|
14191
14918
|
overflow: hidden;
|
|
@@ -14194,48 +14921,29 @@ function build_gobj_nodes(gobj, target_gobj, nodes, edges, parent_id, is_root, c
|
|
|
14194
14921
|
display: flex;
|
|
14195
14922
|
flex-direction: column;
|
|
14196
14923
|
justify-content: center;
|
|
14197
|
-
padding:
|
|
14198
|
-
gap:
|
|
14924
|
+
padding: 6px 9px;
|
|
14925
|
+
gap: 2px;
|
|
14926
|
+
opacity: ${d.disabled ? "0.72" : "1"};
|
|
14199
14927
|
cursor: pointer;
|
|
14200
|
-
" title="${(0, _yuneta_gobj_js.escapeHtml)(
|
|
14928
|
+
" title="${(0, _yuneta_gobj_js.escapeHtml)(card_title)}">
|
|
14201
14929
|
<div style="flex:0 0 auto; display:flex; align-items:center; gap:6px; overflow:hidden;">
|
|
14202
|
-
<span style="flex:1 1 auto; min-width:0; font-size:12px; line-height:1.25; font-weight:
|
|
14930
|
+
<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>
|
|
14203
14931
|
${toggle_html}
|
|
14204
14932
|
</div>
|
|
14205
14933
|
<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>
|
|
14206
14934
|
<div style="flex:0 0 auto; display:flex; justify-content:space-between; align-items:center; gap:6px; overflow:hidden;">
|
|
14207
|
-
<span style="flex:1 1 auto; min-width:0;
|
|
14208
|
-
|
|
14209
|
-
<span style="width:7px; height:7px; border-radius:50%; background:${state_color};"></span>${state_label || status_text}
|
|
14210
|
-
</span>
|
|
14935
|
+
<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>
|
|
14936
|
+
${render_status_html(d, ss, labels, true)}
|
|
14211
14937
|
</div>
|
|
14938
|
+
<div style="flex:0 0 auto; overflow:hidden; white-space:nowrap; text-overflow:ellipsis;">${render_badges_html(d, cs, labels)}</div>
|
|
14212
14939
|
</div>`;
|
|
14213
14940
|
}
|
|
14214
|
-
let parent_gobj = (0, _yuneta_gobj_js.gobj_parent)(target_gobj);
|
|
14215
|
-
let node_data = {
|
|
14216
|
-
gclass: gclass_name,
|
|
14217
|
-
name: instance_name,
|
|
14218
|
-
short_name: (0, _yuneta_gobj_js.gobj_short_name)(target_gobj),
|
|
14219
|
-
full_name,
|
|
14220
|
-
parent_short_name: parent_gobj ? (0, _yuneta_gobj_js.gobj_short_name)(parent_gobj) : "",
|
|
14221
|
-
state,
|
|
14222
|
-
category,
|
|
14223
|
-
is_yuno: is_root,
|
|
14224
|
-
is_service: (0, _yuneta_gobj_js.gobj_is_service)(target_gobj),
|
|
14225
|
-
is_pure_child: (0, _yuneta_gobj_js.gobj_is_pure_child)(target_gobj),
|
|
14226
|
-
is_volatil: (0, _yuneta_gobj_js.gobj_is_volatil)(target_gobj),
|
|
14227
|
-
is_running: running,
|
|
14228
|
-
is_playing: playing,
|
|
14229
|
-
num_children,
|
|
14230
|
-
is_collapsed: collapsed,
|
|
14231
|
-
direct_children
|
|
14232
|
-
};
|
|
14233
14941
|
if (!priv.node_by_id) priv.node_by_id = {};
|
|
14234
|
-
priv.node_by_id[node_id] =
|
|
14942
|
+
priv.node_by_id[node_id] = d;
|
|
14235
14943
|
let node = {
|
|
14236
14944
|
id: node_id,
|
|
14237
14945
|
type: "html",
|
|
14238
|
-
data:
|
|
14946
|
+
data: d,
|
|
14239
14947
|
style: {
|
|
14240
14948
|
innerHTML: node_html,
|
|
14241
14949
|
size: [width, height],
|
|
@@ -14248,11 +14956,11 @@ function build_gobj_nodes(gobj, target_gobj, nodes, edges, parent_id, is_root, c
|
|
|
14248
14956
|
source: parent_id,
|
|
14249
14957
|
target: node_id,
|
|
14250
14958
|
style: {
|
|
14251
|
-
stroke: colors.
|
|
14252
|
-
lineWidth: 1
|
|
14959
|
+
stroke: colors.accent,
|
|
14960
|
+
lineWidth: 1.4
|
|
14253
14961
|
}
|
|
14254
14962
|
});
|
|
14255
|
-
if (!collapsed) for (let child of children) build_gobj_nodes(gobj, child, nodes, edges, node_id,
|
|
14963
|
+
if (!collapsed) for (let child of children) build_gobj_nodes(gobj, child, nodes, edges, node_id, compact, labels);
|
|
14256
14964
|
return node_id;
|
|
14257
14965
|
}
|
|
14258
14966
|
/************************************************************
|
|
@@ -14269,9 +14977,13 @@ function load_tree(gobj) {
|
|
|
14269
14977
|
let priv = gobj.priv;
|
|
14270
14978
|
let graph = priv.graph;
|
|
14271
14979
|
if (!graph) return;
|
|
14272
|
-
let
|
|
14273
|
-
if (!
|
|
14980
|
+
let root = describe_js_gobj((0, _yuneta_gobj_js.gobj_yuno)(), true);
|
|
14981
|
+
if (!root) {
|
|
14982
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no yuno to draw`);
|
|
14983
|
+
return;
|
|
14984
|
+
}
|
|
14274
14985
|
let layout_cfg = get_layout_cfg((0, _yuneta_gobj_js.gobj_read_str_attr)(gobj, "layout"));
|
|
14986
|
+
let labels = node_labels(i18next.t);
|
|
14275
14987
|
let anchor = priv.pending_anchor || null;
|
|
14276
14988
|
let preserve_view = !!priv.pending_preserve_view;
|
|
14277
14989
|
priv.pending_anchor = null;
|
|
@@ -14280,7 +14992,7 @@ function load_tree(gobj) {
|
|
|
14280
14992
|
priv.node_by_id = {};
|
|
14281
14993
|
let nodes = [];
|
|
14282
14994
|
let edges = [];
|
|
14283
|
-
build_gobj_nodes(gobj,
|
|
14995
|
+
build_gobj_nodes(gobj, root, nodes, edges, null, layout_cfg.compact, labels);
|
|
14284
14996
|
if (nodes.length > 0) {
|
|
14285
14997
|
let root_id = nodes[0].id;
|
|
14286
14998
|
graph.setData({
|
|
@@ -14381,46 +15093,17 @@ function refresh_tree(gobj, opts) {
|
|
|
14381
15093
|
}
|
|
14382
15094
|
}
|
|
14383
15095
|
/************************************************************
|
|
14384
|
-
* Rows to show in the popover for a given node data.
|
|
14385
|
-
*
|
|
14386
|
-
* This is the extension point: to surface a new attribute,
|
|
14387
|
-
* collect it into `node_data` in build_gobj_nodes(), then
|
|
14388
|
-
* append a `[label, value]` pair here.
|
|
14389
|
-
************************************************************/
|
|
14390
|
-
function get_node_info_rows(node_data) {
|
|
14391
|
-
let rows = [];
|
|
14392
|
-
rows.push([(0, i18next.t)("gclass"), node_data.gclass || "—"]);
|
|
14393
|
-
rows.push([(0, i18next.t)("name"), node_data.name || "—"]);
|
|
14394
|
-
if (node_data.full_name && node_data.full_name !== node_data.short_name) rows.push([(0, i18next.t)("full name"), node_data.full_name]);
|
|
14395
|
-
let role;
|
|
14396
|
-
if (node_data.is_yuno) role = "yuno";
|
|
14397
|
-
else if (node_data.is_service) role = "service";
|
|
14398
|
-
else if (node_data.is_volatil) role = "volatil child";
|
|
14399
|
-
else if (node_data.is_pure_child) role = "pure child";
|
|
14400
|
-
else role = "child";
|
|
14401
|
-
rows.push([(0, i18next.t)("role"), role]);
|
|
14402
|
-
let status;
|
|
14403
|
-
if (!node_data.is_running) status = "stopped";
|
|
14404
|
-
else if (node_data.is_playing) status = "running + playing";
|
|
14405
|
-
else status = "running";
|
|
14406
|
-
rows.push([(0, i18next.t)("status"), status]);
|
|
14407
|
-
rows.push([(0, i18next.t)("state"), node_data.state || "—"]);
|
|
14408
|
-
if (node_data.parent_short_name) rows.push([(0, i18next.t)("parent"), node_data.parent_short_name]);
|
|
14409
|
-
if (typeof node_data.num_children === "number") {
|
|
14410
|
-
let kids = node_data.num_children;
|
|
14411
|
-
if (kids > 0 && node_data.is_collapsed) rows.push([(0, i18next.t)("children"), kids + " " + (0, i18next.t)("(collapsed)")]);
|
|
14412
|
-
else if (kids > 0) rows.push([(0, i18next.t)("children"), String(kids)]);
|
|
14413
|
-
}
|
|
14414
|
-
return rows;
|
|
14415
|
-
}
|
|
14416
|
-
/************************************************************
|
|
14417
15096
|
* Render popover body from the info rows of a node.
|
|
15097
|
+
*
|
|
15098
|
+
* The rows come from gobj_tree_model.js, so a node of the
|
|
15099
|
+
* browser yuno and a node of a backend one are read out with
|
|
15100
|
+
* the same words in the same order.
|
|
14418
15101
|
************************************************************/
|
|
14419
15102
|
function render_popover_body(gobj, node_data) {
|
|
14420
15103
|
let $body = gobj.priv.$popover_body;
|
|
14421
15104
|
if (!$body) return;
|
|
14422
|
-
$body.innerHTML =
|
|
14423
|
-
<div style="display:grid; grid-template-columns:
|
|
15105
|
+
$body.innerHTML = node_info_rows(node_data, i18next.t).map(([label, value]) => `
|
|
15106
|
+
<div style="display:grid; grid-template-columns: 96px 1fr; column-gap:10px; padding: 2px 0;">
|
|
14424
15107
|
<span style="color:var(--bulma-text-weak, #6B7280); font-weight:500;">${(0, _yuneta_gobj_js.escapeHtml)(String(label))}</span>
|
|
14425
15108
|
<span style="color:var(--bulma-text-strong, #1A1A1A); word-break:break-all;">${(0, _yuneta_gobj_js.escapeHtml)(String(value))}</span>
|
|
14426
15109
|
</div>
|
|
@@ -14437,6 +15120,7 @@ function show_popover(gobj, node_data, client_x, client_y) {
|
|
|
14437
15120
|
let $title = priv.$popover_title;
|
|
14438
15121
|
let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
|
|
14439
15122
|
if (!$popover || !$title || !$container) return;
|
|
15123
|
+
priv.popover_node = node_data;
|
|
14440
15124
|
$title.textContent = node_data.short_name || node_data.gclass + (node_data.name ? "^" + node_data.name : "");
|
|
14441
15125
|
$title.title = $title.textContent;
|
|
14442
15126
|
render_popover_body(gobj, node_data);
|
|
@@ -14467,6 +15151,7 @@ function hide_popover(gobj) {
|
|
|
14467
15151
|
if (priv.$popover) priv.$popover.style.display = "none";
|
|
14468
15152
|
if (priv.$popover_body) priv.$popover_body.innerHTML = "";
|
|
14469
15153
|
if (priv.$popover_title) priv.$popover_title.textContent = "";
|
|
15154
|
+
priv.popover_node = null;
|
|
14470
15155
|
}
|
|
14471
15156
|
/************************************************************
|
|
14472
15157
|
* Generic tree layout engine.
|
|
@@ -15036,6 +15721,68 @@ function ac_hide$5(gobj, event, kw, src) {
|
|
|
15036
15721
|
hide_popover(gobj);
|
|
15037
15722
|
return 0;
|
|
15038
15723
|
}
|
|
15724
|
+
/************************************************************
|
|
15725
|
+
* Open the gclass of the node the popover is showing.
|
|
15726
|
+
*
|
|
15727
|
+
* One window at a time: opening a second gclass replaces the
|
|
15728
|
+
* first rather than stacking windows nobody closes.
|
|
15729
|
+
************************************************************/
|
|
15730
|
+
function ac_open_gclass(gobj, event, kw, src) {
|
|
15731
|
+
let priv = gobj.priv;
|
|
15732
|
+
let node_data = priv.popover_node;
|
|
15733
|
+
if (!node_data || !node_data.gclass) {
|
|
15734
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: EV_OPEN_GCLASS with no node`);
|
|
15735
|
+
return -1;
|
|
15736
|
+
}
|
|
15737
|
+
close_gclass_view(priv.gclass_view);
|
|
15738
|
+
priv.gclass_view = null;
|
|
15739
|
+
priv.gclass_view = open_gclass_view(gobj, node_data.gclass, {
|
|
15740
|
+
title_prefix: (0, _yuneta_gobj_js.gobj_yuno_name)() || "",
|
|
15741
|
+
on_close: () => {
|
|
15742
|
+
if ((0, _yuneta_gobj_js.gobj_is_destroying)(gobj)) return;
|
|
15743
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_GCLASS_CLOSED", {}, gobj);
|
|
15744
|
+
}
|
|
15745
|
+
});
|
|
15746
|
+
return 0;
|
|
15747
|
+
}
|
|
15748
|
+
/************************************************************
|
|
15749
|
+
* The reader dismissed the gclass window (its own ✕).
|
|
15750
|
+
*
|
|
15751
|
+
* C_YUI_WINDOW.close_window() calls on_close and THEN destroys
|
|
15752
|
+
* itself, so here we only drop our reference -- destroying it
|
|
15753
|
+
* again would destroy it twice.
|
|
15754
|
+
************************************************************/
|
|
15755
|
+
function ac_gclass_closed(gobj, event, kw, src) {
|
|
15756
|
+
gobj.priv.gclass_view = null;
|
|
15757
|
+
return 0;
|
|
15758
|
+
}
|
|
15759
|
+
/************************************************************
|
|
15760
|
+
* The gclass viewer asked for a subtree.
|
|
15761
|
+
*
|
|
15762
|
+
* It cannot: the gclass description is a COMPLETE document,
|
|
15763
|
+
* it carries no `__collapsed__` stub, so nothing in it is
|
|
15764
|
+
* fetchable. The event is declared because the viewer is
|
|
15765
|
+
* hosted as a child and subscribes its host to everything it
|
|
15766
|
+
* publishes -- and it is logged, because arriving here means
|
|
15767
|
+
* the document was not the one we built.
|
|
15768
|
+
************************************************************/
|
|
15769
|
+
function ac_gclass_expand_path(gobj, event, kw, src) {
|
|
15770
|
+
(0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: EV_EXPAND_PATH on a complete document: ${kw && kw.path}`);
|
|
15771
|
+
return -1;
|
|
15772
|
+
}
|
|
15773
|
+
/************************************************************
|
|
15774
|
+
* The app switched language.
|
|
15775
|
+
*
|
|
15776
|
+
* A rebuild, not a re-translation: the words live inside the
|
|
15777
|
+
* innerHTML of G6 nodes, where refresh_language() cannot go.
|
|
15778
|
+
************************************************************/
|
|
15779
|
+
function ac_language_changed$3(gobj, event, kw, src) {
|
|
15780
|
+
let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
|
|
15781
|
+
if ($container) (0, _yuneta_gobj_js.refresh_language)($container, i18next.t);
|
|
15782
|
+
hide_popover(gobj);
|
|
15783
|
+
refresh_tree(gobj);
|
|
15784
|
+
return 0;
|
|
15785
|
+
}
|
|
15039
15786
|
/***************************
|
|
15040
15787
|
* FSM
|
|
15041
15788
|
***************************/
|
|
@@ -15069,7 +15816,11 @@ function create_gclass$9(gclass_name) {
|
|
|
15069
15816
|
["EV_CENTER_ANCHOR", 0],
|
|
15070
15817
|
["EV_RESIZE", 0],
|
|
15071
15818
|
["EV_SHOW", 0],
|
|
15072
|
-
["EV_HIDE", 0]
|
|
15819
|
+
["EV_HIDE", 0],
|
|
15820
|
+
["EV_OPEN_GCLASS", 0],
|
|
15821
|
+
["EV_GCLASS_CLOSED", 0],
|
|
15822
|
+
["EV_EXPAND_PATH", 0],
|
|
15823
|
+
["EV_LANGUAGE_CHANGED", 0]
|
|
15073
15824
|
], [["ST_IDLE", [
|
|
15074
15825
|
[
|
|
15075
15826
|
"EV_REFRESH",
|
|
@@ -15150,6 +15901,26 @@ function create_gclass$9(gclass_name) {
|
|
|
15150
15901
|
"EV_HIDE",
|
|
15151
15902
|
ac_hide$5,
|
|
15152
15903
|
null
|
|
15904
|
+
],
|
|
15905
|
+
[
|
|
15906
|
+
"EV_OPEN_GCLASS",
|
|
15907
|
+
ac_open_gclass,
|
|
15908
|
+
null
|
|
15909
|
+
],
|
|
15910
|
+
[
|
|
15911
|
+
"EV_GCLASS_CLOSED",
|
|
15912
|
+
ac_gclass_closed,
|
|
15913
|
+
null
|
|
15914
|
+
],
|
|
15915
|
+
[
|
|
15916
|
+
"EV_EXPAND_PATH",
|
|
15917
|
+
ac_gclass_expand_path,
|
|
15918
|
+
null
|
|
15919
|
+
],
|
|
15920
|
+
[
|
|
15921
|
+
"EV_LANGUAGE_CHANGED",
|
|
15922
|
+
ac_language_changed$3,
|
|
15923
|
+
null
|
|
15153
15924
|
]
|
|
15154
15925
|
]]], gmt$9, 0, attrs_table$9, PRIVATE_DATA$9, 0, 0, 0, 0);
|
|
15155
15926
|
if (!__gclass__$9) return -1;
|