@yuneta/gobj-ui 2.0.0 → 2.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +25 -20
  2. package/dist/gobj-ui.cjs.js +2340 -528
  3. package/dist/gobj-ui.es.js +2340 -530
  4. package/index.js +32 -31
  5. package/package.json +6 -7
  6. package/{c_g6_nodes_tree.js → src/c_g6_nodes_tree.js} +6 -1
  7. package/{c_yui_form.js → src/c_yui_form.js} +1 -1
  8. package/{c_yui_gobj_tree_js.js → src/c_yui_gobj_tree_js.js} +1 -1
  9. package/{c_yui_json_graph.js → src/c_yui_json_graph.js} +1 -1
  10. package/{c_yui_main.js → src/c_yui_main.js} +3 -3
  11. package/{c_yui_map.js → src/c_yui_map.js} +6 -1
  12. package/{c_yui_nav.js → src/c_yui_nav.js} +134 -26
  13. package/{c_yui_pager.js → src/c_yui_pager.js} +1 -1
  14. package/{c_yui_routing.css → src/c_yui_routing.css} +1 -1
  15. package/{c_yui_routing.js → src/c_yui_routing.js} +1 -1
  16. package/{c_yui_shell.css → src/c_yui_shell.css} +110 -0
  17. package/{c_yui_shell.js → src/c_yui_shell.js} +175 -13
  18. package/{c_yui_tabs.js → src/c_yui_tabs.js} +1 -1
  19. package/{c_yui_treedb_graph.js → src/c_yui_treedb_graph.js} +35 -9
  20. package/{c_yui_treedb_topic_with_form.js → src/c_yui_treedb_topic_with_form.js} +1 -1
  21. package/{c_yui_treedb_topics.js → src/c_yui_treedb_topics.js} +36 -8
  22. package/{c_yui_uplot.js → src/c_yui_uplot.js} +1 -1
  23. package/{c_yui_window.js → src/c_yui_window.js} +242 -21
  24. package/src/c_yui_window_manager.js +602 -0
  25. package/{c_yui_wizard.js → src/c_yui_wizard.js} +1 -1
  26. package/{shell_modals.js → src/shell_modals.js} +53 -13
  27. package/src/tabulator.css +53 -0
  28. package/src/yui_dev.js +1484 -0
  29. package/{yui_icons.css → src/yui_icons.css} +5 -0
  30. package/src/yui_inputs.css +32 -0
  31. package/src/yui_inputs.js +71 -0
  32. package/vite.config.js +0 -131
  33. package/yui_dev.js +0 -583
  34. /package/{c_yui_main.css → src/c_yui_main.css} +0 -0
  35. /package/{c_yui_map.css → src/c_yui_map.css} +0 -0
  36. /package/{c_yui_treedb_topic_with_form.css → src/c_yui_treedb_topic_with_form.css} +0 -0
  37. /package/{g6_drag_canvas_touch.js → src/g6_drag_canvas_touch.js} +0 -0
  38. /package/{lib_graph.css → src/lib_graph.css} +0 -0
  39. /package/{lib_graph.js → src/lib_graph.js} +0 -0
  40. /package/{lib_icons.js → src/lib_icons.js} +0 -0
  41. /package/{lib_maplibre.js → src/lib_maplibre.js} +0 -0
  42. /package/{pager_helpers.js → src/pager_helpers.js} +0 -0
  43. /package/{pager_helpers.test.js → src/pager_helpers.test.js} +0 -0
  44. /package/{route_resolver.js → src/route_resolver.js} +0 -0
  45. /package/{route_resolver.test.js → src/route_resolver.test.js} +0 -0
  46. /package/{shell_focus_trap.js → src/shell_focus_trap.js} +0 -0
  47. /package/{shell_focus_trap.test.js → src/shell_focus_trap.test.js} +0 -0
  48. /package/{shell_show_on.js → src/shell_show_on.js} +0 -0
  49. /package/{shell_show_on.test.js → src/shell_show_on.test.js} +0 -0
  50. /package/{shell_toolbar_helpers.js → src/shell_toolbar_helpers.js} +0 -0
  51. /package/{shell_toolbar_helpers.test.js → src/shell_toolbar_helpers.test.js} +0 -0
  52. /package/{themes.js → src/themes.js} +0 -0
  53. /package/{wizard_helpers.js → src/wizard_helpers.js} +0 -0
  54. /package/{wizard_helpers.test.js → src/wizard_helpers.test.js} +0 -0
  55. /package/{ytable.css → src/ytable.css} +0 -0
  56. /package/{ytable.js → src/ytable.js} +0 -0
  57. /package/{yui_toolbar.css → src/yui_toolbar.css} +0 -0
  58. /package/{yui_toolbar.js → src/yui_toolbar.js} +0 -0
@@ -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
+ ******************************/
55
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
+ }
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,401 @@ 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
+ if (e.kind === "log") {
1065
+ out.push(`${e.ts} ${e.level}: ${e.text}`);
1066
+ out.push("");
1067
+ continue;
1068
+ }
1069
+ let head = `${e.ts} ${dir_arrow(e.dir)} ${e.title ? "[" + e.title + "] " : ""}${e.event}${e.command ? " " + e.command : ""}`;
1070
+ out.push(head);
1071
+ let payload = e.kw ? e.kw : e.jn;
1072
+ try {
1073
+ out.push(JSON.stringify(payload, null, 2));
1074
+ } catch (err) {
1075
+ out.push(String(payload));
1076
+ }
1077
+ out.push("");
1078
+ }
1079
+ return out.join("\n");
1080
+ }
1081
+ function dev_copy_text(text) {
1082
+ if (navigator.clipboard && navigator.clipboard.writeText) return navigator.clipboard.writeText(text).catch(() => {
1083
+ dev_fallback_copy(text);
256
1084
  });
1085
+ dev_fallback_copy(text);
1086
+ return Promise.resolve();
1087
+ }
1088
+ function dev_fallback_copy(text) {
1089
+ let ta = document.createElement("textarea");
1090
+ ta.value = text;
1091
+ ta.style.position = "fixed";
1092
+ ta.style.left = "-9999px";
1093
+ document.body.appendChild(ta);
1094
+ ta.select();
1095
+ try {
1096
+ document.execCommand("copy");
1097
+ } catch (e) {}
1098
+ document.body.removeChild(ta);
257
1099
  }
1100
+ function build_control_bar() {
1101
+ let trace_chips = TRACE_DEFS.map(([key, label, fn]) => [
1102
+ "button",
1103
+ {
1104
+ class: "YDEV_CHIP",
1105
+ "data-trace": key,
1106
+ "data-label": label,
1107
+ type: "button"
1108
+ },
1109
+ label,
1110
+ { click: (ev) => {
1111
+ ev.stopPropagation();
1112
+ fn();
1113
+ } }
1114
+ ]);
1115
+ let simple_mach = [
1116
+ "button",
1117
+ {
1118
+ class: "YDEV_CHIP",
1119
+ "data-toggle": "automata-simple",
1120
+ type: "button",
1121
+ title: "Compact automata format (one line per transition, like C)"
1122
+ },
1123
+ "Simple mach",
1124
+ { click: (ev) => {
1125
+ ev.stopPropagation();
1126
+ let v = dev_num("dev_automata_simple", 0) ? 0 : 1;
1127
+ (0, _yuneta_gobj_js.kw_set_local_storage_value)("dev_automata_simple", v);
1128
+ (0, _yuneta_gobj_js.gobj_set_trace_machine_format)(v);
1129
+ refresh_dev_chrome();
1130
+ } }
1131
+ ];
1132
+ let mk_view = (v, label) => [
1133
+ "button",
1134
+ {
1135
+ class: "YDEV_SEG_BTN",
1136
+ "data-view": v,
1137
+ type: "button"
1138
+ },
1139
+ label,
1140
+ { click: (ev) => {
1141
+ ev.stopPropagation();
1142
+ set_view(v);
1143
+ } }
1144
+ ];
1145
+ let view_seg = [
1146
+ "div",
1147
+ {
1148
+ class: "YDEV_SEG",
1149
+ id: "ydev-seg"
1150
+ },
1151
+ [
1152
+ mk_view("detailed", "Detailed"),
1153
+ mk_view("full", "Expanded"),
1154
+ mk_view("compact", "Compact"),
1155
+ mk_view("name", "Name only")
1156
+ ]
1157
+ ];
1158
+ let mk_expand = (key, label) => [
1159
+ "button",
1160
+ {
1161
+ class: "YDEV_CHIP",
1162
+ "data-expand": key,
1163
+ type: "button",
1164
+ title: "Show " + label + " in the Expanded view"
1165
+ },
1166
+ label,
1167
+ { click: (ev) => {
1168
+ ev.stopPropagation();
1169
+ toggle_pref(key, key === "dev_full_data" ? 1 : 0);
1170
+ } }
1171
+ ];
1172
+ let expand_grp = [
1173
+ "div",
1174
+ {
1175
+ class: "YDEV_GROUP",
1176
+ id: "ydev-expand-grp"
1177
+ },
1178
+ [
1179
+ [
1180
+ "span",
1181
+ { class: "YDEV_LABEL" },
1182
+ "Expand"
1183
+ ],
1184
+ mk_expand("dev_full_schema", "Schema"),
1185
+ mk_expand("dev_full_data", "Data"),
1186
+ mk_expand("dev_full_meta", "Metadata")
1187
+ ]
1188
+ ];
1189
+ let mk_dir = (dir, glyph, key, title) => [
1190
+ "button",
1191
+ {
1192
+ class: "YDEV_CHIP s-" + dir,
1193
+ "data-dir": key,
1194
+ type: "button",
1195
+ title
1196
+ },
1197
+ glyph,
1198
+ { click: (ev) => {
1199
+ ev.stopPropagation();
1200
+ toggle_pref(key, 1);
1201
+ } }
1202
+ ];
1203
+ let dir_chips = [
1204
+ mk_dir("out", "⇢", "dev_filter_out", "Outgoing"),
1205
+ mk_dir("in", "⇠", "dev_filter_in", "Incoming"),
1206
+ mk_dir("err", "⚠", "dev_filter_err", "Errors")
1207
+ ];
1208
+ let periodic_chip = [
1209
+ "button",
1210
+ {
1211
+ class: "YDEV_CHIP",
1212
+ "data-toggle": "periodic",
1213
+ type: "button",
1214
+ title: "Hide recurring / periodic events (polls, heartbeats)"
1215
+ },
1216
+ "⊘ Periodic",
1217
+ { click: (ev) => {
1218
+ ev.stopPropagation();
1219
+ toggle_pref("dev_hide_periodic", 0);
1220
+ } }
1221
+ ];
1222
+ let search = [
1223
+ "input",
1224
+ {
1225
+ class: "YDEV_SEARCH",
1226
+ type: "search",
1227
+ placeholder: "filter events / payload…",
1228
+ "data-role": "search"
1229
+ },
1230
+ "",
1231
+ { input: (ev) => {
1232
+ SEARCH_TEXT = String(ev.target.value || "").toLowerCase().trim();
1233
+ rerender_all();
1234
+ } }
1235
+ ];
1236
+ let copy = [
1237
+ "button",
1238
+ {
1239
+ class: "YDEV_CHIP",
1240
+ type: "button",
1241
+ title: "Copy visible traffic to clipboard"
1242
+ },
1243
+ "Copy",
1244
+ { click: (ev) => {
1245
+ ev.stopPropagation();
1246
+ let btn = ev.currentTarget;
1247
+ dev_copy_text(traffic_to_text()).then(() => {
1248
+ let prev = btn.textContent;
1249
+ btn.textContent = "Copied";
1250
+ setTimeout(() => {
1251
+ btn.textContent = prev;
1252
+ }, 1e3);
1253
+ });
1254
+ } }
1255
+ ];
1256
+ let clear = [
1257
+ "button",
1258
+ {
1259
+ class: "YDEV_CHIP",
1260
+ type: "button",
1261
+ title: "Clear captured traffic"
1262
+ },
1263
+ "Clear",
1264
+ { click: (ev) => {
1265
+ ev.stopPropagation();
1266
+ clear_traffic();
1267
+ } }
1268
+ ];
1269
+ let grp = (label, items) => [
1270
+ "div",
1271
+ { class: "YDEV_GROUP" },
1272
+ [[
1273
+ "span",
1274
+ { class: "YDEV_LABEL" },
1275
+ label
1276
+ ], ...items]
1277
+ ];
1278
+ let sep = () => [
1279
+ "span",
1280
+ { class: "YDEV_SEP" },
1281
+ ""
1282
+ ];
1283
+ return (0, _yuneta_gobj_js.createElement2)([
1284
+ "div",
1285
+ { class: "YDEV_BAR" },
1286
+ [
1287
+ grp("Traces", [...trace_chips, simple_mach]),
1288
+ sep(),
1289
+ grp("View", [view_seg]),
1290
+ expand_grp,
1291
+ sep(),
1292
+ grp("Show", [...dir_chips, periodic_chip]),
1293
+ sep(),
1294
+ grp("Find", [search]),
1295
+ sep(),
1296
+ grp("Log", [copy, clear])
1297
+ ]
1298
+ ]);
1299
+ }
1300
+ function build_title_header() {
1301
+ return (0, _yuneta_gobj_js.createElement2)([
1302
+ "div",
1303
+ { class: "YDEV_TITLE" },
1304
+ [[
1305
+ "span",
1306
+ { class: "YDEV_TITLE_MAIN" },
1307
+ "Developer"
1308
+ ], [
1309
+ "span",
1310
+ { class: "YDEV_TITLE_SUB" },
1311
+ "yuno monitor · traffic & traces"
1312
+ ]]
1313
+ ]);
1314
+ }
1315
+ function build_dev_body() {
1316
+ return (0, _yuneta_gobj_js.createElement2)([
1317
+ "div",
1318
+ { class: "YDEV_BODY" },
1319
+ [
1320
+ build_control_bar(),
1321
+ [
1322
+ "div",
1323
+ {
1324
+ class: "YDEV_MUTED",
1325
+ id: "ydev-muted"
1326
+ },
1327
+ []
1328
+ ],
1329
+ [
1330
+ "div",
1331
+ {
1332
+ class: "YDEV_LOG",
1333
+ id: "developer-traffic-logger"
1334
+ },
1335
+ []
1336
+ ],
1337
+ [
1338
+ "div",
1339
+ {
1340
+ class: "YDEV_STATS",
1341
+ id: "ydev-stats"
1342
+ },
1343
+ []
1344
+ ]
1345
+ ]
1346
+ ]);
1347
+ }
1348
+ /******************************
1349
+ * Public API
1350
+ ******************************/
258
1351
  /************************************************************
259
1352
  * Was the developer window open last session? setup_dev()
260
1353
  * persists open_developer_window (1 on open, 0 on close), so
@@ -268,9 +1361,6 @@ function dev_window_was_open() {
268
1361
  * Apply ALL persisted developer-trace flags to the running
269
1362
  * yuno. Independent of the dev window — call it once at app
270
1363
  * 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
1364
  ************************************************************/
