@yuneta/gobj-ui 2.0.0 → 2.2.0

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