strom-research 1.21.0 → 1.22.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 CHANGED
@@ -5,6 +5,16 @@
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.22.0
9
+
10
+ *Připravuje se: bety do 1.22.0-beta.2. Oddíl bude úplný s vydáním.*
11
+
12
+ - **Jméno jednou v tom, co čeká.** Úkol k materiálu poslanému z aplikace Strom jmenoval ve widgetu a na stránce
13
+ telefonu každého člověka dvakrát (podruhé v hranatých závorkách) a jeho text bez jmen nechal prázdné závorky; menu,
14
+ `strom plan`, aplikace Strom i soubor GEDCOM ukazovaly jméno s jeho ID zopakované. Každé teď jméno řekne jednou.
15
+ - **Widget otevře správný výzkum.** Při několika stromech stejného jména v aplikaci Strom mohl widget na ploše Macu
16
+ otevřít jiný z nich; teď otevře strom svého výzkumu.
17
+
8
18
  ## 1.21.0
9
19
 
10
20
  *26. 10. 2026 · patří k aplikaci Strom 3.16.0.*
package/CHANGELOG.de.md CHANGED
@@ -5,6 +5,17 @@
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.22.0
9
+
10
+ *In Vorbereitung: die Betas bis 1.22.0-beta.2. Der Abschnitt ist mit dem Release vollständig.*
11
+
12
+ - **Jeder Name einmal in dem, was wartet.** Eine Aufgabe zu Material, das aus der Strom-App gesendet wurde, nannte im
13
+ Widget und auf der Seite des Telefons jede Person zweimal (das zweite Mal in eckigen Klammern), und ihr Text ohne
14
+ Namen ließ leere Klammern stehen; Menü, `strom plan`, die Strom-App und die GEDCOM-Datei zeigten den Namen mit
15
+ seiner ID wiederholt. Jedes nennt den Namen jetzt einmal.
16
+ - **Das Widget öffnet die richtige Forschung.** Bei mehreren Stammbäumen gleichen Namens in der Strom-App konnte das
17
+ Widget auf dem Mac-Schreibtisch einen der anderen öffnen; jetzt öffnet es den Stammbaum seiner eigenen Forschung.
18
+
8
19
  ## 1.21.0
9
20
 
10
21
  *26.10.2026 · gehört zur Strom-App 3.16.0.*
package/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ 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.22.0
9
+
10
+ *In preparation: the betas up to 1.22.0-beta.2. The section is complete with the release.*
11
+
12
+ - **Each name once in what waits.** A task for material sent from the Strom app named each person twice in the
13
+ widget and on the phone's page (the second time in brackets), and its text without names kept empty brackets; the
14
+ menu, `strom plan`, the Strom app and the GEDCOM file showed the name with its ID repeated. Each says the name once.
15
+ - **The widget opens the right research.** With several trees of one name in the Strom app, the widget on the Mac's
16
+ desktop could open one of the others; it now opens the tree of its own research.
17
+
8
18
  ## 1.21.0
9
19
 
10
20
  *2026-10-26 · goes with the Strom app 3.16.0.*
