@yuneta/gobj-ui 7.25.13 → 7.25.15

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.
@@ -3038,9 +3038,8 @@ function maybe_apply_translator($node, opts) {
3038
3038
  * it -- until every handle is closed or has timed out. Handed the
3039
3039
  * first caller's handle and timer, a repeat asking `timeout: 0`
3040
3040
  * was dismissed on the first one's timer, and closing one
3041
- * caller's handle closed a toast another was still showing
3042
- * (third independent review). The operator's ✕ closes it for
3043
- * everybody.
3041
+ * caller's handle closed a toast another was still showing.
3042
+ * The operator's ✕ closes it for everybody.
3044
3043
  ***************************************************************/
3045
3044
  function show_notification(shell, kind, message, opts) {
3046
3045
  let $layer = notification_layer(shell);
@@ -7425,7 +7424,7 @@ function json_diff_rows(doc_a, doc_b) {
7425
7424
  * A Date -> the value of a `datetime-local` input, in local time,
7426
7425
  * WITH seconds: the input is built with `step: 1`, which is what
7427
7426
  * lets it hold them. Written as "YYYY-MM-DDTHH:mm" it lost them on
7428
- * every save of any field (M26 of the 2026-09-21 review).
7427
+ * every save of any field.
7429
7428
  ************************************************************/
7430
7429
  function date_to_datetime_local(value) {
7431
7430
  if (!(value instanceof Date) || isNaN(value.getTime())) return "";
@@ -23130,8 +23129,7 @@ function yui_shell_show_route_map(shell, opts) {
23130
23129
  * answers, so the host has to answer EVERY way a write ends --
23131
23130
  * and the transport closing under a write in flight, or a
23132
23131
  * command that never left because there was no session, are two
23133
- * of them that nobody answered: the form stayed busy for ever
23134
- * (N8 of the 2026-09-22 review).
23132
+ * of them that nobody answered: the form stayed busy for ever.
23135
23133
  *
23136
23134
  * A write is known by its TOPIC and its `form_write` serial,
23137
23135
  * never by the serial alone: each topic has its own form, and
@@ -23757,7 +23755,7 @@ var __gclass__$6 = null;
23757
23755
  ***************************************************************/
23758
23756
  function mt_create$6(gobj) {
23759
23757
  gobj.priv.form_writes = {};
23760
- gobj.priv.reload_on_session = {};
23758
+ gobj.priv.reread_on_session = false;
23761
23759
  gobj.priv.conn_shell = null;
23762
23760
  build_ui$6(gobj);
23763
23761
  let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
@@ -25193,7 +25191,7 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
25193
25191
  if (is_connected$1(gobj)) (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_REFRESH_TOPIC", { topic_name: failed_topic }, gobj);
25194
25192
  else {
25195
25193
  (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: the write of '${failed_topic}' was cut by the drop: the topic is read again when the session is back`);
25196
- gobj.priv.reload_on_session[failed_topic] = true;
25194
+ gobj.priv.reread_on_session = true;
25197
25195
  }
25198
25196
  }
25199
25197
  if (settle_form_write(gobj.priv.form_writes, form_write, failed_topic)) answer_form_write(gobj, get_gobj_formtable(gobj, failed_topic), form_write, false);
@@ -25394,30 +25392,34 @@ function ac_back_to_topics(gobj, event, kw, src) {
25394
25392
  function ac_transport_state$2(gobj, event, kw, src) {
25395
25393
  let connected = !!(kw && kw.connected);
25396
25394
  refresh_toolbar_buttons(gobj, connected);
25397
- if (!connected) abandon_form_writes(gobj.priv.form_writes).forEach((w) => {
25398
- (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: transport closed, the write ${w.form_write} of '${w.topic_name}' is lost`);
25399
- if (w.topic_name) gobj.priv.reload_on_session[w.topic_name] = true;
25400
- answer_form_write(gobj, get_gobj_formtable(gobj, w.topic_name), w.form_write, false);
25401
- });
25402
- else reload_owed_topics(gobj);
25395
+ if (!connected) {
25396
+ gobj.priv.reread_on_session = true;
25397
+ abandon_form_writes(gobj.priv.form_writes).forEach((w) => {
25398
+ (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: transport closed, the write ${w.form_write} of '${w.topic_name}' is lost`);
25399
+ answer_form_write(gobj, get_gobj_formtable(gobj, w.topic_name), w.form_write, false);
25400
+ });
25401
+ } else reread_open_topics(gobj);
25403
25402
  return 0;
25404
25403
  }
25405
25404
  /************************************************************
25406
- * The session is back: read again the topics a drop left
25407
- * unknown. Only when the TRANSPORT says so too -- the app's
25408
- * connection (EV_CONNECTION_STATE) can report "up" before the
25409
- * transport of this view is in session, and a read asked then is
25410
- * refused. What is owed stays owed until an edge finds the
25411
- * transport in session; each topic is read once.
25405
+ * The session is back after a drop: read every open table again,
25406
+ * as the schema editor reloads its model. A drop cuts the writes
25407
+ * in flight AND the node events published meanwhile, so any
25408
+ * table may be stale, not only the one a cut write left.
25409
+ *
25410
+ * Only when the TRANSPORT says so too -- the app's connection
25411
+ * (EV_CONNECTION_STATE) can report "up" before the transport of
25412
+ * this view is in session, and a read asked then is refused. The
25413
+ * read waits for an edge that finds the transport in session, and
25414
+ * is done once, though both edges report the same "up".
25412
25415
  ************************************************************/
25413
- function reload_owed_topics(gobj) {
25416
+ function reread_open_topics(gobj) {
25414
25417
  let priv = gobj.priv;
25415
- if (!is_connected$1(gobj)) return;
25416
- let topics = Object.keys(priv.reload_on_session);
25417
- priv.reload_on_session = {};
25418
- topics.forEach((topic_name) => {
25419
- if (!get_gobj_formtable(gobj, topic_name)) return;
25420
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_REFRESH_TOPIC", { topic_name }, gobj);
25418
+ if (!priv.reread_on_session || !is_connected$1(gobj)) return;
25419
+ priv.reread_on_session = false;
25420
+ (0, _yuneta_gobj_js.gobj_match_children)(gobj, { __gclass_name__: "C_YUI_TREEDB_TOPIC_WITH_FORM" }).forEach((form) => {
25421
+ let topic_name = (0, _yuneta_gobj_js.gobj_read_attr)(form, "topic_name");
25422
+ if (topic_name) (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_REFRESH_TOPIC", { topic_name }, gobj);
25421
25423
  });
25422
25424
  }
25423
25425
  /************************************************************
@@ -25678,8 +25680,8 @@ function ac_update_record(gobj, event, kw, src) {
25678
25680
  /************************************************************
25679
25681
  * A write sent with no session never leaves: C_IEVENT_CLI logs
25680
25682
  * "Not in session" and answers null, as it does when the command
25681
- * went -- so a form waiting for the answer waited for ever (N8 of
25682
- * the 2026-09-22 review). Refused before it is sent, aloud.
25683
+ * went -- so a form waiting for the answer waited for ever. Refused
25684
+ * before it is sent, aloud.
25683
25685
  ************************************************************/
25684
25686
  function refuse_without_session(gobj, topic_name) {
25685
25687
  if (is_connected$1(gobj)) return false;
@@ -26791,9 +26793,8 @@ function delete_impact(desc, records) {
26791
26793
  * that back. So a second busy(true) while already busy must not
26792
26794
  * run at all -- it read the buttons it had just disabled as
26793
26795
  * "disabled on their own", and the busy(false) of a refused write
26794
- * left Save and Cancel dead (N9 of the 2026-09-22 review: a record
26795
- * with a picked file went busy for the read, then busy again for
26796
- * the write). The caller owns `state` ({busy}) for the form's life.
26796
+ * left Save and Cancel dead (a record with a picked file went
26797
+ * busy for the read, then busy again for the write). The caller owns `state` ({busy}) for the form's life.
26797
26798
  *
26798
26799
  * Copyright (c) 2026, ArtGins.
26799
26800
  * All Rights Reserved.
@@ -26831,7 +26832,7 @@ function set_toolbar_busy($container, state, busy) {
26831
26832
  * (only its own `plaintext` formatter escapes), so a record's text
26832
26833
  * returned as a string is parsed as markup: `a<b and c>d` showed
26833
26834
  * as `ad`, and a field holding `<img src=x onerror=...>` ran it in
26834
- * the operator's browser (M31 of the 2026-09-21 review). Record
26835
+ * the operator's browser. Record
26835
26836
  * data goes in as a TEXT NODE, and a cell built around it as DOM,
26836
26837
  * never as an HTML string with the data interpolated.
26837
26838
  *
@@ -29593,7 +29594,7 @@ function ac_new_row(gobj, event, kw, src) {
29593
29594
  * What crosses the confirmation is the rows' IDENTITY, never their
29594
29595
  * position: the view applies EV_TREEDB_NODE_* of every writer while the
29595
29596
  * dialog is open, and a position names whatever row sits there when the
29596
- * person answers -- with force:true (A6 of the 2026-09-21 review).
29597
+ * person answers -- with force:true.
29597
29598
  ************************************************************/
29598
29599
  function ac_delete_rows(gobj, event, kw, src) {
29599
29600
  if (refuse_if_readonly$2(gobj, event)) return -1;
@@ -29804,8 +29805,8 @@ function ac_files_read(gobj, event, kw, src) {
29804
29805
  * open and BUSY, and the serial it waits for is taken here.
29805
29806
  *
29806
29807
  * A refused write used to close the form all the same and throw
29807
- * away what was typed, although the README said it stayed open
29808
- * (M25 of the 2026-09-21 review): the close was posted with the
29808
+ * away what was typed, although the README said it stayed open:
29809
+ * the close was posted with the
29809
29810
  * publish, before the backend had answered anything.
29810
29811
  ************************************************************/
29811
29812
  function wait_for_write_answer(gobj) {
@@ -30977,6 +30978,28 @@ function apply_graphs_echo(live, saved, records, rec) {
30977
30978
  delete saved[rec.topic];
30978
30979
  }
30979
30980
  }
30981
+ /***************************************************************
30982
+ * A write of one topic's arrangement did NOT land: the backend
30983
+ * refused it, the transport refused it on the way out, or there
30984
+ * was no session to send it on.
30985
+ *
30986
+ * saved {topic_name: properties} what the backend has
30987
+ * topic_name the topic of the refused record
30988
+ *
30989
+ * The view records a write as saved BEFORE it leaves, because
30990
+ * nothing answers it when it lands. So a refused one left the view
30991
+ * believing the backend held an arrangement it does not, and the
30992
+ * next Save found nothing to write. What the backend holds for the
30993
+ * topic is not known any more, so it is forgotten: the next Save
30994
+ * writes the topic again (plan_graph_saves()).
30995
+ *
30996
+ * Returns TRUE if there was something to forget.
30997
+ ***************************************************************/
30998
+ function forget_refused_graphs_write(saved, topic_name) {
30999
+ if (!(0, _yuneta_gobj_js.is_object)(saved) || !topic_name || !Object.prototype.hasOwnProperty.call(saved, topic_name)) return false;
31000
+ delete saved[topic_name];
31001
+ return true;
31002
+ }
30980
31003
  //#endregion
30981
31004
  //#region src/treedb_node_label.js
30982
31005
  /***********************************************************************
@@ -65784,6 +65807,28 @@ function ac_save_graph(gobj, event, kw, src) {
65784
65807
  return 0;
65785
65808
  }
65786
65809
  /************************************************************
65810
+ * The host says a write of `__graphs__` did not land: the
65811
+ * backend refused it, the transport refused it on the way out,
65812
+ * or there was no session to send it on. kw: {topic}
65813
+ *
65814
+ * save_topic_graph_properties() recorded it as saved before it
65815
+ * left. Forgotten now, and the Save is lit again: the next Save
65816
+ * writes the topic again. Before, the view believed the backend
65817
+ * held the arrangement, and the next Save had nothing to write.
65818
+ ************************************************************/
65819
+ function ac_graphs_write_refused(gobj, event, kw, src) {
65820
+ let priv = gobj.priv;
65821
+ let topic_name = kw && kw.topic;
65822
+ if (!topic_name) {
65823
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${event} names no topic`);
65824
+ return -1;
65825
+ }
65826
+ (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: the arrangement of '${topic_name}' was not saved: the next Save writes it again`);
65827
+ forget_refused_graphs_write(priv._saved_graph_properties, topic_name);
65828
+ if (priv.edit_mode) mark_graph_dirty(gobj);
65829
+ return 0;
65830
+ }
65831
+ /************************************************************
65787
65832
  * Node created, from subscription
65788
65833
  ************************************************************/
65789
65834
  function ac_node_created(gobj, event, kw, src) {
@@ -66851,6 +66896,11 @@ function create_gclass$4(gclass_name) {
66851
66896
  ac_node_deleted,
66852
66897
  null
66853
66898
  ],
66899
+ [
66900
+ "EV_GRAPHS_WRITE_REFUSED",
66901
+ ac_graphs_write_refused,
66902
+ null
66903
+ ],
66854
66904
  [
66855
66905
  "EV_NODE_CLICK",
66856
66906
  ac_node_click$2,
@@ -67104,6 +67154,7 @@ function create_gclass$4(gclass_name) {
67104
67154
  ["EV_NODE_CREATED", 0],
67105
67155
  ["EV_NODE_UPDATED", 0],
67106
67156
  ["EV_NODE_DELETED", 0],
67157
+ ["EV_GRAPHS_WRITE_REFUSED", 0],
67107
67158
  ["EV_NODE_CLICK", 0],
67108
67159
  ["EV_EDGE_CLICK", 0],
67109
67160
  ["EV_NODE_CONTEXT_MENU", 0],
@@ -68280,7 +68331,7 @@ function treedb_update_node(gobj, treedb_name, topic_name, record, options) {
68280
68331
  let priv = gobj.priv;
68281
68332
  if (!priv.gobj_remote_yuno) {
68282
68333
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: No gobj_remote_yuno defined`);
68283
- return;
68334
+ return -1;
68284
68335
  }
68285
68336
  let command = "update-node";
68286
68337
  let kw = {
@@ -68292,7 +68343,11 @@ function treedb_update_node(gobj, treedb_name, topic_name, record, options) {
68292
68343
  };
68293
68344
  kw.__md_command__ = { topic_name };
68294
68345
  let ret = (0, _yuneta_gobj_js.gobj_command)(priv.gobj_remote_yuno, command, kw, gobj);
68295
- if (ret) (0, _yuneta_gobj_js.log_error)(ret);
68346
+ if (ret) {
68347
+ (0, _yuneta_gobj_js.log_error)(ret);
68348
+ return -1;
68349
+ }
68350
+ return 0;
68296
68351
  }
68297
68352
  /************************************************************
68298
68353
  * Command to remote service
@@ -68565,6 +68620,7 @@ function ac_mt_command_answer$1(gobj, event, kw, src) {
68565
68620
  case "delete-node":
68566
68621
  case "link-nodes":
68567
68622
  case "unlink-nodes":
68623
+ if (result < 0 && command === "update-node") graphs_write_refused(gobj, (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "topic_name", "", 0), (0, _yuneta_gobj_js.kw_get_dict)(gobj, kw_command, "record", {}, 0));
68568
68624
  if (result >= 0 && (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "topic_name", "", 0) !== "__graphs__") (0, _yuneta_gobj_js.gobj_publish_event)(gobj, "EV_RECORD_WRITTEN", {
68569
68625
  treedb_name: (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "treedb_name", "", 0),
68570
68626
  topic_name: (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "topic_name", "", 0),
@@ -68808,12 +68864,40 @@ function ac_create_node(gobj, event, kw, src) {
68808
68864
  * Send to backend
68809
68865
  ********************************************/
68810
68866
  function ac_update_node(gobj, event, kw, src) {
68811
- let priv = gobj.priv;
68812
- if (refuse_if_readonly$1(gobj, event)) return -1;
68813
- let treedb_name = priv.treedb_name;
68867
+ let treedb_name = gobj.priv.treedb_name;
68814
68868
  let topic_name = kw.topic_name;
68815
68869
  let record = kw.record;
68816
- return treedb_update_node(gobj, treedb_name, topic_name, record, kw.options || {});
68870
+ let options = kw.options || {};
68871
+ if (refuse_if_readonly$1(gobj, event)) {
68872
+ graphs_write_refused(gobj, topic_name, record);
68873
+ return -1;
68874
+ }
68875
+ if (topic_name === "__graphs__" && !is_connected(gobj)) {
68876
+ (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: no session, the arrangement was not sent`);
68877
+ graphs_write_refused(gobj, topic_name, record);
68878
+ return -1;
68879
+ }
68880
+ let ret = treedb_update_node(gobj, treedb_name, topic_name, record, options);
68881
+ if (ret < 0) graphs_write_refused(gobj, topic_name, record);
68882
+ return ret;
68883
+ }
68884
+ /************************************************************
68885
+ * A write of `__graphs__` did not land. The engine
68886
+ * (C_G6_NODES_TREE) took it for granted when it left -- nothing
68887
+ * answers it when it lands -- so it is told, and its next Save
68888
+ * writes the topic again. Other topics are the operator's data:
68889
+ * the answer's error says what happened to them.
68890
+ ************************************************************/
68891
+ function graphs_write_refused(gobj, topic_name, record) {
68892
+ let priv = gobj.priv;
68893
+ if (topic_name !== "__graphs__") return;
68894
+ let topic = record && typeof record === "object" ? record.topic || record.id : "";
68895
+ if (!topic) {
68896
+ (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: a refused __graphs__ write names no topic`);
68897
+ return;
68898
+ }
68899
+ if (!priv.gobj_nodes_tree) return;
68900
+ (0, _yuneta_gobj_js.gobj_send_event)(priv.gobj_nodes_tree, "EV_GRAPHS_WRITE_REFUSED", { topic }, gobj);
68817
68901
  }
68818
68902
  /********************************************
68819
68903
  * Message from G6_nodes_tree
@@ -70469,9 +70553,8 @@ function register_c_yui_treedb_schema() {
70469
70553
  * it, and that mark lived in the memory of the session only:
70470
70554
  * a reload of the page, a reconnect or a refresh of the model
70471
70555
  * lost the chip, the banner and the export warning while
70472
- * __system__ still differed from the file in use (N13 of the
70473
- * 2026-09-22 review). The host reads that difference from
70474
- * C_TREEDB's `saved-schema` (`draft_changed`, by treedb) and
70556
+ * __system__ still differed from the file in use. The host
70557
+ * reads that difference from C_TREEDB's `saved-schema` (`draft_changed`, by treedb) and
70475
70558
  * hands it here; the editor applies it to its model, in place
70476
70559
  * of what the host said before.
70477
70560
  *
@@ -72139,11 +72222,10 @@ function write_options(op) {
72139
72222
  * AN EDIT IS A DRAFT. A write here moves no version and reaches no
72140
72223
  * treedb: the host SAVES it (C_TREEDB's save-schema raises the
72141
72224
  * topic_version of what changed and the schema_version, once) and
72142
- * APPLIES it (apply-schema + a restart of the owning yuno). Until
72143
- * the owner's design of M36 (2026-09-21 review) every write here
72144
- * raised both versions, so an edit half made was already the
72145
- * schema of the next start. The topic list marks what this session
72146
- * wrote and has not saved (EV_REFRESH forgets it: the view's own
72225
+ * APPLIES it (apply-schema + a restart of the owning yuno). If a
72226
+ * write here raised both versions, an edit half made would already
72227
+ * be the schema of the next start. The topic list marks what this
72228
+ * session wrote and has not saved (EV_REFRESH forgets it: the view's own
72147
72229
  * Refresh button sends it, and a host may after a save -- the
72148
72230
  * agent console's does not) and what the host says is not saved
72149
72231
  * (EV_DRAFTS, replaced whole each time it is sent).
@@ -72209,6 +72291,7 @@ var HOOK_TOPICS = "topics";
72209
72291
  var HOOK_COLS = "cols";
72210
72292
  var DIAGRAM_SEG = "diagram";
72211
72293
  var PURPOSE = "schema_editor";
72294
+ var DIALOGS_THAT_READ_NOTHING = ["SCHEMA_EXPORT_DIALOG", "SCHEMA_REPORT_DIALOG"];
72212
72295
  /***************************************************************
72213
72296
  * Attrs
72214
72297
  ***************************************************************/
@@ -72241,6 +72324,7 @@ var PRIVATE_DATA$1 = {
72241
72324
  diagram_gobj: null,
72242
72325
  dialog: null,
72243
72326
  dialog_gen: 0,
72327
+ dialog_kind: "",
72244
72328
  save_queue: null,
72245
72329
  save_return: "",
72246
72330
  save_wrote: false,
@@ -72409,9 +72493,9 @@ function refuse_if_readonly(gobj, event) {
72409
72493
  * dumped by the machine trace).
72410
72494
  *
72411
72495
  * Refused, and said. A column form sends every field it shows, so
72412
- * its Save after a reload wrote the OLD record over the newer one
72413
- * (fourth independent review, probe A3); a form whose column the
72414
- * reload removed answered "names no column" for ever. end_load()
72496
+ * its Save after a reload wrote the OLD record over the newer one;
72497
+ * a form whose column the reload removed answered "names no column"
72498
+ * for ever. end_load()
72415
72499
  * closes such a dialog when the load lands; this is the same rule
72416
72500
  * for whatever arrives anyway -- a stale screen, a confirmation
72417
72501
  * that was up, a keyboard.
@@ -72431,8 +72515,7 @@ function built_on_a_replaced_model(gobj, event, kw) {
72431
72515
  * the model shown is kept, and it may be older than the store.
72432
72516
  * The session is up, so no reconnect will ask for it again -- it
72433
72517
  * was owed for as long as the session lasted, and a form opened
72434
- * on that model wrote every field of it over the newer record
72435
- * (fifth independent review; A3 of the fourth, by another path).
72518
+ * on that model wrote every field of it over the newer record.
72436
72519
  *
72437
72520
  * So the operator's NEXT action asks it. An edit -- a form, a
72438
72521
  * write, a confirmation, what reads the model whole (check,
@@ -72482,7 +72565,7 @@ function reload_after_move(gobj) {
72482
72565
  * none of the actions of a screen, so the screen it replaces goes
72483
72566
  * and the toolbar stops answering: left up, a click on a card, on
72484
72567
  * Back or on the drawing answered "Event NOT DEFINED in state
72485
- * ST_LOADING" and was lost (third independent review). A dialog
72568
+ * ST_LOADING" and was lost. A dialog
72486
72569
  * left open is answered in ST_LOADING instead (ac_wait_for_the_load),
72487
72570
  * and a load that LANDS closes it (end_load()): it was built on the
72488
72571
  * model the load replaced, and its Save sends every field it shows.
@@ -73702,6 +73785,7 @@ function close_dialog(gobj) {
73702
73785
  if (!priv.dialog) return;
73703
73786
  let dialog = priv.dialog;
73704
73787
  priv.dialog = null;
73788
+ priv.dialog_kind = "";
73705
73789
  priv.export_view = null;
73706
73790
  try {
73707
73791
  dialog.close();
@@ -73713,6 +73797,7 @@ function open_dialog(gobj, $content, title, logical, title_prefix) {
73713
73797
  let priv = gobj.priv;
73714
73798
  close_dialog(gobj);
73715
73799
  priv.dialog_gen = priv.model_gen;
73800
+ priv.dialog_kind = logical;
73716
73801
  priv.dialog = yui_shell_show_modal(yui_shell_of(gobj), $content, {
73717
73802
  dialog: true,
73718
73803
  title,
@@ -73721,6 +73806,7 @@ function open_dialog(gobj, $content, title, logical, title_prefix) {
73721
73806
  t: i18next.t,
73722
73807
  on_close: () => {
73723
73808
  priv.dialog = null;
73809
+ priv.dialog_kind = "";
73724
73810
  priv.export_view = null;
73725
73811
  }
73726
73812
  });
@@ -74915,11 +75001,11 @@ function transport_in_session(gobj) {
74915
75001
  * The state is part of the question, and that is only true
74916
75002
  * because NOTHING moves the editor out of ST_LOADING or ST_SAVING
74917
75003
  * but the end of what is in flight: a position the host sends
74918
- * meanwhile waits in `pending_seg` (ac_show()). 7.25.6 let EV_SHOW
74919
- * move a RELOAD out of ST_LOADING, and every answer of it was then
74920
- * "for a load that is over": the records it had emptied stayed
74921
- * empty, and the next write patched them into a model with no
74922
- * treedb at all.
75004
+ * meanwhile waits in `pending_seg` (ac_show()). If EV_SHOW moved a
75005
+ * RELOAD out of ST_LOADING, every answer of it would be "for a load
75006
+ * that is over": the records it had emptied would stay empty, and
75007
+ * the next write would patch them into a model with no treedb at
75008
+ * all.
74923
75009
  ***************************************************************/
74924
75010
  function is_current_load(gobj, md) {
74925
75011
  return (0, _yuneta_gobj_js.gobj_current_state)(gobj) === "ST_LOADING" && (0, _yuneta_gobj_js.kw_get_int)(gobj, md, "round", 0, 0) === gobj.priv.load_round;
@@ -75125,10 +75211,59 @@ function position_seg(gobj) {
75125
75211
  * ...and the same in reverse. A tail naming a treedb or a topic
75126
75212
  * the store does not have lands one level up rather than on an
75127
75213
  * empty screen: a shared link outlives the schema it points at.
75214
+ *
75215
+ * Every position the HOST sends comes through here, at once
75216
+ * (ac_show()) or when what was in flight ends (end_load(),
75217
+ * end_writes()). A move to another position closes the dialog of
75218
+ * the screen the editor left (close_dialog_left_behind()).
75128
75219
  ***************************************************************/
75129
75220
  function apply_seg(gobj, seg) {
75221
+ let before = position_seg(gobj);
75130
75222
  let pos = seg_position(seg);
75131
- return go(gobj, pos.treedb_id, pos.topic_name, pos.diagram, false);
75223
+ let ret = go(gobj, pos.treedb_id, pos.topic_name, pos.diagram, false);
75224
+ if (position_seg(gobj) !== before) close_dialog_left_behind(gobj);
75225
+ return ret;
75226
+ }
75227
+ /***************************************************************
75228
+ * The host moved the editor while a dialog was up. C_YUI_SHELL
75229
+ * keeps the overlays open on a move that changes only the subpath
75230
+ * (a url typed in, a link), so the dialog outlives its screen.
75231
+ *
75232
+ * A form, the import and the orphans send events that only the
75233
+ * screen they were opened from declares, and they name their
75234
+ * topic under the treedb open at the time. Left up on another
75235
+ * screen, their Save answered "Event NOT DEFINED" and the edit was
75236
+ * lost with no word. So the dialog is closed and the operator told.
75237
+ * The import plan goes with its dialog: it was computed for the
75238
+ * treedb the editor left.
75239
+ *
75240
+ * The export and the check stay: they show the texts they were
75241
+ * opened with, and what they send is legal in every state.
75242
+ ***************************************************************/
75243
+ function close_dialog_left_behind(gobj) {
75244
+ let priv = gobj.priv;
75245
+ if (!priv.dialog || DIALOGS_THAT_READ_NOTHING.includes(priv.dialog_kind)) return;
75246
+ (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: the host moved the view while the dialog ${priv.dialog_kind} was open: closed`);
75247
+ close_dialog(gobj);
75248
+ forget_import_plan(gobj);
75249
+ yui_shell_show_error(yui_shell_of(gobj), "the view moved: open the dialog again", { t: i18next.t });
75250
+ }
75251
+ /***************************************************************
75252
+ * A confirmation answered on another screen than the one it was
75253
+ * asked from. A confirmation is a shell modal, so a move sent by
75254
+ * the host does not close it. It names its topic and column, and
75255
+ * the actions look them up under the treedb open NOW: a Yes after
75256
+ * a move to another treedb deleted the topic or column of the same
75257
+ * name THERE.
75258
+ *
75259
+ * Refused, and said. A kw with no `seg` stamp is not a
75260
+ * confirmation's and is not asked.
75261
+ ***************************************************************/
75262
+ function asked_on_another_screen(gobj, event, kw) {
75263
+ if (!kw || kw.seg === void 0 || kw.seg === position_seg(gobj)) return false;
75264
+ (0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${event} refused, it was asked on '${kw.seg}' and the view is on '${position_seg(gobj)}'`);
75265
+ yui_shell_show_error(yui_shell_of(gobj), "the view moved: open the dialog again", { t: i18next.t });
75266
+ return true;
75132
75267
  }
75133
75268
  function seg_position(seg) {
75134
75269
  let parts = String(seg || "").split("/").filter((x) => x.length > 0);
@@ -75304,8 +75439,7 @@ function late_write_done(gobj, command, topic_name, data) {
75304
75439
  * The fkey is read with parse_fkey_ref(), because the store answers
75305
75440
  * in list_dict -- `[{topic_name, id, hook_name}]` -- and not in the
75306
75441
  * `topics^<topic id>^cols` strings this view writes. Split as a
75307
- * string, every production answer marked nothing (fourth independent
75308
- * review, probe C).
75442
+ * string, every production answer marked nothing.
75309
75443
  ***************************************************************/
75310
75444
  function mark_written_record(gobj, topic_name, record) {
75311
75445
  let priv = gobj.priv;
@@ -75362,7 +75496,7 @@ function ac_hide(gobj, event, kw, src) {
75362
75496
  *
75363
75497
  * Nothing else ends a load or a write the drop cut: their answers
75364
75498
  * will never come, and waiting in ST_LOADING skipped the reload
75365
- * itself (M-1 of the independent review of 7.25.4).
75499
+ * itself.
75366
75500
  ***************************************************************/
75367
75501
  function ac_transport_state(gobj, event, kw, src) {
75368
75502
  let priv = gobj.priv;
@@ -75401,12 +75535,11 @@ function ac_refresh$1(gobj, event, kw, src) {
75401
75535
  * a model the answers in the air are about to replace.
75402
75536
  *
75403
75537
  * Refused, and SAID. Undeclared, it answered "Event NOT DEFINED
75404
- * in state ST_LOADING" and the edit was gone with no word (third
75405
- * independent review).
75538
+ * in state ST_LOADING" and the edit was gone with no word.
75406
75539
  *
75407
75540
  * And NOT "try again": the same Save, once the load was in, sent
75408
75541
  * the form built on the old model -- every field of the old record
75409
- * over the newer one (fourth independent review). The load that
75542
+ * over the newer one. The load that
75410
75543
  * lands closes such a dialog (end_load()); one that fails leaves it
75411
75544
  * up, on the model it was built on.
75412
75545
  ***************************************************************/
@@ -75420,8 +75553,7 @@ function ac_wait_for_the_load(gobj, event, kw, src) {
75420
75553
  * reload landed on zero treedbs while it was up) or ST_IDLE (no
75421
75554
  * model). A confirmation is a shell modal, not this view's dialog,
75422
75555
  * so the load that lands does not close it, and its Yes arrives
75423
- * anyway. Undeclared, it answered "Event NOT DEFINED" (fifth
75424
- * independent review).
75556
+ * anyway. Undeclared, it answered "Event NOT DEFINED".
75425
75557
  *
75426
75558
  * Refused with the stamp's own words: whatever it was decided on
75427
75559
  * is not on screen any more.
@@ -75437,13 +75569,12 @@ function ac_confirmed_on_nothing(gobj, event, kw, src) {
75437
75569
  * topics hold a draft in __system__ that is not saved (C_TREEDB's
75438
75570
  * saved-schema, `draft_changed`). The mark of a write lived in this
75439
75571
  * session's memory only, so a reload of the page, a reconnect or a
75440
- * refresh showed no draft while __system__ still differed (N13 of
75441
- * the 2026-09-22 review).
75572
+ * refresh showed no draft while __system__ still differed.
75442
75573
  *
75443
75574
  * REPLACES what the host said before -- the host is the truth after
75444
75575
  * a reload, and a Save is exactly the host saying "none any more".
75445
75576
  * It only ever added, and a topic stayed a draft after the Save that
75446
- * published it (M1 of the 2026-09-23 review). What this session wrote
75577
+ * published it. What this session wrote
75447
75578
  * stays marked: it is kept apart (see topic_is_draft()).
75448
75579
  ***************************************************************/
75449
75580
  function ac_drafts(gobj, event, kw, src) {
@@ -75500,7 +75631,10 @@ function ac_show_orphans(gobj, event, kw, src) {
75500
75631
  * to feed.
75501
75632
  ***************************************************************/
75502
75633
  function confirm_then(gobj, message, detail, kw) {
75503
- kw = Object.assign({ model_gen: gobj.priv.model_gen }, kw);
75634
+ kw = Object.assign({
75635
+ model_gen: gobj.priv.model_gen,
75636
+ seg: position_seg(gobj)
75637
+ }, kw);
75504
75638
  yui_shell_confirm_danger(yui_shell_of(gobj), message, {
75505
75639
  t: i18next.t,
75506
75640
  detail,
@@ -75886,6 +76020,7 @@ function ac_confirmed(gobj, event, kw, src) {
75886
76020
  if (refuse_if_readonly(gobj, event)) return -1;
75887
76021
  if (reload_first(gobj, event)) return -1;
75888
76022
  if (built_on_a_replaced_model(gobj, event, kw)) return -1;
76023
+ if (asked_on_another_screen(gobj, event, kw)) return -1;
75889
76024
  if (kw.what === "import") return run_import(gobj);
75890
76025
  if (kw.what === "topic") {
75891
76026
  let topic = find_topic(priv.model, priv.treedb_id, kw.topic);