@yuneta/gobj-ui 2.0.0 → 2.2.0

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 +2331 -526
  3. package/dist/gobj-ui.es.js +2331 -528
  4. package/index.js +32 -31
  5. package/package.json +5 -6
  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} +163 -10
  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 +1478 -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
@@ -34,7 +34,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
34
34
  let _yuneta_gobj_js = require("@yuneta/gobj-js");
35
35
  let i18next = require("i18next");
36
36
  i18next = __toESM(i18next, 1);
37
- let vanilla_jsoneditor = require("vanilla-jsoneditor");
38
37
  let tom_select = require("tom-select");
39
38
  tom_select = __toESM(tom_select, 1);
40
39
  let tabulator_tables = require("tabulator-tables");
@@ -43,107 +42,908 @@ maplibre_gl = __toESM(maplibre_gl, 1);
43
42
  let uplot = require("uplot");
44
43
  uplot = __toESM(uplot, 1);
45
44
  let _antv_g6 = require("@antv/g6");
46
- //#region yui_dev.js
45
+ let vanilla_jsoneditor = require("vanilla-jsoneditor");
46
+ //#region src/yui_dev.js
47
47
  /***********************************************************************
48
48
  * ui_dev.js
49
49
  *
50
- * Development Tools
50
+ * Development Tools — yuno monitor / audit console
51
51
  *
52
- * Copyright (c) 2024, ArtGins.
52
+ * Copyright (c) 2024-2026, ArtGins.
53
53
  * All Rights Reserved.
54
54
  ***********************************************************************/
