@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.cjs.js
CHANGED
|
@@ -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
|
|
70167
|
-
*
|
|
70168
|
-
*
|
|
70169
|
-
*
|
|
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
|
|
70672
|
-
*
|
|
70673
|
-
* pad
|
|
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
|
-
|
|
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
|
-
*
|
|
71512
|
-
*
|
|
71513
|
-
*
|
|
71514
|
-
*
|
|
71515
|
-
* the
|
|
71516
|
-
*
|
|
71517
|
-
*
|
|
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
|
|
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
|
|
71865
|
-
* list draws a chip with it
|
|
71866
|
-
* and
|
|
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
|
|
71869
|
-
if (!topic || !written
|
|
71870
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
72875
|
+
* "This topic has changes that are not saved."
|
|
72627
72876
|
*
|
|
72628
|
-
*
|
|
72629
|
-
*
|
|
72630
|
-
*
|
|
72631
|
-
*
|
|
72632
|
-
*
|
|
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
|
|
72883
|
+
function draft_banner(gobj, topic) {
|
|
72635
72884
|
let priv = gobj.priv;
|
|
72636
|
-
if (!
|
|
72885
|
+
if (!topic_is_draft(priv.written, topic)) return null;
|
|
72637
72886
|
if (is_readonly(gobj)) return null;
|
|
72638
|
-
|
|
72887
|
+
return (0, _yuneta_gobj_js.createElement2)([
|
|
72639
72888
|
"div",
|
|
72640
72889
|
{
|
|
72641
|
-
class: "
|
|
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
|
-
|
|
72647
|
-
|
|
72648
|
-
|
|
72649
|
-
|
|
72650
|
-
|
|
72651
|
-
"
|
|
72652
|
-
|
|
72653
|
-
|
|
72654
|
-
|
|
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
|
-
}]
|
|
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
|
|
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
|
|
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
|
-
}]
|
|
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
|
|
74951
|
+
return queue_writes(gobj, writes);
|
|
74770
74952
|
}
|
|
74771
74953
|
if (kw.what === "column") {
|
|
74772
|
-
let
|
|
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
|
-
}]
|
|
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],
|