strom-research 1.5.0 → 1.6.0
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/assets/lang/cs.json +82 -0
- package/assets/lang/de.json +82 -0
- package/assets/method/narrate.md +2 -1
- package/assets/method/recording.md +3 -1
- package/dist/agents/files.js +1 -1
- package/dist/cli/human.js +18 -0
- package/dist/cli/main.js +9 -0
- package/dist/cli/menu-links.js +249 -0
- package/dist/cli/menu-parts.js +2 -2
- package/dist/cli/menu-research.js +35 -16
- package/dist/cli/menu-waiting.js +61 -47
- package/dist/cli/ui.js +82 -0
- package/dist/cli/wizard.js +34 -1
- package/dist/commands/analysis.js +6 -2
- package/dist/commands/browse.js +2 -17
- package/dist/commands/guide.js +6 -1
- package/dist/commands/index.js +1 -0
- package/dist/commands/links.js +367 -0
- package/dist/commands/output.js +7 -1
- package/dist/commands/setup.js +13 -2
- package/dist/commands/start.js +6 -2
- package/dist/commands/story.js +17 -1
- package/dist/commands/sync.js +20 -6
- package/dist/core/actions.js +15 -0
- package/dist/core/dialog.js +25 -0
- package/dist/core/excerpt.js +53 -5
- package/dist/core/links.js +357 -0
- package/dist/core/live.js +81 -9
- package/dist/core/schema.js +1 -0
- package/dist/core/session.js +6 -0
- package/dist/core/stromapp.js +25 -0
- package/dist/core/sync.js +92 -2
- package/dist/core/tree.js +1 -1
- package/dist/core/uninstall.js +3 -0
- package/dist/gedcom/export.js +54 -0
- package/dist/gedcom/validate.js +13 -4
- package/package.json +1 -1
package/assets/lang/cs.json
CHANGED
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"ui.yes": "a",
|
|
39
39
|
"ui.no": "n",
|
|
40
40
|
"ui.enter": "Stiskněte Enter pro návrat do nabídky.",
|
|
41
|
+
"ui.enter.close": "Stiskněte Enter pro zavření okna.",
|
|
41
42
|
"ui.choose": "Vyberte",
|
|
42
43
|
"ui.bad.choice": "Napište prosím jedno z čísel.",
|
|
43
44
|
"ui.dialog.title": "Strom výzkum",
|
|
@@ -109,6 +110,8 @@
|
|
|
109
110
|
"ui.setup.level.sure": "Opravdu povolit všechno?",
|
|
110
111
|
"ui.setup.shortcut": "Vytvořit na ploše zástupce „Strom výzkum“?",
|
|
111
112
|
"ui.setup.shortcut.done": "✓ Zástupce: {file}",
|
|
113
|
+
"ui.setup.links": "Dovolit aplikaci Strom spouštět výzkum na tomto počítači? (Jedním klikem pošle úpravy zpět, otevře výřez v plné kvalitě, agenta nebo prověření osoby – vždy se tu nejdřív zeptá.)",
|
|
114
|
+
"ui.setup.links.done": "✓ Odkazy z aplikace Strom vedou do výzkumu na tomto počítači.",
|
|
112
115
|
"ui.setup.skill": "✓ {agent} teď strom zná v jakékoli složce.",
|
|
113
116
|
"ui.setup.app": "✓ Na počítači je aplikace Strom – menu v ní otevře výzkum, a když agent pracuje, aplikace ho sleduje živě.",
|
|
114
117
|
"ui.setup.done": "Hotovo.",
|
|
@@ -199,6 +202,10 @@
|
|
|
199
202
|
"ui.doc.level": "agent smí",
|
|
200
203
|
"ui.doc.model": "model",
|
|
201
204
|
"ui.doc.shortcut": "zástupce na ploše",
|
|
205
|
+
"ui.doc.links": "odkazy z aplikace Strom",
|
|
206
|
+
"ui.doc.links.ours": "ano",
|
|
207
|
+
"ui.doc.links.other": "vedou do jiného programu (nebo tam, kde strom býval)",
|
|
208
|
+
"ui.doc.links.none": "žádné",
|
|
202
209
|
"ui.doc.app": "aplikace Strom",
|
|
203
210
|
"ui.doc.tree": "tento rodokmen",
|
|
204
211
|
"ui.doc.innode": "strom {version} v Node {node}",
|
|
@@ -316,6 +323,7 @@
|
|
|
316
323
|
"ui.setup.stories.no": "Ne – jen rodokmen a prameny",
|
|
317
324
|
"ui.uninstall.agent": "co o stromu ví {agent}",
|
|
318
325
|
"ui.uninstall.shortcut": "zástupce",
|
|
326
|
+
"ui.uninstall.links": "odkazy z aplikace Strom",
|
|
319
327
|
"ui.uninstall.git": "vlastní git stromu",
|
|
320
328
|
"ui.uninstall.path": "záznam instalátoru v PATH",
|
|
321
329
|
"ui.uninstall.program": "program",
|
|
@@ -498,6 +506,7 @@
|
|
|
498
506
|
"ui.sync.bridge.foreign": "tohle nevypadá jako rodokmen tohoto výzkumu — vyexportujte ho a načtěte ve výzkumu jako jiný rodokmen",
|
|
499
507
|
"ui.sync.bridge.empty": "strom přišel prázdný nebo nečitelný",
|
|
500
508
|
"ui.sync.wait": "Otevírá se aplikace Strom s tímto výzkumem — vyberte v ní, který strom poslat, a potvrďte. Čekám na něj (nejvýš {min} min; Ctrl+C přeruší) …",
|
|
509
|
+
"ui.sync.wait.open": "V aplikaci se nic neukázalo (už byla otevřená)? Otevřete tuto adresu v prohlížeči, ve kterém ji máte: {url}",
|
|
501
510
|
"ui.sync.waited": "Aplikace Strom strom do {min} min neposlala — zkuste to znovu: strom sync --app, nebo ho vyexportujte jako GEDCOM: strom sync <soubor>",
|
|
502
511
|
"ui.sync.got": "Aplikace Strom strom poslala.",
|
|
503
512
|
"ui.sync.nothing.unchanged": "Aplikace Strom nic neposlala: strom se od posledního načtení z výzkumu nezměnil.",
|
|
@@ -580,6 +589,79 @@
|
|
|
580
589
|
"ui.more.intake": "Přidat podklady: dokumenty, fotky, rodokmen z jiného programu, co víte",
|
|
581
590
|
"ui.more.research": "Nový směr výzkumu: předkové nebo potomci někoho dalšího, nebo jedna otázka",
|
|
582
591
|
"ui.more.sync": "Načíst úpravy z aplikace Strom nebo z jiného rodokmenu (.ged, .json)",
|
|
592
|
+
"ui.link.ok": "OK",
|
|
593
|
+
"ui.link.bad": "Tento odkaz z aplikace Strom nešel použít ({why}). Nic se nestalo.",
|
|
594
|
+
"ui.link.notree": "Výzkum, pro který odkaz je, na tomto počítači není (nebo strom nezná jeho složku). Nic se nestalo.",
|
|
595
|
+
"ui.link.which": "Tento výzkum je tu vícekrát. Který má úpravy přijmout?",
|
|
596
|
+
"ui.link.noclip": "Tento výřez pramene {source} už ve výzkumu není (byl udělán znovu nebo odebrán). Otevřete aplikaci Strom z výzkumu znovu, dostane aktuální výřezy.",
|
|
597
|
+
"ui.link.noscan": "Sken tohoto výřezu pramene {source} na tomto počítači není a výzkum nezná ani jeho stránku v online archivu.",
|
|
598
|
+
"ui.link.noterminal": "strom nemohl otevřít okno terminálu. Spusťte strom a zvolte: {item} — Přímo z aplikace Strom.",
|
|
599
|
+
"ui.link.on.done": "✓ Odkazy z aplikace Strom vedou do výzkumu na tomto počítači.",
|
|
600
|
+
"ui.link.on.failed": "Odkazy z aplikace Strom se na tomto počítači nepodařilo nastavit.",
|
|
601
|
+
"ui.link.off.done": "Odkazy z aplikace Strom už do výzkumu na tomto počítači nevedou.",
|
|
602
|
+
"ui.link.off.failed": "Odkazy z aplikace Strom se z tohoto počítače nepodařilo odebrat.",
|
|
603
|
+
"ui.link.which.one": "Tento výzkum je tu vícekrát. Který?",
|
|
604
|
+
"ui.link.noterminal.any": "strom nemohl otevřít okno terminálu. Spusťte strom (výzkum {name}) a pokračujte z jeho nabídky.",
|
|
605
|
+
"ui.link.noterminal.new": "strom nemohl otevřít okno terminálu. Spusťte strom a pak to zkuste z aplikace Strom znovu.",
|
|
606
|
+
"ui.link.asks": "Aplikace Strom žádá: {what}\nVýzkum: {name}",
|
|
607
|
+
"ui.link.go": "Pokračovat?",
|
|
608
|
+
"ui.link.cost": "Agent pracuje na vaše předplatné nebo kredit AI.",
|
|
609
|
+
"ui.link.tasks": "Výzkum zapíše úkoly pro agenta – teď se nic nehledá a nic se neplatí.",
|
|
610
|
+
"ui.link.what.chat": "rozhovor s agentem ({agent}) o výzkumu.",
|
|
611
|
+
"ui.link.what.chat.person": "rozhovor s agentem ({agent}) o osobě {person}.",
|
|
612
|
+
"ui.link.chat.say": "Podívejme se na osobu {person}: řekni mi krátce, co o ní výzkum ví, a zeptej se mě, co chci zjistit.",
|
|
613
|
+
"ui.link.what.review.person": "prověřit znovu osobu {person}.",
|
|
614
|
+
"ui.link.what.review.family": "prověřit znovu osobu {person} i s partnery a dětmi.",
|
|
615
|
+
"ui.link.what.review.line": "prověřit znovu osobu {person} i s předky.",
|
|
616
|
+
"ui.link.what.research.ancestors": "hledat předky osoby {person} (nový směr výzkumu).",
|
|
617
|
+
"ui.link.what.research.descendants": "hledat potomky osoby {person} (nový směr výzkumu).",
|
|
618
|
+
"ui.link.noperson": "Osoba {person} v tomto výzkumu není (nebo z něj byla vyřazena). Nic se nestalo.",
|
|
619
|
+
"ui.link.notask": "Tento úkol už na vás nečeká (odpověď už má, nebo ho převzal agent). Nic se nestalo.",
|
|
620
|
+
"ui.link.task.none": "Úkol {task} v tomto výzkumu není. Nic se nestalo.",
|
|
621
|
+
"ui.link.task.cannot": "Tento úkol teď takhle změnit nejde (mezitím se změnil). Nic se nestalo.",
|
|
622
|
+
"ui.link.what.task.park": "odložit úkol: {what}",
|
|
623
|
+
"ui.link.what.task.drop": "vzdát úkol: {what}",
|
|
624
|
+
"ui.link.what.task.wake": "vrátit úkol do fronty: {what}",
|
|
625
|
+
"ui.link.task.why": "Proč? (pár slov, zůstane u úkolu)",
|
|
626
|
+
"ui.link.task.park.default": "Odloženo v aplikaci Strom.",
|
|
627
|
+
"ui.link.task.drop.default": "Vzdáno v aplikaci Strom.",
|
|
628
|
+
"ui.link.task.park.done": "✓ Úkol je odložený.",
|
|
629
|
+
"ui.link.task.drop.done": "✓ Úkol je vzdaný (zůstává i s důvodem).",
|
|
630
|
+
"ui.link.task.wake.done": "✓ Úkol je zpět ve frontě agenta.",
|
|
631
|
+
"ui.link.sessions.title": "Sezení agenta – {name}",
|
|
632
|
+
"ui.link.sessions.none": "Agent na tomto výzkumu zatím nepracoval.",
|
|
633
|
+
"ui.link.sessions.month": "Tento měsíc: sezení {n}, {cost}",
|
|
634
|
+
"ui.link.sessions.more": "… a dalších {n} dříve",
|
|
635
|
+
"ui.link.conflict.none": "Rozpor {id} v tomto výzkumu není. Nic se nestalo.",
|
|
636
|
+
"ui.link.conflict.decided": "Tento rozpor už je rozhodnutý: {resolution}",
|
|
637
|
+
"ui.link.what.conflict.decide": "rozhodnout rozpor: {title}",
|
|
638
|
+
"ui.link.what.conflict.agent": "nechat rozpor na agentovi ({agent}): {title}",
|
|
639
|
+
"ui.link.conflict.pick": "Co platí?",
|
|
640
|
+
"ui.link.conflict.other": "Něco jiného (napíšu sám)",
|
|
641
|
+
"ui.link.conflict.own": "Co platí?",
|
|
642
|
+
"ui.link.conflict.why": "Proč? (co rozhoduje)",
|
|
643
|
+
"ui.link.conflict.by": "{why} (rozhodnutí uživatele)",
|
|
644
|
+
"ui.link.conflict.done": "✓ Rozpor je rozhodnutý.",
|
|
645
|
+
"ui.link.conflict.say": "Podívej se na rozpor {id} ({title}): zvaž prameny, rozhodni ho přes strom conflict resolve a pak mi řekni, k čemu jsi došel a proč.",
|
|
646
|
+
"ui.link.story.none": "Osoba {person} zatím nemá vyprávění. Nic se nestalo.",
|
|
647
|
+
"ui.link.story.final": "Vyprávění osoby {person} už je schválené.",
|
|
648
|
+
"ui.link.what.story": "schválit vyprávění osoby {person}, jak je (už nebude návrh).",
|
|
649
|
+
"ui.link.story.done": "✓ Vyprávění je schválené. Až výzkum v aplikaci Strom otevřete znovu, bude tam bez štítku Návrh.",
|
|
650
|
+
"ui.link.sync.none": "{input} není v tomto výzkumu poslání z aplikace Strom. Nic se nestalo.",
|
|
651
|
+
"ui.link.sync.was": "Toto poslání už bylo vráceno.",
|
|
652
|
+
"ui.link.what.sync": "vrátit, co výzkum převzal z aplikace Strom {day} (změn: {n}).",
|
|
653
|
+
"ui.link.new.title": "Aplikace Strom chce začít výzkum s jedním svým rodokmenem.",
|
|
654
|
+
"ui.link.new.name": "Název výzkumu (rodina, např. Novákovi)",
|
|
655
|
+
"ui.link.new.exists": "Výzkum „{name}“ už tu je, i s osobami. Zvolte jiný název.",
|
|
656
|
+
"ui.link.new.wait": "Čekám, až aplikace Strom strom předá – potvrďte to v ní (nejvýš {min} min; Ctrl+C přeruší).",
|
|
657
|
+
"ui.link.new.none": "Aplikace Strom žádný strom nepředala. Výzkum „{name}“ tu je, zatím prázdný: začněte znovu z aplikace, nebo do něj přidejte v nabídce stromu.",
|
|
658
|
+
"ui.link.new.waited": "Z aplikace Strom do {min} min žádný strom nepřišel. Výzkum „{name}“ tu je, zatím prázdný.",
|
|
659
|
+
"ui.link.new.empty": "Strom, který aplikace Strom poslala, nemá žádné osoby – není co zkoumat. Výzkum „{name}“ tu je, zatím prázdný.",
|
|
660
|
+
"ui.link.new.failed": "Aplikace Strom strom předat nemohla. Výzkum „{name}“ tu je, zatím prázdný: zkuste to z aplikace znovu.",
|
|
661
|
+
"ui.link.new.taken": "✓ Rodokmen je ve výzkumu „{name}“: osob {persons}, rodin {families} – jako vodítka; co doloží záznamy, najde agent.",
|
|
662
|
+
"ui.link.new.back": "Aplikace Strom teď strom ukazuje jako strom výzkumu.",
|
|
663
|
+
"ui.link.new.chat": "Začít teď rozhovor s agentem?",
|
|
664
|
+
"ui.link.new.say": "Předal(a) jsem náš rodokmen z aplikace Strom – podívej se, co v něm je, a navrhni, čím začít.",
|
|
583
665
|
"ui.more.sync.noapp": "Načíst změny z jiného rodokmenu (.ged, .json)",
|
|
584
666
|
"ui.sync.path.noapp": "Přetáhněte sem soubor s rodokmenem (.ged nebo .json), nebo napište cestu (Enter nebo 0 zpět)",
|
|
585
667
|
"ui.sync.path": "Přetáhněte sem soubor vyexportovaný z aplikace Strom (.ged nebo .json), nebo napište cestu (Enter nebo 0 zpět)",
|
package/assets/lang/de.json
CHANGED
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"ui.yes": "j",
|
|
39
39
|
"ui.no": "n",
|
|
40
40
|
"ui.enter": "Drücken Sie Enter, um zum Menü zurückzukehren.",
|
|
41
|
+
"ui.enter.close": "Drücken Sie Enter, um dieses Fenster zu schließen.",
|
|
41
42
|
"ui.choose": "Auswahl",
|
|
42
43
|
"ui.bad.choice": "Bitte geben Sie eine der Zahlen ein.",
|
|
43
44
|
"ui.dialog.title": "Strom Ahnenforschung",
|
|
@@ -109,6 +110,8 @@
|
|
|
109
110
|
"ui.setup.level.sure": "Wirklich alles erlauben?",
|
|
110
111
|
"ui.setup.shortcut": "Eine Verknüpfung „Strom Ahnenforschung“ auf dem Desktop anlegen?",
|
|
111
112
|
"ui.setup.shortcut.done": "✓ Verknüpfung: {file}",
|
|
113
|
+
"ui.setup.links": "Der Strom-App erlauben, die Forschung auf diesem Computer zu starten? (Ein Klick schickt Ihre Änderungen zurück, öffnet einen Ausschnitt in voller Qualität, den Agenten oder die Prüfung einer Person – jeweils erst hier gefragt.)",
|
|
114
|
+
"ui.setup.links.done": "✓ Links aus der Strom-App führen hier zur Forschung.",
|
|
112
115
|
"ui.setup.skill": "✓ {agent} kennt strom jetzt in jedem Ordner.",
|
|
113
116
|
"ui.setup.app": "✓ Die Strom-App ist auf diesem Computer – das Menü öffnet die Forschung darin, und während der Agent arbeitet, verfolgt die App sie live.",
|
|
114
117
|
"ui.setup.done": "Fertig.",
|
|
@@ -199,6 +202,10 @@
|
|
|
199
202
|
"ui.doc.level": "der Agent darf",
|
|
200
203
|
"ui.doc.model": "Modell",
|
|
201
204
|
"ui.doc.shortcut": "Desktop-Verknüpfung",
|
|
205
|
+
"ui.doc.links": "Links aus der Strom-App",
|
|
206
|
+
"ui.doc.links.ours": "ja",
|
|
207
|
+
"ui.doc.links.other": "führen zu einem anderen Programm (oder dorthin, wo strom früher war)",
|
|
208
|
+
"ui.doc.links.none": "keine",
|
|
202
209
|
"ui.doc.app": "Strom-App",
|
|
203
210
|
"ui.doc.tree": "dieser Stammbaum",
|
|
204
211
|
"ui.doc.innode": "strom {version} in Node {node}",
|
|
@@ -316,6 +323,7 @@
|
|
|
316
323
|
"ui.setup.stories.no": "Nein – nur der Stammbaum und seine Quellen",
|
|
317
324
|
"ui.uninstall.agent": "was {agent} über strom weiß",
|
|
318
325
|
"ui.uninstall.shortcut": "die Verknüpfung",
|
|
326
|
+
"ui.uninstall.links": "die Links aus der Strom-App",
|
|
319
327
|
"ui.uninstall.git": "strom's eigenes git",
|
|
320
328
|
"ui.uninstall.path": "der PATH-Eintrag des Installationsprogramms",
|
|
321
329
|
"ui.uninstall.program": "das Programm",
|
|
@@ -498,6 +506,7 @@
|
|
|
498
506
|
"ui.sync.bridge.foreign": "das sieht nicht wie der Stammbaum dieser Forschung aus — exportieren Sie ihn und laden Sie ihn als anderen Stammbaum",
|
|
499
507
|
"ui.sync.bridge.empty": "der Stammbaum kam leer oder unlesbar an",
|
|
500
508
|
"ui.sync.wait": "Die Strom-App öffnet sich mit dieser Forschung — wählen Sie dort, welcher Stammbaum gesendet wird, und bestätigen Sie. Warte darauf (höchstens {min} Min.; Strg+C bricht ab) …",
|
|
509
|
+
"ui.sync.wait.open": "In der App erscheint nichts (sie war schon offen)? Öffnen Sie diese Adresse in dem Browser, in dem Sie sie haben: {url}",
|
|
501
510
|
"ui.sync.waited": "Die Strom-App hat den Stammbaum nicht innerhalb von {min} Min. gesendet — erneut: strom sync --app, oder als GEDCOM exportieren: strom sync <Datei>",
|
|
502
511
|
"ui.sync.got": "Die Strom-App hat den Stammbaum gesendet.",
|
|
503
512
|
"ui.sync.nothing.unchanged": "Die Strom-App sendet nichts: der Stammbaum hat sich seit dem letzten Laden aus der Forschung nicht geändert.",
|
|
@@ -580,6 +589,79 @@
|
|
|
580
589
|
"ui.more.intake": "Unterlagen der Familie: Dokumente, Fotos, ein Stammbaum aus einem anderen Programm, was Sie wissen",
|
|
581
590
|
"ui.more.research": "Eine neue Richtung: die Vorfahren oder Nachkommen einer weiteren Person, oder eine Frage",
|
|
582
591
|
"ui.more.sync": "Änderungen aus der Strom-App oder einem anderen Stammbaum übernehmen (.ged, .json)",
|
|
592
|
+
"ui.link.ok": "OK",
|
|
593
|
+
"ui.link.bad": "Dieser Link aus der Strom-App ließ sich nicht verwenden ({why}). Es ist nichts geschehen.",
|
|
594
|
+
"ui.link.notree": "Die Forschung, für die dieser Link ist, ist nicht auf diesem Computer (oder strom kennt ihren Ordner nicht). Es ist nichts geschehen.",
|
|
595
|
+
"ui.link.which": "Diese Forschung ist hier mehrmals. Welche soll die Änderungen übernehmen?",
|
|
596
|
+
"ui.link.noclip": "Dieser Ausschnitt der Quelle {source} ist nicht mehr in der Forschung (neu gemacht oder entfernt). Öffnen Sie die Strom-App erneut aus der Forschung, dann bekommt sie die aktuellen.",
|
|
597
|
+
"ui.link.noscan": "Der Scan dieses Ausschnitts der Quelle {source} ist nicht auf diesem Computer, und die Forschung kennt auch keine Seite davon im Online-Archiv.",
|
|
598
|
+
"ui.link.noterminal": "strom konnte kein Terminalfenster öffnen. Starten Sie strom und wählen Sie: {item} — Direkt aus der Strom-App.",
|
|
599
|
+
"ui.link.on.done": "✓ Links aus der Strom-App führen hier zur Forschung.",
|
|
600
|
+
"ui.link.on.failed": "Die Links aus der Strom-App ließen sich auf diesem Computer nicht einrichten.",
|
|
601
|
+
"ui.link.off.done": "Links aus der Strom-App führen hier nicht mehr zur Forschung.",
|
|
602
|
+
"ui.link.off.failed": "Die Links aus der Strom-App ließen sich von diesem Computer nicht entfernen.",
|
|
603
|
+
"ui.link.which.one": "Diese Forschung ist hier mehrmals vorhanden. Welche?",
|
|
604
|
+
"ui.link.noterminal.any": "strom konnte kein Terminalfenster öffnen. Starten Sie strom (die Forschung {name}) und machen Sie in seinem Menü weiter.",
|
|
605
|
+
"ui.link.noterminal.new": "strom konnte kein Terminalfenster öffnen. Starten Sie strom und versuchen Sie es dann erneut aus der Strom-App.",
|
|
606
|
+
"ui.link.asks": "Die Strom-App bittet um: {what}\nForschung: {name}",
|
|
607
|
+
"ui.link.go": "Fortfahren?",
|
|
608
|
+
"ui.link.cost": "Der Agent arbeitet auf Ihr Abonnement oder KI-Guthaben.",
|
|
609
|
+
"ui.link.tasks": "Die Forschung schreibt Aufgaben für den Agenten – jetzt wird nichts gesucht und nichts bezahlt.",
|
|
610
|
+
"ui.link.what.chat": "ein Gespräch mit dem Agenten ({agent}) über die Forschung.",
|
|
611
|
+
"ui.link.what.chat.person": "ein Gespräch mit dem Agenten ({agent}) über {person}.",
|
|
612
|
+
"ui.link.chat.say": "Schauen wir uns {person} an: Sag mir kurz, was die Forschung über diese Person weiß, und frag mich, was ich herausfinden möchte.",
|
|
613
|
+
"ui.link.what.review.person": "{person} erneut prüfen.",
|
|
614
|
+
"ui.link.what.review.family": "{person} erneut prüfen, mit Partnern und Kindern.",
|
|
615
|
+
"ui.link.what.review.line": "{person} erneut prüfen, mit den Vorfahren.",
|
|
616
|
+
"ui.link.what.research.ancestors": "die Vorfahren von {person} suchen (eine neue Richtung der Forschung).",
|
|
617
|
+
"ui.link.what.research.descendants": "die Nachkommen von {person} suchen (eine neue Richtung der Forschung).",
|
|
618
|
+
"ui.link.noperson": "{person} ist nicht in dieser Forschung (oder wurde aus ihr entfernt). Es ist nichts geschehen.",
|
|
619
|
+
"ui.link.notask": "Diese Aufgabe wartet nicht mehr auf Sie (sie ist beantwortet, oder ein Agent hat sie übernommen). Es ist nichts geschehen.",
|
|
620
|
+
"ui.link.task.none": "Aufgabe {task} ist nicht in dieser Forschung. Es ist nichts geschehen.",
|
|
621
|
+
"ui.link.task.cannot": "Diese Aufgabe lässt sich jetzt so nicht ändern (sie hat sich inzwischen geändert). Es ist nichts geschehen.",
|
|
622
|
+
"ui.link.what.task.park": "die Aufgabe zurückstellen: {what}",
|
|
623
|
+
"ui.link.what.task.drop": "die Aufgabe aufgeben: {what}",
|
|
624
|
+
"ui.link.what.task.wake": "die Aufgabe zurück in die Warteschlange: {what}",
|
|
625
|
+
"ui.link.task.why": "Warum? (ein paar Worte, bleiben bei der Aufgabe)",
|
|
626
|
+
"ui.link.task.park.default": "In der Strom-App zurückgestellt.",
|
|
627
|
+
"ui.link.task.drop.default": "In der Strom-App aufgegeben.",
|
|
628
|
+
"ui.link.task.park.done": "✓ Die Aufgabe ist zurückgestellt.",
|
|
629
|
+
"ui.link.task.drop.done": "✓ Die Aufgabe ist aufgegeben (bleibt mit dem Grund erhalten).",
|
|
630
|
+
"ui.link.task.wake.done": "✓ Die Aufgabe ist zurück in der Warteschlange des Agenten.",
|
|
631
|
+
"ui.link.sessions.title": "Sitzungen des Agenten – {name}",
|
|
632
|
+
"ui.link.sessions.none": "Der Agent hat an dieser Forschung noch nicht gearbeitet.",
|
|
633
|
+
"ui.link.sessions.month": "Diesen Monat: {n} Sitzungen, {cost}",
|
|
634
|
+
"ui.link.sessions.more": "… und {n} frühere",
|
|
635
|
+
"ui.link.conflict.none": "Widerspruch {id} ist nicht in dieser Forschung. Es ist nichts geschehen.",
|
|
636
|
+
"ui.link.conflict.decided": "Dieser Widerspruch ist bereits entschieden: {resolution}",
|
|
637
|
+
"ui.link.what.conflict.decide": "den Widerspruch entscheiden: {title}",
|
|
638
|
+
"ui.link.what.conflict.agent": "den Widerspruch dem Agenten überlassen ({agent}): {title}",
|
|
639
|
+
"ui.link.conflict.pick": "Was stimmt?",
|
|
640
|
+
"ui.link.conflict.other": "Etwas anderes (ich schreibe es)",
|
|
641
|
+
"ui.link.conflict.own": "Was stimmt?",
|
|
642
|
+
"ui.link.conflict.why": "Warum? (was entscheidet)",
|
|
643
|
+
"ui.link.conflict.by": "{why} (Entscheidung des Nutzers)",
|
|
644
|
+
"ui.link.conflict.done": "✓ Der Widerspruch ist entschieden.",
|
|
645
|
+
"ui.link.conflict.say": "Schau dir den Widerspruch {id} an ({title}): wäge die Quellen ab, entscheide ihn mit strom conflict resolve und sag mir dann, wozu du gekommen bist und warum.",
|
|
646
|
+
"ui.link.story.none": "{person} hat noch keine Erzählung. Es ist nichts geschehen.",
|
|
647
|
+
"ui.link.story.final": "Die Erzählung von {person} ist bereits freigegeben.",
|
|
648
|
+
"ui.link.what.story": "die Erzählung von {person} so freigeben, wie sie ist (dann kein Entwurf mehr).",
|
|
649
|
+
"ui.link.story.done": "✓ Die Erzählung ist freigegeben. Öffnen Sie die Forschung in der Strom-App erneut, dann steht sie ohne das Etikett Entwurf da.",
|
|
650
|
+
"ui.link.sync.none": "{input} ist in dieser Forschung keine Sendung aus der Strom-App. Es ist nichts geschehen.",
|
|
651
|
+
"ui.link.sync.was": "Diese Sendung wurde bereits zurückgenommen.",
|
|
652
|
+
"ui.link.what.sync": "zurücknehmen, was die Forschung am {day} aus der Strom-App übernommen hat ({n} Änderungen).",
|
|
653
|
+
"ui.link.new.title": "Die Strom-App möchte eine Forschung mit einem ihrer Stammbäume beginnen.",
|
|
654
|
+
"ui.link.new.name": "Name der Forschung (die Familie, z. B. Novák)",
|
|
655
|
+
"ui.link.new.exists": "Eine Forschung „{name}“ gibt es hier schon, mit Personen. Wählen Sie einen anderen Namen.",
|
|
656
|
+
"ui.link.new.wait": "Ich warte, bis die Strom-App den Stammbaum übergibt – bestätigen Sie es dort (höchstens {min} Min.; Strg+C bricht ab).",
|
|
657
|
+
"ui.link.new.none": "Die Strom-App hat keinen Stammbaum übergeben. Die Forschung „{name}“ ist hier, noch leer: beginnen Sie erneut aus der App, oder fügen Sie im Menü von strom etwas hinzu.",
|
|
658
|
+
"ui.link.new.waited": "Innerhalb von {min} Min. kam kein Stammbaum aus der Strom-App. Die Forschung „{name}“ ist hier, noch leer.",
|
|
659
|
+
"ui.link.new.empty": "Der Stammbaum, den die Strom-App geschickt hat, hat keine Personen – es gibt nichts zu erforschen. Die Forschung „{name}“ ist hier, noch leer.",
|
|
660
|
+
"ui.link.new.failed": "Die Strom-App konnte den Stammbaum nicht übergeben. Die Forschung „{name}“ ist hier, noch leer: versuchen Sie es erneut aus der App.",
|
|
661
|
+
"ui.link.new.taken": "✓ Der Stammbaum ist in der Forschung „{name}“: {persons} Personen, {families} Familien – als Hinweise; was Quellen belegen, findet der Agent.",
|
|
662
|
+
"ui.link.new.back": "Die Strom-App zeigt den Stammbaum jetzt als Stammbaum der Forschung.",
|
|
663
|
+
"ui.link.new.chat": "Jetzt das Gespräch mit dem Agenten beginnen?",
|
|
664
|
+
"ui.link.new.say": "Ich habe unseren Stammbaum aus der Strom-App übergeben – schau dir an, was darin ist, und schlag vor, womit wir beginnen.",
|
|
583
665
|
"ui.more.sync.noapp": "Änderungen aus einem anderen Stammbaum übernehmen (.ged, .json)",
|
|
584
666
|
"ui.sync.path.noapp": "Ziehen Sie die Stammbaum-Datei (.ged oder .json) hierher oder geben Sie den Pfad ein (Enter oder 0 zurück)",
|
|
585
667
|
"ui.sync.path": "Ziehen Sie die aus der Strom-App exportierte Datei (.ged oder .json) hierher oder geben Sie den Pfad ein (Enter oder 0 zurück)",
|
package/assets/method/narrate.md
CHANGED
|
@@ -10,4 +10,5 @@ A story is written from the facts, for the family.
|
|
|
10
10
|
- Write it to a file in notes/, then `strom story set P… --text @notes/story-P….md
|
|
11
11
|
--fact E… --fact E… --note "what is inferred"`: every fact it leans on goes
|
|
12
12
|
in --fact. A couple's story goes on the family (F…).
|
|
13
|
-
- It stays a draft;
|
|
13
|
+
- It stays a draft; when the user says it is right: `strom story approve P…`
|
|
14
|
+
(the Strom app asks them too).
|
|
@@ -56,7 +56,9 @@ without --dry-run. `#name` labels what a line creates, `@name` uses it later.
|
|
|
56
56
|
- What a finding means for other tasks: `strom task edit T… --note "…"`.
|
|
57
57
|
- Records about one person disagree (a name, a date, an age): record both
|
|
58
58
|
claims and your reasoning — `strom conflict add "<question>" --about P…
|
|
59
|
-
--claim "S…:<what it says>" --claim "S…:<what the other says>"
|
|
59
|
+
--fact BIRT --claim "S…:<what it says>" --claim "S…:<what the other says>"`
|
|
60
|
+
(`--fact`: the fact's tag — BIRT, DEAT, NAME, SEX… — the Strom app shows the
|
|
61
|
+
conflict there). Records
|
|
60
62
|
that give different parents are first two people (see core); they are one
|
|
61
63
|
person with a conflict only once something else proves it. What would decide
|
|
62
64
|
it, if not at hand, is a task about it: `strom task add … --about X…` (or H…).
|
package/dist/agents/files.js
CHANGED
|
@@ -94,7 +94,7 @@ they ask.
|
|
|
94
94
|
- **Their edits in the Strom app** ("I fixed grandma's birthday in the
|
|
95
95
|
app", a .ged or .json they exported): \`strom sync <file>\` shows what it
|
|
96
96
|
would take — tell them, write it on their yes (\`--apply\`); a sync is
|
|
97
|
-
undone with \`strom sync undo I…\`.
|
|
97
|
+
undone with \`strom sync undo I…\`. Straight from the app: \`strom sync --app\`.
|
|
98
98
|
- **What waits for them** (\`strom\` shows it): tell them plainly what to do
|
|
99
99
|
and where, one thing at a time.
|
|
100
100
|
- **Results and the Strom app**: \`output/tree-strom.ged\` is the family tree
|
package/dist/cli/human.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// they are shown.
|
|
5
5
|
import { MONTHS } from "../core/gdate.js";
|
|
6
6
|
import { ui } from "./ui.js";
|
|
7
|
+
import { displayName } from "../core/people.js";
|
|
7
8
|
/**
|
|
8
9
|
* English reads "31 Aug 1830"; the others their own numeric form ("31. 8. 1830", "31.8.1830"). A month and
|
|
9
10
|
* year alone are named ("srpen 1830") — after a word ("před 3/1853") in numbers, which no grammatical case bends.
|
|
@@ -114,3 +115,20 @@ export function humanPlace(place, house, lang) {
|
|
|
114
115
|
return place ?? "";
|
|
115
116
|
return ui(lang, "ui.card.house", { place: place ?? "", house }).replace(/^[,\s]+/, "");
|
|
116
117
|
}
|
|
118
|
+
/** What the agent wrote into a task, for the user: the records it names by ID by their names ("S0001" → its title). */
|
|
119
|
+
export function humanTask(tree, text, lang) {
|
|
120
|
+
const name = (id) => {
|
|
121
|
+
const r = tree.get(id);
|
|
122
|
+
if (!r)
|
|
123
|
+
return id;
|
|
124
|
+
if (r.type === "session")
|
|
125
|
+
return ui(lang, "ui.plan.session", { day: humanDay(r.started, lang) });
|
|
126
|
+
if (r.type === "person")
|
|
127
|
+
return `${displayName(r)} [${r.id}]`;
|
|
128
|
+
if (r.type === "family")
|
|
129
|
+
return r.partners.map((x) => tree.get(x)).filter((x) => !!x).map(displayName).join(" & ") || id;
|
|
130
|
+
return r.title;
|
|
131
|
+
};
|
|
132
|
+
// A list of IDs as a list of names, each once (two sessions of one day read as one).
|
|
133
|
+
return text.replace(/\b[PFSBN]\d{4,}\b(?:, [PFSBN]\d{4,}\b)*/g, (ids) => [...new Set(ids.split(", ").map(name))].join(", "));
|
|
134
|
+
}
|
package/dist/cli/main.js
CHANGED
|
@@ -14,6 +14,7 @@ import { isAgent } from "../core/which.js";
|
|
|
14
14
|
import { noticeStromApp } from "../core/stromapp.js";
|
|
15
15
|
import { isNewer } from "../core/update.js";
|
|
16
16
|
import { refreshGlobal } from "../agents/global.js";
|
|
17
|
+
import { linkFiles, linkHandlerState, registerLinks } from "../core/links.js";
|
|
17
18
|
import { clockLine } from "../core/clock.js";
|
|
18
19
|
import { currentSession } from "../core/session.js";
|
|
19
20
|
import { checkArgs, GroupOnly, parseOptions, resolveCommand, splitPassthrough } from "./execute.js";
|
|
@@ -139,6 +140,14 @@ export async function main(argv, io, env, cwd) {
|
|
|
139
140
|
const last = ctx.settings.config.lastVersion;
|
|
140
141
|
if (ctx.settings.home() && (!last || isNewer(VERSION, last))) {
|
|
141
142
|
refreshGlobal(env);
|
|
143
|
+
// …and the links from the Strom app lead to this strom again (where strom made them)
|
|
144
|
+
try {
|
|
145
|
+
if (linkFiles(env).length && linkHandlerState(env) !== "ours")
|
|
146
|
+
registerLinks(env);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// strom doctor says so
|
|
150
|
+
}
|
|
142
151
|
ctx.settings.config.lastVersion = VERSION;
|
|
143
152
|
ctx.settings.save();
|
|
144
153
|
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
// What the Strom app's menus ask through strom-research:// links (the app's
|
|
2
|
+
// second wave): a task put aside, given up or back in the queue, the agent's
|
|
3
|
+
// sessions and what they cost, a conflict decided, a story approved, a sending
|
|
4
|
+
// taken back — and a tree of the app that becomes a new research. Each is said
|
|
5
|
+
// in the research's terminal and done on the person's word, as the menu does.
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { outOfAnswers, translator } from "./menu-parts.js";
|
|
9
|
+
import { offerChat } from "./menu-research.js";
|
|
10
|
+
import { humanCost, humanDay, humanTask } from "./human.js";
|
|
11
|
+
import { lines, truncate } from "./format.js";
|
|
12
|
+
import { Tree } from "../core/tree.js";
|
|
13
|
+
import { monthSpend } from "../core/session.js";
|
|
14
|
+
import { adoptFailedSince, adoptedSince, awaitAdoption, nothingSince } from "../core/sync.js";
|
|
15
|
+
import { startLive } from "../core/live.js";
|
|
16
|
+
import { adoptAppUrl } from "../core/stromapp.js";
|
|
17
|
+
import { isValidLang } from "../core/lang.js";
|
|
18
|
+
import { agentsHere } from "../core/apps.js";
|
|
19
|
+
import { PROFILES } from "../agents/profiles.js";
|
|
20
|
+
import { foldText, safeFolderName } from "../core/text.js";
|
|
21
|
+
import { StromError } from "../core/errors.js";
|
|
22
|
+
/** What the Strom app asks, said before anything is done; true when the person goes on. */
|
|
23
|
+
export async function asks(ctx, lang, root, what, note) {
|
|
24
|
+
const name = Tree.open(root, ctx.env).config.name;
|
|
25
|
+
ctx.io.stdout(lines(translator(lang)("ui.link.asks", { what, name }), note, "") + "\n");
|
|
26
|
+
return ctx.confirm(translator(lang)("ui.link.go"), true);
|
|
27
|
+
}
|
|
28
|
+
const out = (ctx, line) => ctx.io.stdout(line + "\n");
|
|
29
|
+
/** What stopped it, said; nothing was done. */
|
|
30
|
+
function stop(ctx, line) {
|
|
31
|
+
out(ctx, line);
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
/** A task put aside, given up (each with the person's reason) or back in the queue. */
|
|
35
|
+
export async function taskDo(ctx, run, lang, root, id, what) {
|
|
36
|
+
const t = translator(lang);
|
|
37
|
+
const tree = Tree.open(root, ctx.env);
|
|
38
|
+
const task = tree.get(id);
|
|
39
|
+
if (!task || task.retracted)
|
|
40
|
+
return stop(ctx, t("ui.link.task.none", { task: id }));
|
|
41
|
+
const can = { park: ["open"], drop: ["open", "parked", "waiting"], wake: ["parked", "waiting"] }[what];
|
|
42
|
+
if (!can.includes(task.state))
|
|
43
|
+
return stop(ctx, t("ui.link.task.cannot"));
|
|
44
|
+
if (!(await asks(ctx, lang, root, t(`ui.link.what.task.${what}`, { what: humanTask(tree, task.what, lang) }))))
|
|
45
|
+
return false;
|
|
46
|
+
let reason = [];
|
|
47
|
+
if (what !== "wake") {
|
|
48
|
+
const why = (await ctx.ask(t("ui.link.task.why"), t(`ui.link.task.${what}.default`))).trim();
|
|
49
|
+
if (why === "0")
|
|
50
|
+
return false;
|
|
51
|
+
reason = [`--reason=${why || t(`ui.link.task.${what}.default`)}`];
|
|
52
|
+
}
|
|
53
|
+
if ((await run(["task", what, id, ...reason], true)) !== 0)
|
|
54
|
+
return false;
|
|
55
|
+
out(ctx, t(`ui.link.task.${what}.done`));
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
/** The agent's sessions, the latest first, and what this month's cost. */
|
|
59
|
+
export function sessionsView(ctx, lang, root) {
|
|
60
|
+
const t = translator(lang);
|
|
61
|
+
const tree = Tree.open(root, ctx.env);
|
|
62
|
+
const all = tree.list("session").sort((a, b) => b.started.localeCompare(a.started));
|
|
63
|
+
out(ctx, t("ui.link.sessions.title", { name: tree.config.name }));
|
|
64
|
+
if (!all.length)
|
|
65
|
+
return void out(ctx, ` ${t("ui.link.sessions.none")}`);
|
|
66
|
+
const month = monthSpend(tree, new Date().toISOString().slice(0, 7));
|
|
67
|
+
out(ctx, t("ui.link.sessions.month", { n: month.sessions, cost: humanCost(month.amount, lang) }));
|
|
68
|
+
out(ctx, "");
|
|
69
|
+
const shown = all.slice(0, 15);
|
|
70
|
+
for (const s of shown) {
|
|
71
|
+
const task = s.task ? tree.get(s.task) : undefined;
|
|
72
|
+
const agent = PROFILES[s.agent ?? ""]?.name ?? s.agent ?? "";
|
|
73
|
+
const what = truncate(humanTask(tree, task?.what ?? s.summary ?? "", lang), 80);
|
|
74
|
+
const cost = s.metrics?.costUsd !== undefined ? `${humanCost(s.metrics.costUsd, lang)}${s.metrics.costPartial ? "+" : ""}` : "";
|
|
75
|
+
out(ctx, ` ${humanDay(s.started, lang)} ${[agent, what, cost].filter(Boolean).join(" · ")}`);
|
|
76
|
+
}
|
|
77
|
+
if (all.length > shown.length)
|
|
78
|
+
out(ctx, ` ${t("ui.link.sessions.more", { n: all.length - shown.length })}`);
|
|
79
|
+
}
|
|
80
|
+
/** A conflict the person decides: which claim holds (or their own words), and why. */
|
|
81
|
+
export async function decideConflict(ctx, run, lang, root, id) {
|
|
82
|
+
const t = translator(lang);
|
|
83
|
+
const tree = Tree.open(root, ctx.env);
|
|
84
|
+
const c = tree.get(id);
|
|
85
|
+
if (!c || c.type !== "conflict")
|
|
86
|
+
return stop(ctx, t("ui.link.conflict.none", { id }));
|
|
87
|
+
if (c.state === "resolved")
|
|
88
|
+
return stop(ctx, t("ui.link.conflict.decided", { resolution: c.resolution ?? "" }));
|
|
89
|
+
if (!(await asks(ctx, lang, root, t("ui.link.what.conflict.decide", { title: c.title }))))
|
|
90
|
+
return false;
|
|
91
|
+
const claim = (x) => `${x.value}${x.source ? ` — ${tree.get(x.source)?.title ?? x.source}` : ""}`;
|
|
92
|
+
const i = await ctx.choose(t("ui.link.conflict.pick"), [...c.claims.map((x) => ({ label: claim(x) })), { label: t("ui.link.conflict.other") }], 0, { back: t("ui.browse.back") });
|
|
93
|
+
if (i === undefined)
|
|
94
|
+
return false;
|
|
95
|
+
let resolution = i < c.claims.length ? `${c.claims[i].value}${c.claims[i].source ? ` (${c.claims[i].source})` : ""}` : "";
|
|
96
|
+
if (!resolution) {
|
|
97
|
+
resolution = (await ctx.ask(t("ui.link.conflict.own"))).trim();
|
|
98
|
+
if (!resolution || resolution === "0")
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
let why = "";
|
|
102
|
+
while (!why) {
|
|
103
|
+
why = (await ctx.ask(t("ui.link.conflict.why"))).trim();
|
|
104
|
+
if (why === "0" || outOfAnswers(ctx))
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
if ((await run(["conflict", "resolve", id, `--resolution=${resolution}`, `--reasoning=${t("ui.link.conflict.by", { why })}`], true)) !== 0)
|
|
108
|
+
return false;
|
|
109
|
+
out(ctx, t("ui.link.conflict.done"));
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
/** A story the person read and approves: no longer a draft. */
|
|
113
|
+
export async function approveStory(ctx, run, lang, root, person, who) {
|
|
114
|
+
const t = translator(lang);
|
|
115
|
+
const st = person.story;
|
|
116
|
+
if (!st)
|
|
117
|
+
return stop(ctx, t("ui.link.story.none", { person: who }));
|
|
118
|
+
if (st.status === "final")
|
|
119
|
+
return stop(ctx, t("ui.link.story.final", { person: who }));
|
|
120
|
+
out(ctx, lines(st.title ? `„${st.title}“` : undefined, "", st.text.replace(/\*\*/g, ""), ""));
|
|
121
|
+
if (!(await asks(ctx, lang, root, t("ui.link.what.story", { person: who }))))
|
|
122
|
+
return false;
|
|
123
|
+
if ((await run(["story", "approve", person.id], true)) !== 0)
|
|
124
|
+
return false;
|
|
125
|
+
out(ctx, t("ui.link.story.done"));
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
/** What the research took from the Strom app, taken back on the person's word. */
|
|
129
|
+
export async function undoSending(ctx, run, lang, root, id) {
|
|
130
|
+
const t = translator(lang);
|
|
131
|
+
const input = Tree.open(root, ctx.env).get(id);
|
|
132
|
+
if (!input || input.type !== "input" || !input.sync)
|
|
133
|
+
return stop(ctx, t("ui.link.sync.none", { input: id }));
|
|
134
|
+
if (input.sync.undone)
|
|
135
|
+
return stop(ctx, t("ui.link.sync.was"));
|
|
136
|
+
if (!(await asks(ctx, lang, root, t("ui.link.what.sync", { day: humanDay(input.created, lang), n: input.sync.applied.length }))))
|
|
137
|
+
return false;
|
|
138
|
+
return (await run(["sync", "undo", id])) === 0;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* A tree of the Strom app becomes a new research (strom-research://new?app=<its mark>): named, its language and
|
|
142
|
+
* agent chosen, made; the app opened to hand the tree over (?adopt=<the bridge>), the tree taken in as leads,
|
|
143
|
+
* the research sent back to the app — then the conversation offered.
|
|
144
|
+
*/
|
|
145
|
+
export async function newFromApp(ctx, runIn, token, openAt, forward) {
|
|
146
|
+
// strom not set up yet (the app is how this person came): the setup first
|
|
147
|
+
if (!ctx.settings.home()?.value) {
|
|
148
|
+
await (await runIn(undefined))(["setup"]);
|
|
149
|
+
ctx.settings.reload();
|
|
150
|
+
if (!ctx.settings.home()?.value)
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
let lang = ctx.uiLang();
|
|
154
|
+
let t = translator(lang);
|
|
155
|
+
out(ctx, t("ui.link.new.title"));
|
|
156
|
+
out(ctx, "");
|
|
157
|
+
// the name; an empty research of that name (a handover that did not come) is taken again
|
|
158
|
+
let name = "";
|
|
159
|
+
let root;
|
|
160
|
+
for (;;) {
|
|
161
|
+
name = (await ctx.ask(t("ui.link.new.name"))).trim().replace(/\s+/gu, " ");
|
|
162
|
+
if (!name || name === "0")
|
|
163
|
+
return false;
|
|
164
|
+
const same = ctx.knownTrees().find((k) => foldText(k.name) === foldText(name) || path.basename(k.root) === safeFolderName(name));
|
|
165
|
+
if (!same)
|
|
166
|
+
break;
|
|
167
|
+
if (Tree.open(same.root, ctx.env).count("person") === 0) {
|
|
168
|
+
root = same.root;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
out(ctx, t("ui.link.new.exists", { name }));
|
|
172
|
+
if (outOfAnswers(ctx))
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
for (;;) {
|
|
176
|
+
const a = (await ctx.ask(t("ui.setup.lang"), lang)).trim().toLowerCase();
|
|
177
|
+
if (a === "0")
|
|
178
|
+
return false;
|
|
179
|
+
if (isValidLang(a)) {
|
|
180
|
+
lang = a;
|
|
181
|
+
t = translator(lang);
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
if (outOfAnswers(ctx))
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
const here = agentsHere(ctx.env).map((a) => a.id);
|
|
188
|
+
const mine = ctx.settings.agent().value;
|
|
189
|
+
let agent = mine;
|
|
190
|
+
if (here.length > 1) {
|
|
191
|
+
const i = await ctx.choose(t("ui.setup.agent.pick"), here.map((id) => ({ label: PROFILES[id].name })), Math.max(0, here.indexOf(mine)), { back: t("ui.browse.back") });
|
|
192
|
+
if (i === undefined)
|
|
193
|
+
return false;
|
|
194
|
+
agent = here[i];
|
|
195
|
+
}
|
|
196
|
+
if (!root) {
|
|
197
|
+
let said = "";
|
|
198
|
+
const code = await (await runIn(undefined))(["init", name, "--lang", lang, "--json"], true, (s) => (said += s));
|
|
199
|
+
if (code !== 0)
|
|
200
|
+
return false;
|
|
201
|
+
root = JSON.parse(said).root;
|
|
202
|
+
}
|
|
203
|
+
const run = await runIn(root);
|
|
204
|
+
if (agent !== mine)
|
|
205
|
+
await run(["agents", "use", agent, "--for-tree"], true);
|
|
206
|
+
// the app hands its tree over to the bridge of this research
|
|
207
|
+
awaitAdoption(root, token);
|
|
208
|
+
const info = startLive(root, ctx.env, { current: true });
|
|
209
|
+
if (!info)
|
|
210
|
+
throw new StromError("the bridge did not start", { hint: "strom live serve shows why" });
|
|
211
|
+
const url = adoptAppUrl(info.url, ctx.settings);
|
|
212
|
+
const since = Date.now();
|
|
213
|
+
const waitMs = Number(ctx.env.STROM_ADOPT_WAIT_MS ?? 30 * 60_000);
|
|
214
|
+
const minutes = Math.max(1, Math.round(waitMs / 60_000));
|
|
215
|
+
out(ctx, openAt(url) ? `${t("ui.link.new.wait", { min: minutes })}\n${t("ui.sync.wait.open", { url })}` : t("ui.app.url", { url }));
|
|
216
|
+
let file;
|
|
217
|
+
while (Date.now() < since + waitMs) {
|
|
218
|
+
file = adoptedSince(root, since);
|
|
219
|
+
if (file)
|
|
220
|
+
break;
|
|
221
|
+
const failed = adoptFailedSince(root, since);
|
|
222
|
+
if (failed) {
|
|
223
|
+
forward();
|
|
224
|
+
return stop(ctx, t(failed === "empty" ? "ui.link.new.empty" : "ui.link.new.failed", { name }));
|
|
225
|
+
}
|
|
226
|
+
if (nothingSince(root, since)) {
|
|
227
|
+
forward();
|
|
228
|
+
out(ctx, t("ui.link.new.none", { name }));
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
232
|
+
}
|
|
233
|
+
forward();
|
|
234
|
+
if (!file)
|
|
235
|
+
return stop(ctx, t("ui.link.new.waited", { name, min: minutes }));
|
|
236
|
+
// the family's tree, as leads (without the images written into it: the research keeps its own)
|
|
237
|
+
const { withoutImages } = await import("../commands/sync.js");
|
|
238
|
+
const kept = path.join(path.dirname(file), `${safeFolderName(name)}.ged`);
|
|
239
|
+
fs.writeFileSync(kept, withoutImages(fs.readFileSync(file, "utf8")));
|
|
240
|
+
if ((await run(["intake", kept], true)) !== 0)
|
|
241
|
+
return false;
|
|
242
|
+
const tree = Tree.open(root, ctx.env);
|
|
243
|
+
out(ctx, t("ui.link.new.taken", { name, persons: tree.count("person"), families: tree.count("family") }));
|
|
244
|
+
// the research as it is now back into the app's window: the same tree, now of the research
|
|
245
|
+
if ((await run(["app"], true)) === 0)
|
|
246
|
+
out(ctx, t("ui.link.new.back"));
|
|
247
|
+
await offerChat(ctx, run, lang, root, t("ui.link.new.chat"), t("ui.link.new.say"));
|
|
248
|
+
return true;
|
|
249
|
+
}
|
package/dist/cli/menu-parts.js
CHANGED
|
@@ -11,9 +11,9 @@ export function outOfAnswers(ctx) {
|
|
|
11
11
|
return ctx.io.answers !== undefined && ctx.io.answers.length === 0;
|
|
12
12
|
}
|
|
13
13
|
/** "Press Enter to go back to the menu." — unless there is nobody to press it. */
|
|
14
|
-
export async function pause(ctx, lang) {
|
|
14
|
+
export async function pause(ctx, lang, enter = "ui.enter") {
|
|
15
15
|
if (!outOfAnswers(ctx))
|
|
16
|
-
await ctx.ask(ui(lang,
|
|
16
|
+
await ctx.ask(ui(lang, enter));
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* A submenu: its items (built afresh each time, so what they say is current), numbered in the order shown, 0 back.
|