275
1365
  function apply_dev_traces() {
276
1366
  let traffic = Number((0, _yuneta_gobj_js.kw_get_local_storage_value)("trace_traffic", 0, false));
@@ -290,176 +1380,58 @@ function apply_dev_traces() {
290
1380
  (0, _yuneta_gobj_js.gobj_write_attr)((0, _yuneta_gobj_js.gobj_yuno)(), "trace_subscriptions", subscriptions);
291
1381
  (0, _yuneta_gobj_js.gobj_write_attr)((0, _yuneta_gobj_js.gobj_yuno)(), "no_poll", no_poll);
292
1382
  i18next.default.options.debug = i18n ? true : false;
1383
+ (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);
1384
+ (0, _yuneta_gobj_js.set_log_callback)(info_log);
293
1385
  }
294
1386
  /************************************************************
295
- * Open the developer panel inside a non-modal C_YUI_WINDOW
1387
+ * Open the developer monitor inside a non-modal C_YUI_WINDOW
296
1388
  * (title bar + maximize + close + resize).
297
1389
  *
298
1390
  * Shell-agnostic: the legacy C_YUI_MAIN shell has a
299
1391
  * '#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).
1392
+ * We pass that element when present, otherwise null — C_YUI_WINDOW
1393
+ * falls back to document.body by contract.
305
1394
  ************************************************************/
306
1395
  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
1396
  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>`;
1397
+ ensure_dev_style();
401
1398
  (0, _yuneta_gobj_js.gobj_create_service)("Developer-Window", "C_YUI_WINDOW", {
402
1399
  $parent: document.getElementById("top-layer") || null,
403
1400
  subscriber: null,
404
1401
  showMax: true,
405
1402
  modal: false,
406
- header: $dev_toolbar,
1403
+ header: build_title_header(),
1404
+ body: build_dev_body(),
1405
+ showFooter: false,
407
1406
  auto_save_size_and_position: true,
408
1407
  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>`,
1408
+ title: "Developer",
1409
+ icon: "yi-terminal",
1410
+ manager: (0, _yuneta_gobj_js.gobj_find_service)("__window_manager__", false) || null,
411
1411
  on_close: function() {
412
1412
  (0, _yuneta_gobj_js.kw_set_local_storage_value)("open_developer_window", 0);
413
1413
  }
414
1414
  }, self);
415
1415
  (0, _yuneta_gobj_js.kw_set_local_storage_value)("open_developer_window", 1);
1416
+ setTimeout(() => {
1417
+ refresh_dev_chrome();
1418
+ rerender_all();
1419
+ }, 0);
416
1420
  }
417
1421
  apply_dev_traces();
418
1422
  }
419
1423
  /************************************************************
420
- * Build the developer panel as a self-contained DOM subtree,
1424
+ * Build the developer monitor as a self-contained DOM subtree,
421
1425
  * to be mounted by the new declarative shell via
422
1426
  * yui_shell_show_modal (no C_YUI_WINDOW, no 'top-layer').
423
1427
  *
424
1428
  * Returns { $el, dispose }:
425
- * - $el: the panel element (header tabs + traffic logger
426
- * body + footer counters).
1429
+ * - $el: the panel element (control bar + log + stats).
427
1430
  * - dispose: stops the inter-event traffic trace; call it from
428
1431
  * 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
1432
  ************************************************************/
433
1433
  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
- ]);
1434
+ ensure_dev_style();
463
1435
  let dark = typeof document !== "undefined" && document.documentElement.getAttribute("data-theme") === "dark";
464
1436
  let surface = dark ? "#1f2733" : "#ffffff";
465
1437
  let fg = dark ? "#e8eaed" : "#0f172a";
@@ -468,53 +1440,15 @@ function build_dev_panel() {
468
1440
  "div",
469
1441
  {
470
1442
  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;"
1443
+ 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
1444
  },
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
- ]
1445
+ [build_dev_body()]
516
1446
  ]);
517
1447
  apply_dev_traces();
1448
+ setTimeout(() => {
1449
+ refresh_dev_chrome();
1450
+ rerender_all();
1451
+ }, 0);
518
1452
  let dispose = function() {
519
1453
  (0, _yuneta_gobj_js.gobj_write_attr)((0, _yuneta_gobj_js.gobj_yuno)(), "trace_inter_event", false);
520
1454
  };
@@ -524,7 +1458,7 @@ function build_dev_panel() {
524
1458
  };
525
1459
  }
526
1460
  //#endregion
