@yuneta/gobj-ui 7.23.195 → 7.23.197

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.
@@ -70163,10 +70163,11 @@ function register_c_yui_treedb_schema() {
70163
70163
  * operator drew does nothing at all. Restarting on
70164
70164
  * this is an outage with no gain.
70165
70165
  * `warning` the schema opens and something is not what it looks
70166
- * like — an unbumped `topic_version` masking the whole
70167
- * edit is the one that costs the most time, because
70168
- * the restart SUCCEEDS and the change is simply not
70169
- * there.
70166
+ * like.
70167
+ *
70168
+ * A version is not checked here: an edit is a DRAFT and moves no
70169
+ * version, and C_TREEDB's save-schema raises the versions of what
70170
+ * changed when it publishes it.
70170
70171
  *
70171
70172
  * Every `code` is an i18n key: the caller renders, this decides.
70172
70173
  *
@@ -70213,9 +70214,6 @@ var FKEY_TYPES = [
70213
70214
  ***************************************************************/
70214
70215
  function validate_schema(treedb, options) {
70215
70216
  let findings = [];
70216
- let opts = options || {};
70217
- let written = Array.isArray(opts.written_topics) ? opts.written_topics : [];
70218
- let baseline = opts.baseline || {};
70219
70217
  if (!treedb || !Array.isArray(treedb.topics)) return findings;
70220
70218
  let add = (severity, code, topic, col, detail) => {
70221
70219
  findings.push({
@@ -70236,10 +70234,6 @@ function validate_schema(treedb, options) {
70236
70234
  let pkey = topic.pkey || "id";
70237
70235
  if (col_names.indexOf(pkey) < 0) add("error", "pkey names no column", topic.name, "", pkey);
70238
70236
  for (let pkey2 of topic_pkey2s(topic.record)) if (col_names.indexOf(pkey2) < 0) add("error", "pkey2 names no column", topic.name, "", pkey2);
70239
- if (written.indexOf(topic.id) >= 0) {
70240
- let before = baseline[topic.id];
70241
- if (before !== void 0 && String(before) === String(topic.topic_version)) add("warning", "topic version not bumped", topic.name, "", String(topic.topic_version));
70242
- }
70243
70237
  for (let col of topic.cols) {
70244
70238
  let flags = col_flags(col.record);
70245
70239
  let type = col.record ? col.record.type : "";
@@ -70463,6 +70457,230 @@ function topic_descs(treedb) {
70463
70457
  }
70464
70458
  return descs;
70465
70459
  }
70460
+ //#endregion
70461
+ //#region src/schema_to_diagram.js
70462
+ /***********************************************************************
70463
+ * schema_to_diagram.js
70464
+ *
70465
+ * The graph of a treedb schema, drawn as text.
70466
+ *
70467
+ * It is the comment that heads every `treedb_schema_<db>.c`: one
70468
+ * box per topic, in the order the schema declares them, a row per
70469
+ * column, and every hook -> fkey link drawn as a line from the
70470
+ * hook's row (`◀`) to the fkey's row. It is DERIVED from the
70471
+ * schema, never drawn by hand, so the file that holds the literal
70472
+ * can be replaced whole with an export and its picture is never
70473
+ * behind it.
70474
+ *
70475
+ * Each link runs down its own LANE to the right of the boxes, the
70476
+ * shorter links nearer to them, so no search is needed to lay it
70477
+ * out: the only ambiguity left is where a line crosses a lane that
70478
+ * is in use, and that is drawn as a junction.
70479
+ *
70480
+ * The input is the schema as its literal holds it (what
70481
+ * schema_to_json() answers, or the parsed literal itself):
70482
+ * {id, schema_version, topics: [{id, pkey2s, tkey, main_topic,
70483
+ * cols: {name: {type, flag, hook}}}]}
70484
+ *
70485
+ * Copyright (c) 2026, ArtGins.
70486
+ * All Rights Reserved.
70487
+ ***********************************************************************/
70488
+ var INDENT = 8;
70489
+ var MIN_INNER = 27;
70490
+ var LANE_GAP = 4;
70491
+ var LEGEND = [
70492
+ "{} dict hook (N unique children)",
70493
+ "[] list hook (n not-unique children)",
70494
+ "() string hook (1 unique child)",
70495
+ "(↖) 1 fkey (1 parent)",
70496
+ "[↖] n fkeys (n parents)",
70497
+ "{↖} N fkeys (N parents)",
70498
+ "",
70499
+ "(2) pkey2 - secondary key",
70500
+ "(t) tkey - time key",
70501
+ "* field required",
70502
+ "= field inherited"
70503
+ ];
70504
+ var L = 1;
70505
+ var R = 2;
70506
+ var U = 4;
70507
+ var D = 8;
70508
+ var GLYPH = {
70509
+ [3]: "─",
70510
+ [L]: "─",
70511
+ [R]: "─",
70512
+ [12]: "│",
70513
+ [U]: "│",
70514
+ [D]: "│",
70515
+ [10]: "┌",
70516
+ [9]: "┐",
70517
+ [6]: "└",
70518
+ [5]: "┘",
70519
+ [11]: "┬",
70520
+ [7]: "┴",
70521
+ [14]: "├",
70522
+ [13]: "┤",
70523
+ [15]: "┼"
70524
+ };
70525
+ /***************************************************************
70526
+ * A value that may be one string or a list of them, as a list.
70527
+ ***************************************************************/
70528
+ function as_list(value) {
70529
+ if (typeof value === "string") return value ? [value] : [];
70530
+ if (Array.isArray(value)) {
70531
+ let list = [];
70532
+ for (let v of value) list.push(...as_list(v));
70533
+ return list;
70534
+ }
70535
+ if (value && typeof value === "object") {
70536
+ let list = [];
70537
+ for (let v of Object.values(value)) list.push(...as_list(v));
70538
+ return list;
70539
+ }
70540
+ return [];
70541
+ }
70542
+ /***************************************************************
70543
+ * The columns of a topic as [name, desc] in declared order: a
70544
+ * dict in the literals, a list of {id} in some stores.
70545
+ ***************************************************************/
70546
+ function topic_cols(topic) {
70547
+ let cols = topic.cols || {};
70548
+ if (Array.isArray(cols)) return cols.map((c) => [c.id, c]);
70549
+ return Object.entries(cols);
70550
+ }
70551
+ /***************************************************************
70552
+ * What a link column looks like at its end of the line.
70553
+ ***************************************************************/
70554
+ function hook_marker(type) {
70555
+ if (type === "dict" || type === "object") return "{}";
70556
+ if (type === "array" || type === "list") return "[]";
70557
+ return "()";
70558
+ }
70559
+ function fkey_marker(type) {
70560
+ if (type === "dict" || type === "object") return "{↖}";
70561
+ if (type === "array" || type === "list") return "[↖]";
70562
+ return "(↖)";
70563
+ }
70564
+ /***************************************************************
70565
+ * One row of a box: the text inside it, and whether it is a
70566
+ * link column (written against the right edge, where its line
70567
+ * leaves the box).
70568
+ ***************************************************************/
70569
+ function col_row(topic, name, desc, pkey2s) {
70570
+ let flags = as_list(desc.flag);
70571
+ if (flags.indexOf("hook") >= 0) return {
70572
+ text: `${name} ${hook_marker(desc.type)}`,
70573
+ link: true
70574
+ };
70575
+ if (flags.indexOf("fkey") >= 0) return {
70576
+ text: `${name} ${fkey_marker(desc.type)}`,
70577
+ link: true
70578
+ };
70579
+ let text = `${flags.indexOf("required") >= 0 ? "*" : flags.indexOf("inherit") >= 0 ? "=" : " "} ${name}`;
70580
+ if (pkey2s.indexOf(name) >= 0) text += " (2)";
70581
+ if (topic.tkey && topic.tkey === name) text += " (t)";
70582
+ return {
70583
+ text,
70584
+ link: false
70585
+ };
70586
+ }
70587
+ /***************************************************************
70588
+ * schema_to_diagram(schema) -> the text, one string with a
70589
+ * newline per line and no trailing spaces.
70590
+ ***************************************************************/
70591
+ function schema_to_diagram(schema) {
70592
+ if (!schema || !Array.isArray(schema.topics)) return "";
70593
+ let boxes = [];
70594
+ let inner = MIN_INNER;
70595
+ for (let topic of schema.topics) {
70596
+ let pkey2s = as_list(topic.pkey2s);
70597
+ let rows = topic_cols(topic).map(([name, desc]) => Object.assign({ name }, col_row(topic, name, desc || {}, pkey2s)));
70598
+ for (let row of rows) inner = Math.max(inner, row.text.length + 2);
70599
+ let title = topic.id + (topic.main_topic ? " (main_topic)" : "");
70600
+ inner = Math.max(inner, title.length);
70601
+ boxes.push({
70602
+ topic,
70603
+ title,
70604
+ rows
70605
+ });
70606
+ }
70607
+ let lines = [];
70608
+ let where = {};
70609
+ let edge = INDENT + inner + 1;
70610
+ lines.push(`${schema.id || ""} (schema_version ${schema.schema_version || "?"})`);
70611
+ lines.push("");
70612
+ for (let l of LEGEND) lines.push(l);
70613
+ lines.push("");
70614
+ lines.push("");
70615
+ for (let box of boxes) {
70616
+ let pad = Math.max(0, Math.floor((inner + 2 - box.title.length) / 2));
70617
+ lines.push(" ".repeat(INDENT + pad) + box.title);
70618
+ lines.push(" ".repeat(INDENT) + "┌" + "─".repeat(inner) + "┐");
70619
+ for (let row of box.rows) {
70620
+ let text = row.link ? row.text.padStart(inner - 1) + " " : row.text.padEnd(inner);
70621
+ where[`${box.topic.id}\`${row.name}`] = lines.length;
70622
+ lines.push(" ".repeat(INDENT) + "│" + text + "│");
70623
+ }
70624
+ lines.push(" ".repeat(INDENT) + "└" + "─".repeat(inner) + "┘");
70625
+ lines.push("");
70626
+ }
70627
+ let links = [];
70628
+ for (let topic of schema.topics) for (let [name, desc] of topic_cols(topic)) {
70629
+ desc = desc || {};
70630
+ if (as_list(desc.flag).indexOf("hook") < 0) continue;
70631
+ let hook = desc.hook;
70632
+ if (!hook || typeof hook !== "object" || Array.isArray(hook)) continue;
70633
+ for (let [child_topic, fkey_col] of Object.entries(hook)) {
70634
+ let from = where[`${topic.id}\`${name}`];
70635
+ let to = where[`${child_topic}\`${fkey_col}`];
70636
+ if (from === void 0 || to === void 0) continue;
70637
+ links.push({
70638
+ from,
70639
+ to,
70640
+ top: Math.min(from, to),
70641
+ bottom: Math.max(from, to)
70642
+ });
70643
+ }
70644
+ }
70645
+ links.sort((a, b) => a.bottom - a.top - (b.bottom - b.top) || a.top - b.top);
70646
+ let lanes = [];
70647
+ for (let link of links) {
70648
+ let k = 0;
70649
+ for (;; k++) if ((lanes[k] || []).every((o) => link.bottom < o.top || o.bottom < link.top)) break;
70650
+ lanes[k] = (lanes[k] || []).concat([link]);
70651
+ link.x = edge + LANE_GAP + LANE_GAP * k;
70652
+ }
70653
+ let bits = {};
70654
+ let fixed = {};
70655
+ let join = (y, x, b) => {
70656
+ let key = `${y},${x}`;
70657
+ bits[key] = (bits[key] || 0) | b;
70658
+ };
70659
+ let hline = (y, x1, x2) => {
70660
+ for (let x = x1; x <= x2; x++) join(y, x, (x > x1 ? L : 0) | (x < x2 ? R : 0));
70661
+ };
70662
+ let vline = (x, y1, y2) => {
70663
+ for (let y = y1; y <= y2; y++) join(y, x, (y > y1 ? U : 0) | (y < y2 ? D : 0));
70664
+ };
70665
+ for (let link of links) {
70666
+ hline(link.from, edge + 2, link.x);
70667
+ hline(link.to, edge + 2, link.x);
70668
+ vline(link.x, link.top, link.bottom);
70669
+ fixed[`${link.from},${edge + 2}`] = "◀";
70670
+ }
70671
+ let out = lines.map((line, y) => {
70672
+ let chars = Array.from(line);
70673
+ for (let key of Object.keys(bits)) {
70674
+ let [ky, kx] = key.split(",").map(Number);
70675
+ if (ky !== y) continue;
70676
+ while (chars.length <= kx) chars.push(" ");
70677
+ chars[kx] = fixed[key] || GLYPH[bits[key]] || " ";
70678
+ }
70679
+ return chars.join("").replace(/\s+$/, "");
70680
+ });
70681
+ while (out.length && out[out.length - 1] === "") out.pop();
70682
+ return out.join("\n");
70683
+ }
70466
70684
  var TOPIC_KEY_ORDER = [
70467
70685
  "id",
70468
70686
  "pkey",
@@ -70667,14 +70885,22 @@ function c_pair(lines, key, value, indent, trailing) {
70667
70885
  /***************************************************************
70668
70886
  * schema_to_c(treedb, options) -> the C source text
70669
70887
  *
70670
- * options {var_name, pad}
70671
- * var_name the array's name; defaults to
70672
- * `treedb_schema_<treedb id without its prefix>`
70673
- * pad column of the `\n\` continuation
70888
+ * options {var_name, pad, with_diagram}
70889
+ * var_name the array's name; defaults to
70890
+ * `treedb_schema_<treedb id without its prefix>`
70891
+ * pad column of the `\n\` continuation
70892
+ * with_diagram the graph as a comment above the array
70893
+ * (default true)
70674
70894
  ***************************************************************/
70675
70895
  function schema_to_c(treedb, options) {
70896
+ return json_to_c(schema_to_json(treedb), options);
70897
+ }
70898
+ /***************************************************************
70899
+ * json_to_c(json, options) -> the C source text, from the schema
70900
+ * as its literal holds it. Same options as schema_to_c().
70901
+ ***************************************************************/
70902
+ function json_to_c(json, options) {
70676
70903
  let opts = options || {};
70677
- let json = schema_to_json(treedb);
70678
70904
  if (!json) return "";
70679
70905
  let name = opts.var_name;
70680
70906
  if (!name) {
@@ -70693,7 +70919,24 @@ function schema_to_c(treedb, options) {
70693
70919
  let body = lines.map((line) => {
70694
70920
  return `${line}${line.length < pad ? " ".repeat(pad - line.length) : " "}\\n\\`;
70695
70921
  });
70696
- return `static char ${name}[]= "\\\n${body.join("\n")}\n";\n`;
70922
+ let literal = `static char ${name}[]= "\\\n${body.join("\n")}\n";\n`;
70923
+ if (opts.with_diagram === false) return literal;
70924
+ return c_diagram_comment(schema_to_diagram(json)) + "\n" + literal;
70925
+ }
70926
+ /***************************************************************
70927
+ * The diagram as the comment that heads the file.
70928
+ ***************************************************************/
70929
+ function c_diagram_comment(diagram) {
70930
+ let lines = [
70931
+ "/*",
70932
+ " Generated from the literal below by schema_to_diagram() (gobj-ui).",
70933
+ " This file holds this comment and the literal, nothing else: replace",
70934
+ " it whole with an export of the schema editor.",
70935
+ ""
70936
+ ];
70937
+ for (let line of diagram.split("\n")) lines.push(line ? " " + line : "");
70938
+ lines.push("*/");
70939
+ return lines.join("\n") + "\n";
70697
70940
  }
70698
70941
  //#endregion
70699
70942
  //#region src/schema_import.js
@@ -71508,13 +71751,15 @@ function write_options(op) {
71508
71751
  * fkey, the order of a new sibling, the version that publishes
71509
71752
  * the change.
71510
71753
  *
71511
- * THE VERSION IS THE POINT, not a detail. A topic whose columns
71512
- * changed and whose `topic_version` did not is a topic whose
71513
- * persisted `topic_cols.json` masks the whole edit: the restart
71514
- * succeeds and nothing moved. Nothing in the store says so, and
71515
- * the log that would is on the node. So every write here raises
71516
- * the version of the topic it touched, and the toolbar says which
71517
- * topics are waiting for a restart.
71754
+ * AN EDIT IS A DRAFT. A write here moves no version and reaches no
71755
+ * treedb: the host SAVES it (C_TREEDB's save-schema raises the
71756
+ * topic_version of what changed and the schema_version, once) and
71757
+ * APPLIES it (apply-schema + a restart of the owning yuno). Until
71758
+ * the owner's design of M36 (2026-09-21 review) every write here
71759
+ * raised both versions, so an edit half made was already the
71760
+ * schema of the next start. The topic list marks what this session
71761
+ * wrote and has not saved; the host's EV_REFRESH after a save is
71762
+ * what forgets it.
71518
71763
  *
71519
71764
  * WHAT ELSE IS HERE, and why each is here and not somewhere else:
71520
71765
  *
@@ -71859,16 +72104,20 @@ function start_measuring(gobj) {
71859
72104
  for (let treedb of priv.model.treedbs) for (let topic of treedb.topics) priv.baseline[topic.id] = topic.topic_version;
71860
72105
  }
71861
72106
  /***************************************************************
71862
- * "This topic was written and its version did not move."
72107
+ * "This topic was written in this session and is not saved."
72108
+ *
72109
+ * An edit is a DRAFT: it moves no version and reaches no treedb
72110
+ * until the host saves it (C_TREEDB's save-schema, which raises the
72111
+ * versions of what changed) -- and the save makes the host send
72112
+ * EV_REFRESH, whose reload is what forgets the drafts.
71863
72113
  *
71864
- * ONE predicate, because it is asked in two places -- the topic
71865
- * list draws a chip with it and the column view draws a banner --
71866
- * and two copies of a rule is how they drift.
72114
+ * ONE predicate, because it is asked in three places -- the topic
72115
+ * list draws a chip with it, the column view a banner, the export
72116
+ * a warning -- and copies of a rule are how they drift.
71867
72117
  ***************************************************************/
71868
- function needs_version_bump(written, baseline, topic) {
71869
- if (!topic || !written || !baseline) return false;
71870
- if (!written[topic.id]) return false;
71871
- return String(baseline[topic.id]) === String(topic.topic_version);
72118
+ function topic_is_draft(written, topic) {
72119
+ if (!topic || !written) return false;
72120
+ return written[topic.id] ? true : false;
71872
72121
  }
71873
72122
  /***************************************************************
71874
72123
  * Say what the whole store would be refused for.
@@ -72310,7 +72559,7 @@ function render_topics(gobj, $body) {
72310
72559
  let twice = repeated_names(treedb.topics);
72311
72560
  let $rows = [];
72312
72561
  for (let topic of treedb.topics) {
72313
- let pending = needs_version_bump(priv.written, priv.baseline, topic);
72562
+ let pending = topic_is_draft(priv.written, topic);
72314
72563
  let ambiguous = !!twice[topic.name];
72315
72564
  let address = ambiguous ? topic.id : topic.name;
72316
72565
  let $actions = [];
@@ -72545,7 +72794,7 @@ function render_columns(gobj, $body) {
72545
72794
  { class: "SCHEMA_COLUMNS p-3" },
72546
72795
  []
72547
72796
  ]);
72548
- let $banner = version_banner(gobj, topic);
72797
+ let $banner = draft_banner(gobj, topic);
72549
72798
  if ($banner) $wrap.appendChild($banner);
72550
72799
  let twice = repeated_names(topic.cols);
72551
72800
  let $rows = [];
@@ -72623,66 +72872,37 @@ function render_columns(gobj, $body) {
72623
72872
  (0, _yuneta_gobj_js.refresh_language)($wrap, i18next.t);
72624
72873
  }
72625
72874
  /***************************************************************
72626
- * "This topic changed and its version did not."
72875
+ * "This topic has changes that are not saved."
72627
72876
  *
72628
- * Only shown when it is TRUE — the topic was written in this
72629
- * session and its `topic_version` still reads what it read when
72630
- * the model loaded. Every write this view makes raises it, so
72631
- * the banner is for the case where something else did not: an
72632
- * edit made from the raw tables, or a bump undone by hand.
72877
+ * A draft, not an error: the edit is in __system__ and moves no
72878
+ * version, so the treedb does not see it until the host saves it
72879
+ * (and applies it). The banner says so and offers nothing -- the
72880
+ * save is the host's, one level up, because it publishes the whole
72881
+ * treedb and not one topic.
72633
72882
  ***************************************************************/
72634
- function version_banner(gobj, topic) {
72883
+ function draft_banner(gobj, topic) {
72635
72884
  let priv = gobj.priv;
72636
- if (!needs_version_bump(priv.written, priv.baseline, topic)) return null;
72885
+ if (!topic_is_draft(priv.written, topic)) return null;
72637
72886
  if (is_readonly(gobj)) return null;
72638
- let $banner = (0, _yuneta_gobj_js.createElement2)([
72887
+ return (0, _yuneta_gobj_js.createElement2)([
72639
72888
  "div",
72640
72889
  {
72641
- class: "SCHEMA_VERSION_BANNER notification is-warning is-light p-3 mb-3 is-flex is-align-items-center",
72890
+ class: "SCHEMA_DRAFT_BANNER notification is-warning is-light p-3 mb-3 is-flex is-align-items-center",
72642
72891
  style: "gap:.5rem;"
72643
72892
  },
72644
- [
72645
- [
72646
- "span",
72647
- { class: "icon" },
72648
- [["i", { class: "yi-triangle-exclamation" }]]
72649
- ],
72650
- [
72651
- "span",
72652
- {
72653
- class: "SCHEMA_VERSION_TEXT is-size-7",
72654
- i18n: "version not raised"
72655
- },
72656
- (0, i18next.t)("version not raised")
72657
- ],
72658
- [
72659
- "button",
72660
- {
72661
- class: "SCHEMA_VERSION_BUMP button is-warning",
72662
- type: "button",
72663
- style: "margin-left:auto;",
72664
- title: (0, i18next.t)("raise the version"),
72665
- "aria-label": (0, i18next.t)("raise the version"),
72666
- "data-i18n-title": "raise the version",
72667
- "data-i18n-aria-label": "raise the version"
72668
- },
72669
- [[
72670
- "span",
72671
- { class: "icon" },
72672
- [["i", { class: "yi-circle-up" }]]
72673
- ], [
72674
- "span",
72675
- { i18n: "raise" },
72676
- (0, i18next.t)("raise")
72677
- ]]
72678
- ]
72679
- ]
72893
+ [[
72894
+ "span",
72895
+ { class: "icon" },
72896
+ [["i", { class: "yi-triangle-exclamation" }]]
72897
+ ], [
72898
+ "span",
72899
+ {
72900
+ class: "SCHEMA_DRAFT_TEXT is-size-7",
72901
+ i18n: "unsaved schema changes: save to publish them"
72902
+ },
72903
+ (0, i18next.t)("unsaved schema changes: save to publish them")
72904
+ ]]
72680
72905
  ]);
72681
- $banner.querySelector(".SCHEMA_VERSION_BUMP").addEventListener("click", (evt) => {
72682
- evt.stopPropagation();
72683
- (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_BUMP_VERSION", { topic: topic.name }, gobj);
72684
- });
72685
- return $banner;
72686
72906
  }
72687
72907
  /***************************************************************
72688
72908
  * One column, as a row. The drag events carry INDEXES in the
@@ -73551,6 +73771,8 @@ function open_report(gobj, findings, treedb_id) {
73551
73771
  * as a download, which a published page cannot start anyway.
73552
73772
  ***************************************************************/
73553
73773
  function open_export(gobj, treedb) {
73774
+ let priv = gobj.priv;
73775
+ let drafts = treedb.topics.some((topic) => topic_is_draft(priv.written, topic));
73554
73776
  let c_text = schema_to_c(treedb);
73555
73777
  let json_text = JSON.stringify(schema_to_json(treedb), null, 4);
73556
73778
  let $content = (0, _yuneta_gobj_js.createElement2)([
@@ -73604,13 +73826,29 @@ function open_export(gobj, treedb) {
73604
73826
  },
73605
73827
  (0, i18next.t)("an edit made here works and lives in no source: this is what to paste back")
73606
73828
  ],
73829
+ drafts ? [
73830
+ "p",
73831
+ {
73832
+ class: "SCHEMA_EXPORT_DRAFTS notification is-warning is-light p-2 mb-2 is-size-7",
73833
+ i18n: "unsaved changes: save first, so the literal carries the versions that publish it"
73834
+ },
73835
+ (0, i18next.t)("unsaved changes: save first, so the literal carries the versions that publish it")
73836
+ ] : [
73837
+ "span",
73838
+ {},
73839
+ ""
73840
+ ],
73607
73841
  [
73608
73842
  "textarea",
73609
73843
  {
73610
73844
  class: "SCHEMA_EXPORT_TEXT textarea is-small is-family-monospace",
73611
73845
  rows: "18",
73612
73846
  readonly: "readonly",
73613
- spellcheck: "false"
73847
+ spellcheck: "false",
73848
+ title: (0, i18next.t)("schema"),
73849
+ "data-i18n-title": "schema",
73850
+ "aria-label": (0, i18next.t)("schema"),
73851
+ "data-i18n-aria-label": "schema"
73614
73852
  },
73615
73853
  c_text
73616
73854
  ],
@@ -73624,7 +73862,11 @@ function open_export(gobj, treedb) {
73624
73862
  "button",
73625
73863
  {
73626
73864
  class: "SCHEMA_EXPORT_COPY button",
73627
- type: "button"
73865
+ type: "button",
73866
+ title: (0, i18next.t)("copy"),
73867
+ "data-i18n-title": "copy",
73868
+ "aria-label": (0, i18next.t)("copy"),
73869
+ "data-i18n-aria-label": "copy"
73628
73870
  },
73629
73871
  [[
73630
73872
  "span",
@@ -73959,52 +74201,6 @@ function open_orphans(gobj) {
73959
74201
  * Writing
73960
74202
  ***************************/
73961
74203
  /***************************************************************
73962
- * THE TWO VERSIONS A SCHEMA EDIT HAS TO RAISE, and why both.
73963
- *
73964
- * `topic_version` is what publishes a change of a topic's
73965
- * columns: leave it and the persisted `topic_cols.json` masks
73966
- * the edit, the restart succeeds and nothing moved.
73967
- *
73968
- * `schema_version` is what publishes the schema as a whole —
73969
- * "the stored one wins on ties, and the incoming one has to be
73970
- * strictly newer to take over" (c_treedb.c). It is safe to
73971
- * raise: re-projection from C compares `c_schema_version`, the
73972
- * version of the LITERAL, precisely so that an edit made here
73973
- * survives every start until a newer literal arrives.
73974
- *
73975
- * So every write that changes a schema carries both, and the
73976
- * operator is never asked to remember either.
73977
- ***************************************************************/
73978
- function version_writes(gobj, topic) {
73979
- let treedb = current_treedb(gobj);
73980
- let writes = [];
73981
- if (topic) {
73982
- let version = Number(topic.topic_version);
73983
- if (!isFinite(version)) version = 0;
73984
- writes.push({
73985
- op: "update",
73986
- topic_name: T_TOPICS,
73987
- record: {
73988
- id: topic.id,
73989
- topic_version: version + 1
73990
- }
73991
- });
73992
- }
73993
- if (treedb) {
73994
- let version = Number(treedb.schema_version);
73995
- if (!isFinite(version)) version = 0;
73996
- writes.push({
73997
- op: "update",
73998
- topic_name: T_TREEDBS,
73999
- record: {
74000
- id: treedb.id,
74001
- schema_version: version + 1
74002
- }
74003
- });
74004
- }
74005
- return writes;
74006
- }
74007
- /***************************************************************
74008
74204
  * WHERE THE COLUMNS WERE before this session started moving them.
74009
74205
  *
74010
74206
  * A drag is a WRITE: the order is a field of the column, so the drop
@@ -74463,7 +74659,7 @@ function ac_save_column(gobj, event, kw, src) {
74463
74659
  op: kw.creating ? "create" : "update",
74464
74660
  topic_name: T_COLS,
74465
74661
  record: column_record(gobj, topic, col, values)
74466
- }].concat(version_writes(gobj, topic)));
74662
+ }]);
74467
74663
  }
74468
74664
  function ac_delete_column(gobj, event, kw, src) {
74469
74665
  let priv = gobj.priv;
@@ -74514,7 +74710,7 @@ function ac_move_column(gobj, event, kw, src) {
74514
74710
  };
74515
74711
  });
74516
74712
  if (writes.length === 0) return 0;
74517
- return queue_writes(gobj, writes.concat(version_writes(gobj, topic)));
74713
+ return queue_writes(gobj, writes);
74518
74714
  }
74519
74715
  /***************************************************************
74520
74716
  * Put the columns back where they were before the dragging
@@ -74534,7 +74730,7 @@ function ac_undo_order(gobj, event, kw, src) {
74534
74730
  return 0;
74535
74731
  }
74536
74732
  delete priv.order_undo[topic.id];
74537
- return queue_writes(gobj, writes.concat(version_writes(gobj, topic)));
74733
+ return queue_writes(gobj, writes);
74538
74734
  }
74539
74735
  /***************************************************************
74540
74736
  * Editing a topic.
@@ -74587,7 +74783,7 @@ function ac_save_topic(gobj, event, kw, src) {
74587
74783
  op: kw.creating ? "create" : "update",
74588
74784
  topic_name: T_TOPICS,
74589
74785
  record: topic_record(gobj, treedb, topic, values)
74590
- }].concat(version_writes(gobj, null)));
74786
+ }]);
74591
74787
  }
74592
74788
  function ac_delete_topic(gobj, event, kw, src) {
74593
74789
  let priv = gobj.priv;
@@ -74604,20 +74800,6 @@ function ac_delete_topic(gobj, event, kw, src) {
74604
74800
  return 0;
74605
74801
  }
74606
74802
  /***************************************************************
74607
- * Raising a version by hand: what the banner offers when
74608
- * something else wrote the topic and left its version alone.
74609
- ***************************************************************/
74610
- function ac_bump_version(gobj, event, kw, src) {
74611
- let priv = gobj.priv;
74612
- let topic = find_topic(priv.model, priv.treedb_id, kw.topic);
74613
- if (refuse_if_readonly(gobj, event)) return -1;
74614
- if (!topic) {
74615
- (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${event} names no topic: ${kw.topic}`);
74616
- return -1;
74617
- }
74618
- return queue_writes(gobj, version_writes(gobj, topic));
74619
- }
74620
- /***************************************************************
74621
74803
  * Deleting a leftover.
74622
74804
  ***************************************************************/
74623
74805
  function ac_delete_orphan(gobj, event, kw, src) {
@@ -74766,11 +74948,10 @@ function ac_confirmed(gobj, event, kw, src) {
74766
74948
  topic_name: T_TOPICS,
74767
74949
  record: { id: topic.id }
74768
74950
  });
74769
- return queue_writes(gobj, writes.concat(version_writes(gobj, null)));
74951
+ return queue_writes(gobj, writes);
74770
74952
  }
74771
74953
  if (kw.what === "column") {
74772
- let topic = find_topic(priv.model, priv.treedb_id, kw.topic);
74773
- let col = topic ? find_col(priv.model, priv.treedb_id, kw.topic, kw.col) : null;
74954
+ let col = find_topic(priv.model, priv.treedb_id, kw.topic) ? find_col(priv.model, priv.treedb_id, kw.topic, kw.col) : null;
74774
74955
  if (!col) {
74775
74956
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${event} names no column: ${kw.col}`);
74776
74957
  return -1;
@@ -74779,7 +74960,7 @@ function ac_confirmed(gobj, event, kw, src) {
74779
74960
  op: "delete",
74780
74961
  topic_name: T_COLS,
74781
74962
  record: { id: col.id }
74782
- }].concat(version_writes(gobj, topic)));
74963
+ }]);
74783
74964
  }
74784
74965
  (0, _yuneta_gobj_js.log_error)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: ${event} of nothing this view asked about: ${kw.what}`);
74785
74966
  return -1;
@@ -74968,11 +75149,6 @@ function create_gclass$1(gclass_name) {
74968
75149
  "EV_UNDO_ORDER",
74969
75150
  ac_undo_order,
74970
75151
  null
74971
- ],
74972
- [
74973
- "EV_BUMP_VERSION",
74974
- ac_bump_version,
74975
- null
74976
75152
  ]
74977
75153
  ])],
74978
75154
  ["ST_SAVING", COMMON.slice()]
@@ -75001,7 +75177,6 @@ function create_gclass$1(gclass_name) {
75001
75177
  ["EV_SAVE_COLUMN", 0],
75002
75178
  ["EV_MOVE_COLUMN", 0],
75003
75179
  ["EV_UNDO_ORDER", 0],
75004
- ["EV_BUMP_VERSION", 0],
75005
75180
  ["EV_VALIDATE", 0],
75006
75181
  ["EV_EXPORT", 0],
75007
75182
  ["EV_IMPORT", 0],