55
+ /***********************************************************************
56
+ * Traffic model (bounded ring buffer)
57
+ *
58
+ * Every inter-event message is kept as a lightweight record so view
59
+ * and filter changes re-render instantly from memory instead of
60
+ * losing history. Reopening the window repaints the buffer.
61
+ ***********************************************************************/
62
+ var TRAFFIC_MAX = 600;
63
+ var PERIODIC_THRESHOLD = 5;
64
+ var PERIODIC_RE = /PERIODIC|TIMEOUT|HEARTBEAT|PING/i;
65
+ var TRAFFIC_LOG = [];
66
+ var TRAFFIC_COUNTS = /* @__PURE__ */ new Map();
67
+ var SEARCH_TEXT = "";
68
+ var TRAFFIC_TS_FIELDS = {
69
+ "__t__": 1,
70
+ "__tm__": 1,
71
+ "tm": 1,
72
+ "t": 1,
73
+ "from_t": 1,
74
+ "to_t": 1,
75
+ "from_tm": 1,
76
+ "to_tm": 1,
77
+ "time": 1
78
+ };
79
+ var TRACE_DEFS = [
80
+ [
81
+ "trace_automata",
82
+ "Automata",
83
+ trace_automata
84
+ ],
85
+ [
86
+ "trace_creation",
87
+ "Creation",
88
+ trace_creation
89
+ ],
90
+ [
91
+ "trace_start_stop",
92
+ "Start/Stop",
93
+ trace_start_stop
94
+ ],
95
+ [
96
+ "trace_subscriptions",
97
+ "Subscriptions",
98
+ trace_subscriptions
99
+ ],
100
+ [
101
+ "trace_i18n",
102
+ "I18n",
103
+ trace_i18n
104
+ ],
105
+ [
106
+ "trace_traffic",
107
+ "Traffic",
108
+ trace_traffic
109
+ ],
110
+ [
111
+ "no_poll",
112
+ "No Poll",
113
+ set_no_poll
114
+ ]
115
+ ];
116
+ /******************************
117
+ * Small helpers
118
+ ******************************/
119
+ /************************************************************
120
+ * hh:mm:ss.SSS wall-clock of the moment a message arrives.
121
+ ************************************************************/
122
+ function traffic_now() {
123
+ let now = /* @__PURE__ */ new Date();
124
+ let pad = (num, len) => ("000" + num).slice(len * -1);
125
+ return `${pad(now.getHours(), 2)}:${pad(now.getMinutes(), 2)}:${pad(now.getSeconds(), 2)}.${pad(now.getMilliseconds(), 3)}`;
126
+ }
127
+ /************************************************************
128
+ * Human byte size (B / KB / MB).
129
+ ************************************************************/
130
+ function traffic_size(n) {
131
+ n = Number(n) || 0;
132
+ if (n < 1024) return n + " B";
133
+ if (n < 1024 * 1024) return (n / 1024).toFixed(1) + " KB";
134
+ return (n / (1024 * 1024)).toFixed(1) + " MB";
135
+ }
136
+ /************************************************************
137
+ * Seconds-since-epoch → ISO string, or null if not a plausible
138
+ * timestamp (guards against 0 / NaN / out-of-range values).
139
+ ************************************************************/
140
+ function traffic_iso(value) {
141
+ let n = Number(value);
142
+ if (!isFinite(n) || n <= 0) return null;
143
+ try {
144
+ return (/* @__PURE__ */ new Date(n * 1e3)).toISOString();
145
+ } catch (e) {
146
+ return null;
147
+ }
148
+ }
149
+ /************************************************************
150
+ * Clip a string for inline display (full text kept elsewhere).
151
+ ************************************************************/
152
+ function traffic_clip(s, n) {
153
+ s = String(s);
154
+ return s.length > n ? s.slice(0, n) + "…" : s;
155
+ }
156
+ /************************************************************
157
+ * A scalar rendered as a short inline token (for summaries).
158
+ ************************************************************/
159
+ function traffic_scalar_text(v) {
160
+ if (v === null) return "null";
161
+ if (typeof v === "string") return traffic_clip(v, 40);
162
+ return String(v);
163
+ }
164
+ function dir_class(dir) {
165
+ return dir === 2 ? "dir-in" : dir === 3 ? "dir-err" : "dir-out";
166
+ }
167
+ function dir_arrow(dir) {
168
+ return dir === 2 ? "⇠" : dir === 3 ? "⚠" : "⇢";
169
+ }
170
+ /******************************
171
+ * Preferences
172
+ ******************************/
173
+ function dev_num(key, def) {
174
+ return Number((0, _yuneta_gobj_js.kw_get_local_storage_value)(key, def === void 0 ? 0 : def, false));
175
+ }
176
+ function dev_view() {
177
+ let v = (0, _yuneta_gobj_js.kw_get_local_storage_value)("dev_view_mode", "detailed", false);
178
+ return v === "compact" || v === "name" || v === "full" ? v : "detailed";
179
+ }
180
+ function dev_hide_periodic() {
181
+ return dev_num("dev_hide_periodic", 0) ? true : false;
182
+ }
183
+ function dev_muted() {
184
+ let a = (0, _yuneta_gobj_js.kw_get_local_storage_value)("dev_muted_events", [], false);
185
+ if (!Array.isArray(a)) a = [];
186
+ return new Set(a);
187
+ }
188
+ function dev_set_muted(set) {
189
+ (0, _yuneta_gobj_js.kw_set_local_storage_value)("dev_muted_events", Array.from(set));
190
+ }
191
+ function set_view(v) {
192
+ (0, _yuneta_gobj_js.kw_set_local_storage_value)("dev_view_mode", v);
193
+ rerender_all();
194
+ refresh_dev_chrome();
195
+ }
196
+ function toggle_pref(key, def) {
197
+ (0, _yuneta_gobj_js.kw_set_local_storage_value)(key, dev_num(key, def) ? 0 : 1);
198
+ rerender_all();
199
+ refresh_dev_chrome();
200
+ }
201
+ function mute_signature(sig) {
202
+ let set = dev_muted();
203
+ set.add(sig);
204
+ dev_set_muted(set);
205
+ rerender_all();
206
+ refresh_dev_chrome();
207
+ }
208
+ function unmute_signature(sig) {
209
+ let set = dev_muted();
210
+ set.delete(sig);
211
+ dev_set_muted(set);
212
+ rerender_all();
213
+ refresh_dev_chrome();
214
+ }
215
+ /******************************
216
+ * Filtering
217
+ ******************************/
218
+ function traffic_signature(event, command) {
219
+ return command ? event + " · " + command : event;
220
+ }
221
+ function traffic_is_periodic(sig) {
222
+ if (PERIODIC_RE.test(sig)) return true;
223
+ return (TRAFFIC_COUNTS.get(sig) || 0) >= PERIODIC_THRESHOLD;
224
+ }
225
+ function build_filter_ctx() {
226
+ return {
227
+ out: dev_num("dev_filter_out", 1),
228
+ inc: dev_num("dev_filter_in", 1),
229
+ err: dev_num("dev_filter_err", 1),
230
+ muted: dev_muted(),
231
+ hide_periodic: dev_hide_periodic(),
232
+ search: SEARCH_TEXT
233
+ };
234
+ }
235
+ function entry_hidden(e, ctx) {
236
+ if (e.kind === "log") return !!(ctx.search && e.hay.indexOf(ctx.search) < 0);
237
+ if (e.dir === 1 && !ctx.out) return true;
238
+ if (e.dir === 2 && !ctx.inc) return true;
239
+ if (e.dir === 3 && !ctx.err) return true;
240
+ if (ctx.muted.has(e.sig)) return true;
241
+ if (ctx.hide_periodic && traffic_is_periodic(e.sig)) return true;
242
+ if (ctx.search && e.hay.indexOf(ctx.search) < 0) return true;
243
+ return false;
244
+ }
245
+ /******************************
246
+ * Style
247
+ ******************************/
248
+ /************************************************************
249
+ * Inject the monitor stylesheet once. Theme-aware via
250
+ * <html data-theme>; direction-coloured (out / in / error).
251
+ ************************************************************/
252
+ function ensure_dev_style() {
253
+ if (document.getElementById("yui-dev-style")) return;
254
+ let css = `
255
+ /* -------- layout -------- */
256
+ .YDEV_BODY {
257
+ display: flex;
258
+ flex-direction: column;
259
+ height: 100%;
260
+ min-height: 0;
261
+ box-sizing: border-box;
262
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
263
+ }
264
+ .YDEV_LOG { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 6px 10px; }
265
+ .YDEV_MUTED {
266
+ display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
267
+ padding: 4px 10px; border-bottom: 1px solid rgba(0,0,0,0.08); font-size: 12px;
268
+ }
269
+ .YDEV_MUTED:empty { display: none; }
270
+ .YDEV_STATS {
271
+ flex: 0 0 auto; display: flex; flex-wrap: wrap; gap: 14px;
272
+ padding: 6px 10px; border-top: 1px solid rgba(0,0,0,0.1);
273
+ background: rgba(0,0,0,0.03);
274
+ font-family: "DejaVu Sans Mono", monospace; font-size: 11px;
275
+ opacity: 0.9; font-variant-numeric: tabular-nums;
276
+ }
277
+ /* -------- control bar -------- */
278
+ .YDEV_BAR {
279
+ display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px;
280
+ padding: 8px 10px; border-bottom: 1px solid rgba(0,0,0,0.1);
281
+ background: rgba(0,0,0,0.03);
282
+ }
283
+ .YDEV_GROUP { display: inline-flex; align-items: center; gap: 5px; }
284
+ .YDEV_LABEL {
285
+ font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em;
286
+ opacity: 0.5; align-self: center;
287
+ }
288
+ .YDEV_SEP { width: 1px; align-self: stretch; background: rgba(0,0,0,0.12); }
289
+ .YDEV_CHIP {
290
+ font: inherit; font-size: 12px; line-height: 1.4; padding: 3px 9px;
291
+ border: 1px solid rgba(0,0,0,0.18); border-radius: 999px;
292
+ background: transparent; color: inherit; cursor: pointer;
293
+ display: inline-flex; align-items: center; gap: 5px;
294
+ }
295
+ .YDEV_CHIP:hover { border-color: currentColor; }
296
+ .YDEV_CHIP.is-active { background: rgba(37,99,235,0.14); border-color: #2563eb; color: #2563eb; font-weight: 600; }
297
+ .YDEV_CHIP.s-out.is-active { background: rgba(37,99,235,0.16); border-color: #2563eb; color: #2563eb; }
298
+ .YDEV_CHIP.s-in.is-active { background: rgba(5,150,105,0.16); border-color: #059669; color: #059669; }
299
+ .YDEV_CHIP.s-err.is-active { background: rgba(220,38,38,0.16); border-color: #dc2626; color: #dc2626; }
300
+ .YDEV_CHIP[data-dir]:not(.is-active) { opacity: 0.4; text-decoration: line-through; }
301
+ .YDEV_CHIP[data-toggle="periodic"].is-active { background: rgba(217,119,6,0.16); border-color: #d97706; color: #b45309; font-weight: 600; }
302
+ .YDEV_SEG { display: inline-flex; border: 1px solid rgba(0,0,0,0.18); border-radius: 7px; overflow: hidden; }
303
+ .YDEV_SEG_BTN {
304
+ font: inherit; font-size: 12px; padding: 4px 10px; border: 0;
305
+ border-right: 1px solid rgba(0,0,0,0.12);
306
+ background: transparent; color: inherit; cursor: pointer;
307
+ }
308
+ .YDEV_SEG_BTN:last-child { border-right: 0; }
309
+ .YDEV_SEG_BTN.is-active { background: #2563eb; color: #fff; font-weight: 600; }
310
+ .YDEV_SEARCH {
311
+ font: inherit; font-size: 12px; padding: 4px 9px; min-width: 170px;
312
+ border: 1px solid rgba(0,0,0,0.18); border-radius: 7px;
313
+ background: transparent; color: inherit;
314
+ }
315
+ .YDEV_MUTED_CHIP {
316
+ font: inherit; font-family: "DejaVu Sans Mono", monospace; font-size: 12px;
317
+ display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px;
318
+ border: 1px solid rgba(217,119,6,0.5); border-radius: 999px;
319
+ background: rgba(217,119,6,0.12); color: #b45309; cursor: pointer;
320
+ }
321
+ .YDEV_STAT.s-out { color: #2563eb; } .YDEV_STAT.s-in { color: #059669; } .YDEV_STAT.s-err { color: #dc2626; }
322
+ .YDEV_TITLE { display: flex; align-items: baseline; gap: 8px; }
323
+ .YDEV_TITLE_MAIN { font-weight: 700; }
324
+ .YDEV_TITLE_SUB { opacity: 0.7; font-size: 12px; }
325
+ /* -------- entries (shared) -------- */
326
+ .TRAFFIC_ENTRY, .TRAFFIC_LINE, .TRAFFIC_NAME {
327
+ border-left: 3px solid #94a3b8; border-radius: 3px;
328
+ font-family: "DejaVu Sans Mono", monospace, consolas, monaco; font-size: 13px;
329
+ background: rgba(0,0,0,0.02);
330
+ }
331
+ .TRAFFIC_ENTRY { margin: 6px 0; padding: 4px 8px; line-height: 1.55; }
332
+ .TRAFFIC_LINE { margin: 2px 0; padding: 2px 8px; display: flex; align-items: baseline; gap: 8px; }
333
+ .TRAFFIC_NAME { margin: 1px 0; padding: 1px 8px; display: flex; align-items: baseline; gap: 8px; background: transparent; }
334
+ .TRAFFIC_ENTRY.dir-out, .TRAFFIC_LINE.dir-out, .TRAFFIC_NAME.dir-out { border-left-color: #2563eb; }
335
+ .TRAFFIC_ENTRY.dir-in, .TRAFFIC_LINE.dir-in, .TRAFFIC_NAME.dir-in { border-left-color: #059669; }
336
+ .TRAFFIC_ENTRY.dir-err, .TRAFFIC_LINE.dir-err, .TRAFFIC_NAME.dir-err { border-left-color: #dc2626; }
337
+ .TRAFFIC_HEADER { display: flex; align-items: baseline; gap: 8px; }
338
+ .TRAFFIC_ARROW { font-weight: 700; }
339
+ .TRAFFIC_EVENT { font-weight: 700; }
340
+ .TRAFFIC_CMD { opacity: 0.75; font-weight: 600; }
341
+ .dir-out .TRAFFIC_ARROW, .dir-out .TRAFFIC_EVENT { color: #2563eb; }
342
+ .dir-in .TRAFFIC_ARROW, .dir-in .TRAFFIC_EVENT { color: #059669; }
343
+ .dir-err .TRAFFIC_ARROW, .dir-err .TRAFFIC_EVENT { color: #dc2626; }
344
+ .TRAFFIC_SUMMARY { opacity: 0.6; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1 1 auto; min-width: 0; }
345
+ .TRAFFIC_META { margin-left: auto; opacity: 0.6; font-size: 11px; white-space: nowrap; }
346
+ .TRAFFIC_MUTE { border: 0; background: transparent; color: inherit; cursor: pointer; opacity: 0; font-size: 12px; padding: 0 2px; flex: 0 0 auto; }
347
+ .TRAFFIC_ENTRY:hover .TRAFFIC_MUTE, .TRAFFIC_LINE:hover .TRAFFIC_MUTE, .TRAFFIC_NAME:hover .TRAFFIC_MUTE { opacity: 0.5; }
348
+ .TRAFFIC_MUTE:hover { opacity: 1 !important; color: #d97706; }
349
+ .TRAFFIC_KW { margin: 2px 0 0 16px; }
350
+ .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; }
351
+ .TRAFFIC_ROW { display: flex; gap: 6px; align-items: baseline; }
352
+ .TRAFFIC_BULLET { opacity: 0.45; flex: 0 0 auto; }
353
+ .TRAFFIC_KEY { opacity: 0.85; flex: 0 0 auto; }
354
+ .TRAFFIC_VAL { word-break: break-word; }
355
+ .TRAFFIC_VAL.t-num { color: #0891b2; }
356
+ .TRAFFIC_VAL.t-bool { color: #9333ea; }
357
+ .TRAFFIC_VAL.t-null { color: #9333ea; font-style: italic; }
358
+ .TRAFFIC_VAL.t-empty { opacity: 0.5; }
359
+ .TRAFFIC_TS { opacity: 0.6; margin-left: 8px; }
360
+ details.TRAFFIC_NEST > summary { cursor: pointer; list-style: none; display: flex; gap: 6px; align-items: baseline; }
361
+ details.TRAFFIC_NEST > summary::-webkit-details-marker { display: none; }
362
+ .TRAFFIC_NEST_KEY { opacity: 0.85; }
363
+ .TRAFFIC_NEST_HINT { opacity: 0.5; margin-left: 4px; }
364
+ .YDEV_EMPTY { opacity: 0.5; font-size: 12px; padding: 18px 10px; text-align: center; }
365
+ /* -------- mirrored console logs (error/warning/info/debug/msg; the automata trace shows as debug) -------- */
366
+ .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); }
367
+ .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; }
368
+ .YDEV_LOG_TXT { flex: 1 1 auto; min-width: 0; white-space: pre-wrap; word-break: break-word; opacity: 0.9; }
369
+ .YDEV_LOG_error { border-left-color: #dc2626; } .YDEV_LOG_error .YDEV_LOG_LVL { color: #dc2626; }
370
+ .YDEV_LOG_warning { border-left-color: #d97706; } .YDEV_LOG_warning .YDEV_LOG_LVL { color: #d97706; }
371
+ .YDEV_LOG_info { border-left-color: #2563eb; } .YDEV_LOG_info .YDEV_LOG_LVL { color: #2563eb; }
372
+ .YDEV_LOG_msg { border-left-color: #0891b2; } .YDEV_LOG_msg .YDEV_LOG_LVL { color: #0891b2; }
373
+ .YDEV_LOG_debug { border-left-color: #94a3b8; } .YDEV_LOG_debug .YDEV_LOG_LVL { color: #94a3b8; } .YDEV_LOG_debug .YDEV_LOG_TXT { opacity: 0.72; }
374
+ .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; }
375
+ /* -------- dark theme -------- */
376
+ :root[data-theme="dark"] .TRAFFIC_FULL { background: rgba(255,255,255,0.05); }
377
+ :root[data-theme="dark"] .YDEV_BAR, :root[data-theme="dark"] .YDEV_STATS { background: rgba(255,255,255,0.04); }
378
+ :root[data-theme="dark"] .YDEV_SEP { background: rgba(255,255,255,0.14); }
379
+ :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); }
380
+ :root[data-theme="dark"] .TRAFFIC_ENTRY, :root[data-theme="dark"] .TRAFFIC_LINE { background: rgba(255,255,255,0.03); }
381
+ :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; }
382
+ :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; }
383
+ :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; }
384
+ :root[data-theme="dark"] .dir-out .TRAFFIC_ARROW, :root[data-theme="dark"] .dir-out .TRAFFIC_EVENT { color: #60a5fa; }
385
+ :root[data-theme="dark"] .dir-in .TRAFFIC_ARROW, :root[data-theme="dark"] .dir-in .TRAFFIC_EVENT { color: #34d399; }
386
+ :root[data-theme="dark"] .dir-err .TRAFFIC_ARROW, :root[data-theme="dark"] .dir-err .TRAFFIC_EVENT { color: #f87171; }
387
+ :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; }
388
+ :root[data-theme="dark"] .TRAFFIC_VAL.t-num { color: #22d3ee; }
389
+ :root[data-theme="dark"] .TRAFFIC_VAL.t-bool, :root[data-theme="dark"] .TRAFFIC_VAL.t-null { color: #c084fc; }
390
+ :root[data-theme="dark"] .YDEV_CHIP.is-active { background: rgba(96,165,250,0.2); border-color: #60a5fa; color: #93c5fd; }
391
+ :root[data-theme="dark"] .YDEV_SEG_BTN.is-active { background: #2563eb; color: #fff; }
392
+ :root[data-theme="dark"] .YDEV_CHIP[data-toggle="periodic"].is-active { background: rgba(217,119,6,0.24); border-color: #f59e0b; color: #fbbf24; }
393
+ :root[data-theme="dark"] .YDEV_MUTED_CHIP { border-color: rgba(245,158,11,0.5); background: rgba(245,158,11,0.16); color: #fbbf24; }
394
+ `;
395
+ let $style = document.createElement("style");
396
+ $style.id = "yui-dev-style";
397
+ $style.textContent = css;
398
+ document.head.appendChild($style);
399
+ }
400
+ /******************************
401
+ * kw bullet rendering
402
+ ******************************/
403
+ /************************************************************
404
+ * One scalar field as a bullet row: `• key: value`.
405
+ * Type-coloured; long strings clipped (full text on hover);
406
+ * timestamp fields get an ISO annotation.
407
+ ************************************************************/
408
+ function traffic_scalar_row(key, value) {
409
+ let cls;
410
+ let text;
411
+ if (value === null) {
412
+ cls = "t-null";
413
+ text = "null";
414
+ } else if (typeof value === "boolean") {
415
+ cls = "t-bool";
416
+ text = value ? "true" : "false";
417
+ } else if (typeof value === "number") {
418
+ cls = "t-num";
419
+ text = String(value);
420
+ } else {
421
+ cls = "t-str";
422
+ text = String(value);
423
+ }
424
+ let full = text;
425
+ if (text.length > 200) text = text.slice(0, 200) + "…";
426
+ let val_children = [[
427
+ "span",
428
+ {
429
+ class: "TRAFFIC_VAL " + cls,
430
+ title: full
431
+ },
432
+ text
433
+ ]];
434
+ if (key in TRAFFIC_TS_FIELDS && typeof value === "number") {
435
+ let iso = traffic_iso(value);
436
+ if (iso) val_children.push([
437
+ "span",
438
+ { class: "TRAFFIC_TS" },
439
+ iso
440
+ ]);
441
+ }
442
+ return [
443
+ "div",
444
+ { class: "TRAFFIC_ROW" },
445
+ [
446
+ [
447
+ "span",
448
+ { class: "TRAFFIC_BULLET" },
449
+ "•"
450
+ ],
451
+ [
452
+ "span",
453
+ { class: "TRAFFIC_KEY" },
454
+ key + ":"
455
+ ],
456
+ [
457
+ "span",
458
+ {},
459
+ val_children
460
+ ]
461
+ ]
462
+ ];
463
+ }
464
+ /************************************************************
465
+ * One field of any type. Scalars → a bullet row; objects and
466
+ * arrays → a collapsed <details> so metadata / nested payloads
467
+ * stay folded. Empty containers render inline.
468
+ ************************************************************/
469
+ function traffic_value_node(key, value) {
470
+ if (value === null || typeof value !== "object") return traffic_scalar_row(key, value);
471
+ let is_arr = Array.isArray(value);
472
+ let count = is_arr ? value.length : Object.keys(value).length;
473
+ if (count === 0) return [
474
+ "div",
475
+ { class: "TRAFFIC_ROW" },
476
+ [
477
+ [
478
+ "span",
479
+ { class: "TRAFFIC_BULLET" },
480
+ "•"
481
+ ],
482
+ [
483
+ "span",
484
+ { class: "TRAFFIC_KEY" },
485
+ key + ":"
486
+ ],
487
+ [
488
+ "span",
489
+ { class: "TRAFFIC_VAL t-empty" },
490
+ is_arr ? "[ ]" : "{ }"
491
+ ]
492
+ ]
493
+ ];
494
+ let hint = is_arr ? `[${count}]` : `{${count}}`;
495
+ return [
496
+ "details",
497
+ { class: "TRAFFIC_NEST" },
498
+ [[
499
+ "summary",
500
+ {},
501
+ [
502
+ [
503
+ "span",
504
+ { class: "TRAFFIC_BULLET" },
505
+ "▸"
506
+ ],
507
+ [
508
+ "span",
509
+ { class: "TRAFFIC_NEST_KEY" },
510
+ key
511
+ ],
512
+ [
513
+ "span",
514
+ { class: "TRAFFIC_NEST_HINT" },
515
+ hint
516
+ ]
517
+ ]
518
+ ], [
519
+ "div",
520
+ { class: "TRAFFIC_KW" },
521
+ traffic_bullets(value)
522
+ ]]
523
+ ];
524
+ }
525
+ /************************************************************
526
+ * A whole object/array → an array of bullet nodes.
527
+ ************************************************************/
528
+ function traffic_bullets(obj) {
529
+ let out = [];
530
+ if (Array.isArray(obj)) for (let i = 0; i < obj.length; i++) out.push(traffic_value_node(String(i), obj[i]));
531
+ else for (let k of Object.keys(obj)) out.push(traffic_value_node(k, obj[k]));
532
+ return out;
533
+ }
534
+ /******************************
535
+ * Entry rendering (per view)
536
+ ******************************/
537
+ function mute_button(sig) {
538
+ return [
539
+ "button",
540
+ {
541
+ class: "TRAFFIC_MUTE",
542
+ type: "button",
543
+ title: "Mute " + sig
544
+ },
545
+ "⊘",
546
+ { click: (ev) => {
547
+ ev.stopPropagation();
548
+ ev.preventDefault();
549
+ mute_signature(sig);
550
+ } }
551
+ ];
552
+ }
553
+ function event_spans(e) {
554
+ let spans = [[
555
+ "span",
556
+ { class: "TRAFFIC_ARROW" },
557
+ dir_arrow(e.dir)
558
+ ], [
559
+ "span",
560
+ { class: "TRAFFIC_EVENT" },
561
+ e.event
562
+ ]];
563
+ if (e.command) spans.push([
564
+ "span",
565
+ { class: "TRAFFIC_CMD" },
566
+ e.command
567
+ ]);
568
+ return spans;
569
+ }
570
+ function compact_summary(kw) {
571
+ if (!kw) return "";
572
+ let parts = [];
573
+ if ("result" in kw) parts.push("result=" + traffic_scalar_text(kw.result));
574
+ if (typeof kw.comment === "string" && kw.comment) parts.push(traffic_clip(kw.comment, 80));
575
+ if (!parts.length) {
576
+ let n = 0;
577
+ for (let k of Object.keys(kw)) {
578
+ if (k === "command") continue;
579
+ let v = kw[k];
580
+ if (v === null || typeof v !== "object") {
581
+ parts.push(k + "=" + traffic_scalar_text(v));
582
+ if (++n >= 3) break;
583
+ }
584
+ }
585
+ }
586
+ return traffic_clip(parts.join(" · "), 140);
587
+ }
588
+ function render_detailed(e) {
589
+ let head = event_spans(e);
590
+ head.push(mute_button(e.sig));
591
+ head.push([
592
+ "span",
593
+ { class: "TRAFFIC_META" },
594
+ `${traffic_size(e.size)} · ${e.ts}`
595
+ ]);
596
+ let children = [[
597
+ "div",
598
+ { class: "TRAFFIC_HEADER" },
599
+ head
600
+ ]];
601
+ let kw = e.kw;
602
+ if (kw && Object.keys(kw).length > 0) children.push([
603
+ "div",
604
+ { class: "TRAFFIC_KW" },
605
+ traffic_bullets(kw)
606
+ ]);
607
+ else if (!kw) children.push([
608
+ "div",
609
+ { class: "TRAFFIC_KW" },
610
+ traffic_bullets(e.jn)
611
+ ]);
612
+ return (0, _yuneta_gobj_js.createElement2)([
613
+ "div",
614
+ {
615
+ class: "TRAFFIC_ENTRY " + dir_class(e.dir),
616
+ title: e.title
617
+ },
618
+ children
619
+ ]);
620
+ }
621
+ function full_show(key) {
622
+ return !!dev_num(key, key === "dev_full_data" ? 1 : 0);
623
+ }
624
+ function full_sections(payload) {
625
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return payload;
626
+ let show_schema = full_show("dev_full_schema");
627
+ let show_data = full_show("dev_full_data");
628
+ let show_meta = full_show("dev_full_meta");
629
+ let out = {};
630
+ for (let k of Object.keys(payload)) {
631
+ if (k === "schema") {
632
+ if (show_schema) out[k] = payload[k];
633
+ continue;
634
+ }
635
+ if (k === "data") {
636
+ if (show_data) out[k] = payload[k];
637
+ continue;
638
+ }
639
+ if (/^__.*__$/.test(k)) {
640
+ if (show_meta) out[k] = payload[k];
641
+ continue;
642
+ }
643
+ out[k] = payload[k];
644
+ }
645
+ return out;
646
+ }
647
+ function render_full(e) {
648
+ let head = event_spans(e);
649
+ head.push(mute_button(e.sig));
650
+ head.push([
651
+ "span",
652
+ { class: "TRAFFIC_META" },
653
+ `${traffic_size(e.size)} · ${e.ts}`
654
+ ]);
655
+ let payload = full_sections(e.kw ? e.kw : e.jn);
656
+ let text;
657
+ try {
658
+ text = JSON.stringify(payload, null, 2);
659
+ } catch (err) {
660
+ text = String(payload);
661
+ }
662
+ let children = [[
663
+ "div",
664
+ { class: "TRAFFIC_HEADER" },
665
+ head
666
+ ], [
667
+ "pre",
668
+ { class: "TRAFFIC_FULL" },
669
+ text
670
+ ]];
671
+ return (0, _yuneta_gobj_js.createElement2)([
672
+ "div",
673
+ {
674
+ class: "TRAFFIC_ENTRY " + dir_class(e.dir),
675
+ title: e.title
676
+ },
677
+ children
678
+ ]);
679
+ }
680
+ function render_compact(e) {
681
+ let kids = event_spans(e);
682
+ kids.push([
683
+ "span",
684
+ { class: "TRAFFIC_SUMMARY" },
685
+ compact_summary(e.kw)
686
+ ]);
687
+ kids.push(mute_button(e.sig));
688
+ kids.push([
689
+ "span",
690
+ { class: "TRAFFIC_META" },
691
+ `${traffic_size(e.size)} · ${e.ts}`
692
+ ]);
693
+ return (0, _yuneta_gobj_js.createElement2)([
694
+ "div",
695
+ {
696
+ class: "TRAFFIC_LINE " + dir_class(e.dir),
697
+ title: e.title
698
+ },
699
+ kids
700
+ ]);
701
+ }
702
+ function render_name(e) {
703
+ let kids = event_spans(e);
704
+ kids.push(mute_button(e.sig));
705
+ kids.push([
706
+ "span",
707
+ { class: "TRAFFIC_META" },
708
+ e.ts
709
+ ]);
710
+ return (0, _yuneta_gobj_js.createElement2)([
711
+ "div",
712
+ {
713
+ class: "TRAFFIC_NAME " + dir_class(e.dir),
714
+ title: e.title
715
+ },
716
+ kids
717
+ ]);
718
+ }
719
+ function render_log(e) {
720
+ return (0, _yuneta_gobj_js.createElement2)([
721
+ "div",
722
+ {
723
+ class: "YDEV_LOGROW YDEV_LOG_" + e.level,
724
+ title: e.level
725
+ },
726
+ [
727
+ [
728
+ "span",
729
+ { class: "YDEV_LOG_LVL" },
730
+ e.level
731
+ ],
732
+ [
733
+ "span",
734
+ { class: "YDEV_LOG_TXT" },
735
+ e.text
736
+ ],
737
+ [
738
+ "span",
739
+ { class: "TRAFFIC_META" },
740
+ e.ts
741
+ ]
742
+ ]
743
+ ]);
744
+ }
745
+ function render_entry(e) {
746
+ if (e.kind === "log") return render_log(e);
747
+ let view = dev_view();
748
+ if (view === "full") return render_full(e);
749
+ if (view === "compact") return render_compact(e);
750
+ if (view === "name") return render_name(e);
751
+ return render_detailed(e);
752
+ }
753
+ /******************************
754
+ * Log painting
755
+ ******************************/
756
+ function clear_traffic() {
757
+ TRAFFIC_LOG.length = 0;
758
+ TRAFFIC_COUNTS.clear();
759
+ let logger = document.getElementById("developer-traffic-logger");
760
+ if (logger) logger.replaceChildren();
761
+ update_stats();
762
+ }
763
+ function rerender_all() {
764
+ let logger = document.getElementById("developer-traffic-logger");
765
+ if (!logger) return;
766
+ ensure_dev_style();
767
+ let ctx = build_filter_ctx();
768
+ let frag = document.createDocumentFragment();
769
+ let shown = 0;
770
+ for (let e of TRAFFIC_LOG) {
771
+ e.$node = null;
772
+ if (!entry_hidden(e, ctx)) {
773
+ let node = render_entry(e);
774
+ e.$node = node;
775
+ frag.appendChild(node);
776
+ shown++;
777
+ }
778
+ }
779
+ logger.replaceChildren(frag);
780
+ if (shown === 0) {
781
+ let $empty = document.createElement("div");
782
+ $empty.className = "YDEV_EMPTY";
783
+ $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.";
784
+ logger.appendChild($empty);
785
+ }
786
+ logger.scrollTop = logger.scrollHeight;
787
+ update_stats();
788
+ }
789
+ function update_stats() {
790
+ let $s = document.getElementById("ydev-stats");
791
+ if (!$s) return;
792
+ let ctx = build_filter_ctx();
793
+ let total = TRAFFIC_LOG.length;
794
+ let shown = 0, out = 0, inc = 0, err = 0, hidden = 0, bytes = 0;
795
+ for (let e of TRAFFIC_LOG) {
796
+ bytes += e.size || 0;
797
+ if (e.dir === 1) out++;
798
+ else if (e.dir === 2) inc++;
799
+ else if (e.dir === 3) err++;
800
+ if (entry_hidden(e, ctx)) hidden++;
801
+ else shown++;
802
+ }
803
+ $s.replaceChildren();
804
+ [
805
+ [`${shown}/${total} shown`, ""],
806
+ [`⇢ ${out}`, "s-out"],
807
+ [`⇠ ${inc}`, "s-in"],
808
+ [`⚠ ${err}`, "s-err"],
809
+ [`⊘ ${hidden} hidden`, ""],
810
+ [`${traffic_size(bytes)}`, ""]
811
+ ].forEach(([text, cls]) => {
812
+ let d = document.createElement("span");
813
+ d.className = "YDEV_STAT" + (cls ? " " + cls : "");
814
+ d.textContent = text;
815
+ $s.appendChild(d);
816
+ });
817
+ }
55
818
  /************************************************************
819
+ * Append one inter-event message. Kept in a bounded buffer so
820
+ * view/filter switches repaint from memory. Shared by the legacy
821
+ * C_YUI_WINDOW (setup_dev) and the modal (build_dev_panel).
56
822
  *
823
+ * direction: 1 outgoing (⇢), 2 incoming (⇠), 3 error (⚠).
824
+ * With no logger mounted, fall back to a console dump.
57
825
  ************************************************************/
