@yuneta/gobj-ui 2.0.0 → 2.2.3

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.
Files changed (58) hide show
  1. package/README.md +25 -20
  2. package/dist/gobj-ui.cjs.js +2340 -528
  3. package/dist/gobj-ui.es.js +2340 -530
  4. package/index.js +32 -31
  5. package/package.json +6 -7
  6. package/{c_g6_nodes_tree.js → src/c_g6_nodes_tree.js} +6 -1
  7. package/{c_yui_form.js → src/c_yui_form.js} +1 -1
  8. package/{c_yui_gobj_tree_js.js → src/c_yui_gobj_tree_js.js} +1 -1
  9. package/{c_yui_json_graph.js → src/c_yui_json_graph.js} +1 -1
  10. package/{c_yui_main.js → src/c_yui_main.js} +3 -3
  11. package/{c_yui_map.js → src/c_yui_map.js} +6 -1
  12. package/{c_yui_nav.js → src/c_yui_nav.js} +134 -26
  13. package/{c_yui_pager.js → src/c_yui_pager.js} +1 -1
  14. package/{c_yui_routing.css → src/c_yui_routing.css} +1 -1
  15. package/{c_yui_routing.js → src/c_yui_routing.js} +1 -1
  16. package/{c_yui_shell.css → src/c_yui_shell.css} +110 -0
  17. package/{c_yui_shell.js → src/c_yui_shell.js} +175 -13
  18. package/{c_yui_tabs.js → src/c_yui_tabs.js} +1 -1
  19. package/{c_yui_treedb_graph.js → src/c_yui_treedb_graph.js} +35 -9
  20. package/{c_yui_treedb_topic_with_form.js → src/c_yui_treedb_topic_with_form.js} +1 -1
  21. package/{c_yui_treedb_topics.js → src/c_yui_treedb_topics.js} +36 -8
  22. package/{c_yui_uplot.js → src/c_yui_uplot.js} +1 -1
  23. package/{c_yui_window.js → src/c_yui_window.js} +242 -21
  24. package/src/c_yui_window_manager.js +602 -0
  25. package/{c_yui_wizard.js → src/c_yui_wizard.js} +1 -1
  26. package/{shell_modals.js → src/shell_modals.js} +53 -13
  27. package/src/tabulator.css +53 -0
  28. package/src/yui_dev.js +1484 -0
  29. package/{yui_icons.css → src/yui_icons.css} +5 -0
  30. package/src/yui_inputs.css +32 -0
  31. package/src/yui_inputs.js +71 -0
  32. package/vite.config.js +0 -131
  33. package/yui_dev.js +0 -583
  34. /package/{c_yui_main.css → src/c_yui_main.css} +0 -0
  35. /package/{c_yui_map.css → src/c_yui_map.css} +0 -0
  36. /package/{c_yui_treedb_topic_with_form.css → src/c_yui_treedb_topic_with_form.css} +0 -0
  37. /package/{g6_drag_canvas_touch.js → src/g6_drag_canvas_touch.js} +0 -0
  38. /package/{lib_graph.css → src/lib_graph.css} +0 -0
  39. /package/{lib_graph.js → src/lib_graph.js} +0 -0
  40. /package/{lib_icons.js → src/lib_icons.js} +0 -0
  41. /package/{lib_maplibre.js → src/lib_maplibre.js} +0 -0
  42. /package/{pager_helpers.js → src/pager_helpers.js} +0 -0
  43. /package/{pager_helpers.test.js → src/pager_helpers.test.js} +0 -0
  44. /package/{route_resolver.js → src/route_resolver.js} +0 -0
  45. /package/{route_resolver.test.js → src/route_resolver.test.js} +0 -0
  46. /package/{shell_focus_trap.js → src/shell_focus_trap.js} +0 -0
  47. /package/{shell_focus_trap.test.js → src/shell_focus_trap.test.js} +0 -0
  48. /package/{shell_show_on.js → src/shell_show_on.js} +0 -0
  49. /package/{shell_show_on.test.js → src/shell_show_on.test.js} +0 -0
  50. /package/{shell_toolbar_helpers.js → src/shell_toolbar_helpers.js} +0 -0
  51. /package/{shell_toolbar_helpers.test.js → src/shell_toolbar_helpers.test.js} +0 -0
  52. /package/{themes.js → src/themes.js} +0 -0
  53. /package/{wizard_helpers.js → src/wizard_helpers.js} +0 -0
  54. /package/{wizard_helpers.test.js → src/wizard_helpers.test.js} +0 -0
  55. /package/{ytable.css → src/ytable.css} +0 -0
  56. /package/{ytable.js → src/ytable.js} +0 -0
  57. /package/{yui_toolbar.css → src/yui_toolbar.css} +0 -0
  58. /package/{yui_toolbar.js → src/yui_toolbar.js} +0 -0
@@ -1,11 +1,11 @@
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_flag_t, getPositionRelativeToBody, gobj_command, gobj_create, gobj_create_service, gobj_current_state, gobj_default_service, gobj_destroy, gobj_find_child, gobj_find_service, gobj_full_name, gobj_gclass_name, gobj_has_attr, gobj_is_playing, gobj_is_pure_child, gobj_is_running, gobj_is_service, gobj_is_volatil, gobj_match_children, gobj_name, gobj_parent, 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_short_name, gobj_start, 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_pure_number, 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, list2options, log_error, log_warning, msg_iev_get_stack, parseBoolean, parseSVG, refresh_language, safeSrc, sdata_flag_t, sprintf, str_in_list, template_get_field_desc, trace_json, 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_flag_t, getPositionRelativeToBody, gobj_command, gobj_create, gobj_create_service, gobj_current_state, gobj_default_service, gobj_destroy, gobj_find_child, gobj_find_service, gobj_full_name, gobj_gclass_name, gobj_has_attr, gobj_is_playing, gobj_is_pure_child, gobj_is_running, gobj_is_service, gobj_is_volatil, gobj_match_children, gobj_name, gobj_parent, 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_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_pure_number, 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, list2options, log_error, log_warning, msg_iev_get_stack, parseBoolean, parseSVG, refresh_language, safeSrc, sdata_flag_t, set_log_callback, sprintf, str_in_list, template_get_field_desc, trace_json, 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
- import { JSONEditor } from "vanilla-jsoneditor";
4
3
  import TomSelect from "tom-select";
5
4
  import { TabulatorFull } from "tabulator-tables";
6
5
  import maplibregl from "maplibre-gl";
7
6
  import uPlot from "uplot";
8
7
  import { BaseLayout, CanvasEvent, Circle, CommonEvent, DragCanvas, EdgeEvent, ExtensionCategory, Graph, HistoryEvent, NodeEvent, Toolbar, register } from "@antv/g6";
8
+ import { JSONEditor } from "vanilla-jsoneditor";
9
9
  //#region \0rolldown/runtime.js
10
10
  var __create = Object.create;
11
11
  var __defProp = Object.defineProperty;
@@ -38,107 +38,907 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
38
38
  enumerable: true
39
39
  }) : target, mod));
40
40
  //#endregion
41
- //#region yui_dev.js
41
+ //#region src/yui_dev.js
42
42
  /***********************************************************************
43
43
  * ui_dev.js
44
44
  *
45
- * Development Tools
45
+ * Development Tools — yuno monitor / audit console
46
46
  *
47
- * Copyright (c) 2024, ArtGins.
47
+ * Copyright (c) 2024-2026, ArtGins.
48
48
  * All Rights Reserved.
49
49
  ***********************************************************************/
