@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.
- package/README.md +42 -11
- package/dist/gobj-ui.cjs.js +336 -161
- package/dist/gobj-ui.es.js +336 -161
- package/package.json +1 -1
- package/src/c_yui_schema_editor.js +57 -122
- package/src/schema_to_c.js +49 -9
- package/src/schema_to_c.test.js +21 -3
- package/src/schema_to_diagram.js +301 -0
- package/src/schema_to_diagram.test.js +107 -0
- package/src/schema_validate.js +5 -17
- package/src/schema_validate.test.js +4 -24
- package/src/schema_version_bump.test.js +14 -71
package/dist/gobj-ui.es.js
CHANGED
|
@@ -70158,10 +70158,11 @@ function register_c_yui_treedb_schema() {
|
|
|
70158
70158
|
* operator drew does nothing at all. Restarting on
|
|
70159
70159
|
* this is an outage with no gain.
|
|
70160
70160
|
* `warning` the schema opens and something is not what it looks
|
|
70161
|
-
* like
|
|
70162
|
-
*
|
|
70163
|
-
*
|
|
70164
|
-
*
|
|
70161
|
+
* like.
|
|
70162
|
+
*
|
|
70163
|
+
* A version is not checked here: an edit is a DRAFT and moves no
|
|
70164
|
+
* version, and C_TREEDB's save-schema raises the versions of what
|
|
70165
|
+
* changed when it publishes it.
|
|
70165
70166
|
*
|
|
70166
70167
|
* Every `code` is an i18n key: the caller renders, this decides.
|
|
70167
70168
|
*
|
|
@@ -70208,9 +70209,6 @@ var FKEY_TYPES = [
|
|
|
70208
70209
|
***************************************************************/
|
|
70209
70210
|
function validate_schema(treedb, options) {
|
|
70210
70211
|
let findings = [];
|
|
70211
|
-
let opts = options || {};
|
|
70212
|
-
let written = Array.isArray(opts.written_topics) ? opts.written_topics : [];
|
|
70213
|
-
let baseline = opts.baseline || {};
|
|
70214
70212
|
if (!treedb || !Array.isArray(treedb.topics)) return findings;
|
|
70215
70213
|
let add = (severity, code, topic, col, detail) => {
|
|
70216
70214
|
findings.push({
|
|
@@ -70231,10 +70229,6 @@ function validate_schema(treedb, options) {
|
|
|
70231
70229
|
let pkey = topic.pkey || "id";
|
|
70232
70230
|
if (col_names.indexOf(pkey) < 0) add("error", "pkey names no column", topic.name, "", pkey);
|
|
70233
70231
|
for (let pkey2 of topic_pkey2s(topic.record)) if (col_names.indexOf(pkey2) < 0) add("error", "pkey2 names no column", topic.name, "", pkey2);
|
|
70234
|
-
if (written.indexOf(topic.id) >= 0) {
|
|
70235
|
-
let before = baseline[topic.id];
|
|
70236
|
-
if (before !== void 0 && String(before) === String(topic.topic_version)) add("warning", "topic version not bumped", topic.name, "", String(topic.topic_version));
|
|
70237
|
-
}
|
|
70238
70232
|
for (let col of topic.cols) {
|
|
70239
70233
|
let flags = col_flags(col.record);
|
|
70240
70234
|
let type = col.record ? col.record.type : "";
|
|
@@ -70458,6 +70452,230 @@ function topic_descs(treedb) {
|
|
|
70458
70452
|
}
|
|
70459
70453
|
return descs;
|
|
70460
70454
|
}
|
|
70455
|
+
//#endregion
|
|
70456
|
+
//#region src/schema_to_diagram.js
|
|
70457
|
+
/***********************************************************************
|
|
70458
|
+
* schema_to_diagram.js
|
|
70459
|
+
*
|
|
70460
|
+
* The graph of a treedb schema, drawn as text.
|
|
70461
|
+
*
|
|
70462
|
+
* It is the comment that heads every `treedb_schema_<db>.c`: one
|
|
70463
|
+
* box per topic, in the order the schema declares them, a row per
|
|
70464
|
+
* column, and every hook -> fkey link drawn as a line from the
|
|
70465
|
+
* hook's row (`◀`) to the fkey's row. It is DERIVED from the
|
|
70466
|
+
* schema, never drawn by hand, so the file that holds the literal
|
|
70467
|
+
* can be replaced whole with an export and its picture is never
|
|
70468
|
+
* behind it.
|
|
70469
|
+
*
|
|
70470
|
+
* Each link runs down its own LANE to the right of the boxes, the
|
|
70471
|
+
* shorter links nearer to them, so no search is needed to lay it
|
|
70472
|
+
* out: the only ambiguity left is where a line crosses a lane that
|
|
70473
|
+
* is in use, and that is drawn as a junction.
|
|
70474
|
+
*
|
|
70475
|
+
* The input is the schema as its literal holds it (what
|
|
70476
|
+
* schema_to_json() answers, or the parsed literal itself):
|
|
70477
|
+
* {id, schema_version, topics: [{id, pkey2s, tkey, main_topic,
|
|
70478
|
+
* cols: {name: {type, flag, hook}}}]}
|
|
70479
|
+
*
|
|
70480
|
+
* Copyright (c) 2026, ArtGins.
|
|
70481
|
+
* All Rights Reserved.
|
|
70482
|
+
***********************************************************************/
|
|
70483
|
+
var INDENT = 8;
|
|
70484
|
+
var MIN_INNER = 27;
|
|
70485
|
+
var LANE_GAP = 4;
|
|
70486
|
+
var LEGEND = [
|
|
70487
|
+
"{} dict hook (N unique children)",
|
|
70488
|
+
"[] list hook (n not-unique children)",
|
|
70489
|
+
"() string hook (1 unique child)",
|
|
70490
|
+
"(↖) 1 fkey (1 parent)",
|
|
70491
|
+
"[↖] n fkeys (n parents)",
|
|
70492
|
+
"{↖} N fkeys (N parents)",
|
|
70493
|
+
"",
|
|
70494
|
+
"(2) pkey2 - secondary key",
|
|
70495
|
+
"(t) tkey - time key",
|
|
70496
|
+
"* field required",
|
|
70497
|
+
"= field inherited"
|
|
70498
|
+
];
|
|
70499
|
+
var L = 1;
|
|
70500
|
+
var R = 2;
|
|
70501
|
+
var U = 4;
|
|
70502
|
+
var D = 8;
|
|
70503
|
+
var GLYPH = {
|
|
70504
|
+
[3]: "─",
|
|
70505
|
+
[L]: "─",
|
|
70506
|
+
[R]: "─",
|
|
70507
|
+
[12]: "│",
|
|
70508
|
+
[U]: "│",
|
|
70509
|
+
[D]: "│",
|
|
70510
|
+
[10]: "┌",
|
|
70511
|
+
[9]: "┐",
|
|
70512
|
+
[6]: "└",
|
|
70513
|
+
[5]: "┘",
|
|
70514
|
+
[11]: "┬",
|
|
70515
|
+
[7]: "┴",
|
|
70516
|
+
[14]: "├",
|
|
70517
|
+
[13]: "┤",
|
|
70518
|
+
[15]: "┼"
|
|
70519
|
+
};
|
|
70520
|
+
/***************************************************************
|
|
70521
|
+
* A value that may be one string or a list of them, as a list.
|
|
70522
|
+
***************************************************************/
|
|
70523
|
+
function as_list(value) {
|
|
70524
|
+
if (typeof value === "string") return value ? [value] : [];
|
|
70525
|
+
if (Array.isArray(value)) {
|
|
70526
|
+
let list = [];
|
|
70527
|
+
for (let v of value) list.push(...as_list(v));
|
|
70528
|
+
return list;
|
|
70529
|
+
}
|
|
70530
|
+
if (value && typeof value === "object") {
|
|
70531
|
+
let list = [];
|
|
70532
|
+
for (let v of Object.values(value)) list.push(...as_list(v));
|
|
70533
|
+
return list;
|
|
70534
|
+
}
|
|
70535
|
+
return [];
|
|
70536
|
+
}
|
|
70537
|
+
/***************************************************************
|
|
70538
|
+
* The columns of a topic as [name, desc] in declared order: a
|
|
70539
|
+
* dict in the literals, a list of {id} in some stores.
|
|
70540
|
+
***************************************************************/
|
|
70541
|
+
function topic_cols(topic) {
|
|
70542
|
+
let cols = topic.cols || {};
|
|
70543
|
+
if (Array.isArray(cols)) return cols.map((c) => [c.id, c]);
|
|
70544
|
+
return Object.entries(cols);
|
|
70545
|
+
}
|
|
70546
|
+
/***************************************************************
|
|
70547
|
+
* What a link column looks like at its end of the line.
|
|
70548
|
+
***************************************************************/
|
|
70549
|
+
function hook_marker(type) {
|
|
70550
|
+
if (type === "dict" || type === "object") return "{}";
|
|
70551
|
+
if (type === "array" || type === "list") return "[]";
|
|
70552
|
+
return "()";
|
|
70553
|
+
}
|
|
70554
|
+
function fkey_marker(type) {
|
|
70555
|
+
if (type === "dict" || type === "object") return "{↖}";
|
|
70556
|
+
if (type === "array" || type === "list") return "[↖]";
|
|
70557
|
+
return "(↖)";
|
|
70558
|
+
}
|
|
70559
|
+
/***************************************************************
|
|
70560
|
+
* One row of a box: the text inside it, and whether it is a
|
|
70561
|
+
* link column (written against the right edge, where its line
|
|
70562
|
+
* leaves the box).
|
|
70563
|
+
***************************************************************/
|
|
70564
|
+
function col_row(topic, name, desc, pkey2s) {
|
|
70565
|
+
let flags = as_list(desc.flag);
|
|
70566
|
+
if (flags.indexOf("hook") >= 0) return {
|
|
70567
|
+
text: `${name} ${hook_marker(desc.type)}`,
|
|
70568
|
+
link: true
|
|
70569
|
+
};
|
|
70570
|
+
if (flags.indexOf("fkey") >= 0) return {
|
|
70571
|
+
text: `${name} ${fkey_marker(desc.type)}`,
|
|
70572
|
+
link: true
|
|
70573
|
+
};
|
|
70574
|
+
let text = `${flags.indexOf("required") >= 0 ? "*" : flags.indexOf("inherit") >= 0 ? "=" : " "} ${name}`;
|
|
70575
|
+
if (pkey2s.indexOf(name) >= 0) text += " (2)";
|
|
70576
|
+
if (topic.tkey && topic.tkey === name) text += " (t)";
|
|
70577
|
+
return {
|
|
70578
|
+
text,
|
|
70579
|
+
link: false
|
|
70580
|
+
};
|
|
70581
|
+
}
|
|
70582
|
+
/***************************************************************
|
|
70583
|
+
* schema_to_diagram(schema) -> the text, one string with a
|
|
70584
|
+
* newline per line and no trailing spaces.
|
|
70585
|
+
***************************************************************/
|
|
70586
|
+
function schema_to_diagram(schema) {
|
|
70587
|
+
if (!schema || !Array.isArray(schema.topics)) return "";
|
|
70588
|
+
let boxes = [];
|
|
70589
|
+
let inner = MIN_INNER;
|
|
70590
|
+
for (let topic of schema.topics) {
|
|
70591
|
+
let pkey2s = as_list(topic.pkey2s);
|
|
70592
|
+
let rows = topic_cols(topic).map(([name, desc]) => Object.assign({ name }, col_row(topic, name, desc || {}, pkey2s)));
|
|
70593
|
+
for (let row of rows) inner = Math.max(inner, row.text.length + 2);
|
|
70594
|
+
let title = topic.id + (topic.main_topic ? " (main_topic)" : "");
|
|
70595
|
+
inner = Math.max(inner, title.length);
|
|
70596
|
+
boxes.push({
|
|
70597
|
+
topic,
|
|
70598
|
+
title,
|
|
70599
|
+
rows
|
|
70600
|
+
});
|
|
70601
|
+
}
|
|
70602
|
+
let lines = [];
|
|
70603
|
+
let where = {};
|
|
70604
|
+
let edge = INDENT + inner + 1;
|
|
70605
|
+
lines.push(`${schema.id || ""} (schema_version ${schema.schema_version || "?"})`);
|
|
70606
|
+
lines.push("");
|
|
70607
|
+
for (let l of LEGEND) lines.push(l);
|
|
70608
|
+
lines.push("");
|
|
70609
|
+
lines.push("");
|
|
70610
|
+
for (let box of boxes) {
|
|
70611
|
+
let pad = Math.max(0, Math.floor((inner + 2 - box.title.length) / 2));
|
|
70612
|
+
lines.push(" ".repeat(INDENT + pad) + box.title);
|
|
70613
|
+
lines.push(" ".repeat(INDENT) + "┌" + "─".repeat(inner) + "┐");
|
|
70614
|
+
for (let row of box.rows) {
|
|
70615
|
+
let text = row.link ? row.text.padStart(inner - 1) + " " : row.text.padEnd(inner);
|
|
70616
|
+
where[`${box.topic.id}\`${row.name}`] = lines.length;
|
|
70617
|
+
lines.push(" ".repeat(INDENT) + "│" + text + "│");
|
|
70618
|
+
}
|
|
70619
|
+
lines.push(" ".repeat(INDENT) + "└" + "─".repeat(inner) + "┘");
|
|
70620
|
+
lines.push("");
|
|
70621
|
+
}
|
|
70622
|
+
let links = [];
|
|
70623
|
+
for (let topic of schema.topics) for (let [name, desc] of topic_cols(topic)) {
|
|
70624
|
+
desc = desc || {};
|
|
70625
|
+
if (as_list(desc.flag).indexOf("hook") < 0) continue;
|
|
70626
|
+
let hook = desc.hook;
|
|
70627
|
+
if (!hook || typeof hook !== "object" || Array.isArray(hook)) continue;
|
|
70628
|
+
for (let [child_topic, fkey_col] of Object.entries(hook)) {
|
|
70629
|
+
let from = where[`${topic.id}\`${name}`];
|
|
70630
|
+
let to = where[`${child_topic}\`${fkey_col}`];
|
|
70631
|
+
if (from === void 0 || to === void 0) continue;
|
|
70632
|
+
links.push({
|
|
70633
|
+
from,
|
|
70634
|
+
to,
|
|
70635
|
+
top: Math.min(from, to),
|
|
70636
|
+
bottom: Math.max(from, to)
|
|
70637
|
+
});
|
|
70638
|
+
}
|
|
70639
|
+
}
|
|
70640
|
+
links.sort((a, b) => a.bottom - a.top - (b.bottom - b.top) || a.top - b.top);
|
|
70641
|
+
let lanes = [];
|
|
70642
|
+
for (let link of links) {
|
|
70643
|
+
let k = 0;
|
|
70644
|
+
for (;; k++) if ((lanes[k] || []).every((o) => link.bottom < o.top || o.bottom < link.top)) break;
|
|
70645
|
+
lanes[k] = (lanes[k] || []).concat([link]);
|
|
70646
|
+
link.x = edge + LANE_GAP + LANE_GAP * k;
|
|
70647
|
+
}
|
|
70648
|
+
let bits = {};
|
|
70649
|
+
let fixed = {};
|
|
70650
|
+
let join = (y, x, b) => {
|
|
70651
|
+
let key = `${y},${x}`;
|
|
70652
|
+
bits[key] = (bits[key] || 0) | b;
|
|
70653
|
+
};
|
|
70654
|
+
let hline = (y, x1, x2) => {
|
|
70655
|
+
for (let x = x1; x <= x2; x++) join(y, x, (x > x1 ? L : 0) | (x < x2 ? R : 0));
|
|
70656
|
+
};
|
|
70657
|
+
let vline = (x, y1, y2) => {
|
|
70658
|
+
for (let y = y1; y <= y2; y++) join(y, x, (y > y1 ? U : 0) | (y < y2 ? D : 0));
|
|
70659
|
+
};
|
|
70660
|
+
for (let link of links) {
|
|
70661
|
+
hline(link.from, edge + 2, link.x);
|
|
70662
|
+
hline(link.to, edge + 2, link.x);
|
|
70663
|
+
vline(link.x, link.top, link.bottom);
|
|
70664
|
+
fixed[`${link.from},${edge + 2}`] = "◀";
|
|
70665
|
+
}
|
|
70666
|
+
let out = lines.map((line, y) => {
|
|
70667
|
+
let chars = Array.from(line);
|
|
70668
|
+
for (let key of Object.keys(bits)) {
|
|
70669
|
+
let [ky, kx] = key.split(",").map(Number);
|
|
70670
|
+
if (ky !== y) continue;
|
|
70671
|
+
while (chars.length <= kx) chars.push(" ");
|
|
70672
|
+
chars[kx] = fixed[key] || GLYPH[bits[key]] || " ";
|
|
70673
|
+
}
|
|
70674
|
+
return chars.join("").replace(/\s+$/, "");
|
|
70675
|
+
});
|
|
70676
|
+
while (out.length && out[out.length - 1] === "") out.pop();
|
|
70677
|
+
return out.join("\n");
|
|
70678
|
+
}
|
|
70461
70679
|
var TOPIC_KEY_ORDER = [
|
|
70462
70680
|
"id",
|
|
70463
70681
|
"pkey",
|
|
@@ -70662,14 +70880,22 @@ function c_pair(lines, key, value, indent, trailing) {
|
|
|
70662
70880
|
/***************************************************************
|
|
70663
70881
|
* schema_to_c(treedb, options) -> the C source text
|
|
70664
70882
|
*
|
|
70665
|
-
* options {var_name, pad}
|
|
70666
|
-
* var_name
|
|
70667
|
-
*
|
|
70668
|
-
* pad
|
|
70883
|
+
* options {var_name, pad, with_diagram}
|
|
70884
|
+
* var_name the array's name; defaults to
|
|
70885
|
+
* `treedb_schema_<treedb id without its prefix>`
|
|
70886
|
+
* pad column of the `\n\` continuation
|
|
70887
|
+
* with_diagram the graph as a comment above the array
|
|
70888
|
+
* (default true)
|
|
70669
70889
|
***************************************************************/
|
|
70670
70890
|
function schema_to_c(treedb, options) {
|
|
70891
|
+
return json_to_c(schema_to_json(treedb), options);
|
|
70892
|
+
}
|
|
70893
|
+
/***************************************************************
|
|
70894
|
+
* json_to_c(json, options) -> the C source text, from the schema
|
|
70895
|
+
* as its literal holds it. Same options as schema_to_c().
|
|
70896
|
+
***************************************************************/
|
|
70897
|
+
function json_to_c(json, options) {
|
|
70671
70898
|
let opts = options || {};
|
|
70672
|
-
let json = schema_to_json(treedb);
|
|
70673
70899
|
if (!json) return "";
|
|
70674
70900
|
let name = opts.var_name;
|
|
70675
70901
|
if (!name) {
|
|
@@ -70688,7 +70914,24 @@ function schema_to_c(treedb, options) {
|
|
|
70688
70914
|
let body = lines.map((line) => {
|
|
70689
70915
|
return `${line}${line.length < pad ? " ".repeat(pad - line.length) : " "}\\n\\`;
|
|
70690
70916
|
});
|
|
70691
|
-
|
|
70917
|
+
let literal = `static char ${name}[]= "\\\n${body.join("\n")}\n";\n`;
|
|
70918
|
+
if (opts.with_diagram === false) return literal;
|
|
70919
|
+
return c_diagram_comment(schema_to_diagram(json)) + "\n" + literal;
|
|
70920
|
+
}
|
|
70921
|
+
/***************************************************************
|
|
70922
|
+
* The diagram as the comment that heads the file.
|
|
70923
|
+
***************************************************************/
|
|
70924
|
+
function c_diagram_comment(diagram) {
|
|
70925
|
+
let lines = [
|
|
70926
|
+
"/*",
|
|
70927
|
+
" Generated from the literal below by schema_to_diagram() (gobj-ui).",
|
|
70928
|
+
" This file holds this comment and the literal, nothing else: replace",
|
|
70929
|
+
" it whole with an export of the schema editor.",
|
|
70930
|
+
""
|
|
70931
|
+
];
|
|
70932
|
+
for (let line of diagram.split("\n")) lines.push(line ? " " + line : "");
|
|
70933
|
+
lines.push("*/");
|
|
70934
|
+
return lines.join("\n") + "\n";
|
|
70692
70935
|
}
|
|
70693
70936
|
//#endregion
|
|
70694
70937
|
//#region src/schema_import.js
|
|
@@ -71503,13 +71746,15 @@ function write_options(op) {
|
|
|
71503
71746
|
* fkey, the order of a new sibling, the version that publishes
|
|
71504
71747
|
* the change.
|
|
71505
71748
|
*
|
|
71506
|
-
*
|
|
71507
|
-
*
|
|
71508
|
-
*
|
|
71509
|
-
*
|
|
71510
|
-
* the
|
|
71511
|
-
*
|
|
71512
|
-
*
|
|
71749
|
+
* AN EDIT IS A DRAFT. A write here moves no version and reaches no
|
|
71750
|
+
* treedb: the host SAVES it (C_TREEDB's save-schema raises the
|
|
71751
|
+
* topic_version of what changed and the schema_version, once) and
|
|
71752
|
+
* APPLIES it (apply-schema + a restart of the owning yuno). Until
|
|
71753
|
+
* the owner's design of M36 (2026-09-21 review) every write here
|
|
71754
|
+
* raised both versions, so an edit half made was already the
|
|
71755
|
+
* schema of the next start. The topic list marks what this session
|
|
71756
|
+
* wrote and has not saved; the host's EV_REFRESH after a save is
|
|
71757
|
+
* what forgets it.
|
|
71513
71758
|
*
|
|
71514
71759
|
* WHAT ELSE IS HERE, and why each is here and not somewhere else:
|
|
71515
71760
|
*
|
|
@@ -71854,16 +72099,20 @@ function start_measuring(gobj) {
|
|
|
71854
72099
|
for (let treedb of priv.model.treedbs) for (let topic of treedb.topics) priv.baseline[topic.id] = topic.topic_version;
|
|
71855
72100
|
}
|
|
71856
72101
|
/***************************************************************
|
|
71857
|
-
* "This topic was written
|
|
72102
|
+
* "This topic was written in this session and is not saved."
|
|
72103
|
+
*
|
|
72104
|
+
* An edit is a DRAFT: it moves no version and reaches no treedb
|
|
72105
|
+
* until the host saves it (C_TREEDB's save-schema, which raises the
|
|
72106
|
+
* versions of what changed) -- and the save makes the host send
|
|
72107
|
+
* EV_REFRESH, whose reload is what forgets the drafts.
|
|
71858
72108
|
*
|
|
71859
|
-
* ONE predicate, because it is asked in
|
|
71860
|
-
* list draws a chip with it
|
|
71861
|
-
* and
|
|
72109
|
+
* ONE predicate, because it is asked in three places -- the topic
|
|
72110
|
+
* list draws a chip with it, the column view a banner, the export
|
|
72111
|
+
* a warning -- and copies of a rule are how they drift.
|
|
71862
72112
|
***************************************************************/
|
|
71863
|
-
function
|
|
71864
|
-
if (!topic || !written
|
|
71865
|
-
|
|
71866
|
-
return String(baseline[topic.id]) === String(topic.topic_version);
|
|
72113
|
+
function topic_is_draft(written, topic) {
|
|
72114
|
+
if (!topic || !written) return false;
|
|
72115
|
+
return written[topic.id] ? true : false;
|
|
71867
72116
|
}
|
|
71868
72117
|
/***************************************************************
|
|
71869
72118
|
* Say what the whole store would be refused for.
|
|
@@ -72305,7 +72554,7 @@ function render_topics(gobj, $body) {
|
|
|
72305
72554
|
let twice = repeated_names(treedb.topics);
|
|
72306
72555
|
let $rows = [];
|
|
72307
72556
|
for (let topic of treedb.topics) {
|
|
72308
|
-
let pending =
|
|
72557
|
+
let pending = topic_is_draft(priv.written, topic);
|
|
72309
72558
|
let ambiguous = !!twice[topic.name];
|
|
72310
72559
|
let address = ambiguous ? topic.id : topic.name;
|
|
72311
72560
|
let $actions = [];
|
|
@@ -72540,7 +72789,7 @@ function render_columns(gobj, $body) {
|
|
|
72540
72789
|
{ class: "SCHEMA_COLUMNS p-3" },
|
|
72541
72790
|
[]
|
|
72542
72791
|
]);
|
|
72543
|
-
let $banner =
|
|
72792
|
+
let $banner = draft_banner(gobj, topic);
|
|
72544
72793
|
if ($banner) $wrap.appendChild($banner);
|
|
72545
72794
|
let twice = repeated_names(topic.cols);
|
|
72546
72795
|
let $rows = [];
|
|
@@ -72618,66 +72867,37 @@ function render_columns(gobj, $body) {
|
|
|
72618
72867
|
refresh_language($wrap, t);
|
|
72619
72868
|
}
|
|
72620
72869
|
/***************************************************************
|
|
72621
|
-
* "This topic
|
|
72870
|
+
* "This topic has changes that are not saved."
|
|
72622
72871
|
*
|
|
72623
|
-
*
|
|
72624
|
-
*
|
|
72625
|
-
*
|
|
72626
|
-
*
|
|
72627
|
-
*
|
|
72872
|
+
* A draft, not an error: the edit is in __system__ and moves no
|
|
72873
|
+
* version, so the treedb does not see it until the host saves it
|
|
72874
|
+
* (and applies it). The banner says so and offers nothing -- the
|
|
72875
|
+
* save is the host's, one level up, because it publishes the whole
|
|
72876
|
+
* treedb and not one topic.
|
|
72628
72877
|
***************************************************************/
|
|
72629
|
-
function
|
|
72878
|
+
function draft_banner(gobj, topic) {
|
|
72630
72879
|
let priv = gobj.priv;
|
|
72631
|
-
if (!
|
|
72880
|
+
if (!topic_is_draft(priv.written, topic)) return null;
|
|
72632
72881
|
if (is_readonly(gobj)) return null;
|
|
72633
|
-
|
|
72882
|
+
return createElement2([
|
|
72634
72883
|
"div",
|
|
72635
72884
|
{
|
|
72636
|
-
class: "
|
|
72885
|
+
class: "SCHEMA_DRAFT_BANNER notification is-warning is-light p-3 mb-3 is-flex is-align-items-center",
|
|
72637
72886
|
style: "gap:.5rem;"
|
|
72638
72887
|
},
|
|
72639
|
-
[
|
|
72640
|
-
|
|
72641
|
-
|
|
72642
|
-
|
|
72643
|
-
|
|
72644
|
-
|
|
72645
|
-
|
|
72646
|
-
"
|
|
72647
|
-
|
|
72648
|
-
|
|
72649
|
-
|
|
72650
|
-
|
|
72651
|
-
t("version not raised")
|
|
72652
|
-
],
|
|
72653
|
-
[
|
|
72654
|
-
"button",
|
|
72655
|
-
{
|
|
72656
|
-
class: "SCHEMA_VERSION_BUMP button is-warning",
|
|
72657
|
-
type: "button",
|
|
72658
|
-
style: "margin-left:auto;",
|
|
72659
|
-
title: t("raise the version"),
|
|
72660
|
-
"aria-label": t("raise the version"),
|
|
72661
|
-
"data-i18n-title": "raise the version",
|
|
72662
|
-
"data-i18n-aria-label": "raise the version"
|
|
72663
|
-
},
|
|
72664
|
-
[[
|
|
72665
|
-
"span",
|
|
72666
|
-
{ class: "icon" },
|
|
72667
|
-
[["i", { class: "yi-circle-up" }]]
|
|
72668
|
-
], [
|
|
72669
|
-
"span",
|
|
72670
|
-
{ i18n: "raise" },
|
|
72671
|
-
t("raise")
|
|
72672
|
-
]]
|
|
72673
|
-
]
|
|
72674
|
-
]
|
|
72888
|
+
[[
|
|
72889
|
+
"span",
|
|
72890
|
+
{ class: "icon" },
|
|
72891
|
+
[["i", { class: "yi-triangle-exclamation" }]]
|
|
72892
|
+
], [
|
|
72893
|
+
"span",
|
|
72894
|
+
{
|
|
72895
|
+
class: "SCHEMA_DRAFT_TEXT is-size-7",
|
|
72896
|
+
i18n: "unsaved schema changes: save to publish them"
|
|
72897
|
+
},
|
|
72898
|
+
t("unsaved schema changes: save to publish them")
|
|
72899
|
+
]]
|
|
72675
72900
|
]);
|
|
72676
|
-
$banner.querySelector(".SCHEMA_VERSION_BUMP").addEventListener("click", (evt) => {
|
|
72677
|
-
evt.stopPropagation();
|
|
72678
|
-
gobj_send_event(gobj, "EV_BUMP_VERSION", { topic: topic.name }, gobj);
|
|
72679
|
-
});
|
|
72680
|
-
return $banner;
|
|
72681
72901
|
}
|
|
72682
72902
|
/***************************************************************
|
|
72683
72903
|
* One column, as a row. The drag events carry INDEXES in the
|
|
@@ -73546,6 +73766,8 @@ function open_report(gobj, findings, treedb_id) {
|
|
|
73546
73766
|
* as a download, which a published page cannot start anyway.
|
|
73547
73767
|
***************************************************************/
|
|
73548
73768
|
function open_export(gobj, treedb) {
|
|
73769
|
+
let priv = gobj.priv;
|
|
73770
|
+
let drafts = treedb.topics.some((topic) => topic_is_draft(priv.written, topic));
|
|
73549
73771
|
let c_text = schema_to_c(treedb);
|
|
73550
73772
|
let json_text = JSON.stringify(schema_to_json(treedb), null, 4);
|
|
73551
73773
|
let $content = createElement2([
|
|
@@ -73599,13 +73821,29 @@ function open_export(gobj, treedb) {
|
|
|
73599
73821
|
},
|
|
73600
73822
|
t("an edit made here works and lives in no source: this is what to paste back")
|
|
73601
73823
|
],
|
|
73824
|
+
drafts ? [
|
|
73825
|
+
"p",
|
|
73826
|
+
{
|
|
73827
|
+
class: "SCHEMA_EXPORT_DRAFTS notification is-warning is-light p-2 mb-2 is-size-7",
|
|
73828
|
+
i18n: "unsaved changes: save first, so the literal carries the versions that publish it"
|
|
73829
|
+
},
|
|
73830
|
+
t("unsaved changes: save first, so the literal carries the versions that publish it")
|
|
73831
|
+
] : [
|
|
73832
|
+
"span",
|
|
73833
|
+
{},
|
|
73834
|
+
""
|
|
73835
|
+
],
|
|
73602
73836
|
[
|
|
73603
73837
|
"textarea",
|
|
73604
73838
|
{
|
|
73605
73839
|
class: "SCHEMA_EXPORT_TEXT textarea is-small is-family-monospace",
|
|
73606
73840
|
rows: "18",
|
|
73607
73841
|
readonly: "readonly",
|
|
73608
|
-
spellcheck: "false"
|
|
73842
|
+
spellcheck: "false",
|
|
73843
|
+
title: t("schema"),
|
|
73844
|
+
"data-i18n-title": "schema",
|
|
73845
|
+
"aria-label": t("schema"),
|
|
73846
|
+
"data-i18n-aria-label": "schema"
|
|
73609
73847
|
},
|
|
73610
73848
|
c_text
|
|
73611
73849
|
],
|
|
@@ -73619,7 +73857,11 @@ function open_export(gobj, treedb) {
|
|
|
73619
73857
|
"button",
|
|
73620
73858
|
{
|
|
73621
73859
|
class: "SCHEMA_EXPORT_COPY button",
|
|
73622
|
-
type: "button"
|
|
73860
|
+
type: "button",
|
|
73861
|
+
title: t("copy"),
|
|
73862
|
+
"data-i18n-title": "copy",
|
|
73863
|
+
"aria-label": t("copy"),
|
|
73864
|
+
"data-i18n-aria-label": "copy"
|
|
73623
73865
|
},
|
|
73624
73866
|
[[
|
|
73625
73867
|
"span",
|
|
@@ -73954,52 +74196,6 @@ function open_orphans(gobj) {
|
|
|
73954
74196
|
* Writing
|
|
73955
74197
|
***************************/
|
|
73956
74198
|
/***************************************************************
|
|
73957
|
-
* THE TWO VERSIONS A SCHEMA EDIT HAS TO RAISE, and why both.
|
|
73958
|
-
*
|
|
73959
|
-
* `topic_version` is what publishes a change of a topic's
|
|
73960
|
-
* columns: leave it and the persisted `topic_cols.json` masks
|
|
73961
|
-
* the edit, the restart succeeds and nothing moved.
|
|
73962
|
-
*
|
|
73963
|
-
* `schema_version` is what publishes the schema as a whole —
|
|
73964
|
-
* "the stored one wins on ties, and the incoming one has to be
|
|
73965
|
-
* strictly newer to take over" (c_treedb.c). It is safe to
|
|
73966
|
-
* raise: re-projection from C compares `c_schema_version`, the
|
|
73967
|
-
* version of the LITERAL, precisely so that an edit made here
|
|
73968
|
-
* survives every start until a newer literal arrives.
|
|
73969
|
-
*
|
|
73970
|
-
* So every write that changes a schema carries both, and the
|
|
73971
|
-
* operator is never asked to remember either.
|
|
73972
|
-
***************************************************************/
|
|
73973
|
-
function version_writes(gobj, topic) {
|
|
73974
|
-
let treedb = current_treedb(gobj);
|
|
73975
|
-
let writes = [];
|
|
73976
|
-
if (topic) {
|
|
73977
|
-
let version = Number(topic.topic_version);
|
|
73978
|
-
if (!isFinite(version)) version = 0;
|
|
73979
|
-
writes.push({
|
|
73980
|
-
op: "update",
|
|
73981
|
-
topic_name: T_TOPICS,
|
|
73982
|
-
record: {
|
|
73983
|
-
id: topic.id,
|
|
73984
|
-
topic_version: version + 1
|
|
73985
|
-
}
|
|
73986
|
-
});
|
|
73987
|
-
}
|
|
73988
|
-
if (treedb) {
|
|
73989
|
-
let version = Number(treedb.schema_version);
|
|
73990
|
-
if (!isFinite(version)) version = 0;
|
|
73991
|
-
writes.push({
|
|
73992
|
-
op: "update",
|
|
73993
|
-
topic_name: T_TREEDBS,
|
|
73994
|
-
record: {
|
|
73995
|
-
id: treedb.id,
|
|
73996
|
-
schema_version: version + 1
|
|
73997
|
-
}
|
|
73998
|
-
});
|
|
73999
|
-
}
|
|
74000
|
-
return writes;
|
|
74001
|
-
}
|
|
74002
|
-
/***************************************************************
|
|
74003
74199
|
* WHERE THE COLUMNS WERE before this session started moving them.
|
|
74004
74200
|
*
|
|
74005
74201
|
* A drag is a WRITE: the order is a field of the column, so the drop
|
|
@@ -74458,7 +74654,7 @@ function ac_save_column(gobj, event, kw, src) {
|
|
|
74458
74654
|
op: kw.creating ? "create" : "update",
|
|
74459
74655
|
topic_name: T_COLS,
|
|
74460
74656
|
record: column_record(gobj, topic, col, values)
|
|
74461
|
-
}]
|
|
74657
|
+
}]);
|
|
74462
74658
|
}
|
|
74463
74659
|
function ac_delete_column(gobj, event, kw, src) {
|
|
74464
74660
|
let priv = gobj.priv;
|
|
@@ -74509,7 +74705,7 @@ function ac_move_column(gobj, event, kw, src) {
|
|
|
74509
74705
|
};
|
|
74510
74706
|
});
|
|
74511
74707
|
if (writes.length === 0) return 0;
|
|
74512
|
-
return queue_writes(gobj, writes
|
|
74708
|
+
return queue_writes(gobj, writes);
|
|
74513
74709
|
}
|
|
74514
74710
|
/***************************************************************
|
|
74515
74711
|
* Put the columns back where they were before the dragging
|
|
@@ -74529,7 +74725,7 @@ function ac_undo_order(gobj, event, kw, src) {
|
|
|
74529
74725
|
return 0;
|
|
74530
74726
|
}
|
|
74531
74727
|
delete priv.order_undo[topic.id];
|
|
74532
|
-
return queue_writes(gobj, writes
|
|
74728
|
+
return queue_writes(gobj, writes);
|
|
74533
74729
|
}
|
|
74534
74730
|
/***************************************************************
|
|
74535
74731
|
* Editing a topic.
|
|
@@ -74582,7 +74778,7 @@ function ac_save_topic(gobj, event, kw, src) {
|
|
|
74582
74778
|
op: kw.creating ? "create" : "update",
|
|
74583
74779
|
topic_name: T_TOPICS,
|
|
74584
74780
|
record: topic_record(gobj, treedb, topic, values)
|
|
74585
|
-
}]
|
|
74781
|
+
}]);
|
|
74586
74782
|
}
|
|
74587
74783
|
function ac_delete_topic(gobj, event, kw, src) {
|
|
74588
74784
|
let priv = gobj.priv;
|
|
@@ -74599,20 +74795,6 @@ function ac_delete_topic(gobj, event, kw, src) {
|
|
|
74599
74795
|
return 0;
|
|
74600
74796
|
}
|
|
74601
74797
|
/***************************************************************
|
|
74602
|
-
* Raising a version by hand: what the banner offers when
|
|
74603
|
-
* something else wrote the topic and left its version alone.
|
|
74604
|
-
***************************************************************/
|
|
74605
|
-
function ac_bump_version(gobj, event, kw, src) {
|
|
74606
|
-
let priv = gobj.priv;
|
|
74607
|
-
let topic = find_topic(priv.model, priv.treedb_id, kw.topic);
|
|
74608
|
-
if (refuse_if_readonly(gobj, event)) return -1;
|
|
74609
|
-
if (!topic) {
|
|
74610
|
-
log_error(`${gobj_short_name(gobj)}: ${event} names no topic: ${kw.topic}`);
|
|
74611
|
-
return -1;
|
|
74612
|
-
}
|
|
74613
|
-
return queue_writes(gobj, version_writes(gobj, topic));
|
|
74614
|
-
}
|
|
74615
|
-
/***************************************************************
|
|
74616
74798
|
* Deleting a leftover.
|
|
74617
74799
|
***************************************************************/
|
|
74618
74800
|
function ac_delete_orphan(gobj, event, kw, src) {
|
|
@@ -74761,11 +74943,10 @@ function ac_confirmed(gobj, event, kw, src) {
|
|
|
74761
74943
|
topic_name: T_TOPICS,
|
|
74762
74944
|
record: { id: topic.id }
|
|
74763
74945
|
});
|
|
74764
|
-
return queue_writes(gobj, writes
|
|
74946
|
+
return queue_writes(gobj, writes);
|
|
74765
74947
|
}
|
|
74766
74948
|
if (kw.what === "column") {
|
|
74767
|
-
let
|
|
74768
|
-
let col = topic ? find_col(priv.model, priv.treedb_id, kw.topic, kw.col) : null;
|
|
74949
|
+
let col = find_topic(priv.model, priv.treedb_id, kw.topic) ? find_col(priv.model, priv.treedb_id, kw.topic, kw.col) : null;
|
|
74769
74950
|
if (!col) {
|
|
74770
74951
|
log_error(`${gobj_short_name(gobj)}: ${event} names no column: ${kw.col}`);
|
|
74771
74952
|
return -1;
|
|
@@ -74774,7 +74955,7 @@ function ac_confirmed(gobj, event, kw, src) {
|
|
|
74774
74955
|
op: "delete",
|
|
74775
74956
|
topic_name: T_COLS,
|
|
74776
74957
|
record: { id: col.id }
|
|
74777
|
-
}]
|
|
74958
|
+
}]);
|
|
74778
74959
|
}
|
|
74779
74960
|
log_error(`${gobj_short_name(gobj)}: ${event} of nothing this view asked about: ${kw.what}`);
|
|
74780
74961
|
return -1;
|
|
@@ -74963,11 +75144,6 @@ function create_gclass$1(gclass_name) {
|
|
|
74963
75144
|
"EV_UNDO_ORDER",
|
|
74964
75145
|
ac_undo_order,
|
|
74965
75146
|
null
|
|
74966
|
-
],
|
|
74967
|
-
[
|
|
74968
|
-
"EV_BUMP_VERSION",
|
|
74969
|
-
ac_bump_version,
|
|
74970
|
-
null
|
|
74971
75147
|
]
|
|
74972
75148
|
])],
|
|
74973
75149
|
["ST_SAVING", COMMON.slice()]
|
|
@@ -74996,7 +75172,6 @@ function create_gclass$1(gclass_name) {
|
|
|
74996
75172
|
["EV_SAVE_COLUMN", 0],
|
|
74997
75173
|
["EV_MOVE_COLUMN", 0],
|
|
74998
75174
|
["EV_UNDO_ORDER", 0],
|
|
74999
|
-
["EV_BUMP_VERSION", 0],
|
|
75000
75175
|
["EV_VALIDATE", 0],
|
|
75001
75176
|
["EV_EXPORT", 0],
|
|
75002
75177
|
["EV_IMPORT", 0],
|