58
826
  function info_traffic(title, msg, direction, size) {
59
- if (!document.getElementById("developer-traffic-logger")) {
827
+ let logger = document.getElementById("developer-traffic-logger");
828
+ if (!logger) {
60
829
  (0, _yuneta_gobj_js.trace_json)(msg);
61
830
  return;
62
831
  }
63
832
  if (!size) size = 0;
64
- let jn_msg;
833
+ let jn;
65
834
  try {
66
- if ((0, _yuneta_gobj_js.is_string)(msg)) jn_msg = JSON.parse(msg);
67
- else jn_msg = JSON.parse(JSON.stringify(msg));
835
+ jn = (0, _yuneta_gobj_js.is_string)(msg) ? JSON.parse(msg) : JSON.parse(JSON.stringify(msg));
68
836
  } catch (e) {
69
837
  return;
70
838
  }
71
- let content = {
72
- text: void 0,
73
- json: jn_msg
839
+ ensure_dev_style();
840
+ let event = jn && jn.event ? String(jn.event) : "(no event)";
841
+ let kw = jn && jn.kw && typeof jn.kw === "object" ? jn.kw : null;
842
+ let command = kw && typeof kw.command === "string" ? kw.command : "";
843
+ let sig = traffic_signature(event, command);
844
+ let hay = "";
845
+ try {
846
+ hay = (event + " " + command + " " + (kw ? JSON.stringify(kw) : "")).toLowerCase();
847
+ } catch (e) {
848
+ hay = (event + " " + command).toLowerCase();
849
+ }
850
+ let entry = {
851
+ title: title || "",
852
+ event,
853
+ command,
854
+ sig,
855
+ dir: direction,
856
+ size,
857
+ ts: traffic_now(),
858
+ kw,
859
+ jn,
860
+ hay,
861
+ $node: null
74
862
  };
75
- function formatCurrentTime() {
76
- let now = /* @__PURE__ */ new Date();
77
- let pad = (num, size) => ("000" + num).slice(size * -1);
78
- return `${pad(now.getHours(), 2)}:${pad(now.getMinutes(), 2)}:${pad(now.getSeconds(), 2)} .${pad(now.getMilliseconds(), 4)}`;
79
- }
80
- let element = document.getElementById("developer-traffic-logger");
81
- if (element) {
82
- let style = "background-color:#3883FA;";
83
- if (direction === 2) style += "color:yellow;";
84
- else if (direction === 3) style += "color:red;";
85
- else style += "color:white;";
86
- let $item = (0, _yuneta_gobj_js.createElement2)([
87
- "div",
88
- { class: "mt-4" },
89
- [[
90
- "div",
91
- {
92
- class: "is-flex with-border is-justify-content-space-between",
93
- style
94
- },
95
- [
96
- [
97
- "div",
98
- { class: "p-1" },
99
- title
100
- ],
101
- [
102
- "div",
103
- { class: "p-1" },
104
- `(${size} bytes)`
105
- ],
106
- [
107
- "div",
108
- { class: "p-1" },
109
- formatCurrentTime()
110
- ]
111
- ]
112
- ], [
113
- "div",
114
- { class: "x-jsoneditor jse-theme-dark" },
115
- []
116
- ]]
117
- ]);
118
- let $target = $item.querySelector(".x-jsoneditor");
119
- let font_family = "DejaVu Sans Mono, monospace, consolas, monaco";
120
- $target.style.setProperty("--jse-font-size-mono", "15px");
121
- $target.style.setProperty("--jse-font-family-mono", font_family);
122
- document.getElementById("developer-traffic-logger").appendChild($item);
123
- new vanilla_jsoneditor.JSONEditor({
124
- target: $target,
125
- props: {
126
- content,
127
- readOnly: true,
128
- timestampTag: function({ field, value, path }) {
129
- 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;
130
- return false;
131
- },
132
- timestampFormat: function({ field, value, path }) {
133
- 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();
134
- return null;
135
- }
136
- }
137
- }).expand((path) => path.length < 2);
138
- element.scrollIntoView({ block: "end" });
863
+ TRAFFIC_LOG.push(entry);
864
+ TRAFFIC_COUNTS.set(sig, (TRAFFIC_COUNTS.get(sig) || 0) + 1);
865
+ let crossed = dev_hide_periodic() && TRAFFIC_COUNTS.get(sig) === PERIODIC_THRESHOLD;
866
+ if (TRAFFIC_LOG.length > TRAFFIC_MAX) {
867
+ let old = TRAFFIC_LOG.shift();
868
+ let c = (TRAFFIC_COUNTS.get(old.sig) || 0) - 1;
869
+ if (c <= 0) TRAFFIC_COUNTS.delete(old.sig);
870
+ else TRAFFIC_COUNTS.set(old.sig, c);
871
+ if (old.$node && old.$node.parentNode) old.$node.parentNode.removeChild(old.$node);
872
+ }
873
+ if (crossed) rerender_all();
874
+ else if (!entry_hidden(entry, build_filter_ctx())) {
875
+ let node = render_entry(entry);
876
+ entry.$node = node;
877
+ let ph = logger.querySelector(".YDEV_EMPTY");
878
+ if (ph) ph.remove();
879
+ logger.appendChild(node);
880
+ node.scrollIntoView({ block: "end" });
881
+ }
882
+ update_stats();
883
+ }
884
+ var __in_info_log__ = false;
885
+ /************************************************************
886
+ * Mirror one framework log line into the monitor, alongside the
887
+ * inter-event traffic. level ∈ error|warning|info|debug|msg — the automata
888
+ * (FSM) trace arrives here too, as `debug`. No-op while the window is closed
889
+ * (the line already went to the browser console).
890
+ ************************************************************/
891
+ function info_log(level, msg, hora) {
892
+ if (__in_info_log__) return;
893
+ let logger = document.getElementById("developer-traffic-logger");
894
+ if (!logger) return;
895
+ __in_info_log__ = true;
896
+ try {
897
+ ensure_dev_style();
898
+ let lvl = level || "debug";
899
+ let text;
900
+ if (lvl === "json") {
901
+ try {
902
+ text = JSON.stringify(msg, null, 2);
903
+ } catch (e) {
904
+ text = String(msg);
905
+ }
906
+ if (text.length > 4e3) text = text.slice(0, 4e3) + "\n…(truncated)";
907
+ } else text = (0, _yuneta_gobj_js.is_string)(msg) ? msg : String(msg);
908
+ let entry = {
909
+ kind: "log",
910
+ level: lvl,
911
+ text,
912
+ dir: 0,
913
+ size: 0,
914
+ ts: traffic_now(),
915
+ sig: "log:" + lvl,
916
+ hay: (lvl + " " + text).toLowerCase(),
917
+ $node: null
918
+ };
919
+ TRAFFIC_LOG.push(entry);
920
+ if (TRAFFIC_LOG.length > TRAFFIC_MAX) {
921
+ let old = TRAFFIC_LOG.shift();
922
+ if (old.kind !== "log") {
923
+ let c = (TRAFFIC_COUNTS.get(old.sig) || 0) - 1;
924
+ if (c <= 0) TRAFFIC_COUNTS.delete(old.sig);
925
+ else TRAFFIC_COUNTS.set(old.sig, c);
926
+ }
927
+ if (old.$node && old.$node.parentNode) old.$node.parentNode.removeChild(old.$node);
928
+ }
929
+ if (!entry_hidden(entry, build_filter_ctx())) {
930
+ let node = render_entry(entry);
931
+ entry.$node = node;
932
+ let ph = logger.querySelector(".YDEV_EMPTY");
933
+ if (ph) ph.remove();
934
+ logger.appendChild(node);
935
+ node.scrollIntoView({ block: "end" });
936
+ }
937
+ update_stats();
938
+ } finally {
939
+ __in_info_log__ = false;
139
940
  }
140
941
  }
141
- /************************************************************
142
- *
143
- ************************************************************/
942
+ /******************************
943
+ * Trace toggles
944
+ ******************************/
144
945
  function trace_traffic() {
145
- let v = (0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_traffic");
146
- v = Number(v);
946
+ let v = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_traffic"));
147
947
  if (v) {
148
948
  (0, _yuneta_gobj_js.gobj_write_attr)((0, _yuneta_gobj_js.gobj_yuno)(), "trace_inter_event", false);
149
949
  v = 0;
@@ -153,108 +953,396 @@ function trace_traffic() {
153
953
  v = 1;
154
954
  }
155
955
  (0, _yuneta_gobj_js.kw_set_local_storage_value)("trace_traffic", v);
156
- info_user();
956
+ refresh_dev_chrome();
157
957
  }
158
- /************************************************************
159
- *
160
- ************************************************************/
161
958
  function trace_automata() {
162
- let v = (0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_automata");
163
- v = Number(v);
959
+ let v = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_automata"));
164
960
  if (v === 0) v = 1;
165
961
  else if (v === 1) v = 2;
166
962
  else v = 0;
167
963
  (0, _yuneta_gobj_js.gobj_write_attr)((0, _yuneta_gobj_js.gobj_yuno)(), "tracing", v);
168
964
  (0, _yuneta_gobj_js.kw_set_local_storage_value)("trace_automata", v);
169
- info_user();
965
+ refresh_dev_chrome();
170
966
  }
171
- /************************************************************
172
- *
173
- ************************************************************/
174
967
  function trace_creation() {
175
- let v = (0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_creation");
176
- v = Number(v);
177
- if (v === 0) v = 1;
178
- else v = 0;
968
+ let v = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_creation"));
969
+ v = v === 0 ? 1 : 0;
179
970
  (0, _yuneta_gobj_js.gobj_write_attr)((0, _yuneta_gobj_js.gobj_yuno)(), "trace_creation", v);
180
971
  (0, _yuneta_gobj_js.kw_set_local_storage_value)("trace_creation", v);
181
- info_user();
972
+ refresh_dev_chrome();
182
973
  }
183
- /************************************************************
184
- *
185
- ************************************************************/
186
974
  function trace_start_stop() {
187
- let v = (0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_start_stop");
188
- v = Number(v);
189
- if (v === 0) v = 1;
190
- else v = 0;
975
+ let v = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_start_stop"));
976
+ v = v === 0 ? 1 : 0;
191
977
  (0, _yuneta_gobj_js.gobj_write_attr)((0, _yuneta_gobj_js.gobj_yuno)(), "trace_start_stop", v);
192
978
  (0, _yuneta_gobj_js.kw_set_local_storage_value)("trace_start_stop", v);
193
- info_user();
979
+ refresh_dev_chrome();
194
980
  }
195
- /************************************************************
196
- *
197
- ************************************************************/
198
981
  function trace_subscriptions() {
199
- let v = (0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_subscriptions");
200
- v = Number(v);
201
- if (v === 0) v = 1;
202
- else v = 0;
982
+ let v = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_subscriptions"));
983
+ v = v === 0 ? 1 : 0;
203
984
  (0, _yuneta_gobj_js.gobj_write_attr)((0, _yuneta_gobj_js.gobj_yuno)(), "trace_subscriptions", v);
204
985
  (0, _yuneta_gobj_js.kw_set_local_storage_value)("trace_subscriptions", v);
205
- info_user();
986
+ refresh_dev_chrome();
206
987
  }
207
- /************************************************************
208
- *
209
- ************************************************************/
210
988
  function trace_i18n() {
211
- let v = (0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_i18n");
212
- v = Number(v);
213
- if (v === 0) v = 1;
214
- else v = 0;
989
+ let v = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_i18n"));
990
+ v = v === 0 ? 1 : 0;
215
991
  i18next.default.options.debug = v ? true : false;
216
992
  (0, _yuneta_gobj_js.kw_set_local_storage_value)("trace_i18n", v);
217
- info_user();
993
+ refresh_dev_chrome();
218
994
  }
219
- /************************************************************
220
- *
221
- ************************************************************/
222
995
  function set_no_poll() {
223
- let v = (0, _yuneta_gobj_js.kw_get_local_storage_value)("no_poll");
224
- v = Number(v);
225
- if (v) v = 0;
226
- else v = 1;
996
+ let v = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("no_poll"));
997
+ v = v ? 0 : 1;
227
998
  (0, _yuneta_gobj_js.gobj_write_attr)((0, _yuneta_gobj_js.gobj_yuno)(), "no_poll", v);
228
999
  (0, _yuneta_gobj_js.kw_set_local_storage_value)("no_poll", v);
229
- info_user();
1000
+ refresh_dev_chrome();
230
1001
  }
231
- /************************************************************
232
- *
233
- ************************************************************/
234
- function info_user() {
235
- let $info = document.getElementById("developer-window-info");
236
- let traffic = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_traffic", 0, false));
237
- let trace = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_automata", 0, false));
238
- let creation = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_creation", 0, false));
239
- let start_stop = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_start_stop", 0, false));
240
- let subscriptions = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_subscriptions", 0, false));
241
- let i18n = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_i18n", 0, false));
242
- let no_poll = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("no_poll", 0, false));
243
- $info.replaceChildren();
244
- [
245
- `Automata: ${trace}`,
246
- `Creation: ${creation}`,
247
- `Start/Stop: ${start_stop}`,
248
- `Subscriptions: ${subscriptions}`,
249
- `I18n: ${i18n}`,
250
- `Traffic: ${traffic}`,
251
- `No poll: ${no_poll}`
252
- ].forEach((text) => {
253
- const div = document.createElement("div");
254
- div.textContent = text;
255
- $info.appendChild(div);
1002
+ /******************************
1003
+ * Chrome (controls)
1004
+ ******************************/
1005
+ function refresh_dev_chrome() {
1006
+ document.querySelectorAll(".YDEV_CHIP[data-trace]").forEach(($b) => {
1007
+ let key = $b.getAttribute("data-trace");
1008
+ let label = $b.getAttribute("data-label") || "";
1009
+ let v = dev_num(key, 0);
1010
+ $b.textContent = key === "trace_automata" && v > 0 ? label + " " + v : label;
1011
+ $b.classList.toggle("is-active", v > 0);
1012
+ });
1013
+ let view = dev_view();
1014
+ document.querySelectorAll(".YDEV_SEG_BTN[data-view]").forEach(($b) => {
1015
+ $b.classList.toggle("is-active", $b.getAttribute("data-view") === view);
1016
+ });
1017
+ let $eg = document.getElementById("ydev-expand-grp");
1018
+ if ($eg) $eg.style.display = view === "full" ? "" : "none";
1019
+ document.querySelectorAll(".YDEV_CHIP[data-expand]").forEach(($b) => {
1020
+ $b.classList.toggle("is-active", full_show($b.getAttribute("data-expand")));
1021
+ });
1022
+ document.querySelectorAll(".YDEV_CHIP[data-dir]").forEach(($b) => {
1023
+ $b.classList.toggle("is-active", !!dev_num($b.getAttribute("data-dir"), 1));
1024
+ });
1025
+ document.querySelectorAll(".YDEV_CHIP[data-toggle=\"periodic\"]").forEach(($b) => {
1026
+ $b.classList.toggle("is-active", dev_hide_periodic());
1027
+ });
1028
+ document.querySelectorAll(".YDEV_CHIP[data-toggle=\"automata-simple\"]").forEach(($b) => {
1029
+ $b.classList.toggle("is-active", !!dev_num("dev_automata_simple", 0));
1030
+ });
1031
+ let $m = document.getElementById("ydev-muted");
1032
+ if ($m) {
1033
+ $m.replaceChildren();
1034
+ let set = dev_muted();
1035
+ if (set.size) {
1036
+ let $lbl = document.createElement("span");
1037
+ $lbl.className = "YDEV_LABEL";
1038
+ $lbl.textContent = "Muted";
1039
+ $m.appendChild($lbl);
1040
+ set.forEach((sig) => {
1041
+ $m.appendChild((0, _yuneta_gobj_js.createElement2)([
1042
+ "button",
1043
+ {
1044
+ class: "YDEV_MUTED_CHIP",
1045
+ type: "button",
1046
+ title: "Unmute"
1047
+ },
1048
+ "⊘ " + sig + " ✕",
1049
+ { click: (ev) => {
1050
+ ev.stopPropagation();
1051
+ unmute_signature(sig);
1052
+ } }
1053
+ ]));
1054
+ });
1055
+ }
1056
+ }
1057
+ update_stats();
1058
+ }
1059
+ function traffic_to_text() {
1060
+ let ctx = build_filter_ctx();
1061
+ let out = [];
1062
+ for (let e of TRAFFIC_LOG) {
1063
+ if (entry_hidden(e, ctx)) continue;
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);
256
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);
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
+ (0, _yuneta_gobj_js.kw_set_local_storage_value)("dev_automata_simple", v);
1123
+ (0, _yuneta_gobj_js.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 (0, _yuneta_gobj_js.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 (0, _yuneta_gobj_js.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 (0, _yuneta_gobj_js.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
+ ]);
257
1342
  }
1343
+ /******************************
1344
+ * Public API
1345
+ ******************************/
258
1346
  /************************************************************
259
1347
  * Was the developer window open last session? setup_dev()
260
1348
  * persists open_developer_window (1 on open, 0 on close), so
@@ -268,9 +1356,6 @@ function dev_window_was_open() {
268
1356
  * Apply ALL persisted developer-trace flags to the running
269
1357
  * yuno. Independent of the dev window — call it once at app
270
1358
  * startup so a refresh keeps logging whatever was enabled.
271
- * Single source of truth for "localStorage flag → effect";
272
- * setup_dev() and build_dev_panel() reuse it instead of each
273
- * re-applying a partial subset.
274
1359
  ************************************************************/
275
1360
  function apply_dev_traces() {
276
1361
  let traffic = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_traffic", 0, false));
@@ -290,176 +1375,58 @@ function apply_dev_traces() {
290
1375
  (0, _yuneta_gobj_js.gobj_write_attr)((0, _yuneta_gobj_js.gobj_yuno)(), "trace_subscriptions", subscriptions);
291
1376
  (0, _yuneta_gobj_js.gobj_write_attr)((0, _yuneta_gobj_js.gobj_yuno)(), "no_poll", no_poll);
292
1377
  i18next.default.options.debug = i18n ? true : false;
1378
+ (0, _yuneta_gobj_js.gobj_set_trace_machine_format)(Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("dev_automata_simple", 0, false)) ? 1 : 0);
1379
+ (0, _yuneta_gobj_js.set_log_callback)(info_log);
293
1380
  }
294
1381
  /************************************************************
295
- * Open the developer panel inside a non-modal C_YUI_WINDOW
1382
+ * Open the developer monitor inside a non-modal C_YUI_WINDOW
296
1383
  * (title bar + maximize + close + resize).
297
1384
  *
298
1385
  * Shell-agnostic: the legacy C_YUI_MAIN shell has a
299
1386
  * '#top-layer' stacking element; the new C_YUI_SHELL does not.
300
- * We pass that element when present, otherwise null — and
301
- * C_YUI_WINDOW falls back to document.body by contract. So the
302
- * new shell gets the same windowed dev panel instead of the
303
- * floating build_dev_panel() box. Legacy behaviour is
304
- * 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.
305
1389
  ************************************************************/
306
1390
  function setup_dev(self, show) {
307
- let traffic = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_traffic", 0, false));
308
- let trace = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_automata", 0, false));
309
- let creation = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_creation", 0, false));
310
- let start_stop = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_start_stop", 0, false));
311
- let subscriptions = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_subscriptions", 0, false));
312
- let i18n = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_i18n", 0, false));
313
- let no_poll = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("no_poll", 0, false));
314
1391
  if (show) {
315
- const $dev_toolbar = (0, _yuneta_gobj_js.createElement2)([
316
- "div",
317
- { class: "buttons" },
318
- [
319
- [
320
- "button",
321
- { class: "button" },
322
- "Automata",
323
- { click: (evt) => {
324
- evt.stopPropagation();
325
- trace_automata();
326
- } }
327
- ],
328
- [
329
- "button",
330
- { class: "button" },
331
- "Creation",
332
- { click: (evt) => {
333
- evt.stopPropagation();
334
- trace_creation();
335
- } }
336
- ],
337
- [
338
- "button",
339
- { class: "button" },
340
- "Star/Stop",
341
- { click: (evt) => {
342
- evt.stopPropagation();
343
- trace_start_stop();
344
- } }
345
- ],
346
- [
347
- "button",
348
- { class: "button" },
349
- "Subscriptions",
350
- { click: (evt) => {
351
- evt.stopPropagation();
352
- trace_subscriptions();
353
- } }
354
- ],
355
- [
356
- "button",
357
- { class: "button" },
358
- "I18n",
359
- { click: (evt) => {
360
- evt.stopPropagation();
361
- trace_i18n();
362
- } }
363
- ],
364
- [
365
- "button",
366
- { class: "button" },
367
- "Traffic",
368
- { click: (evt) => {
369
- evt.stopPropagation();
370
- trace_traffic();
371
- } }
372
- ],
373
- [
374
- "button",
375
- { class: "button" },
376
- "No Poll",
377
- { click: (evt) => {
378
- evt.stopPropagation();
379
- set_no_poll();
380
- } }
381
- ],
382
- [
383
- "button",
384
- { class: "button" },
385
- "Clear Traffic",
386
- { click: (evt) => {
387
- evt.stopPropagation();
388
- document.getElementById("developer-traffic-logger").innerHTML = "";
389
- } }
390
- ]
391
- ]
392
- ]);
393
- let estados = `
394
- <div>Automata: ${trace}</div>
395
- <div>Creation: ${creation}</div>
396
- <div>Start/Stop: ${start_stop}</div>
397
- <div>Subscriptions: ${subscriptions}</div>
398
- <div>I18n: ${i18n}</div>
399
- <div>Traffic: ${traffic}</div>
400
- <div>No poll: ${no_poll}</div>`;
1392
+ ensure_dev_style();
401
1393
  (0, _yuneta_gobj_js.gobj_create_service)("Developer-Window", "C_YUI_WINDOW", {
402
1394
  $parent: document.getElementById("top-layer") || null,
403
1395
  subscriber: null,
404
1396
  showMax: true,
405
1397
  modal: false,
406
- header: $dev_toolbar,
1398
+ header: build_title_header(),
1399
+ body: build_dev_body(),
1400
+ showFooter: false,
407
1401
  auto_save_size_and_position: true,
408
1402
  center: false,
409
- body: "<div style=\"overflow:scroll;height:100%;\"><div id=\"developer-traffic-logger\" style=\"margin-left:10px;margin-right:10px;\"/></div>",
410
- 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: (0, _yuneta_gobj_js.gobj_find_service)("__window_manager__", false) || null,
411
1406
  on_close: function() {
412
1407
  (0, _yuneta_gobj_js.kw_set_local_storage_value)("open_developer_window", 0);
413
1408
  }
414
1409
  }, self);
415
1410
  (0, _yuneta_gobj_js.kw_set_local_storage_value)("open_developer_window", 1);
1411
+ setTimeout(() => {
1412
+ refresh_dev_chrome();
1413
+ rerender_all();
1414
+ }, 0);
416
1415
  }
417
1416
  apply_dev_traces();
418
1417
  }
419
1418
  /************************************************************
420
- * Build the developer panel as a self-contained DOM subtree,
1419
+ * Build the developer monitor as a self-contained DOM subtree,
421
1420
  * to be mounted by the new declarative shell via
422
1421
  * yui_shell_show_modal (no C_YUI_WINDOW, no 'top-layer').
423
1422
  *
424
1423
  * Returns { $el, dispose }:
425
- * - $el: the panel element (header tabs + traffic logger
426
- * body + footer counters).
1424
+ * - $el: the panel element (control bar + log + stats).
427
1425
  * - dispose: stops the inter-event traffic trace; call it from
428
1426
  * the modal's on_close.
429
- *
430
- * Backwards compatible: setup_dev() (old shell, C_YUI_WINDOW) is
431
- * untouched; the trace_* helpers and info_traffic are shared.
432
1427
  ************************************************************/
433
1428
  function build_dev_panel() {
434
- let traffic = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_traffic", 0, false));
435
- let trace = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_automata", 0, false));
436
- let creation = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_creation", 0, false));
437
- let start_stop = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_start_stop", 0, false));
438
- let subscriptions = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_subscriptions", 0, false));
439
- let i18n = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_i18n", 0, false));
440
- let no_poll = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("no_poll", 0, false));
441
- let mk_btn = (label, fn) => [
442
- "button",
443
- { class: "button is-small" },
444
- label,
445
- { click: (evt) => {
446
- evt.stopPropagation();
447
- fn();
448
- } }
449
- ];
450
- let counters = [
451
- `Automata: ${trace}`,
452
- `Creation: ${creation}`,
453
- `Start/Stop: ${start_stop}`,
454
- `Subscriptions: ${subscriptions}`,
455
- `I18n: ${i18n}`,
456
- `Traffic: ${traffic}`,
457
- `No poll: ${no_poll}`
458
- ].map((txt) => [
459
- "div",
460
- { style: "padding:0 8px;" },
461
- txt
462
- ]);
1429
+ ensure_dev_style();
463
1430
  let dark = typeof document !== "undefined" && document.documentElement.getAttribute("data-theme") === "dark";
464
1431
  let surface = dark ? "#1f2733" : "#ffffff";
465
1432
  let fg = dark ? "#e8eaed" : "#0f172a";
@@ -468,53 +1435,15 @@ function build_dev_panel() {
468
1435
  "div",
469
1436
  {
470
1437
  class: "yui-dev-panel",
471
- 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;"
472
1439
  },
473
- [
474
- [
475
- "div",
476
- {
477
- class: "buttons",
478
- style: "flex:0 0 auto;display:flex;flex-wrap:wrap;gap:6px;margin:0 0 8px 0;"
479
- },
480
- [
481
- mk_btn("Automata", trace_automata),
482
- mk_btn("Creation", trace_creation),
483
- mk_btn("Star/Stop", trace_start_stop),
484
- mk_btn("Subscriptions", trace_subscriptions),
485
- mk_btn("I18n", trace_i18n),
486
- mk_btn("Traffic", trace_traffic),
487
- mk_btn("No Poll", set_no_poll),
488
- mk_btn("Clear Traffic", () => {
489
- let l = document.getElementById("developer-traffic-logger");
490
- if (l) l.innerHTML = "";
491
- })
492
- ]
493
- ],
494
- [
495
- "div",
496
- { style: "flex:1 1 auto;min-height:0;overflow:auto;" },
497
- [[
498
- "div",
499
- {
500
- id: "developer-traffic-logger",
501
- style: "margin:0 4px;"
502
- },
503
- []
504
- ]]
505
- ],
506
- [
507
- "div",
508
- {
509
- id: "developer-window-info",
510
- class: "is-flex is-justify-content-space-between",
511
- 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;"
512
- },
513
- counters
514
- ]
515
- ]
1440
+ [build_dev_body()]
516
1441
  ]);
517
1442
  apply_dev_traces();
1443
+ setTimeout(() => {
1444
+ refresh_dev_chrome();
1445
+ rerender_all();
1446
+ }, 0);
518
1447
  let dispose = function() {
519
1448
  (0, _yuneta_gobj_js.gobj_write_attr)((0, _yuneta_gobj_js.gobj_yuno)(), "trace_inter_event", false);
520
1449
  };
@@ -524,7 +1453,7 @@ function build_dev_panel() {
524
1453
  };
525
1454
  }
526
1455
  //#endregion
527
- //#region yui_toolbar.js
1456
+ //#region src/yui_toolbar.js
528
1457
  function yui_toolbar(attrs = {}, items = []) {
529
1458
  const left_button = `
530
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>
@@ -589,7 +1518,7 @@ function yui_toolbar(attrs = {}, items = []) {
589
1518
  return $toolbar;
590
1519
  }
591
1520
  //#endregion
592
- //#region c_yui_main.js
1521
+ //#region src/c_yui_main.js
593
1522
  /***********************************************************************
594
1523
  * yui_main.js
595
1524
  *
@@ -625,11 +1554,11 @@ function yui_toolbar(attrs = {}, items = []) {
625
1554
  /***************************************************************
626
1555
  * Constants
627
1556
  ***************************************************************/
628
- var GCLASS_NAME$16 = "C_YUI_MAIN";
1557
+ var GCLASS_NAME$17 = "C_YUI_MAIN";
629
1558
  /***************************************************************
630
1559
  * Data
631
1560
  ***************************************************************/
632
- var attrs_table$16 = [
1561
+ var attrs_table$17 = [
633
1562
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of output events"),
634
1563
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "username", 0, "", "username logged"),
635
1564
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "publi_page", 0, "publi_page", "Publicity page shown when user is not logged in"),
@@ -645,16 +1574,16 @@ var attrs_table$16 = [
645
1574
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "color_user_logout", 0, "#FF4500", "OrangeRed - user logged out"),
646
1575
  (0, _yuneta_gobj_js.SDATA_END)()
647
1576
  ];
648
- var PRIVATE_DATA$16 = {};
649
- var __gclass__$16 = null;
1577
+ var PRIVATE_DATA$17 = {};
1578
+ var __gclass__$17 = null;
650
1579
  /******************************
651
1580
  * Framework Methods
652
1581
  ******************************/
653
1582
  /***************************************************************
654
1583
  * Framework Method: Create
655
1584
  ***************************************************************/
656
- function mt_create$16(gobj) {
657
- build_ui$13(gobj);
1585
+ function mt_create$17(gobj) {
1586
+ build_ui$14(gobj);
658
1587
  set_theme(gobj, (0, _yuneta_gobj_js.kw_get_local_storage_value)("theme", "light", true));
659
1588
  const subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
660
1589
  if (subscriber) (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
@@ -672,19 +1601,19 @@ function mt_stop$16(gobj) {}
672
1601
  /***************************************************************
673
1602
  * Framework Method: Destroy
674
1603
  ***************************************************************/
675
- function mt_destroy$16(gobj) {}
1604
+ function mt_destroy$17(gobj) {}
676
1605
  /***************************
677
1606
  * Local Methods
678
1607
  ***************************/
679
1608
  /************************************************************
680
1609
  * Build UI
681
1610
  ************************************************************/
682
- function build_ui$13(gobj) {
683
- const $layout = (0, _yuneta_gobj_js.createElement2)([
1611
+ function build_ui$14(gobj) {
1612
+ const $container = (0, _yuneta_gobj_js.createElement2)([
684
1613
  "div",
685
1614
  {
686
1615
  id: "root",
687
- class: "root"
1616
+ class: "C_YUI_MAIN root"
688
1617
  },
689
1618
  [
690
1619
  ["div", {
@@ -717,7 +1646,7 @@ function build_ui$13(gobj) {
717
1646
  ]
718
1647
  ]
719
1648
  ]);
720
- document.body.appendChild($layout);
1649
+ document.body.appendChild($container);
721
1650
  const iframe = document.getElementById("iframe-publi-page");
722
1651
  if (iframe) iframe.onload = () => {
723
1652
  const theme = (0, _yuneta_gobj_js.kw_get_local_storage_value)("theme", "light", true);
@@ -1631,17 +2560,17 @@ function ac_timeout$1(gobj, event, kw, src) {
1631
2560
  /***************************
1632
2561
  * FSM
1633
2562
  ***************************/
1634
- var gmt$16 = {
1635
- mt_create: mt_create$16,
2563
+ var gmt$17 = {
2564
+ mt_create: mt_create$17,
1636
2565
  mt_start: mt_start$16,
1637
2566
  mt_stop: mt_stop$16,
1638
- mt_destroy: mt_destroy$16
2567
+ mt_destroy: mt_destroy$17
1639
2568
  };
1640
2569
  /***************************************************************
1641
2570
  * Create the GClass
1642
2571
  ***************************************************************/
1643
- function create_gclass$16(gclass_name) {
1644
- if (__gclass__$16) {
2572
+ function create_gclass$17(gclass_name) {
2573
+ if (__gclass__$17) {
1645
2574
  (0, _yuneta_gobj_js.log_error)(`GClass ALREADY created: ${gclass_name}`);
1646
2575
  return -1;
1647
2576
  }
@@ -1725,7 +2654,7 @@ function create_gclass$16(gclass_name) {
1725
2654
  null
1726
2655
  ]
1727
2656
  ]]];
1728
- __gclass__$16 = (0, _yuneta_gobj_js.gclass_create)(gclass_name, [
2657
+ __gclass__$17 = (0, _yuneta_gobj_js.gclass_create)(gclass_name, [
1729
2658
  ["EV_LOGIN_ACCEPTED", 0],
1730
2659
  ["EV_LOGIN_DENIED", 0],
1731
2660
  ["EV_LOGIN_REFRESHED", 0],
@@ -1743,18 +2672,18 @@ function create_gclass$16(gclass_name) {
1743
2672
  ["EV_ON_CLOSE", 0],
1744
2673
  ["EV_DO_RECOVERY_PASSWORD", 0],
1745
2674
  ["EV_TIMEOUT", 0]
1746
- ], states, gmt$16, 0, attrs_table$16, PRIVATE_DATA$16, 0, 0, 0, 0);
1747
- 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;
1748
2677
  return 0;
1749
2678
  }
1750
2679
  /***************************************************************
1751
2680
  * Register GClass
1752
2681
  ***************************************************************/
1753
2682
  function register_c_yui_main() {
1754
- return create_gclass$16(GCLASS_NAME$16);
2683
+ return create_gclass$17(GCLASS_NAME$17);
1755
2684
  }
1756
2685
  //#endregion
1757
- //#region c_yui_window.js
2686
+ //#region src/c_yui_window.js
1758
2687
  /***********************************************************************
1759
2688
  * c_yui_window.js
1760
2689
  *
@@ -1766,11 +2695,16 @@ function register_c_yui_main() {
1766
2695
  /***************************************************************
1767
2696
  * Constants
1768
2697
  ***************************************************************/
1769
- 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;
1770
2704
  /***************************************************************
1771
2705
  * Data
1772
2706
  ***************************************************************/
1773
- var attrs_table$15 = [
2707
+ var attrs_table$16 = [
1774
2708
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of output events"),
1775
2709
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "$parent", 0, null, "$container will be appended to $parent if not null, else to document.body"),
1776
2710
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_INTEGER, "x", 0, "300", "X position"),
@@ -1788,9 +2722,13 @@ var attrs_table$15 = [
1788
2722
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "showFooter", 0, true, "Show footer"),
1789
2723
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "openMaximized", 0, false, "Open the window maximized"),
1790
2724
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "showMax", 0, true, "Show maximize button"),
2725
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "showMin", 0, true, "Show minimize (roll-up / shade) button"),
1791
2726
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "maximized", 0, false, "Flag to indicate if maximized"),
1792
2727
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "window_style", 0, "{}", "Override window style"),
1793
2728
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "on_close", 0, null, "Callback on destroy"),
2729
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "manager", 0, null, "Optional C_YUI_WINDOW_MANAGER (gobj or service name) for dock/taskbar"),
2730
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "title", 0, "", "Window title, shown on the dock chip"),
2731
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "icon", 0, "", "Dock-chip icon: a yi-* class name or inline SVG (by window type)"),
1794
2732
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "focus", 0, null, "Brings focus to the element, can be a number or selector"),
1795
2733
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "modal", 0, false, "Enable modal mode"),
1796
2734
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "keyboard", 0, true, "Close window on ESC if not modal"),
@@ -1799,19 +2737,35 @@ var attrs_table$15 = [
1799
2737
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "win_resize_handler", 0, null, "Internal: native window 'resize' listener"),
1800
2738
  (0, _yuneta_gobj_js.SDATA_END)()
1801
2739
  ];
1802
- var PRIVATE_DATA$15 = { prevSize: 0 };
1803
- var __gclass__$15 = null;
2740
+ var PRIVATE_DATA$16 = {
2741
+ prevSize: 0,
2742
+ preShadeHeight: 0
2743
+ };
2744
+ var __gclass__$16 = null;
1804
2745
  /******************************
1805
2746
  * Framework Methods
1806
2747
  ******************************/
1807
2748
  /***************************************************************
1808
2749
  * Framework Method: Create
1809
2750
  ***************************************************************/
1810
- function mt_create$15(gobj) {
2751
+ function mt_create$16(gobj) {
1811
2752
  const subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
1812
2753
  if (subscriber) (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
1813
2754
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "window_id", "window-" + (0, _yuneta_gobj_js.clean_name)((0, _yuneta_gobj_js.gobj_short_name)(gobj)));
1814
- build_ui$12(gobj);
2755
+ build_ui$13(gobj);
2756
+ let manager = resolve_manager(gobj);
2757
+ if (manager) {
2758
+ let $c = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
2759
+ if ($c) $c.addEventListener("pointerdown", function() {
2760
+ let m = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "manager");
2761
+ if (m) (0, _yuneta_gobj_js.gobj_send_event)(m, "EV_FOCUS_WINDOW", { window: gobj }, gobj);
2762
+ }, true);
2763
+ (0, _yuneta_gobj_js.gobj_send_event)(manager, "EV_REGISTER_WINDOW", {
2764
+ window: gobj,
2765
+ title: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "title") || (0, _yuneta_gobj_js.gobj_short_name)(gobj),
2766
+ icon: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "icon") || ""
2767
+ }, gobj);
2768
+ }
1815
2769
  let on_win_resize = function() {
1816
2770
  handleResize(gobj);
1817
2771
  };
@@ -1835,7 +2789,9 @@ function mt_stop$15(gobj) {
1835
2789
  /***************************************************************
1836
2790
  * Framework Method: Destroy
1837
2791
  ***************************************************************/
1838
- function mt_destroy$15(gobj) {
2792
+ function mt_destroy$16(gobj) {
2793
+ let manager = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "manager");
2794
+ if (manager) (0, _yuneta_gobj_js.gobj_send_event)(manager, "EV_UNREGISTER_WINDOW", { window: gobj }, gobj);
1839
2795
  let on_win_resize = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "win_resize_handler");
1840
2796
  if (on_win_resize) {
1841
2797
  window.removeEventListener("resize", on_win_resize);
@@ -1847,9 +2803,97 @@ function mt_destroy$15(gobj) {
1847
2803
  * Local Methods
1848
2804
  ***************************/
1849
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 = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "manager");
2858
+ if (!m) return null;
2859
+ if (typeof m === "string") {
2860
+ m = (0, _yuneta_gobj_js.gobj_find_service)(m, false);
2861
+ (0, _yuneta_gobj_js.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 = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "manager");
2871
+ if (manager) (0, _yuneta_gobj_js.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 = (0, _yuneta_gobj_js.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
+ /************************************************************
1850
2892
  * Build UI
1851
2893
  ************************************************************/
1852
- function build_ui$12(gobj) {
2894
+ function build_ui$13(gobj) {
2895
+ ensure_window_style();
2896
+ let mobile = is_mobile();
1853
2897
  let header = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "header");
1854
2898
  if ((0, _yuneta_gobj_js.is_gobj)(header)) header = (0, _yuneta_gobj_js.gobj_read_attr)(header, "$container");
1855
2899
  let body = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "body");
@@ -1872,6 +2916,12 @@ function build_ui$12(gobj) {
1872
2916
  }
1873
2917
  let rect = do_fix_dimension_to_screen(gobj, (0, _yuneta_gobj_js.gobj_read_integer_attr)(gobj, "x"), (0, _yuneta_gobj_js.gobj_read_integer_attr)(gobj, "y"), (0, _yuneta_gobj_js.gobj_read_integer_attr)(gobj, "width"), (0, _yuneta_gobj_js.gobj_read_integer_attr)(gobj, "height"));
1874
2918
  if ((0, _yuneta_gobj_js.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
+ };
1875
2925
  let window_style = {
1876
2926
  position: "fixed",
1877
2927
  "z-index": 3,
@@ -1893,14 +2943,14 @@ function build_ui$12(gobj) {
1893
2943
  let $container = (0, _yuneta_gobj_js.createElement2)([
1894
2944
  "div",
1895
2945
  {
1896
- 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" : ""),
1897
2947
  style: window_style
1898
2948
  },
1899
2949
  [
1900
2950
  [
1901
2951
  "div",
1902
2952
  {
1903
- 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",
1904
2954
  style: "border-bottom:1px solid var(--bulma-border); cursor:move; box-sizing: border-box;"
1905
2955
  },
1906
2956
  [[
@@ -1920,37 +2970,52 @@ function build_ui$12(gobj) {
1920
2970
  ], [
1921
2971
  "div",
1922
2972
  {
1923
- 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",
1924
2974
  style: "position:relative; z-index:1; cursor:default;"
1925
2975
  },
1926
- [[
1927
- "button",
1928
- {
1929
- class: "without-border pr-4",
1930
- style: {
1931
- color: "var(--bulma-text)",
1932
- "font-size": "1.4em",
1933
- "cursor": "pointer",
1934
- "display": (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "showMax") ? "inline-block" : "none"
1935
- }
1936
- },
1937
- "<i class=\"yi-square\"></i>",
1938
- { click: (evt) => {
1939
- evt.stopPropagation();
1940
- toggle(gobj);
1941
- } }
1942
- ], [
1943
- "button",
1944
- {
1945
- class: "without-border pr-2",
1946
- style: "color:var(--bulma-text);font-size:1.6em;cursor:pointer;"
1947
- },
1948
- "<i class=\"yi-xmark\"></i>",
1949
- { click: (evt) => {
1950
- evt.stopPropagation();
1951
- close_window(gobj);
1952
- } }
1953
- ]]
2976
+ [
2977
+ [
2978
+ "button",
2979
+ {
2980
+ class: "yui-wc wc-min",
2981
+ type: "button",
2982
+ "aria-label": "minimize",
2983
+ style: (0, _yuneta_gobj_js.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: (0, _yuneta_gobj_js.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
+ ]
1954
3019
  ]],
1955
3020
  { pointerdown: (evt) => {
1956
3021
  if (evt.target.closest && evt.target.closest("button")) return;
@@ -2035,7 +3100,7 @@ function build_ui$12(gobj) {
2035
3100
  let $resizable_btn = (0, _yuneta_gobj_js.createElement2)([
2036
3101
  "div",
2037
3102
  {
2038
- class: "without-border",
3103
+ class: "without-border yui-window-resize",
2039
3104
  style: {
2040
3105
  cursor: "nwse-resize",
2041
3106
  display: "flex",
@@ -2062,7 +3127,7 @@ function build_ui$12(gobj) {
2062
3127
  if ($parent) $parent.appendChild($container);
2063
3128
  else document.body.appendChild($container);
2064
3129
  if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "openMaximized")) max$2(gobj);
2065
- else {
3130
+ else if (!mobile) {
2066
3131
  if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "content_size")) $container.style.height = "auto";
2067
3132
  if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "center")) {
2068
3133
  rect = $container.getBoundingClientRect();
@@ -2123,9 +3188,22 @@ function max$2(gobj) {
2123
3188
  $container.style.width = parseInt(rect.width) + "px";
2124
3189
  $container.style.height = parseInt(rect.height) + "px";
2125
3190
  (0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "maximized", true);
3191
+ set_max_icon(gobj, true);
2126
3192
  return rect;
2127
3193
  }
2128
3194
  /************************************************************
3195
+ * Swap the maximize/restore glyph to match the state.
3196
+ ************************************************************/
3197
+ function set_max_icon(gobj, maximized) {
3198
+ let $container = (0, _yuneta_gobj_js.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
+ /************************************************************
2129
3207
  *
2130
3208
  ************************************************************/
2131
3209
  function min$1(gobj) {
@@ -2138,12 +3216,14 @@ function min$1(gobj) {
2138
3216
  $container.style.width = parseInt(rect.width) + "px";
2139
3217
  $container.style.height = parseInt(rect.height) + "px";
2140
3218
  (0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "maximized", false);
3219
+ set_max_icon(gobj, false);
2141
3220
  return rect;
2142
3221
  }
2143
3222
  /************************************************************
2144
3223
  * handlers moving
2145
3224
  ************************************************************/
2146
3225
  function mvStart(gobj, evt) {
3226
+ if (is_mobile()) return;
2147
3227
  let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
2148
3228
  let window_rect = $container.getBoundingClientRect();
2149
3229
  let x = evt.screenX;
@@ -2300,6 +3380,17 @@ function do_center(gobj, x, y, width, height) {
2300
3380
  function handleResize(gobj) {
2301
3381
  let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
2302
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");
2303
3394
  if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "maximized") === true) {
2304
3395
  let r = do_fix_dimension_to_screen(gobj, 0, 0, 1e4, 1e4);
2305
3396
  if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "center")) r = do_center(gobj, r.x, r.y, r.width, r.height);
@@ -2344,20 +3435,28 @@ function ac_show$11(gobj, event, kw, src) {
2344
3435
  function ac_hide$11(gobj, event, kw, src) {
2345
3436
  return 0;
2346
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
+ }
2347
3446
  /***************************
2348
3447
  * FSM
2349
3448
  ***************************/
2350
- var gmt$15 = {
2351
- mt_create: mt_create$15,
3449
+ var gmt$16 = {
3450
+ mt_create: mt_create$16,
2352
3451
  mt_start: mt_start$15,
2353
3452
  mt_stop: mt_stop$15,
2354
- mt_destroy: mt_destroy$15
3453
+ mt_destroy: mt_destroy$16
2355
3454
  };
2356
3455
  /***************************************************************
2357
3456
  * Create the GClass
2358
3457
  ***************************************************************/
2359
- function create_gclass$15(gclass_name) {
2360
- if (__gclass__$15) {
3458
+ function create_gclass$16(gclass_name) {
3459
+ if (__gclass__$16) {
2361
3460
  (0, _yuneta_gobj_js.log_error)(`GClass ALREADY created: ${gclass_name}`);
2362
3461
  return -1;
2363
3462
  }
@@ -2381,28 +3480,481 @@ function create_gclass$15(gclass_name) {
2381
3480
  "EV_HIDE",
2382
3481
  ac_hide$11,
2383
3482
  null
3483
+ ],
3484
+ [
3485
+ "EV_CLOSE_WINDOW",
3486
+ ac_close,
3487
+ null
2384
3488
  ]
2385
3489
  ]]];
2386
- __gclass__$15 = (0, _yuneta_gobj_js.gclass_create)(gclass_name, [
3490
+ __gclass__$16 = (0, _yuneta_gobj_js.gclass_create)(gclass_name, [
2387
3491
  ["EV_WINDOW_TO_CLOSE", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
2388
3492
  ["EV_WINDOW_MOVED", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
2389
3493
  ["EV_WINDOW_RESIZED", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
2390
3494
  ["EV_RESIZE", 0],
2391
3495
  ["EV_REFRESH", 0],
2392
3496
  ["EV_SHOW", 0],
2393
- ["EV_HIDE", 0]
2394
- ], states, gmt$15, 0, attrs_table$15, PRIVATE_DATA$15, 0, 0, 0, 0);
2395
- 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;
2396
3501
  return 0;
2397
3502
  }
2398
3503
  /***************************************************************
2399
3504
  * Register GClass
2400
3505
  ***************************************************************/
2401
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
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of output events"),
3553
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "dock_mode", 0, "floating", "floating | inline | responsive (floating on wide screens, inline on narrow)"),
3554
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "dock_corner", 0, "bottom-left", "Floating placement: top-left | top-right | bottom-left | bottom-right"),
3555
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.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
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "responsive_query", 0, "(max-width: 768px)", "Media query that selects inline placement when dock_mode is 'responsive'"),
3557
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "$parent", 0, null, "Where the floating dock mounts (default document.body)"),
3558
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "$container", 0, null, "Internal: the dock element"),
3559
+ (0, _yuneta_gobj_js.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 (((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "dock_mode") || "floating") === "responsive") {
3579
+ let q = (0, _yuneta_gobj_js.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 = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
3588
+ if (subscriber) (0, _yuneta_gobj_js.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
+ (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", (0, _yuneta_gobj_js.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 = (0, _yuneta_gobj_js.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 = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
3708
+ if (!$dock) return;
3709
+ let $inline = null;
3710
+ if (want_inline_placement(gobj)) {
3711
+ let sel = (0, _yuneta_gobj_js.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 = (0, _yuneta_gobj_js.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 : (0, _yuneta_gobj_js.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 = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
3732
+ if ($dock) {
3733
+ if ($dock.parentNode) $dock.parentNode.removeChild($dock);
3734
+ (0, _yuneta_gobj_js.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 = (0, _yuneta_gobj_js.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 = (0, _yuneta_gobj_js.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 = (0, _yuneta_gobj_js.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 || (0, _yuneta_gobj_js.gobj_name)(win) || "window";
3830
+ let entry = {
3831
+ gobj: win,
3832
+ $chip: null,
3833
+ minimized: false
3834
+ };
3835
+ let $chip = (0, _yuneta_gobj_js.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
+ (0, _yuneta_gobj_js.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 = (0, _yuneta_gobj_js.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
+ (0, _yuneta_gobj_js.log_error)(`GClass ALREADY created: ${gclass_name}`);
3918
+ return -1;
3919
+ }
3920
+ __gclass__$15 = (0, _yuneta_gobj_js.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() {
2402
3954
  return create_gclass$15(GCLASS_NAME$15);
2403
3955
  }
2404
3956
  //#endregion
2405
- //#region c_yui_tabs.js
3957
+ //#region src/c_yui_tabs.js
2406
3958
  /***********************************************************************
2407
3959
  * c_yui_tabs.js
2408
3960
  *
@@ -2456,7 +4008,7 @@ var __gclass__$14 = null;
2456
4008
  * Framework Method: Create
2457
4009
  ***************************************************************/
2458
4010
  function mt_create$14(gobj) {
2459
- build_ui$11(gobj);
4011
+ build_ui$12(gobj);
2460
4012
  let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
2461
4013
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
2462
4014
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
@@ -2498,11 +4050,11 @@ function mt_child_removed(gobj, child) {
2498
4050
  /************************************************************
2499
4051
  * Build UI
2500
4052
  ************************************************************/
2501
- function build_ui$11(gobj) {
4053
+ function build_ui$12(gobj) {
2502
4054
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", (0, _yuneta_gobj_js.createElement2)([
2503
4055
  "div",
2504
4056
  {
2505
- class: `yui_tabs`,
4057
+ class: `C_YUI_TABS`,
2506
4058
  style: "height:100%; display:flex; flex-direction:column;"
2507
4059
  },
2508
4060
  [[
@@ -2693,7 +4245,7 @@ function register_c_yui_tabs() {
2693
4245
  return create_gclass$14(GCLASS_NAME$14);
2694
4246
  }
2695
4247
  //#endregion
2696
- //#region pager_helpers.js
4248
+ //#region src/pager_helpers.js
2697
4249
  /***************************************************************
2698
4250
  * Top entry or null.
2699
4251
  ***************************************************************/
@@ -2747,7 +4299,7 @@ function pager_back_action(stack, back_on_root) {
2747
4299
  return { type: "noop" };
2748
4300
  }
2749
4301
  //#endregion
2750
- //#region c_yui_pager.js
4302
+ //#region src/c_yui_pager.js
2751
4303
  /***********************************************************************
2752
4304
  * c_yui_pager.js
2753
4305
  *
@@ -2799,7 +4351,7 @@ var __gclass__$13 = null;
2799
4351
  function mt_create$13(gobj) {
2800
4352
  let priv = gobj.priv;
2801
4353
  priv.stack = [];
2802
- build_ui$10(gobj);
4354
+ build_ui$11(gobj);
2803
4355
  let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
2804
4356
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
2805
4357
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
@@ -2826,11 +4378,11 @@ function mt_destroy$13(gobj) {
2826
4378
  /************************************************************
2827
4379
  * Build UI
2828
4380
  ************************************************************/
2829
- function build_ui$10(gobj) {
4381
+ function build_ui$11(gobj) {
2830
4382
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", (0, _yuneta_gobj_js.createElement2)([
2831
4383
  "div",
2832
4384
  {
2833
- class: "yui-pager",
4385
+ class: "C_YUI_PAGER",
2834
4386
  style: "height:100%; display:flex; flex-direction:column;"
2835
4387
  },
2836
4388
  [[
@@ -3180,7 +4732,7 @@ function register_c_yui_pager() {
3180
4732
  return create_gclass$13(GCLASS_NAME$13);
3181
4733
  }
3182
4734
  //#endregion
3183
- //#region wizard_helpers.js
4735
+ //#region src/wizard_helpers.js
3184
4736
  /***********************************************************************
3185
4737
  * wizard_helpers.js
3186
4738
  *
@@ -3269,7 +4821,7 @@ function wizard_should_validate(linear, has_gobj) {
3269
4821
  return !!linear && !!has_gobj;
3270
4822
  }
3271
4823
  //#endregion
3272
- //#region c_yui_wizard.js
4824
+ //#region src/c_yui_wizard.js
3273
4825
  /***********************************************************************
3274
4826
  * c_yui_wizard.js
3275
4827
  *
@@ -3331,7 +4883,7 @@ function mt_create$12(gobj) {
3331
4883
  priv.idx = 0;
3332
4884
  priv.acc = null;
3333
4885
  priv.awaiting = false;
3334
- build_ui$9(gobj);
4886
+ build_ui$10(gobj);
3335
4887
  let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
3336
4888
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
3337
4889
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
@@ -3358,11 +4910,11 @@ function mt_destroy$12(gobj) {
3358
4910
  /************************************************************
3359
4911
  * Build UI
3360
4912
  ************************************************************/
3361
- function build_ui$9(gobj) {
4913
+ function build_ui$10(gobj) {
3362
4914
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", (0, _yuneta_gobj_js.createElement2)([
3363
4915
  "div",
3364
4916
  {
3365
- class: "yui-wizard",
4917
+ class: "C_YUI_WIZARD",
3366
4918
  style: "height:100%; display:flex; flex-direction:column;"
3367
4919
  },
3368
4920
  [
@@ -3798,7 +5350,7 @@ function register_c_yui_wizard() {
3798
5350
  return create_gclass$12(GCLASS_NAME$12);
3799
5351
  }
3800
5352
  //#endregion
3801
- //#region c_yui_form.js
5353
+ //#region src/c_yui_form.js
3802
5354
  /***********************************************************************
3803
5355
  * c_yui_form.js
3804
5356
  *
@@ -3850,7 +5402,7 @@ function mt_create$11(gobj) {
3850
5402
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
3851
5403
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
3852
5404
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "form_id", "form" + (0, _yuneta_gobj_js.clean_name)((0, _yuneta_gobj_js.gobj_name)(gobj)));
3853
- build_ui$8(gobj);
5405
+ build_ui$9(gobj);
3854
5406
  }
3855
5407
  /************************************************************
3856
5408
  * Framework Method destroy
@@ -3937,11 +5489,11 @@ async function readClipboard$1(gobj) {
3937
5489
  /************************************************************
3938
5490
  * Build UI
3939
5491
  ************************************************************/
3940
- function build_ui$8(gobj) {
5492
+ function build_ui$9(gobj) {
3941
5493
  let $container = (0, _yuneta_gobj_js.createElement2)([
3942
5494
  "div",
3943
5495
  {
3944
- 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",
3945
5497
  style: "height:100%; width:100%; box-sizing:border-box;"
3946
5498
  },
3947
5499
  [[
@@ -5801,7 +7353,7 @@ function register_c_yui_form() {
5801
7353
  return create_gclass$11(GCLASS_NAME$11);
5802
7354
  }
5803
7355
  //#endregion
5804
- //#region c_yui_routing.js
7356
+ //#region src/c_yui_routing.js
5805
7357
  /***********************************************************************
5806
7358
  * ui_routing.js
5807
7359
  *
@@ -6008,7 +7560,7 @@ function build_layout(gobj) {
6008
7560
  const $container = (0, _yuneta_gobj_js.createElement2)([
6009
7561
  "div",
6010
7562
  {
6011
- class: "container-grid-2col",
7563
+ class: "C_YUI_ROUTING",
6012
7564
  style: ""
6013
7565
  },
6014
7566
  [[
@@ -6368,7 +7920,7 @@ function register_c_yui_routing() {
6368
7920
  return create_gclass$10(GCLASS_NAME$10);
6369
7921
  }
6370
7922
  //#endregion
6371
- //#region lib_maplibre.js
7923
+ //#region src/lib_maplibre.js
6372
7924
  /***********************************************************************
6373
7925
  * lib_maplibre.js
6374
7926
  *
@@ -6513,7 +8065,7 @@ var MarkerControl = class {
6513
8065
  }
6514
8066
  };
6515
8067
  //#endregion
6516
- //#region c_yui_map.js
8068
+ //#region src/c_yui_map.js
6517
8069
  /***********************************************************************
6518
8070
  * c_yui_map.js
6519
8071
  *
@@ -6572,7 +8124,9 @@ function mt_create$9(gobj) {
6572
8124
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
6573
8125
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
6574
8126
  let map_settings = (0, _yuneta_gobj_js.json_deep_copy)((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "map_settings"));
6575
- Object.assign(map_settings, { container: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$map") });
8127
+ let $map = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$map");
8128
+ if ($map) $map.classList.add(GCLASS_NAME$9);
8129
+ Object.assign(map_settings, { container: $map });
6576
8130
  priv.onClick = (ev) => {
6577
8131
  onClick(gobj, ev);
6578
8132
  };
@@ -7120,7 +8674,7 @@ function register_c_yui_map() {
7120
8674
  return create_gclass$9(GCLASS_NAME$9);
7121
8675
  }
7122
8676
  //#endregion
7123
- //#region c_yui_uplot.js
8677
+ //#region src/c_yui_uplot.js
7124
8678
  /***********************************************************************
7125
8679
  * c_yui_uplot.js
7126
8680
  *
@@ -7180,7 +8734,7 @@ var __gclass__$8 = null;
7180
8734
  * Framework Method: Create
7181
8735
  ***************************************************************/
7182
8736
  function mt_create$8(gobj) {
7183
- build_ui$7(gobj);
8737
+ build_ui$8(gobj);
7184
8738
  let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
7185
8739
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
7186
8740
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
@@ -7210,11 +8764,11 @@ function mt_destroy$8(gobj) {
7210
8764
  /************************************************************
7211
8765
  * Build UI
7212
8766
  ************************************************************/
7213
- function build_ui$7(gobj) {
8767
+ function build_ui$8(gobj) {
7214
8768
  let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
7215
8769
  if (!$container) {
7216
8770
  $container = (0, _yuneta_gobj_js.createElement2)(["div", {
7217
- class: "",
8771
+ class: "C_YUI_UPLOT",
7218
8772
  style: "overflow:auto;"
7219
8773
  }]);
7220
8774
  (0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "$container", $container);
@@ -7413,7 +8967,7 @@ function register_c_yui_uplot() {
7413
8967
  return create_gclass$8(GCLASS_NAME$8);
7414
8968
  }
7415
8969
  //#endregion
7416
- //#region g6_drag_canvas_touch.js
8970
+ //#region src/g6_drag_canvas_touch.js
7417
8971
  /***********************************************************************
7418
8972
  * g6_drag_canvas_touch.js
7419
8973
  *
@@ -7521,7 +9075,7 @@ function ensure_drag_canvas_patch() {
7521
9075
  __drag_canvas_patched__ = true;
7522
9076
  }
7523
9077
  //#endregion
7524
- //#region c_yui_json_graph.js
9078
+ //#region src/c_yui_json_graph.js
7525
9079
  /***********************************************************************
7526
9080
  * c_yui_json_graph.js
7527
9081
  *
@@ -7594,7 +9148,7 @@ function mt_create$7(gobj) {
7594
9148
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
7595
9149
  let __yui_main__ = (0, _yuneta_gobj_js.gobj_find_service)("__yui_main__", true);
7596
9150
  if (__yui_main__) priv.theme = (0, _yuneta_gobj_js.gobj_read_str_attr)(__yui_main__, "theme");
7597
- build_ui$6(gobj);
9151
+ build_ui$7(gobj);
7598
9152
  ensure_drag_canvas_patch();
7599
9153
  if (!__layout_registered__$1) {
7600
9154
  (0, _antv_g6.register)(_antv_g6.ExtensionCategory.LAYOUT, "json-tree-layout", JsonTreeLayout);
@@ -7637,12 +9191,12 @@ function mt_destroy$7(gobj) {
7637
9191
  /************************************************************
7638
9192
  * Build UI
7639
9193
  ************************************************************/
7640
- function build_ui$6(gobj) {
9194
+ function build_ui$7(gobj) {
7641
9195
  let priv = gobj.priv;
7642
9196
  let $container = (0, _yuneta_gobj_js.createElement2)([
7643
9197
  "div",
7644
9198
  {
7645
- class: "json-graph",
9199
+ class: "C_YUI_JSON_GRAPH",
7646
9200
  style: "height:100%; display:flex; flex-direction:column;"
7647
9201
  },
7648
9202
  [[
@@ -8275,7 +9829,7 @@ function register_c_yui_json_graph() {
8275
9829
  return create_gclass$7(GCLASS_NAME$7);
8276
9830
  }
8277
9831
  //#endregion
8278
- //#region c_yui_gobj_tree_js.js
9832
+ //#region src/c_yui_gobj_tree_js.js
8279
9833
  /***********************************************************************
8280
9834
  * c_yui_gobj_tree_js.js
8281
9835
  *
@@ -8510,7 +10064,7 @@ function mt_create$6(gobj) {
8510
10064
  let __yui_main__ = (0, _yuneta_gobj_js.gobj_find_service)("__yui_main__");
8511
10065
  if (__yui_main__) priv.theme = (0, _yuneta_gobj_js.gobj_read_str_attr)(__yui_main__, "theme");
8512
10066
  else priv.theme = gt_is_dark() ? "dark" : "light";
8513
- build_ui$5(gobj);
10067
+ build_ui$6(gobj);
8514
10068
  ensure_drag_canvas_patch();
8515
10069
  if (!__layout_registered__) {
8516
10070
  (0, _antv_g6.register)(_antv_g6.ExtensionCategory.LAYOUT, "gobj-tree-v", GobjTreeVLayout);
@@ -8562,12 +10116,12 @@ function mt_destroy$6(gobj) {
8562
10116
  /************************************************************
8563
10117
  * Build UI
8564
10118
  ************************************************************/
8565
- function build_ui$5(gobj) {
10119
+ function build_ui$6(gobj) {
8566
10120
  let priv = gobj.priv;
8567
10121
  let $container = (0, _yuneta_gobj_js.createElement2)([
8568
10122
  "div",
8569
10123
  {
8570
- class: "gobj-tree",
10124
+ class: "C_YUI_GOBJ_TREE_JS",
8571
10125
  style: "height:100%; display:flex; flex-direction:column; position:relative;"
8572
10126
  },
8573
10127
  [[
@@ -9840,7 +11394,7 @@ function register_c_yui_gobj_tree_js() {
9840
11394
  return create_gclass$6(GCLASS_NAME$6);
9841
11395
  }
9842
11396
  //#endregion
9843
- //#region shell_show_on.js
11397
+ //#region src/shell_show_on.js
9844
11398
  /***********************************************************************
9845
11399
  * shell_show_on.js
9846
11400
  *
@@ -9916,7 +11470,7 @@ function bulma_hidden_class(bp) {
9916
11470
  return "";
9917
11471
  }
9918
11472
  //#endregion
9919
- //#region shell_focus_trap.js
11473
+ //#region src/shell_focus_trap.js
9920
11474
  /***********************************************************************
9921
11475
  * shell_focus_trap.js
9922
11476
  *
@@ -9990,7 +11544,7 @@ function activate_focus_trap_on($panel, doc) {
9990
11544
  };
9991
11545
  }
9992
11546
  //#endregion
9993
- //#region shell_toolbar_helpers.js
11547
+ //#region src/shell_toolbar_helpers.js
9994
11548
  var TOOLBAR_ACTION_TYPES = [
9995
11549
  "navigate",
9996
11550
  "drawer",
@@ -10116,7 +11670,7 @@ function _is_non_empty_string(v) {
10116
11670
  return typeof v === "string" && v.trim().length > 0;
10117
11671
  }
10118
11672
  //#endregion
10119
- //#region route_resolver.js
11673
+ //#region src/route_resolver.js
10120
11674
  /***********************************************************************
10121
11675
  * route_resolver.js
10122
11676
  *
@@ -10167,7 +11721,7 @@ function resolve_route(item_index, route) {
10167
11721
  };
10168
11722
  }
10169
11723
  //#endregion
10170
- //#region c_yui_shell.js
11724
+ //#region src/c_yui_shell.js
10171
11725
  /***********************************************************************
10172
11726
  * c_yui_shell.js
10173
11727
  *
@@ -10253,7 +11807,7 @@ function mt_create$5(gobj) {
10253
11807
  conn_nodes: [],
10254
11808
  active_dropdown: null
10255
11809
  });
10256
- build_ui$4(gobj);
11810
+ build_ui$5(gobj);
10257
11811
  }
10258
11812
  /***************************************************************
10259
11813
  * Framework Method: Start
@@ -10458,16 +12012,16 @@ function check_route_unique(route_owner, item, menu_id) {
10458
12012
  /************************************************************
10459
12013
  * Build the DOM: layers → base → zones
10460
12014
  ************************************************************/
10461
- function build_ui$4(gobj) {
12015
+ function build_ui$5(gobj) {
10462
12016
  let priv = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "priv");
10463
12017
  let shell_cfg = ((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "config") || {}).shell || {};
10464
- let $root = (0, _yuneta_gobj_js.createElement2)(["div", { class: "yui-shell" }]);
12018
+ let $container = (0, _yuneta_gobj_js.createElement2)(["div", { class: "C_YUI_SHELL yui-shell" }]);
10465
12019
  for (let [id, z] of LAYER_DEFS) {
10466
12020
  let $layer = (0, _yuneta_gobj_js.createElement2)(["div", {
10467
12021
  class: `yui-layer yui-layer-${id}`,
10468
12022
  style: `z-index:${z};`
10469
12023
  }]);
10470
- $root.appendChild($layer);
12024
+ $container.appendChild($layer);
10471
12025
  priv.layers[id] = $layer;
10472
12026
  }
10473
12027
  let $base = priv.layers.base;
@@ -10516,8 +12070,8 @@ function build_ui$4(gobj) {
10516
12070
  };
10517
12071
  priv.zones.center.classList.add("yui-stage", "yui-stage-main");
10518
12072
  }
10519
- ((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "mount_element") || document.body).appendChild($root);
10520
- (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", $root);
12073
+ ((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "mount_element") || document.body).appendChild($container);
12074
+ (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", $container);
10521
12075
  }
10522
12076
  /************************************************************
10523
12077
  * Translate zone id to grid-area name
@@ -11482,6 +13036,21 @@ function ac_drawer_close_requested(gobj, event, kw, src) {
11482
13036
  close_drawer(gobj, kw && kw.menu_id || "");
11483
13037
  return 0;
11484
13038
  }
13039
+ /************************************************************
13040
+ * Close 'x' on a closable tab (from a C_YUI_NAV child). The
13041
+ * shell does not own the item set — re-publish so the app (which
13042
+ * owns the underlying data, e.g. the selected-nodes list) removes
13043
+ * the item and calls yui_shell_set_submenu() with the new list.
13044
+ ************************************************************/
13045
+ function ac_nav_item_close(gobj, event, kw, src) {
13046
+ (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_NAV_ITEM_CLOSE", {
13047
+ item_id: kw && kw.item_id || "",
13048
+ route: kw && kw.route || "",
13049
+ menu_id: kw && kw.menu_id || "",
13050
+ zone: kw && kw.zone || ""
13051
+ });
13052
+ return 0;
13053
+ }
11485
13054
  /***************************************************************
11486
13055
  * FSM
11487
13056
  ***************************************************************/
@@ -11496,18 +13065,27 @@ function create_gclass$5(gclass_name) {
11496
13065
  (0, _yuneta_gobj_js.log_error)(`GClass ALREADY created: ${gclass_name}`);
11497
13066
  return -1;
11498
13067
  }
11499
- const states = [["ST_IDLE", [[
11500
- "EV_NAV_CLICKED",
11501
- ac_nav_clicked,
11502
- null
11503
- ], [
11504
- "EV_DRAWER_CLOSE_REQUESTED",
11505
- ac_drawer_close_requested,
11506
- null
11507
- ]]]];
13068
+ const states = [["ST_IDLE", [
13069
+ [
13070
+ "EV_NAV_CLICKED",
13071
+ ac_nav_clicked,
13072
+ null
13073
+ ],
13074
+ [
13075
+ "EV_DRAWER_CLOSE_REQUESTED",
13076
+ ac_drawer_close_requested,
13077
+ null
13078
+ ],
13079
+ [
13080
+ "EV_NAV_ITEM_CLOSE",
13081
+ ac_nav_item_close,
13082
+ null
13083
+ ]
13084
+ ]]];
11508
13085
  __gclass__$5 = (0, _yuneta_gobj_js.gclass_create)(gclass_name, [
11509
13086
  ["EV_NAV_CLICKED", 0],
11510
13087
  ["EV_DRAWER_CLOSE_REQUESTED", 0],
13088
+ ["EV_NAV_ITEM_CLOSE", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_PUBLIC_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
11511
13089
  ["EV_ROUTE_REQUESTED", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_PUBLIC_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
11512
13090
  ["EV_ROUTE_CHANGED", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_PUBLIC_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS]
11513
13091
  ], states, gmt$5, 0, attrs_table$5, PRIVATE_DATA$5, 0, 0, 0, _yuneta_gobj_js.gclass_flag_t.gcflag_no_check_output_events);
@@ -11599,9 +13177,9 @@ function yui_shell_set_connection_state(shell_gobj, connected) {
11599
13177
  * `icon_class` fully replaces the <i> class.
11600
13178
  ************************************************************/
11601
13179
  function yui_shell_set_toolbar_item_icon(shell_gobj, item_id, icon_class) {
11602
- let $root = (0, _yuneta_gobj_js.gobj_read_attr)(shell_gobj, "$container");
11603
- if (!$root || (0, _yuneta_gobj_js.empty_string)(item_id) || (0, _yuneta_gobj_js.empty_string)(icon_class)) return;
11604
- let $i = $root.querySelector(`[data-toolbar-item-id="${item_id}"] .icon i`);
13180
+ let $container = (0, _yuneta_gobj_js.gobj_read_attr)(shell_gobj, "$container");
13181
+ if (!$container || (0, _yuneta_gobj_js.empty_string)(item_id) || (0, _yuneta_gobj_js.empty_string)(icon_class)) return;
13182
+ let $i = $container.querySelector(`[data-toolbar-item-id="${item_id}"] .icon i`);
11605
13183
  if ($i) $i.className = icon_class;
11606
13184
  }
11607
13185
  /************************************************************
@@ -11613,7 +13191,7 @@ function yui_shell_close_dropdown(shell_gobj) {
11613
13191
  close_toolbar_dropdown(shell_gobj);
11614
13192
  }
11615
13193
  //#endregion
11616
- //#region c_yui_nav.js
13194
+ //#region src/c_yui_nav.js
11617
13195
  /***********************************************************************
11618
13196
  * c_yui_nav.js
11619
13197
  *
@@ -11631,6 +13209,15 @@ function yui_shell_close_dropdown(shell_gobj) {
11631
13209
  *
11632
13210
  * Each item supports:
11633
13211
  * id, name, icon (CSS class or svg id), route, badge, disabled
13212
+ * class — extra CSS class(es) on the item (tabs layout), e.g.
13213
+ * a per-item state colour like "yui-nav-disconnected"
13214
+ * closable — render a trailing ✕ that emits EV_NAV_ITEM_CLOSE
13215
+ * instead of navigating (tabs layout)
13216
+ *
13217
+ * Items can be replaced at runtime by sending EV_SET_ITEMS
13218
+ * {items:[...]} to the nav; the DOM is rebuilt in place and the
13219
+ * active highlight restored. The app_config path is simply the
13220
+ * first, startup caller of the same mechanism.
11634
13221
  *
11635
13222
  * In addition to navigable items, secondary navs accept two
11636
13223
  * decorative item kinds for in-place visual grouping:
@@ -11694,7 +13281,7 @@ function mt_create$4(gobj) {
11694
13281
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
11695
13282
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
11696
13283
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "priv", { click_handler: null });
11697
- build_ui$3(gobj);
13284
+ build_ui$4(gobj);
11698
13285
  }
11699
13286
  /***************************************************************
11700
13287
  * Framework Method: Start
@@ -11728,7 +13315,7 @@ function mt_destroy$4(gobj) {
11728
13315
  /************************************************************
11729
13316
  * Build the UI: dispatch to one of the six layout renderers
11730
13317
  ************************************************************/
11731
- function build_ui$3(gobj) {
13318
+ function build_ui$4(gobj) {
11732
13319
  let layout = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "layout") || "vertical";
11733
13320
  if (SUPPORTED_LAYOUTS.indexOf(layout) < 0) {
11734
13321
  (0, _yuneta_gobj_js.log_error)(`C_YUI_NAV: unsupported layout '${layout}', falling back to vertical`);
@@ -11737,40 +13324,75 @@ function build_ui$3(gobj) {
11737
13324
  let items = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "menu_items") || [];
11738
13325
  let zone = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "zone") || "";
11739
13326
  let menu_id = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "menu_id") || "nav";
11740
- let $root;
13327
+ let $container;
11741
13328
  switch (layout) {
11742
13329
  case "vertical":
11743
- $root = render_vertical(gobj, items);
13330
+ $container = render_vertical(gobj, items);
11744
13331
  break;
11745
13332
  case "icon-bar":
11746
- $root = render_icon_bar(gobj, items);
13333
+ $container = render_icon_bar(gobj, items);
11747
13334
  break;
11748
13335
  case "tabs":
11749
- $root = render_tabs(gobj, items);
13336
+ $container = render_tabs(gobj, items);
11750
13337
  break;
11751
13338
  case "drawer":
11752
- $root = render_drawer(gobj, items);
13339
+ $container = render_drawer(gobj, items);
11753
13340
  break;
11754
13341
  case "submenu":
11755
- $root = render_submenu(gobj, items);
13342
+ $container = render_submenu(gobj, items);
11756
13343
  break;
11757
13344
  case "accordion":
11758
- $root = render_accordion(gobj, items);
13345
+ $container = render_accordion(gobj, items);
11759
13346
  break;
11760
- default: $root = render_vertical(gobj, items);
13347
+ default: $container = render_vertical(gobj, items);
11761
13348
  }
11762
- $root.setAttribute("data-nav-zone", zone);
11763
- $root.setAttribute("data-nav-layout", layout);
11764
- $root.classList.add("yui-nav", `yui-nav-${layout}`);
13349
+ $container.setAttribute("data-nav-zone", zone);
13350
+ $container.setAttribute("data-nav-layout", layout);
13351
+ $container.classList.add("C_YUI_NAV", "yui-nav", `yui-nav-${layout}`);
11765
13352
  if (layout !== "drawer") {
11766
- $root.setAttribute("role", "navigation");
11767
- if (!$root.hasAttribute("aria-label")) {
13353
+ $container.setAttribute("role", "navigation");
13354
+ if (!$container.hasAttribute("aria-label")) {
11768
13355
  let label = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "nav_label") || menu_id;
11769
- $root.setAttribute("aria-label", label);
13356
+ $container.setAttribute("aria-label", label);
11770
13357
  }
11771
13358
  }
11772
- wire_clicks(gobj, $root);
11773
- (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", $root);
13359
+ wire_clicks(gobj, $container);
13360
+ (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", $container);
13361
+ }
13362
+ /************************************************************
13363
+ * Rebuild the nav DOM in place from the current menu_items.
13364
+ * Used by EV_SET_ITEMS so a nav's tabs can be changed at runtime
13365
+ * (the app_config path is just the first, startup caller).
13366
+ ************************************************************/
13367
+ function rebuild(gobj) {
13368
+ let $old = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
13369
+ let priv = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "priv") || {};
13370
+ let parent = $old ? $old.parentNode : null;
13371
+ let next = $old ? $old.nextSibling : null;
13372
+ let was_hidden = !!($old && $old.classList.contains("is-hidden"));
13373
+ if ($old && priv.click_handler) $old.removeEventListener("click", priv.click_handler);
13374
+ build_ui$4(gobj);
13375
+ let $new = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
13376
+ if ($new && was_hidden) $new.classList.add("is-hidden");
13377
+ if (parent && $new) parent.insertBefore($new, next);
13378
+ if ($old && $old.parentNode) $old.parentNode.removeChild($old);
13379
+ apply_active_route(gobj);
13380
+ }
13381
+ /************************************************************
13382
+ * Re-apply the active-route highlight after a rebuild (the
13383
+ * shell only pushes EV_ROUTE_CHANGED on navigation, not on a
13384
+ * self-triggered items change).
13385
+ ************************************************************/
13386
+ function apply_active_route(gobj) {
13387
+ let $c = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
13388
+ let route = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "active_route") || "";
13389
+ if (!$c || (0, _yuneta_gobj_js.empty_string)(route)) return;
13390
+ let $a = $c.querySelector(`a[data-route="${css_escape(route)}"]`);
13391
+ if ($a) {
13392
+ let $li = $a.closest("li");
13393
+ if ($li) $li.classList.add("is-active");
13394
+ $a.classList.add("is-active");
13395
+ }
11774
13396
  }
11775
13397
  /************************************************************
11776
13398
  * Layouts
@@ -11848,6 +13470,20 @@ function render_tabs(gobj, items) {
11848
13470
  { i18n: it.name },
11849
13471
  it.name
11850
13472
  ]);
13473
+ if (it.closable) children.push([
13474
+ "span",
13475
+ {
13476
+ class: "icon is-small yui-nav-close ml-2",
13477
+ "data-close-item": it.id,
13478
+ role: "button",
13479
+ "aria-label": "close",
13480
+ title: "close"
13481
+ },
13482
+ ["i", {
13483
+ class: "yi-xmark",
13484
+ "aria-hidden": "true"
13485
+ }]
13486
+ ]);
11851
13487
  let a_attrs = {
11852
13488
  href: it.route ? "#" + it.route : "#",
11853
13489
  "data-item-id": it.id,
@@ -11858,10 +13494,11 @@ function render_tabs(gobj, items) {
11858
13494
  a_attrs.title = tip;
11859
13495
  a_attrs["data-i18n-title"] = tip;
11860
13496
  }
13497
+ let li_class = (0, _yuneta_gobj_js.empty_string)(it.class) ? "" : String(it.class);
11861
13498
  lis.push([
11862
13499
  "li",
11863
13500
  {
11864
- class: "",
13501
+ class: li_class,
11865
13502
  "data-item-id": it.id,
11866
13503
  "data-route": it.route || ""
11867
13504
  },
@@ -11934,7 +13571,7 @@ function render_submenu(gobj, items) {
11934
13571
  function render_accordion(gobj, items) {
11935
13572
  let show_label = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "show_label");
11936
13573
  let icon_pos = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "icon_pos");
11937
- let $root = (0, _yuneta_gobj_js.createElement2)(["aside", { class: "menu yui-nav-accordion p-2" }]);
13574
+ let $container = (0, _yuneta_gobj_js.createElement2)(["aside", { class: "menu yui-nav-accordion p-2" }]);
11938
13575
  for (let it of items) {
11939
13576
  if (is_decorative(it)) continue;
11940
13577
  let acc_id = ++__nav_aria_seq__;
@@ -11956,7 +13593,7 @@ function render_accordion(gobj, items) {
11956
13593
  head_attrs,
11957
13594
  head_text
11958
13595
  ]);
11959
- $root.appendChild($hdr);
13596
+ $container.appendChild($hdr);
11960
13597
  let $ul = (0, _yuneta_gobj_js.createElement2)(["ul", {
11961
13598
  id: body_id,
11962
13599
  class: "menu-list yui-accordion-body is-hidden",
@@ -11968,9 +13605,9 @@ function render_accordion(gobj, items) {
11968
13605
  show_label,
11969
13606
  stacked: false
11970
13607
  })));
11971
- $root.appendChild($ul);
13608
+ $container.appendChild($ul);
11972
13609
  }
11973
- return $root;
13610
+ return $container;
11974
13611
  }
11975
13612
  /************************************************************
11976
13613
  * Shared helpers
@@ -12105,18 +13742,31 @@ function item_iconbar(gobj, it, opts) {
12105
13742
  ]
12106
13743
  ];
12107
13744
  }
12108
- function wire_clicks(gobj, $root) {
13745
+ function wire_clicks(gobj, $container) {
12109
13746
  let priv = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "priv") || {};
12110
13747
  let handler = (ev) => {
12111
13748
  let target = ev.target;
12112
13749
  if (!target || !target.closest) return;
13750
+ let $close = target.closest("[data-close-item]");
13751
+ if ($close) {
13752
+ let $routed = $close.closest("[data-route]");
13753
+ (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_NAV_ITEM_CLOSE", {
13754
+ item_id: $close.getAttribute("data-close-item") || "",
13755
+ route: $routed && $routed.getAttribute("data-route") || "",
13756
+ menu_id: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "menu_id") || "",
13757
+ zone: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "zone") || ""
13758
+ });
13759
+ ev.preventDefault();
13760
+ ev.stopPropagation();
13761
+ return;
13762
+ }
12113
13763
  if (target.closest("[data-close-drawer]")) {
12114
13764
  (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_DRAWER_CLOSE_REQUESTED", { menu_id: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "menu_id") || "" });
12115
13765
  ev.preventDefault();
12116
13766
  return;
12117
13767
  }
12118
13768
  let $head = target.closest(".yui-accordion-head");
12119
- if ($head && $root.contains($head)) {
13769
+ if ($head && $container.contains($head)) {
12120
13770
  let $next = $head.nextElementSibling;
12121
13771
  if ($next && $next.classList.contains("yui-accordion-body")) {
12122
13772
  let open = $next.classList.contains("is-hidden");
@@ -12143,13 +13793,23 @@ function wire_clicks(gobj, $root) {
12143
13793
  level: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "level") || "primary"
12144
13794
  });
12145
13795
  };
12146
- $root.addEventListener("click", handler);
13796
+ $container.addEventListener("click", handler);
12147
13797
  priv.click_handler = handler;
12148
13798
  }
12149
13799
  /***************************
12150
13800
  * Actions
12151
13801
  ***************************/
12152
13802
  /************************************************************
13803
+ * EV_SET_ITEMS — replace this nav's items and re-render in place.
13804
+ * This is the runtime counterpart of the startup config path:
13805
+ * the shell (or app, via the shell) drives dynamic navs through it.
13806
+ ************************************************************/
13807
+ function ac_set_items(gobj, event, kw, src) {
13808
+ (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "menu_items", (0, _yuneta_gobj_js.is_array)(kw.items) ? kw.items : []);
13809
+ rebuild(gobj);
13810
+ return 0;
13811
+ }
13812
+ /************************************************************
12153
13813
  * EV_ROUTE_CHANGED from the shell — refresh active highlight
12154
13814
  ************************************************************/
12155
13815
  function ac_route_changed(gobj, event, kw, src) {
@@ -12208,10 +13868,16 @@ function create_gclass$4(gclass_name) {
12208
13868
  "EV_ROUTE_CHANGED",
12209
13869
  ac_route_changed,
12210
13870
  null
13871
+ ], [
13872
+ "EV_SET_ITEMS",
13873
+ ac_set_items,
13874
+ null
12211
13875
  ]]]];
12212
13876
  __gclass__$4 = (0, _yuneta_gobj_js.gclass_create)(gclass_name, [
12213
13877
  ["EV_ROUTE_CHANGED", 0],
13878
+ ["EV_SET_ITEMS", 0],
12214
13879
  ["EV_NAV_CLICKED", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_PUBLIC_EVENT],
13880
+ ["EV_NAV_ITEM_CLOSE", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_PUBLIC_EVENT],
12215
13881
  ["EV_DRAWER_CLOSE_REQUESTED", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_PUBLIC_EVENT]
12216
13882
  ], states, gmt$4, 0, attrs_table$4, PRIVATE_DATA$4, 0, 0, 0, 0);
12217
13883
  if (!__gclass__$4) return -1;
@@ -12221,7 +13887,7 @@ function register_c_yui_nav() {
12221
13887
  return create_gclass$4(GCLASS_NAME$4);
12222
13888
  }
12223
13889
  //#endregion
12224
- //#region shell_modals.js
13890
+ //#region src/shell_modals.js
12225
13891
  /***********************************************************************
12226
13892
  * shell_modals.js
12227
13893
  *
@@ -12355,27 +14021,74 @@ function yui_shell_show_modal(shell, content, opts) {
12355
14021
  content
12356
14022
  ]]
12357
14023
  ] : null;
14024
+ let dialog = !!(opts && opts.dialog);
14025
+ let title = opts && opts.title || "";
12358
14026
  let with_close = !(opts && opts.with_close_button === false);
12359
- let modal_children = [["div", { class: "modal-background" }], [
12360
- "div",
12361
- { class: "modal-content" },
12362
- inner ? [inner] : []
12363
- ]];
12364
- if (with_close) modal_children.push(["button", {
12365
- class: "modal-close is-large",
12366
- "aria-label": "close"
12367
- }]);
14027
+ let modal_children;
14028
+ if (dialog) {
14029
+ modal_children = [["div", { class: "modal-background" }], [
14030
+ "div",
14031
+ { class: "modal-content yui-dialog-content" },
14032
+ [[
14033
+ "div",
14034
+ { class: "yui-dialog-header" },
14035
+ [
14036
+ [
14037
+ "button",
14038
+ {
14039
+ class: "yui-dialog-back",
14040
+ type: "button",
14041
+ "aria-label": "back"
14042
+ },
14043
+ [["i", { class: "yi-arrow-left" }]]
14044
+ ],
14045
+ [
14046
+ "span",
14047
+ {
14048
+ class: "yui-dialog-title",
14049
+ i18n: title
14050
+ },
14051
+ title
14052
+ ],
14053
+ [
14054
+ "button",
14055
+ {
14056
+ class: "yui-dialog-x",
14057
+ type: "button",
14058
+ "aria-label": "close"
14059
+ },
14060
+ [["i", { class: "yi-xmark" }]]
14061
+ ]
14062
+ ]
14063
+ ], [
14064
+ "div",
14065
+ { class: "yui-dialog-body" },
14066
+ inner ? [inner] : []
14067
+ ]]
14068
+ ]];
14069
+ with_close = false;
14070
+ } else {
14071
+ modal_children = [["div", { class: "modal-background" }], [
14072
+ "div",
14073
+ { class: "modal-content" },
14074
+ inner ? [inner] : []
14075
+ ]];
14076
+ if (with_close) modal_children.push(["button", {
14077
+ class: "modal-close is-large",
14078
+ "aria-label": "close"
14079
+ }]);
14080
+ }
12368
14081
  let $modal = (0, _yuneta_gobj_js.createElement2)([
12369
14082
  "div",
12370
14083
  {
12371
- class: "modal yui-modal is-active",
14084
+ class: "modal yui-modal" + (dialog ? " yui-dialog" : "") + " is-active",
12372
14085
  role: "dialog",
12373
14086
  "aria-modal": "true"
12374
14087
  },
12375
14088
  modal_children
12376
14089
  ]);
12377
14090
  $layer.appendChild($modal);
12378
- if (!inner && content && typeof content.appendChild !== "undefined") $modal.querySelector(".modal-content").appendChild(content);
14091
+ if (!inner && content && typeof content.appendChild !== "undefined") (dialog ? $modal.querySelector(".yui-dialog-body") : $modal.querySelector(".modal-content")).appendChild(content);
12379
14092
  maybe_apply_translator($modal, opts);
12380
14093
  let closed = false;
12381
14094
  let close_fn = null;
@@ -12404,6 +14117,12 @@ function yui_shell_show_modal(shell, content, opts) {
12404
14117
  if (opts == null || opts.dismiss_on_background !== false) $modal.querySelector(".modal-background").addEventListener("click", close);
12405
14118
  let $close_btn = $modal.querySelector(".modal-close");
12406
14119
  if ($close_btn) $close_btn.addEventListener("click", close);
14120
+ if (dialog) {
14121
+ let $back = $modal.querySelector(".yui-dialog-back");
14122
+ if ($back) $back.addEventListener("click", close);
14123
+ let $x = $modal.querySelector(".yui-dialog-x");
14124
+ if ($x) $x.addEventListener("click", close);
14125
+ }
12407
14126
  return { close };
12408
14127
  }
12409
14128
  /***************************************************************
@@ -12555,7 +14274,7 @@ function yui_shell_confirm_yesnocancel(shell, message, opts) {
12555
14274
  ], opts);
12556
14275
  }
12557
14276
  //#endregion
12558
- //#region c_yui_treedb_topics.js
14277
+ //#region src/c_yui_treedb_topics.js
12559
14278
  /***********************************************************************
12560
14279
  * c_yui_treedb_topics.js
12561
14280
  *
@@ -12598,7 +14317,7 @@ var __gclass__$3 = null;
12598
14317
  * Framework Method: Create
12599
14318
  ***************************************************************/
12600
14319
  function mt_create$3(gobj) {
12601
- build_ui$2(gobj);
14320
+ build_ui$3(gobj);
12602
14321
  let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
12603
14322
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
12604
14323
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
@@ -12672,11 +14391,11 @@ function cmd_get_topic_data$1(gobj, cmd, kw, src) {
12672
14391
  /************************************************************
12673
14392
  * Build UI
12674
14393
  ************************************************************/
12675
- function build_ui$2(gobj) {
14394
+ function build_ui$3(gobj) {
12676
14395
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", (0, _yuneta_gobj_js.createElement2)([
12677
14396
  "div",
12678
14397
  {
12679
- class: `${(0, _yuneta_gobj_js.gobj_read_attr)(gobj, "treedb_name")}`,
14398
+ class: `C_YUI_TREEDB_TOPICS ${(0, _yuneta_gobj_js.gobj_read_attr)(gobj, "treedb_name")}`,
12680
14399
  style: "height:100%; display:flex; flex-direction:column;"
12681
14400
  },
12682
14401
  [[
@@ -12701,6 +14420,26 @@ function build_ui$2(gobj) {
12701
14420
  ]));
12702
14421
  }
12703
14422
  /************************************************************
14423
+ * Show a non-blocking inline error banner at the top of the view
14424
+ * (used when the treedb schema `descs` cannot load). Reuses a single
14425
+ * banner so retries don't stack.
14426
+ ************************************************************/
14427
+ function show_load_error$1(gobj, message) {
14428
+ let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
14429
+ if (!$container) return;
14430
+ let $err = $container.querySelector(".treedb-load-error");
14431
+ if (!$err) {
14432
+ $err = (0, _yuneta_gobj_js.createElement2)([
14433
+ "div",
14434
+ { class: "notification is-danger is-light m-3 treedb-load-error" },
14435
+ []
14436
+ ]);
14437
+ $container.insertBefore($err, $container.firstChild);
14438
+ }
14439
+ let treedb = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "treedb_name") || "";
14440
+ $err.textContent = (treedb ? `${treedb}: ` : "") + (message || "cannot load treedb");
14441
+ }
14442
+ /************************************************************
12704
14443
  * Destroy UI
12705
14444
  ************************************************************/
12706
14445
  function destroy_ui$2(gobj) {
@@ -12920,10 +14659,14 @@ function ac_mt_command_answer$1(gobj, event, kw, src) {
12920
14659
  (0, _yuneta_gobj_js.log_error)(e);
12921
14660
  return;
12922
14661
  }
12923
- if (result < 0) display_error_message("Error", (0, i18next.t)(comment));
12924
14662
  let __command__ = (0, _yuneta_gobj_js.msg_iev_get_stack)(gobj, kw, "command_stack", true);
12925
14663
  let command = (0, _yuneta_gobj_js.kw_get_str)(gobj, __command__, "command", "", _yuneta_gobj_js.kw_flag_t.KW_REQUIRED);
12926
14664
  let kw_command = (0, _yuneta_gobj_js.kw_get_dict)(gobj, __command__, "kw", {}, _yuneta_gobj_js.kw_flag_t.KW_REQUIRED);
14665
+ if (result < 0) {
14666
+ if (command === "descs") show_load_error$1(gobj, (0, i18next.t)(comment));
14667
+ else display_error_message("Error", (0, i18next.t)(comment));
14668
+ return 0;
14669
+ }
12927
14670
  switch (command) {
12928
14671
  case "descs":
12929
14672
  if (result >= 0) {
@@ -13188,7 +14931,7 @@ function register_c_yui_treedb_topics() {
13188
14931
  return create_gclass$3(GCLASS_NAME$3);
13189
14932
  }
13190
14933
  //#endregion
13191
- //#region c_yui_treedb_topic_with_form.js
14934
+ //#region src/c_yui_treedb_topic_with_form.js
13192
14935
  /***********************************************************************
13193
14936
  * c_yui_treedb_topic_with_form.js
13194
14937
  *
@@ -13266,7 +15009,7 @@ function mt_create$2(gobj) {
13266
15009
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "toolbar_id", "toolbar" + name);
13267
15010
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "form_id", "form" + name);
13268
15011
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "popup_id", "popup" + name);
13269
- build_ui$1(gobj);
15012
+ build_ui$2(gobj);
13270
15013
  let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
13271
15014
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
13272
15015
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
@@ -13350,7 +15093,7 @@ async function readClipboard(gobj) {
13350
15093
  /************************************************************
13351
15094
  * Build UI
13352
15095
  ************************************************************/
13353
- function build_ui$1(gobj) {
15096
+ function build_ui$2(gobj) {
13354
15097
  function create_table_toolbar() {
13355
15098
  let $table_toolbar = [];
13356
15099
  let with_edition_mode = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_edition_mode");
@@ -13567,7 +15310,7 @@ function build_ui$1(gobj) {
13567
15310
  let $container = (0, _yuneta_gobj_js.createElement2)([
13568
15311
  "div",
13569
15312
  {
13570
- class: "container-treedb-topic-with-form",
15313
+ class: "C_YUI_TREEDB_TOPIC_WITH_FORM",
13571
15314
  style: "height:100%;"
13572
15315
  },
13573
15316
  [["div", {
@@ -15578,7 +17321,7 @@ function register_c_yui_treedb_topic_with_form() {
15578
17321
  return create_gclass$2(GCLASS_NAME$2);
15579
17322
  }
15580
17323
  //#endregion
15581
- //#region lib_graph.js
17324
+ //#region src/lib_graph.js
15582
17325
  /***********************************************************************
15583
17326
  * lib_graph.js
15584
17327
  *
@@ -15690,7 +17433,7 @@ function getStrokeColor(fillColor, theme = "light", factor = .2) {
15690
17433
  return `rgba(${adjust(r)}, ${adjust(g)}, ${adjust(b)}, ${a.toFixed(3)})`;
15691
17434
  }
15692
17435
  //#endregion
15693
- //#region c_yui_treedb_graph.js
17436
+ //#region src/c_yui_treedb_graph.js
15694
17437
  /***********************************************************************
15695
17438
  * c_yui_treedb_graph.js
15696
17439
  *
@@ -15766,7 +17509,7 @@ function mt_create$1(gobj) {
15766
17509
  let __yui_main__ = (0, _yuneta_gobj_js.gobj_find_service)("__yui_main__");
15767
17510
  if (__yui_main__) (0, _yuneta_gobj_js.gobj_subscribe_event)(__yui_main__, "EV_RESIZE", {}, gobj);
15768
17511
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "canvas_id", (0, _yuneta_gobj_js.clean_name)((0, _yuneta_gobj_js.gobj_name)(gobj)) + "-canvas");
15769
- let $container_canvas = build_ui(gobj).querySelector(`#${priv.canvas_id}`);
17512
+ let $container_canvas = build_ui$1(gobj).querySelector(`#${priv.canvas_id}`);
15770
17513
  priv.gobj_nodes_tree = (0, _yuneta_gobj_js.gobj_create_service)(`${(0, _yuneta_gobj_js.gobj_name)(gobj)}-g6`, "C_G6_NODES_TREE", {
15771
17514
  $container: $container_canvas,
15772
17515
  subscriber: gobj,
@@ -15816,13 +17559,13 @@ function mt_destroy$1(gobj) {
15816
17559
  /************************************************************
15817
17560
  * Build UI
15818
17561
  ************************************************************/
15819
- function build_ui(gobj) {
17562
+ function build_ui$1(gobj) {
15820
17563
  let priv = gobj.priv;
15821
17564
  let padding = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "padding");
15822
17565
  let $container = (0, _yuneta_gobj_js.createElement2)([
15823
17566
  "div",
15824
17567
  {
15825
- class: "graphs",
17568
+ class: "C_YUI_TREEDB_GRAPH",
15826
17569
  style: `height:100%; display:flex; flex-direction:column;`
15827
17570
  },
15828
17571
  [[
@@ -15851,6 +17594,26 @@ function build_ui(gobj) {
15851
17594
  return $container;
15852
17595
  }
15853
17596
  /************************************************************
17597
+ * Show a non-blocking inline error banner at the top of the view
17598
+ * (used when the treedb schema `descs` cannot load). Reuses a single
17599
+ * banner so retries don't stack.
17600
+ ************************************************************/
17601
+ function show_load_error(gobj, message) {
17602
+ let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
17603
+ if (!$container) return;
17604
+ let $err = $container.querySelector(".treedb-load-error");
17605
+ if (!$err) {
17606
+ $err = (0, _yuneta_gobj_js.createElement2)([
17607
+ "div",
17608
+ { class: "notification is-danger is-light m-3 treedb-load-error" },
17609
+ []
17610
+ ]);
17611
+ $container.insertBefore($err, $container.firstChild);
17612
+ }
17613
+ let treedb = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "treedb_name") || "";
17614
+ $err.textContent = (treedb ? `${treedb}: ` : "") + (message || "cannot load treedb");
17615
+ }
17616
+ /************************************************************
15854
17617
  * Destroy UI
15855
17618
  ************************************************************/
15856
17619
  function destroy_ui(gobj) {
@@ -16192,10 +17955,11 @@ function ac_mt_command_answer(gobj, event, kw, src) {
16192
17955
  (0, _yuneta_gobj_js.log_error)(e);
16193
17956
  return;
16194
17957
  }
16195
- if (result < 0) display_error_message("Error", (0, i18next.t)(comment));
16196
17958
  let __command__ = (0, _yuneta_gobj_js.msg_iev_get_stack)(gobj, kw, "command_stack", true);
16197
17959
  let command = (0, _yuneta_gobj_js.kw_get_str)(gobj, __command__, "command", "", _yuneta_gobj_js.kw_flag_t.KW_REQUIRED);
16198
17960
  let kw_command = (0, _yuneta_gobj_js.kw_get_dict)(gobj, __command__, "kw", {}, _yuneta_gobj_js.kw_flag_t.KW_REQUIRED);
17961
+ if (result < 0) if (command === "descs") show_load_error(gobj, (0, i18next.t)(comment));
17962
+ else display_error_message("Error", (0, i18next.t)(comment));
16199
17963
  switch (command) {
16200
17964
  case "descs":
16201
17965
  if (result >= 0) process_treedb_descs(gobj, data);
@@ -43119,7 +44883,7 @@ Sector.PARSED_STYLE_LIST = new Set([].concat(_toConsumableArray(Path.PARSED_STYL
43119
44883
  "endAngle"
43120
44884
  ]));
43121
44885
  //#endregion
43122
- //#region lib_icons.js
44886
+ //#region src/lib_icons.js
43123
44887
  /*********************************************************
43124
44888
  * lib_icons.js
43125
44889
  *
@@ -43230,7 +44994,7 @@ function inject_svg_icons() {
43230
44994
  document.body.appendChild(div);
43231
44995
  }
43232
44996
  //#endregion
43233
- //#region c_g6_nodes_tree.js
44997
+ //#region src/c_g6_nodes_tree.js
43234
44998
  /***********************************************************************
43235
44999
  * c_g6_nodes_tree.js
43236
45000
  *
@@ -43416,6 +45180,7 @@ function mt_create(gobj) {
43416
45180
  gobj.priv.theme_observer = mo;
43417
45181
  }
43418
45182
  }
45183
+ build_ui(gobj);
43419
45184
  register_layouts(gobj);
43420
45185
  build_graph(gobj);
43421
45186
  if (typeof ResizeObserver !== "undefined" && priv.$container) {
@@ -43514,6 +45279,13 @@ function get_default_ne_xy(gobj) {
43514
45279
  return xy;
43515
45280
  }
43516
45281
  /************************************************************
45282
+ * Build UI
45283
+ ************************************************************/
45284
+ function build_ui(gobj) {
45285
+ let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
45286
+ if ($container) $container.classList.add(GCLASS_NAME);
45287
+ }
45288
+ /************************************************************
43517
45289
  * Register custom G6 layouts
43518
45290
  ************************************************************/
43519
45291
  var _g6_extensions_registered = false;
@@ -47428,7 +49200,7 @@ function register_c_g6_nodes_tree() {
47428
49200
  return create_gclass(GCLASS_NAME);
47429
49201
  }
47430
49202
  //#endregion
47431
- //#region themes.js
49203
+ //#region src/themes.js
47432
49204
  function themes() {
47433
49205
  function openModal($el) {
47434
49206
  $el.classList.add("is-active");
@@ -47558,7 +49330,7 @@ function themes() {
47558
49330
  onClickOutside(".js-menu");
47559
49331
  }
47560
49332
  //#endregion
47561
- //#region ytable.js
49333
+ //#region src/ytable.js
47562
49334
  var default_options = {
47563
49335
  id: void 0,
47564
49336
  width: "100%",
@@ -47830,11 +49602,43 @@ function createYTable($container, columns, options) {
47830
49602
  return new YTable($container, columns, options);
47831
49603
  }
47832
49604
  //#endregion
49605
+ //#region src/yui_inputs.js
49606
+ /***************************************************************
49607
+ * Add a clear (✕) button to a Bulma control wrapping an input.
49608
+ * Returns the button element (already appended to $control).
49609
+ ***************************************************************/
49610
+ function attach_clear($control, $input, on_clear) {
49611
+ if (!$control || !$input) return null;
49612
+ $control.classList.add("has-clear");
49613
+ let $btn = (0, _yuneta_gobj_js.createElement2)(["button", {
49614
+ type: "button",
49615
+ class: "delete is-medium yui-input-clear",
49616
+ tabindex: "-1",
49617
+ title: i18next.default.t("clear"),
49618
+ "aria-label": i18next.default.t("clear")
49619
+ }]);
49620
+ function sync() {
49621
+ $btn.classList.toggle("is-visible", !!$input.value);
49622
+ }
49623
+ $input.addEventListener("input", sync);
49624
+ $btn.addEventListener("click", () => {
49625
+ $input.value = "";
49626
+ $input.dispatchEvent(new Event("input", { bubbles: true }));
49627
+ if (typeof on_clear === "function") on_clear();
49628
+ $input.focus();
49629
+ sync();
49630
+ });
49631
+ $control.appendChild($btn);
49632
+ sync();
49633
+ return $btn;
49634
+ }
49635
+ //#endregion
47833
49636
  exports.EditControl = EditControl;
47834
49637
  exports.MarkerControl = MarkerControl;
47835
49638
  exports.YTable = YTable;
47836
49639
  exports.addClasses = addClasses;
47837
49640
  exports.apply_dev_traces = apply_dev_traces;
49641
+ exports.attach_clear = attach_clear;
47838
49642
  exports.build_dev_panel = build_dev_panel;
47839
49643
  exports.createYTable = createYTable;
47840
49644
  exports.dev_window_was_open = dev_window_was_open;
@@ -47866,6 +49670,7 @@ exports.register_c_yui_treedb_topic_with_form = register_c_yui_treedb_topic_with
47866
49670
  exports.register_c_yui_treedb_topics = register_c_yui_treedb_topics;
47867
49671
  exports.register_c_yui_uplot = register_c_yui_uplot;
47868
49672
  exports.register_c_yui_window = register_c_yui_window;
49673
+ exports.register_c_yui_window_manager = register_c_yui_window_manager;
47869
49674
  exports.register_c_yui_wizard = register_c_yui_wizard;
47870
49675
  exports.removeChildElements = removeChildElements;
47871
49676
  exports.removeClasses = removeClasses;