@yuneta/gobj-ui 7.25.8 → 7.25.10

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.
@@ -3057,6 +3057,8 @@ function show_notification(shell, kind, message, opts) {
3057
3057
  },
3058
3058
  [["button", {
3059
3059
  class: "TOAST_CLOSE delete",
3060
+ title: t("close"),
3061
+ "data-i18n-title": "close",
3060
3062
  "aria-label": t("close"),
3061
3063
  "data-i18n-aria-label": "close"
3062
3064
  }], [
@@ -3154,6 +3156,8 @@ function yui_shell_show_modal(shell, content, opts) {
3154
3156
  {
3155
3157
  class: "MODAL_BACK yui-dialog-back",
3156
3158
  type: "button",
3159
+ title: t("back"),
3160
+ "data-i18n-title": "back",
3157
3161
  "aria-label": t("back"),
3158
3162
  "data-i18n-aria-label": "back"
3159
3163
  },
@@ -3180,6 +3184,8 @@ function yui_shell_show_modal(shell, content, opts) {
3180
3184
  {
3181
3185
  class: "MODAL_CLOSE yui-dialog-x",
3182
3186
  type: "button",
3187
+ title: t("close"),
3188
+ "data-i18n-title": "close",
3183
3189
  "aria-label": t("close"),
3184
3190
  "data-i18n-aria-label": "close"
3185
3191
  },
@@ -3201,6 +3207,8 @@ function yui_shell_show_modal(shell, content, opts) {
3201
3207
  ]];
3202
3208
  if (with_close) modal_children.push(["button", {
3203
3209
  class: "MODAL_CLOSE modal-close is-large",
3210
+ title: t("close"),
3211
+ "data-i18n-title": "close",
3204
3212
  "aria-label": t("close"),
3205
3213
  "data-i18n-aria-label": "close"
3206
3214
  }]);
@@ -3330,7 +3338,13 @@ function build_dialog(shell, message, buttons, opts) {
3330
3338
  type: "button",
3331
3339
  "data-modal-button-value": b.value
3332
3340
  };
3333
- if (typeof b.label === "string") btn_attrs.i18n = b.label;
3341
+ if (typeof b.label === "string") {
3342
+ btn_attrs.i18n = b.label;
3343
+ btn_attrs.title = t(b.label);
3344
+ btn_attrs["data-i18n-title"] = b.label;
3345
+ btn_attrs["aria-label"] = t(b.label);
3346
+ btn_attrs["data-i18n-aria-label"] = b.label;
3347
+ }
3334
3348
  return [
3335
3349
  "button",
3336
3350
  btn_attrs,
@@ -3355,6 +3369,8 @@ function build_dialog(shell, message, buttons, opts) {
3355
3369
  ]);
3356
3370
  $card_children.push(["button", {
3357
3371
  class: "CONFIRM_CLOSE delete yui-confirm-x",
3372
+ title: t("close"),
3373
+ "data-i18n-title": "close",
3358
3374
  "aria-label": t("close"),
3359
3375
  "data-i18n-aria-label": "close"
3360
3376
  }], [
@@ -72089,9 +72105,10 @@ function write_options(op) {
72089
72105
  * the owner's design of M36 (2026-09-21 review) every write here
72090
72106
  * raised both versions, so an edit half made was already the
72091
72107
  * schema of the next start. The topic list marks what this session
72092
- * wrote and has not saved (the host's EV_REFRESH after a save is
72093
- * what forgets it) and what the host says is not saved (EV_DRAFTS,
72094
- * replaced whole each time it is sent).
72108
+ * wrote and has not saved (EV_REFRESH forgets it: the view's own
72109
+ * Refresh button sends it, and a host may after a save -- the
72110
+ * agent console's does not) and what the host says is not saved
72111
+ * (EV_DRAFTS, replaced whole each time it is sent).
72095
72112
  *
72096
72113
  * WHAT ELSE IS HERE, and why each is here and not somewhere else:
72097
72114
  *
@@ -72168,7 +72185,9 @@ var attrs_table$1 = [
72168
72185
  ];
72169
72186
  var PRIVATE_DATA$1 = {
72170
72187
  records: null,
72188
+ records_before: null,
72171
72189
  model: null,
72190
+ model_gen: 0,
72172
72191
  pending: 0,
72173
72192
  load_error: "",
72174
72193
  load_round: 0,
@@ -72183,6 +72202,7 @@ var PRIVATE_DATA$1 = {
72183
72202
  order_undo: null,
72184
72203
  diagram_gobj: null,
72185
72204
  dialog: null,
72205
+ dialog_gen: 0,
72186
72206
  save_queue: null,
72187
72207
  save_return: "",
72188
72208
  save_wrote: false,
@@ -72343,6 +72363,68 @@ function refuse_if_readonly(gobj, event) {
72343
72363
  yui_shell_show_error(yui_shell_of(gobj), "the treedb is read-only", { t });
72344
72364
  return true;
72345
72365
  }
72366
+ /***************************************************************
72367
+ * A decision taken on a model a load has since REPLACED: the Save
72368
+ * of a form, a confirmation, an import. Each carries the
72369
+ * `model_gen` it was built on (a number, not the model: a kw is
72370
+ * dumped by the machine trace).
72371
+ *
72372
+ * Refused, and said. A column form sends every field it shows, so
72373
+ * its Save after a reload wrote the OLD record over the newer one
72374
+ * (fourth independent review, probe A3); a form whose column the
72375
+ * reload removed answered "names no column" for ever. end_load()
72376
+ * closes such a dialog when the load lands; this is the same rule
72377
+ * for whatever arrives anyway -- a stale screen, a confirmation
72378
+ * that was up, a keyboard.
72379
+ *
72380
+ * A kw with no stamp is not a dialog's and is not asked.
72381
+ ***************************************************************/
72382
+ function built_on_a_replaced_model(gobj, event, kw) {
72383
+ let priv = gobj.priv;
72384
+ if (!kw || kw.model_gen === void 0 || kw.model_gen === priv.model_gen) return false;
72385
+ log_warning(`${gobj_short_name(gobj)}: ${event} refused, it was decided on the schemas a reload replaced`);
72386
+ yui_shell_show_error(yui_shell_of(gobj), "the schemas were read again: open the dialog again", { t });
72387
+ return true;
72388
+ }
72389
+ /***************************************************************
72390
+ * The reload a load refused IN session left owed (a routing
72391
+ * adapter's deadline on a slow `nodes`, ac_mt_command_answer()):
72392
+ * the model shown is kept, and it may be older than the store.
72393
+ * The session is up, so no reconnect will ask for it again -- it
72394
+ * was owed for as long as the session lasted, and a form opened
72395
+ * on that model wrote every field of it over the newer record
72396
+ * (fifth independent review; A3 of the fourth, by another path).
72397
+ *
72398
+ * So the operator's NEXT action asks it. An edit -- a form, a
72399
+ * write, a confirmation, what reads the model whole (check,
72400
+ * export) -- is refused and said: it would be computed on what
72401
+ * the store may no longer hold, and the load that lands closes a
72402
+ * dialog built on the old model anyway (end_load()). A move is
72403
+ * not refused (reload_after_move()): it goes, and reads there.
72404
+ *
72405
+ * Not on a timer, and not at once: a load refused on a deadline
72406
+ * asked again at once is refused the same way, and one asked on a
72407
+ * clock is polling. Out of session nothing is asked; the reconnect
72408
+ * does it (ac_transport_state()). The reload keeps this session's
72409
+ * draft chips: the host did not ask for it.
72410
+ ***************************************************************/
72411
+ function owes_reload(gobj) {
72412
+ let state = gobj_current_state(gobj);
72413
+ return gobj.priv.reload_on_open && transport_in_session(gobj) && state !== "ST_LOADING" && state !== "ST_SAVING";
72414
+ }
72415
+ function reload_first(gobj, event) {
72416
+ if (!owes_reload(gobj)) return false;
72417
+ gobj.priv.reload_on_open = false;
72418
+ log_warning(`${gobj_short_name(gobj)}: ${event} refused, the schemas shown may be out of date: they are read again first`);
72419
+ yui_shell_show_error(yui_shell_of(gobj), "the schemas shown may be out of date: they are read again, try again when they are in", { t });
72420
+ request_model(gobj, true);
72421
+ return true;
72422
+ }
72423
+ function reload_after_move(gobj) {
72424
+ if (!owes_reload(gobj)) return 0;
72425
+ gobj.priv.reload_on_open = false;
72426
+ return request_model(gobj, true);
72427
+ }
72346
72428
  /***************************
72347
72429
  * The model
72348
72430
  ***************************/
@@ -72357,22 +72439,32 @@ function refuse_if_readonly(gobj, event) {
72357
72439
  * and the toolbar stops answering: left up, a click on a card, on
72358
72440
  * Back or on the drawing answered "Event NOT DEFINED in state
72359
72441
  * ST_LOADING" and was lost (third independent review). A dialog
72360
- * left open is answered in ST_LOADING instead (ac_wait_for_the_load).
72361
- *
72362
- * `keep_written`: a load the host did not ask for -- the reconnect,
72363
- * a write that turned out done -- keeps what this session wrote.
72364
- * Only the host's EV_REFRESH (a Save sends it) forgets it.
72442
+ * left open is answered in ST_LOADING instead (ac_wait_for_the_load),
72443
+ * and a load that LANDS closes it (end_load()): it was built on the
72444
+ * model the load replaced, and its Save sends every field it shows.
72445
+ * An import plan is forgotten here for the same reason: it is a list
72446
+ * of writes computed against the model going away -- once the load
72447
+ * has LEFT (one that could not be sent replaces nothing).
72448
+ *
72449
+ * `keep_written`: a load nobody ASKED for -- the reconnect, a write
72450
+ * that turned out done, the reload a refused load owed -- keeps what
72451
+ * this session wrote. Only EV_REFRESH forgets it: the view's own
72452
+ * Refresh button, or a host that sends it (after a Save, say; the
72453
+ * agent console does not).
72365
72454
  *
72366
72455
  * A load that cannot leave WHOLE did not happen (out of session it
72367
72456
  * is not even asked): the records the model was built on are put
72368
72457
  * back (emptied, the next write patched them into a model with no
72369
72458
  * treedb), the round is spent so a request that did leave is
72370
- * stale, and the session back asks again.
72459
+ * stale, and the session back asks again. A load that left and
72460
+ * FAILED keeps them the same way (`records_before`,
72461
+ * ac_mt_command_answer()).
72371
72462
  ***************************************************************/
72372
72463
  function request_model(gobj, keep_written) {
72373
72464
  let priv = gobj.priv;
72374
72465
  if (!gobj_read_pointer_attr(gobj, "gobj_remote_yuno")) return -1;
72375
- let previous = priv.records;
72466
+ let previous = gobj_current_state(gobj) === "ST_LOADING" ? priv.records_before : priv.records;
72467
+ priv.records_before = previous;
72376
72468
  priv.records = {
72377
72469
  treedbs: [],
72378
72470
  topics: [],
@@ -72398,6 +72490,7 @@ function request_model(gobj, keep_written) {
72398
72490
  }
72399
72491
  if (left < 3) {
72400
72492
  priv.records = previous;
72493
+ priv.records_before = null;
72401
72494
  priv.load_round++;
72402
72495
  priv.reload_on_open = true;
72403
72496
  priv.load_error = t("cannot reach the treedb");
@@ -72405,6 +72498,7 @@ function request_model(gobj, keep_written) {
72405
72498
  end_load(gobj);
72406
72499
  return -1;
72407
72500
  }
72501
+ forget_import_plan(gobj);
72408
72502
  priv.pending = left;
72409
72503
  return 0;
72410
72504
  }
@@ -72470,8 +72564,8 @@ function build_model(gobj) {
72470
72564
  * Called ONLY when records arrive from the store: that is what a
72471
72565
  * measurement of "changed since" can be measured against.
72472
72566
  *
72473
- * `written` is forgotten only by a load the HOST asked for (its
72474
- * EV_REFRESH, which a Save sends). Since M36 a write moves no
72567
+ * `written` is forgotten only by an EV_REFRESH (the view's own
72568
+ * Refresh button, or a host that sends one). Since M36 a write moves no
72475
72569
  * version, so `written` is the draft chip of what this session
72476
72570
  * wrote and no longer half of a version measurement: a reload of
72477
72571
  * the reconnect, or of a write that turned out done, forgot the
@@ -72493,9 +72587,12 @@ function start_measuring(gobj) {
72493
72587
  * until the host saves it (C_TREEDB's save-schema, which raises the
72494
72588
  * versions of what changed). Two sources say a topic is one, and
72495
72589
  * they are kept APART because they are forgotten differently:
72496
- * - `written`: what THIS session wrote since the model was loaded
72497
- * (a reload, the host's EV_REFRESH, forgets it -- the store says
72498
- * what is left);
72590
+ * - `written`: what THIS session wrote. Only EV_REFRESH forgets
72591
+ * it -- the view's own Refresh button sends it (in the agent
72592
+ * console that is the only sender), and a host may after a
72593
+ * Save; the reconnect's reload, the one owed after a write, a
72594
+ * refused load's and a late write's keep it (start_measuring(),
72595
+ * request_model());
72499
72596
  * - `host_ids`: what the host said last (EV_DRAFTS), replaced WHOLE
72500
72597
  * by the next EV_DRAFTS. Folded into `written`, a topic the host
72501
72598
  * named once stayed a draft after the Save that published it.
@@ -72663,6 +72760,8 @@ function render_toolbar(gobj) {
72663
72760
  let readonly = is_readonly(gobj);
72664
72761
  let has_model = !!priv.model;
72665
72762
  let on_a_treedb = state !== "ST_IDLE" && state !== "ST_EMPTY";
72763
+ let treedb_here = on_a_treedb && !!current_treedb(gobj);
72764
+ let topic_here = treedb_here && !!current_topic(gobj);
72666
72765
  let in_flight = state === "ST_SAVING" || state === "ST_LOADING";
72667
72766
  let $left = [];
72668
72767
  if (priv.treedb_id && on_a_treedb) $left.push([
@@ -72738,17 +72837,17 @@ function render_toolbar(gobj) {
72738
72837
  $trail
72739
72838
  ]);
72740
72839
  let $right = [];
72741
- if (priv.treedb_id && has_model && on_a_treedb) {
72840
+ if (priv.treedb_id && has_model && treedb_here) {
72742
72841
  $right.push(toolbar_button("SCHEMA_DIAGRAM_BTN", "yi-compass-drafting", priv.diagram ? "topics" : "diagram", "EV_TOGGLE_DIAGRAM", false));
72743
72842
  $right.push(toolbar_button("SCHEMA_VALIDATE_BTN", "yi-square-check", "check", "EV_VALIDATE", false));
72744
72843
  $right.push(toolbar_button("SCHEMA_EXPORT_BTN", "yi-download", "export", "EV_EXPORT", false));
72745
72844
  if (!readonly) $right.push(toolbar_button("SCHEMA_IMPORT_BTN", "yi-upload", "import", "EV_IMPORT", false));
72746
72845
  }
72747
- if (priv.topic_name && !readonly && !priv.diagram && on_a_treedb) {
72846
+ if (priv.topic_name && !readonly && !priv.diagram && topic_here) {
72748
72847
  if (undo_order_writes(gobj, current_topic(gobj)).length > 0) $right.push(toolbar_button("SCHEMA_UNDO_ORDER_BTN", "yi-arrow-rotate-left", "undo the order", "EV_UNDO_ORDER", false));
72749
72848
  $right.push(toolbar_button("SCHEMA_ADD_COL_BTN", "yi-plus", "new column", "EV_ADD_COLUMN", false));
72750
72849
  }
72751
- if (priv.treedb_id && !priv.topic_name && !readonly && !priv.diagram && on_a_treedb) $right.push(toolbar_button("SCHEMA_ADD_TOPIC_BTN", "yi-plus", "new topic", "EV_ADD_TOPIC", false));
72850
+ if (priv.treedb_id && !priv.topic_name && !readonly && !priv.diagram && treedb_here) $right.push(toolbar_button("SCHEMA_ADD_TOPIC_BTN", "yi-plus", "new topic", "EV_ADD_TOPIC", false));
72752
72851
  $right.push(toolbar_button("SCHEMA_REFRESH_BTN", "yi-arrows-rotate", "refresh", "EV_REFRESH", state === "ST_LOADING"));
72753
72852
  let $bar = createElement2([
72754
72853
  "div",
@@ -73568,6 +73667,7 @@ function close_dialog(gobj) {
73568
73667
  function open_dialog(gobj, $content, title, logical, title_prefix) {
73569
73668
  let priv = gobj.priv;
73570
73669
  close_dialog(gobj);
73670
+ priv.dialog_gen = priv.model_gen;
73571
73671
  priv.dialog = yui_shell_show_modal(yui_shell_of(gobj), $content, {
73572
73672
  dialog: true,
73573
73673
  title,
@@ -73614,11 +73714,28 @@ function name_control($spec, label) {
73614
73714
  $spec[1]["aria-label"] = t(label);
73615
73715
  $spec[1]["data-i18n-aria-label"] = label;
73616
73716
  }
73717
+ if (!$spec[1].title) {
73718
+ $spec[1].title = t(label);
73719
+ $spec[1]["data-i18n-title"] = label;
73720
+ }
73617
73721
  return true;
73618
73722
  }
73619
73723
  for (let child of $spec[2] || []) if (name_control(child, label)) return true;
73620
73724
  return false;
73621
73725
  }
73726
+ /***************************************************************
73727
+ * The four attributes that NAME a control -- a title and an
73728
+ * aria-label, each with the key a language change re-translates
73729
+ * it by. A visible label is not a name (CLAUDE.md).
73730
+ ***************************************************************/
73731
+ function name_attrs(key, attrs) {
73732
+ return Object.assign({
73733
+ title: t(key),
73734
+ "data-i18n-title": key,
73735
+ "aria-label": t(key),
73736
+ "data-i18n-aria-label": key
73737
+ }, attrs);
73738
+ }
73622
73739
  function field(logical, name, label, $control, help) {
73623
73740
  name_control($control, label);
73624
73741
  return [
@@ -73795,7 +73912,7 @@ function read_flags($root) {
73795
73912
  * than not offering it.
73796
73913
  ***************************************************************/
73797
73914
  function open_column_form(gobj, topic, col, prefill) {
73798
- gobj.priv;
73915
+ let priv = gobj.priv;
73799
73916
  let record = prefill || col && col.record || {};
73800
73917
  let creating = !col;
73801
73918
  let type = record.type || "string";
@@ -73805,6 +73922,7 @@ function open_column_form(gobj, topic, col, prefill) {
73805
73922
  let hook_col = hook_topic ? hook[hook_topic] : "";
73806
73923
  let treedb = current_treedb(gobj);
73807
73924
  let sibling_topics = treedb ? treedb.topics.map((x) => x.name) : [];
73925
+ let model_gen = priv.model_gen;
73808
73926
  let $form = createElement2([
73809
73927
  "div",
73810
73928
  { class: "SCHEMA_COL_FORM box" },
@@ -73867,10 +73985,10 @@ function open_column_form(gobj, topic, col, prefill) {
73867
73985
  },
73868
73986
  [[
73869
73987
  "button",
73870
- {
73988
+ name_attrs("cancel", {
73871
73989
  class: "SCHEMA_COL_FORM_CANCEL button",
73872
73990
  type: "button"
73873
- },
73991
+ }),
73874
73992
  [[
73875
73993
  "span",
73876
73994
  { i18n: "cancel" },
@@ -73878,10 +73996,10 @@ function open_column_form(gobj, topic, col, prefill) {
73878
73996
  ]]
73879
73997
  ], [
73880
73998
  "button",
73881
- {
73999
+ name_attrs("save", {
73882
74000
  class: "SCHEMA_COL_FORM_SAVE button is-primary",
73883
74001
  type: "button"
73884
- },
74002
+ }),
73885
74003
  [[
73886
74004
  "span",
73887
74005
  { class: "icon" },
@@ -73921,7 +74039,8 @@ function open_column_form(gobj, topic, col, prefill) {
73921
74039
  topic: topic.name,
73922
74040
  col: creating ? "" : col.name,
73923
74041
  creating,
73924
- values
74042
+ values,
74043
+ model_gen
73925
74044
  }, gobj);
73926
74045
  });
73927
74046
  open_dialog(gobj, $form, creating ? "new column" : "edit column", "SCHEMA_COL_FORM_DIALOG", creating ? topic.name : `${topic.name}.${col.name}`);
@@ -73944,6 +74063,7 @@ function open_topic_form(gobj, treedb, topic) {
73944
74063
  let record = topic && topic.record || {};
73945
74064
  let creating = !topic;
73946
74065
  let col_names = topic ? topic.cols.map((c) => c.name) : [];
74066
+ let model_gen = gobj.priv.model_gen;
73947
74067
  let $form = createElement2([
73948
74068
  "div",
73949
74069
  { class: "SCHEMA_TOPIC_FORM box" },
@@ -74002,10 +74122,10 @@ function open_topic_form(gobj, treedb, topic) {
74002
74122
  },
74003
74123
  [[
74004
74124
  "button",
74005
- {
74125
+ name_attrs("cancel", {
74006
74126
  class: "SCHEMA_TOPIC_FORM_CANCEL button",
74007
74127
  type: "button"
74008
- },
74128
+ }),
74009
74129
  [[
74010
74130
  "span",
74011
74131
  { i18n: "cancel" },
@@ -74013,10 +74133,10 @@ function open_topic_form(gobj, treedb, topic) {
74013
74133
  ]]
74014
74134
  ], [
74015
74135
  "button",
74016
- {
74136
+ name_attrs("save", {
74017
74137
  class: "SCHEMA_TOPIC_FORM_SAVE button is-primary",
74018
74138
  type: "button"
74019
- },
74139
+ }),
74020
74140
  [[
74021
74141
  "span",
74022
74142
  { class: "icon" },
@@ -74039,7 +74159,8 @@ function open_topic_form(gobj, treedb, topic) {
74039
74159
  gobj_send_event(gobj, "EV_SAVE_TOPIC", {
74040
74160
  topic: creating ? "" : topic.name,
74041
74161
  creating,
74042
- values: read_form($form)
74162
+ values: read_form($form),
74163
+ model_gen
74043
74164
  }, gobj);
74044
74165
  });
74045
74166
  open_dialog(gobj, $form, creating ? "new topic" : "edit topic", "SCHEMA_TOPIC_FORM_DIALOG", creating ? treedb.id : `${treedb.id}.${topic.name}`);
@@ -74180,40 +74301,40 @@ function open_export(gobj, treedb) {
74180
74301
  [
74181
74302
  [
74182
74303
  "div",
74183
- { class: "SCHEMA_EXPORT_TABS tabs is-toggle mb-2" },
74304
+ { class: "SCHEMA_EXPORT_TABS buttons has-addons mb-2" },
74184
74305
  [[
74185
- "ul",
74186
- {},
74306
+ "button",
74307
+ {
74308
+ class: "SCHEMA_EXPORT_TAB button selected_state",
74309
+ type: "button",
74310
+ "data-pane": "c",
74311
+ "aria-pressed": "true",
74312
+ title: t("schema as c source"),
74313
+ "data-i18n-title": "schema as c source",
74314
+ "aria-label": t("schema as c source"),
74315
+ "data-i18n-aria-label": "schema as c source"
74316
+ },
74187
74317
  [[
74188
- "li",
74189
- {
74190
- class: "SCHEMA_EXPORT_TAB is-active",
74191
- "data-pane": "c"
74192
- },
74193
- [[
74194
- "a",
74195
- {},
74196
- [[
74197
- "span",
74198
- {},
74199
- "C"
74200
- ]]
74201
- ]]
74202
- ], [
74203
- "li",
74204
- {
74205
- class: "SCHEMA_EXPORT_TAB",
74206
- "data-pane": "json"
74207
- },
74208
- [[
74209
- "a",
74210
- {},
74211
- [[
74212
- "span",
74213
- {},
74214
- "JSON"
74215
- ]]
74216
- ]]
74318
+ "span",
74319
+ {},
74320
+ "C"
74321
+ ]]
74322
+ ], [
74323
+ "button",
74324
+ {
74325
+ class: "SCHEMA_EXPORT_TAB button",
74326
+ type: "button",
74327
+ "data-pane": "json",
74328
+ "aria-pressed": "false",
74329
+ title: t("schema as json"),
74330
+ "data-i18n-title": "schema as json",
74331
+ "aria-label": t("schema as json"),
74332
+ "data-i18n-aria-label": "schema as json"
74333
+ },
74334
+ [[
74335
+ "span",
74336
+ {},
74337
+ "JSON"
74217
74338
  ]]
74218
74339
  ]]
74219
74340
  ],
@@ -74283,7 +74404,10 @@ function open_export(gobj, treedb) {
74283
74404
  let $text = $content.querySelector(".SCHEMA_EXPORT_TEXT");
74284
74405
  for (let $tab of $content.querySelectorAll(".SCHEMA_EXPORT_TAB")) $tab.addEventListener("click", (evt) => {
74285
74406
  evt.stopPropagation();
74286
- for (let $other of $content.querySelectorAll(".SCHEMA_EXPORT_TAB")) $other.classList.toggle("is-active", $other === $tab);
74407
+ for (let $other of $content.querySelectorAll(".SCHEMA_EXPORT_TAB")) {
74408
+ $other.classList.toggle("selected_state", $other === $tab);
74409
+ $other.setAttribute("aria-pressed", $other === $tab ? "true" : "false");
74410
+ }
74287
74411
  $text.value = $tab.dataset.pane === "c" ? c_text : json_text;
74288
74412
  });
74289
74413
  $content.querySelector(".SCHEMA_EXPORT_COPY").addEventListener("click", (evt) => {
@@ -74319,6 +74443,7 @@ function copy_text(gobj, $text) {
74319
74443
  * plan itself, so what is confirmed is what runs.
74320
74444
  ***************************************************************/
74321
74445
  function open_import(gobj, treedb) {
74446
+ let model_gen = gobj.priv.model_gen;
74322
74447
  let $content = createElement2([
74323
74448
  "div",
74324
74449
  { class: "SCHEMA_IMPORT box" },
@@ -74333,12 +74458,12 @@ function open_import(gobj, treedb) {
74333
74458
  ],
74334
74459
  [
74335
74460
  "textarea",
74336
- {
74461
+ name_attrs("schema", {
74337
74462
  class: "SCHEMA_IMPORT_TEXT textarea is-small is-family-monospace",
74338
74463
  rows: "12",
74339
74464
  spellcheck: "false",
74340
74465
  placeholder: "{\"id\": \"...\", \"schema_version\": \"1\", \"topics\": []}"
74341
- },
74466
+ }),
74342
74467
  ""
74343
74468
  ],
74344
74469
  [
@@ -74371,10 +74496,10 @@ function open_import(gobj, treedb) {
74371
74496
  [
74372
74497
  [
74373
74498
  "button",
74374
- {
74499
+ name_attrs("cancel", {
74375
74500
  class: "SCHEMA_IMPORT_CANCEL button",
74376
74501
  type: "button"
74377
- },
74502
+ }),
74378
74503
  [[
74379
74504
  "span",
74380
74505
  { i18n: "cancel" },
@@ -74383,10 +74508,10 @@ function open_import(gobj, treedb) {
74383
74508
  ],
74384
74509
  [
74385
74510
  "button",
74386
- {
74511
+ name_attrs("preview", {
74387
74512
  class: "SCHEMA_IMPORT_PREVIEW button",
74388
74513
  type: "button"
74389
- },
74514
+ }),
74390
74515
  [[
74391
74516
  "span",
74392
74517
  { class: "icon" },
@@ -74399,11 +74524,11 @@ function open_import(gobj, treedb) {
74399
74524
  ],
74400
74525
  [
74401
74526
  "button",
74402
- {
74527
+ name_attrs("import", {
74403
74528
  class: "SCHEMA_IMPORT_RUN button is-warning",
74404
74529
  type: "button",
74405
74530
  disabled: "disabled"
74406
- },
74531
+ }),
74407
74532
  [[
74408
74533
  "span",
74409
74534
  { class: "icon" },
@@ -74430,18 +74555,31 @@ function open_import(gobj, treedb) {
74430
74555
  evt.stopPropagation();
74431
74556
  gobj_send_event(gobj, "EV_PREVIEW_IMPORT", {
74432
74557
  text: $text.value,
74433
- prune: $prune.checked
74558
+ prune: $prune.checked,
74559
+ model_gen
74434
74560
  }, gobj);
74435
74561
  });
74436
74562
  $run.addEventListener("click", (evt) => {
74437
74563
  evt.stopPropagation();
74438
- gobj_send_event(gobj, "EV_APPLY_IMPORT", {}, gobj);
74564
+ gobj_send_event(gobj, "EV_APPLY_IMPORT", { model_gen }, gobj);
74439
74565
  });
74440
74566
  gobj.priv.import_pane = $plan_pane;
74441
74567
  gobj.priv.import_run = $run;
74442
74568
  open_dialog(gobj, $content, "import", "SCHEMA_IMPORT_DIALOG", treedb.id);
74443
74569
  }
74444
74570
  /***************************************************************
74571
+ * The plan is a list of writes computed against the model shown:
74572
+ * a load replaces the model, so the plan goes with it and the
74573
+ * dialog, if it stays up (a load that failed keeps the model),
74574
+ * asks for a Preview again.
74575
+ ***************************************************************/
74576
+ function forget_import_plan(gobj) {
74577
+ let priv = gobj.priv;
74578
+ priv.import_plan = null;
74579
+ clear(priv.import_pane);
74580
+ if (priv.import_run) priv.import_run.disabled = true;
74581
+ }
74582
+ /***************************************************************
74445
74583
  * The plan, drawn where the paste is: one line per write, the
74446
74584
  * deletions apart and counted, because they are what the
74447
74585
  * operator is being asked about.
@@ -74568,7 +74706,8 @@ function open_orphans(gobj) {
74568
74706
  { class: "SCHEMA_ORPHAN_ACTIONS has-text-right" },
74569
74707
  readonly ? [] : [row_icon(gobj, "SCHEMA_ORPHAN_DELETE", "yi-trash", "delete", "EV_DELETE_ORPHAN", {
74570
74708
  kind,
74571
- id: entry.id
74709
+ id: entry.id,
74710
+ model_gen: priv.model_gen
74572
74711
  })]
74573
74712
  ]
74574
74713
  ]
@@ -74740,6 +74879,10 @@ function transport_dropped(gobj) {
74740
74879
  priv.load_round++;
74741
74880
  priv.pending = 0;
74742
74881
  priv.load_error = "";
74882
+ if (priv.records_before) {
74883
+ priv.records = priv.records_before;
74884
+ priv.records_before = null;
74885
+ }
74743
74886
  return end_load(gobj);
74744
74887
  }
74745
74888
  return 0;
@@ -74750,11 +74893,22 @@ function transport_dropped(gobj) {
74750
74893
  * in flight (ac_show() kept it), or the one it had. Without a
74751
74894
  * model there is no screen to go to: the position is kept for the
74752
74895
  * next load, and the editor says why it is empty.
74896
+ *
74897
+ * A dialog built on the model this load REPLACED is closed, and the
74898
+ * operator told to open it again: it shows the old record, and its
74899
+ * Save would write every field of it over the newer one. A load
74900
+ * that failed or never left replaced nothing (`model_gen` did not
74901
+ * move), and the dialog stays with what was typed.
74753
74902
  ***************************************************************/
74754
74903
  function end_load(gobj) {
74755
74904
  let priv = gobj.priv;
74756
74905
  let seg = priv.pending_seg;
74757
74906
  priv.pending_seg = null;
74907
+ if (priv.dialog && priv.dialog_gen !== priv.model_gen) {
74908
+ log_warning(`${gobj_short_name(gobj)}: a dialog was open on the schemas the load replaced: closed`);
74909
+ close_dialog(gobj);
74910
+ yui_shell_show_error(yui_shell_of(gobj), "the schemas were read again: open the dialog again", { t });
74911
+ }
74758
74912
  if (priv.model) {
74759
74913
  if (seg !== null) return apply_seg(gobj, seg);
74760
74914
  return go(gobj, priv.treedb_id, priv.topic_name, priv.diagram, false);
@@ -74970,10 +75124,20 @@ function ac_mt_command_answer(gobj, event, kw, src) {
74970
75124
  else if (priv.records && Array.isArray(priv.records[topic_name])) priv.records[topic_name] = Array.isArray(data) ? data : [];
74971
75125
  if (priv.pending > 0) return 0;
74972
75126
  if (priv.load_error) {
75127
+ if (priv.model) {
75128
+ log_warning(`${gobj_short_name(gobj)}: the schemas could not be read again (${priv.load_error}): the previous ones stay`);
75129
+ priv.records = priv.records_before;
75130
+ priv.records_before = null;
75131
+ priv.reload_on_open = true;
75132
+ yui_shell_show_error(yui_shell_of(gobj), "cannot read the schemas again: the ones shown may be out of date, your next change reads them first", { t });
75133
+ return end_load(gobj);
75134
+ }
74973
75135
  priv.model = null;
74974
75136
  return end_load(gobj);
74975
75137
  }
74976
75138
  priv.order_undo = {};
75139
+ priv.records_before = null;
75140
+ priv.model_gen++;
74977
75141
  build_model(gobj);
74978
75142
  start_measuring(gobj);
74979
75143
  return end_load(gobj);
@@ -74995,11 +75159,14 @@ function ac_mt_command_answer(gobj, event, kw, src) {
74995
75159
  let record = is_object(data) ? data : Array.isArray(data) && is_object(data[0]) ? data[0] : null;
74996
75160
  if (record) patch_record(gobj, topic_name, record);
74997
75161
  else {
74998
- log_warning(`${gobj_short_name(gobj)}: '${command}' answered no record`);
75162
+ let not_sent = priv.save_queue.length - 1;
75163
+ log_warning(`${gobj_short_name(gobj)}: '${command}' answered no record` + (not_sent > 0 ? `: ${not_sent} write(s) after it were not sent` : ""));
75164
+ if (not_sent > 0) yui_shell_show_error(yui_shell_of(gobj), "the treedb did not describe a write back: the writes after it were not sent", { t });
74999
75165
  if (topic_name === T_TOPICS || topic_name === T_COLS) {
75000
75166
  let write = priv.save_queue[0];
75001
75167
  mark_written(gobj, topic_name, write ? write.record : null);
75002
75168
  }
75169
+ priv.reload_after_write = false;
75003
75170
  priv.save_queue = [];
75004
75171
  priv.save_return = "";
75005
75172
  priv.save_wrote = false;
@@ -75057,8 +75224,14 @@ function late_write_done(gobj, command, topic_name, data) {
75057
75224
  /***************************************************************
75058
75225
  * The topic a record written belongs to, read off the RECORD and
75059
75226
  * not off the screen: a write answered late may be about a topic
75060
- * the operator has left. A column names its topic in its fkey
75061
- * (`topics^<topic id>^cols`); a topic is its own id.
75227
+ * the operator has left. A column names its topic in its fkey; a
75228
+ * topic is its own id.
75229
+ *
75230
+ * The fkey is read with parse_fkey_ref(), because the store answers
75231
+ * in list_dict -- `[{topic_name, id, hook_name}]` -- and not in the
75232
+ * `topics^<topic id>^cols` strings this view writes. Split as a
75233
+ * string, every production answer marked nothing (fourth independent
75234
+ * review, probe C).
75062
75235
  ***************************************************************/
75063
75236
  function mark_written_record(gobj, topic_name, record) {
75064
75237
  let priv = gobj.priv;
@@ -75068,11 +75241,7 @@ function mark_written_record(gobj, topic_name, record) {
75068
75241
  return;
75069
75242
  }
75070
75243
  if (topic_name !== T_COLS) return;
75071
- let refs = Array.isArray(record.topics) ? record.topics : typeof record.topics === "string" ? [record.topics] : [];
75072
- for (let ref of refs) {
75073
- let parts = String(ref).split("^");
75074
- if (parts.length >= 2 && parts[1]) priv.written[parts[1]] = true;
75075
- }
75244
+ for (let ref of parse_fkey_ref(record.topics)) priv.written[ref.id] = true;
75076
75245
  }
75077
75246
  /***************************************************************
75078
75247
  * Remember which topic this write belongs to, which is what
@@ -75156,14 +75325,36 @@ function ac_refresh$1(gobj, event, kw, src) {
75156
75325
  * operator can still reach, and what it asks is computed against
75157
75326
  * a model the answers in the air are about to replace.
75158
75327
  *
75159
- * Refused, and SAID -- and the dialog stays open with what was
75160
- * typed, so the same Save works once the load is in. Undeclared,
75161
- * it answered "Event NOT DEFINED in state ST_LOADING" and the
75162
- * edit was gone with no word (third independent review).
75328
+ * Refused, and SAID. Undeclared, it answered "Event NOT DEFINED
75329
+ * in state ST_LOADING" and the edit was gone with no word (third
75330
+ * independent review).
75331
+ *
75332
+ * And NOT "try again": the same Save, once the load was in, sent
75333
+ * the form built on the old model -- every field of the old record
75334
+ * over the newer one (fourth independent review). The load that
75335
+ * lands closes such a dialog (end_load()); one that fails leaves it
75336
+ * up, on the model it was built on.
75163
75337
  ***************************************************************/
75164
75338
  function ac_wait_for_the_load(gobj, event, kw, src) {
75165
75339
  log_warning(`${gobj_short_name(gobj)}: ${event} refused, the schemas are loading`);
75166
- yui_shell_show_error(yui_shell_of(gobj), "the schemas are loading: try again when they are in", { t });
75340
+ yui_shell_show_error(yui_shell_of(gobj), "the schemas are loading: wait for them", { t });
75341
+ return -1;
75342
+ }
75343
+ /***************************************************************
75344
+ * A confirmation answered where no treedb is open: ST_EMPTY (a
75345
+ * reload landed on zero treedbs while it was up) or ST_IDLE (no
75346
+ * model). A confirmation is a shell modal, not this view's dialog,
75347
+ * so the load that lands does not close it, and its Yes arrives
75348
+ * anyway. Undeclared, it answered "Event NOT DEFINED" (fifth
75349
+ * independent review).
75350
+ *
75351
+ * Refused with the stamp's own words: whatever it was decided on
75352
+ * is not on screen any more.
75353
+ ***************************************************************/
75354
+ function ac_confirmed_on_nothing(gobj, event, kw, src) {
75355
+ if (built_on_a_replaced_model(gobj, event, kw)) return -1;
75356
+ log_warning(`${gobj_short_name(gobj)}: ${event} refused, no treedb is open`);
75357
+ yui_shell_show_error(yui_shell_of(gobj), "the schemas were read again: open the dialog again", { t });
75167
75358
  return -1;
75168
75359
  }
75169
75360
  /***************************************************************
@@ -75191,20 +75382,23 @@ function ac_drafts(gobj, event, kw, src) {
75191
75382
  * Moving about.
75192
75383
  ***************************************************************/
75193
75384
  function ac_select_treedb(gobj, event, kw, src) {
75194
- return go(gobj, kw.treedb, "", false, true);
75385
+ go(gobj, kw.treedb, "", false, true);
75386
+ return reload_after_move(gobj);
75195
75387
  }
75196
75388
  function ac_select_topic(gobj, event, kw, src) {
75197
- return go(gobj, gobj.priv.treedb_id, kw.topic, false, true);
75389
+ go(gobj, gobj.priv.treedb_id, kw.topic, false, true);
75390
+ return reload_after_move(gobj);
75198
75391
  }
75199
75392
  function ac_back(gobj, event, kw, src) {
75200
75393
  let priv = gobj.priv;
75201
- if (priv.diagram) return go(gobj, priv.treedb_id, "", false, true);
75202
- if (priv.topic_name) return go(gobj, priv.treedb_id, "", false, true);
75203
- return go(gobj, "", "", false, true);
75394
+ if (priv.diagram || priv.topic_name) go(gobj, priv.treedb_id, "", false, true);
75395
+ else go(gobj, "", "", false, true);
75396
+ return reload_after_move(gobj);
75204
75397
  }
75205
75398
  function ac_toggle_diagram(gobj, event, kw, src) {
75206
75399
  let priv = gobj.priv;
75207
- return go(gobj, priv.treedb_id, priv.topic_name, !priv.diagram, true);
75400
+ go(gobj, priv.treedb_id, priv.topic_name, !priv.diagram, true);
75401
+ return reload_after_move(gobj);
75208
75402
  }
75209
75403
  /***************************************************************
75210
75404
  * A topic clicked in the drawing. The diagram is a CHILD here
@@ -75214,9 +75408,11 @@ function ac_toggle_diagram(gobj, event, kw, src) {
75214
75408
  function ac_node_click(gobj, event, kw, src) {
75215
75409
  let topic = kw && kw.topic;
75216
75410
  if (!topic) return 0;
75217
- return go(gobj, gobj.priv.treedb_id, topic, false, true);
75411
+ go(gobj, gobj.priv.treedb_id, topic, false, true);
75412
+ return reload_after_move(gobj);
75218
75413
  }
75219
75414
  function ac_show_orphans(gobj, event, kw, src) {
75415
+ if (reload_first(gobj, event)) return -1;
75220
75416
  open_orphans(gobj);
75221
75417
  return 0;
75222
75418
  }
@@ -75229,6 +75425,7 @@ function ac_show_orphans(gobj, event, kw, src) {
75229
75425
  * to feed.
75230
75426
  ***************************************************************/
75231
75427
  function confirm_then(gobj, message, detail, kw) {
75428
+ kw = Object.assign({ model_gen: gobj.priv.model_gen }, kw);
75232
75429
  yui_shell_confirm_danger(yui_shell_of(gobj), message, {
75233
75430
  t,
75234
75431
  detail
@@ -75243,6 +75440,7 @@ function confirm_then(gobj, message, detail, kw) {
75243
75440
  function ac_add_column(gobj, event, kw, src) {
75244
75441
  let topic = current_topic(gobj);
75245
75442
  if (refuse_if_readonly(gobj, event)) return -1;
75443
+ if (reload_first(gobj, event)) return -1;
75246
75444
  if (!topic) {
75247
75445
  log_error(`${gobj_short_name(gobj)}: ${event} with no topic open`);
75248
75446
  return -1;
@@ -75255,6 +75453,7 @@ function ac_edit_column(gobj, event, kw, src) {
75255
75453
  let topic = current_topic(gobj);
75256
75454
  let col = topic ? find_col(priv.model, priv.treedb_id, topic.name, kw.col) : null;
75257
75455
  if (refuse_if_readonly(gobj, event)) return -1;
75456
+ if (reload_first(gobj, event)) return -1;
75258
75457
  if (!col) {
75259
75458
  log_error(`${gobj_short_name(gobj)}: ${event} names no column: ${kw.col}`);
75260
75459
  return -1;
@@ -75272,6 +75471,7 @@ function ac_duplicate_column(gobj, event, kw, src) {
75272
75471
  let topic = current_topic(gobj);
75273
75472
  let col = topic ? find_col(priv.model, priv.treedb_id, topic.name, kw.col) : null;
75274
75473
  if (refuse_if_readonly(gobj, event)) return -1;
75474
+ if (reload_first(gobj, event)) return -1;
75275
75475
  if (!col) {
75276
75476
  log_error(`${gobj_short_name(gobj)}: ${event} names no column: ${kw.col}`);
75277
75477
  return -1;
@@ -75290,6 +75490,8 @@ function ac_save_column(gobj, event, kw, src) {
75290
75490
  let topic = find_topic(priv.model, priv.treedb_id, kw.topic);
75291
75491
  let col = kw.creating ? null : find_col(priv.model, priv.treedb_id, kw.topic, kw.col);
75292
75492
  if (refuse_if_readonly(gobj, event)) return -1;
75493
+ if (reload_first(gobj, event)) return -1;
75494
+ if (built_on_a_replaced_model(gobj, event, kw)) return -1;
75293
75495
  if (!topic || !kw.creating && !col) {
75294
75496
  log_error(`${gobj_short_name(gobj)}: ${event} names no column: ${kw.topic}.${kw.col}`);
75295
75497
  return -1;
@@ -75315,6 +75517,7 @@ function ac_delete_column(gobj, event, kw, src) {
75315
75517
  let topic = current_topic(gobj);
75316
75518
  let col = topic ? find_col(priv.model, priv.treedb_id, topic.name, kw.col) : null;
75317
75519
  if (refuse_if_readonly(gobj, event)) return -1;
75520
+ if (reload_first(gobj, event)) return -1;
75318
75521
  if (!col) {
75319
75522
  log_error(`${gobj_short_name(gobj)}: ${event} names no column: ${kw.col}`);
75320
75523
  return -1;
@@ -75334,6 +75537,7 @@ function ac_delete_column(gobj, event, kw, src) {
75334
75537
  function ac_move_column(gobj, event, kw, src) {
75335
75538
  let topic = current_topic(gobj);
75336
75539
  if (refuse_if_readonly(gobj, event)) return -1;
75540
+ if (reload_first(gobj, event)) return -1;
75337
75541
  if (!topic) {
75338
75542
  log_error(`${gobj_short_name(gobj)}: ${event} with no topic open`);
75339
75543
  return -1;
@@ -75369,6 +75573,7 @@ function ac_undo_order(gobj, event, kw, src) {
75369
75573
  let priv = gobj.priv;
75370
75574
  let topic = current_topic(gobj);
75371
75575
  if (refuse_if_readonly(gobj, event)) return -1;
75576
+ if (reload_first(gobj, event)) return -1;
75372
75577
  if (!topic) {
75373
75578
  log_error(`${gobj_short_name(gobj)}: ${event} with no topic open`);
75374
75579
  return -1;
@@ -75387,6 +75592,7 @@ function ac_undo_order(gobj, event, kw, src) {
75387
75592
  function ac_add_topic(gobj, event, kw, src) {
75388
75593
  let treedb = current_treedb(gobj);
75389
75594
  if (refuse_if_readonly(gobj, event)) return -1;
75595
+ if (reload_first(gobj, event)) return -1;
75390
75596
  if (!treedb) {
75391
75597
  log_error(`${gobj_short_name(gobj)}: ${event} with no treedb open`);
75392
75598
  return -1;
@@ -75399,6 +75605,7 @@ function ac_edit_topic(gobj, event, kw, src) {
75399
75605
  let treedb = current_treedb(gobj);
75400
75606
  let topic = treedb ? find_topic(priv.model, priv.treedb_id, kw.topic) : null;
75401
75607
  if (refuse_if_readonly(gobj, event)) return -1;
75608
+ if (reload_first(gobj, event)) return -1;
75402
75609
  if (!topic) {
75403
75610
  log_error(`${gobj_short_name(gobj)}: ${event} names no topic: ${kw.topic}`);
75404
75611
  return -1;
@@ -75411,6 +75618,8 @@ function ac_save_topic(gobj, event, kw, src) {
75411
75618
  let treedb = current_treedb(gobj);
75412
75619
  let topic = kw.creating ? null : find_topic(priv.model, priv.treedb_id, kw.topic);
75413
75620
  if (refuse_if_readonly(gobj, event)) return -1;
75621
+ if (reload_first(gobj, event)) return -1;
75622
+ if (built_on_a_replaced_model(gobj, event, kw)) return -1;
75414
75623
  if (!treedb || !kw.creating && !topic) {
75415
75624
  log_error(`${gobj_short_name(gobj)}: ${event} names no topic: ${kw.topic}`);
75416
75625
  return -1;
@@ -75438,6 +75647,7 @@ function ac_delete_topic(gobj, event, kw, src) {
75438
75647
  let priv = gobj.priv;
75439
75648
  let topic = find_topic(priv.model, priv.treedb_id, kw.topic);
75440
75649
  if (refuse_if_readonly(gobj, event)) return -1;
75650
+ if (reload_first(gobj, event)) return -1;
75441
75651
  if (!topic) {
75442
75652
  log_error(`${gobj_short_name(gobj)}: ${event} names no topic: ${kw.topic}`);
75443
75653
  return -1;
@@ -75453,6 +75663,8 @@ function ac_delete_topic(gobj, event, kw, src) {
75453
75663
  ***************************************************************/
75454
75664
  function ac_delete_orphan(gobj, event, kw, src) {
75455
75665
  if (refuse_if_readonly(gobj, event)) return -1;
75666
+ if (reload_first(gobj, event)) return -1;
75667
+ if (built_on_a_replaced_model(gobj, event, kw)) return -1;
75456
75668
  if (empty_string(kw.id)) {
75457
75669
  log_error(`${gobj_short_name(gobj)}: ${event} names nothing`);
75458
75670
  return -1;
@@ -75470,6 +75682,7 @@ function ac_delete_orphan(gobj, event, kw, src) {
75470
75682
  function ac_validate(gobj, event, kw, src) {
75471
75683
  let priv = gobj.priv;
75472
75684
  let treedb = current_treedb(gobj);
75685
+ if (reload_first(gobj, event)) return -1;
75473
75686
  if (!treedb) {
75474
75687
  log_error(`${gobj_short_name(gobj)}: ${event} with no treedb open`);
75475
75688
  return -1;
@@ -75484,6 +75697,7 @@ function ac_validate(gobj, event, kw, src) {
75484
75697
  }
75485
75698
  function ac_export(gobj, event, kw, src) {
75486
75699
  let treedb = current_treedb(gobj);
75700
+ if (reload_first(gobj, event)) return -1;
75487
75701
  if (!treedb) {
75488
75702
  log_error(`${gobj_short_name(gobj)}: ${event} with no treedb open`);
75489
75703
  return -1;
@@ -75494,6 +75708,7 @@ function ac_export(gobj, event, kw, src) {
75494
75708
  function ac_import(gobj, event, kw, src) {
75495
75709
  let treedb = current_treedb(gobj);
75496
75710
  if (refuse_if_readonly(gobj, event)) return -1;
75711
+ if (reload_first(gobj, event)) return -1;
75497
75712
  if (!treedb) {
75498
75713
  log_error(`${gobj_short_name(gobj)}: ${event} with no treedb open`);
75499
75714
  return -1;
@@ -75509,6 +75724,8 @@ function ac_preview_import(gobj, event, kw, src) {
75509
75724
  let priv = gobj.priv;
75510
75725
  let treedb = current_treedb(gobj);
75511
75726
  let incoming = null;
75727
+ if (reload_first(gobj, event)) return -1;
75728
+ if (built_on_a_replaced_model(gobj, event, kw)) return -1;
75512
75729
  if (!treedb) {
75513
75730
  log_error(`${gobj_short_name(gobj)}: ${event} with no treedb open`);
75514
75731
  return -1;
@@ -75536,6 +75753,8 @@ function ac_preview_import(gobj, event, kw, src) {
75536
75753
  function ac_apply_import(gobj, event, kw, src) {
75537
75754
  let plan = gobj.priv.import_plan;
75538
75755
  if (refuse_if_readonly(gobj, event)) return -1;
75756
+ if (reload_first(gobj, event)) return -1;
75757
+ if (built_on_a_replaced_model(gobj, event, kw)) return -1;
75539
75758
  if (!plan || plan.writes.length === 0) {
75540
75759
  log_error(`${gobj_short_name(gobj)}: ${event} with no plan to run`);
75541
75760
  return -1;
@@ -75553,7 +75772,8 @@ function run_import(gobj) {
75553
75772
  let priv = gobj.priv;
75554
75773
  let plan = priv.import_plan;
75555
75774
  if (!plan) {
75556
- log_error(`${gobj_short_name(gobj)}: the import plan is gone`);
75775
+ log_warning(`${gobj_short_name(gobj)}: the import plan is gone`);
75776
+ yui_shell_show_error(yui_shell_of(gobj), "the import plan is gone: preview it again", { t });
75557
75777
  return -1;
75558
75778
  }
75559
75779
  close_dialog(gobj);
@@ -75578,6 +75798,8 @@ function run_import(gobj) {
75578
75798
  function ac_confirmed(gobj, event, kw, src) {
75579
75799
  let priv = gobj.priv;
75580
75800
  if (refuse_if_readonly(gobj, event)) return -1;
75801
+ if (reload_first(gobj, event)) return -1;
75802
+ if (built_on_a_replaced_model(gobj, event, kw)) return -1;
75581
75803
  if (kw.what === "import") return run_import(gobj);
75582
75804
  if (kw.what === "topic") {
75583
75805
  let topic = find_topic(priv.model, priv.treedb_id, kw.topic);
@@ -75711,7 +75933,11 @@ function create_gclass$1(gclass_name) {
75711
75933
  ]
75712
75934
  ];
75713
75935
  const states = [
75714
- ["ST_IDLE", COMMON.slice()],
75936
+ ["ST_IDLE", COMMON.concat([[
75937
+ "EV_CONFIRMED",
75938
+ ac_confirmed_on_nothing,
75939
+ null
75940
+ ]])],
75715
75941
  ["ST_LOADING", COMMON.concat([
75716
75942
  [
75717
75943
  "EV_SAVE_COLUMN",
@@ -75744,7 +75970,11 @@ function create_gclass$1(gclass_name) {
75744
75970
  null
75745
75971
  ]
75746
75972
  ])],
75747
- ["ST_EMPTY", COMMON.slice()],
75973
+ ["ST_EMPTY", COMMON.concat([[
75974
+ "EV_CONFIRMED",
75975
+ ac_confirmed_on_nothing,
75976
+ null
75977
+ ]])],
75748
75978
  ["ST_TREEDBS", COMMON.concat([
75749
75979
  [
75750
75980
  "EV_SELECT_TREEDB",