strom-research 1.22.2 → 1.23.0-beta.2
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/CHANGELOG.cs.md +18 -0
- package/CHANGELOG.de.md +22 -0
- package/CHANGELOG.md +18 -0
- package/assets/lang/cs.json +13 -0
- package/assets/lang/de.json +13 -0
- package/dist/brief/brief.js +1 -1
- package/dist/cli/seal.js +4 -2
- package/dist/cli/ui.js +11 -0
- package/dist/commands/checks.js +34 -11
- package/dist/commands/people.js +35 -6
- package/dist/commands/sync.js +28 -26
- package/dist/core/actions.js +61 -10
- package/dist/core/changelog.js +5 -0
- package/dist/core/import.js +10 -4
- package/dist/core/integrity.js +30 -5
- package/dist/core/live.js +1 -1
- package/dist/core/overview.js +3 -2
- package/dist/core/people.js +14 -1
- package/dist/core/phrases.js +2 -0
- package/dist/core/retractions.js +6 -4
- package/dist/core/stromapp.js +21 -0
- package/dist/core/sync.js +110 -8
- package/dist/core/tree.js +1 -1
- package/dist/core/validate.js +4 -0
- package/dist/gedcom/export.js +14 -2
- package/dist/gedcom/validate.js +13 -5
- package/package.json +4 -1
package/CHANGELOG.cs.md
CHANGED
|
@@ -5,6 +5,24 @@
|
|
|
5
5
|
Změny každé verze ve zkratce. Úplné poznámky a instalační řádky jsou na
|
|
6
6
|
[GitHub Releases](https://github.com/ACiDekCZ/strom-research/releases) (anglicky).
|
|
7
7
|
|
|
8
|
+
## 1.23.0
|
|
9
|
+
|
|
10
|
+
*Připravuje se: bety do 1.23.0-beta.2. Oddíl bude úplný s vydáním.*
|
|
11
|
+
|
|
12
|
+
- **Osoba ukázaná zvoleným jménem.** Žena narozená pod jedním jménem, provdaná pod druhým a po rozvodu zase u prvního
|
|
13
|
+
se dá ukazovat tímto jménem znovu, aniž by se přidávalo podruhé: `strom name primary <osoba> "<jméno>"` (a
|
|
14
|
+
`--clear`), nebo `strom name add --primary`. Volba patří jen uživateli, nikdy se nečte ze záznamu; opravy, citace
|
|
15
|
+
i synchronizace s aplikací Strom se dál řídí rodným jménem. Soubor pro Strom zvolené jméno označí, standardní soubor
|
|
16
|
+
GEDCOM ho dá na první místo.
|
|
17
|
+
- **Soubor pro aplikaci Strom bez varování.** Export pro aplikaci Strom varoval před nestandardní značkou u každé
|
|
18
|
+
osoby se zvoleným jménem a u několika značek zdrojů; kontrola teď zná všechny vlastní značky Stromu.
|
|
19
|
+
- **Commit gitem navrch výzkumu zapečetěného dvěma klíči (Windows).** `strom check` radil zároveň `strom repair` i
|
|
20
|
+
„nikdy `strom repair`“ a `strom repair` vrátil zpět práci zapsanou klíčem terminálu. `strom repair` teď vrací jen
|
|
21
|
+
to, co se udělalo mimo strom, práce obou klíčů zůstává; potom klíče sjednotí `strom seal adopt` ve vlastním
|
|
22
|
+
terminálu – `strom check` říká oba kroky v tomto pořadí. Spuštěný z aplikace agenta balené pro Windows nechá `strom
|
|
23
|
+
repair` takový výzkum beze změny a odkáže na terminál. Po commitu gitem, který nezměnil žádný záznam, `strom repair`
|
|
24
|
+
řekne, že výzkum znovu zapečetil, už ne „nothing to repair“.
|
|
25
|
+
|
|
8
26
|
## 1.22.2
|
|
9
27
|
|
|
10
28
|
*30. 10. 2026 · patří k aplikaci Strom 3.17.1.*
|
package/CHANGELOG.de.md
CHANGED
|
@@ -5,6 +5,28 @@
|
|
|
5
5
|
Die Änderungen jeder Version in Kürze. Die vollständigen Hinweise und die Installationszeilen stehen bei
|
|
6
6
|
[GitHub Releases](https://github.com/ACiDekCZ/strom-research/releases) (auf Englisch).
|
|
7
7
|
|
|
8
|
+
## 1.23.0
|
|
9
|
+
|
|
10
|
+
*In Vorbereitung: die Betas bis 1.23.0-beta.2. Der Abschnitt ist mit dem Release vollständig.*
|
|
11
|
+
|
|
12
|
+
- **Eine Person unter dem gewählten Namen.** Eine Frau, unter einem Namen geboren, unter einem anderen verheiratet und
|
|
13
|
+
nach einer Scheidung wieder beim ersten, lässt sich wieder unter diesem Namen zeigen, ohne dass er ein zweites Mal
|
|
14
|
+
hinzukommt: `strom name primary <Person> "<Name>"` (und `--clear`) oder `strom name add --primary`. Die Wahl trifft
|
|
15
|
+
allein der Nutzer, nie wird sie aus einem Eintrag gelesen; Korrekturen, Zitate und der Abgleich mit der Strom-App
|
|
16
|
+
richten sich weiter nach dem Geburtsnamen. Die Strom-Datei markiert den gewählten Namen, die Standard-GEDCOM-Datei
|
|
17
|
+
setzt ihn an die erste Stelle.
|
|
18
|
+
- **Die Datei für die Strom-App ohne Warnungen.** Der Export für die Strom-App warnte bei jeder Person mit gewähltem
|
|
19
|
+
Namen und bei mehreren Kennzeichen von Quellen vor einem nicht standardisierten Tag; die Prüfung kennt jetzt alle
|
|
20
|
+
eigenen Tags von Strom.
|
|
21
|
+
- **Ein Commit mit git über einer mit zwei Schlüsseln versiegelten Forschung (Windows).** `strom check` riet zugleich
|
|
22
|
+
zu `strom repair` und zu „nie `strom repair`“, und `strom repair` machte die mit dem Schlüssel des Terminals
|
|
23
|
+
geschriebene Arbeit rückgängig. `strom repair` macht jetzt nur rückgängig, was außerhalb von strom geschah, die
|
|
24
|
+
Arbeit beider Schlüssel bleibt; danach vereint `strom seal adopt` in einem eigenen Terminal die Schlüssel – `strom
|
|
25
|
+
check` nennt beide Schritte in dieser Reihenfolge. Aus der für Windows paketierten Desktop-App eines Agenten
|
|
26
|
+
gestartet, lässt `strom repair` eine solche Forschung unverändert und verweist auf das Terminal. Nach einem Commit
|
|
27
|
+
mit git, der keinen Eintrag änderte, sagt `strom repair`, dass es die Forschung neu versiegelt hat, nicht mehr
|
|
28
|
+
„nothing to repair“.
|
|
29
|
+
|
|
8
30
|
## 1.22.2
|
|
9
31
|
|
|
10
32
|
*30.10.2026 · gehört zur Strom-App 3.17.1.*
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,24 @@ English · [Čeština](CHANGELOG.cs.md) · [Deutsch](CHANGELOG.de.md)
|
|
|
5
5
|
The changes of each version in short. The full notes and the install lines are on
|
|
6
6
|
[GitHub Releases](https://github.com/ACiDekCZ/strom-research/releases).
|
|
7
7
|
|
|
8
|
+
## 1.23.0
|
|
9
|
+
|
|
10
|
+
*In preparation: the betas up to 1.23.0-beta.2. The section is complete with the release.*
|
|
11
|
+
|
|
12
|
+
- **A person shown by the name chosen.** A woman born under one name, married under another and back at the first
|
|
13
|
+
after a divorce can be shown by that name again without it being added twice: `strom name primary <person>
|
|
14
|
+
"<name>"` (and `--clear`), or `strom name add --primary`. The choice is the user's alone, never read from a record;
|
|
15
|
+
corrections, citations and the sync with the Strom app still go by the birth name. The Strom file marks the chosen
|
|
16
|
+
name, the standard GEDCOM file gives it first.
|
|
17
|
+
- **The file for the Strom app without warnings.** Exporting for the Strom app warned of a non-standard tag on each
|
|
18
|
+
person with a chosen name and on several marks of sources; the check now knows every tag of Strom's own.
|
|
19
|
+
- **A commit made with git on top of a research sealed with two keys (Windows).** `strom check` advised `strom repair`
|
|
20
|
+
and never `strom repair` at once, and `strom repair` undid the work written with the terminal's key. `strom repair`
|
|
21
|
+
now undoes only what was done outside strom, the work of both keys stays; then `strom seal adopt` in a terminal
|
|
22
|
+
unites the keys — `strom check` says the two steps in this order. Started by an agent's desktop app packaged for
|
|
23
|
+
Windows, `strom repair` leaves such a research as it is and points to the terminal. After a commit made with git
|
|
24
|
+
that changed no record, `strom repair` says it sealed the research again, no longer "nothing to repair".
|
|
25
|
+
|
|
8
26
|
## 1.22.2
|
|
9
27
|
|
|
10
28
|
*2026-10-30 · goes with the Strom app 3.17.1.*
|
package/assets/lang/cs.json
CHANGED
|
@@ -1065,6 +1065,7 @@
|
|
|
1065
1065
|
"ui.show.name.married": "po sňatku",
|
|
1066
1066
|
"ui.show.name.religious": "řeholní",
|
|
1067
1067
|
"ui.show.name.alias": "jinak též",
|
|
1068
|
+
"ui.show.name.current": "hlavní",
|
|
1068
1069
|
"ui.show.role.godparent": "kmotr",
|
|
1069
1070
|
"ui.show.role.witness": "svědek",
|
|
1070
1071
|
"ui.show.role.officiant": "duchovní",
|
|
@@ -1353,7 +1354,10 @@
|
|
|
1353
1354
|
"ui.sync.do.name.gone": "odebere se (měly ho jen úpravy v aplikaci nebo jiné rodokmeny)",
|
|
1354
1355
|
"ui.sync.do.name.kept": "zůstává: dokládá ho záznam",
|
|
1355
1356
|
"ui.sync.do.name.user": "platí úprava z aplikace: osoba se ukazuje pod novým jménem, jméno ze záznamu zůstane vedle něj",
|
|
1357
|
+
"ui.sync.do.name.shown": "osoba se ukazuje tímto jménem",
|
|
1358
|
+
"ui.sync.do.name.shown.conflict": "rozpor k rozhodnutí (výzkum ho změnil také)",
|
|
1356
1359
|
"ui.sync.name.title": "{n}. {who}: {title} — {old} → {new}",
|
|
1360
|
+
"ui.sync.name.shown": "{n}. {who}: hlavní jméno — {old} → {new}",
|
|
1357
1361
|
"ui.sync.do.title": "doplní se ke jménu s odkazem na strom z aplikace",
|
|
1358
1362
|
"ui.sync.do.title.user": "platí úprava z aplikace (verze výzkumu zůstane v historii)",
|
|
1359
1363
|
"ui.sync.sex.changed": "{n}. {who}: pohlaví {text}",
|
|
@@ -2247,6 +2251,8 @@
|
|
|
2247
2251
|
"log.note.edit": "Opravená poznámka: {name}",
|
|
2248
2252
|
"log.name.retract": "Odvolané jméno: {name} – {who}",
|
|
2249
2253
|
"log.name.restore": "Vrácené jméno: {name} – {who}",
|
|
2254
|
+
"log.name.primary": "Za hlavní zvoleno jméno: {name} – {who}",
|
|
2255
|
+
"log.name.primary.clear": "Za hlavní zase rodné jméno: {name} – {who}",
|
|
2250
2256
|
"log.note.retract": "Odvolaná poznámka: {name}",
|
|
2251
2257
|
"log.note.restore": "Vrácená poznámka: {name}",
|
|
2252
2258
|
"log.event.status": "Změněná jistota údaje: {who} – {fact}",
|
|
@@ -2512,6 +2518,8 @@
|
|
|
2512
2518
|
"ui.conflict.sex": "pohlaví",
|
|
2513
2519
|
"ui.conflict.sex.U": "neznámé",
|
|
2514
2520
|
"ui.conflict.name": "jméno",
|
|
2521
|
+
"ui.conflict.primary": "hlavní jméno",
|
|
2522
|
+
"ui.conflict.primary.none": "nezvoleno",
|
|
2515
2523
|
"ui.conflict.titleBefore": "titul před jménem",
|
|
2516
2524
|
"ui.conflict.titleAfter": "titul za jménem",
|
|
2517
2525
|
"ui.conflict.parents": "rodiče",
|
|
@@ -3101,6 +3109,11 @@
|
|
|
3101
3109
|
"ui.check.otherkey": "zapečetěno dvěma klíči stromu na tomto počítači (strom z aplikace a z terminálu): zápisy obou jsou stromu",
|
|
3102
3110
|
"ui.check.otherkey.hint": "sjednotit ve vlastním terminálu: strom seal adopt – nikdy strom repair",
|
|
3103
3111
|
"ui.seal.problems": "nepřevzato: záznamy nesedí se svou pečetí – co, řekne strom check",
|
|
3112
|
+
"ui.check.otherkey.afterrepair.hint": "po strom repair sjednotit ve vlastním terminálu: strom seal adopt",
|
|
3113
|
+
"ui.repair.resealed": "znovu zapečetěno – commit udělaný mimo strom nezměnil žádný záznam",
|
|
3114
|
+
"ui.repair.unite": "dál: sjednotit klíče stromu na tomto počítači ve vlastním terminálu: strom seal adopt",
|
|
3115
|
+
"ui.error.seal.repair-in-terminal": "výzkum „{name}“ obsahuje práci zapečetěnou klíčem stromu z terminálu, který strom spuštěný z aplikace nevidí: nic se neopravilo",
|
|
3116
|
+
"ui.error.seal.repair-in-terminal.hint": "ve vlastním terminálu: strom repair, pak strom seal adopt – ten vidí oba klíče a vrátí jen to, co se udělalo mimo strom",
|
|
3104
3117
|
"ui.menu.seal": "Zapečetěno na jiném počítači: zapisovat tu jde až po převzetí (strom seal adopt).",
|
|
3105
3118
|
"ui.seal.bridge": "výzkum „{name}“ je zapečetěný na jiném počítači: nic se nezapsalo – po převzetí na tomto počítači (terminál: strom seal adopt, nebo nabídka strom) poslat znovu",
|
|
3106
3119
|
"ui.seal.bridge.twokeys": "výzkum „{name}“ je na tomto počítači zapečetěný dvěma klíči stromu (strom z aplikace a z terminálu): nic se nezapsalo – po sjednocení (terminál: strom seal adopt, nebo nabídka strom) poslat znovu",
|
package/assets/lang/de.json
CHANGED
|
@@ -1065,6 +1065,7 @@
|
|
|
1065
1065
|
"ui.show.name.married": "nach der Heirat",
|
|
1066
1066
|
"ui.show.name.religious": "Ordensname",
|
|
1067
1067
|
"ui.show.name.alias": "auch genannt",
|
|
1068
|
+
"ui.show.name.current": "Hauptname",
|
|
1068
1069
|
"ui.show.role.godparent": "Pate",
|
|
1069
1070
|
"ui.show.role.witness": "Zeuge",
|
|
1070
1071
|
"ui.show.role.officiant": "Geistlicher",
|
|
@@ -1353,7 +1354,10 @@
|
|
|
1353
1354
|
"ui.sync.do.name.gone": "wird entfernt (nur Änderungen in der App oder andere Stammbäume gaben ihn)",
|
|
1354
1355
|
"ui.sync.do.name.kept": "bleibt: ein Eintrag belegt ihn",
|
|
1355
1356
|
"ui.sync.do.name.user": "Die eigene Änderung gilt: die Person erscheint unter dem neuen Namen, der Name aus dem Eintrag bleibt daneben",
|
|
1357
|
+
"ui.sync.do.name.shown": "die Person wird so angezeigt",
|
|
1358
|
+
"ui.sync.do.name.shown.conflict": "ein Widerspruch zum Entscheiden (auch die Forschung hat es geändert)",
|
|
1356
1359
|
"ui.sync.name.title": "{n}. {who}: {title} — {old} → {new}",
|
|
1360
|
+
"ui.sync.name.shown": "{n}. {who}: Hauptname — {old} → {new}",
|
|
1357
1361
|
"ui.sync.do.title": "wird dem Namen hinzugefügt, mit Verweis auf den eigenen Stammbaum",
|
|
1358
1362
|
"ui.sync.do.title.user": "Die eigene Änderung gilt (die der Forschung bleibt in ihrer Geschichte)",
|
|
1359
1363
|
"ui.sync.sex.changed": "{n}. {who}: Geschlecht {text}",
|
|
@@ -2247,6 +2251,8 @@
|
|
|
2247
2251
|
"log.note.edit": "Notiz berichtigt: {name}",
|
|
2248
2252
|
"log.name.retract": "Name zurückgezogen: {name} – {who}",
|
|
2249
2253
|
"log.name.restore": "Name wiederhergestellt: {name} – {who}",
|
|
2254
|
+
"log.name.primary": "Hauptname: {name} – {who}",
|
|
2255
|
+
"log.name.primary.clear": "Hauptname wieder der Geburtsname: {name} – {who}",
|
|
2250
2256
|
"log.note.retract": "Notiz zurückgezogen: {name}",
|
|
2251
2257
|
"log.note.restore": "Notiz wiederhergestellt: {name}",
|
|
2252
2258
|
"log.event.status": "Sicherheit einer Angabe geändert: {who} – {fact}",
|
|
@@ -2512,6 +2518,8 @@
|
|
|
2512
2518
|
"ui.conflict.sex": "Geschlecht",
|
|
2513
2519
|
"ui.conflict.sex.U": "unbekannt",
|
|
2514
2520
|
"ui.conflict.name": "Name",
|
|
2521
|
+
"ui.conflict.primary": "Hauptname",
|
|
2522
|
+
"ui.conflict.primary.none": "nicht gewählt",
|
|
2515
2523
|
"ui.conflict.titleBefore": "Titel vor dem Namen",
|
|
2516
2524
|
"ui.conflict.titleAfter": "Titel nach dem Namen",
|
|
2517
2525
|
"ui.conflict.parents": "Eltern",
|
|
@@ -3101,6 +3109,11 @@
|
|
|
3101
3109
|
"ui.check.otherkey": "mit zwei Schlüsseln von strom auf diesem Computer versiegelt (strom aus einer App und aus einem Terminal): was beide schrieben, ist von strom",
|
|
3102
3110
|
"ui.check.otherkey.hint": "vereinen in einem eigenen Terminal: strom seal adopt – nie strom repair",
|
|
3103
3111
|
"ui.seal.problems": "nicht übernommen: was im Stammbaum steht, ist nicht so versiegelt, wie strom es schrieb – was, sagt strom check",
|
|
3112
|
+
"ui.check.otherkey.afterrepair.hint": "nach strom repair vereinen in einem eigenen Terminal: strom seal adopt",
|
|
3113
|
+
"ui.repair.resealed": "neu versiegelt – der Commit außerhalb von strom änderte keinen Eintrag",
|
|
3114
|
+
"ui.repair.unite": "weiter: die Schlüssel von strom auf diesem Computer vereinen in einem eigenen Terminal: strom seal adopt",
|
|
3115
|
+
"ui.error.seal.repair-in-terminal": "die Forschung „{name}“ enthält Arbeit, versiegelt mit dem Schlüssel des strom aus dem Terminal, den ein aus einer App gestartetes strom nicht sieht: nichts wurde repariert",
|
|
3116
|
+
"ui.error.seal.repair-in-terminal.hint": "in einem eigenen Terminal: strom repair, dann strom seal adopt – es sieht beide Schlüssel und macht nur rückgängig, was außerhalb von strom geschah",
|
|
3104
3117
|
"ui.menu.seal": "Auf einem anderen Computer versiegelt: schreiben hier erst nach der Übernahme (strom seal adopt).",
|
|
3105
3118
|
"ui.seal.bridge": "die Forschung „{name}“ ist auf einem anderen Computer versiegelt: nichts wurde geschrieben – nach der Übernahme auf diesem Computer (Terminal: strom seal adopt, oder das Menü von strom) erneut senden",
|
|
3106
3119
|
"ui.seal.bridge.twokeys": "die Forschung „{name}“ ist auf diesem Computer mit zwei Schlüsseln von strom versiegelt (strom einer App und eines Terminals): nichts wurde geschrieben – nach dem Vereinen (Terminal: strom seal adopt, oder das Menü von strom) erneut senden",
|
package/dist/brief/brief.js
CHANGED
|
@@ -116,7 +116,7 @@ function personBlock(tree, p, depth, short = false) {
|
|
|
116
116
|
const out = [` ${label(p)} ${p.sex}`];
|
|
117
117
|
// other names, and where any name comes from
|
|
118
118
|
if (p.names.length > 1 || p.names.some((n) => n.citations?.length))
|
|
119
|
-
out.push(` names: ${p.names.map((n) => `${formatName(n)}${n.kind ? ` (${n.kind})` : ""}${n.citations?.length ? ` ← ${citesOf(n.citations)}` : ""}`).join("; ")}`);
|
|
119
|
+
out.push(` names: ${p.names.map((n) => `${formatName(n)}${n.kind ? ` (${n.kind})` : ""}${n.primary ? " (shown by it)" : ""}${n.citations?.length ? ` ← ${citesOf(n.citations)}` : ""}`).join("; ")}`);
|
|
120
120
|
for (const e of p.events.filter((x) => !x.retracted))
|
|
121
121
|
out.push(eventLine(e));
|
|
122
122
|
const parents = parentsOf(tree, p.id);
|
package/dist/cli/seal.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Taking a family tree over on this computer (its seal): strom seal adopt, the menu and the setup go the same way —
|
|
2
2
|
// every record checked against its seal, the person's yes, then this computer's key. Never by itself, never by an agent.
|
|
3
3
|
import { ui } from "./ui.js";
|
|
4
|
-
import { verifyFull } from "../core/integrity.js";
|
|
4
|
+
import { oneWay, verifyFull } from "../core/integrity.js";
|
|
5
|
+
import { settingsHidden } from "../core/oldsettings.js";
|
|
5
6
|
import { createKey, fingerprint, otherKeys } from "../core/seal.js";
|
|
6
7
|
import { sealedHere } from "../core/tree.js";
|
|
7
8
|
/**
|
|
@@ -79,7 +80,8 @@ async function uniteSeal(ctx, tree, lang) {
|
|
|
79
80
|
const others = keysToUnite(tree);
|
|
80
81
|
if (!others.length)
|
|
81
82
|
return { adopted: false, why: "already" };
|
|
82
|
-
|
|
83
|
+
// a change outside strom too: strom repair first (it keeps the work of both keys), then this again
|
|
84
|
+
const problems = oneWay(verifyFull(tree, { trust: others }).findings.filter((f) => f.level === "error"), () => settingsHidden(tree.env));
|
|
83
85
|
if (problems.length)
|
|
84
86
|
return { adopted: false, why: "problems", problems };
|
|
85
87
|
if (!(await ctx.confirm(ui(lang, "ui.seal.unite.ask", { name: tree.config.name }), true)))
|
package/dist/cli/ui.js
CHANGED
|
@@ -889,6 +889,7 @@ export const UI = {
|
|
|
889
889
|
"ui.show.name.married": "married",
|
|
890
890
|
"ui.show.name.religious": "religious",
|
|
891
891
|
"ui.show.name.alias": "alias",
|
|
892
|
+
"ui.show.name.current": "current",
|
|
892
893
|
"ui.show.role.godparent": "godparent",
|
|
893
894
|
"ui.show.role.witness": "witness",
|
|
894
895
|
"ui.show.role.officiant": "officiant",
|
|
@@ -1318,7 +1319,10 @@ export const UI = {
|
|
|
1318
1319
|
"ui.sync.do.name.gone": "taken off (only edits in the app or other family trees gave it)",
|
|
1319
1320
|
"ui.sync.do.name.kept": "kept: a record gives it",
|
|
1320
1321
|
"ui.sync.do.name.user": "the edit wins: shown by the new name, the record's kept beside it",
|
|
1322
|
+
"ui.sync.do.name.shown": "the person shown by it",
|
|
1323
|
+
"ui.sync.do.name.shown.conflict": "a conflict to decide (the research changed it too)",
|
|
1321
1324
|
"ui.sync.name.title": "{n}. {who}: {title} — {old} → {new}",
|
|
1325
|
+
"ui.sync.name.shown": "{n}. {who}: name shown — {old} → {new}",
|
|
1322
1326
|
"ui.sync.do.title": "added to the name, citing the tree sent",
|
|
1323
1327
|
"ui.sync.do.title.user": "the edit wins (the research's in its history)",
|
|
1324
1328
|
"ui.sync.sex.changed": "{n}. {who}: sex {text}",
|
|
@@ -2439,6 +2443,8 @@ export const UI = {
|
|
|
2439
2443
|
"ui.conflict.sex": "sex",
|
|
2440
2444
|
"ui.conflict.sex.U": "not known",
|
|
2441
2445
|
"ui.conflict.name": "name",
|
|
2446
|
+
"ui.conflict.primary": "name shown",
|
|
2447
|
+
"ui.conflict.primary.none": "none chosen",
|
|
2442
2448
|
"ui.conflict.titleBefore": "title before the name",
|
|
2443
2449
|
"ui.conflict.titleAfter": "title after the name",
|
|
2444
2450
|
"ui.conflict.parents": "parents",
|
|
@@ -2795,6 +2801,11 @@ export const UI = {
|
|
|
2795
2801
|
"ui.check.otherkey": "sealed with two keys of strom on this computer (strom of an app and of a terminal): the writes of both are strom's own",
|
|
2796
2802
|
"ui.check.otherkey.hint": "united in a terminal of its own: strom seal adopt — never strom repair",
|
|
2797
2803
|
"ui.seal.problems": "not taken over: records do not match their seals — what, strom check says",
|
|
2804
|
+
"ui.check.otherkey.afterrepair.hint": "after strom repair, united in a terminal of its own: strom seal adopt",
|
|
2805
|
+
"ui.repair.resealed": "sealed again — the commit made outside strom changed no record",
|
|
2806
|
+
"ui.repair.unite": "next: the keys of strom on this computer united in a terminal of its own: strom seal adopt",
|
|
2807
|
+
"ui.error.seal.repair-in-terminal": "\"{name}\" holds work sealed with the key of the terminal's strom, which a strom started by an app cannot see: nothing repaired",
|
|
2808
|
+
"ui.error.seal.repair-in-terminal.hint": "in a terminal of its own: strom repair, then strom seal adopt — it sees both keys and undoes only what was done outside strom",
|
|
2798
2809
|
"ui.menu.seal": "Sealed on another computer: writing here only once taken over (strom seal adopt).",
|
|
2799
2810
|
"ui.seal.bridge": "\"{name}\" was sealed on another computer: nothing was written — once it is taken over on this one (a terminal: strom seal adopt, or strom's menu), send again",
|
|
2800
2811
|
"ui.seal.bridge.twokeys": "\"{name}\" was sealed with two keys of strom on this computer (strom of an app and of a terminal): nothing was written — once they are united (a terminal: strom seal adopt, or strom's menu), send again",
|
package/dist/commands/checks.js
CHANGED
|
@@ -8,8 +8,9 @@ import { counted, lines, table } from "../cli/format.js";
|
|
|
8
8
|
import { check, SHARED_MISSING, sharedMissing } from "../core/check.js";
|
|
9
9
|
import { guard } from "../core/guard.js";
|
|
10
10
|
import { unprovenPeople } from "../core/review.js";
|
|
11
|
-
import { NOT_DOWNLOADED, OTHER_KEY, SEALED_PATHS, notDownloaded, notDownloadedError, otherKeyError, verifyFast, verifyFull } from "../core/integrity.js";
|
|
12
|
-
import { verifyCommitSeal } from "../core/seal.js";
|
|
11
|
+
import { AFTER_REPAIR_HINT, NOT_DOWNLOADED, OTHER_KEY, SEALED_PATHS, notDownloaded, notDownloadedError, otherKeyError, verifyFast, verifyFull } from "../core/integrity.js";
|
|
12
|
+
import { otherKeys, readCommitSeal, verifyCommitSeal } from "../core/seal.js";
|
|
13
|
+
import { settingsHidden } from "../core/oldsettings.js";
|
|
13
14
|
import { adoptSeal, keysToUnite } from "../cli/seal.js";
|
|
14
15
|
import { NeedsConsentError, StromError } from "../core/errors.js";
|
|
15
16
|
import * as git from "../core/git.js";
|
|
@@ -48,7 +49,7 @@ function hintSaid(f, lang) {
|
|
|
48
49
|
if (lang && f.code === "foreign-seal")
|
|
49
50
|
return ui(lang, "ui.check.foreignseal.hint");
|
|
50
51
|
if (lang && f.code === OTHER_KEY)
|
|
51
|
-
return ui(lang, "ui.check.otherkey.hint");
|
|
52
|
+
return ui(lang, f.hint === AFTER_REPAIR_HINT ? "ui.check.otherkey.afterrepair.hint" : "ui.check.otherkey.hint");
|
|
52
53
|
if (lang && f.code === NOT_DOWNLOADED)
|
|
53
54
|
return ui(lang, "ui.check.notdownloaded.hint");
|
|
54
55
|
if (lang && f.code === SHARED_MISSING)
|
|
@@ -156,10 +157,22 @@ function repair(tree) {
|
|
|
156
157
|
const key = tree.requireKey();
|
|
157
158
|
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
158
159
|
const head = git.headInfo(tree.root);
|
|
159
|
-
|
|
160
|
+
// the newest commit any key of this computer sealed (strom of an app packaged for Windows and of a terminal each had
|
|
161
|
+
// their own): only what was done outside strom undone, the other key's work kept — strom seal adopt unites them after
|
|
162
|
+
const keys = [key, ...otherKeys(tree.env, tree.config.id, key).map((o) => o.key)];
|
|
163
|
+
const above = [];
|
|
164
|
+
const sealed = git.findCommit(tree.root, (t, m) => keys.some((k) => verifyCommitSeal(k, t, m)) || (above.push(m), false));
|
|
160
165
|
if (!sealed)
|
|
161
166
|
throw new StromError("no sealed commit found — nothing to restore from", { hint: "ask the user; the history may have been rewritten" });
|
|
162
|
-
|
|
167
|
+
// started by an app packaged for Windows: a commit above it with strom's seal may be the terminal's key out of its
|
|
168
|
+
// sight — never undone from here, the terminal's strom sees both keys
|
|
169
|
+
if (above.some((m) => readCommitSeal(m) !== undefined) && settingsHidden(tree.env))
|
|
170
|
+
throw new StromError(`"${tree.config.name}" holds work sealed with the key of the terminal's strom, which this strom (started by an app packaged for Windows) does not see — nothing repaired`, {
|
|
171
|
+
hint: "the user runs in their terminal: strom repair, then strom seal adopt (it sees both keys and undoes only what was done outside strom)",
|
|
172
|
+
code: "seal.repair-in-terminal",
|
|
173
|
+
params: { name: tree.config.name },
|
|
174
|
+
});
|
|
175
|
+
const headSealed = head !== undefined && head.hash === sealed;
|
|
163
176
|
// Paths to compare: whatever differs between the sealed commit and the working tree.
|
|
164
177
|
const candidates = new Set();
|
|
165
178
|
for (const c of git.changes(tree.root, SEALED_PATHS))
|
|
@@ -193,7 +206,7 @@ function repair(tree) {
|
|
|
193
206
|
// commit stays unsealed and every write refused, with nothing left to repair
|
|
194
207
|
if (!headSealed)
|
|
195
208
|
tree.commit(`Repair: restore the sealed state of ${sealed.slice(0, 8)}\n\nCommits made outside strom were undone for data/, strom.json, history/ and quality/.`, SEALED_PATHS, true);
|
|
196
|
-
return { restored, quarantined, from: sealed };
|
|
209
|
+
return { restored, quarantined, from: sealed, resealed: !headSealed };
|
|
197
210
|
}
|
|
198
211
|
register({
|
|
199
212
|
path: ["repair"],
|
|
@@ -201,15 +214,25 @@ register({
|
|
|
201
214
|
group: "history",
|
|
202
215
|
tree: true,
|
|
203
216
|
description: "Edited records are restored, records created outside strom are removed; copies of everything\n" +
|
|
204
|
-
"that was replaced are kept in .strom/quarantine/. Commits made with git directly are undone for data
|
|
217
|
+
"that was replaced are kept in .strom/quarantine/. Commits made with git directly are undone for data/.\n" +
|
|
218
|
+
"A tree sealed with two keys of this computer (an app packaged for Windows and a terminal): the work of both kept,\n" +
|
|
219
|
+
"the keys united after it with strom seal adopt.",
|
|
205
220
|
run(ctx) {
|
|
206
221
|
const tree = ctx.tree();
|
|
222
|
+
// what a person meets here in the research's language, an agent in English
|
|
223
|
+
const lang = isAgent(ctx.env) ? "en" : tree.lang;
|
|
207
224
|
const r = tree.withTreeLock(() => repair(tree));
|
|
208
|
-
|
|
225
|
+
// the work of another key of this computer kept: united next, by the person in their terminal (no problem left of
|
|
226
|
+
// what strom repair undoes)
|
|
227
|
+
const after = verifyFast(tree).findings.filter((f) => f.level === "error" && f.code !== OTHER_KEY);
|
|
228
|
+
const unite = !!tree.key && otherKeys(tree.env, tree.config.id, tree.key).length > 0 && keysToUnite(tree).length > 0;
|
|
229
|
+
const next = unite ? ui(lang, "ui.repair.unite") : undefined;
|
|
209
230
|
const text = r.restored.length + r.quarantined.length === 0
|
|
210
|
-
?
|
|
211
|
-
|
|
212
|
-
|
|
231
|
+
? r.resealed
|
|
232
|
+
? lines(ui(lang, "ui.repair.resealed"), next)
|
|
233
|
+
: lines("nothing to repair — every record matches its seal", next)
|
|
234
|
+
: lines(...r.restored.map((f) => `restored ${f}`), ...r.quarantined.map((f) => `removed ${f}`), `sealed state: ${r.from.slice(0, 8)} · copies of replaced files: .strom/quarantine/`, after.length ? `still ${counted(after.length, "problem")} → strom verify` : next);
|
|
235
|
+
return { text, data: { ...r, ok: after.length === 0, ...(unite ? { unite: "strom seal adopt" } : {}) }, exitCode: after.length ? 1 : 0 };
|
|
213
236
|
},
|
|
214
237
|
}, {
|
|
215
238
|
path: ["seal", "adopt"],
|
package/dist/commands/people.js
CHANGED
|
@@ -3,13 +3,13 @@ import { register } from "../cli/registry.js";
|
|
|
3
3
|
import { lines, moreLine, paginate, table, truncate } from "../cli/format.js";
|
|
4
4
|
import { suggestionLines, suggestPeople } from "../core/participantlinks.js";
|
|
5
5
|
import { checkStreetPlace } from "../core/streets.js";
|
|
6
|
-
import { addChild, addEvent, addFamily, addName, addNote, addPerson, citeEvent, citeRecord, editEvent, editFamily, editPerson, findEventOwner, mergeFamilies, notRetracted, mergePersons, parseParticipant, retractEvent, retractPerson } from "../core/actions.js";
|
|
6
|
+
import { addChild, addEvent, addFamily, addName, addNote, addPerson, citeEvent, citeRecord, editEvent, editFamily, editPerson, findEventOwner, markPrimaryName, mergeFamilies, notRetracted, mergePersons, parseParticipant, retractEvent, retractPerson } from "../core/actions.js";
|
|
7
7
|
import { listOpt, normId, requireRecord } from "../core/records.js";
|
|
8
8
|
import { SINGLE_KINDS } from "../core/actions.js";
|
|
9
9
|
import { UsageError } from "../core/errors.js";
|
|
10
10
|
import { INFORMATION } from "../core/evidence.js";
|
|
11
11
|
import { EVENT_KINDS, NAME_KINDS } from "../core/model.js";
|
|
12
|
-
import { ancestorGenerations, displayName, familiesAsChild, familiesAsPartner, findPersons, formatName, gedcomName, label, lifespan, parseName, primaryName, relationTo, resolvePerson, sameName, titledName, } from "../core/people.js";
|
|
12
|
+
import { ancestorGenerations, currentName, displayName, familiesAsChild, familiesAsPartner, findPersons, formatName, gedcomName, label, lifespan, parseName, primaryName, relationTo, resolvePerson, sameName, titledName, } from "../core/people.js";
|
|
13
13
|
import { resolveResearch } from "./research.js";
|
|
14
14
|
import { editNote, notesOf, restoreName, restoreNote, retractName, retractNote } from "../core/retractions.js";
|
|
15
15
|
import { DEATH_AFTER_YEARS, unprovenPeople } from "../core/review.js";
|
|
@@ -126,7 +126,7 @@ function citesOf(citations, w = ENGLISH) {
|
|
|
126
126
|
}
|
|
127
127
|
function nameLine(n, w = ENGLISH) {
|
|
128
128
|
const cites = citesOf(n.citations, w);
|
|
129
|
-
return `${titledName(n)}${n.kind ? ` (${w.word("name", n.kind)})` : ""}${cites ? ` ← ${cites}` : ""}`;
|
|
129
|
+
return `${titledName(n)}${n.kind ? ` (${w.word("name", n.kind)})` : ""}${n.primary ? ` · ${w.word("name", "current")}` : ""}${cites ? ` ← ${cites}` : ""}`;
|
|
130
130
|
}
|
|
131
131
|
function historyOf(p) {
|
|
132
132
|
const out = [];
|
|
@@ -305,7 +305,7 @@ register({
|
|
|
305
305
|
const who = (x) => (shown.add(x), relative(x, w));
|
|
306
306
|
const out = [
|
|
307
307
|
...(removed ? [removed.line, ""] : []),
|
|
308
|
-
`${p.id} ${titledName(
|
|
308
|
+
`${p.id} ${titledName(currentName(p))}${lifespan(p) ? ` (${lifespan(p)})` : ""} ${w.lang ? w.word("sex", p.sex) : `sex ${p.sex}`}`,
|
|
309
309
|
...(p.names.length > 1 || p.names.some((n) => n.citations?.length) ? ["", w.say("ui.show.names", "names"), ...p.names.map((n) => ` ${nameLine(n, w)}`)] : []),
|
|
310
310
|
"",
|
|
311
311
|
w.say("ui.show.facts", "facts"),
|
|
@@ -426,14 +426,15 @@ register({
|
|
|
426
426
|
tree: true,
|
|
427
427
|
writes: true,
|
|
428
428
|
description: "A name without a surname (\"Markéta\") is completed by the full one and the person is shown by it; the same\n" +
|
|
429
|
-
"name again only adds its citation. A woman is shown by her birth name; --kind married keeps the married one aside
|
|
429
|
+
"name again only adds its citation. A woman is shown by her birth name; --kind married keeps the married one aside.\n" +
|
|
430
|
+
"--primary shows the person by this name from now on (strom name primary marks a name they have already).",
|
|
430
431
|
args: [
|
|
431
432
|
{ name: "person", description: "ID (P0001) or name", required: true },
|
|
432
433
|
{ name: "name", description: 'the name, surname in slashes: "Markéta /Růžičková/"', required: true },
|
|
433
434
|
],
|
|
434
435
|
options: [
|
|
435
436
|
{ name: "kind", type: "string", value: "<kind>", description: `${NAME_KINDS.join(", ")} (default: plain)` },
|
|
436
|
-
{ name: "primary", type: "boolean", description: "show the person by this name" },
|
|
437
|
+
{ name: "primary", type: "boolean", description: "show the person by this name now (their identity stays the birth name)" },
|
|
437
438
|
...TITLE_OPTIONS,
|
|
438
439
|
...CITE_OPTIONS.filter((o) => o.name !== "status"),
|
|
439
440
|
],
|
|
@@ -454,6 +455,34 @@ register({
|
|
|
454
455
|
const r = addName(tree, p.id, { name: args[1], kind: opts.kind, citation: citationOf(opts), primary: Boolean(opts.primary), prefix: opts.prefix, suffix: opts.suffix });
|
|
455
456
|
return { text: written(tree), data: { person: r.person, name: r.name } };
|
|
456
457
|
},
|
|
458
|
+
}, {
|
|
459
|
+
path: ["name", "primary"],
|
|
460
|
+
summary: "Show a person by one of their names now — a divorced woman back at her birth surname — without adding it again",
|
|
461
|
+
group: "people",
|
|
462
|
+
tree: true,
|
|
463
|
+
writes: true,
|
|
464
|
+
description: "The user's choice, never told from a record (a divorce does not say it): the name is marked, every other name\n" +
|
|
465
|
+
"stays as it is. person show, lists, the brief, the Strom app show the person by it; their identity — what a\n" +
|
|
466
|
+
"correction (person edit --name), a citation of the person and pairing go by — stays the birth name. --clear takes\n" +
|
|
467
|
+
"the mark off: shown by the birth name again (the Strom app: by the married surname, as before).",
|
|
468
|
+
args: [
|
|
469
|
+
{ name: "person", description: "ID (P0001) or name", required: true },
|
|
470
|
+
{ name: "name", description: 'one of their names, surname in slashes: "Jana /Lhotová/" (none with --clear)' },
|
|
471
|
+
],
|
|
472
|
+
options: [
|
|
473
|
+
{ name: "clear", type: "boolean", description: "take the mark off: no name chosen" },
|
|
474
|
+
],
|
|
475
|
+
examples: ['strom name primary P0002 "Josef /Nowak/"', "strom name primary P0002 --clear"],
|
|
476
|
+
run(ctx, { args, opts }) {
|
|
477
|
+
const tree = ctx.tree();
|
|
478
|
+
const p = resolvePerson(tree, args[0]);
|
|
479
|
+
if (opts.clear && args[1] !== undefined)
|
|
480
|
+
throw new UsageError("a name or --clear, not both", { hint: `strom name primary ${p.id} "<Given /Surname/>" · strom name primary ${p.id} --clear` });
|
|
481
|
+
if (!opts.clear && args[1] === undefined)
|
|
482
|
+
throw new UsageError("which name?", { hint: `its names: ${p.names.map((n) => `"${gedcomName(n)}"`).join(", ")} · strom name primary ${p.id} "<Given /Surname/>" · --clear` });
|
|
483
|
+
const r = markPrimaryName(tree, p.id, opts.clear ? undefined : args[1], opts.reason);
|
|
484
|
+
return { text: written(tree), data: { person: r.person, name: r.name, ...(r.was ? { was: r.was } : {}) } };
|
|
485
|
+
},
|
|
457
486
|
}, {
|
|
458
487
|
path: ["name", "retract"],
|
|
459
488
|
summary: "Take back a name of a person that turned out wrong (kept with the reason, shown nowhere but in the history)",
|
package/dist/commands/sync.js
CHANGED
|
@@ -110,8 +110,8 @@ function changeLine(tree, c, incoming, lang, readings) {
|
|
|
110
110
|
now: c.kind === "fact.detail" ? detailText(c.fact, lang, short) : factText(c.fact, lang, short),
|
|
111
111
|
when: [humanDate(c.fact?.date, lang), humanPlace(c.fact?.place, undefined, lang)].filter(Boolean).join(", "),
|
|
112
112
|
text: truncate(c.text ?? "", 120),
|
|
113
|
-
old: c.note ? truncate(c.note.was, 80) : c.title ? truncate(c.title.was, 80) || "—" : c.wasTitle !== undefined ? truncate(c.wasTitle, 80) : shownName(c.wasName),
|
|
114
|
-
new: c.note ? truncate(c.text ?? "", 80) : c.title || c.wasTitle !== undefined ? truncate(c.text ?? "", 80) || "—" : shownName(c.text),
|
|
113
|
+
old: c.note ? truncate(c.note.was, 80) : c.title ? truncate(c.title.was, 80) || "—" : c.wasTitle !== undefined ? truncate(c.wasTitle, 80) : c.shown ? shownName(c.wasName) || ui(lang, "ui.conflict.primary.none") : shownName(c.wasName),
|
|
114
|
+
new: c.note ? truncate(c.text ?? "", 80) : c.title || c.wasTitle !== undefined ? truncate(c.text ?? "", 80) || "—" : c.shown ? shownName(c.text) || ui(lang, "ui.conflict.primary.none") : shownName(c.text),
|
|
115
115
|
title: c.title ? ui(lang, c.title.part === "before" ? "ui.conflict.titleBefore" : "ui.conflict.titleAfter") : "",
|
|
116
116
|
child: name(c.child),
|
|
117
117
|
// a child's tie to each parent: one word for both, else each parent's
|
|
@@ -148,30 +148,32 @@ function changeLine(tree, c, incoming, lang, readings) {
|
|
|
148
148
|
? `ui.sync.do.removed${c.action === "conflict" ? ".conflict" : ""}`
|
|
149
149
|
: c.kind === "name.changed" && c.action === "user"
|
|
150
150
|
? "ui.sync.do.name.user"
|
|
151
|
-
: c.kind === "name.
|
|
152
|
-
? `ui.sync.do.name
|
|
153
|
-
: c.kind === "name.
|
|
154
|
-
? `ui.sync.do.
|
|
155
|
-
: c.kind === "
|
|
156
|
-
?
|
|
157
|
-
: c.kind === "
|
|
158
|
-
? "ui.sync.do.
|
|
159
|
-
: c.kind === "
|
|
160
|
-
? "ui.sync.do.
|
|
161
|
-
: c.kind === "fact.
|
|
162
|
-
?
|
|
163
|
-
: c.kind === "
|
|
164
|
-
? `ui.sync.do.
|
|
165
|
-
: c.kind === "source.
|
|
166
|
-
? `ui.sync.do.source.
|
|
167
|
-
: c.kind
|
|
168
|
-
? `ui.sync.do
|
|
169
|
-
: c.kind === "fact.
|
|
170
|
-
?
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
:
|
|
151
|
+
: c.kind === "name.shown"
|
|
152
|
+
? `ui.sync.do.name.shown${c.action === "conflict" ? ".conflict" : ""}`
|
|
153
|
+
: c.kind === "name.gone"
|
|
154
|
+
? `ui.sync.do.name.${c.action === "remove" ? "gone" : "kept"}`
|
|
155
|
+
: c.kind === "name.title" && (c.action === "add" || c.action === "user")
|
|
156
|
+
? `ui.sync.do.title${c.action === "user" ? ".user" : ""}`
|
|
157
|
+
: c.kind === "note.changed" && c.action !== "pick"
|
|
158
|
+
? "ui.sync.do.note.changed"
|
|
159
|
+
: c.kind === "family.union" && c.action !== "report"
|
|
160
|
+
? "ui.sync.do.family.union"
|
|
161
|
+
: c.kind === "fact.detail" && c.action === "add"
|
|
162
|
+
? "ui.sync.do.detail"
|
|
163
|
+
: c.kind === "fact.part" && (c.action === "add" || c.action === "user")
|
|
164
|
+
? `ui.sync.do.part${c.action === "user" ? ".user" : ""}`
|
|
165
|
+
: c.kind === "source.title"
|
|
166
|
+
? `ui.sync.do.source.title${c.action === "add" ? ".beside" : c.action === "user" ? ".user" : ""}`
|
|
167
|
+
: c.kind === "source.new" && c.reading?.ok
|
|
168
|
+
? `ui.sync.do.source.new.reading${c.reading.edited ? ".edited" : ""}`
|
|
169
|
+
: c.kind.startsWith("source.") || c.kind === "fact.cite" || c.kind === "person.cite" || c.kind === "family.cite"
|
|
170
|
+
? `ui.sync.do.${c.kind}${c.reads || (c.kind === "source.new" && newReads(c, incoming)) ? ".read" : ""}`
|
|
171
|
+
: c.kind === "fact.new" && c.reads
|
|
172
|
+
? "ui.sync.do.reading"
|
|
173
|
+
// a fact on an entry of the research's reading of a document: as sure as its readers read it
|
|
174
|
+
: c.kind === "fact.new" && c.fact?.cites?.some((x) => readings?.get(x.source)?.ok)
|
|
175
|
+
? "ui.sync.do.reading.document"
|
|
176
|
+
: `ui.sync.do.${c.action}`;
|
|
175
177
|
// an archive: nobody reads a record there, nor is an agent told — said without it
|
|
176
178
|
const said = actsAsArchive(tree) && `${does}.archive` in UI ? `${does}.archive` : does;
|
|
177
179
|
return `${ui(lang, key, v)} → ${ui(lang, said)}`;
|