package/dist/cli/human.js CHANGED
@@ -136,7 +136,8 @@ function namesOne(word, names) {
136
136
  /**
137
137
  * What the agent wrote into a task, for the user: the records it names by ID by their names ("S0001" → its title).
138
138
  * `asWritten`: a person's ID right after their name in the text stays the bare ID in brackets ("Václava Nováka P0001" →
139
- * "Václava Nováka [P0001]"), never the name said twice — the short lines of What waits.
139
+ * "Václava Nováka [P0001]"), never the name said twice — the short lines of What waits. A person's ID in brackets right
140
+ * after their name, as strom writes it ("Karel Němec [P0001]"), stays as it is either way.
140
141
  */
141
142
  export function humanTask(tree, text, lang, o = {}) {
142
143
  const named = (id, before) => {
@@ -161,5 +162,11 @@ export function humanTask(tree, text, lang, o = {}) {
161
162
  return r.title;
162
163
  };
163
164
  // A list of IDs as a list of names, each once (two sessions of one day read as one).
164
- return text.replace(/\b[PFSBN]\d{4,}\b(?:, [PFSBN]\d{4,}\b)*/g, (ids, at) => o.asWritten && !ids.includes(",") && named(ids, text.slice(0, at)) ? `[${ids}]` : [...new Set(ids.split(", ").map(name))].join(", "));
165
+ const names = (ids, at) => o.asWritten && !ids.includes(",") && named(ids, text.slice(0, at)) ? `[${ids}]` : [...new Set(ids.split(", ").map(name))].join(", ");
166
+ return text.replace(/(\[)?\b([PFSBN]\d{4,}\b(?:, [PFSBN]\d{4,}\b)*)(\])?/g, (all, open, ids, close, at) => {
167
+ // a person's ID in brackets as strom writes it ("Karel Němec [P0001]"): kept after their name, else their name in its place
168
+ if (open && close && !ids.includes(",") && tree.get(ids)?.type === "person")
169
+ return named(ids, text.slice(0, at)) ? all : name(ids);
170
+ return `${open ?? ""}${names(ids, at + (open ? 1 : 0))}${close ?? ""}`;
171
+ });
165
172
  }
@@ -118,7 +118,7 @@ export function forPhone(text) {
118
118
  }
119
119
  return /[\p{L}\p{N}]/u.test(s) ? s : "";
120
120
  }
121
- const taskText = (tree, t) => forPhone(humanTask(tree, t.what, tree.lang));
121
+ const taskText = (tree, t) => forPhone(humanTask(tree, t.what, tree.lang, { asWritten: true }));
122
122
  function head(root) {
123
123
  const r = runGit(root, ["rev-parse", "HEAD"]);
124
124
  return r.status === 0 ? r.stdout.trim() : "";
@@ -202,8 +202,8 @@ export function treeOverview(root, env) {
202
202
  }
203
203
  /** One of what waits as the phone shows it, whole; a text of nothing but IDs, paths and links: said in general. */
204
204
  function waitingLine(tree, { item }) {
205
- const on = item.on ? forPhone(humanTask(tree, item.on, tree.lang)) : "";
206
- return { what: forPhone(humanTask(tree, item.what, tree.lang)) || ui(tree.lang, "ui.phone.waiting.item"), ...(on ? { on } : {}) };
205
+ const on = item.on ? forPhone(humanTask(tree, item.on, tree.lang, { asWritten: true })) : "";
206
+ return { what: forPhone(humanTask(tree, item.what, tree.lang, { asWritten: true })) || ui(tree.lang, "ui.phone.waiting.item"), ...(on ? { on } : {}) };
207
207
  }
208
208
  /**
209
209
  * More of what waits than the overview gives: `limit` of them (at most WAITING_ASKED) from `from`, of the same list the
package/dist/core/tree.js CHANGED
@@ -29,7 +29,7 @@ export const TREE_FILE = "strom.json";
29
29
  export const SEAL_FOREIGN = "seal.foreign";
30
30
  // between releases the candidate being made (a bridge run from the sources says what it is: the Strom app goes by it);
31
31
  // at a release the release's own (package.json)
32
- export const VERSION = "1.21.0";
32
+ export const VERSION = "1.22.0-beta.2";
33
33
  const GITIGNORE = `# Strom Research — local state and large files are not versioned
34
34
  .strom/
35
35
  /media/
@@ -66,12 +66,21 @@ export function appSlug(name) {
66
66
  .replace(/[^a-z0-9]+/gu, "-")
67
67
  .replace(/^-+|-+$/gu, "");
68
68
  }
69
- /** The tree in the Strom app strom uses (the setting strom.app.url, the channel's copy): ?tree=<slug of its name>. */
70
- function treeUrl(env, name) {
69
+ /** A tree's ID as the app keeps it (research.id, from the GEDCOM's _STROM_TREE): a UUID. */
70
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u;
71
+ /**
72
+ * The tree in the Strom app strom uses (the setting strom.app.url, the channel's copy): ?tree=<slug of its name> for
73
+ * an app that knows only names, and &research=<the tree's ID, lowercase> — the one research of that name, when several
74
+ * trees in the app have it. An ID that is no UUID: the name alone.
75
+ */
76
+ export function treeUrl(env, name, id) {
71
77
  const url = new URL(appUrlSetting(new Settings(env, {})).url);
72
78
  const slug = appSlug(name);
73
79
  if (slug)
74
80
  url.searchParams.set("tree", slug);
81
+ const research = id.trim().toLowerCase();
82
+ if (UUID.test(research))
83
+ url.searchParams.set("research", research);
75
84
  return url.href;
76
85
  }
77
86
  // ── names out of a text ─────────────────────────────────────────────────────────────────────────────────────────────
@@ -264,7 +273,7 @@ export function widgetTree(root, env, opts) {
264
273
  if (actsAsArchive(tree))
265
274
  return undefined;
266
275
  const lang = tree.lang;
267
- const openUrl = treeUrl(env, tree.config.name);
276
+ const openUrl = treeUrl(env, tree.config.name, tree.config.id);
268
277
  const o = treeOverview(root, env);
269
278
  const stems = nameStems(tree, o.latest ?? "");
270
279
  const lines = o.now.map((l) => {
@@ -285,7 +294,7 @@ export function widgetTree(root, env, opts) {
285
294
  const finds = treeFinds(root, env, new Date(windowStart).toISOString(), now);
286
295
  const waits = pendingOf(tree, env);
287
296
  const pendingItems = waits.slice(0, WIDGET_PENDING).map(({ item }) => {
288
- const text = forPhone(humanTask(tree, item.what, lang)) || ui(lang, "ui.phone.waiting.item");
297
+ const text = forPhone(humanTask(tree, item.what, lang, { asWritten: true })) || ui(lang, "ui.phone.waiting.item");
289
298
  const none = withoutNames(text, stems);
290
299
  return { text, ...(none ? { textNoNames: none } : {}) };
291
300
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strom-research",
3
- "version": "1.21.0",
3
+ "version": "1.22.0-beta.2",
4
4
  "description": "Genealogical research toolkit for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -57,5 +57,8 @@
57
57
  "devDependencies": {
58
58
  "@types/node": "^22.20.4",
59
59
  "typescript": "^7.0.2"
60
+ },
61
+ "publishConfig": {
62
+ "tag": "beta"
60
63
  }
61
64
  }