527
- //#region yui_toolbar.js
1461
+ //#region src/yui_toolbar.js
528
1462
  function yui_toolbar(attrs = {}, items = []) {
529
1463
  const left_button = `
530
1464
  <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 +1523,7 @@ function yui_toolbar(attrs = {}, items = []) {
589
1523
  return $toolbar;
590
1524
  }
591
1525
  //#endregion
592
- //#region c_yui_main.js
1526
+ //#region src/c_yui_main.js
593
1527
  /***********************************************************************
594
1528
  * yui_main.js
595
1529
  *
@@ -625,11 +1559,11 @@ function yui_toolbar(attrs = {}, items = []) {
625
1559
  /***************************************************************
626
1560
  * Constants
627
1561
  ***************************************************************/
628
- var GCLASS_NAME$16 = "C_YUI_MAIN";
1562
+ var GCLASS_NAME$17 = "C_YUI_MAIN";
629
1563
  /***************************************************************
630
1564
  * Data
631
1565
  ***************************************************************/
632
- var attrs_table$16 = [
1566
+ var attrs_table$17 = [
633
1567
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of output events"),
634
1568
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "username", 0, "", "username logged"),
635
1569
  (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 +1579,16 @@ var attrs_table$16 = [
645
1579
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "color_user_logout", 0, "#FF4500", "OrangeRed - user logged out"),
646
1580
  (0, _yuneta_gobj_js.SDATA_END)()
647
1581
  ];
648
- var PRIVATE_DATA$16 = {};
649
- var __gclass__$16 = null;
1582
+ var PRIVATE_DATA$17 = {};
1583
+ var __gclass__$17 = null;
650
1584
  /******************************
651
1585
  * Framework Methods
652
1586
  ******************************/
653
1587
  /***************************************************************
654
1588
  * Framework Method: Create
655
1589
  ***************************************************************/
656
- function mt_create$16(gobj) {
657
- build_ui$13(gobj);
1590
+ function mt_create$17(gobj) {
1591
+ build_ui$14(gobj);
658
1592
  set_theme(gobj, (0, _yuneta_gobj_js.kw_get_local_storage_value)("theme", "light", true));
659
1593
  const subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
660
1594
  if (subscriber) (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
@@ -672,19 +1606,19 @@ function mt_stop$16(gobj) {}
672
1606
  /***************************************************************
673
1607
  * Framework Method: Destroy
674
1608
  ***************************************************************/
675
- function mt_destroy$16(gobj) {}
1609
+ function mt_destroy$17(gobj) {}
676
1610
  /***************************
677
1611
  * Local Methods
678
1612
  ***************************/
679
1613
  /************************************************************
680
1614
  * Build UI
681
1615
  ************************************************************/
682
- function build_ui$13(gobj) {
683
- const $layout = (0, _yuneta_gobj_js.createElement2)([
1616
+ function build_ui$14(gobj) {
1617
+ const $container = (0, _yuneta_gobj_js.createElement2)([
684
1618
  "div",
685
1619
  {
686
1620
  id: "root",
687
- class: "root"
1621
+ class: "C_YUI_MAIN root"
688
1622
  },
689
1623
  [
690
1624
  ["div", {
@@ -717,7 +1651,7 @@ function build_ui$13(gobj) {
717
1651
  ]
718
1652
  ]
719
1653
  ]);
720
- document.body.appendChild($layout);
1654
+ document.body.appendChild($container);
721
1655
  const iframe = document.getElementById("iframe-publi-page");
722
1656
  if (iframe) iframe.onload = () => {
723
1657
  const theme = (0, _yuneta_gobj_js.kw_get_local_storage_value)("theme", "light", true);
@@ -1631,17 +2565,17 @@ function ac_timeout$1(gobj, event, kw, src) {
1631
2565
  /***************************
1632
2566
  * FSM
1633
2567
  ***************************/
1634
- var gmt$16 = {
1635
- mt_create: mt_create$16,
2568
+ var gmt$17 = {
2569
+ mt_create: mt_create$17,
1636
2570
  mt_start: mt_start$16,
1637
2571
  mt_stop: mt_stop$16,
1638
- mt_destroy: mt_destroy$16
2572
+ mt_destroy: mt_destroy$17
1639
2573
  };
1640
2574
  /***************************************************************
1641
2575
  * Create the GClass
1642
2576
  ***************************************************************/
1643
- function create_gclass$16(gclass_name) {
1644
- if (__gclass__$16) {
2577
+ function create_gclass$17(gclass_name) {
2578
+ if (__gclass__$17) {
1645
2579
  (0, _yuneta_gobj_js.log_error)(`GClass ALREADY created: ${gclass_name}`);
1646
2580
  return -1;
1647
2581
  }
@@ -1725,7 +2659,7 @@ function create_gclass$16(gclass_name) {
1725
2659
  null
1726
2660
  ]
1727
2661
  ]]];
1728
- __gclass__$16 = (0, _yuneta_gobj_js.gclass_create)(gclass_name, [
2662
+ __gclass__$17 = (0, _yuneta_gobj_js.gclass_create)(gclass_name, [
1729
2663
  ["EV_LOGIN_ACCEPTED", 0],
1730
2664
  ["EV_LOGIN_DENIED", 0],
1731
2665
  ["EV_LOGIN_REFRESHED", 0],
@@ -1743,18 +2677,18 @@ function create_gclass$16(gclass_name) {
1743
2677
  ["EV_ON_CLOSE", 0],
1744
2678
  ["EV_DO_RECOVERY_PASSWORD", 0],
1745
2679
  ["EV_TIMEOUT", 0]
1746
- ], states, gmt$16, 0, attrs_table$16, PRIVATE_DATA$16, 0, 0, 0, 0);
1747
- if (!__gclass__$16) return -1;
2680
+ ], states, gmt$17, 0, attrs_table$17, PRIVATE_DATA$17, 0, 0, 0, 0);
2681
+ if (!__gclass__$17) return -1;
1748
2682
  return 0;
1749
2683
  }
1750
2684
  /***************************************************************
1751
2685
  * Register GClass
1752
2686
  ***************************************************************/
1753
2687
  function register_c_yui_main() {
1754
- return create_gclass$16(GCLASS_NAME$16);
2688
+ return create_gclass$17(GCLASS_NAME$17);
1755
2689
  }
1756
2690
  //#endregion
1757
- //#region c_yui_window.js
2691
+ //#region src/c_yui_window.js
1758
2692
  /***********************************************************************
1759
2693
  * c_yui_window.js
1760
2694
  *
@@ -1766,11 +2700,16 @@ function register_c_yui_main() {
1766
2700
  /***************************************************************
1767
2701
  * Constants
1768
2702
  ***************************************************************/
1769
- var GCLASS_NAME$15 = "C_YUI_WINDOW";
2703
+ var GCLASS_NAME$16 = "C_YUI_WINDOW";
2704
+ 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>";
2705
+ 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>";
2706
+ 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>";
2707
+ 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>";
2708
+ var MOBILE_MAX = 768;
1770
2709
  /***************************************************************
1771
2710
  * Data
1772
2711
  ***************************************************************/
1773
- var attrs_table$15 = [
2712
+ var attrs_table$16 = [
1774
2713
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of output events"),
1775
2714
  (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
2715
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_INTEGER, "x", 0, "300", "X position"),
@@ -1788,9 +2727,13 @@ var attrs_table$15 = [
1788
2727
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "showFooter", 0, true, "Show footer"),
1789
2728
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "openMaximized", 0, false, "Open the window maximized"),
1790
2729
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "showMax", 0, true, "Show maximize button"),
2730
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "showMin", 0, true, "Show minimize (roll-up / shade) button"),
1791
2731
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "maximized", 0, false, "Flag to indicate if maximized"),
1792
2732
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_JSON, "window_style", 0, "{}", "Override window style"),
1793
2733
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "on_close", 0, null, "Callback on destroy"),
2734
+ (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"),
2735
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_STRING, "title", 0, "", "Window title, shown on the dock chip"),
2736
+ (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
2737
  (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
2738
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_BOOLEAN, "modal", 0, false, "Enable modal mode"),
1796
2739
  (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 +2742,35 @@ var attrs_table$15 = [
1799
2742
  (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "win_resize_handler", 0, null, "Internal: native window 'resize' listener"),
1800
2743
  (0, _yuneta_gobj_js.SDATA_END)()
1801
2744
  ];
1802
- var PRIVATE_DATA$15 = { prevSize: 0 };
1803
- var __gclass__$15 = null;
2745
+ var PRIVATE_DATA$16 = {
2746
+ prevSize: 0,
2747
+ preShadeHeight: 0
2748
+ };
2749
+ var __gclass__$16 = null;
1804
2750
  /******************************
1805
2751
  * Framework Methods
1806
2752
  ******************************/
1807
2753
  /***************************************************************
1808
2754
  * Framework Method: Create
1809
2755
  ***************************************************************/
1810
- function mt_create$15(gobj) {
2756
+ function mt_create$16(gobj) {
1811
2757
  const subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
1812
2758
  if (subscriber) (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
1813
2759
  (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);
2760
+ build_ui$13(gobj);
2761
+ let manager = resolve_manager(gobj);
2762
+ if (manager) {
2763
+ let $c = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
2764
+ if ($c) $c.addEventListener("pointerdown", function() {
2765
+ let m = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "manager");
2766
+ if (m) (0, _yuneta_gobj_js.gobj_send_event)(m, "EV_FOCUS_WINDOW", { window: gobj }, gobj);
2767
+ }, true);
2768
+ (0, _yuneta_gobj_js.gobj_send_event)(manager, "EV_REGISTER_WINDOW", {
2769
+ window: gobj,
2770
+ title: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "title") || (0, _yuneta_gobj_js.gobj_short_name)(gobj),
2771
+ icon: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "icon") || ""
2772
+ }, gobj);
2773
+ }
1815
2774
  let on_win_resize = function() {
1816
2775
  handleResize(gobj);
1817
2776
  };
@@ -1835,7 +2794,9 @@ function mt_stop$15(gobj) {
1835
2794
  /***************************************************************
1836
2795
  * Framework Method: Destroy
1837
2796
  ***************************************************************/
1838
- function mt_destroy$15(gobj) {
2797
+ function mt_destroy$16(gobj) {
2798
+ let manager = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "manager");
2799
+ if (manager) (0, _yuneta_gobj_js.gobj_send_event)(manager, "EV_UNREGISTER_WINDOW", { window: gobj }, gobj);
1839
2800
  let on_win_resize = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "win_resize_handler");
1840
2801
  if (on_win_resize) {
1841
2802
  window.removeEventListener("resize", on_win_resize);
@@ -1847,9 +2808,97 @@ function mt_destroy$15(gobj) {
1847
2808
  * Local Methods
1848
2809
  ***************************/
1849
2810
  /************************************************************
2811
+ * Inject the window-chrome stylesheet once. Theme-aware via
2812
+ * Bulma scheme vars (flips on <html data-theme>); replaces the
2813
+ * old saturated `has-background-info` bar + forced black text.
2814
+ ************************************************************/
2815
+ function ensure_window_style() {
2816
+ if (document.getElementById("yui-window-style")) return;
2817
+ let css = `
2818
+ .yui-window-header {
2819
+ background: var(--bulma-scheme-main-bis);
2820
+ color: var(--bulma-text-strong);
2821
+ -webkit-user-select: none; user-select: none;
2822
+ }
2823
+ .yui-window-titlebar-controls { display: flex; align-items: center; gap: 2px; padding-left: 6px; }
2824
+ .yui-wc {
2825
+ width: 30px; height: 26px; display: inline-flex; align-items: center; justify-content: center;
2826
+ padding: 0; border: 0; background: transparent; color: var(--bulma-text); cursor: pointer;
2827
+ border-radius: 5px; -webkit-tap-highlight-color: transparent;
2828
+ }
2829
+ .yui-wc:hover { background: var(--bulma-scheme-main-ter); color: var(--bulma-text-strong); }
2830
+ .yui-wc.wc-close:hover { background: #e0364a; color: #fff; }
2831
+ .yui-wc svg { width: 15px; height: 15px; display: block; }
2832
+ .C_YUI_WINDOW.is-shaded .yui-window-body,
2833
+ .C_YUI_WINDOW.is-shaded .yui-window-footer { display: none !important; }
2834
+ .C_YUI_WINDOW.is-shaded { height: auto !important; }
2835
+ .yui-window-resize { color: var(--bulma-text-weak); opacity: 0.55; }
2836
+ .yui-window-resize:hover { opacity: 1; color: var(--bulma-text); }
2837
+ @media (max-width: ${MOBILE_MAX}px) {
2838
+ .C_YUI_WINDOW.is-mobile-sheet { border-radius: 0 !important; box-shadow: none !important; }
2839
+ .C_YUI_WINDOW.is-mobile-sheet .yui-window-resize { display: none !important; }
2840
+ .yui-wc { width: 42px; height: 38px; }
2841
+ .yui-wc.wc-max { display: none !important; }
2842
+ .yui-wc svg { width: 18px; height: 18px; }
2843
+ }
2844
+ `;
2845
+ let $style = document.createElement("style");
2846
+ $style.id = "yui-window-style";
2847
+ $style.textContent = css;
2848
+ document.head.appendChild($style);
2849
+ }
2850
+ /************************************************************
2851
+ * Below the mobile breakpoint the window is a full-screen
2852
+ * sheet (no float / drag / resize).
2853
+ ************************************************************/
2854
+ function is_mobile() {
2855
+ return (window.innerWidth || document.documentElement.offsetWidth) <= MOBILE_MAX;
2856
+ }
2857
+ /************************************************************
2858
+ * Resolve the optional `manager` attr (a gobj or a service
2859
+ * name) to a gobj, caching the resolution back into the attr.
2860
+ ************************************************************/
2861
+ function resolve_manager(gobj) {
2862
+ let m = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "manager");
2863
+ if (!m) return null;
2864
+ if (typeof m === "string") {
2865
+ m = (0, _yuneta_gobj_js.gobj_find_service)(m, false);
2866
+ (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "manager", m);
2867
+ }
2868
+ return m;
2869
+ }
2870
+ /************************************************************
2871
+ * Minimize: with a manager, send the window to the dock;
2872
+ * otherwise "shade" it in place. Called by the minimize button.
2873
+ ************************************************************/
2874
+ function do_minimize(gobj) {
2875
+ let manager = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "manager");
2876
+ if (manager) (0, _yuneta_gobj_js.gobj_send_event)(manager, "EV_MINIMIZE_WINDOW", { window: gobj }, gobj);
2877
+ else toggle_shade(gobj);
2878
+ }
2879
+ /************************************************************
2880
+ * Minimize as "shade": roll the window up to its title bar
2881
+ * in place (self-contained; no window manager needed). Toggles.
2882
+ ************************************************************/
2883
+ function toggle_shade(gobj) {
2884
+ let priv = gobj.priv;
2885
+ let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
2886
+ if (!$container) return;
2887
+ if ($container.classList.contains("is-shaded")) {
2888
+ $container.classList.remove("is-shaded");
2889
+ if (priv.preShadeHeight) $container.style.height = parseInt(priv.preShadeHeight) + "px";
2890
+ } else {
2891
+ priv.preShadeHeight = $container.getBoundingClientRect().height;
2892
+ $container.classList.add("is-shaded");
2893
+ $container.style.height = "auto";
2894
+ }
2895
+ }
2896
+ /************************************************************
1850
2897
  * Build UI
1851
2898
  ************************************************************/
1852
- function build_ui$12(gobj) {
2899
+ function build_ui$13(gobj) {
2900
+ ensure_window_style();
2901
+ let mobile = is_mobile();
1853
2902
  let header = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "header");
1854
2903
  if ((0, _yuneta_gobj_js.is_gobj)(header)) header = (0, _yuneta_gobj_js.gobj_read_attr)(header, "$container");
1855
2904
  let body = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "body");
@@ -1872,6 +2921,12 @@ function build_ui$12(gobj) {
1872
2921
  }
1873
2922
  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
2923
  if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "center")) rect = do_center(gobj, rect.x, rect.y, rect.width, rect.height);
2924
+ if (mobile) rect = {
2925
+ x: 0,
2926
+ y: 0,
2927
+ width: window.innerWidth || document.documentElement.offsetWidth,
2928
+ height: window.innerHeight || document.documentElement.offsetHeight
2929
+ };
1875
2930
  let window_style = {
1876
2931
  position: "fixed",
1877
2932
  "z-index": 3,
@@ -1893,14 +2948,14 @@ function build_ui$12(gobj) {
1893
2948
  let $container = (0, _yuneta_gobj_js.createElement2)([
1894
2949
  "div",
1895
2950
  {
1896
- class: "yui-window strong-shadow is-flex is-flex-direction-column",
2951
+ class: "C_YUI_WINDOW strong-shadow is-flex is-flex-direction-column" + (mobile ? " is-mobile-sheet" : ""),
1897
2952
  style: window_style
1898
2953
  },
1899
2954
  [
1900
2955
  [
1901
2956
  "div",
1902
2957
  {
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",
2958
+ 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
2959
  style: "border-bottom:1px solid var(--bulma-border); cursor:move; box-sizing: border-box;"
1905
2960
  },
1906
2961
  [[
@@ -1920,37 +2975,52 @@ function build_ui$12(gobj) {
1920
2975
  ], [
1921
2976
  "div",
1922
2977
  {
1923
- class: "is-flex-shrink-0 is-flex is-flex-nowrap is-align-items-flex-start",
2978
+ class: "yui-window-titlebar-controls is-flex-shrink-0",
1924
2979
  style: "position:relative; z-index:1; cursor:default;"
1925
2980
  },
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
- ]]
2981
+ [
2982
+ [
2983
+ "button",
2984
+ {
2985
+ class: "yui-wc wc-min",
2986
+ type: "button",
2987
+ "aria-label": "minimize",
2988
+ style: (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "showMin") ? "" : "display:none;"
2989
+ },
2990
+ WC_MIN,
2991
+ { click: (evt) => {
2992
+ evt.stopPropagation();
2993
+ do_minimize(gobj);
2994
+ } }
2995
+ ],
2996
+ [
2997
+ "button",
2998
+ {
2999
+ class: "yui-wc wc-max",
3000
+ type: "button",
3001
+ "aria-label": "maximize",
3002
+ style: (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "showMax") ? "" : "display:none;"
3003
+ },
3004
+ WC_MAX,
3005
+ { click: (evt) => {
3006
+ evt.stopPropagation();
3007
+ toggle(gobj);
3008
+ } }
3009
+ ],
3010
+ [
3011
+ "button",
3012
+ {
3013
+ class: "yui-wc wc-close",
3014
+ type: "button",
3015
+ "aria-label": "close"
3016
+ },
3017
+ WC_CLOSE,
3018
+ { click: (evt) => {
3019
+ evt.stopPropagation();
3020
+ close_window(gobj);
3021
+ } }
3022
+ ]
3023
+ ]
1954
3024
  ]],
1955
3025
  { pointerdown: (evt) => {
1956
3026
  if (evt.target.closest && evt.target.closest("button")) return;
@@ -2035,7 +3105,7 @@ function build_ui$12(gobj) {
2035
3105
  let $resizable_btn = (0, _yuneta_gobj_js.createElement2)([
2036
3106
  "div",
2037
3107
  {
2038
- class: "without-border",
3108
+ class: "without-border yui-window-resize",
2039
3109
  style: {
2040
3110
  cursor: "nwse-resize",
2041
3111
  display: "flex",
@@ -2062,7 +3132,7 @@ function build_ui$12(gobj) {
2062
3132
  if ($parent) $parent.appendChild($container);
2063
3133
  else document.body.appendChild($container);
2064
3134
  if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "openMaximized")) max$2(gobj);
2065
- else {
3135
+ else if (!mobile) {
2066
3136
  if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "content_size")) $container.style.height = "auto";
2067
3137
  if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "center")) {
2068
3138
  rect = $container.getBoundingClientRect();
@@ -2123,9 +3193,22 @@ function max$2(gobj) {
2123
3193
  $container.style.width = parseInt(rect.width) + "px";
2124
3194
  $container.style.height = parseInt(rect.height) + "px";
2125
3195
  (0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "maximized", true);
3196
+ set_max_icon(gobj, true);
2126
3197
  return rect;
2127
3198
  }
2128
3199
  /************************************************************
3200
+ * Swap the maximize/restore glyph to match the state.
3201
+ ************************************************************/
3202
+ function set_max_icon(gobj, maximized) {
3203
+ let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
3204
+ if (!$container) return;
3205
+ let $btn = $container.querySelector(".wc-max");
3206
+ if ($btn) {
3207
+ $btn.innerHTML = maximized ? WC_RESTORE : WC_MAX;
3208
+ $btn.setAttribute("aria-label", maximized ? "restore" : "maximize");
3209
+ }
3210
+ }
3211
+ /************************************************************
2129
3212
  *
2130
3213
  ************************************************************/
2131
3214
  function min$1(gobj) {
@@ -2138,12 +3221,14 @@ function min$1(gobj) {
2138
3221
  $container.style.width = parseInt(rect.width) + "px";
2139
3222
  $container.style.height = parseInt(rect.height) + "px";
2140
3223
  (0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "maximized", false);
3224
+ set_max_icon(gobj, false);
2141
3225
  return rect;
2142
3226
  }
2143
3227
  /************************************************************
2144
3228
  * handlers moving
2145
3229
  ************************************************************/
2146
3230
  function mvStart(gobj, evt) {
3231
+ if (is_mobile()) return;
2147
3232
  let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
2148
3233
  let window_rect = $container.getBoundingClientRect();
2149
3234
  let x = evt.screenX;
@@ -2300,6 +3385,17 @@ function do_center(gobj, x, y, width, height) {
2300
3385
  function handleResize(gobj) {
2301
3386
  let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
2302
3387
  if (!$container) return;
3388
+ if (is_mobile()) {
3389
+ $container.classList.add("is-mobile-sheet");
3390
+ let vw = window.innerWidth || document.documentElement.offsetWidth;
3391
+ let vh = window.innerHeight || document.documentElement.offsetHeight;
3392
+ $container.style.left = "0px";
3393
+ $container.style.top = "0px";
3394
+ $container.style.width = vw + "px";
3395
+ $container.style.height = vh + "px";
3396
+ return;
3397
+ }
3398
+ $container.classList.remove("is-mobile-sheet");
2303
3399
  if ((0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "maximized") === true) {
2304
3400
  let r = do_fix_dimension_to_screen(gobj, 0, 0, 1e4, 1e4);
2305
3401
  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 +3440,28 @@ function ac_show$11(gobj, event, kw, src) {
2344
3440
  function ac_hide$11(gobj, event, kw, src) {
2345
3441
  return 0;
2346
3442
  }
3443
+ /************************************************************
3444
+ * EV_CLOSE_WINDOW — close from outside (e.g. the dock chip ✕),
3445
+ * the same teardown as the title-bar close button.
3446
+ ************************************************************/
3447
+ function ac_close(gobj, event, kw, src) {
3448
+ close_window(gobj);
3449
+ return 0;
3450
+ }
2347
3451
  /***************************
2348
3452
  * FSM
2349
3453
  ***************************/
2350
- var gmt$15 = {
2351
- mt_create: mt_create$15,
3454
+ var gmt$16 = {
3455
+ mt_create: mt_create$16,
2352
3456
  mt_start: mt_start$15,
2353
3457
  mt_stop: mt_stop$15,
2354
- mt_destroy: mt_destroy$15
3458
+ mt_destroy: mt_destroy$16
2355
3459
  };
2356
3460
  /***************************************************************
2357
3461
  * Create the GClass
2358
3462
  ***************************************************************/
2359
- function create_gclass$15(gclass_name) {
2360
- if (__gclass__$15) {
3463
+ function create_gclass$16(gclass_name) {
3464
+ if (__gclass__$16) {
2361
3465
  (0, _yuneta_gobj_js.log_error)(`GClass ALREADY created: ${gclass_name}`);
2362
3466
  return -1;
2363
3467
  }
@@ -2381,28 +3485,481 @@ function create_gclass$15(gclass_name) {
2381
3485
  "EV_HIDE",
2382
3486
  ac_hide$11,
2383
3487
  null
3488
+ ],
3489
+ [
3490
+ "EV_CLOSE_WINDOW",
3491
+ ac_close,
3492
+ null
2384
3493
  ]
2385
3494
  ]]];
2386
- __gclass__$15 = (0, _yuneta_gobj_js.gclass_create)(gclass_name, [
3495
+ __gclass__$16 = (0, _yuneta_gobj_js.gclass_create)(gclass_name, [
2387
3496
  ["EV_WINDOW_TO_CLOSE", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
2388
3497
  ["EV_WINDOW_MOVED", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
2389
3498
  ["EV_WINDOW_RESIZED", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_NO_WARN_SUBS],
2390
3499
  ["EV_RESIZE", 0],
2391
3500
  ["EV_REFRESH", 0],
2392
3501
  ["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;
3502
+ ["EV_HIDE", 0],
3503
+ ["EV_CLOSE_WINDOW", 0]
3504
+ ], states, gmt$16, 0, attrs_table$16, PRIVATE_DATA$16, 0, 0, 0, 0);
3505
+ if (!__gclass__$16) return -1;
2396
3506
  return 0;
2397
3507
  }
2398
3508
  /***************************************************************
2399
3509
  * Register GClass
2400
3510
  ***************************************************************/
2401
3511
  function register_c_yui_window() {
3512
+ return create_gclass$16(GCLASS_NAME$16);
3513
+ }
3514
+ //#endregion
3515
+ //#region src/c_yui_window_manager.js
3516
+ /***********************************************************************
3517
+ * c_yui_window_manager.js
3518
+ *
3519
+ * Window manager / dock (taskbar).
3520
+ *
3521
+ * A light registry + dock strip for C_YUI_WINDOW instances. Windows
3522
+ * opt in via their `manager` attr: on create they REGISTER, on
3523
+ * destroy they UNREGISTER, their minimize button MINIMIZEs (roll to
3524
+ * the dock instead of shading in place), and any pointer press
3525
+ * FOCUSes (raise z-order + highlight the dock chip).
3526
+ *
3527
+ * The manager never owns window lifecycle: it only toggles a
3528
+ * window's `$container` display / z-index and reflects state in the
3529
+ * dock. Closing stays the window's own affordance (its ✕ →
3530
+ * gobj_destroy → EV_UNREGISTER_WINDOW removes the chip).
3531
+ *
3532
+ * Orthogonal to C_YUI_PAGER (page-stack): a window may host a pager
3533
+ * in its body; the manager only arranges the windows themselves.
3534
+ *
3535
+ * The dock placement is configurable via `dock_mode`:
3536
+ * - "floating" (default) a detached bar pinned to a corner
3537
+ * (`dock_corner`), hovering over the content.
3538
+ * - "inline" a full-width taskbar row mounted inside a layout
3539
+ * container (`inline_selector`, e.g. a shell zone).
3540
+ * - "responsive" floating on wide viewports, inline on narrow ones
3541
+ * (`responsive_query`) — so on mobile it lives above
3542
+ * the bottom menu instead of covering it.
3543
+ *
3544
+ * Copyright (c) 2026, ArtGins.
3545
+ * All Rights Reserved.
3546
+ ***********************************************************************/
3547
+ /***************************************************************
3548
+ * Constants
3549
+ ***************************************************************/
3550
+ var GCLASS_NAME$15 = "C_YUI_WINDOW_MANAGER";
3551
+ var DOCK_Z = 1e6;
3552
+ 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>";
3553
+ /***************************************************************
3554
+ * Data
3555
+ ***************************************************************/
3556
+ var attrs_table$15 = [
3557
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "subscriber", 0, null, "Subscriber of output events"),
3558
+ (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)"),
3559
+ (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"),
3560
+ (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)"),
3561
+ (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'"),
3562
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "$parent", 0, null, "Where the floating dock mounts (default document.body)"),
3563
+ (0, _yuneta_gobj_js.SDATA)(_yuneta_gobj_js.data_type_t.DTP_POINTER, "$container", 0, null, "Internal: the dock element"),
3564
+ (0, _yuneta_gobj_js.SDATA_END)()
3565
+ ];
3566
+ var PRIVATE_DATA$15 = {
3567
+ windows: null,
3568
+ z: 0,
3569
+ mql: null,
3570
+ mql_handler: null
3571
+ };
3572
+ var __gclass__$15 = null;
3573
+ /******************************
3574
+ * Framework Methods
3575
+ ******************************/
3576
+ /***************************************************************
3577
+ * Framework Method: Create
3578
+ ***************************************************************/
3579
+ function mt_create$15(gobj) {
3580
+ let priv = gobj.priv;
3581
+ priv.windows = [];
3582
+ priv.z = 10;
3583
+ if (((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "dock_mode") || "floating") === "responsive") {
3584
+ let q = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "responsive_query") || "(max-width: 768px)";
3585
+ priv.mql = window.matchMedia(q);
3586
+ priv.mql_handler = () => {
3587
+ apply_placement(gobj);
3588
+ };
3589
+ priv.mql.addEventListener("change", priv.mql_handler);
3590
+ }
3591
+ build_dock(gobj);
3592
+ const subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
3593
+ if (subscriber) (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
3594
+ }
3595
+ /***************************************************************
3596
+ * Framework Method: Destroy
3597
+ ***************************************************************/
3598
+ function mt_destroy$15(gobj) {
3599
+ let priv = gobj.priv;
3600
+ if (priv.mql && priv.mql_handler) {
3601
+ priv.mql.removeEventListener("change", priv.mql_handler);
3602
+ priv.mql = null;
3603
+ priv.mql_handler = null;
3604
+ }
3605
+ destroy_dock(gobj);
3606
+ }
3607
+ /***************************
3608
+ * Local Methods
3609
+ ***************************/
3610
+ /************************************************************
3611
+ * Inject the dock stylesheet once (theme-aware).
3612
+ ************************************************************/
3613
+ function ensure_dock_style() {
3614
+ if (document.getElementById("yui-dock-style")) return;
3615
+ let css = `
3616
+ .yui-dock {
3617
+ z-index: ${DOCK_Z};
3618
+ display: flex; gap: 6px; align-items: center; padding: 5px 8px;
3619
+ overflow-x: auto;
3620
+ background: var(--bulma-scheme-main-bis); color: var(--bulma-text);
3621
+ border: 1px solid var(--bulma-border);
3622
+ font-family: var(--bulma-family-primary, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
3623
+ }
3624
+ .yui-dock.is-empty { display: none; }
3625
+
3626
+ /* Floating: a detached bar hovering over the content, pinned to a corner. */
3627
+ .yui-dock--floating {
3628
+ position: fixed;
3629
+ max-width: calc(100vw - 24px);
3630
+ border-radius: 10px;
3631
+ box-shadow: 0 6px 22px rgba(0,0,0,0.25);
3632
+ }
3633
+ .yui-dock--bottom-left { bottom: 12px; left: 12px; }
3634
+ .yui-dock--bottom-right { bottom: 12px; right: 12px; }
3635
+ .yui-dock--top-left { top: 12px; left: 12px; }
3636
+ .yui-dock--top-right { top: 12px; right: 12px; }
3637
+
3638
+ /* Inline: flows inside a layout zone as a full-width taskbar row (the zone
3639
+ * already provides the separating border, so the bar itself stays flat). */
3640
+ .yui-dock--inline {
3641
+ position: relative;
3642
+ width: 100%; max-width: 100%;
3643
+ border: 0;
3644
+ border-radius: 0;
3645
+ box-shadow: none;
3646
+ }
3647
+ .yui-dock-chip {
3648
+ display: inline-flex; align-items: center; gap: 7px; font: inherit; font-size: 12px;
3649
+ padding: 4px 5px 4px 11px; border: 1px solid var(--bulma-border); border-radius: 7px;
3650
+ background: var(--bulma-scheme-main); color: var(--bulma-text); cursor: pointer; white-space: nowrap;
3651
+ }
3652
+ .yui-dock-chip:hover { border-color: var(--bulma-text-weak); }
3653
+ .yui-dock-chip.is-active { border-color: #2563eb; color: #2563eb; background: rgba(37,99,235,0.10); font-weight: 600; }
3654
+ .yui-dock-chip.is-min { opacity: 0.65; }
3655
+ .yui-dock-dot { width: 8px; height: 8px; border-radius: 50%; background: #22c55e; flex: 0 0 auto; }
3656
+ .yui-dock-chip.is-min .yui-dock-dot { background: var(--bulma-text-weak); }
3657
+ .yui-dock-icon { display: inline-flex; align-items: center; justify-content: center; width: 15px; height: 15px; font-size: 14px; flex: 0 0 auto; }
3658
+ .yui-dock-icon svg { width: 14px; height: 14px; display: block; }
3659
+ .yui-dock-icon i { line-height: 1; }
3660
+ .yui-dock-label { flex: 0 0 auto; }
3661
+ .yui-dock-close {
3662
+ width: 18px; height: 18px; display: inline-flex; align-items: center; justify-content: center;
3663
+ padding: 0; margin-left: 1px; border: 0; border-radius: 4px;
3664
+ background: transparent; color: var(--bulma-text-weak); cursor: pointer; flex: 0 0 auto;
3665
+ }
3666
+ .yui-dock-close:hover { background: #e0364a; color: #fff; }
3667
+ .yui-dock-close svg { width: 11px; height: 11px; display: block; }
3668
+ :root[data-theme="dark"] .yui-dock-chip.is-active { border-color: #60a5fa; color: #93c5fd; background: rgba(96,165,250,0.16); }
3669
+ `;
3670
+ let $style = document.createElement("style");
3671
+ $style.id = "yui-dock-style";
3672
+ $style.textContent = css;
3673
+ document.head.appendChild($style);
3674
+ }
3675
+ /************************************************************
3676
+ * Build the dock element (empty, hidden until a window
3677
+ * registers).
3678
+ ************************************************************/
3679
+ function build_dock(gobj) {
3680
+ ensure_dock_style();
3681
+ (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", (0, _yuneta_gobj_js.createElement2)([
3682
+ "div",
3683
+ { class: "yui-dock is-empty" },
3684
+ []
3685
+ ]));
3686
+ apply_placement(gobj);
3687
+ }
3688
+ /************************************************************
3689
+ * Whether the dock should render inline (in a layout zone)
3690
+ * rather than floating, given the mode and — for
3691
+ * 'responsive' — the current viewport.
3692
+ ************************************************************/
3693
+ function want_inline_placement(gobj) {
3694
+ let mode = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "dock_mode") || "floating";
3695
+ if (mode === "inline") return true;
3696
+ if (mode === "responsive") {
3697
+ let priv = gobj.priv;
3698
+ return !!(priv.mql && priv.mql.matches);
3699
+ }
3700
+ return false;
3701
+ }
3702
+ /************************************************************
3703
+ * Place (or re-place) the dock: pick floating vs inline,
3704
+ * set the modifier classes, and mount it in the right
3705
+ * container. Also re-attaches the dock if it got detached
3706
+ * (e.g. a shell that replaced its parent's children after
3707
+ * the dock was first mounted, or the responsive breakpoint
3708
+ * flipped). Idempotent — safe to call on every register and
3709
+ * on every media-query change.
3710
+ ************************************************************/
3711
+ function apply_placement(gobj) {
3712
+ let $dock = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
3713
+ if (!$dock) return;
3714
+ let $inline = null;
3715
+ if (want_inline_placement(gobj)) {
3716
+ let sel = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "inline_selector");
3717
+ if (sel) $inline = document.querySelector(sel);
3718
+ }
3719
+ let inline = !!$inline;
3720
+ $dock.classList.toggle("yui-dock--inline", inline);
3721
+ $dock.classList.toggle("yui-dock--floating", !inline);
3722
+ let corner = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "dock_corner") || "bottom-left";
3723
+ for (let c of [
3724
+ "top-left",
3725
+ "top-right",
3726
+ "bottom-left",
3727
+ "bottom-right"
3728
+ ]) $dock.classList.toggle(`yui-dock--${c}`, !inline && c === corner);
3729
+ let target = inline ? $inline : (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$parent") || document.body;
3730
+ if ($dock.parentNode !== target) target.appendChild($dock);
3731
+ }
3732
+ /************************************************************
3733
+ * Destroy the dock element.
3734
+ ************************************************************/
3735
+ function destroy_dock(gobj) {
3736
+ let $dock = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
3737
+ if ($dock) {
3738
+ if ($dock.parentNode) $dock.parentNode.removeChild($dock);
3739
+ (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", null);
3740
+ }
3741
+ }
3742
+ /************************************************************
3743
+ * The chip's leading mark: a per-type icon if the window
3744
+ * provides one (a `yi-*` class name, or inline SVG/HTML), else
3745
+ * the status dot. Returns a createElement2() spec.
3746
+ ************************************************************/
3747
+ function chip_lead(icon) {
3748
+ if (!icon) return [
3749
+ "span",
3750
+ { class: "yui-dock-dot" },
3751
+ ""
3752
+ ];
3753
+ if (icon.charAt(0) === "<") return [
3754
+ "span",
3755
+ { class: "yui-dock-icon" },
3756
+ icon
3757
+ ];
3758
+ return [
3759
+ "span",
3760
+ { class: "yui-dock-icon" },
3761
+ [["i", { class: icon }]]
3762
+ ];
3763
+ }
3764
+ /************************************************************
3765
+ * Find a registry entry by its window gobj.
3766
+ ************************************************************/
3767
+ function find_entry(priv, win) {
3768
+ for (let e of priv.windows) if (e.gobj === win) return e;
3769
+ return null;
3770
+ }
3771
+ /************************************************************
3772
+ * Show / hide the empty state.
3773
+ ************************************************************/
3774
+ function sync_dock_visibility(gobj) {
3775
+ let priv = gobj.priv;
3776
+ let $dock = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
3777
+ if ($dock) $dock.classList.toggle("is-empty", priv.windows.length === 0);
3778
+ }
3779
+ /************************************************************
3780
+ * Mark one entry active (raised), the rest inactive.
3781
+ ************************************************************/
3782
+ function set_active(gobj, entry) {
3783
+ let priv = gobj.priv;
3784
+ for (let e of priv.windows) if (e.$chip) e.$chip.classList.toggle("is-active", e === entry);
3785
+ }
3786
+ /************************************************************
3787
+ * Bring a window to front and mark it active/visible.
3788
+ ************************************************************/
3789
+ function focus_entry(gobj, entry) {
3790
+ let priv = gobj.priv;
3791
+ let $win = (0, _yuneta_gobj_js.gobj_read_attr)(entry.gobj, "$container");
3792
+ if ($win) {
3793
+ if (entry.minimized) {
3794
+ $win.style.removeProperty("display");
3795
+ entry.minimized = false;
3796
+ if (entry.$chip) entry.$chip.classList.remove("is-min");
3797
+ }
3798
+ priv.z += 1;
3799
+ $win.style.zIndex = priv.z;
3800
+ }
3801
+ set_active(gobj, entry);
3802
+ }
3803
+ /************************************************************
3804
+ * Send a window to the dock (hidden, chip dimmed).
3805
+ ************************************************************/
3806
+ function minimize_entry(gobj, entry) {
3807
+ let $win = (0, _yuneta_gobj_js.gobj_read_attr)(entry.gobj, "$container");
3808
+ if ($win) $win.style.setProperty("display", "none", "important");
3809
+ entry.minimized = true;
3810
+ if (entry.$chip) {
3811
+ entry.$chip.classList.add("is-min");
3812
+ entry.$chip.classList.remove("is-active");
3813
+ }
3814
+ }
3815
+ /************************************************************
3816
+ * Dock chip click: taskbar toggle — restore+focus a
3817
+ * minimized/inactive window, minimize the active one.
3818
+ ************************************************************/
3819
+ function on_chip_click(gobj, entry) {
3820
+ if (entry.minimized || !entry.$chip.classList.contains("is-active")) focus_entry(gobj, entry);
3821
+ else minimize_entry(gobj, entry);
3822
+ }
3823
+ /***************************
3824
+ * Actions
3825
+ ***************************/
3826
+ /************************************************************
3827
+ * EV_REGISTER_WINDOW { window, title }
3828
+ ************************************************************/
3829
+ function ac_register_window(gobj, event, kw, src) {
3830
+ let priv = gobj.priv;
3831
+ let win = kw.window;
3832
+ if (!win || find_entry(priv, win)) return 0;
3833
+ apply_placement(gobj);
3834
+ let title = kw.title || (0, _yuneta_gobj_js.gobj_name)(win) || "window";
3835
+ let entry = {
3836
+ gobj: win,
3837
+ $chip: null,
3838
+ minimized: false
3839
+ };
3840
+ let $chip = (0, _yuneta_gobj_js.createElement2)([
3841
+ "div",
3842
+ {
3843
+ class: "yui-dock-chip",
3844
+ role: "button",
3845
+ tabindex: "0",
3846
+ title
3847
+ },
3848
+ [
3849
+ chip_lead(kw.icon),
3850
+ [
3851
+ "span",
3852
+ { class: "yui-dock-label" },
3853
+ title
3854
+ ],
3855
+ [
3856
+ "button",
3857
+ {
3858
+ class: "yui-dock-close",
3859
+ type: "button",
3860
+ "aria-label": "close"
3861
+ },
3862
+ WC_X,
3863
+ { click: (evt) => {
3864
+ evt.stopPropagation();
3865
+ (0, _yuneta_gobj_js.gobj_send_event)(entry.gobj, "EV_CLOSE_WINDOW", {}, gobj);
3866
+ } }
3867
+ ]
3868
+ ],
3869
+ { click: (evt) => {
3870
+ evt.stopPropagation();
3871
+ on_chip_click(gobj, entry);
3872
+ } }
3873
+ ]);
3874
+ entry.$chip = $chip;
3875
+ priv.windows.push(entry);
3876
+ let $dock = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
3877
+ if ($dock) $dock.appendChild($chip);
3878
+ sync_dock_visibility(gobj);
3879
+ focus_entry(gobj, entry);
3880
+ return 0;
3881
+ }
3882
+ /************************************************************
3883
+ * EV_UNREGISTER_WINDOW { window }
3884
+ ************************************************************/
3885
+ function ac_unregister_window(gobj, event, kw, src) {
3886
+ let priv = gobj.priv;
3887
+ let entry = find_entry(priv, kw.window);
3888
+ if (!entry) return 0;
3889
+ if (entry.$chip && entry.$chip.parentNode) entry.$chip.parentNode.removeChild(entry.$chip);
3890
+ priv.windows = priv.windows.filter((e) => e !== entry);
3891
+ sync_dock_visibility(gobj);
3892
+ return 0;
3893
+ }
3894
+ /************************************************************
3895
+ * EV_MINIMIZE_WINDOW { window }
3896
+ ************************************************************/
3897
+ function ac_minimize_window(gobj, event, kw, src) {
3898
+ let entry = find_entry(gobj.priv, kw.window);
3899
+ if (entry) minimize_entry(gobj, entry);
3900
+ return 0;
3901
+ }
3902
+ /************************************************************
3903
+ * EV_FOCUS_WINDOW { window }
3904
+ ************************************************************/
3905
+ function ac_focus_window(gobj, event, kw, src) {
3906
+ let entry = find_entry(gobj.priv, kw.window);
3907
+ if (entry) focus_entry(gobj, entry);
3908
+ return 0;
3909
+ }
3910
+ /***************************
3911
+ * FSM
3912
+ ***************************/
3913
+ var gmt$15 = {
3914
+ mt_create: mt_create$15,
3915
+ mt_destroy: mt_destroy$15
3916
+ };
3917
+ /***************************************************************
3918
+ * Create the GClass
3919
+ ***************************************************************/
3920
+ function create_gclass$15(gclass_name) {
3921
+ if (__gclass__$15) {
3922
+ (0, _yuneta_gobj_js.log_error)(`GClass ALREADY created: ${gclass_name}`);
3923
+ return -1;
3924
+ }
3925
+ __gclass__$15 = (0, _yuneta_gobj_js.gclass_create)(gclass_name, [
3926
+ ["EV_REGISTER_WINDOW", 0],
3927
+ ["EV_UNREGISTER_WINDOW", 0],
3928
+ ["EV_MINIMIZE_WINDOW", 0],
3929
+ ["EV_FOCUS_WINDOW", 0]
3930
+ ], [["ST_IDLE", [
3931
+ [
3932
+ "EV_REGISTER_WINDOW",
3933
+ ac_register_window,
3934
+ null
3935
+ ],
3936
+ [
3937
+ "EV_UNREGISTER_WINDOW",
3938
+ ac_unregister_window,
3939
+ null
3940
+ ],
3941
+ [
3942
+ "EV_MINIMIZE_WINDOW",
3943
+ ac_minimize_window,
3944
+ null
3945
+ ],
3946
+ [
3947
+ "EV_FOCUS_WINDOW",
3948
+ ac_focus_window,
3949
+ null
3950
+ ]
3951
+ ]]], gmt$15, 0, attrs_table$15, PRIVATE_DATA$15, 0, 0, 0, 0);
3952
+ if (!__gclass__$15) return -1;
3953
+ return 0;
3954
+ }
3955
+ /***************************************************************
3956
+ * Register GClass
3957
+ ***************************************************************/
3958
+ function register_c_yui_window_manager() {
2402
3959
  return create_gclass$15(GCLASS_NAME$15);
2403
3960
  }
2404
3961
  //#endregion
2405
- //#region c_yui_tabs.js
3962
+ //#region src/c_yui_tabs.js
2406
3963
  /***********************************************************************
2407
3964
  * c_yui_tabs.js
2408
3965
  *
@@ -2456,7 +4013,7 @@ var __gclass__$14 = null;
2456
4013
  * Framework Method: Create
2457
4014
  ***************************************************************/
2458
4015
  function mt_create$14(gobj) {
2459
- build_ui$11(gobj);
4016
+ build_ui$12(gobj);
2460
4017
  let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
2461
4018
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
2462
4019
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
@@ -2498,11 +4055,11 @@ function mt_child_removed(gobj, child) {
2498
4055
  /************************************************************
2499
4056
  * Build UI
2500
4057
  ************************************************************/
2501
- function build_ui$11(gobj) {
4058
+ function build_ui$12(gobj) {
2502
4059
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", (0, _yuneta_gobj_js.createElement2)([
2503
4060
  "div",
2504
4061
  {
2505
- class: `yui_tabs`,
4062
+ class: `C_YUI_TABS`,
2506
4063
  style: "height:100%; display:flex; flex-direction:column;"
2507
4064
  },
2508
4065
  [[
@@ -2693,7 +4250,7 @@ function register_c_yui_tabs() {
2693
4250
  return create_gclass$14(GCLASS_NAME$14);
2694
4251
  }
2695
4252
  //#endregion
2696
- //#region pager_helpers.js
4253
+ //#region src/pager_helpers.js
2697
4254
  /***************************************************************
2698
4255
  * Top entry or null.
2699
4256
  ***************************************************************/
@@ -2747,7 +4304,7 @@ function pager_back_action(stack, back_on_root) {
2747
4304
  return { type: "noop" };
2748
4305
  }
2749
4306
  //#endregion
2750
- //#region c_yui_pager.js
4307
+ //#region src/c_yui_pager.js
2751
4308
  /***********************************************************************
2752
4309
  * c_yui_pager.js
2753
4310
  *
@@ -2799,7 +4356,7 @@ var __gclass__$13 = null;
2799
4356
  function mt_create$13(gobj) {
2800
4357
  let priv = gobj.priv;
2801
4358
  priv.stack = [];
2802
- build_ui$10(gobj);
4359
+ build_ui$11(gobj);
2803
4360
  let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
2804
4361
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
2805
4362
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
@@ -2826,11 +4383,11 @@ function mt_destroy$13(gobj) {
2826
4383
  /************************************************************
2827
4384
  * Build UI
2828
4385
  ************************************************************/
2829
- function build_ui$10(gobj) {
4386
+ function build_ui$11(gobj) {
2830
4387
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", (0, _yuneta_gobj_js.createElement2)([
2831
4388
  "div",
2832
4389
  {
2833
- class: "yui-pager",
4390
+ class: "C_YUI_PAGER",
2834
4391
  style: "height:100%; display:flex; flex-direction:column;"
2835
4392
  },
2836
4393
  [[
@@ -3180,7 +4737,7 @@ function register_c_yui_pager() {
3180
4737
  return create_gclass$13(GCLASS_NAME$13);
3181
4738
  }
3182
4739
  //#endregion
3183
- //#region wizard_helpers.js
4740
+ //#region src/wizard_helpers.js
3184
4741
  /***********************************************************************
3185
4742
  * wizard_helpers.js
3186
4743
  *
@@ -3269,7 +4826,7 @@ function wizard_should_validate(linear, has_gobj) {
3269
4826
  return !!linear && !!has_gobj;
3270
4827
  }
3271
4828
  //#endregion
3272
- //#region c_yui_wizard.js
4829
+ //#region src/c_yui_wizard.js
3273
4830
  /***********************************************************************
3274
4831
  * c_yui_wizard.js
3275
4832
  *
@@ -3331,7 +4888,7 @@ function mt_create$12(gobj) {
3331
4888
  priv.idx = 0;
3332
4889
  priv.acc = null;
3333
4890
  priv.awaiting = false;
3334
- build_ui$9(gobj);
4891
+ build_ui$10(gobj);
3335
4892
  let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
3336
4893
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
3337
4894
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
@@ -3358,11 +4915,11 @@ function mt_destroy$12(gobj) {
3358
4915
  /************************************************************
3359
4916
  * Build UI
3360
4917
  ************************************************************/
3361
- function build_ui$9(gobj) {
4918
+ function build_ui$10(gobj) {
3362
4919
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", (0, _yuneta_gobj_js.createElement2)([
3363
4920
  "div",
3364
4921
  {
3365
- class: "yui-wizard",
4922
+ class: "C_YUI_WIZARD",
3366
4923
  style: "height:100%; display:flex; flex-direction:column;"
3367
4924
  },
3368
4925
  [
@@ -3798,7 +5355,7 @@ function register_c_yui_wizard() {
3798
5355
  return create_gclass$12(GCLASS_NAME$12);
3799
5356
  }
3800
5357
  //#endregion
3801
- //#region c_yui_form.js
5358
+ //#region src/c_yui_form.js
3802
5359
  /***********************************************************************
3803
5360
  * c_yui_form.js
3804
5361
  *
@@ -3850,7 +5407,7 @@ function mt_create$11(gobj) {
3850
5407
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
3851
5408
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
3852
5409
  (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);
5410
+ build_ui$9(gobj);
3854
5411
  }
3855
5412
  /************************************************************
3856
5413
  * Framework Method destroy
@@ -3937,11 +5494,11 @@ async function readClipboard$1(gobj) {
3937
5494
  /************************************************************
3938
5495
  * Build UI
3939
5496
  ************************************************************/
3940
- function build_ui$8(gobj) {
5497
+ function build_ui$9(gobj) {
3941
5498
  let $container = (0, _yuneta_gobj_js.createElement2)([
3942
5499
  "div",
3943
5500
  {
3944
- class: "yui-form-container is-flex is-flex-direction-row overscroll-contain",
5501
+ class: "C_YUI_FORM is-flex is-flex-direction-row overscroll-contain",
3945
5502
  style: "height:100%; width:100%; box-sizing:border-box;"
3946
5503
  },
3947
5504
  [[
@@ -5801,7 +7358,7 @@ function register_c_yui_form() {
5801
7358
  return create_gclass$11(GCLASS_NAME$11);
5802
7359
  }
5803
7360
  //#endregion
5804
- //#region c_yui_routing.js
7361
+ //#region src/c_yui_routing.js
5805
7362
  /***********************************************************************
5806
7363
  * ui_routing.js
5807
7364
  *
@@ -6008,7 +7565,7 @@ function build_layout(gobj) {
6008
7565
  const $container = (0, _yuneta_gobj_js.createElement2)([
6009
7566
  "div",
6010
7567
  {
6011
- class: "container-grid-2col",
7568
+ class: "C_YUI_ROUTING",
6012
7569
  style: ""
6013
7570
  },
6014
7571
  [[
@@ -6368,7 +7925,7 @@ function register_c_yui_routing() {
6368
7925
  return create_gclass$10(GCLASS_NAME$10);
6369
7926
  }
6370
7927
  //#endregion
6371
- //#region lib_maplibre.js
7928
+ //#region src/lib_maplibre.js
6372
7929
  /***********************************************************************
6373
7930
  * lib_maplibre.js
6374
7931
  *
@@ -6513,7 +8070,7 @@ var MarkerControl = class {
6513
8070
  }
6514
8071
  };
6515
8072
  //#endregion
6516
- //#region c_yui_map.js
8073
+ //#region src/c_yui_map.js
6517
8074
  /***********************************************************************
6518
8075
  * c_yui_map.js
6519
8076
  *
@@ -6572,7 +8129,9 @@ function mt_create$9(gobj) {
6572
8129
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
6573
8130
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
6574
8131
  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") });
8132
+ let $map = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$map");
8133
+ if ($map) $map.classList.add(GCLASS_NAME$9);
8134
+ Object.assign(map_settings, { container: $map });
6576
8135
  priv.onClick = (ev) => {
6577
8136
  onClick(gobj, ev);
6578
8137
  };
@@ -7120,7 +8679,7 @@ function register_c_yui_map() {
7120
8679
  return create_gclass$9(GCLASS_NAME$9);
7121
8680
  }
7122
8681
  //#endregion
7123
- //#region c_yui_uplot.js
8682
+ //#region src/c_yui_uplot.js
7124
8683
  /***********************************************************************
7125
8684
  * c_yui_uplot.js
7126
8685
  *
@@ -7180,7 +8739,7 @@ var __gclass__$8 = null;
7180
8739
  * Framework Method: Create
7181
8740
  ***************************************************************/
7182
8741
  function mt_create$8(gobj) {
7183
- build_ui$7(gobj);
8742
+ build_ui$8(gobj);
7184
8743
  let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
7185
8744
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
7186
8745
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
@@ -7210,11 +8769,11 @@ function mt_destroy$8(gobj) {
7210
8769
  /************************************************************
7211
8770
  * Build UI
7212
8771
  ************************************************************/
7213
- function build_ui$7(gobj) {
8772
+ function build_ui$8(gobj) {
7214
8773
  let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
7215
8774
  if (!$container) {
7216
8775
  $container = (0, _yuneta_gobj_js.createElement2)(["div", {
7217
- class: "",
8776
+ class: "C_YUI_UPLOT",
7218
8777
  style: "overflow:auto;"
7219
8778
  }]);
7220
8779
  (0, _yuneta_gobj_js.gobj_write_bool_attr)(gobj, "$container", $container);
@@ -7413,7 +8972,7 @@ function register_c_yui_uplot() {
7413
8972
  return create_gclass$8(GCLASS_NAME$8);
7414
8973
  }
7415
8974
  //#endregion
7416
- //#region g6_drag_canvas_touch.js
8975
+ //#region src/g6_drag_canvas_touch.js
7417
8976
  /***********************************************************************
7418
8977
  * g6_drag_canvas_touch.js
7419
8978
  *
@@ -7521,7 +9080,7 @@ function ensure_drag_canvas_patch() {
7521
9080
  __drag_canvas_patched__ = true;
7522
9081
  }
7523
9082
  //#endregion
7524
- //#region c_yui_json_graph.js
9083
+ //#region src/c_yui_json_graph.js
7525
9084
  /***********************************************************************
7526
9085
  * c_yui_json_graph.js
7527
9086
  *
@@ -7594,7 +9153,7 @@ function mt_create$7(gobj) {
7594
9153
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
7595
9154
  let __yui_main__ = (0, _yuneta_gobj_js.gobj_find_service)("__yui_main__", true);
7596
9155
  if (__yui_main__) priv.theme = (0, _yuneta_gobj_js.gobj_read_str_attr)(__yui_main__, "theme");
7597
- build_ui$6(gobj);
9156
+ build_ui$7(gobj);
7598
9157
  ensure_drag_canvas_patch();
7599
9158
  if (!__layout_registered__$1) {
7600
9159
  (0, _antv_g6.register)(_antv_g6.ExtensionCategory.LAYOUT, "json-tree-layout", JsonTreeLayout);
@@ -7637,12 +9196,12 @@ function mt_destroy$7(gobj) {
7637
9196
  /************************************************************
7638
9197
  * Build UI
7639
9198
  ************************************************************/
7640
- function build_ui$6(gobj) {
9199
+ function build_ui$7(gobj) {
7641
9200
  let priv = gobj.priv;
7642
9201
  let $container = (0, _yuneta_gobj_js.createElement2)([
7643
9202
  "div",
7644
9203
  {
7645
- class: "json-graph",
9204
+ class: "C_YUI_JSON_GRAPH",
7646
9205
  style: "height:100%; display:flex; flex-direction:column;"
7647
9206
  },
7648
9207
  [[
@@ -8275,7 +9834,7 @@ function register_c_yui_json_graph() {
8275
9834
  return create_gclass$7(GCLASS_NAME$7);
8276
9835
  }
8277
9836
  //#endregion
8278
- //#region c_yui_gobj_tree_js.js
9837
+ //#region src/c_yui_gobj_tree_js.js
8279
9838
  /***********************************************************************
8280
9839
  * c_yui_gobj_tree_js.js
8281
9840
  *
@@ -8510,7 +10069,7 @@ function mt_create$6(gobj) {
8510
10069
  let __yui_main__ = (0, _yuneta_gobj_js.gobj_find_service)("__yui_main__");
8511
10070
  if (__yui_main__) priv.theme = (0, _yuneta_gobj_js.gobj_read_str_attr)(__yui_main__, "theme");
8512
10071
  else priv.theme = gt_is_dark() ? "dark" : "light";
8513
- build_ui$5(gobj);
10072
+ build_ui$6(gobj);
8514
10073
  ensure_drag_canvas_patch();
8515
10074
  if (!__layout_registered__) {
8516
10075
  (0, _antv_g6.register)(_antv_g6.ExtensionCategory.LAYOUT, "gobj-tree-v", GobjTreeVLayout);
@@ -8562,12 +10121,12 @@ function mt_destroy$6(gobj) {
8562
10121
  /************************************************************
8563
10122
  * Build UI
8564
10123
  ************************************************************/
8565
- function build_ui$5(gobj) {
10124
+ function build_ui$6(gobj) {
8566
10125
  let priv = gobj.priv;
8567
10126
  let $container = (0, _yuneta_gobj_js.createElement2)([
8568
10127
  "div",
8569
10128
  {
8570
- class: "gobj-tree",
10129
+ class: "C_YUI_GOBJ_TREE_JS",
8571
10130
  style: "height:100%; display:flex; flex-direction:column; position:relative;"
8572
10131
  },
8573
10132
  [[
@@ -9840,7 +11399,7 @@ function register_c_yui_gobj_tree_js() {
9840
11399
  return create_gclass$6(GCLASS_NAME$6);
9841
11400
  }
9842
11401
  //#endregion
9843
- //#region shell_show_on.js
11402
+ //#region src/shell_show_on.js
9844
11403
  /***********************************************************************
9845
11404
  * shell_show_on.js
9846
11405
  *
@@ -9916,7 +11475,7 @@ function bulma_hidden_class(bp) {
9916
11475
  return "";
9917
11476
  }
9918
11477
  //#endregion
9919
- //#region shell_focus_trap.js
11478
+ //#region src/shell_focus_trap.js
9920
11479
  /***********************************************************************
9921
11480
  * shell_focus_trap.js
9922
11481
  *
@@ -9990,7 +11549,7 @@ function activate_focus_trap_on($panel, doc) {
9990
11549
  };
9991
11550
  }
9992
11551
  //#endregion
9993
- //#region shell_toolbar_helpers.js
11552
+ //#region src/shell_toolbar_helpers.js
9994
11553
  var TOOLBAR_ACTION_TYPES = [
9995
11554
  "navigate",
9996
11555
  "drawer",
@@ -10116,7 +11675,7 @@ function _is_non_empty_string(v) {
10116
11675
  return typeof v === "string" && v.trim().length > 0;
10117
11676
  }
10118
11677
  //#endregion
10119
- //#region route_resolver.js
11678
+ //#region src/route_resolver.js
10120
11679
  /***********************************************************************
10121
11680
  * route_resolver.js
10122
11681
  *
@@ -10167,7 +11726,7 @@ function resolve_route(item_index, route) {
10167
11726
  };
10168
11727
  }
10169
11728
  //#endregion
10170
- //#region c_yui_shell.js
11729
+ //#region src/c_yui_shell.js
10171
11730
  /***********************************************************************
10172
11731
  * c_yui_shell.js
10173
11732
  *
@@ -10253,7 +11812,7 @@ function mt_create$5(gobj) {
10253
11812
  conn_nodes: [],
10254
11813
  active_dropdown: null
10255
11814
  });
10256
- build_ui$4(gobj);
11815
+ build_ui$5(gobj);
10257
11816
  }
10258
11817
  /***************************************************************
10259
11818
  * Framework Method: Start
@@ -10458,16 +12017,16 @@ function check_route_unique(route_owner, item, menu_id) {
10458
12017
  /************************************************************
10459
12018
  * Build the DOM: layers → base → zones
10460
12019
  ************************************************************/
10461
- function build_ui$4(gobj) {
12020
+ function build_ui$5(gobj) {
10462
12021
  let priv = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "priv");
10463
12022
  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" }]);
12023
+ let $container = (0, _yuneta_gobj_js.createElement2)(["div", { class: "C_YUI_SHELL yui-shell" }]);
10465
12024
  for (let [id, z] of LAYER_DEFS) {
10466
12025
  let $layer = (0, _yuneta_gobj_js.createElement2)(["div", {
10467
12026
  class: `yui-layer yui-layer-${id}`,
10468
12027
  style: `z-index:${z};`
10469
12028
  }]);
10470
- $root.appendChild($layer);
12029
+ $container.appendChild($layer);
10471
12030
  priv.layers[id] = $layer;
10472
12031
  }
10473
12032
  let $base = priv.layers.base;
@@ -10516,8 +12075,8 @@ function build_ui$4(gobj) {
10516
12075
  };
10517
12076
  priv.zones.center.classList.add("yui-stage", "yui-stage-main");
10518
12077
  }
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);
12078
+ ((0, _yuneta_gobj_js.gobj_read_attr)(gobj, "mount_element") || document.body).appendChild($container);
12079
+ (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", $container);
10521
12080
  }
10522
12081
  /************************************************************
10523
12082
  * Translate zone id to grid-area name
@@ -10786,9 +12345,10 @@ function navigate_to(gobj, route) {
10786
12345
  let prev_route = stage.active_route;
10787
12346
  if (prev_route && prev_route !== matched_route) {
10788
12347
  let prev_gobj = stage.items[prev_route];
12348
+ let $prev = null;
10789
12349
  if (prev_gobj) {
10790
- let $c = (0, _yuneta_gobj_js.gobj_read_attr)(prev_gobj, "$container");
10791
- if ($c) $c.classList.add("is-hidden");
12350
+ $prev = (0, _yuneta_gobj_js.gobj_read_attr)(prev_gobj, "$container");
12351
+ if ($prev) $prev.classList.add("is-hidden");
10792
12352
  }
10793
12353
  let prev_entry = priv.item_index[prev_route];
10794
12354
  if (prev_entry && prev_entry.target && prev_entry.target.lifecycle === "lazy_destroy") {
@@ -10798,6 +12358,7 @@ function navigate_to(gobj, route) {
10798
12358
  } catch (e) {
10799
12359
  (0, _yuneta_gobj_js.log_warning)(`C_YUI_SHELL: lazy_destroy of '${prev_route}' failed: ${e}`);
10800
12360
  }
12361
+ if ($prev && $prev.parentNode) $prev.parentNode.removeChild($prev);
10801
12362
  delete stage.items[prev_route];
10802
12363
  }
10803
12364
  }
@@ -11482,6 +13043,21 @@ function ac_drawer_close_requested(gobj, event, kw, src) {
11482
13043
  close_drawer(gobj, kw && kw.menu_id || "");
11483
13044
  return 0;
11484
13045
  }
13046
+ /************************************************************
13047
+ * Close 'x' on a closable tab (from a C_YUI_NAV child). The
13048
+ * shell does not own the item set — re-publish so the app (which
13049
+ * owns the underlying data, e.g. the selected-nodes list) removes
13050
+ * the item and calls yui_shell_set_submenu() with the new list.
13051
+ ************************************************************/
13052
+ function ac_nav_item_close(gobj, event, kw, src) {
13053
+ (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_NAV_ITEM_CLOSE", {
13054
+ item_id: kw && kw.item_id || "",
13055
+ route: kw && kw.route || "",
13056
+ menu_id: kw && kw.menu_id || "",
13057
+ zone: kw && kw.zone || ""
13058
+ });
13059
+ return 0;
13060
+ }
11485
13061
  /***************************************************************
11486
13062
  * FSM
11487
13063
  ***************************************************************/
@@ -11496,18 +13072,27 @@ function create_gclass$5(gclass_name) {
11496
13072
  (0, _yuneta_gobj_js.log_error)(`GClass ALREADY created: ${gclass_name}`);
11497
13073
  return -1;
11498
13074
  }
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
- ]]]];
13075
+ const states = [["ST_IDLE", [
13076
+ [
13077
+ "EV_NAV_CLICKED",
13078
+ ac_nav_clicked,
13079
+ null
13080
+ ],
13081
+ [
13082
+ "EV_DRAWER_CLOSE_REQUESTED",
13083
+ ac_drawer_close_requested,
13084
+ null
13085
+ ],
13086
+ [
13087
+ "EV_NAV_ITEM_CLOSE",
13088
+ ac_nav_item_close,
13089
+ null
13090
+ ]
13091
+ ]]];
11508
13092
  __gclass__$5 = (0, _yuneta_gobj_js.gclass_create)(gclass_name, [
11509
13093
  ["EV_NAV_CLICKED", 0],
11510
13094
  ["EV_DRAWER_CLOSE_REQUESTED", 0],
13095
+ ["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
13096
  ["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
13097
  ["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
13098
  ], 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 +13184,9 @@ function yui_shell_set_connection_state(shell_gobj, connected) {
11599
13184
  * `icon_class` fully replaces the <i> class.
11600
13185
  ************************************************************/
11601
13186
  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`);
13187
+ let $container = (0, _yuneta_gobj_js.gobj_read_attr)(shell_gobj, "$container");
13188
+ if (!$container || (0, _yuneta_gobj_js.empty_string)(item_id) || (0, _yuneta_gobj_js.empty_string)(icon_class)) return;
13189
+ let $i = $container.querySelector(`[data-toolbar-item-id="${item_id}"] .icon i`);
11605
13190
  if ($i) $i.className = icon_class;
11606
13191
  }
11607
13192
  /************************************************************
@@ -11613,7 +13198,7 @@ function yui_shell_close_dropdown(shell_gobj) {
11613
13198
  close_toolbar_dropdown(shell_gobj);
11614
13199
  }
11615
13200
  //#endregion
11616
- //#region c_yui_nav.js
13201
+ //#region src/c_yui_nav.js
11617
13202
  /***********************************************************************
11618
13203
  * c_yui_nav.js
11619
13204
  *
@@ -11631,6 +13216,15 @@ function yui_shell_close_dropdown(shell_gobj) {
11631
13216
  *
11632
13217
  * Each item supports:
11633
13218
  * id, name, icon (CSS class or svg id), route, badge, disabled
13219
+ * class — extra CSS class(es) on the item (tabs layout), e.g.
13220
+ * a per-item state colour like "yui-nav-disconnected"
13221
+ * closable — render a trailing ✕ that emits EV_NAV_ITEM_CLOSE
13222
+ * instead of navigating (tabs layout)
13223
+ *
13224
+ * Items can be replaced at runtime by sending EV_SET_ITEMS
13225
+ * {items:[...]} to the nav; the DOM is rebuilt in place and the
13226
+ * active highlight restored. The app_config path is simply the
13227
+ * first, startup caller of the same mechanism.
11634
13228
  *
11635
13229
  * In addition to navigable items, secondary navs accept two
11636
13230
  * decorative item kinds for in-place visual grouping:
@@ -11694,7 +13288,7 @@ function mt_create$4(gobj) {
11694
13288
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
11695
13289
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
11696
13290
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "priv", { click_handler: null });
11697
- build_ui$3(gobj);
13291
+ build_ui$4(gobj);
11698
13292
  }
11699
13293
  /***************************************************************
11700
13294
  * Framework Method: Start
@@ -11728,7 +13322,7 @@ function mt_destroy$4(gobj) {
11728
13322
  /************************************************************
11729
13323
  * Build the UI: dispatch to one of the six layout renderers
11730
13324
  ************************************************************/
11731
- function build_ui$3(gobj) {
13325
+ function build_ui$4(gobj) {
11732
13326
  let layout = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "layout") || "vertical";
11733
13327
  if (SUPPORTED_LAYOUTS.indexOf(layout) < 0) {
11734
13328
  (0, _yuneta_gobj_js.log_error)(`C_YUI_NAV: unsupported layout '${layout}', falling back to vertical`);
@@ -11737,40 +13331,75 @@ function build_ui$3(gobj) {
11737
13331
  let items = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "menu_items") || [];
11738
13332
  let zone = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "zone") || "";
11739
13333
  let menu_id = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "menu_id") || "nav";
11740
- let $root;
13334
+ let $container;
11741
13335
  switch (layout) {
11742
13336
  case "vertical":
11743
- $root = render_vertical(gobj, items);
13337
+ $container = render_vertical(gobj, items);
11744
13338
  break;
11745
13339
  case "icon-bar":
11746
- $root = render_icon_bar(gobj, items);
13340
+ $container = render_icon_bar(gobj, items);
11747
13341
  break;
11748
13342
  case "tabs":
11749
- $root = render_tabs(gobj, items);
13343
+ $container = render_tabs(gobj, items);
11750
13344
  break;
11751
13345
  case "drawer":
11752
- $root = render_drawer(gobj, items);
13346
+ $container = render_drawer(gobj, items);
11753
13347
  break;
11754
13348
  case "submenu":
11755
- $root = render_submenu(gobj, items);
13349
+ $container = render_submenu(gobj, items);
11756
13350
  break;
11757
13351
  case "accordion":
11758
- $root = render_accordion(gobj, items);
13352
+ $container = render_accordion(gobj, items);
11759
13353
  break;
11760
- default: $root = render_vertical(gobj, items);
13354
+ default: $container = render_vertical(gobj, items);
11761
13355
  }
11762
- $root.setAttribute("data-nav-zone", zone);
11763
- $root.setAttribute("data-nav-layout", layout);
11764
- $root.classList.add("yui-nav", `yui-nav-${layout}`);
13356
+ $container.setAttribute("data-nav-zone", zone);
13357
+ $container.setAttribute("data-nav-layout", layout);
13358
+ $container.classList.add("C_YUI_NAV", "yui-nav", `yui-nav-${layout}`);
11765
13359
  if (layout !== "drawer") {
11766
- $root.setAttribute("role", "navigation");
11767
- if (!$root.hasAttribute("aria-label")) {
13360
+ $container.setAttribute("role", "navigation");
13361
+ if (!$container.hasAttribute("aria-label")) {
11768
13362
  let label = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "nav_label") || menu_id;
11769
- $root.setAttribute("aria-label", label);
13363
+ $container.setAttribute("aria-label", label);
11770
13364
  }
11771
13365
  }
11772
- wire_clicks(gobj, $root);
11773
- (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", $root);
13366
+ wire_clicks(gobj, $container);
13367
+ (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", $container);
13368
+ }
13369
+ /************************************************************
13370
+ * Rebuild the nav DOM in place from the current menu_items.
13371
+ * Used by EV_SET_ITEMS so a nav's tabs can be changed at runtime
13372
+ * (the app_config path is just the first, startup caller).
13373
+ ************************************************************/
13374
+ function rebuild(gobj) {
13375
+ let $old = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
13376
+ let priv = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "priv") || {};
13377
+ let parent = $old ? $old.parentNode : null;
13378
+ let next = $old ? $old.nextSibling : null;
13379
+ let was_hidden = !!($old && $old.classList.contains("is-hidden"));
13380
+ if ($old && priv.click_handler) $old.removeEventListener("click", priv.click_handler);
13381
+ build_ui$4(gobj);
13382
+ let $new = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
13383
+ if ($new && was_hidden) $new.classList.add("is-hidden");
13384
+ if (parent && $new) parent.insertBefore($new, next);
13385
+ if ($old && $old.parentNode) $old.parentNode.removeChild($old);
13386
+ apply_active_route(gobj);
13387
+ }
13388
+ /************************************************************
13389
+ * Re-apply the active-route highlight after a rebuild (the
13390
+ * shell only pushes EV_ROUTE_CHANGED on navigation, not on a
13391
+ * self-triggered items change).
13392
+ ************************************************************/
13393
+ function apply_active_route(gobj) {
13394
+ let $c = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
13395
+ let route = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "active_route") || "";
13396
+ if (!$c || (0, _yuneta_gobj_js.empty_string)(route)) return;
13397
+ let $a = $c.querySelector(`a[data-route="${css_escape(route)}"]`);
13398
+ if ($a) {
13399
+ let $li = $a.closest("li");
13400
+ if ($li) $li.classList.add("is-active");
13401
+ $a.classList.add("is-active");
13402
+ }
11774
13403
  }
11775
13404
  /************************************************************
11776
13405
  * Layouts
@@ -11848,6 +13477,20 @@ function render_tabs(gobj, items) {
11848
13477
  { i18n: it.name },
11849
13478
  it.name
11850
13479
  ]);
13480
+ if (it.closable) children.push([
13481
+ "span",
13482
+ {
13483
+ class: "icon is-small yui-nav-close ml-2",
13484
+ "data-close-item": it.id,
13485
+ role: "button",
13486
+ "aria-label": "close",
13487
+ title: "close"
13488
+ },
13489
+ ["i", {
13490
+ class: "yi-xmark",
13491
+ "aria-hidden": "true"
13492
+ }]
13493
+ ]);
11851
13494
  let a_attrs = {
11852
13495
  href: it.route ? "#" + it.route : "#",
11853
13496
  "data-item-id": it.id,
@@ -11858,10 +13501,11 @@ function render_tabs(gobj, items) {
11858
13501
  a_attrs.title = tip;
11859
13502
  a_attrs["data-i18n-title"] = tip;
11860
13503
  }
13504
+ let li_class = (0, _yuneta_gobj_js.empty_string)(it.class) ? "" : String(it.class);
11861
13505
  lis.push([
11862
13506
  "li",
11863
13507
  {
11864
- class: "",
13508
+ class: li_class,
11865
13509
  "data-item-id": it.id,
11866
13510
  "data-route": it.route || ""
11867
13511
  },
@@ -11934,7 +13578,7 @@ function render_submenu(gobj, items) {
11934
13578
  function render_accordion(gobj, items) {
11935
13579
  let show_label = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "show_label");
11936
13580
  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" }]);
13581
+ let $container = (0, _yuneta_gobj_js.createElement2)(["aside", { class: "menu yui-nav-accordion p-2" }]);
11938
13582
  for (let it of items) {
11939
13583
  if (is_decorative(it)) continue;
11940
13584
  let acc_id = ++__nav_aria_seq__;
@@ -11956,7 +13600,7 @@ function render_accordion(gobj, items) {
11956
13600
  head_attrs,
11957
13601
  head_text
11958
13602
  ]);
11959
- $root.appendChild($hdr);
13603
+ $container.appendChild($hdr);
11960
13604
  let $ul = (0, _yuneta_gobj_js.createElement2)(["ul", {
11961
13605
  id: body_id,
11962
13606
  class: "menu-list yui-accordion-body is-hidden",
@@ -11968,9 +13612,9 @@ function render_accordion(gobj, items) {
11968
13612
  show_label,
11969
13613
  stacked: false
11970
13614
  })));
11971
- $root.appendChild($ul);
13615
+ $container.appendChild($ul);
11972
13616
  }
11973
- return $root;
13617
+ return $container;
11974
13618
  }
11975
13619
  /************************************************************
11976
13620
  * Shared helpers
@@ -12105,18 +13749,31 @@ function item_iconbar(gobj, it, opts) {
12105
13749
  ]
12106
13750
  ];
12107
13751
  }
12108
- function wire_clicks(gobj, $root) {
13752
+ function wire_clicks(gobj, $container) {
12109
13753
  let priv = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "priv") || {};
12110
13754
  let handler = (ev) => {
12111
13755
  let target = ev.target;
12112
13756
  if (!target || !target.closest) return;
13757
+ let $close = target.closest("[data-close-item]");
13758
+ if ($close) {
13759
+ let $routed = $close.closest("[data-route]");
13760
+ (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_NAV_ITEM_CLOSE", {
13761
+ item_id: $close.getAttribute("data-close-item") || "",
13762
+ route: $routed && $routed.getAttribute("data-route") || "",
13763
+ menu_id: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "menu_id") || "",
13764
+ zone: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "zone") || ""
13765
+ });
13766
+ ev.preventDefault();
13767
+ ev.stopPropagation();
13768
+ return;
13769
+ }
12113
13770
  if (target.closest("[data-close-drawer]")) {
12114
13771
  (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
13772
  ev.preventDefault();
12116
13773
  return;
12117
13774
  }
12118
13775
  let $head = target.closest(".yui-accordion-head");
12119
- if ($head && $root.contains($head)) {
13776
+ if ($head && $container.contains($head)) {
12120
13777
  let $next = $head.nextElementSibling;
12121
13778
  if ($next && $next.classList.contains("yui-accordion-body")) {
12122
13779
  let open = $next.classList.contains("is-hidden");
@@ -12143,13 +13800,23 @@ function wire_clicks(gobj, $root) {
12143
13800
  level: (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "level") || "primary"
12144
13801
  });
12145
13802
  };
12146
- $root.addEventListener("click", handler);
13803
+ $container.addEventListener("click", handler);
12147
13804
  priv.click_handler = handler;
12148
13805
  }
12149
13806
  /***************************
12150
13807
  * Actions
12151
13808
  ***************************/
12152
13809
  /************************************************************
13810
+ * EV_SET_ITEMS — replace this nav's items and re-render in place.
13811
+ * This is the runtime counterpart of the startup config path:
13812
+ * the shell (or app, via the shell) drives dynamic navs through it.
13813
+ ************************************************************/
13814
+ function ac_set_items(gobj, event, kw, src) {
13815
+ (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "menu_items", (0, _yuneta_gobj_js.is_array)(kw.items) ? kw.items : []);
13816
+ rebuild(gobj);
13817
+ return 0;
13818
+ }
13819
+ /************************************************************
12153
13820
  * EV_ROUTE_CHANGED from the shell — refresh active highlight
12154
13821
  ************************************************************/
12155
13822
  function ac_route_changed(gobj, event, kw, src) {
@@ -12208,10 +13875,16 @@ function create_gclass$4(gclass_name) {
12208
13875
  "EV_ROUTE_CHANGED",
12209
13876
  ac_route_changed,
12210
13877
  null
13878
+ ], [
13879
+ "EV_SET_ITEMS",
13880
+ ac_set_items,
13881
+ null
12211
13882
  ]]]];
12212
13883
  __gclass__$4 = (0, _yuneta_gobj_js.gclass_create)(gclass_name, [
12213
13884
  ["EV_ROUTE_CHANGED", 0],
13885
+ ["EV_SET_ITEMS", 0],
12214
13886
  ["EV_NAV_CLICKED", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_PUBLIC_EVENT],
13887
+ ["EV_NAV_ITEM_CLOSE", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_PUBLIC_EVENT],
12215
13888
  ["EV_DRAWER_CLOSE_REQUESTED", _yuneta_gobj_js.event_flag_t.EVF_OUTPUT_EVENT | _yuneta_gobj_js.event_flag_t.EVF_PUBLIC_EVENT]
12216
13889
  ], states, gmt$4, 0, attrs_table$4, PRIVATE_DATA$4, 0, 0, 0, 0);
12217
13890
  if (!__gclass__$4) return -1;
@@ -12221,7 +13894,7 @@ function register_c_yui_nav() {
12221
13894
  return create_gclass$4(GCLASS_NAME$4);
12222
13895
  }
12223
13896
  //#endregion
12224
- //#region shell_modals.js
13897
+ //#region src/shell_modals.js
12225
13898
  /***********************************************************************
12226
13899
  * shell_modals.js
12227
13900
  *
@@ -12355,27 +14028,74 @@ function yui_shell_show_modal(shell, content, opts) {
12355
14028
  content
12356
14029
  ]]
12357
14030
  ] : null;
14031
+ let dialog = !!(opts && opts.dialog);
14032
+ let title = opts && opts.title || "";
12358
14033
  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
- }]);
14034
+ let modal_children;
14035
+ if (dialog) {
14036
+ modal_children = [["div", { class: "modal-background" }], [
14037
+ "div",
14038
+ { class: "modal-content yui-dialog-content" },
14039
+ [[
14040
+ "div",
14041
+ { class: "yui-dialog-header" },
14042
+ [
14043
+ [
14044
+ "button",
14045
+ {
14046
+ class: "yui-dialog-back",
14047
+ type: "button",
14048
+ "aria-label": "back"
14049
+ },
14050
+ [["i", { class: "yi-arrow-left" }]]
14051
+ ],
14052
+ [
14053
+ "span",
14054
+ {
14055
+ class: "yui-dialog-title",
14056
+ i18n: title
14057
+ },
14058
+ title
14059
+ ],
14060
+ [
14061
+ "button",
14062
+ {
14063
+ class: "yui-dialog-x",
14064
+ type: "button",
14065
+ "aria-label": "close"
14066
+ },
14067
+ [["i", { class: "yi-xmark" }]]
14068
+ ]
14069
+ ]
14070
+ ], [
14071
+ "div",
14072
+ { class: "yui-dialog-body" },
14073
+ inner ? [inner] : []
14074
+ ]]
14075
+ ]];
14076
+ with_close = false;
14077
+ } else {
14078
+ modal_children = [["div", { class: "modal-background" }], [
14079
+ "div",
14080
+ { class: "modal-content" },
14081
+ inner ? [inner] : []
14082
+ ]];
14083
+ if (with_close) modal_children.push(["button", {
14084
+ class: "modal-close is-large",
14085
+ "aria-label": "close"
14086
+ }]);
14087
+ }
12368
14088
  let $modal = (0, _yuneta_gobj_js.createElement2)([
12369
14089
  "div",
12370
14090
  {
12371
- class: "modal yui-modal is-active",
14091
+ class: "modal yui-modal" + (dialog ? " yui-dialog" : "") + " is-active",
12372
14092
  role: "dialog",
12373
14093
  "aria-modal": "true"
12374
14094
  },
12375
14095
  modal_children
12376
14096
  ]);
12377
14097
  $layer.appendChild($modal);
12378
- if (!inner && content && typeof content.appendChild !== "undefined") $modal.querySelector(".modal-content").appendChild(content);
14098
+ if (!inner && content && typeof content.appendChild !== "undefined") (dialog ? $modal.querySelector(".yui-dialog-body") : $modal.querySelector(".modal-content")).appendChild(content);
12379
14099
  maybe_apply_translator($modal, opts);
12380
14100
  let closed = false;
12381
14101
  let close_fn = null;
@@ -12404,6 +14124,12 @@ function yui_shell_show_modal(shell, content, opts) {
12404
14124
  if (opts == null || opts.dismiss_on_background !== false) $modal.querySelector(".modal-background").addEventListener("click", close);
12405
14125
  let $close_btn = $modal.querySelector(".modal-close");
12406
14126
  if ($close_btn) $close_btn.addEventListener("click", close);
14127
+ if (dialog) {
14128
+ let $back = $modal.querySelector(".yui-dialog-back");
14129
+ if ($back) $back.addEventListener("click", close);
14130
+ let $x = $modal.querySelector(".yui-dialog-x");
14131
+ if ($x) $x.addEventListener("click", close);
14132
+ }
12407
14133
  return { close };
12408
14134
  }
12409
14135
  /***************************************************************
@@ -12555,7 +14281,7 @@ function yui_shell_confirm_yesnocancel(shell, message, opts) {
12555
14281
  ], opts);
12556
14282
  }
12557
14283
  //#endregion
12558
- //#region c_yui_treedb_topics.js
14284
+ //#region src/c_yui_treedb_topics.js
12559
14285
  /***********************************************************************
12560
14286
  * c_yui_treedb_topics.js
12561
14287
  *
@@ -12598,7 +14324,7 @@ var __gclass__$3 = null;
12598
14324
  * Framework Method: Create
12599
14325
  ***************************************************************/
12600
14326
  function mt_create$3(gobj) {
12601
- build_ui$2(gobj);
14327
+ build_ui$3(gobj);
12602
14328
  let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
12603
14329
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
12604
14330
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
@@ -12672,11 +14398,11 @@ function cmd_get_topic_data$1(gobj, cmd, kw, src) {
12672
14398
  /************************************************************
12673
14399
  * Build UI
12674
14400
  ************************************************************/
12675
- function build_ui$2(gobj) {
14401
+ function build_ui$3(gobj) {
12676
14402
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "$container", (0, _yuneta_gobj_js.createElement2)([
12677
14403
  "div",
12678
14404
  {
12679
- class: `${(0, _yuneta_gobj_js.gobj_read_attr)(gobj, "treedb_name")}`,
14405
+ class: `C_YUI_TREEDB_TOPICS ${(0, _yuneta_gobj_js.gobj_read_attr)(gobj, "treedb_name")}`,
12680
14406
  style: "height:100%; display:flex; flex-direction:column;"
12681
14407
  },
12682
14408
  [[
@@ -12701,6 +14427,26 @@ function build_ui$2(gobj) {
12701
14427
  ]));
12702
14428
  }
12703
14429
  /************************************************************
14430
+ * Show a non-blocking inline error banner at the top of the view
14431
+ * (used when the treedb schema `descs` cannot load). Reuses a single
14432
+ * banner so retries don't stack.
14433
+ ************************************************************/
14434
+ function show_load_error$1(gobj, message) {
14435
+ let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
14436
+ if (!$container) return;
14437
+ let $err = $container.querySelector(".treedb-load-error");
14438
+ if (!$err) {
14439
+ $err = (0, _yuneta_gobj_js.createElement2)([
14440
+ "div",
14441
+ { class: "notification is-danger is-light m-3 treedb-load-error" },
14442
+ []
14443
+ ]);
14444
+ $container.insertBefore($err, $container.firstChild);
14445
+ }
14446
+ let treedb = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "treedb_name") || "";
14447
+ $err.textContent = (treedb ? `${treedb}: ` : "") + (message || "cannot load treedb");
14448
+ }
14449
+ /************************************************************
12704
14450
  * Destroy UI
12705
14451
  ************************************************************/
12706
14452
  function destroy_ui$2(gobj) {
@@ -12920,10 +14666,14 @@ function ac_mt_command_answer$1(gobj, event, kw, src) {
12920
14666
  (0, _yuneta_gobj_js.log_error)(e);
12921
14667
  return;
12922
14668
  }
12923
- if (result < 0) display_error_message("Error", (0, i18next.t)(comment));
12924
14669
  let __command__ = (0, _yuneta_gobj_js.msg_iev_get_stack)(gobj, kw, "command_stack", true);
12925
14670
  let command = (0, _yuneta_gobj_js.kw_get_str)(gobj, __command__, "command", "", _yuneta_gobj_js.kw_flag_t.KW_REQUIRED);
12926
14671
  let kw_command = (0, _yuneta_gobj_js.kw_get_dict)(gobj, __command__, "kw", {}, _yuneta_gobj_js.kw_flag_t.KW_REQUIRED);
14672
+ if (result < 0) {
14673
+ if (command === "descs") show_load_error$1(gobj, (0, i18next.t)(comment));
14674
+ else display_error_message("Error", (0, i18next.t)(comment));
14675
+ return 0;
14676
+ }
12927
14677
  switch (command) {
12928
14678
  case "descs":
12929
14679
  if (result >= 0) {
@@ -13188,7 +14938,7 @@ function register_c_yui_treedb_topics() {
13188
14938
  return create_gclass$3(GCLASS_NAME$3);
13189
14939
  }
13190
14940
  //#endregion
13191
- //#region c_yui_treedb_topic_with_form.js
14941
+ //#region src/c_yui_treedb_topic_with_form.js
13192
14942
  /***********************************************************************
13193
14943
  * c_yui_treedb_topic_with_form.js
13194
14944
  *
@@ -13266,7 +15016,7 @@ function mt_create$2(gobj) {
13266
15016
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "toolbar_id", "toolbar" + name);
13267
15017
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "form_id", "form" + name);
13268
15018
  (0, _yuneta_gobj_js.gobj_write_attr)(gobj, "popup_id", "popup" + name);
13269
- build_ui$1(gobj);
15019
+ build_ui$2(gobj);
13270
15020
  let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
13271
15021
  if (!subscriber) subscriber = (0, _yuneta_gobj_js.gobj_parent)(gobj);
13272
15022
  (0, _yuneta_gobj_js.gobj_subscribe_event)(gobj, null, {}, subscriber);
@@ -13350,7 +15100,7 @@ async function readClipboard(gobj) {
13350
15100
  /************************************************************
13351
15101
  * Build UI
13352
15102
  ************************************************************/
13353
- function build_ui$1(gobj) {
15103
+ function build_ui$2(gobj) {
13354
15104
  function create_table_toolbar() {
13355
15105
  let $table_toolbar = [];
13356
15106
  let with_edition_mode = (0, _yuneta_gobj_js.gobj_read_bool_attr)(gobj, "with_edition_mode");
@@ -13567,7 +15317,7 @@ function build_ui$1(gobj) {
13567
15317
  let $container = (0, _yuneta_gobj_js.createElement2)([
13568
15318
  "div",
13569
15319
  {
13570
- class: "container-treedb-topic-with-form",
15320
+ class: "C_YUI_TREEDB_TOPIC_WITH_FORM",
13571
15321
  style: "height:100%;"
13572
15322
  },
13573
15323
  [["div", {
@@ -15578,7 +17328,7 @@ function register_c_yui_treedb_topic_with_form() {
15578
17328
  return create_gclass$2(GCLASS_NAME$2);
15579
17329
  }
15580
17330
  //#endregion
15581
- //#region lib_graph.js
17331
+ //#region src/lib_graph.js
15582
17332
  /***********************************************************************
15583
17333
  * lib_graph.js
15584
17334
  *
@@ -15690,7 +17440,7 @@ function getStrokeColor(fillColor, theme = "light", factor = .2) {
15690
17440
  return `rgba(${adjust(r)}, ${adjust(g)}, ${adjust(b)}, ${a.toFixed(3)})`;
15691
17441
  }
15692
17442
  //#endregion
15693
- //#region c_yui_treedb_graph.js
17443
+ //#region src/c_yui_treedb_graph.js
15694
17444
  /***********************************************************************
15695
17445
  * c_yui_treedb_graph.js
15696
17446
  *
@@ -15766,7 +17516,7 @@ function mt_create$1(gobj) {
15766
17516
  let __yui_main__ = (0, _yuneta_gobj_js.gobj_find_service)("__yui_main__");
15767
17517
  if (__yui_main__) (0, _yuneta_gobj_js.gobj_subscribe_event)(__yui_main__, "EV_RESIZE", {}, gobj);
15768
17518
  (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}`);
17519
+ let $container_canvas = build_ui$1(gobj).querySelector(`#${priv.canvas_id}`);
15770
17520
  priv.gobj_nodes_tree = (0, _yuneta_gobj_js.gobj_create_service)(`${(0, _yuneta_gobj_js.gobj_name)(gobj)}-g6`, "C_G6_NODES_TREE", {
15771
17521
  $container: $container_canvas,
15772
17522
  subscriber: gobj,
@@ -15816,13 +17566,13 @@ function mt_destroy$1(gobj) {
15816
17566
  /************************************************************
15817
17567
  * Build UI
15818
17568
  ************************************************************/
15819
- function build_ui(gobj) {
17569
+ function build_ui$1(gobj) {
15820
17570
  let priv = gobj.priv;
15821
17571
  let padding = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "padding");
15822
17572
  let $container = (0, _yuneta_gobj_js.createElement2)([
15823
17573
  "div",
15824
17574
  {
15825
- class: "graphs",
17575
+ class: "C_YUI_TREEDB_GRAPH",
15826
17576
  style: `height:100%; display:flex; flex-direction:column;`
15827
17577
  },
15828
17578
  [[
@@ -15851,6 +17601,26 @@ function build_ui(gobj) {
15851
17601
  return $container;
15852
17602
  }
15853
17603
  /************************************************************
17604
+ * Show a non-blocking inline error banner at the top of the view
17605
+ * (used when the treedb schema `descs` cannot load). Reuses a single
17606
+ * banner so retries don't stack.
17607
+ ************************************************************/
17608
+ function show_load_error(gobj, message) {
17609
+ let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
17610
+ if (!$container) return;
17611
+ let $err = $container.querySelector(".treedb-load-error");
17612
+ if (!$err) {
17613
+ $err = (0, _yuneta_gobj_js.createElement2)([
17614
+ "div",
17615
+ { class: "notification is-danger is-light m-3 treedb-load-error" },
17616
+ []
17617
+ ]);
17618
+ $container.insertBefore($err, $container.firstChild);
17619
+ }
17620
+ let treedb = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "treedb_name") || "";
17621
+ $err.textContent = (treedb ? `${treedb}: ` : "") + (message || "cannot load treedb");
17622
+ }
17623
+ /************************************************************
15854
17624
  * Destroy UI
15855
17625
  ************************************************************/
15856
17626
  function destroy_ui(gobj) {
@@ -16192,10 +17962,11 @@ function ac_mt_command_answer(gobj, event, kw, src) {
16192
17962
  (0, _yuneta_gobj_js.log_error)(e);
16193
17963
  return;
16194
17964
  }
16195
- if (result < 0) display_error_message("Error", (0, i18next.t)(comment));
16196
17965
  let __command__ = (0, _yuneta_gobj_js.msg_iev_get_stack)(gobj, kw, "command_stack", true);
16197
17966
  let command = (0, _yuneta_gobj_js.kw_get_str)(gobj, __command__, "command", "", _yuneta_gobj_js.kw_flag_t.KW_REQUIRED);
16198
17967
  let kw_command = (0, _yuneta_gobj_js.kw_get_dict)(gobj, __command__, "kw", {}, _yuneta_gobj_js.kw_flag_t.KW_REQUIRED);
17968
+ if (result < 0) if (command === "descs") show_load_error(gobj, (0, i18next.t)(comment));
17969
+ else display_error_message("Error", (0, i18next.t)(comment));
16199
17970
  switch (command) {
16200
17971
  case "descs":
16201
17972
  if (result >= 0) process_treedb_descs(gobj, data);
@@ -43119,7 +44890,7 @@ Sector.PARSED_STYLE_LIST = new Set([].concat(_toConsumableArray(Path.PARSED_STYL
43119
44890
  "endAngle"
43120
44891
  ]));
43121
44892
  //#endregion
43122
- //#region lib_icons.js
44893
+ //#region src/lib_icons.js
43123
44894
  /*********************************************************
43124
44895
  * lib_icons.js
43125
44896
  *
@@ -43230,7 +45001,7 @@ function inject_svg_icons() {
43230
45001
  document.body.appendChild(div);
43231
45002
  }
43232
45003
  //#endregion
43233
- //#region c_g6_nodes_tree.js
45004
+ //#region src/c_g6_nodes_tree.js
43234
45005
  /***********************************************************************
43235
45006
  * c_g6_nodes_tree.js
43236
45007
  *
@@ -43416,6 +45187,7 @@ function mt_create(gobj) {
43416
45187
  gobj.priv.theme_observer = mo;
43417
45188
  }
43418
45189
  }
45190
+ build_ui(gobj);
43419
45191
  register_layouts(gobj);
43420
45192
  build_graph(gobj);
43421
45193
  if (typeof ResizeObserver !== "undefined" && priv.$container) {
@@ -43514,6 +45286,13 @@ function get_default_ne_xy(gobj) {
43514
45286
  return xy;
43515
45287
  }
43516
45288
  /************************************************************
45289
+ * Build UI
45290
+ ************************************************************/
45291
+ function build_ui(gobj) {
45292
+ let $container = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "$container");
45293
+ if ($container) $container.classList.add(GCLASS_NAME);
45294
+ }
45295
+ /************************************************************
43517
45296
  * Register custom G6 layouts
43518
45297
  ************************************************************/
43519
45298
  var _g6_extensions_registered = false;
@@ -47428,7 +49207,7 @@ function register_c_g6_nodes_tree() {
47428
49207
  return create_gclass(GCLASS_NAME);
47429
49208
  }
47430
49209
  //#endregion
47431
- //#region themes.js
49210
+ //#region src/themes.js
47432
49211
  function themes() {
47433
49212
  function openModal($el) {
47434
49213
  $el.classList.add("is-active");
@@ -47558,7 +49337,7 @@ function themes() {
47558
49337
  onClickOutside(".js-menu");
47559
49338
  }
47560
49339
  //#endregion
47561
- //#region ytable.js
49340
+ //#region src/ytable.js
47562
49341
  var default_options = {
47563
49342
  id: void 0,
47564
49343
  width: "100%",
@@ -47830,11 +49609,43 @@ function createYTable($container, columns, options) {
47830
49609
  return new YTable($container, columns, options);
47831
49610
  }
47832
49611
  //#endregion
49612
+ //#region src/yui_inputs.js
49613
+ /***************************************************************
49614
+ * Add a clear (✕) button to a Bulma control wrapping an input.
49615
+ * Returns the button element (already appended to $control).
49616
+ ***************************************************************/
49617
+ function attach_clear($control, $input, on_clear) {
49618
+ if (!$control || !$input) return null;
49619
+ $control.classList.add("has-clear");
49620
+ let $btn = (0, _yuneta_gobj_js.createElement2)(["button", {
49621
+ type: "button",
49622
+ class: "delete is-medium yui-input-clear",
49623
+ tabindex: "-1",
49624
+ title: i18next.default.t("clear"),
49625
+ "aria-label": i18next.default.t("clear")
49626
+ }]);
49627
+ function sync() {
49628
+ $btn.classList.toggle("is-visible", !!$input.value);
49629
+ }
49630
+ $input.addEventListener("input", sync);
49631
+ $btn.addEventListener("click", () => {
49632
+ $input.value = "";
49633
+ $input.dispatchEvent(new Event("input", { bubbles: true }));
49634
+ if (typeof on_clear === "function") on_clear();
49635
+ $input.focus();
49636
+ sync();
49637
+ });
49638
+ $control.appendChild($btn);
49639
+ sync();
49640
+ return $btn;
49641
+ }
49642
+ //#endregion
47833
49643
  exports.EditControl = EditControl;
47834
49644
  exports.MarkerControl = MarkerControl;
47835
49645
  exports.YTable = YTable;
47836
49646
  exports.addClasses = addClasses;
47837
49647
  exports.apply_dev_traces = apply_dev_traces;
49648
+ exports.attach_clear = attach_clear;
47838
49649
  exports.build_dev_panel = build_dev_panel;
47839
49650
  exports.createYTable = createYTable;
47840
49651
  exports.dev_window_was_open = dev_window_was_open;
@@ -47866,6 +49677,7 @@ exports.register_c_yui_treedb_topic_with_form = register_c_yui_treedb_topic_with
47866
49677
  exports.register_c_yui_treedb_topics = register_c_yui_treedb_topics;
47867
49678
  exports.register_c_yui_uplot = register_c_yui_uplot;
47868
49679
  exports.register_c_yui_window = register_c_yui_window;
49680
+ exports.register_c_yui_window_manager = register_c_yui_window_manager;
47869
49681
  exports.register_c_yui_wizard = register_c_yui_wizard;
47870
49682
  exports.removeChildElements = removeChildElements;
47871
49683
  exports.removeClasses = removeClasses;