50
+ /***********************************************************************
51
+ * Traffic model (bounded ring buffer)
52
+ *
53
+ * Every inter-event message is kept as a lightweight record so view
54
+ * and filter changes re-render instantly from memory instead of
55
+ * losing history. Reopening the window repaints the buffer.
56
+ ***********************************************************************/
57
+ var TRAFFIC_MAX = 600;
58
+ var PERIODIC_THRESHOLD = 5;
59
+ var PERIODIC_RE = /PERIODIC|TIMEOUT|HEARTBEAT|PING/i;
60
+ var TRAFFIC_LOG = [];
61
+ var TRAFFIC_COUNTS = /* @__PURE__ */ new Map();
62
+ var SEARCH_TEXT = "";
63
+ var TRAFFIC_TS_FIELDS = {
64
+ "__t__": 1,
65
+ "__tm__": 1,
66
+ "tm": 1,
67
+ "t": 1,
68
+ "from_t": 1,
69
+ "to_t": 1,
70
+ "from_tm": 1,
71
+ "to_tm": 1,
72
+ "time": 1
73
+ };
74
+ var TRACE_DEFS = [
75
+ [
76
+ "trace_automata",
77
+ "Automata",
78
+ trace_automata
79
+ ],
80
+ [
81
+ "trace_creation",
82
+ "Creation",
83
+ trace_creation
84
+ ],
85
+ [
86
+ "trace_start_stop",
87
+ "Start/Stop",
88
+ trace_start_stop
89
+ ],
90
+ [
91
+ "trace_subscriptions",
92
+ "Subscriptions",
93
+ trace_subscriptions
94
+ ],
95
+ [
96
+ "trace_i18n",
97
+ "I18n",
98
+ trace_i18n
99
+ ],
100
+ [
101
+ "trace_traffic",
102
+ "Traffic",
103
+ trace_traffic
104
+ ],
105
+ [
106
+ "no_poll",
107
+ "No Poll",
108
+ set_no_poll
109
+ ]
110
+ ];
111
+ /******************************
112
+ * Small helpers
113
+ ******************************/
114
+ /************************************************************
115
+ * hh:mm:ss.SSS wall-clock of the moment a message arrives.
116
+ ************************************************************/
117
+ function traffic_now() {
118
+ let now = /* @__PURE__ */ new Date();
119
+ let pad = (num, len) => ("000" + num).slice(len * -1);
120
+ return `${pad(now.getHours(), 2)}:${pad(now.getMinutes(), 2)}:${pad(now.getSeconds(), 2)}.${pad(now.getMilliseconds(), 3)}`;
121
+ }
122
+ /************************************************************
123
+ * Human byte size (B / KB / MB).
124
+ ************************************************************/
125
+ function traffic_size(n) {
126
+ n = Number(n) || 0;
127
+ if (n < 1024) return n + " B";
128
+ if (n < 1024 * 1024) return (n / 1024).toFixed(1) + " KB";
129
+ return (n / (1024 * 1024)).toFixed(1) + " MB";
130
+ }
131
+ /************************************************************
132
+ * Seconds-since-epoch → ISO string, or null if not a plausible
133
+ * timestamp (guards against 0 / NaN / out-of-range values).
134
+ ************************************************************/
135
+ function traffic_iso(value) {
136
+ let n = Number(value);
137
+ if (!isFinite(n) || n <= 0) return null;
138
+ try {
139
+ return (/* @__PURE__ */ new Date(n * 1e3)).toISOString();
140
+ } catch (e) {
141
+ return null;
142
+ }
143
+ }
144
+ /************************************************************
145
+ * Clip a string for inline display (full text kept elsewhere).
146
+ ************************************************************/
147
+ function traffic_clip(s, n) {
148
+ s = String(s);
149
+ return s.length > n ? s.slice(0, n) + "…" : s;
150
+ }
151
+ /************************************************************
152
+ * A scalar rendered as a short inline token (for summaries).
153
+ ************************************************************/
154
+ function traffic_scalar_text(v) {
155
+ if (v === null) return "null";
156
+ if (typeof v === "string") return traffic_clip(v, 40);
157
+ return String(v);
158
+ }
159
+ function dir_class(dir) {
160
+ return dir === 2 ? "dir-in" : dir === 3 ? "dir-err" : "dir-out";
161
+ }
162
+ function dir_arrow(dir) {
163
+ return dir === 2 ? "⇠" : dir === 3 ? "⚠" : "⇢";
164
+ }
165
+ /******************************
166
+ * Preferences
167
+ ******************************/
168
+ function dev_num(key, def) {
169
+ return Number(kw_get_local_storage_value(key, def === void 0 ? 0 : def, false));
170
+ }
171
+ function dev_view() {
172
+ let v = kw_get_local_storage_value("dev_view_mode", "detailed", false);
173
+ return v === "compact" || v === "name" || v === "full" ? v : "detailed";
174
+ }
175
+ function dev_hide_periodic() {
176
+ return dev_num("dev_hide_periodic", 0) ? true : false;
177
+ }
178
+ function dev_muted() {
179
+ let a = kw_get_local_storage_value("dev_muted_events", [], false);
180
+ if (!Array.isArray(a)) a = [];
181
+ return new Set(a);
182
+ }
183
+ function dev_set_muted(set) {
184
+ kw_set_local_storage_value("dev_muted_events", Array.from(set));
185
+ }
186
+ function set_view(v) {
187
+ kw_set_local_storage_value("dev_view_mode", v);
188
+ rerender_all();
189
+ refresh_dev_chrome();
190
+ }
191
+ function toggle_pref(key, def) {
192
+ kw_set_local_storage_value(key, dev_num(key, def) ? 0 : 1);
193
+ rerender_all();
194
+ refresh_dev_chrome();
195
+ }
196
+ function mute_signature(sig) {
197
+ let set = dev_muted();
198
+ set.add(sig);
199
+ dev_set_muted(set);
200
+ rerender_all();
201
+ refresh_dev_chrome();
202
+ }
203
+ function unmute_signature(sig) {
204
+ let set = dev_muted();
205
+ set.delete(sig);
206
+ dev_set_muted(set);
207
+ rerender_all();
208
+ refresh_dev_chrome();
209
+ }
210
+ /******************************
211
+ * Filtering
212
+ ******************************/
213
+ function traffic_signature(event, command) {
214
+ return command ? event + " · " + command : event;
215
+ }
216
+ function traffic_is_periodic(sig) {
217
+ if (PERIODIC_RE.test(sig)) return true;
218
+ return (TRAFFIC_COUNTS.get(sig) || 0) >= PERIODIC_THRESHOLD;
219
+ }
220
+ function build_filter_ctx() {
221
+ return {
222
+ out: dev_num("dev_filter_out", 1),
223
+ inc: dev_num("dev_filter_in", 1),
224
+ err: dev_num("dev_filter_err", 1),
225
+ muted: dev_muted(),
226
+ hide_periodic: dev_hide_periodic(),
227
+ search: SEARCH_TEXT
228
+ };
229
+ }
230
+ function entry_hidden(e, ctx) {
231
+ if (e.kind === "log") return !!(ctx.search && e.hay.indexOf(ctx.search) < 0);
232
+ if (e.dir === 1 && !ctx.out) return true;
233
+ if (e.dir === 2 && !ctx.inc) return true;
234
+ if (e.dir === 3 && !ctx.err) return true;
235
+ if (ctx.muted.has(e.sig)) return true;
236
+ if (ctx.hide_periodic && traffic_is_periodic(e.sig)) return true;
237
+ if (ctx.search && e.hay.indexOf(ctx.search) < 0) return true;
238
+ return false;
239
+ }
240
+ /******************************
241
+ * Style
242
+ ******************************/
243
+ /************************************************************
244
+ * Inject the monitor stylesheet once. Theme-aware via
245
+ * <html data-theme>; direction-coloured (out / in / error).
246
+ ************************************************************/
247
+ function ensure_dev_style() {
248
+ if (document.getElementById("yui-dev-style")) return;
249
+ let css = `
250
+ /* -------- layout -------- */
251
+ .YDEV_BODY {
252
+ display: flex;
253
+ flex-direction: column;
254
+ height: 100%;
255
+ min-height: 0;
256
+ box-sizing: border-box;
257
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
258
+ }
259
+ .YDEV_LOG { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 6px 10px; }
260
+ .YDEV_MUTED {
261
+ display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
262
+ padding: 4px 10px; border-bottom: 1px solid rgba(0,0,0,0.08); font-size: 12px;
263
+ }
264
+ .YDEV_MUTED:empty { display: none; }
265
+ .YDEV_STATS {
266
+ flex: 0 0 auto; display: flex; flex-wrap: wrap; gap: 14px;
267
+ padding: 6px 10px; border-top: 1px solid rgba(0,0,0,0.1);
268
+ background: rgba(0,0,0,0.03);
269
+ font-family: "DejaVu Sans Mono", monospace; font-size: 11px;
270
+ opacity: 0.9; font-variant-numeric: tabular-nums;
271
+ }
272
+ /* -------- control bar -------- */
273
+ .YDEV_BAR {
274
+ display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px;
275
+ padding: 8px 10px; border-bottom: 1px solid rgba(0,0,0,0.1);
276
+ background: rgba(0,0,0,0.03);
277
+ }
278
+ .YDEV_GROUP { display: inline-flex; align-items: center; gap: 5px; }
279
+ .YDEV_LABEL {
280
+ font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em;
281
+ opacity: 0.5; align-self: center;
282
+ }
283
+ .YDEV_SEP { width: 1px; align-self: stretch; background: rgba(0,0,0,0.12); }
284
+ .YDEV_CHIP {
285
+ font: inherit; font-size: 12px; line-height: 1.4; padding: 3px 9px;
286
+ border: 1px solid rgba(0,0,0,0.18); border-radius: 999px;
287
+ background: transparent; color: inherit; cursor: pointer;
288
+ display: inline-flex; align-items: center; gap: 5px;
289
+ }
290
+ .YDEV_CHIP:hover { border-color: currentColor; }
291
+ .YDEV_CHIP.is-active { background: rgba(37,99,235,0.14); border-color: #2563eb; color: #2563eb; font-weight: 600; }
292
+ .YDEV_CHIP.s-out.is-active { background: rgba(37,99,235,0.16); border-color: #2563eb; color: #2563eb; }
293
+ .YDEV_CHIP.s-in.is-active { background: rgba(5,150,105,0.16); border-color: #059669; color: #059669; }
294
+ .YDEV_CHIP.s-err.is-active { background: rgba(220,38,38,0.16); border-color: #dc2626; color: #dc2626; }
295
+ .YDEV_CHIP[data-dir]:not(.is-active) { opacity: 0.4; text-decoration: line-through; }
296
+ .YDEV_CHIP[data-toggle="periodic"].is-active { background: rgba(217,119,6,0.16); border-color: #d97706; color: #b45309; font-weight: 600; }
297
+ .YDEV_SEG { display: inline-flex; border: 1px solid rgba(0,0,0,0.18); border-radius: 7px; overflow: hidden; }
298
+ .YDEV_SEG_BTN {
299
+ font: inherit; font-size: 12px; padding: 4px 10px; border: 0;
300
+ border-right: 1px solid rgba(0,0,0,0.12);
301
+ background: transparent; color: inherit; cursor: pointer;
302
+ }
303
+ .YDEV_SEG_BTN:last-child { border-right: 0; }
304
+ .YDEV_SEG_BTN.is-active { background: #2563eb; color: #fff; font-weight: 600; }
305
+ .YDEV_SEARCH {
306
+ font: inherit; font-size: 12px; padding: 4px 9px; min-width: 170px;
307
+ border: 1px solid rgba(0,0,0,0.18); border-radius: 7px;
308
+ background: transparent; color: inherit;
309
+ }
310
+ .YDEV_MUTED_CHIP {
311
+ font: inherit; font-family: "DejaVu Sans Mono", monospace; font-size: 12px;
312
+ display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px;
313
+ border: 1px solid rgba(217,119,6,0.5); border-radius: 999px;
314
+ background: rgba(217,119,6,0.12); color: #b45309; cursor: pointer;
315
+ }
316
+ .YDEV_STAT.s-out { color: #2563eb; } .YDEV_STAT.s-in { color: #059669; } .YDEV_STAT.s-err { color: #dc2626; }
317
+ .YDEV_TITLE { display: flex; align-items: baseline; gap: 8px; }
318
+ .YDEV_TITLE_MAIN { font-weight: 700; }
319
+ .YDEV_TITLE_SUB { opacity: 0.7; font-size: 12px; }
320
+ /* -------- entries (shared) -------- */
321
+ .TRAFFIC_ENTRY, .TRAFFIC_LINE, .TRAFFIC_NAME {
322
+ border-left: 3px solid #94a3b8; border-radius: 3px;
323
+ font-family: "DejaVu Sans Mono", monospace, consolas, monaco; font-size: 13px;
324
+ background: rgba(0,0,0,0.02);
325
+ }
326
+ .TRAFFIC_ENTRY { margin: 6px 0; padding: 4px 8px; line-height: 1.55; }
327
+ .TRAFFIC_LINE { margin: 2px 0; padding: 2px 8px; display: flex; align-items: baseline; gap: 8px; }
328
+ .TRAFFIC_NAME { margin: 1px 0; padding: 1px 8px; display: flex; align-items: baseline; gap: 8px; background: transparent; }
329
+ .TRAFFIC_ENTRY.dir-out, .TRAFFIC_LINE.dir-out, .TRAFFIC_NAME.dir-out { border-left-color: #2563eb; }
330
+ .TRAFFIC_ENTRY.dir-in, .TRAFFIC_LINE.dir-in, .TRAFFIC_NAME.dir-in { border-left-color: #059669; }
331
+ .TRAFFIC_ENTRY.dir-err, .TRAFFIC_LINE.dir-err, .TRAFFIC_NAME.dir-err { border-left-color: #dc2626; }
332
+ .TRAFFIC_HEADER { display: flex; align-items: baseline; gap: 8px; }
333
+ .TRAFFIC_ARROW { font-weight: 700; }
334
+ .TRAFFIC_EVENT { font-weight: 700; }
335
+ .TRAFFIC_CMD { opacity: 0.75; font-weight: 600; }
336
+ .dir-out .TRAFFIC_ARROW, .dir-out .TRAFFIC_EVENT { color: #2563eb; }
337
+ .dir-in .TRAFFIC_ARROW, .dir-in .TRAFFIC_EVENT { color: #059669; }
338
+ .dir-err .TRAFFIC_ARROW, .dir-err .TRAFFIC_EVENT { color: #dc2626; }
339
+ .TRAFFIC_SUMMARY { opacity: 0.6; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1 1 auto; min-width: 0; }
340
+ .TRAFFIC_META { margin-left: auto; opacity: 0.6; font-size: 11px; white-space: nowrap; }
341
+ .TRAFFIC_MUTE { border: 0; background: transparent; color: inherit; cursor: pointer; opacity: 0; font-size: 12px; padding: 0 2px; flex: 0 0 auto; }
342
+ .TRAFFIC_ENTRY:hover .TRAFFIC_MUTE, .TRAFFIC_LINE:hover .TRAFFIC_MUTE, .TRAFFIC_NAME:hover .TRAFFIC_MUTE { opacity: 0.5; }
343
+ .TRAFFIC_MUTE:hover { opacity: 1 !important; color: #d97706; }
344
+ .TRAFFIC_KW { margin: 2px 0 0 16px; }
345
+ .TRAFFIC_FULL { margin: 4px 0 0 16px; padding: 6px 8px; font-family: monospace; font-size: 11px; line-height: 1.4; white-space: pre-wrap; word-break: break-word; background: rgba(0,0,0,0.04); border-radius: 4px; overflow-x: auto; }
346
+ .TRAFFIC_ROW { display: flex; gap: 6px; align-items: baseline; }
347
+ .TRAFFIC_BULLET { opacity: 0.45; flex: 0 0 auto; }
348
+ .TRAFFIC_KEY { opacity: 0.85; flex: 0 0 auto; }
349
+ .TRAFFIC_VAL { word-break: break-word; }
350
+ .TRAFFIC_VAL.t-num { color: #0891b2; }
351
+ .TRAFFIC_VAL.t-bool { color: #9333ea; }
352
+ .TRAFFIC_VAL.t-null { color: #9333ea; font-style: italic; }
353
+ .TRAFFIC_VAL.t-empty { opacity: 0.5; }
354
+ .TRAFFIC_TS { opacity: 0.6; margin-left: 8px; }
355
+ details.TRAFFIC_NEST > summary { cursor: pointer; list-style: none; display: flex; gap: 6px; align-items: baseline; }
356
+ details.TRAFFIC_NEST > summary::-webkit-details-marker { display: none; }
357
+ .TRAFFIC_NEST_KEY { opacity: 0.85; }
358
+ .TRAFFIC_NEST_HINT { opacity: 0.5; margin-left: 4px; }
359
+ .YDEV_EMPTY { opacity: 0.5; font-size: 12px; padding: 18px 10px; text-align: center; }
360
+ /* -------- mirrored console logs (error/warning/info/debug/msg; the automata trace shows as debug) -------- */
361
+ .YDEV_LOGROW { display: flex; align-items: baseline; gap: 8px; margin: 1px 0; padding: 2px 8px; border-left: 3px solid #94a3b8; border-radius: 3px; font-family: "DejaVu Sans Mono", monospace, consolas, monaco; font-size: 12px; background: rgba(0,0,0,0.015); }
362
+ .YDEV_LOG_LVL { flex: 0 0 auto; text-transform: uppercase; font-size: 9px; font-weight: 700; letter-spacing: 0.04em; opacity: 0.8; min-width: 48px; }
363
+ .YDEV_LOG_TXT { flex: 1 1 auto; min-width: 0; white-space: pre-wrap; word-break: break-word; opacity: 0.9; }
364
+ .YDEV_LOG_error { border-left-color: #dc2626; } .YDEV_LOG_error .YDEV_LOG_LVL { color: #dc2626; }
365
+ .YDEV_LOG_warning { border-left-color: #d97706; } .YDEV_LOG_warning .YDEV_LOG_LVL { color: #d97706; }
366
+ .YDEV_LOG_info { border-left-color: #2563eb; } .YDEV_LOG_info .YDEV_LOG_LVL { color: #2563eb; }
367
+ .YDEV_LOG_msg { border-left-color: #0891b2; } .YDEV_LOG_msg .YDEV_LOG_LVL { color: #0891b2; }
368
+ .YDEV_LOG_debug { border-left-color: #94a3b8; } .YDEV_LOG_debug .YDEV_LOG_LVL { color: #94a3b8; } .YDEV_LOG_debug .YDEV_LOG_TXT { opacity: 0.72; }
369
+ .YDEV_LOG_json { border-left-color: #9333ea; align-items: flex-start; } .YDEV_LOG_json .YDEV_LOG_LVL { color: #9333ea; } .YDEV_LOG_json .YDEV_LOG_TXT { font-size: 11px; line-height: 1.35; opacity: 0.8; }
370
+ /* -------- dark theme -------- */
371
+ :root[data-theme="dark"] .TRAFFIC_FULL { background: rgba(255,255,255,0.05); }
372
+ :root[data-theme="dark"] .YDEV_BAR, :root[data-theme="dark"] .YDEV_STATS { background: rgba(255,255,255,0.04); }
373
+ :root[data-theme="dark"] .YDEV_SEP { background: rgba(255,255,255,0.14); }
374
+ :root[data-theme="dark"] .YDEV_CHIP, :root[data-theme="dark"] .YDEV_SEG, :root[data-theme="dark"] .YDEV_SEARCH { border-color: rgba(255,255,255,0.2); }
375
+ :root[data-theme="dark"] .TRAFFIC_ENTRY, :root[data-theme="dark"] .TRAFFIC_LINE { background: rgba(255,255,255,0.03); }
376
+ :root[data-theme="dark"] .TRAFFIC_ENTRY.dir-out, :root[data-theme="dark"] .TRAFFIC_LINE.dir-out, :root[data-theme="dark"] .TRAFFIC_NAME.dir-out { border-left-color: #60a5fa; }
377
+ :root[data-theme="dark"] .TRAFFIC_ENTRY.dir-in, :root[data-theme="dark"] .TRAFFIC_LINE.dir-in, :root[data-theme="dark"] .TRAFFIC_NAME.dir-in { border-left-color: #34d399; }
378
+ :root[data-theme="dark"] .TRAFFIC_ENTRY.dir-err, :root[data-theme="dark"] .TRAFFIC_LINE.dir-err, :root[data-theme="dark"] .TRAFFIC_NAME.dir-err { border-left-color: #f87171; }
379
+ :root[data-theme="dark"] .dir-out .TRAFFIC_ARROW, :root[data-theme="dark"] .dir-out .TRAFFIC_EVENT { color: #60a5fa; }
380
+ :root[data-theme="dark"] .dir-in .TRAFFIC_ARROW, :root[data-theme="dark"] .dir-in .TRAFFIC_EVENT { color: #34d399; }
381
+ :root[data-theme="dark"] .dir-err .TRAFFIC_ARROW, :root[data-theme="dark"] .dir-err .TRAFFIC_EVENT { color: #f87171; }
382
+ :root[data-theme="dark"] .YDEV_STAT.s-out { color: #60a5fa; } :root[data-theme="dark"] .YDEV_STAT.s-in { color: #34d399; } :root[data-theme="dark"] .YDEV_STAT.s-err { color: #f87171; }
383
+ :root[data-theme="dark"] .TRAFFIC_VAL.t-num { color: #22d3ee; }
384
+ :root[data-theme="dark"] .TRAFFIC_VAL.t-bool, :root[data-theme="dark"] .TRAFFIC_VAL.t-null { color: #c084fc; }
385
+ :root[data-theme="dark"] .YDEV_CHIP.is-active { background: rgba(96,165,250,0.2); border-color: #60a5fa; color: #93c5fd; }
386
+ :root[data-theme="dark"] .YDEV_SEG_BTN.is-active { background: #2563eb; color: #fff; }
387
+ :root[data-theme="dark"] .YDEV_CHIP[data-toggle="periodic"].is-active { background: rgba(217,119,6,0.24); border-color: #f59e0b; color: #fbbf24; }
388
+ :root[data-theme="dark"] .YDEV_MUTED_CHIP { border-color: rgba(245,158,11,0.5); background: rgba(245,158,11,0.16); color: #fbbf24; }
389
+ `;
390
+ let $style = document.createElement("style");
391
+ $style.id = "yui-dev-style";
392
+ $style.textContent = css;
393
+ document.head.appendChild($style);
394
+ }
395
+ /******************************
396
+ * kw bullet rendering
397
+ ******************************/
50
398
  /************************************************************
399
+ * One scalar field as a bullet row: `• key: value`.
400
+ * Type-coloured; long strings clipped (full text on hover);
401
+ * timestamp fields get an ISO annotation.
402
+ ************************************************************/
403
+ function traffic_scalar_row(key, value) {
404
+ let cls;
405
+ let text;
406
+ if (value === null) {
407
+ cls = "t-null";
408
+ text = "null";
409
+ } else if (typeof value === "boolean") {
410
+ cls = "t-bool";
411
+ text = value ? "true" : "false";
412
+ } else if (typeof value === "number") {
413
+ cls = "t-num";
414
+ text = String(value);
415
+ } else {
416
+ cls = "t-str";
417
+ text = String(value);
418
+ }
419
+ let full = text;
420
+ if (text.length > 200) text = text.slice(0, 200) + "…";
421
+ let val_children = [[
422
+ "span",
423
+ {
424
+ class: "TRAFFIC_VAL " + cls,
425
+ title: full
426
+ },
427
+ text
428
+ ]];
429
+ if (key in TRAFFIC_TS_FIELDS && typeof value === "number") {
430
+ let iso = traffic_iso(value);
431
+ if (iso) val_children.push([
432
+ "span",
433
+ { class: "TRAFFIC_TS" },
434
+ iso
435
+ ]);
436
+ }
437
+ return [
438
+ "div",
439
+ { class: "TRAFFIC_ROW" },
440
+ [
441
+ [
442
+ "span",
443
+ { class: "TRAFFIC_BULLET" },
444
+ "•"
445
+ ],
446
+ [
447
+ "span",
448
+ { class: "TRAFFIC_KEY" },
449
+ key + ":"
450
+ ],
451
+ [
452
+ "span",
453
+ {},
454
+ val_children
455
+ ]
456
+ ]
457
+ ];
458
+ }
459
+ /************************************************************
460
+ * One field of any type. Scalars → a bullet row; objects and
461
+ * arrays → a collapsed <details> so metadata / nested payloads
462
+ * stay folded. Empty containers render inline.
463
+ ************************************************************/
464
+ function traffic_value_node(key, value) {
465
+ if (value === null || typeof value !== "object") return traffic_scalar_row(key, value);
466
+ let is_arr = Array.isArray(value);
467
+ let count = is_arr ? value.length : Object.keys(value).length;
468
+ if (count === 0) return [
469
+ "div",
470
+ { class: "TRAFFIC_ROW" },
471
+ [
472
+ [
473
+ "span",
474
+ { class: "TRAFFIC_BULLET" },
475
+ "•"
476
+ ],
477
+ [
478
+ "span",
479
+ { class: "TRAFFIC_KEY" },
480
+ key + ":"
481
+ ],
482
+ [
483
+ "span",
484
+ { class: "TRAFFIC_VAL t-empty" },
485
+ is_arr ? "[ ]" : "{ }"
486
+ ]
487
+ ]
488
+ ];
489
+ let hint = is_arr ? `[${count}]` : `{${count}}`;
490
+ return [
491
+ "details",
492
+ { class: "TRAFFIC_NEST" },
493
+ [[
494
+ "summary",
495
+ {},
496
+ [
497
+ [
498
+ "span",
499
+ { class: "TRAFFIC_BULLET" },
500
+ "▸"
501
+ ],
502
+ [
503
+ "span",
504
+ { class: "TRAFFIC_NEST_KEY" },
505
+ key
506
+ ],
507
+ [
508
+ "span",
509
+ { class: "TRAFFIC_NEST_HINT" },
510
+ hint
511
+ ]
512
+ ]
513
+ ], [
514
+ "div",
515
+ { class: "TRAFFIC_KW" },
516
+ traffic_bullets(value)
517
+ ]]
518
+ ];
519
+ }
520
+ /************************************************************
521
+ * A whole object/array → an array of bullet nodes.
522
+ ************************************************************/
523
+ function traffic_bullets(obj) {
524
+ let out = [];
525
+ if (Array.isArray(obj)) for (let i = 0; i < obj.length; i++) out.push(traffic_value_node(String(i), obj[i]));
526
+ else for (let k of Object.keys(obj)) out.push(traffic_value_node(k, obj[k]));
527
+ return out;
528
+ }
529
+ /******************************
530
+ * Entry rendering (per view)
531
+ ******************************/
532
+ function mute_button(sig) {
533
+ return [
534
+ "button",
535
+ {
536
+ class: "TRAFFIC_MUTE",
537
+ type: "button",
538
+ title: "Mute " + sig
539
+ },
540
+ "⊘",
541
+ { click: (ev) => {
542
+ ev.stopPropagation();
543
+ ev.preventDefault();
544
+ mute_signature(sig);
545
+ } }
546
+ ];
547
+ }
548
+ function event_spans(e) {
549
+ let spans = [[
550
+ "span",
551
+ { class: "TRAFFIC_ARROW" },
552
+ dir_arrow(e.dir)
553
+ ], [
554
+ "span",
555
+ { class: "TRAFFIC_EVENT" },
556
+ e.event
557
+ ]];
558
+ if (e.command) spans.push([
559
+ "span",
560
+ { class: "TRAFFIC_CMD" },
561
+ e.command
562
+ ]);
563
+ return spans;
564
+ }
565
+ function compact_summary(kw) {
566
+ if (!kw) return "";
567
+ let parts = [];
568
+ if ("result" in kw) parts.push("result=" + traffic_scalar_text(kw.result));
569
+ if (typeof kw.comment === "string" && kw.comment) parts.push(traffic_clip(kw.comment, 80));
570
+ if (!parts.length) {
571
+ let n = 0;
572
+ for (let k of Object.keys(kw)) {
573
+ if (k === "command") continue;
574
+ let v = kw[k];
575
+ if (v === null || typeof v !== "object") {
576
+ parts.push(k + "=" + traffic_scalar_text(v));
577
+ if (++n >= 3) break;
578
+ }
579
+ }
580
+ }
581
+ return traffic_clip(parts.join(" · "), 140);
582
+ }
583
+ function render_detailed(e) {
584
+ let head = event_spans(e);
585
+ head.push(mute_button(e.sig));
586
+ head.push([
587
+ "span",
588
+ { class: "TRAFFIC_META" },
589
+ `${traffic_size(e.size)} · ${e.ts}`
590
+ ]);
591
+ let children = [[
592
+ "div",
593
+ { class: "TRAFFIC_HEADER" },
594
+ head
595
+ ]];
596
+ let kw = e.kw;
597
+ if (kw && Object.keys(kw).length > 0) children.push([
598
+ "div",
599
+ { class: "TRAFFIC_KW" },
600
+ traffic_bullets(kw)
601
+ ]);
602
+ else if (!kw) children.push([
603
+ "div",
604
+ { class: "TRAFFIC_KW" },
605
+ traffic_bullets(e.jn)
606
+ ]);
607
+ return createElement2([
608
+ "div",
609
+ {
610
+ class: "TRAFFIC_ENTRY " + dir_class(e.dir),
611
+ title: e.title
612
+ },
613
+ children
614
+ ]);
615
+ }
616
+ function full_show(key) {
617
+ return !!dev_num(key, key === "dev_full_data" ? 1 : 0);
618
+ }
619
+ function full_sections(payload) {
620
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return payload;
621
+ let show_schema = full_show("dev_full_schema");
622
+ let show_data = full_show("dev_full_data");
623
+ let show_meta = full_show("dev_full_meta");
624
+ let out = {};
625
+ for (let k of Object.keys(payload)) {
626
+ if (k === "schema") {
627
+ if (show_schema) out[k] = payload[k];
628
+ continue;
629
+ }
630
+ if (k === "data") {
631
+ if (show_data) out[k] = payload[k];
632
+ continue;
633
+ }
634
+ if (/^__.*__$/.test(k)) {
635
+ if (show_meta) out[k] = payload[k];
636
+ continue;
637
+ }
638
+ out[k] = payload[k];
639
+ }
640
+ return out;
641
+ }
642
+ function render_full(e) {
643
+ let head = event_spans(e);
644
+ head.push(mute_button(e.sig));
645
+ head.push([
646
+ "span",
647
+ { class: "TRAFFIC_META" },
648
+ `${traffic_size(e.size)} · ${e.ts}`
649
+ ]);
650
+ let payload = full_sections(e.kw ? e.kw : e.jn);
651
+ let text;
652
+ try {
653
+ text = JSON.stringify(payload, null, 2);
654
+ } catch (err) {
655
+ text = String(payload);
656
+ }
657
+ let children = [[
658
+ "div",
659
+ { class: "TRAFFIC_HEADER" },
660
+ head
661
+ ], [
662
+ "pre",
663
+ { class: "TRAFFIC_FULL" },
664
+ text
665
+ ]];
666
+ return createElement2([
667
+ "div",
668
+ {
669
+ class: "TRAFFIC_ENTRY " + dir_class(e.dir),
670
+ title: e.title
671
+ },
672
+ children
673
+ ]);
674
+ }
675
+ function render_compact(e) {
676
+ let kids = event_spans(e);
677
+ kids.push([
678
+ "span",
679
+ { class: "TRAFFIC_SUMMARY" },
680
+ compact_summary(e.kw)
681
+ ]);
682
+ kids.push(mute_button(e.sig));
683
+ kids.push([
684
+ "span",
685
+ { class: "TRAFFIC_META" },
686
+ `${traffic_size(e.size)} · ${e.ts}`
687
+ ]);
688
+ return createElement2([
689
+ "div",
690
+ {
691
+ class: "TRAFFIC_LINE " + dir_class(e.dir),
692
+ title: e.title
693
+ },
694
+ kids
695
+ ]);
696
+ }
697
+ function render_name(e) {
698
+ let kids = event_spans(e);
699
+ kids.push(mute_button(e.sig));
700
+ kids.push([
701
+ "span",
702
+ { class: "TRAFFIC_META" },
703
+ e.ts
704
+ ]);
705
+ return createElement2([
706
+ "div",
707
+ {
708
+ class: "TRAFFIC_NAME " + dir_class(e.dir),
709
+ title: e.title
710
+ },
711
+ kids
712
+ ]);
713
+ }
714
+ function render_log(e) {
715
+ return createElement2([
716
+ "div",
717
+ {
718
+ class: "YDEV_LOGROW YDEV_LOG_" + e.level,
719
+ title: e.level
720
+ },
721
+ [
722
+ [
723
+ "span",
724
+ { class: "YDEV_LOG_LVL" },
725
+ e.level
726
+ ],
727
+ [
728
+ "span",
729
+ { class: "YDEV_LOG_TXT" },
730
+ e.text
731
+ ],
732
+ [
733
+ "span",
734
+ { class: "TRAFFIC_META" },
735
+ e.ts
736
+ ]
737
+ ]
738
+ ]);
739
+ }
740
+ function render_entry(e) {
741
+ if (e.kind === "log") return render_log(e);
742
+ let view = dev_view();
743
+ if (view === "full") return render_full(e);
744
+ if (view === "compact") return render_compact(e);
745
+ if (view === "name") return render_name(e);
746
+ return render_detailed(e);
747
+ }
748
+ /******************************
749
+ * Log painting
750
+ ******************************/
751
+ function clear_traffic() {
752
+ TRAFFIC_LOG.length = 0;
753
+ TRAFFIC_COUNTS.clear();
754
+ let logger = document.getElementById("developer-traffic-logger");
755
+ if (logger) logger.replaceChildren();
756
+ update_stats();
757
+ }
758
+ function rerender_all() {
759
+ let logger = document.getElementById("developer-traffic-logger");
760
+ if (!logger) return;
761
+ ensure_dev_style();
762
+ let ctx = build_filter_ctx();
763
+ let frag = document.createDocumentFragment();
764
+ let shown = 0;
765
+ for (let e of TRAFFIC_LOG) {
766
+ e.$node = null;
767
+ if (!entry_hidden(e, ctx)) {
768
+ let node = render_entry(e);
769
+ e.$node = node;
770
+ frag.appendChild(node);
771
+ shown++;
772
+ }
773
+ }
774
+ logger.replaceChildren(frag);
775
+ if (shown === 0) {
776
+ let $empty = document.createElement("div");
777
+ $empty.className = "YDEV_EMPTY";
778
+ $empty.textContent = TRAFFIC_LOG.length ? "No messages match the current filters." : "Waiting for activity — console logs and errors show automatically; enable Traffic or Automata for more.";
779
+ logger.appendChild($empty);
780
+ }
781
+ logger.scrollTop = logger.scrollHeight;
782
+ update_stats();
783
+ }
784
+ function update_stats() {
785
+ let $s = document.getElementById("ydev-stats");
786
+ if (!$s) return;
787
+ let ctx = build_filter_ctx();
788
+ let total = TRAFFIC_LOG.length;
789
+ let shown = 0, out = 0, inc = 0, err = 0, hidden = 0, bytes = 0;
790
+ for (let e of TRAFFIC_LOG) {
791
+ bytes += e.size || 0;
792
+ if (e.dir === 1) out++;
793
+ else if (e.dir === 2) inc++;
794
+ else if (e.dir === 3) err++;
795
+ if (entry_hidden(e, ctx)) hidden++;
796
+ else shown++;
797
+ }
798
+ $s.replaceChildren();
799
+ [
800
+ [`${shown}/${total} shown`, ""],
801
+ [`⇢ ${out}`, "s-out"],
802
+ [`⇠ ${inc}`, "s-in"],
803
+ [`⚠ ${err}`, "s-err"],
804
+ [`⊘ ${hidden} hidden`, ""],
805
+ [`${traffic_size(bytes)}`, ""]
806
+ ].forEach(([text, cls]) => {
807
+ let d = document.createElement("span");
808
+ d.className = "YDEV_STAT" + (cls ? " " + cls : "");
809
+ d.textContent = text;
810
+ $s.appendChild(d);
811
+ });
812
+ }
813
+ /************************************************************
814
+ * Append one inter-event message. Kept in a bounded buffer so
815
+ * view/filter switches repaint from memory. Shared by the legacy
816
+ * C_YUI_WINDOW (setup_dev) and the modal (build_dev_panel).
51
817
  *
818
+ * direction: 1 outgoing (⇢), 2 incoming (⇠), 3 error (⚠).
819
+ * With no logger mounted, fall back to a console dump.
52
820
  ************************************************************/
