@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.
@@ -3033,9 +3033,8 @@ function maybe_apply_translator($node, opts) {
3033
3033
  * it -- until every handle is closed or has timed out. Handed the
3034
3034
  * first caller's handle and timer, a repeat asking `timeout: 0`
3035
3035
  * was dismissed on the first one's timer, and closing one
3036
- * caller's handle closed a toast another was still showing
3037
- * (third independent review). The operator's ✕ closes it for
3038
- * everybody.
3036
+ * caller's handle closed a toast another was still showing.
3037
+ * The operator's ✕ closes it for everybody.
3039
3038
  ***************************************************************/
3040
3039
  function show_notification(shell, kind, message, opts) {
3041
3040
  let $layer = notification_layer(shell);
@@ -7420,7 +7419,7 @@ function json_diff_rows(doc_a, doc_b) {
7420
7419
  * A Date -> the value of a `datetime-local` input, in local time,
7421
7420
  * WITH seconds: the input is built with `step: 1`, which is what
7422
7421
  * lets it hold them. Written as "YYYY-MM-DDTHH:mm" it lost them on
7423
- * every save of any field (M26 of the 2026-09-21 review).
7422
+ * every save of any field.
7424
7423
  ************************************************************/
7425
7424
  function date_to_datetime_local(value) {
7426
7425
  if (!(value instanceof Date) || isNaN(value.getTime())) return "";
@@ -23125,8 +23124,7 @@ function yui_shell_show_route_map(shell, opts) {
23125
23124
  * answers, so the host has to answer EVERY way a write ends --
23126
23125
  * and the transport closing under a write in flight, or a
23127
23126
  * command that never left because there was no session, are two
23128
- * of them that nobody answered: the form stayed busy for ever
23129
- * (N8 of the 2026-09-22 review).
23127
+ * of them that nobody answered: the form stayed busy for ever.
23130
23128
  *
23131
23129
  * A write is known by its TOPIC and its `form_write` serial,
23132
23130
  * never by the serial alone: each topic has its own form, and
@@ -23752,7 +23750,7 @@ var __gclass__$6 = null;
23752
23750
  ***************************************************************/
23753
23751
  function mt_create$6(gobj) {
23754
23752
  gobj.priv.form_writes = {};
23755
- gobj.priv.reload_on_session = {};
23753
+ gobj.priv.reread_on_session = false;
23756
23754
  gobj.priv.conn_shell = null;
23757
23755
  build_ui$6(gobj);
23758
23756
  let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
@@ -25188,7 +25186,7 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
25188
25186
  if (is_connected$1(gobj)) gobj_send_event(gobj, "EV_REFRESH_TOPIC", { topic_name: failed_topic }, gobj);
25189
25187
  else {
25190
25188
  log_warning(`${gobj_short_name(gobj)}: the write of '${failed_topic}' was cut by the drop: the topic is read again when the session is back`);
25191
- gobj.priv.reload_on_session[failed_topic] = true;
25189
+ gobj.priv.reread_on_session = true;
25192
25190
  }
25193
25191
  }
25194
25192
  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);
@@ -25389,30 +25387,34 @@ function ac_back_to_topics(gobj, event, kw, src) {
25389
25387
  function ac_transport_state$2(gobj, event, kw, src) {
25390
25388
  let connected = !!(kw && kw.connected);
25391
25389
  refresh_toolbar_buttons(gobj, connected);
25392
- if (!connected) abandon_form_writes(gobj.priv.form_writes).forEach((w) => {
25393
- log_warning(`${gobj_short_name(gobj)}: transport closed, the write ${w.form_write} of '${w.topic_name}' is lost`);
25394
- if (w.topic_name) gobj.priv.reload_on_session[w.topic_name] = true;
25395
- answer_form_write(gobj, get_gobj_formtable(gobj, w.topic_name), w.form_write, false);
25396
- });
25397
- else reload_owed_topics(gobj);
25390
+ if (!connected) {
25391
+ gobj.priv.reread_on_session = true;
25392
+ abandon_form_writes(gobj.priv.form_writes).forEach((w) => {
25393
+ log_warning(`${gobj_short_name(gobj)}: transport closed, the write ${w.form_write} of '${w.topic_name}' is lost`);
25394
+ answer_form_write(gobj, get_gobj_formtable(gobj, w.topic_name), w.form_write, false);
25395
+ });
25396
+ } else reread_open_topics(gobj);
25398
25397
  return 0;
25399
25398
  }
25400
25399
  /************************************************************
25401
- * The session is back: read again the topics a drop left
25402
- * unknown. Only when the TRANSPORT says so too -- the app's
25403
- * connection (EV_CONNECTION_STATE) can report "up" before the
25404
- * transport of this view is in session, and a read asked then is
25405
- * refused. What is owed stays owed until an edge finds the
25406
- * transport in session; each topic is read once.
25400
+ * The session is back after a drop: read every open table again,
25401
+ * as the schema editor reloads its model. A drop cuts the writes
25402
+ * in flight AND the node events published meanwhile, so any
25403
+ * table may be stale, not only the one a cut write left.
25404
+ *
25405
+ * Only when the TRANSPORT says so too -- the app's connection
25406
+ * (EV_CONNECTION_STATE) can report "up" before the transport of
25407
+ * this view is in session, and a read asked then is refused. The
25408
+ * read waits for an edge that finds the transport in session, and
25409
+ * is done once, though both edges report the same "up".
25407
25410
  ************************************************************/
25408
- function reload_owed_topics(gobj) {
25411
+ function reread_open_topics(gobj) {
25409
25412
  let priv = gobj.priv;
25410
- if (!is_connected$1(gobj)) return;
25411
- let topics = Object.keys(priv.reload_on_session);
25412
- priv.reload_on_session = {};
25413
- topics.forEach((topic_name) => {
25414
- if (!get_gobj_formtable(gobj, topic_name)) return;
25415
- gobj_send_event(gobj, "EV_REFRESH_TOPIC", { topic_name }, gobj);
25413
+ if (!priv.reread_on_session || !is_connected$1(gobj)) return;
25414
+ priv.reread_on_session = false;
25415
+ gobj_match_children(gobj, { __gclass_name__: "C_YUI_TREEDB_TOPIC_WITH_FORM" }).forEach((form) => {
25416
+ let topic_name = gobj_read_attr(form, "topic_name");
25417
+ if (topic_name) gobj_send_event(gobj, "EV_REFRESH_TOPIC", { topic_name }, gobj);
25416
25418
  });
25417
25419
  }
25418
25420
  /************************************************************
@@ -25673,8 +25675,8 @@ function ac_update_record(gobj, event, kw, src) {
25673
25675
  /************************************************************
25674
25676
  * A write sent with no session never leaves: C_IEVENT_CLI logs
25675
25677
  * "Not in session" and answers null, as it does when the command
25676
- * went -- so a form waiting for the answer waited for ever (N8 of
25677
- * the 2026-09-22 review). Refused before it is sent, aloud.
25678
+ * went -- so a form waiting for the answer waited for ever. Refused
25679
+ * before it is sent, aloud.
25678
25680
  ************************************************************/
25679
25681
  function refuse_without_session(gobj, topic_name) {
25680
25682
  if (is_connected$1(gobj)) return false;
@@ -26786,9 +26788,8 @@ function delete_impact(desc, records) {
26786
26788
  * that back. So a second busy(true) while already busy must not
26787
26789
  * run at all -- it read the buttons it had just disabled as
26788
26790
  * "disabled on their own", and the busy(false) of a refused write
26789
- * left Save and Cancel dead (N9 of the 2026-09-22 review: a record
26790
- * with a picked file went busy for the read, then busy again for
26791
- * the write). The caller owns `state` ({busy}) for the form's life.
26791
+ * left Save and Cancel dead (a record with a picked file went
26792
+ * busy for the read, then busy again for the write). The caller owns `state` ({busy}) for the form's life.
26792
26793
  *
26793
26794
  * Copyright (c) 2026, ArtGins.
26794
26795
  * All Rights Reserved.
@@ -26826,7 +26827,7 @@ function set_toolbar_busy($container, state, busy) {
26826
26827
  * (only its own `plaintext` formatter escapes), so a record's text
26827
26828
  * returned as a string is parsed as markup: `a<b and c>d` showed
26828
26829
  * as `ad`, and a field holding `<img src=x onerror=...>` ran it in
26829
- * the operator's browser (M31 of the 2026-09-21 review). Record
26830
+ * the operator's browser. Record
26830
26831
  * data goes in as a TEXT NODE, and a cell built around it as DOM,
26831
26832
  * never as an HTML string with the data interpolated.
26832
26833
  *
@@ -29588,7 +29589,7 @@ function ac_new_row(gobj, event, kw, src) {
29588
29589
  * What crosses the confirmation is the rows' IDENTITY, never their
29589
29590
  * position: the view applies EV_TREEDB_NODE_* of every writer while the
29590
29591
  * dialog is open, and a position names whatever row sits there when the
29591
- * person answers -- with force:true (A6 of the 2026-09-21 review).
29592
+ * person answers -- with force:true.
29592
29593
  ************************************************************/
29593
29594
  function ac_delete_rows(gobj, event, kw, src) {
29594
29595
  if (refuse_if_readonly$2(gobj, event)) return -1;
@@ -29799,8 +29800,8 @@ function ac_files_read(gobj, event, kw, src) {
29799
29800
  * open and BUSY, and the serial it waits for is taken here.
29800
29801
  *
29801
29802
  * A refused write used to close the form all the same and throw
29802
- * away what was typed, although the README said it stayed open
29803
- * (M25 of the 2026-09-21 review): the close was posted with the
29803
+ * away what was typed, although the README said it stayed open:
29804
+ * the close was posted with the
29804
29805
  * publish, before the backend had answered anything.
29805
29806
  ************************************************************/
29806
29807
  function wait_for_write_answer(gobj) {
@@ -30972,6 +30973,28 @@ function apply_graphs_echo(live, saved, records, rec) {
30972
30973
  delete saved[rec.topic];
30973
30974
  }
30974
30975
  }
30976
+ /***************************************************************
30977
+ * A write of one topic's arrangement did NOT land: the backend
30978
+ * refused it, the transport refused it on the way out, or there
30979
+ * was no session to send it on.
30980
+ *
30981
+ * saved {topic_name: properties} what the backend has
30982
+ * topic_name the topic of the refused record
30983
+ *
30984
+ * The view records a write as saved BEFORE it leaves, because
30985
+ * nothing answers it when it lands. So a refused one left the view
30986
+ * believing the backend held an arrangement it does not, and the
30987
+ * next Save found nothing to write. What the backend holds for the
30988
+ * topic is not known any more, so it is forgotten: the next Save
30989
+ * writes the topic again (plan_graph_saves()).
30990
+ *
30991
+ * Returns TRUE if there was something to forget.
30992
+ ***************************************************************/
30993
+ function forget_refused_graphs_write(saved, topic_name) {
30994
+ if (!is_object(saved) || !topic_name || !Object.prototype.hasOwnProperty.call(saved, topic_name)) return false;
30995
+ delete saved[topic_name];
30996
+ return true;
30997
+ }
30975
30998
  //#endregion
30976
30999
  //#region src/treedb_node_label.js
30977
31000
  /***********************************************************************
@@ -65779,6 +65802,28 @@ function ac_save_graph(gobj, event, kw, src) {
65779
65802
  return 0;
65780
65803
  }
65781
65804
  /************************************************************
65805
+ * The host says a write of `__graphs__` did not land: the
65806
+ * backend refused it, the transport refused it on the way out,
65807
+ * or there was no session to send it on. kw: {topic}
65808
+ *
65809
+ * save_topic_graph_properties() recorded it as saved before it
65810
+ * left. Forgotten now, and the Save is lit again: the next Save
65811
+ * writes the topic again. Before, the view believed the backend
65812
+ * held the arrangement, and the next Save had nothing to write.
65813
+ ************************************************************/
65814
+ function ac_graphs_write_refused(gobj, event, kw, src) {
65815
+ let priv = gobj.priv;
65816
+ let topic_name = kw && kw.topic;
65817
+ if (!topic_name) {
65818
+ log_error(`${gobj_short_name(gobj)}: ${event} names no topic`);
65819
+ return -1;
65820
+ }
65821
+ log_warning(`${gobj_short_name(gobj)}: the arrangement of '${topic_name}' was not saved: the next Save writes it again`);
65822
+ forget_refused_graphs_write(priv._saved_graph_properties, topic_name);
65823
+ if (priv.edit_mode) mark_graph_dirty(gobj);
65824
+ return 0;
65825
+ }
65826
+ /************************************************************
65782
65827
  * Node created, from subscription
65783
65828
  ************************************************************/
65784
65829
  function ac_node_created(gobj, event, kw, src) {
@@ -66846,6 +66891,11 @@ function create_gclass$4(gclass_name) {
66846
66891
  ac_node_deleted,
66847
66892
  null
66848
66893
  ],
66894
+ [
66895
+ "EV_GRAPHS_WRITE_REFUSED",
66896
+ ac_graphs_write_refused,
66897
+ null
66898
+ ],
66849
66899
  [
66850
66900
  "EV_NODE_CLICK",
66851
66901
  ac_node_click$2,
@@ -67099,6 +67149,7 @@ function create_gclass$4(gclass_name) {
67099
67149
  ["EV_NODE_CREATED", 0],
67100
67150
  ["EV_NODE_UPDATED", 0],
67101
67151
  ["EV_NODE_DELETED", 0],
67152
+ ["EV_GRAPHS_WRITE_REFUSED", 0],
67102
67153
  ["EV_NODE_CLICK", 0],
67103
67154
  ["EV_EDGE_CLICK", 0],
67104
67155
  ["EV_NODE_CONTEXT_MENU", 0],
@@ -68275,7 +68326,7 @@ function treedb_update_node(gobj, treedb_name, topic_name, record, options) {
68275
68326
  let priv = gobj.priv;
68276
68327
  if (!priv.gobj_remote_yuno) {
68277
68328
  log_error(`${gobj_short_name(gobj)}: No gobj_remote_yuno defined`);
68278
- return;
68329
+ return -1;
68279
68330
  }
68280
68331
  let command = "update-node";
68281
68332
  let kw = {
@@ -68287,7 +68338,11 @@ function treedb_update_node(gobj, treedb_name, topic_name, record, options) {
68287
68338
  };
68288
68339
  kw.__md_command__ = { topic_name };
68289
68340
  let ret = gobj_command(priv.gobj_remote_yuno, command, kw, gobj);
68290
- if (ret) log_error(ret);
68341
+ if (ret) {
68342
+ log_error(ret);
68343
+ return -1;
68344
+ }
68345
+ return 0;
68291
68346
  }
68292
68347
  /************************************************************
68293
68348
  * Command to remote service
@@ -68560,6 +68615,7 @@ function ac_mt_command_answer$1(gobj, event, kw, src) {
68560
68615
  case "delete-node":
68561
68616
  case "link-nodes":
68562
68617
  case "unlink-nodes":
68618
+ if (result < 0 && command === "update-node") graphs_write_refused(gobj, kw_get_str(gobj, kw_command, "topic_name", "", 0), kw_get_dict(gobj, kw_command, "record", {}, 0));
68563
68619
  if (result >= 0 && kw_get_str(gobj, kw_command, "topic_name", "", 0) !== "__graphs__") gobj_publish_event(gobj, "EV_RECORD_WRITTEN", {
68564
68620
  treedb_name: kw_get_str(gobj, kw_command, "treedb_name", "", 0),
68565
68621
  topic_name: kw_get_str(gobj, kw_command, "topic_name", "", 0),
@@ -68803,12 +68859,40 @@ function ac_create_node(gobj, event, kw, src) {
68803
68859
  * Send to backend
68804
68860
  ********************************************/
68805
68861
  function ac_update_node(gobj, event, kw, src) {
68806
- let priv = gobj.priv;
68807
- if (refuse_if_readonly$1(gobj, event)) return -1;
68808
- let treedb_name = priv.treedb_name;
68862
+ let treedb_name = gobj.priv.treedb_name;
68809
68863
  let topic_name = kw.topic_name;
68810
68864
  let record = kw.record;
68811
- return treedb_update_node(gobj, treedb_name, topic_name, record, kw.options || {});
68865
+ let options = kw.options || {};
68866
+ if (refuse_if_readonly$1(gobj, event)) {
68867
+ graphs_write_refused(gobj, topic_name, record);
68868
+ return -1;
68869
+ }
68870
+ if (topic_name === "__graphs__" && !is_connected(gobj)) {
68871
+ log_warning(`${gobj_short_name(gobj)}: no session, the arrangement was not sent`);
68872
+ graphs_write_refused(gobj, topic_name, record);
68873
+ return -1;
68874
+ }
68875
+ let ret = treedb_update_node(gobj, treedb_name, topic_name, record, options);
68876
+ if (ret < 0) graphs_write_refused(gobj, topic_name, record);
68877
+ return ret;
68878
+ }
68879
+ /************************************************************
68880
+ * A write of `__graphs__` did not land. The engine
68881
+ * (C_G6_NODES_TREE) took it for granted when it left -- nothing
68882
+ * answers it when it lands -- so it is told, and its next Save
68883
+ * writes the topic again. Other topics are the operator's data:
68884
+ * the answer's error says what happened to them.
68885
+ ************************************************************/
68886
+ function graphs_write_refused(gobj, topic_name, record) {
68887
+ let priv = gobj.priv;
68888
+ if (topic_name !== "__graphs__") return;
68889
+ let topic = record && typeof record === "object" ? record.topic || record.id : "";
68890
+ if (!topic) {
68891
+ log_error(`${gobj_short_name(gobj)}: a refused __graphs__ write names no topic`);
68892
+ return;
68893
+ }
68894
+ if (!priv.gobj_nodes_tree) return;
68895
+ gobj_send_event(priv.gobj_nodes_tree, "EV_GRAPHS_WRITE_REFUSED", { topic }, gobj);
68812
68896
  }
68813
68897
  /********************************************
68814
68898
  * Message from G6_nodes_tree
@@ -70464,9 +70548,8 @@ function register_c_yui_treedb_schema() {
70464
70548
  * it, and that mark lived in the memory of the session only:
70465
70549
  * a reload of the page, a reconnect or a refresh of the model
70466
70550
  * lost the chip, the banner and the export warning while
70467
- * __system__ still differed from the file in use (N13 of the
70468
- * 2026-09-22 review). The host reads that difference from
70469
- * C_TREEDB's `saved-schema` (`draft_changed`, by treedb) and
70551
+ * __system__ still differed from the file in use. The host
70552
+ * reads that difference from C_TREEDB's `saved-schema` (`draft_changed`, by treedb) and
70470
70553
  * hands it here; the editor applies it to its model, in place
70471
70554
  * of what the host said before.
70472
70555
  *
@@ -72134,11 +72217,10 @@ function write_options(op) {
72134
72217
  * AN EDIT IS A DRAFT. A write here moves no version and reaches no
72135
72218
  * treedb: the host SAVES it (C_TREEDB's save-schema raises the
72136
72219
  * topic_version of what changed and the schema_version, once) and
72137
- * APPLIES it (apply-schema + a restart of the owning yuno). Until
72138
- * the owner's design of M36 (2026-09-21 review) every write here
72139
- * raised both versions, so an edit half made was already the
72140
- * schema of the next start. The topic list marks what this session
72141
- * wrote and has not saved (EV_REFRESH forgets it: the view's own
72220
+ * APPLIES it (apply-schema + a restart of the owning yuno). If a
72221
+ * write here raised both versions, an edit half made would already
72222
+ * be the schema of the next start. The topic list marks what this
72223
+ * session wrote and has not saved (EV_REFRESH forgets it: the view's own
72142
72224
  * Refresh button sends it, and a host may after a save -- the
72143
72225
  * agent console's does not) and what the host says is not saved
72144
72226
  * (EV_DRAFTS, replaced whole each time it is sent).
@@ -72204,6 +72286,7 @@ var HOOK_TOPICS = "topics";
72204
72286
  var HOOK_COLS = "cols";
72205
72287
  var DIAGRAM_SEG = "diagram";
72206
72288
  var PURPOSE = "schema_editor";
72289
+ var DIALOGS_THAT_READ_NOTHING = ["SCHEMA_EXPORT_DIALOG", "SCHEMA_REPORT_DIALOG"];
72207
72290
  /***************************************************************
72208
72291
  * Attrs
72209
72292
  ***************************************************************/
@@ -72236,6 +72319,7 @@ var PRIVATE_DATA$1 = {
72236
72319
  diagram_gobj: null,
72237
72320
  dialog: null,
72238
72321
  dialog_gen: 0,
72322
+ dialog_kind: "",
72239
72323
  save_queue: null,
72240
72324
  save_return: "",
72241
72325
  save_wrote: false,
@@ -72404,9 +72488,9 @@ function refuse_if_readonly(gobj, event) {
72404
72488
  * dumped by the machine trace).
72405
72489
  *
72406
72490
  * Refused, and said. A column form sends every field it shows, so
72407
- * its Save after a reload wrote the OLD record over the newer one
72408
- * (fourth independent review, probe A3); a form whose column the
72409
- * reload removed answered "names no column" for ever. end_load()
72491
+ * its Save after a reload wrote the OLD record over the newer one;
72492
+ * a form whose column the reload removed answered "names no column"
72493
+ * for ever. end_load()
72410
72494
  * closes such a dialog when the load lands; this is the same rule
72411
72495
  * for whatever arrives anyway -- a stale screen, a confirmation
72412
72496
  * that was up, a keyboard.
@@ -72426,8 +72510,7 @@ function built_on_a_replaced_model(gobj, event, kw) {
72426
72510
  * the model shown is kept, and it may be older than the store.
72427
72511
  * The session is up, so no reconnect will ask for it again -- it
72428
72512
  * was owed for as long as the session lasted, and a form opened
72429
- * on that model wrote every field of it over the newer record
72430
- * (fifth independent review; A3 of the fourth, by another path).
72513
+ * on that model wrote every field of it over the newer record.
72431
72514
  *
72432
72515
  * So the operator's NEXT action asks it. An edit -- a form, a
72433
72516
  * write, a confirmation, what reads the model whole (check,
@@ -72477,7 +72560,7 @@ function reload_after_move(gobj) {
72477
72560
  * none of the actions of a screen, so the screen it replaces goes
72478
72561
  * and the toolbar stops answering: left up, a click on a card, on
72479
72562
  * Back or on the drawing answered "Event NOT DEFINED in state
72480
- * ST_LOADING" and was lost (third independent review). A dialog
72563
+ * ST_LOADING" and was lost. A dialog
72481
72564
  * left open is answered in ST_LOADING instead (ac_wait_for_the_load),
72482
72565
  * and a load that LANDS closes it (end_load()): it was built on the
72483
72566
  * model the load replaced, and its Save sends every field it shows.
@@ -73697,6 +73780,7 @@ function close_dialog(gobj) {
73697
73780
  if (!priv.dialog) return;
73698
73781
  let dialog = priv.dialog;
73699
73782
  priv.dialog = null;
73783
+ priv.dialog_kind = "";
73700
73784
  priv.export_view = null;
73701
73785
  try {
73702
73786
  dialog.close();
@@ -73708,6 +73792,7 @@ function open_dialog(gobj, $content, title, logical, title_prefix) {
73708
73792
  let priv = gobj.priv;
73709
73793
  close_dialog(gobj);
73710
73794
  priv.dialog_gen = priv.model_gen;
73795
+ priv.dialog_kind = logical;
73711
73796
  priv.dialog = yui_shell_show_modal(yui_shell_of(gobj), $content, {
73712
73797
  dialog: true,
73713
73798
  title,
@@ -73716,6 +73801,7 @@ function open_dialog(gobj, $content, title, logical, title_prefix) {
73716
73801
  t,
73717
73802
  on_close: () => {
73718
73803
  priv.dialog = null;
73804
+ priv.dialog_kind = "";
73719
73805
  priv.export_view = null;
73720
73806
  }
73721
73807
  });
@@ -74910,11 +74996,11 @@ function transport_in_session(gobj) {
74910
74996
  * The state is part of the question, and that is only true
74911
74997
  * because NOTHING moves the editor out of ST_LOADING or ST_SAVING
74912
74998
  * but the end of what is in flight: a position the host sends
74913
- * meanwhile waits in `pending_seg` (ac_show()). 7.25.6 let EV_SHOW
74914
- * move a RELOAD out of ST_LOADING, and every answer of it was then
74915
- * "for a load that is over": the records it had emptied stayed
74916
- * empty, and the next write patched them into a model with no
74917
- * treedb at all.
74999
+ * meanwhile waits in `pending_seg` (ac_show()). If EV_SHOW moved a
75000
+ * RELOAD out of ST_LOADING, every answer of it would be "for a load
75001
+ * that is over": the records it had emptied would stay empty, and
75002
+ * the next write would patch them into a model with no treedb at
75003
+ * all.
74918
75004
  ***************************************************************/
74919
75005
  function is_current_load(gobj, md) {
74920
75006
  return gobj_current_state(gobj) === "ST_LOADING" && kw_get_int(gobj, md, "round", 0, 0) === gobj.priv.load_round;
@@ -75120,10 +75206,59 @@ function position_seg(gobj) {
75120
75206
  * ...and the same in reverse. A tail naming a treedb or a topic
75121
75207
  * the store does not have lands one level up rather than on an
75122
75208
  * empty screen: a shared link outlives the schema it points at.
75209
+ *
75210
+ * Every position the HOST sends comes through here, at once
75211
+ * (ac_show()) or when what was in flight ends (end_load(),
75212
+ * end_writes()). A move to another position closes the dialog of
75213
+ * the screen the editor left (close_dialog_left_behind()).
75123
75214
  ***************************************************************/
75124
75215
  function apply_seg(gobj, seg) {
75216
+ let before = position_seg(gobj);
75125
75217
  let pos = seg_position(seg);
75126
- return go(gobj, pos.treedb_id, pos.topic_name, pos.diagram, false);
75218
+ let ret = go(gobj, pos.treedb_id, pos.topic_name, pos.diagram, false);
75219
+ if (position_seg(gobj) !== before) close_dialog_left_behind(gobj);
75220
+ return ret;
75221
+ }
75222
+ /***************************************************************
75223
+ * The host moved the editor while a dialog was up. C_YUI_SHELL
75224
+ * keeps the overlays open on a move that changes only the subpath
75225
+ * (a url typed in, a link), so the dialog outlives its screen.
75226
+ *
75227
+ * A form, the import and the orphans send events that only the
75228
+ * screen they were opened from declares, and they name their
75229
+ * topic under the treedb open at the time. Left up on another
75230
+ * screen, their Save answered "Event NOT DEFINED" and the edit was
75231
+ * lost with no word. So the dialog is closed and the operator told.
75232
+ * The import plan goes with its dialog: it was computed for the
75233
+ * treedb the editor left.
75234
+ *
75235
+ * The export and the check stay: they show the texts they were
75236
+ * opened with, and what they send is legal in every state.
75237
+ ***************************************************************/
75238
+ function close_dialog_left_behind(gobj) {
75239
+ let priv = gobj.priv;
75240
+ if (!priv.dialog || DIALOGS_THAT_READ_NOTHING.includes(priv.dialog_kind)) return;
75241
+ log_warning(`${gobj_short_name(gobj)}: the host moved the view while the dialog ${priv.dialog_kind} was open: closed`);
75242
+ close_dialog(gobj);
75243
+ forget_import_plan(gobj);
75244
+ yui_shell_show_error(yui_shell_of(gobj), "the view moved: open the dialog again", { t });
75245
+ }
75246
+ /***************************************************************
75247
+ * A confirmation answered on another screen than the one it was
75248
+ * asked from. A confirmation is a shell modal, so a move sent by
75249
+ * the host does not close it. It names its topic and column, and
75250
+ * the actions look them up under the treedb open NOW: a Yes after
75251
+ * a move to another treedb deleted the topic or column of the same
75252
+ * name THERE.
75253
+ *
75254
+ * Refused, and said. A kw with no `seg` stamp is not a
75255
+ * confirmation's and is not asked.
75256
+ ***************************************************************/
75257
+ function asked_on_another_screen(gobj, event, kw) {
75258
+ if (!kw || kw.seg === void 0 || kw.seg === position_seg(gobj)) return false;
75259
+ log_warning(`${gobj_short_name(gobj)}: ${event} refused, it was asked on '${kw.seg}' and the view is on '${position_seg(gobj)}'`);
75260
+ yui_shell_show_error(yui_shell_of(gobj), "the view moved: open the dialog again", { t });
75261
+ return true;
75127
75262
  }
75128
75263
  function seg_position(seg) {
75129
75264
  let parts = String(seg || "").split("/").filter((x) => x.length > 0);
@@ -75299,8 +75434,7 @@ function late_write_done(gobj, command, topic_name, data) {
75299
75434
  * The fkey is read with parse_fkey_ref(), because the store answers
75300
75435
  * in list_dict -- `[{topic_name, id, hook_name}]` -- and not in the
75301
75436
  * `topics^<topic id>^cols` strings this view writes. Split as a
75302
- * string, every production answer marked nothing (fourth independent
75303
- * review, probe C).
75437
+ * string, every production answer marked nothing.
75304
75438
  ***************************************************************/
75305
75439
  function mark_written_record(gobj, topic_name, record) {
75306
75440
  let priv = gobj.priv;
@@ -75357,7 +75491,7 @@ function ac_hide(gobj, event, kw, src) {
75357
75491
  *
75358
75492
  * Nothing else ends a load or a write the drop cut: their answers
75359
75493
  * will never come, and waiting in ST_LOADING skipped the reload
75360
- * itself (M-1 of the independent review of 7.25.4).
75494
+ * itself.
75361
75495
  ***************************************************************/
75362
75496
  function ac_transport_state(gobj, event, kw, src) {
75363
75497
  let priv = gobj.priv;
@@ -75396,12 +75530,11 @@ function ac_refresh$1(gobj, event, kw, src) {
75396
75530
  * a model the answers in the air are about to replace.
75397
75531
  *
75398
75532
  * Refused, and SAID. Undeclared, it answered "Event NOT DEFINED
75399
- * in state ST_LOADING" and the edit was gone with no word (third
75400
- * independent review).
75533
+ * in state ST_LOADING" and the edit was gone with no word.
75401
75534
  *
75402
75535
  * And NOT "try again": the same Save, once the load was in, sent
75403
75536
  * the form built on the old model -- every field of the old record
75404
- * over the newer one (fourth independent review). The load that
75537
+ * over the newer one. The load that
75405
75538
  * lands closes such a dialog (end_load()); one that fails leaves it
75406
75539
  * up, on the model it was built on.
75407
75540
  ***************************************************************/
@@ -75415,8 +75548,7 @@ function ac_wait_for_the_load(gobj, event, kw, src) {
75415
75548
  * reload landed on zero treedbs while it was up) or ST_IDLE (no
75416
75549
  * model). A confirmation is a shell modal, not this view's dialog,
75417
75550
  * so the load that lands does not close it, and its Yes arrives
75418
- * anyway. Undeclared, it answered "Event NOT DEFINED" (fifth
75419
- * independent review).
75551
+ * anyway. Undeclared, it answered "Event NOT DEFINED".
75420
75552
  *
75421
75553
  * Refused with the stamp's own words: whatever it was decided on
75422
75554
  * is not on screen any more.
@@ -75432,13 +75564,12 @@ function ac_confirmed_on_nothing(gobj, event, kw, src) {
75432
75564
  * topics hold a draft in __system__ that is not saved (C_TREEDB's
75433
75565
  * saved-schema, `draft_changed`). The mark of a write lived in this
75434
75566
  * session's memory only, so a reload of the page, a reconnect or a
75435
- * refresh showed no draft while __system__ still differed (N13 of
75436
- * the 2026-09-22 review).
75567
+ * refresh showed no draft while __system__ still differed.
75437
75568
  *
75438
75569
  * REPLACES what the host said before -- the host is the truth after
75439
75570
  * a reload, and a Save is exactly the host saying "none any more".
75440
75571
  * It only ever added, and a topic stayed a draft after the Save that
75441
- * published it (M1 of the 2026-09-23 review). What this session wrote
75572
+ * published it. What this session wrote
75442
75573
  * stays marked: it is kept apart (see topic_is_draft()).
75443
75574
  ***************************************************************/
75444
75575
  function ac_drafts(gobj, event, kw, src) {
@@ -75495,7 +75626,10 @@ function ac_show_orphans(gobj, event, kw, src) {
75495
75626
  * to feed.
75496
75627
  ***************************************************************/
75497
75628
  function confirm_then(gobj, message, detail, kw) {
75498
- kw = Object.assign({ model_gen: gobj.priv.model_gen }, kw);
75629
+ kw = Object.assign({
75630
+ model_gen: gobj.priv.model_gen,
75631
+ seg: position_seg(gobj)
75632
+ }, kw);
75499
75633
  yui_shell_confirm_danger(yui_shell_of(gobj), message, {
75500
75634
  t,
75501
75635
  detail,
@@ -75881,6 +76015,7 @@ function ac_confirmed(gobj, event, kw, src) {
75881
76015
  if (refuse_if_readonly(gobj, event)) return -1;
75882
76016
  if (reload_first(gobj, event)) return -1;
75883
76017
  if (built_on_a_replaced_model(gobj, event, kw)) return -1;
76018
+ if (asked_on_another_screen(gobj, event, kw)) return -1;
75884
76019
  if (kw.what === "import") return run_import(gobj);
75885
76020
  if (kw.what === "topic") {
75886
76021
  let topic = find_topic(priv.model, priv.treedb_id, kw.topic);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.25.13",
3
+ "version": "7.25.15",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",