53
821
  function info_traffic(title, msg, direction, size) {
54
- if (!document.getElementById("developer-traffic-logger")) {
822
+ let logger = document.getElementById("developer-traffic-logger");
823
+ if (!logger) {
55
824
  trace_json(msg);
56
825
  return;
57
826
  }
58
827
  if (!size) size = 0;
59
- let jn_msg;
828
+ let jn;
60
829
  try {
61
- if (is_string(msg)) jn_msg = JSON.parse(msg);
62
- else jn_msg = JSON.parse(JSON.stringify(msg));
830
+ jn = is_string(msg) ? JSON.parse(msg) : JSON.parse(JSON.stringify(msg));
63
831
  } catch (e) {
64
832
  return;
65
833
  }
66
- let content = {
67
- text: void 0,
68
- json: jn_msg
834
+ ensure_dev_style();
835
+ let event = jn && jn.event ? String(jn.event) : "(no event)";
836
+ let kw = jn && jn.kw && typeof jn.kw === "object" ? jn.kw : null;
837
+ let command = kw && typeof kw.command === "string" ? kw.command : "";
838
+ let sig = traffic_signature(event, command);
839
+ let hay = "";
840
+ try {
841
+ hay = (event + " " + command + " " + (kw ? JSON.stringify(kw) : "")).toLowerCase();
842
+ } catch (e) {
843
+ hay = (event + " " + command).toLowerCase();
844
+ }
845
+ let entry = {
846
+ title: title || "",
847
+ event,
848
+ command,
849
+ sig,
850
+ dir: direction,
851
+ size,
852
+ ts: traffic_now(),
853
+ kw,
854
+ jn,
855
+ hay,
856
+ $node: null
69
857
  };
70
- function formatCurrentTime() {
71
- let now = /* @__PURE__ */ new Date();
72
- let pad = (num, size) => ("000" + num).slice(size * -1);
73
- return `${pad(now.getHours(), 2)}:${pad(now.getMinutes(), 2)}:${pad(now.getSeconds(), 2)} .${pad(now.getMilliseconds(), 4)}`;
74
- }
75
- let element = document.getElementById("developer-traffic-logger");
76
- if (element) {
77
- let style = "background-color:#3883FA;";
78
- if (direction === 2) style += "color:yellow;";
79
- else if (direction === 3) style += "color:red;";
80
- else style += "color:white;";
81
- let $item = createElement2([
82
- "div",
83
- { class: "mt-4" },
84
- [[
85
- "div",
86
- {
87
- class: "is-flex with-border is-justify-content-space-between",
88
- style
89
- },
90
- [
91
- [
92
- "div",
93
- { class: "p-1" },
94
- title
95
- ],
96
- [
97
- "div",
98
- { class: "p-1" },
99
- `(${size} bytes)`
100
- ],
101
- [
102
- "div",
103
- { class: "p-1" },
104
- formatCurrentTime()
105
- ]
106
- ]
107
- ], [
108
- "div",
109
- { class: "x-jsoneditor jse-theme-dark" },
110
- []
111
- ]]
112
- ]);
113
- let $target = $item.querySelector(".x-jsoneditor");
114
- let font_family = "DejaVu Sans Mono, monospace, consolas, monaco";
115
- $target.style.setProperty("--jse-font-size-mono", "15px");
116
- $target.style.setProperty("--jse-font-family-mono", font_family);
117
- document.getElementById("developer-traffic-logger").appendChild($item);
118
- new JSONEditor({
119
- target: $target,
120
- props: {
121
- content,
122
- readOnly: true,
123
- timestampTag: function({ field, value, path }) {
124
- if (field === "__t__" || field === "__tm__" || field === "tm" || field === "from_t" || field === "to_t" || field === "t" || field === "from_tm" || field === "to_tm" || field === "time") return true;
125
- return false;
126
- },
127
- timestampFormat: function({ field, value, path }) {
128
- if (field === "__t__" || field === "__tm__" || field === "tm" || field === "from_t" || field === "to_t" || field === "t" || field === "from_tm" || field === "to_tm" || field === "time") return (/* @__PURE__ */ new Date(value * 1e3)).toISOString();
129
- return null;
130
- }
131
- }
132
- }).expand((path) => path.length < 2);
133
- element.scrollIntoView({ block: "end" });
858
+ TRAFFIC_LOG.push(entry);
859
+ TRAFFIC_COUNTS.set(sig, (TRAFFIC_COUNTS.get(sig) || 0) + 1);
860
+ let crossed = dev_hide_periodic() && TRAFFIC_COUNTS.get(sig) === PERIODIC_THRESHOLD;
861
+ if (TRAFFIC_LOG.length > TRAFFIC_MAX) {
862
+ let old = TRAFFIC_LOG.shift();
863
+ let c = (TRAFFIC_COUNTS.get(old.sig) || 0) - 1;
864
+ if (c <= 0) TRAFFIC_COUNTS.delete(old.sig);
865
+ else TRAFFIC_COUNTS.set(old.sig, c);
866
+ if (old.$node && old.$node.parentNode) old.$node.parentNode.removeChild(old.$node);
867
+ }
868
+ if (crossed) rerender_all();
869
+ else if (!entry_hidden(entry, build_filter_ctx())) {
870
+ let node = render_entry(entry);
871
+ entry.$node = node;
872
+ let ph = logger.querySelector(".YDEV_EMPTY");
873
+ if (ph) ph.remove();
874
+ logger.appendChild(node);
875
+ node.scrollIntoView({ block: "end" });
876
+ }
877
+ update_stats();
878
+ }
879
+ var __in_info_log__ = false;
880
+ /************************************************************
881
+ * Mirror one framework log line into the monitor, alongside the
882
+ * inter-event traffic. level ∈ error|warning|info|debug|msg — the automata
883
+ * (FSM) trace arrives here too, as `debug`. No-op while the window is closed
884
+ * (the line already went to the browser console).
885
+ ************************************************************/
886
+ function info_log(level, msg, hora) {
887
+ if (__in_info_log__) return;
888
+ let logger = document.getElementById("developer-traffic-logger");
889
+ if (!logger) return;
890
+ __in_info_log__ = true;
891
+ try {
892
+ ensure_dev_style();
893
+ let lvl = level || "debug";
894
+ let text;
895
+ if (lvl === "json") {
896
+ try {
897
+ text = JSON.stringify(msg, null, 2);
898
+ } catch (e) {
899
+ text = String(msg);
900
+ }
901
+ if (text.length > 4e3) text = text.slice(0, 4e3) + "\n…(truncated)";
902
+ } else text = is_string(msg) ? msg : String(msg);
903
+ let entry = {
904
+ kind: "log",
905
+ level: lvl,
906
+ text,
907
+ dir: 0,
908
+ size: 0,
909
+ ts: traffic_now(),
910
+ sig: "log:" + lvl,
911
+ hay: (lvl + " " + text).toLowerCase(),
912
+ $node: null
913
+ };
914
+ TRAFFIC_LOG.push(entry);
915
+ if (TRAFFIC_LOG.length > TRAFFIC_MAX) {
916
+ let old = TRAFFIC_LOG.shift();
917
+ if (old.kind !== "log") {
918
+ let c = (TRAFFIC_COUNTS.get(old.sig) || 0) - 1;
919
+ if (c <= 0) TRAFFIC_COUNTS.delete(old.sig);
920
+ else TRAFFIC_COUNTS.set(old.sig, c);
921
+ }
922
+ if (old.$node && old.$node.parentNode) old.$node.parentNode.removeChild(old.$node);
923
+ }
924
+ if (!entry_hidden(entry, build_filter_ctx())) {
925
+ let node = render_entry(entry);
926
+ entry.$node = node;
927
+ let ph = logger.querySelector(".YDEV_EMPTY");
928
+ if (ph) ph.remove();
929
+ logger.appendChild(node);
930
+ node.scrollIntoView({ block: "end" });
931
+ }
932
+ update_stats();
933
+ } finally {
934
+ __in_info_log__ = false;
134
935
  }
135
936
  }
136
- /************************************************************
137
- *
138
- ************************************************************/
937
+ /******************************
938
+ * Trace toggles
939
+ ******************************/
139
940
  function trace_traffic() {
140
- let v = kw_get_local_storage_value("trace_traffic");
141
- v = Number(v);
941
+ let v = Number(kw_get_local_storage_value("trace_traffic"));
142
942
  if (v) {
143
943
  gobj_write_attr(gobj_yuno(), "trace_inter_event", false);
144
944
  v = 0;
@@ -148,108 +948,401 @@ function trace_traffic() {
148
948
  v = 1;
149
949
  }
150
950
  kw_set_local_storage_value("trace_traffic", v);
151
- info_user();
951
+ refresh_dev_chrome();
152
952
  }
153
- /************************************************************
154
- *
155
- ************************************************************/
156
953
  function trace_automata() {
157
- let v = kw_get_local_storage_value("trace_automata");
158
- v = Number(v);
954
+ let v = Number(kw_get_local_storage_value("trace_automata"));
159
955
  if (v === 0) v = 1;
160
956
  else if (v === 1) v = 2;
161
957
  else v = 0;
162
958
  gobj_write_attr(gobj_yuno(), "tracing", v);
163
959
  kw_set_local_storage_value("trace_automata", v);
164
- info_user();
960
+ refresh_dev_chrome();
165
961
  }
166
- /************************************************************
167
- *
168
- ************************************************************/
169
962
  function trace_creation() {
170
- let v = kw_get_local_storage_value("trace_creation");
171
- v = Number(v);
172
- if (v === 0) v = 1;
173
- else v = 0;
963
+ let v = Number(kw_get_local_storage_value("trace_creation"));
964
+ v = v === 0 ? 1 : 0;
174
965
  gobj_write_attr(gobj_yuno(), "trace_creation", v);
175
966
  kw_set_local_storage_value("trace_creation", v);
176
- info_user();
967
+ refresh_dev_chrome();
177
968
  }
178
- /************************************************************
179
- *
180
- ************************************************************/
181
969
  function trace_start_stop() {
182
- let v = kw_get_local_storage_value("trace_start_stop");
183
- v = Number(v);
184
- if (v === 0) v = 1;
185
- else v = 0;
970
+ let v = Number(kw_get_local_storage_value("trace_start_stop"));
971
+ v = v === 0 ? 1 : 0;
186
972
  gobj_write_attr(gobj_yuno(), "trace_start_stop", v);
187
973
  kw_set_local_storage_value("trace_start_stop", v);
188
- info_user();
974
+ refresh_dev_chrome();
189
975
  }
190
- /************************************************************
191
- *
192
- ************************************************************/
193
976
  function trace_subscriptions() {
194
- let v = kw_get_local_storage_value("trace_subscriptions");
195
- v = Number(v);
196
- if (v === 0) v = 1;
197
- else v = 0;
977
+ let v = Number(kw_get_local_storage_value("trace_subscriptions"));
978
+ v = v === 0 ? 1 : 0;
198
979
  gobj_write_attr(gobj_yuno(), "trace_subscriptions", v);
199
980
  kw_set_local_storage_value("trace_subscriptions", v);
200
- info_user();
981
+ refresh_dev_chrome();
201
982
  }
202
- /************************************************************
203
- *
204
- ************************************************************/
205
983
  function trace_i18n() {
206
- let v = kw_get_local_storage_value("trace_i18n");
207
- v = Number(v);
208
- if (v === 0) v = 1;
209
- else v = 0;
984
+ let v = Number(kw_get_local_storage_value("trace_i18n"));
985
+ v = v === 0 ? 1 : 0;
210
986
  i18next.options.debug = v ? true : false;
211
987
  kw_set_local_storage_value("trace_i18n", v);
212
- info_user();
988
+ refresh_dev_chrome();
213
989
  }
214
- /************************************************************
215
- *
216
- ************************************************************/
217
990
  function set_no_poll() {
218
- let v = kw_get_local_storage_value("no_poll");
219
- v = Number(v);
220
- if (v) v = 0;
221
- else v = 1;
991
+ let v = Number(kw_get_local_storage_value("no_poll"));
992
+ v = v ? 0 : 1;
222
993
  gobj_write_attr(gobj_yuno(), "no_poll", v);
223
994
  kw_set_local_storage_value("no_poll", v);
224
- info_user();
995
+ refresh_dev_chrome();
225
996
  }
226
- /************************************************************
227
- *
228
- ************************************************************/
229
- function info_user() {
230
- let $info = document.getElementById("developer-window-info");
231
- let traffic = Number(kw_get_local_storage_value("trace_traffic", 0, false));
232
- let trace = Number(kw_get_local_storage_value("trace_automata", 0, false));
233
- let creation = Number(kw_get_local_storage_value("trace_creation", 0, false));
234
- let start_stop = Number(kw_get_local_storage_value("trace_start_stop", 0, false));
235
- let subscriptions = Number(kw_get_local_storage_value("trace_subscriptions", 0, false));
236
- let i18n = Number(kw_get_local_storage_value("trace_i18n", 0, false));
237
- let no_poll = Number(kw_get_local_storage_value("no_poll", 0, false));
238
- $info.replaceChildren();
239
- [
240
- `Automata: ${trace}`,
241
- `Creation: ${creation}`,
242
- `Start/Stop: ${start_stop}`,
243
- `Subscriptions: ${subscriptions}`,
244
- `I18n: ${i18n}`,
245
- `Traffic: ${traffic}`,
246
- `No poll: ${no_poll}`
247
- ].forEach((text) => {
248
- const div = document.createElement("div");
249
- div.textContent = text;
250
- $info.appendChild(div);
997
+ /******************************
998
+ * Chrome (controls)
999
+ ******************************/
1000
+ function refresh_dev_chrome() {
1001
+ document.querySelectorAll(".YDEV_CHIP[data-trace]").forEach(($b) => {
1002
+ let key = $b.getAttribute("data-trace");
1003
+ let label = $b.getAttribute("data-label") || "";
1004
+ let v = dev_num(key, 0);
1005
+ $b.textContent = key === "trace_automata" && v > 0 ? label + " " + v : label;
1006
+ $b.classList.toggle("is-active", v > 0);
1007
+ });
1008
+ let view = dev_view();
1009
+ document.querySelectorAll(".YDEV_SEG_BTN[data-view]").forEach(($b) => {
1010
+ $b.classList.toggle("is-active", $b.getAttribute("data-view") === view);
1011
+ });
1012
+ let $eg = document.getElementById("ydev-expand-grp");
1013
+ if ($eg) $eg.style.display = view === "full" ? "" : "none";
1014
+ document.querySelectorAll(".YDEV_CHIP[data-expand]").forEach(($b) => {
1015
+ $b.classList.toggle("is-active", full_show($b.getAttribute("data-expand")));
1016
+ });
1017
+ document.querySelectorAll(".YDEV_CHIP[data-dir]").forEach(($b) => {
1018
+ $b.classList.toggle("is-active", !!dev_num($b.getAttribute("data-dir"), 1));
1019
+ });
1020
+ document.querySelectorAll(".YDEV_CHIP[data-toggle=\"periodic\"]").forEach(($b) => {
1021
+ $b.classList.toggle("is-active", dev_hide_periodic());
1022
+ });
1023
+ document.querySelectorAll(".YDEV_CHIP[data-toggle=\"automata-simple\"]").forEach(($b) => {
1024
+ $b.classList.toggle("is-active", !!dev_num("dev_automata_simple", 0));
1025
+ });
1026
+ let $m = document.getElementById("ydev-muted");
1027
+ if ($m) {
1028
+ $m.replaceChildren();
1029
+ let set = dev_muted();
1030
+ if (set.size) {
1031
+ let $lbl = document.createElement("span");
1032
+ $lbl.className = "YDEV_LABEL";
1033
+ $lbl.textContent = "Muted";
1034
+ $m.appendChild($lbl);
1035
+ set.forEach((sig) => {
1036
+ $m.appendChild(createElement2([
1037
+ "button",
1038
+ {
1039
+ class: "YDEV_MUTED_CHIP",
1040
+ type: "button",
1041
+ title: "Unmute"
1042
+ },
1043
+ "⊘ " + sig + " ✕",
1044
+ { click: (ev) => {
1045
+ ev.stopPropagation();
1046
+ unmute_signature(sig);
1047
+ } }
1048
+ ]));
1049
+ });
1050
+ }
1051
+ }
1052
+ update_stats();
1053
+ }
1054
+ function traffic_to_text() {
1055
+ let ctx = build_filter_ctx();
1056
+ let out = [];
1057
+ for (let e of TRAFFIC_LOG) {
1058
+ if (entry_hidden(e, ctx)) continue;
1059
+ if (e.kind === "log") {
1060
+ out.push(`${e.ts} ${e.level}: ${e.text}`);
1061
+ out.push("");
1062
+ continue;
1063
+ }
1064
+ let head = `${e.ts} ${dir_arrow(e.dir)} ${e.title ? "[" + e.title + "] " : ""}${e.event}${e.command ? " " + e.command : ""}`;
1065
+ out.push(head);
1066
+ let payload = e.kw ? e.kw : e.jn;
1067
+ try {
1068
+ out.push(JSON.stringify(payload, null, 2));
1069
+ } catch (err) {
1070
+ out.push(String(payload));
1071
+ }
1072
+ out.push("");
1073
+ }
1074
+ return out.join("\n");
1075
+ }
1076
+ function dev_copy_text(text) {
1077
+ if (navigator.clipboard && navigator.clipboard.writeText) return navigator.clipboard.writeText(text).catch(() => {
1078
+ dev_fallback_copy(text);
251
1079
  });
1080
+ dev_fallback_copy(text);
1081
+ return Promise.resolve();
1082
+ }
1083
+ function dev_fallback_copy(text) {
1084
+ let ta = document.createElement("textarea");
1085
+ ta.value = text;
1086
+ ta.style.position = "fixed";
1087
+ ta.style.left = "-9999px";
1088
+ document.body.appendChild(ta);
1089
+ ta.select();
1090
+ try {
1091
+ document.execCommand("copy");
1092
+ } catch (e) {}
1093
+ document.body.removeChild(ta);
252
1094
  }
1095
+ function build_control_bar() {
1096
+ let trace_chips = TRACE_DEFS.map(([key, label, fn]) => [
1097
+ "button",
1098
+ {
1099
+ class: "YDEV_CHIP",
1100
+ "data-trace": key,
1101
+ "data-label": label,
1102
+ type: "button"
1103
+ },
1104
+ label,
1105
+ { click: (ev) => {
1106
+ ev.stopPropagation();
1107
+ fn();
1108
+ } }
1109
+ ]);
1110
+ let simple_mach = [
1111
+ "button",
1112
+ {
1113
+ class: "YDEV_CHIP",
1114
+ "data-toggle": "automata-simple",
1115
+ type: "button",
1116
+ title: "Compact automata format (one line per transition, like C)"
1117
+ },
1118
+ "Simple mach",
1119
+ { click: (ev) => {
1120
+ ev.stopPropagation();
1121
+ let v = dev_num("dev_automata_simple", 0) ? 0 : 1;
1122
+ kw_set_local_storage_value("dev_automata_simple", v);
1123
+ gobj_set_trace_machine_format(v);
1124
+ refresh_dev_chrome();
1125
+ } }
1126
+ ];
1127
+ let mk_view = (v, label) => [
1128
+ "button",
1129
+ {
1130
+ class: "YDEV_SEG_BTN",
1131
+ "data-view": v,
1132
+ type: "button"
1133
+ },
1134
+ label,
1135
+ { click: (ev) => {
1136
+ ev.stopPropagation();
1137
+ set_view(v);
1138
+ } }
1139
+ ];
1140
+ let view_seg = [
1141
+ "div",
1142
+ {
1143
+ class: "YDEV_SEG",
1144
+ id: "ydev-seg"
1145
+ },
1146
+ [
1147
+ mk_view("detailed", "Detailed"),
1148
+ mk_view("full", "Expanded"),
1149
+ mk_view("compact", "Compact"),
1150
+ mk_view("name", "Name only")
1151
+ ]
1152
+ ];
1153
+ let mk_expand = (key, label) => [
1154
+ "button",
1155
+ {
1156
+ class: "YDEV_CHIP",
1157
+ "data-expand": key,
1158
+ type: "button",
1159
+ title: "Show " + label + " in the Expanded view"
1160
+ },
1161
+ label,
1162
+ { click: (ev) => {
1163
+ ev.stopPropagation();
1164
+ toggle_pref(key, key === "dev_full_data" ? 1 : 0);
1165
+ } }
1166
+ ];
1167
+ let expand_grp = [
1168
+ "div",
1169
+ {
1170
+ class: "YDEV_GROUP",
1171
+ id: "ydev-expand-grp"
1172
+ },
1173
+ [
1174
+ [
1175
+ "span",
1176
+ { class: "YDEV_LABEL" },
1177
+ "Expand"
1178
+ ],
1179
+ mk_expand("dev_full_schema", "Schema"),
1180
+ mk_expand("dev_full_data", "Data"),
1181
+ mk_expand("dev_full_meta", "Metadata")
1182
+ ]
1183
+ ];
1184
+ let mk_dir = (dir, glyph, key, title) => [
1185
+ "button",
1186
+ {
1187
+ class: "YDEV_CHIP s-" + dir,
1188
+ "data-dir": key,
1189
+ type: "button",
1190
+ title
1191
+ },
1192
+ glyph,
1193
+ { click: (ev) => {
1194
+ ev.stopPropagation();
1195
+ toggle_pref(key, 1);
1196
+ } }
1197
+ ];
1198
+ let dir_chips = [
1199
+ mk_dir("out", "⇢", "dev_filter_out", "Outgoing"),
1200
+ mk_dir("in", "⇠", "dev_filter_in", "Incoming"),
1201
+ mk_dir("err", "⚠", "dev_filter_err", "Errors")
1202
+ ];
1203
+ let periodic_chip = [
1204
+ "button",
1205
+ {
1206
+ class: "YDEV_CHIP",
1207
+ "data-toggle": "periodic",
1208
+ type: "button",
1209
+ title: "Hide recurring / periodic events (polls, heartbeats)"
1210
+ },
1211
+ "⊘ Periodic",
1212
+ { click: (ev) => {
1213
+ ev.stopPropagation();
1214
+ toggle_pref("dev_hide_periodic", 0);
1215
+ } }
1216
+ ];
1217
+ let search = [
1218
+ "input",
1219
+ {
1220
+ class: "YDEV_SEARCH",
1221
+ type: "search",
1222
+ placeholder: "filter events / payload…",
1223
+ "data-role": "search"
1224
+ },
1225
+ "",
1226
+ { input: (ev) => {
1227
+ SEARCH_TEXT = String(ev.target.value || "").toLowerCase().trim();
1228
+ rerender_all();
1229
+ } }
1230
+ ];
1231
+ let copy = [
1232
+ "button",
1233
+ {
1234
+ class: "YDEV_CHIP",
1235
+ type: "button",
1236
+ title: "Copy visible traffic to clipboard"
1237
+ },
1238
+ "Copy",
1239
+ { click: (ev) => {
1240
+ ev.stopPropagation();
1241
+ let btn = ev.currentTarget;
1242
+ dev_copy_text(traffic_to_text()).then(() => {
1243
+ let prev = btn.textContent;
1244
+ btn.textContent = "Copied";
1245
+ setTimeout(() => {
1246
+ btn.textContent = prev;
1247
+ }, 1e3);
1248
+ });
1249
+ } }
1250
+ ];
1251
+ let clear = [
1252
+ "button",
1253
+ {
1254
+ class: "YDEV_CHIP",
1255
+ type: "button",
1256
+ title: "Clear captured traffic"
1257
+ },
1258
+ "Clear",
1259
+ { click: (ev) => {
1260
+ ev.stopPropagation();
1261
+ clear_traffic();
1262
+ } }
1263
+ ];
1264
+ let grp = (label, items) => [
1265
+ "div",
1266
+ { class: "YDEV_GROUP" },
1267
+ [[
1268
+ "span",
1269
+ { class: "YDEV_LABEL" },
1270
+ label
1271
+ ], ...items]
1272
+ ];
1273
+ let sep = () => [
1274
+ "span",
1275
+ { class: "YDEV_SEP" },
1276
+ ""
1277
+ ];
1278
+ return createElement2([
1279
+ "div",
1280
+ { class: "YDEV_BAR" },
1281
+ [
1282
+ grp("Traces", [...trace_chips, simple_mach]),
1283
+ sep(),
1284
+ grp("View", [view_seg]),
1285
+ expand_grp,
1286
+ sep(),
1287
+ grp("Show", [...dir_chips, periodic_chip]),
1288
+ sep(),
1289
+ grp("Find", [search]),
1290
+ sep(),
1291
+ grp("Log", [copy, clear])
1292
+ ]
1293
+ ]);
1294
+ }
1295
+ function build_title_header() {
1296
+ return createElement2([
1297
+ "div",
1298
+ { class: "YDEV_TITLE" },
1299
+ [[
1300
+ "span",
1301
+ { class: "YDEV_TITLE_MAIN" },
1302
+ "Developer"
1303
+ ], [
1304
+ "span",
1305
+ { class: "YDEV_TITLE_SUB" },
1306
+ "yuno monitor · traffic & traces"
1307
+ ]]
1308
+ ]);
1309
+ }
1310
+ function build_dev_body() {
1311
+ return createElement2([
1312
+ "div",
1313
+ { class: "YDEV_BODY" },
1314
+ [
1315
+ build_control_bar(),
1316
+ [
1317
+ "div",
1318
+ {
1319
+ class: "YDEV_MUTED",
1320
+ id: "ydev-muted"
1321
+ },
1322
+ []
1323
+ ],
1324
+ [
1325
+ "div",
1326
+ {
1327
+ class: "YDEV_LOG",
1328
+ id: "developer-traffic-logger"
1329
+ },
1330
+ []
1331
+ ],
1332
+ [
1333
+ "div",
1334
+ {
1335
+ class: "YDEV_STATS",
1336
+ id: "ydev-stats"
1337
+ },
1338
+ []
1339
+ ]
1340
+ ]
1341
+ ]);
1342
+ }
1343
+ /******************************
1344
+ * Public API
1345
+ ******************************/
253
1346
  /************************************************************
254
1347
  * Was the developer window open last session? setup_dev()
255
1348
  * persists open_developer_window (1 on open, 0 on close), so
@@ -263,9 +1356,6 @@ function dev_window_was_open() {
263
1356
  * Apply ALL persisted developer-trace flags to the running
264
1357
  * yuno. Independent of the dev window — call it once at app
265
1358
  * startup so a refresh keeps logging whatever was enabled.
266
- * Single source of truth for "localStorage flag → effect";
267
- * setup_dev() and build_dev_panel() reuse it instead of each
268
- * re-applying a partial subset.
269
1359
  ************************************************************/
270
1360
  function apply_dev_traces() {
271
1361
  let traffic = Number(kw_get_local_storage_value("trace_traffic", 0, false));
@@ -285,176 +1375,58 @@ function apply_dev_traces() {
285
1375
  gobj_write_attr(gobj_yuno(), "trace_subscriptions", subscriptions);
286
1376
  gobj_write_attr(gobj_yuno(), "no_poll", no_poll);
287
1377
  i18next.options.debug = i18n ? true : false;
1378
+ gobj_set_trace_machine_format(Number(kw_get_local_storage_value("dev_automata_simple", 0, false)) ? 1 : 0);
1379
+ set_log_callback(info_log);
288
1380
  }
289
1381
  /************************************************************
290
- * Open the developer panel inside a non-modal C_YUI_WINDOW
1382
+ * Open the developer monitor inside a non-modal C_YUI_WINDOW
291
1383
  * (title bar + maximize + close + resize).
292
1384
  *
293
1385
  * Shell-agnostic: the legacy C_YUI_MAIN shell has a
294
1386
  * '#top-layer' stacking element; the new C_YUI_SHELL does not.
295
- * We pass that element when present, otherwise null — and
296
- * C_YUI_WINDOW falls back to document.body by contract. So the
297
- * new shell gets the same windowed dev panel instead of the
298
- * floating build_dev_panel() box. Legacy behaviour is
299
- * unchanged (when '#top-layer' exists it is still used).
1387
+ * We pass that element when present, otherwise null — C_YUI_WINDOW
1388
+ * falls back to document.body by contract.
300
1389
  ************************************************************/
301
1390
  function setup_dev(self, show) {
302
- let traffic = Number(kw_get_local_storage_value("trace_traffic", 0, false));
303
- let trace = Number(kw_get_local_storage_value("trace_automata", 0, false));
304
- let creation = Number(kw_get_local_storage_value("trace_creation", 0, false));
305
- let start_stop = Number(kw_get_local_storage_value("trace_start_stop", 0, false));
306
- let subscriptions = Number(kw_get_local_storage_value("trace_subscriptions", 0, false));
307
- let i18n = Number(kw_get_local_storage_value("trace_i18n", 0, false));
308
- let no_poll = Number(kw_get_local_storage_value("no_poll", 0, false));
309
1391
  if (show) {
310
- const $dev_toolbar = createElement2([
311
- "div",
312
- { class: "buttons" },
313
- [
314
- [
315
- "button",
316
- { class: "button" },
317
- "Automata",
318
- { click: (evt) => {
319
- evt.stopPropagation();
320
- trace_automata();
321
- } }
322
- ],
323
- [
324
- "button",
325
- { class: "button" },
326
- "Creation",
327
- { click: (evt) => {
328
- evt.stopPropagation();
329
- trace_creation();
330
- } }
331
- ],
332
- [
333
- "button",
334
- { class: "button" },
335
- "Star/Stop",
336
- { click: (evt) => {
337
- evt.stopPropagation();
338
- trace_start_stop();
339
- } }
340
- ],
341
- [
342
- "button",
343
- { class: "button" },
344
- "Subscriptions",
345
- { click: (evt) => {
346
- evt.stopPropagation();
347
- trace_subscriptions();
348
- } }
349
- ],
350
- [
351
- "button",
352
- { class: "button" },
353
- "I18n",
354
- { click: (evt) => {
355
- evt.stopPropagation();
356
- trace_i18n();
357
- } }
358
- ],
359
- [
360
- "button",
361
- { class: "button" },
362
- "Traffic",
363
- { click: (evt) => {
364
- evt.stopPropagation();
365
- trace_traffic();
366
- } }
367
- ],
368
- [
369
- "button",
370
- { class: "button" },
371
- "No Poll",
372
- { click: (evt) => {
373
- evt.stopPropagation();
374
- set_no_poll();
375
- } }
376
- ],
377
- [
378
- "button",
379
- { class: "button" },
380
- "Clear Traffic",
381
- { click: (evt) => {
382
- evt.stopPropagation();
383
- document.getElementById("developer-traffic-logger").innerHTML = "";
384
- } }
385
- ]
386
- ]
387
- ]);
388
- let estados = `
389
- <div>Automata: ${trace}</div>
390
- <div>Creation: ${creation}</div>
391
- <div>Start/Stop: ${start_stop}</div>
392
- <div>Subscriptions: ${subscriptions}</div>
393
- <div>I18n: ${i18n}</div>
394
- <div>Traffic: ${traffic}</div>
395
- <div>No poll: ${no_poll}</div>`;
1392
+ ensure_dev_style();
396
1393
  gobj_create_service("Developer-Window", "C_YUI_WINDOW", {
397
1394
  $parent: document.getElementById("top-layer") || null,
398
1395
  subscriber: null,
399
1396
  showMax: true,
400
1397
  modal: false,
401
- header: $dev_toolbar,
1398
+ header: build_title_header(),
1399
+ body: build_dev_body(),
1400
+ showFooter: false,
402
1401
  auto_save_size_and_position: true,
403
1402
  center: false,
404
- body: "<div style=\"overflow:scroll;height:100%;\"><div id=\"developer-traffic-logger\" style=\"margin-left:10px;margin-right:10px;\"/></div>",
405
- footer: `<div id="developer-window-info" class="is-flex is-justify-content-space-between" style="gap:1.25rem;white-space:nowrap;">${estados}</div>`,
1403
+ title: "Developer",
1404
+ icon: "yi-terminal",
1405
+ manager: gobj_find_service("__window_manager__", false) || null,
406
1406
  on_close: function() {
407
1407
  kw_set_local_storage_value("open_developer_window", 0);
408
1408
  }
409
1409
  }, self);
410
1410
  kw_set_local_storage_value("open_developer_window", 1);
1411
+ setTimeout(() => {
1412
+ refresh_dev_chrome();
1413
+ rerender_all();
1414
+ }, 0);
411
1415
  }
412
1416
  apply_dev_traces();
413
1417
  }
414
1418
  /************************************************************
415
- * Build the developer panel as a self-contained DOM subtree,
1419
+ * Build the developer monitor as a self-contained DOM subtree,
416
1420
  * to be mounted by the new declarative shell via
417
1421
  * yui_shell_show_modal (no C_YUI_WINDOW, no 'top-layer').
418
1422
  *
419
1423
  * Returns { $el, dispose }:
420
- * - $el: the panel element (header tabs + traffic logger
421
- * body + footer counters).
1424
+ * - $el: the panel element (control bar + log + stats).
422
1425
  * - dispose: stops the inter-event traffic trace; call it from
423
1426
  * the modal's on_close.
424
- *
425
- * Backwards compatible: setup_dev() (old shell, C_YUI_WINDOW) is
426
- * untouched; the trace_* helpers and info_traffic are shared.
427
1427
  ************************************************************/
428
1428
  function build_dev_panel() {
429
- let traffic = Number(kw_get_local_storage_value("trace_traffic", 0, false));
430
- let trace = Number(kw_get_local_storage_value("trace_automata", 0, false));
431
- let creation = Number(kw_get_local_storage_value("trace_creation", 0, false));
432
- let start_stop = Number(kw_get_local_storage_value("trace_start_stop", 0, false));
433
- let subscriptions = Number(kw_get_local_storage_value("trace_subscriptions", 0, false));
434
- let i18n = Number(kw_get_local_storage_value("trace_i18n", 0, false));
435
- let no_poll = Number(kw_get_local_storage_value("no_poll", 0, false));
436
- let mk_btn = (label, fn) => [
437
- "button",
438
- { class: "button is-small" },
439
- label,
440
- { click: (evt) => {
441
- evt.stopPropagation();
442
- fn();
443
- } }
444
- ];
445
- let counters = [
446
- `Automata: ${trace}`,
447
- `Creation: ${creation}`,
448
- `Start/Stop: ${start_stop}`,
449
- `Subscriptions: ${subscriptions}`,
450
- `I18n: ${i18n}`,
451
- `Traffic: ${traffic}`,
452
- `No poll: ${no_poll}`
453
- ].map((txt) => [
454
- "div",
455
- { style: "padding:0 8px;" },
456
- txt
457
- ]);
1429
+ ensure_dev_style();
458
1430
  let dark = typeof document !== "undefined" && document.documentElement.getAttribute("data-theme") === "dark";
459
1431
  let surface = dark ? "#1f2733" : "#ffffff";
460
1432
  let fg = dark ? "#e8eaed" : "#0f172a";
@@ -463,53 +1435,15 @@ function build_dev_panel() {
463
1435
  "div",
464
1436
  {
465
1437
  class: "yui-dev-panel",
466
- style: "display:flex;flex-direction:column;box-sizing:border-box;width:100%;height:min(72vh,720px);max-height:82vh;background:" + surface + ";color:" + fg + ";border:1px solid " + bd + ";border-radius:10px;box-shadow:0 10px 30px rgba(0,0,0,0.35);padding:14px;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;"
1438
+ style: "display:flex;flex-direction:column;box-sizing:border-box;width:100%;height:min(72vh,720px);max-height:82vh;background:" + surface + ";color:" + fg + ";border:1px solid " + bd + ";border-radius:10px;box-shadow:0 10px 30px rgba(0,0,0,0.35);overflow:hidden;"
467
1439
  },
468
- [
469
- [
470
- "div",
471
- {
472
- class: "buttons",
473
- style: "flex:0 0 auto;display:flex;flex-wrap:wrap;gap:6px;margin:0 0 8px 0;"
474
- },
475
- [
476
- mk_btn("Automata", trace_automata),
477
- mk_btn("Creation", trace_creation),
478
- mk_btn("Star/Stop", trace_start_stop),
479
- mk_btn("Subscriptions", trace_subscriptions),
480
- mk_btn("I18n", trace_i18n),
481
- mk_btn("Traffic", trace_traffic),
482
- mk_btn("No Poll", set_no_poll),
483
- mk_btn("Clear Traffic", () => {
484
- let l = document.getElementById("developer-traffic-logger");
485
- if (l) l.innerHTML = "";
486
- })
487
- ]
488
- ],
489
- [
490
- "div",
491
- { style: "flex:1 1 auto;min-height:0;overflow:auto;" },
492
- [[
493
- "div",
494
- {
495
- id: "developer-traffic-logger",
496
- style: "margin:0 4px;"
497
- },
498
- []
499
- ]]
500
- ],
501
- [
502
- "div",
503
- {
504
- id: "developer-window-info",
505
- class: "is-flex is-justify-content-space-between",
506
- style: "flex:0 0 auto;border-top:1px solid " + bd + ";padding-top:6px;margin-top:6px;font-size:12px;opacity:0.85;flex-wrap:nowrap;gap:1.25rem;white-space:nowrap;"
507
- },
508
- counters
509
- ]
510
- ]
1440
+ [build_dev_body()]
511
1441
  ]);
512
1442
  apply_dev_traces();
1443
+ setTimeout(() => {
1444
+ refresh_dev_chrome();
1445
+ rerender_all();
1446
+ }, 0);
513
1447
  let dispose = function() {
514
1448
  gobj_write_attr(gobj_yuno(), "trace_inter_event", false);
515
1449
  };
@@ -519,7 +1453,7 @@ function build_dev_panel() {
519
1453
  };
520
1454
  }
521
1455
  //#endregion
522
- //#region yui_toolbar.js
1456
+ //#region src/yui_toolbar.js
523
1457
  function yui_toolbar(attrs = {}, items = []) {
524
1458
  const left_button = `
525
1459
  <span class="has-text-primary is-flex"><svg viewBox="0 0 320 512" style="fill:var(--bulma-link)"><path class="fa-secondary" opacity="1" d="M41.4 278.6c-12.5-12.5-12.5-32.8 0-45.3l160-160c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3L109.3 256 246.6 393.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-160-160z"/><path class="fa-primary" d=""/></svg></span>
@@ -584,7 +1518,7 @@ function yui_toolbar(attrs = {}, items = []) {
584
1518
  return $toolbar;
585
1519
  }
586
1520
  //#endregion
587
- //#region c_yui_main.js
1521
+ //#region src/c_yui_main.js
588
1522
  /***********************************************************************
589
1523
  * yui_main.js
590
1524
  *
@@ -620,11 +1554,11 @@ function yui_toolbar(attrs = {}, items = []) {
620
1554
  /***************************************************************
621
1555
  * Constants
622
1556
  ***************************************************************/
623
- var GCLASS_NAME$16 = "C_YUI_MAIN";
1557
+ var GCLASS_NAME$17 = "C_YUI_MAIN";
624
1558
  /***************************************************************
625
1559
  * Data
626
1560
  ***************************************************************/
627
- var attrs_table$16 = [
1561
+ var attrs_table$17 = [
628
1562
  SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of output events"),
629
1563
  SDATA(data_type_t.DTP_STRING, "username", 0, "", "username logged"),
630
1564
  SDATA(data_type_t.DTP_STRING, "publi_page", 0, "publi_page", "Publicity page shown when user is not logged in"),
@@ -640,16 +1574,16 @@ var attrs_table$16 = [
640
1574
  SDATA(data_type_t.DTP_STRING, "color_user_logout", 0, "#FF4500", "OrangeRed - user logged out"),
641
1575
  SDATA_END()
642
1576
  ];
643
- var PRIVATE_DATA$16 = {};
644
- var __gclass__$16 = null;
1577
+ var PRIVATE_DATA$17 = {};
1578
+ var __gclass__$17 = null;
645
1579
  /******************************
646
1580
  * Framework Methods
647
1581
  ******************************/
648
1582
  /***************************************************************
649
1583
  * Framework Method: Create
650
1584
  ***************************************************************/
651
- function mt_create$16(gobj) {
652
- build_ui$13(gobj);
1585
+ function mt_create$17(gobj) {
1586
+ build_ui$14(gobj);
653
1587
  set_theme(gobj, kw_get_local_storage_value("theme", "light", true));
654
1588
  const subscriber = gobj_read_pointer_attr(gobj, "subscriber");
655
1589
  if (subscriber) gobj_subscribe_event(gobj, null, {}, subscriber);
@@ -667,19 +1601,19 @@ function mt_stop$16(gobj) {}
667
1601
  /***************************************************************
668
1602
  * Framework Method: Destroy
669
1603
  ***************************************************************/
670
- function mt_destroy$16(gobj) {}
1604
+ function mt_destroy$17(gobj) {}
671
1605
  /***************************
672
1606
  * Local Methods
673
1607
  ***************************/
674
1608
  /************************************************************
675
1609
  * Build UI
676
1610
  ************************************************************/
677
- function build_ui$13(gobj) {
678
- const $layout = createElement2([
1611
+ function build_ui$14(gobj) {
1612
+ const $container = createElement2([
679
1613
  "div",
680
1614
  {
681
1615
  id: "root",
682
- class: "root"
1616
+ class: "C_YUI_MAIN root"
683
1617
  },
684
1618
  [
685
1619
  ["div", {
@@ -712,7 +1646,7 @@ function build_ui$13(gobj) {
712
1646
  ]
713
1647
  ]
714
1648
  ]);
715
- document.body.appendChild($layout);
1649
+ document.body.appendChild($container);
716
1650
  const iframe = document.getElementById("iframe-publi-page");
717
1651
  if (iframe) iframe.onload = () => {
718
1652
  const theme = kw_get_local_storage_value("theme", "light", true);
@@ -1626,17 +2560,17 @@ function ac_timeout$1(gobj, event, kw, src) {
1626
2560
  /***************************
1627
2561
  * FSM
1628
2562
  ***************************/
1629
- var gmt$16 = {
1630
- mt_create: mt_create$16,
2563
+ var gmt$17 = {
2564
+ mt_create: mt_create$17,
1631
2565
  mt_start: mt_start$16,
1632
2566
  mt_stop: mt_stop$16,
1633
- mt_destroy: mt_destroy$16
2567
+ mt_destroy: mt_destroy$17
1634
2568
  };
1635
2569
  /***************************************************************
1636
2570
  * Create the GClass
1637
2571
  ***************************************************************/
1638
- function create_gclass$16(gclass_name) {
1639
- if (__gclass__$16) {
2572
+ function create_gclass$17(gclass_name) {
2573
+ if (__gclass__$17) {
1640
2574
  log_error(`GClass ALREADY created: ${gclass_name}`);
1641
2575
  return -1;
1642
2576
  }
@@ -1720,7 +2654,7 @@ function create_gclass$16(gclass_name) {
1720
2654
  null
1721
2655
  ]
1722
2656
  ]]];
1723
- __gclass__$16 = gclass_create(gclass_name, [
2657
+ __gclass__$17 = gclass_create(gclass_name, [
1724
2658
  ["EV_LOGIN_ACCEPTED", 0],
1725
2659
  ["EV_LOGIN_DENIED", 0],
1726
2660
  ["EV_LOGIN_REFRESHED", 0],
@@ -1738,18 +2672,18 @@ function create_gclass$16(gclass_name) {
1738
2672
  ["EV_ON_CLOSE", 0],
1739
2673
  ["EV_DO_RECOVERY_PASSWORD", 0],
1740
2674
  ["EV_TIMEOUT", 0]
1741
- ], states, gmt$16, 0, attrs_table$16, PRIVATE_DATA$16, 0, 0, 0, 0);
1742
- if (!__gclass__$16) return -1;
2675
+ ], states, gmt$17, 0, attrs_table$17, PRIVATE_DATA$17, 0, 0, 0, 0);
2676
+ if (!__gclass__$17) return -1;
1743
2677
  return 0;
1744
2678
  }
1745
2679
  /***************************************************************
1746
2680
  * Register GClass
1747
2681
  ***************************************************************/
1748
2682
  function register_c_yui_main() {
1749
- return create_gclass$16(GCLASS_NAME$16);
2683
+ return create_gclass$17(GCLASS_NAME$17);
1750
2684
  }
1751
2685
  //#endregion
1752
- //#region c_yui_window.js
2686
+ //#region src/c_yui_window.js
1753
2687
  /***********************************************************************
1754
2688
  * c_yui_window.js
1755
2689
  *
@@ -1761,11 +2695,16 @@ function register_c_yui_main() {
1761
2695
  /***************************************************************
1762
2696
  * Constants
1763
2697
  ***************************************************************/
1764
- var GCLASS_NAME$15 = "C_YUI_WINDOW";
2698
+ var GCLASS_NAME$16 = "C_YUI_WINDOW";
2699
+ var WC_MIN = "<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><rect x=\"3\" y=\"11\" width=\"10\" height=\"1.6\" rx=\"0.8\" fill=\"currentColor\"/></svg>";
2700
+ var WC_MAX = "<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><rect x=\"3.3\" y=\"3.3\" width=\"9.4\" height=\"9.4\" rx=\"1.6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"/></svg>";
2701
+ var WC_RESTORE = "<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><rect x=\"5.2\" y=\"3\" width=\"7.8\" height=\"7.8\" rx=\"1.3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.4\"/><rect x=\"3\" y=\"5.2\" width=\"7.8\" height=\"7.8\" rx=\"1.3\" fill=\"var(--bulma-scheme-main)\" stroke=\"currentColor\" stroke-width=\"1.4\"/></svg>";
2702
+ var WC_CLOSE = "<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><path d=\"M4 4 L12 12 M12 4 L4 12\" stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\"/></svg>";
2703
+ var MOBILE_MAX = 768;
1765
2704
  /***************************************************************
1766
2705
  * Data
1767
2706
  ***************************************************************/
1768
- var attrs_table$15 = [
2707
+ var attrs_table$16 = [
1769
2708
  SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of output events"),
1770
2709
  SDATA(data_type_t.DTP_POINTER, "$parent", 0, null, "$container will be appended to $parent if not null, else to document.body"),
1771
2710
  SDATA(data_type_t.DTP_INTEGER, "x", 0, "300", "X position"),
@@ -1783,9 +2722,13 @@ var attrs_table$15 = [
1783
2722
  SDATA(data_type_t.DTP_BOOLEAN, "showFooter", 0, true, "Show footer"),
1784
2723
  SDATA(data_type_t.DTP_BOOLEAN, "openMaximized", 0, false, "Open the window maximized"),
1785
2724
  SDATA(data_type_t.DTP_BOOLEAN, "showMax", 0, true, "Show maximize button"),
2725
+ SDATA(data_type_t.DTP_BOOLEAN, "showMin", 0, true, "Show minimize (roll-up / shade) button"),
1786
2726
  SDATA(data_type_t.DTP_BOOLEAN, "maximized", 0, false, "Flag to indicate if maximized"),
1787
2727
  SDATA(data_type_t.DTP_JSON, "window_style", 0, "{}", "Override window style"),
1788
2728
  SDATA(data_type_t.DTP_POINTER, "on_close", 0, null, "Callback on destroy"),
2729
+ SDATA(data_type_t.DTP_POINTER, "manager", 0, null, "Optional C_YUI_WINDOW_MANAGER (gobj or service name) for dock/taskbar"),
2730
+ SDATA(data_type_t.DTP_STRING, "title", 0, "", "Window title, shown on the dock chip"),
2731
+ SDATA(data_type_t.DTP_STRING, "icon", 0, "", "Dock-chip icon: a yi-* class name or inline SVG (by window type)"),
1789
2732
  SDATA(data_type_t.DTP_POINTER, "focus", 0, null, "Brings focus to the element, can be a number or selector"),
1790
2733
  SDATA(data_type_t.DTP_BOOLEAN, "modal", 0, false, "Enable modal mode"),
1791
2734
  SDATA(data_type_t.DTP_BOOLEAN, "keyboard", 0, true, "Close window on ESC if not modal"),
@@ -1794,19 +2737,35 @@ var attrs_table$15 = [
1794
2737
  SDATA(data_type_t.DTP_POINTER, "win_resize_handler", 0, null, "Internal: native window 'resize' listener"),
1795
2738
  SDATA_END()
1796
2739
  ];
1797
- var PRIVATE_DATA$15 = { prevSize: 0 };
1798
- var __gclass__$15 = null;
2740
+ var PRIVATE_DATA$16 = {
2741
+ prevSize: 0,
2742
+ preShadeHeight: 0
2743
+ };
2744
+ var __gclass__$16 = null;
1799
2745
  /******************************
1800
2746
  * Framework Methods
1801
2747
  ******************************/
1802
2748
  /***************************************************************
1803
2749
  * Framework Method: Create
1804
2750
  ***************************************************************/
1805
- function mt_create$15(gobj) {
2751
+ function mt_create$16(gobj) {
1806
2752
  const subscriber = gobj_read_pointer_attr(gobj, "subscriber");
1807
2753
  if (subscriber) gobj_subscribe_event(gobj, null, {}, subscriber);
1808
2754
  gobj_write_attr(gobj, "window_id", "window-" + clean_name(gobj_short_name(gobj)));
1809
- build_ui$12(gobj);
2755
+ build_ui$13(gobj);
2756
+ let manager = resolve_manager(gobj);
2757
+ if (manager) {
2758
+ let $c = gobj_read_attr(gobj, "$container");
2759
+ if ($c) $c.addEventListener("pointerdown", function() {
2760
+ let m = gobj_read_pointer_attr(gobj, "manager");
2761
+ if (m) gobj_send_event(m, "EV_FOCUS_WINDOW", { window: gobj }, gobj);
2762
+ }, true);
2763
+ gobj_send_event(manager, "EV_REGISTER_WINDOW", {
2764
+ window: gobj,
2765
+ title: gobj_read_attr(gobj, "title") || gobj_short_name(gobj),
2766
+ icon: gobj_read_attr(gobj, "icon") || ""
2767
+ }, gobj);
2768
+ }
1810
2769
  let on_win_resize = function() {
1811
2770
  handleResize(gobj);
1812
2771
  };
@@ -1830,7 +2789,9 @@ function mt_stop$15(gobj) {
1830
2789
  /***************************************************************
1831
2790
  * Framework Method: Destroy
1832
2791
  ***************************************************************/
1833
- function mt_destroy$15(gobj) {
2792
+ function mt_destroy$16(gobj) {
2793
+ let manager = gobj_read_pointer_attr(gobj, "manager");
2794
+ if (manager) gobj_send_event(manager, "EV_UNREGISTER_WINDOW", { window: gobj }, gobj);
1834
2795
  let on_win_resize = gobj_read_attr(gobj, "win_resize_handler");
1835
2796
  if (on_win_resize) {
1836
2797
  window.removeEventListener("resize", on_win_resize);
@@ -1842,9 +2803,97 @@ function mt_destroy$15(gobj) {
1842
2803
  * Local Methods
1843
2804
  ***************************/
1844
2805
  /************************************************************
2806
+ * Inject the window-chrome stylesheet once. Theme-aware via
2807
+ * Bulma scheme vars (flips on <html data-theme>); replaces the
2808
+ * old saturated `has-background-info` bar + forced black text.
2809
+ ************************************************************/
2810
+ function ensure_window_style() {
2811
+ if (document.getElementById("yui-window-style")) return;
2812
+ let css = `
2813
+ .yui-window-header {
2814
+ background: var(--bulma-scheme-main-bis);
2815
+ color: var(--bulma-text-strong);
2816
+ -webkit-user-select: none; user-select: none;
2817
+ }
2818
+ .yui-window-titlebar-controls { display: flex; align-items: center; gap: 2px; padding-left: 6px; }
2819
+ .yui-wc {
2820
+ width: 30px; height: 26px; display: inline-flex; align-items: center; justify-content: center;
2821
+ padding: 0; border: 0; background: transparent; color: var(--bulma-text); cursor: pointer;
2822
+ border-radius: 5px; -webkit-tap-highlight-color: transparent;
2823
+ }
2824
+ .yui-wc:hover { background: var(--bulma-scheme-main-ter); color: var(--bulma-text-strong); }
2825
+ .yui-wc.wc-close:hover { background: #e0364a; color: #fff; }
2826
+ .yui-wc svg { width: 15px; height: 15px; display: block; }
2827
+ .C_YUI_WINDOW.is-shaded .yui-window-body,
2828
+ .C_YUI_WINDOW.is-shaded .yui-window-footer { display: none !important; }
2829
+ .C_YUI_WINDOW.is-shaded { height: auto !important; }
2830
+ .yui-window-resize { color: var(--bulma-text-weak); opacity: 0.55; }
2831
+ .yui-window-resize:hover { opacity: 1; color: var(--bulma-text); }
2832
+ @media (max-width: ${MOBILE_MAX}px) {
2833
+ .C_YUI_WINDOW.is-mobile-sheet { border-radius: 0 !important; box-shadow: none !important; }
2834
+ .C_YUI_WINDOW.is-mobile-sheet .yui-window-resize { display: none !important; }
2835
+ .yui-wc { width: 42px; height: 38px; }
2836
+ .yui-wc.wc-max { display: none !important; }
2837
+ .yui-wc svg { width: 18px; height: 18px; }
2838
+ }
2839
+ `;
2840
+ let $style = document.createElement("style");
2841
+ $style.id = "yui-window-style";
2842
+ $style.textContent = css;
2843
+ document.head.appendChild($style);
2844
+ }
2845
+ /************************************************************
2846
+ * Below the mobile breakpoint the window is a full-screen
2847
+ * sheet (no float / drag / resize).
2848
+ ************************************************************/
2849
+ function is_mobile() {
2850
+ return (window.innerWidth || document.documentElement.offsetWidth) <= MOBILE_MAX;
2851
+ }
2852
+ /************************************************************
2853
+ * Resolve the optional `manager` attr (a gobj or a service
2854
+ * name) to a gobj, caching the resolution back into the attr.
2855
+ ************************************************************/
2856
+ function resolve_manager(gobj) {
2857
+ let m = gobj_read_pointer_attr(gobj, "manager");
2858
+ if (!m) return null;
2859
+ if (typeof m === "string") {
2860
+ m = gobj_find_service(m, false);
2861
+ gobj_write_attr(gobj, "manager", m);
2862
+ }
2863
+ return m;
2864
+ }
2865
+ /************************************************************
2866
+ * Minimize: with a manager, send the window to the dock;
2867
+ * otherwise "shade" it in place. Called by the minimize button.
2868
+ ************************************************************/
2869
+ function do_minimize(gobj) {
2870
+ let manager = gobj_read_pointer_attr(gobj, "manager");
2871
+ if (manager) gobj_send_event(manager, "EV_MINIMIZE_WINDOW", { window: gobj }, gobj);
2872
+ else toggle_shade(gobj);
2873
+ }
2874
+ /************************************************************
2875
+ * Minimize as "shade": roll the window up to its title bar
2876
+ * in place (self-contained; no window manager needed). Toggles.
2877
+ ************************************************************/
2878
+ function toggle_shade(gobj) {
2879
+ let priv = gobj.priv;
2880
+ let $container = gobj_read_attr(gobj, "$container");
2881
+ if (!$container) return;
2882
+ if ($container.classList.contains("is-shaded")) {
2883
+ $container.classList.remove("is-shaded");
2884
+ if (priv.preShadeHeight) $container.style.height = parseInt(priv.preShadeHeight) + "px";
2885
+ } else {
2886
+ priv.preShadeHeight = $container.getBoundingClientRect().height;
2887
+ $container.classList.add("is-shaded");
2888
+ $container.style.height = "auto";
2889
+ }
2890
+ }
2891
+ /************************************************************
1845
2892
  * Build UI
1846
2893
  ************************************************************/
1847
- function build_ui$12(gobj) {
2894
+ function build_ui$13(gobj) {
2895
+ ensure_window_style();
2896
+ let mobile = is_mobile();
1848
2897
  let header = gobj_read_attr(gobj, "header");
1849
2898
  if (is_gobj(header)) header = gobj_read_attr(header, "$container");
1850
2899
  let body = gobj_read_attr(gobj, "body");
@@ -1867,6 +2916,12 @@ function build_ui$12(gobj) {
1867
2916
  }
1868
2917
  let rect = do_fix_dimension_to_screen(gobj, gobj_read_integer_attr(gobj, "x"), gobj_read_integer_attr(gobj, "y"), gobj_read_integer_attr(gobj, "width"), gobj_read_integer_attr(gobj, "height"));
1869
2918
  if (gobj_read_bool_attr(gobj, "center")) rect = do_center(gobj, rect.x, rect.y, rect.width, rect.height);
2919
+ if (mobile) rect = {
2920
+ x: 0,
2921
+ y: 0,
2922
+ width: window.innerWidth || document.documentElement.offsetWidth,
2923
+ height: window.innerHeight || document.documentElement.offsetHeight
2924
+ };
1870
2925
  let window_style = {
1871
2926
  position: "fixed",
1872
2927
  "z-index": 3,
@@ -1888,14 +2943,14 @@ function build_ui$12(gobj) {
1888
2943
  let $container = createElement2([
1889
2944
  "div",
1890
2945
  {
1891
- class: "yui-window strong-shadow is-flex is-flex-direction-column",
2946
+ class: "C_YUI_WINDOW strong-shadow is-flex is-flex-direction-column" + (mobile ? " is-mobile-sheet" : ""),
1892
2947
  style: window_style
1893
2948
  },
1894
2949
  [
1895
2950
  [
1896
2951
  "div",
1897
2952
  {
1898
- class: "yui-window-header p-1 is-flex-shrink-0 is-flex is-flex-nowrap is-justify-content-space-between is-align-items-flex-start has-text-black has-background-info",
2953
+ class: "yui-window-header p-1 is-flex-shrink-0 is-flex is-flex-nowrap is-justify-content-space-between is-align-items-center",
1899
2954
  style: "border-bottom:1px solid var(--bulma-border); cursor:move; box-sizing: border-box;"
1900
2955
  },
1901
2956
  [[
@@ -1915,37 +2970,52 @@ function build_ui$12(gobj) {
1915
2970
  ], [
1916
2971
  "div",
1917
2972
  {
1918
- class: "is-flex-shrink-0 is-flex is-flex-nowrap is-align-items-flex-start",
2973
+ class: "yui-window-titlebar-controls is-flex-shrink-0",
1919
2974
  style: "position:relative; z-index:1; cursor:default;"
1920
2975
  },
1921
- [[
1922
- "button",
1923
- {
1924
- class: "without-border pr-4",
1925
- style: {
1926
- color: "var(--bulma-text)",
1927
- "font-size": "1.4em",
1928
- "cursor": "pointer",
1929
- "display": gobj_read_bool_attr(gobj, "showMax") ? "inline-block" : "none"
1930
- }
1931
- },
1932
- "<i class=\"yi-square\"></i>",
1933
- { click: (evt) => {
1934
- evt.stopPropagation();
1935
- toggle(gobj);
1936
- } }
1937
- ], [
1938
- "button",
1939
- {
1940
- class: "without-border pr-2",
1941
- style: "color:var(--bulma-text);font-size:1.6em;cursor:pointer;"
1942
- },
1943
- "<i class=\"yi-xmark\"></i>",
1944
- { click: (evt) => {
1945
- evt.stopPropagation();
1946
- close_window(gobj);
1947
- } }
1948
- ]]
2976
+ [
2977
+ [
2978
+ "button",
2979
+ {
2980
+ class: "yui-wc wc-min",
2981
+ type: "button",
2982
+ "aria-label": "minimize",
2983
+ style: gobj_read_bool_attr(gobj, "showMin") ? "" : "display:none;"
2984
+ },
2985
+ WC_MIN,
2986
+ { click: (evt) => {
2987
+ evt.stopPropagation();
2988
+ do_minimize(gobj);
2989
+ } }
2990
+ ],
2991
+ [
2992
+ "button",
2993
+ {
2994
+ class: "yui-wc wc-max",
2995
+ type: "button",
2996
+ "aria-label": "maximize",
2997
+ style: gobj_read_bool_attr(gobj, "showMax") ? "" : "display:none;"
2998
+ },
2999
+ WC_MAX,
3000
+ { click: (evt) => {
3001
+ evt.stopPropagation();
3002
+ toggle(gobj);
3003
+ } }
3004
+ ],
3005
+ [
3006
+ "button",
3007
+ {
3008
+ class: "yui-wc wc-close",
3009
+ type: "button",
3010
+ "aria-label": "close"
3011
+ },
3012
+ WC_CLOSE,
3013
+ { click: (evt) => {
3014
+ evt.stopPropagation();
3015
+ close_window(gobj);
3016
+ } }
3017
+ ]
3018
+ ]
1949
3019
  ]],
1950
3020
  { pointerdown: (evt) => {
1951
3021
  if (evt.target.closest && evt.target.closest("button")) return;
@@ -2030,7 +3100,7 @@ function build_ui$12(gobj) {
2030
3100
  let $resizable_btn = createElement2([
2031
3101
  "div",
2032
3102
  {
2033
- class: "without-border",
3103
+ class: "without-border yui-window-resize",
2034
3104
  style: {
2035
3105
  cursor: "nwse-resize",
2036
3106
  display: "flex",
@@ -2057,7 +3127,7 @@ function build_ui$12(gobj) {
2057
3127
  if ($parent) $parent.appendChild($container);
2058
3128
  else document.body.appendChild($container);
2059
3129
  if (gobj_read_bool_attr(gobj, "openMaximized")) max$2(gobj);
2060
- else {
3130
+ else if (!mobile) {
2061
3131
  if (gobj_read_bool_attr(gobj, "content_size")) $container.style.height = "auto";
2062
3132
  if (gobj_read_bool_attr(gobj, "center")) {
2063
3133
  rect = $container.getBoundingClientRect();
@@ -2118,9 +3188,22 @@ function max$2(gobj) {
2118
3188
  $container.style.width = parseInt(rect.width) + "px";
2119
3189
  $container.style.height = parseInt(rect.height) + "px";
2120
3190
  gobj_write_bool_attr(gobj, "maximized", true);
3191
+ set_max_icon(gobj, true);
2121
3192
  return rect;
2122
3193
  }
2123
3194
  /************************************************************
3195
+ * Swap the maximize/restore glyph to match the state.
3196
+ ************************************************************/
3197
+ function set_max_icon(gobj, maximized) {
3198
+ let $container = gobj_read_attr(gobj, "$container");
3199
+ if (!$container) return;
3200
+ let $btn = $container.querySelector(".wc-max");
3201
+ if ($btn) {
3202
+ $btn.innerHTML = maximized ? WC_RESTORE : WC_MAX;
3203
+ $btn.setAttribute("aria-label", maximized ? "restore" : "maximize");
3204
+ }
3205
+ }
3206
+ /************************************************************
2124
3207
  *
2125
3208
  ************************************************************/
2126
3209
  function min$1(gobj) {
@@ -2133,12 +3216,14 @@ function min$1(gobj) {
2133
3216
  $container.style.width = parseInt(rect.width) + "px";
2134
3217
  $container.style.height = parseInt(rect.height) + "px";
2135
3218
  gobj_write_bool_attr(gobj, "maximized", false);
3219
+ set_max_icon(gobj, false);
2136
3220
  return rect;
2137
3221
  }
2138
3222
  /************************************************************
2139
3223
  * handlers moving
2140
3224
  ************************************************************/
2141
3225
  function mvStart(gobj, evt) {
3226
+ if (is_mobile()) return;
2142
3227
  let $container = gobj_read_attr(gobj, "$container");
2143
3228
  let window_rect = $container.getBoundingClientRect();
2144
3229
  let x = evt.screenX;
@@ -2295,6 +3380,17 @@ function do_center(gobj, x, y, width, height) {
2295
3380
  function handleResize(gobj) {
2296
3381
  let $container = gobj_read_attr(gobj, "$container");
2297
3382
  if (!$container) return;
3383
+ if (is_mobile()) {
3384
+ $container.classList.add("is-mobile-sheet");
3385
+ let vw = window.innerWidth || document.documentElement.offsetWidth;
3386
+ let vh = window.innerHeight || document.documentElement.offsetHeight;
3387
+ $container.style.left = "0px";
3388
+ $container.style.top = "0px";
3389
+ $container.style.width = vw + "px";
3390
+ $container.style.height = vh + "px";
3391
+ return;
3392
+ }
3393
+ $container.classList.remove("is-mobile-sheet");
2298
3394
  if (gobj_read_bool_attr(gobj, "maximized") === true) {
2299
3395
  let r = do_fix_dimension_to_screen(gobj, 0, 0, 1e4, 1e4);
2300
3396
  if (gobj_read_bool_attr(gobj, "center")) r = do_center(gobj, r.x, r.y, r.width, r.height);
@@ -2339,20 +3435,28 @@ function ac_show$11(gobj, event, kw, src) {
2339
3435
  function ac_hide$11(gobj, event, kw, src) {
2340
3436
  return 0;
2341
3437
  }
3438
+ /************************************************************
3439
+ * EV_CLOSE_WINDOW — close from outside (e.g. the dock chip ✕),
3440
+ * the same teardown as the title-bar close button.
3441
+ ************************************************************/
3442
+ function ac_close(gobj, event, kw, src) {
3443
+ close_window(gobj);
3444
+ return 0;
3445
+ }
2342
3446
  /***************************
2343
3447
  * FSM
2344
3448
  ***************************/
2345
- var gmt$15 = {
2346
- mt_create: mt_create$15,
3449
+ var gmt$16 = {
3450
+ mt_create: mt_create$16,
2347
3451
  mt_start: mt_start$15,
2348
3452
  mt_stop: mt_stop$15,
2349
- mt_destroy: mt_destroy$15
3453
+ mt_destroy: mt_destroy$16
2350
3454
  };
2351
3455
  /***************************************************************
2352
3456
  * Create the GClass
2353
3457
  ***************************************************************/
2354
- function create_gclass$15(gclass_name) {
2355
- if (__gclass__$15) {
3458
+ function create_gclass$16(gclass_name) {
3459
+ if (__gclass__$16) {
2356
3460
  log_error(`GClass ALREADY created: ${gclass_name}`);
2357
3461
  return -1;
2358
3462
  }
@@ -2376,28 +3480,481 @@ function create_gclass$15(gclass_name) {
2376
3480
  "EV_HIDE",
2377
3481
  ac_hide$11,
2378
3482
  null
3483
+ ],
3484
+ [
3485
+ "EV_CLOSE_WINDOW",
3486
+ ac_close,
3487
+ null
2379
3488
  ]
2380
3489
  ]]];
2381
- __gclass__$15 = gclass_create(gclass_name, [
3490
+ __gclass__$16 = gclass_create(gclass_name, [
2382
3491
  ["EV_WINDOW_TO_CLOSE", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
2383
3492
  ["EV_WINDOW_MOVED", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
2384
3493
  ["EV_WINDOW_RESIZED", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
2385
3494
  ["EV_RESIZE", 0],
2386
3495
  ["EV_REFRESH", 0],
2387
3496
  ["EV_SHOW", 0],
2388
- ["EV_HIDE", 0]
2389
- ], states, gmt$15, 0, attrs_table$15, PRIVATE_DATA$15, 0, 0, 0, 0);
2390
- if (!__gclass__$15) return -1;
3497
+ ["EV_HIDE", 0],
3498
+ ["EV_CLOSE_WINDOW", 0]
3499
+ ], states, gmt$16, 0, attrs_table$16, PRIVATE_DATA$16, 0, 0, 0, 0);
3500
+ if (!__gclass__$16) return -1;
2391
3501
  return 0;
2392
3502
  }
2393
3503
  /***************************************************************
2394
3504
  * Register GClass
2395
3505
  ***************************************************************/
2396
3506
  function register_c_yui_window() {
3507
+ return create_gclass$16(GCLASS_NAME$16);
3508
+ }
3509
+ //#endregion
3510
+ //#region src/c_yui_window_manager.js
3511
+ /***********************************************************************
3512
+ * c_yui_window_manager.js
3513
+ *
3514
+ * Window manager / dock (taskbar).
3515
+ *
3516
+ * A light registry + dock strip for C_YUI_WINDOW instances. Windows
3517
+ * opt in via their `manager` attr: on create they REGISTER, on
3518
+ * destroy they UNREGISTER, their minimize button MINIMIZEs (roll to
3519
+ * the dock instead of shading in place), and any pointer press
3520
+ * FOCUSes (raise z-order + highlight the dock chip).
3521
+ *
3522
+ * The manager never owns window lifecycle: it only toggles a
3523
+ * window's `$container` display / z-index and reflects state in the
3524
+ * dock. Closing stays the window's own affordance (its ✕ →
3525
+ * gobj_destroy → EV_UNREGISTER_WINDOW removes the chip).
3526
+ *
3527
+ * Orthogonal to C_YUI_PAGER (page-stack): a window may host a pager
3528
+ * in its body; the manager only arranges the windows themselves.
3529
+ *
3530
+ * The dock placement is configurable via `dock_mode`:
3531
+ * - "floating" (default) a detached bar pinned to a corner
3532
+ * (`dock_corner`), hovering over the content.
3533
+ * - "inline" a full-width taskbar row mounted inside a layout
3534
+ * container (`inline_selector`, e.g. a shell zone).
3535
+ * - "responsive" floating on wide viewports, inline on narrow ones
3536
+ * (`responsive_query`) — so on mobile it lives above
3537
+ * the bottom menu instead of covering it.
3538
+ *
3539
+ * Copyright (c) 2026, ArtGins.
3540
+ * All Rights Reserved.
3541
+ ***********************************************************************/
3542
+ /***************************************************************
3543
+ * Constants
3544
+ ***************************************************************/
3545
+ var GCLASS_NAME$15 = "C_YUI_WINDOW_MANAGER";
3546
+ var DOCK_Z = 1e6;
3547
+ var WC_X = "<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><path d=\"M4.5 4.5 L11.5 11.5 M11.5 4.5 L4.5 11.5\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"/></svg>";
3548
+ /***************************************************************
3549
+ * Data
3550
+ ***************************************************************/
3551
+ var attrs_table$15 = [
3552
+ SDATA(data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of output events"),
3553
+ SDATA(data_type_t.DTP_STRING, "dock_mode", 0, "floating", "floating | inline | responsive (floating on wide screens, inline on narrow)"),
3554
+ SDATA(data_type_t.DTP_STRING, "dock_corner", 0, "bottom-left", "Floating placement: top-left | top-right | bottom-left | bottom-right"),
3555
+ SDATA(data_type_t.DTP_STRING, "inline_selector", 0, "", "CSS selector of the container the dock mounts into when inline (e.g. a shell zone)"),
3556
+ SDATA(data_type_t.DTP_STRING, "responsive_query", 0, "(max-width: 768px)", "Media query that selects inline placement when dock_mode is 'responsive'"),
3557
+ SDATA(data_type_t.DTP_POINTER, "$parent", 0, null, "Where the floating dock mounts (default document.body)"),
3558
+ SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "Internal: the dock element"),
3559
+ SDATA_END()
3560
+ ];
3561
+ var PRIVATE_DATA$15 = {
3562
+ windows: null,
3563
+ z: 0,
3564
+ mql: null,
3565
+ mql_handler: null
3566
+ };
3567
+ var __gclass__$15 = null;
3568
+ /******************************
3569
+ * Framework Methods
3570
+ ******************************/
3571
+ /***************************************************************
3572
+ * Framework Method: Create
3573
+ ***************************************************************/
3574
+ function mt_create$15(gobj) {
3575
+ let priv = gobj.priv;
3576
+ priv.windows = [];
3577
+ priv.z = 10;
3578
+ if ((gobj_read_attr(gobj, "dock_mode") || "floating") === "responsive") {
3579
+ let q = gobj_read_attr(gobj, "responsive_query") || "(max-width: 768px)";
3580
+ priv.mql = window.matchMedia(q);
3581
+ priv.mql_handler = () => {
3582
+ apply_placement(gobj);
3583
+ };
3584
+ priv.mql.addEventListener("change", priv.mql_handler);
3585
+ }
3586
+ build_dock(gobj);
3587
+ const subscriber = gobj_read_pointer_attr(gobj, "subscriber");
3588
+ if (subscriber) gobj_subscribe_event(gobj, null, {}, subscriber);
3589
+ }
3590
+ /***************************************************************
3591
+ * Framework Method: Destroy
3592
+ ***************************************************************/
3593
+ function mt_destroy$15(gobj) {
3594
+ let priv = gobj.priv;
3595
+ if (priv.mql && priv.mql_handler) {
3596
+ priv.mql.removeEventListener("change", priv.mql_handler);
3597
+ priv.mql = null;
3598
+ priv.mql_handler = null;
3599
+ }
3600
+ destroy_dock(gobj);
3601
+ }
3602
+ /***************************
3603
+ * Local Methods
3604
+ ***************************/
3605
+ /************************************************************
3606
+ * Inject the dock stylesheet once (theme-aware).
3607
+ ************************************************************/
3608
+ function ensure_dock_style() {
3609
+ if (document.getElementById("yui-dock-style")) return;
3610
+ let css = `
3611
+ .yui-dock {
3612
+ z-index: ${DOCK_Z};
3613
+ display: flex; gap: 6px; align-items: center; padding: 5px 8px;
3614
+ overflow-x: auto;
3615
+ background: var(--bulma-scheme-main-bis); color: var(--bulma-text);
3616
+ border: 1px solid var(--bulma-border);
3617
+ font-family: var(--bulma-family-primary, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
3618
+ }
3619
+ .yui-dock.is-empty { display: none; }
3620
+
3621
+ /* Floating: a detached bar hovering over the content, pinned to a corner. */
3622
+ .yui-dock--floating {
3623
+ position: fixed;
3624
+ max-width: calc(100vw - 24px);
3625
+ border-radius: 10px;
3626
+ box-shadow: 0 6px 22px rgba(0,0,0,0.25);
3627
+ }
3628
+ .yui-dock--bottom-left { bottom: 12px; left: 12px; }
3629
+ .yui-dock--bottom-right { bottom: 12px; right: 12px; }
3630
+ .yui-dock--top-left { top: 12px; left: 12px; }
3631
+ .yui-dock--top-right { top: 12px; right: 12px; }
3632
+
3633
+ /* Inline: flows inside a layout zone as a full-width taskbar row (the zone
3634
+ * already provides the separating border, so the bar itself stays flat). */
3635
+ .yui-dock--inline {
3636
+ position: relative;
3637
+ width: 100%; max-width: 100%;
3638
+ border: 0;
3639
+ border-radius: 0;
3640
+ box-shadow: none;
3641
+ }
3642
+ .yui-dock-chip {
3643
+ display: inline-flex; align-items: center; gap: 7px; font: inherit; font-size: 12px;
3644
+ padding: 4px 5px 4px 11px; border: 1px solid var(--bulma-border); border-radius: 7px;
3645
+ background: var(--bulma-scheme-main); color: var(--bulma-text); cursor: pointer; white-space: nowrap;
3646
+ }
3647
+ .yui-dock-chip:hover { border-color: var(--bulma-text-weak); }
3648
+ .yui-dock-chip.is-active { border-color: #2563eb; color: #2563eb; background: rgba(37,99,235,0.10); font-weight: 600; }
3649
+ .yui-dock-chip.is-min { opacity: 0.65; }
3650
+ .yui-dock-dot { width: 8px; height: 8px; border-radius: 50%; background: #22c55e; flex: 0 0 auto; }
3651
+ .yui-dock-chip.is-min .yui-dock-dot { background: var(--bulma-text-weak); }
3652
+ .yui-dock-icon { display: inline-flex; align-items: center; justify-content: center; width: 15px; height: 15px; font-size: 14px; flex: 0 0 auto; }
3653
+ .yui-dock-icon svg { width: 14px; height: 14px; display: block; }
3654
+ .yui-dock-icon i { line-height: 1; }
3655
+ .yui-dock-label { flex: 0 0 auto; }
3656
+ .yui-dock-close {
3657
+ width: 18px; height: 18px; display: inline-flex; align-items: center; justify-content: center;
3658
+ padding: 0; margin-left: 1px; border: 0; border-radius: 4px;
3659
+ background: transparent; color: var(--bulma-text-weak); cursor: pointer; flex: 0 0 auto;
3660
+ }
3661
+ .yui-dock-close:hover { background: #e0364a; color: #fff; }
3662
+ .yui-dock-close svg { width: 11px; height: 11px; display: block; }
3663
+ :root[data-theme="dark"] .yui-dock-chip.is-active { border-color: #60a5fa; color: #93c5fd; background: rgba(96,165,250,0.16); }
3664
+ `;
3665
+ let $style = document.createElement("style");
3666
+ $style.id = "yui-dock-style";
3667
+ $style.textContent = css;
3668
+ document.head.appendChild($style);
3669
+ }
3670
+ /************************************************************
3671
+ * Build the dock element (empty, hidden until a window
3672
+ * registers).
3673
+ ************************************************************/
3674
+ function build_dock(gobj) {
3675
+ ensure_dock_style();
3676
+ gobj_write_attr(gobj, "$container", createElement2([
3677
+ "div",
3678
+ { class: "yui-dock is-empty" },
3679
+ []
3680
+ ]));
3681
+ apply_placement(gobj);
3682
+ }
3683
+ /************************************************************
3684
+ * Whether the dock should render inline (in a layout zone)
3685
+ * rather than floating, given the mode and — for
3686
+ * 'responsive' — the current viewport.
3687
+ ************************************************************/
3688
+ function want_inline_placement(gobj) {
3689
+ let mode = gobj_read_attr(gobj, "dock_mode") || "floating";
3690
+ if (mode === "inline") return true;
3691
+ if (mode === "responsive") {
3692
+ let priv = gobj.priv;
3693
+ return !!(priv.mql && priv.mql.matches);
3694
+ }
3695
+ return false;
3696
+ }
3697
+ /************************************************************
3698
+ * Place (or re-place) the dock: pick floating vs inline,
3699
+ * set the modifier classes, and mount it in the right
3700
+ * container. Also re-attaches the dock if it got detached
3701
+ * (e.g. a shell that replaced its parent's children after
3702
+ * the dock was first mounted, or the responsive breakpoint
3703
+ * flipped). Idempotent — safe to call on every register and
3704
+ * on every media-query change.
3705
+ ************************************************************/
3706
+ function apply_placement(gobj) {
3707
+ let $dock = gobj_read_attr(gobj, "$container");
3708
+ if (!$dock) return;
3709
+ let $inline = null;
3710
+ if (want_inline_placement(gobj)) {
3711
+ let sel = gobj_read_attr(gobj, "inline_selector");
3712
+ if (sel) $inline = document.querySelector(sel);
3713
+ }
3714
+ let inline = !!$inline;
3715
+ $dock.classList.toggle("yui-dock--inline", inline);
3716
+ $dock.classList.toggle("yui-dock--floating", !inline);
3717
+ let corner = gobj_read_attr(gobj, "dock_corner") || "bottom-left";
3718
+ for (let c of [
3719
+ "top-left",
3720
+ "top-right",
3721
+ "bottom-left",
3722
+ "bottom-right"
3723
+ ]) $dock.classList.toggle(`yui-dock--${c}`, !inline && c === corner);
3724
+ let target = inline ? $inline : gobj_read_attr(gobj, "$parent") || document.body;
3725
+ if ($dock.parentNode !== target) target.appendChild($dock);
3726
+ }
3727
+ /************************************************************
3728
+ * Destroy the dock element.
3729
+ ************************************************************/
3730
+ function destroy_dock(gobj) {
3731
+ let $dock = gobj_read_attr(gobj, "$container");
3732
+ if ($dock) {
3733
+ if ($dock.parentNode) $dock.parentNode.removeChild($dock);
3734
+ gobj_write_attr(gobj, "$container", null);
3735
+ }
3736
+ }
3737
+ /************************************************************
3738
+ * The chip's leading mark: a per-type icon if the window
3739
+ * provides one (a `yi-*` class name, or inline SVG/HTML), else
3740
+ * the status dot. Returns a createElement2() spec.
3741
+ ************************************************************/
3742
+ function chip_lead(icon) {
3743
+ if (!icon) return [
3744
+ "span",
3745
+ { class: "yui-dock-dot" },
3746
+ ""
3747
+ ];
3748
+ if (icon.charAt(0) === "<") return [
3749
+ "span",
3750
+ { class: "yui-dock-icon" },
3751
+ icon
3752
+ ];
3753
+ return [
3754
+ "span",
3755
+ { class: "yui-dock-icon" },
3756
+ [["i", { class: icon }]]
3757
+ ];
3758
+ }
3759
+ /************************************************************
3760
+ * Find a registry entry by its window gobj.
3761
+ ************************************************************/
3762
+ function find_entry(priv, win) {
3763
+ for (let e of priv.windows) if (e.gobj === win) return e;
3764
+ return null;
3765
+ }
3766
+ /************************************************************
3767
+ * Show / hide the empty state.
3768
+ ************************************************************/
3769
+ function sync_dock_visibility(gobj) {
3770
+ let priv = gobj.priv;
3771
+ let $dock = gobj_read_attr(gobj, "$container");
3772
+ if ($dock) $dock.classList.toggle("is-empty", priv.windows.length === 0);
3773
+ }
3774
+ /************************************************************
3775
+ * Mark one entry active (raised), the rest inactive.
3776
+ ************************************************************/
3777
+ function set_active(gobj, entry) {
3778
+ let priv = gobj.priv;
3779
+ for (let e of priv.windows) if (e.$chip) e.$chip.classList.toggle("is-active", e === entry);
3780
+ }
3781
+ /************************************************************
3782
+ * Bring a window to front and mark it active/visible.
3783
+ ************************************************************/
3784
+ function focus_entry(gobj, entry) {
3785
+ let priv = gobj.priv;
3786
+ let $win = gobj_read_attr(entry.gobj, "$container");
3787
+ if ($win) {
3788
+ if (entry.minimized) {
3789
+ $win.style.removeProperty("display");
3790
+ entry.minimized = false;
3791
+ if (entry.$chip) entry.$chip.classList.remove("is-min");
3792
+ }
3793
+ priv.z += 1;
3794
+ $win.style.zIndex = priv.z;
3795
+ }
3796
+ set_active(gobj, entry);
3797
+ }
3798
+ /************************************************************
3799
+ * Send a window to the dock (hidden, chip dimmed).
3800
+ ************************************************************/
3801
+ function minimize_entry(gobj, entry) {
3802
+ let $win = gobj_read_attr(entry.gobj, "$container");
3803
+ if ($win) $win.style.setProperty("display", "none", "important");
3804
+ entry.minimized = true;
3805
+ if (entry.$chip) {
3806
+ entry.$chip.classList.add("is-min");
3807
+ entry.$chip.classList.remove("is-active");
3808
+ }
3809
+ }
3810
+ /************************************************************
3811
+ * Dock chip click: taskbar toggle — restore+focus a
3812
+ * minimized/inactive window, minimize the active one.
3813
+ ************************************************************/
3814
+ function on_chip_click(gobj, entry) {
3815
+ if (entry.minimized || !entry.$chip.classList.contains("is-active")) focus_entry(gobj, entry);
3816
+ else minimize_entry(gobj, entry);
3817
+ }
3818
+ /***************************
3819
+ * Actions
3820
+ ***************************/
3821
+ /************************************************************
3822
+ * EV_REGISTER_WINDOW { window, title }
3823
+ ************************************************************/
3824
+ function ac_register_window(gobj, event, kw, src) {
3825
+ let priv = gobj.priv;
3826
+ let win = kw.window;
3827
+ if (!win || find_entry(priv, win)) return 0;
3828
+ apply_placement(gobj);
3829
+ let title = kw.title || gobj_name(win) || "window";
3830
+ let entry = {
3831
+ gobj: win,
3832
+ $chip: null,
3833
+ minimized: false
3834
+ };
3835
+ let $chip = createElement2([
3836
+ "div",
3837
+ {
3838
+ class: "yui-dock-chip",
3839
+ role: "button",
3840
+ tabindex: "0",
3841
+ title
3842
+ },
3843
+ [
3844
+ chip_lead(kw.icon),
3845
+ [
3846
+ "span",
3847
+ { class: "yui-dock-label" },
3848
+ title
3849
+ ],
3850
+ [
3851
+ "button",
3852
+ {
3853
+ class: "yui-dock-close",
3854
+ type: "button",
3855
+ "aria-label": "close"
3856
+ },
3857
+ WC_X,
3858
+ { click: (evt) => {
3859
+ evt.stopPropagation();
3860
+ gobj_send_event(entry.gobj, "EV_CLOSE_WINDOW", {}, gobj);
3861
+ } }
3862
+ ]
3863
+ ],
3864
+ { click: (evt) => {
3865
+ evt.stopPropagation();
3866
+ on_chip_click(gobj, entry);
3867
+ } }
3868
+ ]);
3869
+ entry.$chip = $chip;
3870
+ priv.windows.push(entry);
3871
+ let $dock = gobj_read_attr(gobj, "$container");
3872
+ if ($dock) $dock.appendChild($chip);
3873
+ sync_dock_visibility(gobj);
3874
+ focus_entry(gobj, entry);
3875
+ return 0;
3876
+ }
3877
+ /************************************************************
3878
+ * EV_UNREGISTER_WINDOW { window }
3879
+ ************************************************************/
3880
+ function ac_unregister_window(gobj, event, kw, src) {
3881
+ let priv = gobj.priv;
3882
+ let entry = find_entry(priv, kw.window);
3883
+ if (!entry) return 0;
3884
+ if (entry.$chip && entry.$chip.parentNode) entry.$chip.parentNode.removeChild(entry.$chip);
3885
+ priv.windows = priv.windows.filter((e) => e !== entry);
3886
+ sync_dock_visibility(gobj);
3887
+ return 0;
3888
+ }
3889
+ /************************************************************
3890
+ * EV_MINIMIZE_WINDOW { window }
3891
+ ************************************************************/
3892
+ function ac_minimize_window(gobj, event, kw, src) {
3893
+ let entry = find_entry(gobj.priv, kw.window);
3894
+ if (entry) minimize_entry(gobj, entry);
3895
+ return 0;
3896
+ }
3897
+ /************************************************************
3898
+ * EV_FOCUS_WINDOW { window }
3899
+ ************************************************************/
3900
+ function ac_focus_window(gobj, event, kw, src) {
3901
+ let entry = find_entry(gobj.priv, kw.window);
3902
+ if (entry) focus_entry(gobj, entry);
3903
+ return 0;
3904
+ }
3905
+ /***************************
3906
+ * FSM
3907
+ ***************************/
3908
+ var gmt$15 = {
3909
+ mt_create: mt_create$15,
3910
+ mt_destroy: mt_destroy$15
3911
+ };
3912
+ /***************************************************************
3913
+ * Create the GClass
3914
+ ***************************************************************/
3915
+ function create_gclass$15(gclass_name) {
3916
+ if (__gclass__$15) {
3917
+ log_error(`GClass ALREADY created: ${gclass_name}`);
3918
+ return -1;
3919
+ }
3920
+ __gclass__$15 = gclass_create(gclass_name, [
3921
+ ["EV_REGISTER_WINDOW", 0],
3922
+ ["EV_UNREGISTER_WINDOW", 0],
3923
+ ["EV_MINIMIZE_WINDOW", 0],
3924
+ ["EV_FOCUS_WINDOW", 0]
3925
+ ], [["ST_IDLE", [
3926
+ [
3927
+ "EV_REGISTER_WINDOW",
3928
+ ac_register_window,
3929
+ null
3930
+ ],
3931
+ [
3932
+ "EV_UNREGISTER_WINDOW",
3933
+ ac_unregister_window,
3934
+ null
3935
+ ],
3936
+ [
3937
+ "EV_MINIMIZE_WINDOW",
3938
+ ac_minimize_window,
3939
+ null
3940
+ ],
3941
+ [
3942
+ "EV_FOCUS_WINDOW",
3943
+ ac_focus_window,
3944
+ null
3945
+ ]
3946
+ ]]], gmt$15, 0, attrs_table$15, PRIVATE_DATA$15, 0, 0, 0, 0);
3947
+ if (!__gclass__$15) return -1;
3948
+ return 0;
3949
+ }
3950
+ /***************************************************************
3951
+ * Register GClass
3952
+ ***************************************************************/
3953
+ function register_c_yui_window_manager() {
2397
3954
  return create_gclass$15(GCLASS_NAME$15);
2398
3955
  }
2399
3956
  //#endregion
2400
- //#region c_yui_tabs.js
3957
+ //#region src/c_yui_tabs.js
2401
3958
  /***********************************************************************
2402
3959
  * c_yui_tabs.js
2403
3960
  *
@@ -2451,7 +4008,7 @@ var __gclass__$14 = null;
2451
4008
  * Framework Method: Create
2452
4009
  ***************************************************************/
2453
4010
  function mt_create$14(gobj) {
2454
- build_ui$11(gobj);
4011
+ build_ui$12(gobj);
2455
4012
  let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
2456
4013
  if (!subscriber) subscriber = gobj_parent(gobj);
2457
4014
  gobj_subscribe_event(gobj, null, {}, subscriber);
@@ -2493,11 +4050,11 @@ function mt_child_removed(gobj, child) {
2493
4050
  /************************************************************
2494
4051
  * Build UI
2495
4052
  ************************************************************/
2496
- function build_ui$11(gobj) {
4053
+ function build_ui$12(gobj) {
2497
4054
  gobj_write_attr(gobj, "$container", createElement2([
2498
4055
  "div",
2499
4056
  {
2500
- class: `yui_tabs`,
4057
+ class: `C_YUI_TABS`,
2501
4058
  style: "height:100%; display:flex; flex-direction:column;"
2502
4059
  },
2503
4060
  [[
@@ -2688,7 +4245,7 @@ function register_c_yui_tabs() {
2688
4245
  return create_gclass$14(GCLASS_NAME$14);
2689
4246
  }
2690
4247
  //#endregion
2691
- //#region pager_helpers.js
4248
+ //#region src/pager_helpers.js
2692
4249
  /***************************************************************
2693
4250
  * Top entry or null.
2694
4251
  ***************************************************************/
@@ -2742,7 +4299,7 @@ function pager_back_action(stack, back_on_root) {
2742
4299
  return { type: "noop" };
2743
4300
  }
2744
4301
  //#endregion
2745
- //#region c_yui_pager.js
4302
+ //#region src/c_yui_pager.js
2746
4303
  /***********************************************************************
2747
4304
  * c_yui_pager.js
2748
4305
  *
@@ -2794,7 +4351,7 @@ var __gclass__$13 = null;
2794
4351
  function mt_create$13(gobj) {
2795
4352
  let priv = gobj.priv;
2796
4353
  priv.stack = [];
2797
- build_ui$10(gobj);
4354
+ build_ui$11(gobj);
2798
4355
  let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
2799
4356
  if (!subscriber) subscriber = gobj_parent(gobj);
2800
4357
  gobj_subscribe_event(gobj, null, {}, subscriber);
@@ -2821,11 +4378,11 @@ function mt_destroy$13(gobj) {
2821
4378
  /************************************************************
2822
4379
  * Build UI
2823
4380
  ************************************************************/
2824
- function build_ui$10(gobj) {
4381
+ function build_ui$11(gobj) {
2825
4382
  gobj_write_attr(gobj, "$container", createElement2([
2826
4383
  "div",
2827
4384
  {
2828
- class: "yui-pager",
4385
+ class: "C_YUI_PAGER",
2829
4386
  style: "height:100%; display:flex; flex-direction:column;"
2830
4387
  },
2831
4388
  [[
@@ -3175,7 +4732,7 @@ function register_c_yui_pager() {
3175
4732
  return create_gclass$13(GCLASS_NAME$13);
3176
4733
  }
3177
4734
  //#endregion
3178
- //#region wizard_helpers.js
4735
+ //#region src/wizard_helpers.js
3179
4736
  /***********************************************************************
3180
4737
  * wizard_helpers.js
3181
4738
  *
@@ -3264,7 +4821,7 @@ function wizard_should_validate(linear, has_gobj) {
3264
4821
  return !!linear && !!has_gobj;
3265
4822
  }
3266
4823
  //#endregion
3267
- //#region c_yui_wizard.js
4824
+ //#region src/c_yui_wizard.js
3268
4825
  /***********************************************************************
3269
4826
  * c_yui_wizard.js
3270
4827
  *
@@ -3326,7 +4883,7 @@ function mt_create$12(gobj) {
3326
4883
  priv.idx = 0;
3327
4884
  priv.acc = null;
3328
4885
  priv.awaiting = false;
3329
- build_ui$9(gobj);
4886
+ build_ui$10(gobj);
3330
4887
  let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
3331
4888
  if (!subscriber) subscriber = gobj_parent(gobj);
3332
4889
  gobj_subscribe_event(gobj, null, {}, subscriber);
@@ -3353,11 +4910,11 @@ function mt_destroy$12(gobj) {
3353
4910
  /************************************************************
3354
4911
  * Build UI
3355
4912
  ************************************************************/
3356
- function build_ui$9(gobj) {
4913
+ function build_ui$10(gobj) {
3357
4914
  gobj_write_attr(gobj, "$container", createElement2([
3358
4915
  "div",
3359
4916
  {
3360
- class: "yui-wizard",
4917
+ class: "C_YUI_WIZARD",
3361
4918
  style: "height:100%; display:flex; flex-direction:column;"
3362
4919
  },
3363
4920
  [
@@ -3793,7 +5350,7 @@ function register_c_yui_wizard() {
3793
5350
  return create_gclass$12(GCLASS_NAME$12);
3794
5351
  }
3795
5352
  //#endregion
3796
- //#region c_yui_form.js
5353
+ //#region src/c_yui_form.js
3797
5354
  /***********************************************************************
3798
5355
  * c_yui_form.js
3799
5356
  *
@@ -3845,7 +5402,7 @@ function mt_create$11(gobj) {
3845
5402
  if (!subscriber) subscriber = gobj_parent(gobj);
3846
5403
  gobj_subscribe_event(gobj, null, {}, subscriber);
3847
5404
  gobj_write_attr(gobj, "form_id", "form" + clean_name(gobj_name(gobj)));
3848
- build_ui$8(gobj);
5405
+ build_ui$9(gobj);
3849
5406
  }
3850
5407
  /************************************************************
3851
5408
  * Framework Method destroy
@@ -3932,11 +5489,11 @@ async function readClipboard$1(gobj) {
3932
5489
  /************************************************************
3933
5490
  * Build UI
3934
5491
  ************************************************************/
3935
- function build_ui$8(gobj) {
5492
+ function build_ui$9(gobj) {
3936
5493
  let $container = createElement2([
3937
5494
  "div",
3938
5495
  {
3939
- class: "yui-form-container is-flex is-flex-direction-row overscroll-contain",
5496
+ class: "C_YUI_FORM is-flex is-flex-direction-row overscroll-contain",
3940
5497
  style: "height:100%; width:100%; box-sizing:border-box;"
3941
5498
  },
3942
5499
  [[
@@ -5796,7 +7353,7 @@ function register_c_yui_form() {
5796
7353
  return create_gclass$11(GCLASS_NAME$11);
5797
7354
  }
5798
7355
  //#endregion
5799
- //#region c_yui_routing.js
7356
+ //#region src/c_yui_routing.js
5800
7357
  /***********************************************************************
5801
7358
  * ui_routing.js
5802
7359
  *
@@ -6003,7 +7560,7 @@ function build_layout(gobj) {
6003
7560
  const $container = createElement2([
6004
7561
  "div",
6005
7562
  {
6006
- class: "container-grid-2col",
7563
+ class: "C_YUI_ROUTING",
6007
7564
  style: ""
6008
7565
  },
6009
7566
  [[
@@ -6363,7 +7920,7 @@ function register_c_yui_routing() {
6363
7920
  return create_gclass$10(GCLASS_NAME$10);
6364
7921
  }
6365
7922
  //#endregion
6366
- //#region lib_maplibre.js
7923
+ //#region src/lib_maplibre.js
6367
7924
  /***********************************************************************
6368
7925
  * lib_maplibre.js
6369
7926
  *
@@ -6508,7 +8065,7 @@ var MarkerControl = class {
6508
8065
  }
6509
8066
  };
6510
8067
  //#endregion
6511
- //#region c_yui_map.js
8068
+ //#region src/c_yui_map.js
6512
8069
  /***********************************************************************
6513
8070
  * c_yui_map.js
6514
8071
  *
@@ -6567,7 +8124,9 @@ function mt_create$9(gobj) {
6567
8124
  if (!subscriber) subscriber = gobj_parent(gobj);
6568
8125
  gobj_subscribe_event(gobj, null, {}, subscriber);
6569
8126
  let map_settings = json_deep_copy(gobj_read_attr(gobj, "map_settings"));
6570
- Object.assign(map_settings, { container: gobj_read_attr(gobj, "$map") });
8127
+ let $map = gobj_read_attr(gobj, "$map");
8128
+ if ($map) $map.classList.add(GCLASS_NAME$9);
8129
+ Object.assign(map_settings, { container: $map });
6571
8130
  priv.onClick = (ev) => {
6572
8131
  onClick(gobj, ev);
6573
8132
  };
@@ -7115,7 +8674,7 @@ function register_c_yui_map() {
7115
8674
  return create_gclass$9(GCLASS_NAME$9);
7116
8675
  }
7117
8676
  //#endregion
7118
- //#region c_yui_uplot.js
8677
+ //#region src/c_yui_uplot.js
7119
8678
  /***********************************************************************
7120
8679
  * c_yui_uplot.js
7121
8680
  *
@@ -7175,7 +8734,7 @@ var __gclass__$8 = null;
7175
8734
  * Framework Method: Create
7176
8735
  ***************************************************************/
7177
8736
  function mt_create$8(gobj) {
7178
- build_ui$7(gobj);
8737
+ build_ui$8(gobj);
7179
8738
  let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
7180
8739
  if (!subscriber) subscriber = gobj_parent(gobj);
7181
8740
  gobj_subscribe_event(gobj, null, {}, subscriber);
@@ -7205,11 +8764,11 @@ function mt_destroy$8(gobj) {
7205
8764
  /************************************************************
7206
8765
  * Build UI
7207
8766
  ************************************************************/
7208
- function build_ui$7(gobj) {
8767
+ function build_ui$8(gobj) {
7209
8768
  let $container = gobj_read_attr(gobj, "$container");
7210
8769
  if (!$container) {
7211
8770
  $container = createElement2(["div", {
7212
- class: "",
8771
+ class: "C_YUI_UPLOT",
7213
8772
  style: "overflow:auto;"
7214
8773
  }]);
7215
8774
  gobj_write_bool_attr(gobj, "$container", $container);
@@ -7408,7 +8967,7 @@ function register_c_yui_uplot() {
7408
8967
  return create_gclass$8(GCLASS_NAME$8);
7409
8968
  }
7410
8969
  //#endregion
7411
- //#region g6_drag_canvas_touch.js
8970
+ //#region src/g6_drag_canvas_touch.js
7412
8971
  /***********************************************************************
7413
8972
  * g6_drag_canvas_touch.js
7414
8973
  *
@@ -7516,7 +9075,7 @@ function ensure_drag_canvas_patch() {
7516
9075
  __drag_canvas_patched__ = true;
7517
9076
  }
7518
9077
  //#endregion
7519
- //#region c_yui_json_graph.js
9078
+ //#region src/c_yui_json_graph.js
7520
9079
  /***********************************************************************
7521
9080
  * c_yui_json_graph.js
7522
9081
  *
@@ -7589,7 +9148,7 @@ function mt_create$7(gobj) {
7589
9148
  gobj_subscribe_event(gobj, null, {}, subscriber);
7590
9149
  let __yui_main__ = gobj_find_service("__yui_main__", true);
7591
9150
  if (__yui_main__) priv.theme = gobj_read_str_attr(__yui_main__, "theme");
7592
- build_ui$6(gobj);
9151
+ build_ui$7(gobj);
7593
9152
  ensure_drag_canvas_patch();
7594
9153
  if (!__layout_registered__$1) {
7595
9154
  register(ExtensionCategory.LAYOUT, "json-tree-layout", JsonTreeLayout);
@@ -7632,12 +9191,12 @@ function mt_destroy$7(gobj) {
7632
9191
  /************************************************************
7633
9192
  * Build UI
7634
9193
  ************************************************************/
7635
- function build_ui$6(gobj) {
9194
+ function build_ui$7(gobj) {
7636
9195
  let priv = gobj.priv;
7637
9196
  let $container = createElement2([
7638
9197
  "div",
7639
9198
  {
7640
- class: "json-graph",
9199
+ class: "C_YUI_JSON_GRAPH",
7641
9200
  style: "height:100%; display:flex; flex-direction:column;"
7642
9201
  },
7643
9202
  [[
@@ -8270,7 +9829,7 @@ function register_c_yui_json_graph() {
8270
9829
  return create_gclass$7(GCLASS_NAME$7);
8271
9830
  }
8272
9831
  //#endregion
8273
- //#region c_yui_gobj_tree_js.js
9832
+ //#region src/c_yui_gobj_tree_js.js
8274
9833
  /***********************************************************************
8275
9834
  * c_yui_gobj_tree_js.js
8276
9835
  *
@@ -8505,7 +10064,7 @@ function mt_create$6(gobj) {
8505
10064
  let __yui_main__ = gobj_find_service("__yui_main__");
8506
10065
  if (__yui_main__) priv.theme = gobj_read_str_attr(__yui_main__, "theme");
8507
10066
  else priv.theme = gt_is_dark() ? "dark" : "light";
8508
- build_ui$5(gobj);
10067
+ build_ui$6(gobj);
8509
10068
  ensure_drag_canvas_patch();
8510
10069
  if (!__layout_registered__) {
8511
10070
  register(ExtensionCategory.LAYOUT, "gobj-tree-v", GobjTreeVLayout);
@@ -8557,12 +10116,12 @@ function mt_destroy$6(gobj) {
8557
10116
  /************************************************************
8558
10117
  * Build UI
8559
10118
  ************************************************************/
8560
- function build_ui$5(gobj) {
10119
+ function build_ui$6(gobj) {
8561
10120
  let priv = gobj.priv;
8562
10121
  let $container = createElement2([
8563
10122
  "div",
8564
10123
  {
8565
- class: "gobj-tree",
10124
+ class: "C_YUI_GOBJ_TREE_JS",
8566
10125
  style: "height:100%; display:flex; flex-direction:column; position:relative;"
8567
10126
  },
8568
10127
  [[
@@ -9835,7 +11394,7 @@ function register_c_yui_gobj_tree_js() {
9835
11394
  return create_gclass$6(GCLASS_NAME$6);
9836
11395
  }
9837
11396
  //#endregion
9838
- //#region shell_show_on.js
11397
+ //#region src/shell_show_on.js
9839
11398
  /***********************************************************************
9840
11399
  * shell_show_on.js
9841
11400
  *
@@ -9911,7 +11470,7 @@ function bulma_hidden_class(bp) {
9911
11470
  return "";
9912
11471
  }
9913
11472
  //#endregion
9914
- //#region shell_focus_trap.js
11473
+ //#region src/shell_focus_trap.js
9915
11474
  /***********************************************************************
9916
11475
  * shell_focus_trap.js
9917
11476
  *
@@ -9985,7 +11544,7 @@ function activate_focus_trap_on($panel, doc) {
9985
11544
  };
9986
11545
  }
9987
11546
  //#endregion
9988
- //#region shell_toolbar_helpers.js
11547
+ //#region src/shell_toolbar_helpers.js
9989
11548
  var TOOLBAR_ACTION_TYPES = [
9990
11549
  "navigate",
9991
11550
  "drawer",
@@ -10111,7 +11670,7 @@ function _is_non_empty_string(v) {
10111
11670
  return typeof v === "string" && v.trim().length > 0;
10112
11671
  }
10113
11672
  //#endregion
10114
- //#region route_resolver.js
11673
+ //#region src/route_resolver.js
10115
11674
  /***********************************************************************
10116
11675
  * route_resolver.js
10117
11676
  *
@@ -10162,7 +11721,7 @@ function resolve_route(item_index, route) {
10162
11721
  };
10163
11722
  }
10164
11723
  //#endregion
10165
- //#region c_yui_shell.js
11724
+ //#region src/c_yui_shell.js
10166
11725
  /***********************************************************************
10167
11726
  * c_yui_shell.js
10168
11727
  *
@@ -10248,7 +11807,7 @@ function mt_create$5(gobj) {
10248
11807
  conn_nodes: [],
10249
11808
  active_dropdown: null
10250
11809
  });
10251
- build_ui$4(gobj);
11810
+ build_ui$5(gobj);
10252
11811
  }
10253
11812
  /***************************************************************
10254
11813
  * Framework Method: Start
@@ -10453,16 +12012,16 @@ function check_route_unique(route_owner, item, menu_id) {
10453
12012
  /************************************************************
10454
12013
  * Build the DOM: layers → base → zones
10455
12014
  ************************************************************/
10456
- function build_ui$4(gobj) {
12015
+ function build_ui$5(gobj) {
10457
12016
  let priv = gobj_read_attr(gobj, "priv");
10458
12017
  let shell_cfg = (gobj_read_attr(gobj, "config") || {}).shell || {};
10459
- let $root = createElement2(["div", { class: "yui-shell" }]);
12018
+ let $container = createElement2(["div", { class: "C_YUI_SHELL yui-shell" }]);
10460
12019
  for (let [id, z] of LAYER_DEFS) {
10461
12020
  let $layer = createElement2(["div", {
10462
12021
  class: `yui-layer yui-layer-${id}`,
10463
12022
  style: `z-index:${z};`
10464
12023
  }]);
10465
- $root.appendChild($layer);
12024
+ $container.appendChild($layer);
10466
12025
  priv.layers[id] = $layer;
10467
12026
  }
10468
12027
  let $base = priv.layers.base;
@@ -10511,8 +12070,8 @@ function build_ui$4(gobj) {
10511
12070
  };
10512
12071
  priv.zones.center.classList.add("yui-stage", "yui-stage-main");
10513
12072
  }
10514
- (gobj_read_attr(gobj, "mount_element") || document.body).appendChild($root);
10515
- gobj_write_attr(gobj, "$container", $root);
12073
+ (gobj_read_attr(gobj, "mount_element") || document.body).appendChild($container);
12074
+ gobj_write_attr(gobj, "$container", $container);
10516
12075
  }
10517
12076
  /************************************************************
10518
12077
  * Translate zone id to grid-area name
@@ -10781,9 +12340,10 @@ function navigate_to(gobj, route) {
10781
12340
  let prev_route = stage.active_route;
10782
12341
  if (prev_route && prev_route !== matched_route) {
10783
12342
  let prev_gobj = stage.items[prev_route];
12343
+ let $prev = null;
10784
12344
  if (prev_gobj) {
10785
- let $c = gobj_read_attr(prev_gobj, "$container");
10786
- if ($c) $c.classList.add("is-hidden");
12345
+ $prev = gobj_read_attr(prev_gobj, "$container");
12346
+ if ($prev) $prev.classList.add("is-hidden");
10787
12347
  }
10788
12348
  let prev_entry = priv.item_index[prev_route];
10789
12349
  if (prev_entry && prev_entry.target && prev_entry.target.lifecycle === "lazy_destroy") {
@@ -10793,6 +12353,7 @@ function navigate_to(gobj, route) {
10793
12353
  } catch (e) {
10794
12354
  log_warning(`C_YUI_SHELL: lazy_destroy of '${prev_route}' failed: ${e}`);
10795
12355
  }
12356
+ if ($prev && $prev.parentNode) $prev.parentNode.removeChild($prev);
10796
12357
  delete stage.items[prev_route];
10797
12358
  }
10798
12359
  }
@@ -11477,6 +13038,21 @@ function ac_drawer_close_requested(gobj, event, kw, src) {
11477
13038
  close_drawer(gobj, kw && kw.menu_id || "");
11478
13039
  return 0;
11479
13040
  }
13041
+ /************************************************************
13042
+ * Close 'x' on a closable tab (from a C_YUI_NAV child). The
13043
+ * shell does not own the item set — re-publish so the app (which
13044
+ * owns the underlying data, e.g. the selected-nodes list) removes
13045
+ * the item and calls yui_shell_set_submenu() with the new list.
13046
+ ************************************************************/
13047
+ function ac_nav_item_close(gobj, event, kw, src) {
13048
+ gobj_publish_event(gobj, "EV_NAV_ITEM_CLOSE", {
13049
+ item_id: kw && kw.item_id || "",
13050
+ route: kw && kw.route || "",
13051
+ menu_id: kw && kw.menu_id || "",
13052
+ zone: kw && kw.zone || ""
13053
+ });
13054
+ return 0;
13055
+ }
11480
13056
  /***************************************************************
11481
13057
  * FSM
11482
13058
  ***************************************************************/
@@ -11491,18 +13067,27 @@ function create_gclass$5(gclass_name) {
11491
13067
  log_error(`GClass ALREADY created: ${gclass_name}`);
11492
13068
  return -1;
11493
13069
  }
11494
- const states = [["ST_IDLE", [[
11495
- "EV_NAV_CLICKED",
11496
- ac_nav_clicked,
11497
- null
11498
- ], [
11499
- "EV_DRAWER_CLOSE_REQUESTED",
11500
- ac_drawer_close_requested,
11501
- null
11502
- ]]]];
13070
+ const states = [["ST_IDLE", [
13071
+ [
13072
+ "EV_NAV_CLICKED",
13073
+ ac_nav_clicked,
13074
+ null
13075
+ ],
13076
+ [
13077
+ "EV_DRAWER_CLOSE_REQUESTED",
13078
+ ac_drawer_close_requested,
13079
+ null
13080
+ ],
13081
+ [
13082
+ "EV_NAV_ITEM_CLOSE",
13083
+ ac_nav_item_close,
13084
+ null
13085
+ ]
13086
+ ]]];
11503
13087
  __gclass__$5 = gclass_create(gclass_name, [
11504
13088
  ["EV_NAV_CLICKED", 0],
11505
13089
  ["EV_DRAWER_CLOSE_REQUESTED", 0],
13090
+ ["EV_NAV_ITEM_CLOSE", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_PUBLIC_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
11506
13091
  ["EV_ROUTE_REQUESTED", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_PUBLIC_EVENT | event_flag_t.EVF_NO_WARN_SUBS],
11507
13092
  ["EV_ROUTE_CHANGED", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_PUBLIC_EVENT | event_flag_t.EVF_NO_WARN_SUBS]
11508
13093
  ], states, gmt$5, 0, attrs_table$5, PRIVATE_DATA$5, 0, 0, 0, gclass_flag_t.gcflag_no_check_output_events);
@@ -11594,9 +13179,9 @@ function yui_shell_set_connection_state(shell_gobj, connected) {
11594
13179
  * `icon_class` fully replaces the <i> class.
11595
13180
  ************************************************************/
11596
13181
  function yui_shell_set_toolbar_item_icon(shell_gobj, item_id, icon_class) {
11597
- let $root = gobj_read_attr(shell_gobj, "$container");
11598
- if (!$root || empty_string(item_id) || empty_string(icon_class)) return;
11599
- let $i = $root.querySelector(`[data-toolbar-item-id="${item_id}"] .icon i`);
13182
+ let $container = gobj_read_attr(shell_gobj, "$container");
13183
+ if (!$container || empty_string(item_id) || empty_string(icon_class)) return;
13184
+ let $i = $container.querySelector(`[data-toolbar-item-id="${item_id}"] .icon i`);
11600
13185
  if ($i) $i.className = icon_class;
11601
13186
  }
11602
13187
  /************************************************************
@@ -11608,7 +13193,7 @@ function yui_shell_close_dropdown(shell_gobj) {
11608
13193
  close_toolbar_dropdown(shell_gobj);
11609
13194
  }
11610
13195
  //#endregion
11611
- //#region c_yui_nav.js
13196
+ //#region src/c_yui_nav.js
11612
13197
  /***********************************************************************
11613
13198
  * c_yui_nav.js
11614
13199
  *
@@ -11626,6 +13211,15 @@ function yui_shell_close_dropdown(shell_gobj) {
11626
13211
  *
11627
13212
  * Each item supports:
11628
13213
  * id, name, icon (CSS class or svg id), route, badge, disabled
13214
+ * class — extra CSS class(es) on the item (tabs layout), e.g.
13215
+ * a per-item state colour like "yui-nav-disconnected"
13216
+ * closable — render a trailing ✕ that emits EV_NAV_ITEM_CLOSE
13217
+ * instead of navigating (tabs layout)
13218
+ *
13219
+ * Items can be replaced at runtime by sending EV_SET_ITEMS
13220
+ * {items:[...]} to the nav; the DOM is rebuilt in place and the
13221
+ * active highlight restored. The app_config path is simply the
13222
+ * first, startup caller of the same mechanism.
11629
13223
  *
11630
13224
  * In addition to navigable items, secondary navs accept two
11631
13225
  * decorative item kinds for in-place visual grouping:
@@ -11689,7 +13283,7 @@ function mt_create$4(gobj) {
11689
13283
  if (!subscriber) subscriber = gobj_parent(gobj);
11690
13284
  gobj_subscribe_event(gobj, null, {}, subscriber);
11691
13285
  gobj_write_attr(gobj, "priv", { click_handler: null });
11692
- build_ui$3(gobj);
13286
+ build_ui$4(gobj);
11693
13287
  }
11694
13288
  /***************************************************************
11695
13289
  * Framework Method: Start
@@ -11723,7 +13317,7 @@ function mt_destroy$4(gobj) {
11723
13317
  /************************************************************
11724
13318
  * Build the UI: dispatch to one of the six layout renderers
11725
13319
  ************************************************************/
11726
- function build_ui$3(gobj) {
13320
+ function build_ui$4(gobj) {
11727
13321
  let layout = gobj_read_attr(gobj, "layout") || "vertical";
11728
13322
  if (SUPPORTED_LAYOUTS.indexOf(layout) < 0) {
11729
13323
  log_error(`C_YUI_NAV: unsupported layout '${layout}', falling back to vertical`);
@@ -11732,40 +13326,75 @@ function build_ui$3(gobj) {
11732
13326
  let items = gobj_read_attr(gobj, "menu_items") || [];
11733
13327
  let zone = gobj_read_attr(gobj, "zone") || "";
11734
13328
  let menu_id = gobj_read_attr(gobj, "menu_id") || "nav";
11735
- let $root;
13329
+ let $container;
11736
13330
  switch (layout) {
11737
13331
  case "vertical":
11738
- $root = render_vertical(gobj, items);
13332
+ $container = render_vertical(gobj, items);
11739
13333
  break;
11740
13334
  case "icon-bar":
11741
- $root = render_icon_bar(gobj, items);
13335
+ $container = render_icon_bar(gobj, items);
11742
13336
  break;
11743
13337
  case "tabs":
11744
- $root = render_tabs(gobj, items);
13338
+ $container = render_tabs(gobj, items);
11745
13339
  break;
11746
13340
  case "drawer":
11747
- $root = render_drawer(gobj, items);
13341
+ $container = render_drawer(gobj, items);
11748
13342
  break;
11749
13343
  case "submenu":
11750
- $root = render_submenu(gobj, items);
13344
+ $container = render_submenu(gobj, items);
11751
13345
  break;
11752
13346
  case "accordion":
11753
- $root = render_accordion(gobj, items);
13347
+ $container = render_accordion(gobj, items);
11754
13348
  break;
11755
- default: $root = render_vertical(gobj, items);
13349
+ default: $container = render_vertical(gobj, items);
11756
13350
  }
11757
- $root.setAttribute("data-nav-zone", zone);
11758
- $root.setAttribute("data-nav-layout", layout);
11759
- $root.classList.add("yui-nav", `yui-nav-${layout}`);
13351
+ $container.setAttribute("data-nav-zone", zone);
13352
+ $container.setAttribute("data-nav-layout", layout);
13353
+ $container.classList.add("C_YUI_NAV", "yui-nav", `yui-nav-${layout}`);
11760
13354
  if (layout !== "drawer") {
11761
- $root.setAttribute("role", "navigation");
11762
- if (!$root.hasAttribute("aria-label")) {
13355
+ $container.setAttribute("role", "navigation");
13356
+ if (!$container.hasAttribute("aria-label")) {
11763
13357
  let label = gobj_read_attr(gobj, "nav_label") || menu_id;
11764
- $root.setAttribute("aria-label", label);
13358
+ $container.setAttribute("aria-label", label);
11765
13359
  }
11766
13360
  }
11767
- wire_clicks(gobj, $root);
11768
- gobj_write_attr(gobj, "$container", $root);
13361
+ wire_clicks(gobj, $container);
13362
+ gobj_write_attr(gobj, "$container", $container);
13363
+ }
13364
+ /************************************************************
13365
+ * Rebuild the nav DOM in place from the current menu_items.
13366
+ * Used by EV_SET_ITEMS so a nav's tabs can be changed at runtime
13367
+ * (the app_config path is just the first, startup caller).
13368
+ ************************************************************/
13369
+ function rebuild(gobj) {
13370
+ let $old = gobj_read_attr(gobj, "$container");
13371
+ let priv = gobj_read_attr(gobj, "priv") || {};
13372
+ let parent = $old ? $old.parentNode : null;
13373
+ let next = $old ? $old.nextSibling : null;
13374
+ let was_hidden = !!($old && $old.classList.contains("is-hidden"));
13375
+ if ($old && priv.click_handler) $old.removeEventListener("click", priv.click_handler);
13376
+ build_ui$4(gobj);
13377
+ let $new = gobj_read_attr(gobj, "$container");
13378
+ if ($new && was_hidden) $new.classList.add("is-hidden");
13379
+ if (parent && $new) parent.insertBefore($new, next);
13380
+ if ($old && $old.parentNode) $old.parentNode.removeChild($old);
13381
+ apply_active_route(gobj);
13382
+ }
13383
+ /************************************************************
13384
+ * Re-apply the active-route highlight after a rebuild (the
13385
+ * shell only pushes EV_ROUTE_CHANGED on navigation, not on a
13386
+ * self-triggered items change).
13387
+ ************************************************************/
13388
+ function apply_active_route(gobj) {
13389
+ let $c = gobj_read_attr(gobj, "$container");
13390
+ let route = gobj_read_attr(gobj, "active_route") || "";
13391
+ if (!$c || empty_string(route)) return;
13392
+ let $a = $c.querySelector(`a[data-route="${css_escape(route)}"]`);
13393
+ if ($a) {
13394
+ let $li = $a.closest("li");
13395
+ if ($li) $li.classList.add("is-active");
13396
+ $a.classList.add("is-active");
13397
+ }
11769
13398
  }
11770
13399
  /************************************************************
11771
13400
  * Layouts
@@ -11843,6 +13472,20 @@ function render_tabs(gobj, items) {
11843
13472
  { i18n: it.name },
11844
13473
  it.name
11845
13474
  ]);
13475
+ if (it.closable) children.push([
13476
+ "span",
13477
+ {
13478
+ class: "icon is-small yui-nav-close ml-2",
13479
+ "data-close-item": it.id,
13480
+ role: "button",
13481
+ "aria-label": "close",
13482
+ title: "close"
13483
+ },
13484
+ ["i", {
13485
+ class: "yi-xmark",
13486
+ "aria-hidden": "true"
13487
+ }]
13488
+ ]);
11846
13489
  let a_attrs = {
11847
13490
  href: it.route ? "#" + it.route : "#",
11848
13491
  "data-item-id": it.id,
@@ -11853,10 +13496,11 @@ function render_tabs(gobj, items) {
11853
13496
  a_attrs.title = tip;
11854
13497
  a_attrs["data-i18n-title"] = tip;
11855
13498
  }
13499
+ let li_class = empty_string(it.class) ? "" : String(it.class);
11856
13500
  lis.push([
11857
13501
  "li",
11858
13502
  {
11859
- class: "",
13503
+ class: li_class,
11860
13504
  "data-item-id": it.id,
11861
13505
  "data-route": it.route || ""
11862
13506
  },
@@ -11929,7 +13573,7 @@ function render_submenu(gobj, items) {
11929
13573
  function render_accordion(gobj, items) {
11930
13574
  let show_label = gobj_read_attr(gobj, "show_label");
11931
13575
  let icon_pos = gobj_read_attr(gobj, "icon_pos");
11932
- let $root = createElement2(["aside", { class: "menu yui-nav-accordion p-2" }]);
13576
+ let $container = createElement2(["aside", { class: "menu yui-nav-accordion p-2" }]);
11933
13577
  for (let it of items) {
11934
13578
  if (is_decorative(it)) continue;
11935
13579
  let acc_id = ++__nav_aria_seq__;
@@ -11951,7 +13595,7 @@ function render_accordion(gobj, items) {
11951
13595
  head_attrs,
11952
13596
  head_text
11953
13597
  ]);
11954
- $root.appendChild($hdr);
13598
+ $container.appendChild($hdr);
11955
13599
  let $ul = createElement2(["ul", {
11956
13600
  id: body_id,
11957
13601
  class: "menu-list yui-accordion-body is-hidden",
@@ -11963,9 +13607,9 @@ function render_accordion(gobj, items) {
11963
13607
  show_label,
11964
13608
  stacked: false
11965
13609
  })));
11966
- $root.appendChild($ul);
13610
+ $container.appendChild($ul);
11967
13611
  }
11968
- return $root;
13612
+ return $container;
11969
13613
  }
11970
13614
  /************************************************************
11971
13615
  * Shared helpers
@@ -12100,18 +13744,31 @@ function item_iconbar(gobj, it, opts) {
12100
13744
  ]
12101
13745
  ];
12102
13746
  }
12103
- function wire_clicks(gobj, $root) {
13747
+ function wire_clicks(gobj, $container) {
12104
13748
  let priv = gobj_read_attr(gobj, "priv") || {};
12105
13749
  let handler = (ev) => {
12106
13750
  let target = ev.target;
12107
13751
  if (!target || !target.closest) return;
13752
+ let $close = target.closest("[data-close-item]");
13753
+ if ($close) {
13754
+ let $routed = $close.closest("[data-route]");
13755
+ gobj_publish_event(gobj, "EV_NAV_ITEM_CLOSE", {
13756
+ item_id: $close.getAttribute("data-close-item") || "",
13757
+ route: $routed && $routed.getAttribute("data-route") || "",
13758
+ menu_id: gobj_read_attr(gobj, "menu_id") || "",
13759
+ zone: gobj_read_attr(gobj, "zone") || ""
13760
+ });
13761
+ ev.preventDefault();
13762
+ ev.stopPropagation();
13763
+ return;
13764
+ }
12108
13765
  if (target.closest("[data-close-drawer]")) {
12109
13766
  gobj_publish_event(gobj, "EV_DRAWER_CLOSE_REQUESTED", { menu_id: gobj_read_attr(gobj, "menu_id") || "" });
12110
13767
  ev.preventDefault();
12111
13768
  return;
12112
13769
  }
12113
13770
  let $head = target.closest(".yui-accordion-head");
12114
- if ($head && $root.contains($head)) {
13771
+ if ($head && $container.contains($head)) {
12115
13772
  let $next = $head.nextElementSibling;
12116
13773
  if ($next && $next.classList.contains("yui-accordion-body")) {
12117
13774
  let open = $next.classList.contains("is-hidden");
@@ -12138,13 +13795,23 @@ function wire_clicks(gobj, $root) {
12138
13795
  level: gobj_read_attr(gobj, "level") || "primary"
12139
13796
  });
12140
13797
  };
12141
- $root.addEventListener("click", handler);
13798
+ $container.addEventListener("click", handler);
12142
13799
  priv.click_handler = handler;
12143
13800
  }
12144
13801
  /***************************
12145
13802
  * Actions
12146
13803
  ***************************/
12147
13804
  /************************************************************
13805
+ * EV_SET_ITEMS — replace this nav's items and re-render in place.
13806
+ * This is the runtime counterpart of the startup config path:
13807
+ * the shell (or app, via the shell) drives dynamic navs through it.
13808
+ ************************************************************/
13809
+ function ac_set_items(gobj, event, kw, src) {
13810
+ gobj_write_attr(gobj, "menu_items", is_array(kw.items) ? kw.items : []);
13811
+ rebuild(gobj);
13812
+ return 0;
13813
+ }
13814
+ /************************************************************
12148
13815
  * EV_ROUTE_CHANGED from the shell — refresh active highlight
12149
13816
  ************************************************************/
12150
13817
  function ac_route_changed(gobj, event, kw, src) {
@@ -12203,10 +13870,16 @@ function create_gclass$4(gclass_name) {
12203
13870
  "EV_ROUTE_CHANGED",
12204
13871
  ac_route_changed,
12205
13872
  null
13873
+ ], [
13874
+ "EV_SET_ITEMS",
13875
+ ac_set_items,
13876
+ null
12206
13877
  ]]]];
12207
13878
  __gclass__$4 = gclass_create(gclass_name, [
12208
13879
  ["EV_ROUTE_CHANGED", 0],
13880
+ ["EV_SET_ITEMS", 0],
12209
13881
  ["EV_NAV_CLICKED", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_PUBLIC_EVENT],
13882
+ ["EV_NAV_ITEM_CLOSE", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_PUBLIC_EVENT],
12210
13883
  ["EV_DRAWER_CLOSE_REQUESTED", event_flag_t.EVF_OUTPUT_EVENT | event_flag_t.EVF_PUBLIC_EVENT]
12211
13884
  ], states, gmt$4, 0, attrs_table$4, PRIVATE_DATA$4, 0, 0, 0, 0);
12212
13885
  if (!__gclass__$4) return -1;
@@ -12216,7 +13889,7 @@ function register_c_yui_nav() {
12216
13889
  return create_gclass$4(GCLASS_NAME$4);
12217
13890
  }
12218
13891
  //#endregion
12219
- //#region shell_modals.js
13892
+ //#region src/shell_modals.js
12220
13893
  /***********************************************************************
12221
13894
  * shell_modals.js
12222
13895
  *
@@ -12350,27 +14023,74 @@ function yui_shell_show_modal(shell, content, opts) {
12350
14023
  content
12351
14024
  ]]
12352
14025
  ] : null;
14026
+ let dialog = !!(opts && opts.dialog);
14027
+ let title = opts && opts.title || "";
12353
14028
  let with_close = !(opts && opts.with_close_button === false);
12354
- let modal_children = [["div", { class: "modal-background" }], [
12355
- "div",
12356
- { class: "modal-content" },
12357
- inner ? [inner] : []
12358
- ]];
12359
- if (with_close) modal_children.push(["button", {
12360
- class: "modal-close is-large",
12361
- "aria-label": "close"
12362
- }]);
14029
+ let modal_children;
14030
+ if (dialog) {
14031
+ modal_children = [["div", { class: "modal-background" }], [
14032
+ "div",
14033
+ { class: "modal-content yui-dialog-content" },
14034
+ [[
14035
+ "div",
14036
+ { class: "yui-dialog-header" },
14037
+ [
14038
+ [
14039
+ "button",
14040
+ {
14041
+ class: "yui-dialog-back",
14042
+ type: "button",
14043
+ "aria-label": "back"
14044
+ },
14045
+ [["i", { class: "yi-arrow-left" }]]
14046
+ ],
14047
+ [
14048
+ "span",
14049
+ {
14050
+ class: "yui-dialog-title",
14051
+ i18n: title
14052
+ },
14053
+ title
14054
+ ],
14055
+ [
14056
+ "button",
14057
+ {
14058
+ class: "yui-dialog-x",
14059
+ type: "button",
14060
+ "aria-label": "close"
14061
+ },
14062
+ [["i", { class: "yi-xmark" }]]
14063
+ ]
14064
+ ]
14065
+ ], [
14066
+ "div",
14067
+ { class: "yui-dialog-body" },
14068
+ inner ? [inner] : []
14069
+ ]]
14070
+ ]];
14071
+ with_close = false;
14072
+ } else {
14073
+ modal_children = [["div", { class: "modal-background" }], [
14074
+ "div",
14075
+ { class: "modal-content" },
14076
+ inner ? [inner] : []
14077
+ ]];
14078
+ if (with_close) modal_children.push(["button", {
14079
+ class: "modal-close is-large",
14080
+ "aria-label": "close"
14081
+ }]);
14082
+ }
12363
14083
  let $modal = createElement2([
12364
14084
  "div",
12365
14085
  {
12366
- class: "modal yui-modal is-active",
14086
+ class: "modal yui-modal" + (dialog ? " yui-dialog" : "") + " is-active",
12367
14087
  role: "dialog",
12368
14088
  "aria-modal": "true"
12369
14089
  },
12370
14090
  modal_children
12371
14091
  ]);
12372
14092
  $layer.appendChild($modal);
12373
- if (!inner && content && typeof content.appendChild !== "undefined") $modal.querySelector(".modal-content").appendChild(content);
14093
+ if (!inner && content && typeof content.appendChild !== "undefined") (dialog ? $modal.querySelector(".yui-dialog-body") : $modal.querySelector(".modal-content")).appendChild(content);
12374
14094
  maybe_apply_translator($modal, opts);
12375
14095
  let closed = false;
12376
14096
  let close_fn = null;
@@ -12399,6 +14119,12 @@ function yui_shell_show_modal(shell, content, opts) {
12399
14119
  if (opts == null || opts.dismiss_on_background !== false) $modal.querySelector(".modal-background").addEventListener("click", close);
12400
14120
  let $close_btn = $modal.querySelector(".modal-close");
12401
14121
  if ($close_btn) $close_btn.addEventListener("click", close);
14122
+ if (dialog) {
14123
+ let $back = $modal.querySelector(".yui-dialog-back");
14124
+ if ($back) $back.addEventListener("click", close);
14125
+ let $x = $modal.querySelector(".yui-dialog-x");
14126
+ if ($x) $x.addEventListener("click", close);
14127
+ }
12402
14128
  return { close };
12403
14129
  }
12404
14130
  /***************************************************************
@@ -12550,7 +14276,7 @@ function yui_shell_confirm_yesnocancel(shell, message, opts) {
12550
14276
  ], opts);
12551
14277
  }
12552
14278
  //#endregion
12553
- //#region c_yui_treedb_topics.js
14279
+ //#region src/c_yui_treedb_topics.js
12554
14280
  /***********************************************************************
12555
14281
  * c_yui_treedb_topics.js
12556
14282
  *
@@ -12593,7 +14319,7 @@ var __gclass__$3 = null;
12593
14319
  * Framework Method: Create
12594
14320
  ***************************************************************/
12595
14321
  function mt_create$3(gobj) {
12596
- build_ui$2(gobj);
14322
+ build_ui$3(gobj);
12597
14323
  let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
12598
14324
  if (!subscriber) subscriber = gobj_parent(gobj);
12599
14325
  gobj_subscribe_event(gobj, null, {}, subscriber);
@@ -12667,11 +14393,11 @@ function cmd_get_topic_data$1(gobj, cmd, kw, src) {
12667
14393
  /************************************************************
12668
14394
  * Build UI
12669
14395
  ************************************************************/
12670
- function build_ui$2(gobj) {
14396
+ function build_ui$3(gobj) {
12671
14397
  gobj_write_attr(gobj, "$container", createElement2([
12672
14398
  "div",
12673
14399
  {
12674
- class: `${gobj_read_attr(gobj, "treedb_name")}`,
14400
+ class: `C_YUI_TREEDB_TOPICS ${gobj_read_attr(gobj, "treedb_name")}`,
12675
14401
  style: "height:100%; display:flex; flex-direction:column;"
12676
14402
  },
12677
14403
  [[
@@ -12696,6 +14422,26 @@ function build_ui$2(gobj) {
12696
14422
  ]));
12697
14423
  }
12698
14424
  /************************************************************
14425
+ * Show a non-blocking inline error banner at the top of the view
14426
+ * (used when the treedb schema `descs` cannot load). Reuses a single
14427
+ * banner so retries don't stack.
14428
+ ************************************************************/
14429
+ function show_load_error$1(gobj, message) {
14430
+ let $container = gobj_read_attr(gobj, "$container");
14431
+ if (!$container) return;
14432
+ let $err = $container.querySelector(".treedb-load-error");
14433
+ if (!$err) {
14434
+ $err = createElement2([
14435
+ "div",
14436
+ { class: "notification is-danger is-light m-3 treedb-load-error" },
14437
+ []
14438
+ ]);
14439
+ $container.insertBefore($err, $container.firstChild);
14440
+ }
14441
+ let treedb = gobj_read_attr(gobj, "treedb_name") || "";
14442
+ $err.textContent = (treedb ? `${treedb}: ` : "") + (message || "cannot load treedb");
14443
+ }
14444
+ /************************************************************
12699
14445
  * Destroy UI
12700
14446
  ************************************************************/
12701
14447
  function destroy_ui$2(gobj) {
@@ -12915,10 +14661,14 @@ function ac_mt_command_answer$1(gobj, event, kw, src) {
12915
14661
  log_error(e);
12916
14662
  return;
12917
14663
  }
12918
- if (result < 0) display_error_message("Error", t(comment));
12919
14664
  let __command__ = msg_iev_get_stack(gobj, kw, "command_stack", true);
12920
14665
  let command = kw_get_str(gobj, __command__, "command", "", kw_flag_t.KW_REQUIRED);
12921
14666
  let kw_command = kw_get_dict(gobj, __command__, "kw", {}, kw_flag_t.KW_REQUIRED);
14667
+ if (result < 0) {
14668
+ if (command === "descs") show_load_error$1(gobj, t(comment));
14669
+ else display_error_message("Error", t(comment));
14670
+ return 0;
14671
+ }
12922
14672
  switch (command) {
12923
14673
  case "descs":
12924
14674
  if (result >= 0) {
@@ -13183,7 +14933,7 @@ function register_c_yui_treedb_topics() {
13183
14933
  return create_gclass$3(GCLASS_NAME$3);
13184
14934
  }
13185
14935
  //#endregion
13186
- //#region c_yui_treedb_topic_with_form.js
14936
+ //#region src/c_yui_treedb_topic_with_form.js
13187
14937
  /***********************************************************************
13188
14938
  * c_yui_treedb_topic_with_form.js
13189
14939
  *
@@ -13261,7 +15011,7 @@ function mt_create$2(gobj) {
13261
15011
  gobj_write_attr(gobj, "toolbar_id", "toolbar" + name);
13262
15012
  gobj_write_attr(gobj, "form_id", "form" + name);
13263
15013
  gobj_write_attr(gobj, "popup_id", "popup" + name);
13264
- build_ui$1(gobj);
15014
+ build_ui$2(gobj);
13265
15015
  let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
13266
15016
  if (!subscriber) subscriber = gobj_parent(gobj);
13267
15017
  gobj_subscribe_event(gobj, null, {}, subscriber);
@@ -13345,7 +15095,7 @@ async function readClipboard(gobj) {
13345
15095
  /************************************************************
13346
15096
  * Build UI
13347
15097
  ************************************************************/
13348
- function build_ui$1(gobj) {
15098
+ function build_ui$2(gobj) {
13349
15099
  function create_table_toolbar() {
13350
15100
  let $table_toolbar = [];
13351
15101
  let with_edition_mode = gobj_read_bool_attr(gobj, "with_edition_mode");
@@ -13562,7 +15312,7 @@ function build_ui$1(gobj) {
13562
15312
  let $container = createElement2([
13563
15313
  "div",
13564
15314
  {
13565
- class: "container-treedb-topic-with-form",
15315
+ class: "C_YUI_TREEDB_TOPIC_WITH_FORM",
13566
15316
  style: "height:100%;"
13567
15317
  },
13568
15318
  [["div", {
@@ -15573,7 +17323,7 @@ function register_c_yui_treedb_topic_with_form() {
15573
17323
  return create_gclass$2(GCLASS_NAME$2);
15574
17324
  }
15575
17325
  //#endregion
15576
- //#region lib_graph.js
17326
+ //#region src/lib_graph.js
15577
17327
  /************************************************************
15578
17328
  * Add class to elements selected
15579
17329
  ************************************************************/
@@ -15677,7 +17427,7 @@ function getStrokeColor(fillColor, theme = "light", factor = .2) {
15677
17427
  return `rgba(${adjust(r)}, ${adjust(g)}, ${adjust(b)}, ${a.toFixed(3)})`;
15678
17428
  }
15679
17429
  //#endregion
15680
- //#region c_yui_treedb_graph.js
17430
+ //#region src/c_yui_treedb_graph.js
15681
17431
  /***********************************************************************
15682
17432
  * c_yui_treedb_graph.js
15683
17433
  *
@@ -15753,7 +17503,7 @@ function mt_create$1(gobj) {
15753
17503
  let __yui_main__ = gobj_find_service("__yui_main__");
15754
17504
  if (__yui_main__) gobj_subscribe_event(__yui_main__, "EV_RESIZE", {}, gobj);
15755
17505
  gobj_write_attr(gobj, "canvas_id", clean_name(gobj_name(gobj)) + "-canvas");
15756
- let $container_canvas = build_ui(gobj).querySelector(`#${priv.canvas_id}`);
17506
+ let $container_canvas = build_ui$1(gobj).querySelector(`#${priv.canvas_id}`);
15757
17507
  priv.gobj_nodes_tree = gobj_create_service(`${gobj_name(gobj)}-g6`, "C_G6_NODES_TREE", {
15758
17508
  $container: $container_canvas,
15759
17509
  subscriber: gobj,
@@ -15803,13 +17553,13 @@ function mt_destroy$1(gobj) {
15803
17553
  /************************************************************
15804
17554
  * Build UI
15805
17555
  ************************************************************/
15806
- function build_ui(gobj) {
17556
+ function build_ui$1(gobj) {
15807
17557
  let priv = gobj.priv;
15808
17558
  let padding = gobj_read_attr(gobj, "padding");
15809
17559
  let $container = createElement2([
15810
17560
  "div",
15811
17561
  {
15812
- class: "graphs",
17562
+ class: "C_YUI_TREEDB_GRAPH",
15813
17563
  style: `height:100%; display:flex; flex-direction:column;`
15814
17564
  },
15815
17565
  [[
@@ -15838,6 +17588,26 @@ function build_ui(gobj) {
15838
17588
  return $container;
15839
17589
  }
15840
17590
  /************************************************************
17591
+ * Show a non-blocking inline error banner at the top of the view
17592
+ * (used when the treedb schema `descs` cannot load). Reuses a single
17593
+ * banner so retries don't stack.
17594
+ ************************************************************/
17595
+ function show_load_error(gobj, message) {
17596
+ let $container = gobj_read_attr(gobj, "$container");
17597
+ if (!$container) return;
17598
+ let $err = $container.querySelector(".treedb-load-error");
17599
+ if (!$err) {
17600
+ $err = createElement2([
17601
+ "div",
17602
+ { class: "notification is-danger is-light m-3 treedb-load-error" },
17603
+ []
17604
+ ]);
17605
+ $container.insertBefore($err, $container.firstChild);
17606
+ }
17607
+ let treedb = gobj_read_attr(gobj, "treedb_name") || "";
17608
+ $err.textContent = (treedb ? `${treedb}: ` : "") + (message || "cannot load treedb");
17609
+ }
17610
+ /************************************************************
15841
17611
  * Destroy UI
15842
17612
  ************************************************************/
15843
17613
  function destroy_ui(gobj) {
@@ -16179,10 +17949,11 @@ function ac_mt_command_answer(gobj, event, kw, src) {
16179
17949
  log_error(e);
16180
17950
  return;
16181
17951
  }
16182
- if (result < 0) display_error_message("Error", t(comment));
16183
17952
  let __command__ = msg_iev_get_stack(gobj, kw, "command_stack", true);
16184
17953
  let command = kw_get_str(gobj, __command__, "command", "", kw_flag_t.KW_REQUIRED);
16185
17954
  let kw_command = kw_get_dict(gobj, __command__, "kw", {}, kw_flag_t.KW_REQUIRED);
17955
+ if (result < 0) if (command === "descs") show_load_error(gobj, t(comment));
17956
+ else display_error_message("Error", t(comment));
16186
17957
  switch (command) {
16187
17958
  case "descs":
16188
17959
  if (result >= 0) process_treedb_descs(gobj, data);
@@ -43106,7 +44877,7 @@ Sector.PARSED_STYLE_LIST = new Set([].concat(_toConsumableArray(Path.PARSED_STYL
43106
44877
  "endAngle"
43107
44878
  ]));
43108
44879
  //#endregion
43109
- //#region lib_icons.js
44880
+ //#region src/lib_icons.js
43110
44881
  /*********************************************************
43111
44882
  * lib_icons.js
43112
44883
  *
@@ -43217,7 +44988,7 @@ function inject_svg_icons() {
43217
44988
  document.body.appendChild(div);
43218
44989
  }
43219
44990
  //#endregion
43220
- //#region c_g6_nodes_tree.js
44991
+ //#region src/c_g6_nodes_tree.js
43221
44992
  /***********************************************************************
43222
44993
  * c_g6_nodes_tree.js
43223
44994
  *
@@ -43403,6 +45174,7 @@ function mt_create(gobj) {
43403
45174
  gobj.priv.theme_observer = mo;
43404
45175
  }
43405
45176
  }
45177
+ build_ui(gobj);
43406
45178
  register_layouts(gobj);
43407
45179
  build_graph(gobj);
43408
45180
  if (typeof ResizeObserver !== "undefined" && priv.$container) {
@@ -43501,6 +45273,13 @@ function get_default_ne_xy(gobj) {
43501
45273
  return xy;
43502
45274
  }
43503
45275
  /************************************************************
45276
+ * Build UI
45277
+ ************************************************************/
45278
+ function build_ui(gobj) {
45279
+ let $container = gobj_read_attr(gobj, "$container");
45280
+ if ($container) $container.classList.add(GCLASS_NAME);
45281
+ }
45282
+ /************************************************************
43504
45283
  * Register custom G6 layouts
43505
45284
  ************************************************************/
43506
45285
  var _g6_extensions_registered = false;
@@ -47415,7 +49194,7 @@ function register_c_g6_nodes_tree() {
47415
49194
  return create_gclass(GCLASS_NAME);
47416
49195
  }
47417
49196
  //#endregion
47418
- //#region themes.js
49197
+ //#region src/themes.js
47419
49198
  function themes() {
47420
49199
  function openModal($el) {
47421
49200
  $el.classList.add("is-active");
@@ -47545,7 +49324,7 @@ function themes() {
47545
49324
  onClickOutside(".js-menu");
47546
49325
  }
47547
49326
  //#endregion
47548
- //#region ytable.js
49327
+ //#region src/ytable.js
47549
49328
  var default_options = {
47550
49329
  id: void 0,
47551
49330
  width: "100%",
@@ -47817,6 +49596,37 @@ function createYTable($container, columns, options) {
47817
49596
  return new YTable($container, columns, options);
47818
49597
  }
47819
49598
  //#endregion
47820
- export { EditControl, MarkerControl, YTable, addClasses, apply_dev_traces, build_dev_panel, createYTable, dev_window_was_open, disableElements, display_error_message, display_info_message, display_volatil_modal, display_warning_message, enableElements, getStrokeColor, get_ok, get_yesno, get_yesnocancel, info_traffic, inject_svg_icons, register_c_g6_nodes_tree, register_c_yui_form, register_c_yui_gobj_tree_js, register_c_yui_json_graph, register_c_yui_main, register_c_yui_map, register_c_yui_nav, register_c_yui_pager, register_c_yui_routing, register_c_yui_shell, register_c_yui_tabs, register_c_yui_treedb_graph, register_c_yui_treedb_topic_with_form, register_c_yui_treedb_topics, register_c_yui_uplot, register_c_yui_window, register_c_yui_wizard, removeChildElements, removeClasses, set_active_state, set_cancel_state, set_submit_state, setup_dev, themes, toggleClasses, yui_shell_close_drawer, yui_shell_close_dropdown, yui_shell_confirm_ok, yui_shell_confirm_yesno, yui_shell_confirm_yesnocancel, yui_shell_navigate, yui_shell_open_drawer, yui_shell_pop_escape, yui_shell_push_escape, yui_shell_refresh_avatars, yui_shell_set_avatar_provider, yui_shell_set_connection_state, yui_shell_set_toolbar_item_icon, yui_shell_set_translator, yui_shell_show_error, yui_shell_show_info, yui_shell_show_modal, yui_shell_show_warning, yui_shell_toggle_drawer, yui_toolbar };
49599
+ //#region src/yui_inputs.js
49600
+ /***************************************************************
49601
+ * Add a clear (✕) button to a Bulma control wrapping an input.
49602
+ * Returns the button element (already appended to $control).
49603
+ ***************************************************************/
49604
+ function attach_clear($control, $input, on_clear) {
49605
+ if (!$control || !$input) return null;
49606
+ $control.classList.add("has-clear");
49607
+ let $btn = createElement2(["button", {
49608
+ type: "button",
49609
+ class: "delete is-medium yui-input-clear",
49610
+ tabindex: "-1",
49611
+ title: i18next.t("clear"),
49612
+ "aria-label": i18next.t("clear")
49613
+ }]);
49614
+ function sync() {
49615
+ $btn.classList.toggle("is-visible", !!$input.value);
49616
+ }
49617
+ $input.addEventListener("input", sync);
49618
+ $btn.addEventListener("click", () => {
49619
+ $input.value = "";
49620
+ $input.dispatchEvent(new Event("input", { bubbles: true }));
49621
+ if (typeof on_clear === "function") on_clear();
49622
+ $input.focus();
49623
+ sync();
49624
+ });
49625
+ $control.appendChild($btn);
49626
+ sync();
49627
+ return $btn;
49628
+ }
49629
+ //#endregion
49630
+ export { EditControl, MarkerControl, YTable, addClasses, apply_dev_traces, attach_clear, build_dev_panel, createYTable, dev_window_was_open, disableElements, display_error_message, display_info_message, display_volatil_modal, display_warning_message, enableElements, getStrokeColor, get_ok, get_yesno, get_yesnocancel, info_traffic, inject_svg_icons, register_c_g6_nodes_tree, register_c_yui_form, register_c_yui_gobj_tree_js, register_c_yui_json_graph, register_c_yui_main, register_c_yui_map, register_c_yui_nav, register_c_yui_pager, register_c_yui_routing, register_c_yui_shell, register_c_yui_tabs, register_c_yui_treedb_graph, register_c_yui_treedb_topic_with_form, register_c_yui_treedb_topics, register_c_yui_uplot, register_c_yui_window, register_c_yui_window_manager, register_c_yui_wizard, removeChildElements, removeClasses, set_active_state, set_cancel_state, set_submit_state, setup_dev, themes, toggleClasses, yui_shell_close_drawer, yui_shell_close_dropdown, yui_shell_confirm_ok, yui_shell_confirm_yesno, yui_shell_confirm_yesnocancel, yui_shell_navigate, yui_shell_open_drawer, yui_shell_pop_escape, yui_shell_push_escape, yui_shell_refresh_avatars, yui_shell_set_avatar_provider, yui_shell_set_connection_state, yui_shell_set_toolbar_item_icon, yui_shell_set_translator, yui_shell_show_error, yui_shell_show_info, yui_shell_show_modal, yui_shell_show_warning, yui_shell_toggle_drawer, yui_toolbar };
47821
49631
 
47822
49632
  //# sourceMappingURL=gobj-ui.es.js.map