@uniweb/semantic-parser 1.13.1 → 1.13.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/semantic-parser",
3
- "version": "1.13.1",
3
+ "version": "1.13.3",
4
4
  "description": "Semantic parser for ProseMirror/TipTap content structures",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -211,8 +211,15 @@ function isBannerImage(sequence, i) {
211
211
  * back up — ends the stack, and the rest of the run starts a new block.
212
212
  *
213
213
  * Before the title: `#>` label lines, and any smaller headings ascending to a
214
- * more important one, are the pretitle. A label line anywhere in the stack
215
- * also names the block — it never competes for the title.
214
+ * more important one, are the pretitle. After the title, a label line names
215
+ * this block when body, the end, or a line of this headline follows it; written
216
+ * straight above a heading that starts the next block, it is that block's, so
217
+ * the stack ends before it. A label never competes for the title.
218
+ *
219
+ * ⛔ Until 2026-11-10 a label anywhere after the title named this block, so an
220
+ * entry's label written straight under a bodiless title (`# Team`, `#> Role`,
221
+ * `### Name`) became the section's pretitle, and only the first entry lost its
222
+ * own. The docs had always said a label "names the block that starts next".
216
223
  *
217
224
  * `alwaysItems` (a tagged concept block's grouping) suppresses the same-size
218
225
  * continuation: two bodiless same-level headings are two ITEMS, not one
@@ -268,24 +275,41 @@ function readStack(sequence, startIdx, options = {}) {
268
275
  while (i < sequence.length && sequence[i].type === "heading") {
269
276
  const el = sequence[i];
270
277
  if (isLabel(el)) {
271
- // A label inside the stack still names this block.
272
- pre.push(el);
273
- i++;
274
- } else if (el.level === prevLevel && !options.alwaysItems) {
278
+ // A label names the block it opens. Labels written straight above a
279
+ // heading that starts the next block are that block's pretitle, so
280
+ // this stack ends before them. Above body, the end, or a line that
281
+ // continues this headline, they name this block.
282
+ let next = i;
283
+ while (next < sequence.length && isLabel(sequence[next])) next++;
284
+ const after = sequence[next];
285
+ if (after?.type === "heading" && !continuesHeadline(after, prevLevel, options)) break;
286
+ pre.push(...sequence.slice(i, next));
287
+ i = next;
288
+ } else if (!continuesHeadline(el, prevLevel, options)) {
289
+ break; // two+ steps down, or back up: a new block starts here
290
+ } else if (el.level === prevLevel) {
275
291
  part.push(el); // another line of the same part
276
292
  i++;
277
- } else if (el.level === prevLevel + 1) {
293
+ } else {
278
294
  sub.push(el); // the next line of the headline, one step down
279
295
  part = sub;
280
296
  prevLevel = el.level;
281
297
  i++;
282
- } else {
283
- break; // two+ steps down, or back up: a new block starts here
284
298
  }
285
299
  }
286
300
  return { pre, title, sub, length: i - startIdx };
287
301
  }
288
302
 
303
+ /**
304
+ * Whether a heading written right after a headline's last line (at `prevLevel`)
305
+ * extends the headline: a line of the same size, unless `alwaysItems` makes
306
+ * each one an item, or one step smaller. Anything else starts a new block.
307
+ * The one test, so a label's binding and the stack's end cannot disagree.
308
+ */
309
+ function continuesHeadline(el, prevLevel, options = {}) {
310
+ return (el.level === prevLevel && !options.alwaysItems) || el.level === prevLevel + 1;
311
+ }
312
+
289
313
  /**
290
314
  * An element's text as a flat field holds it, without the markers of the icons before its words.
291
315
  *
@@ -588,10 +612,12 @@ function processInlineElements(children, body) {
588
612
  } else if (item.type === "document") {
589
613
  // And an inline `{role=pdf}` in documents[].
590
614
  body.documents.push(item.attrs);
591
- } else if (item.type === "link") {
592
- // Handle inline links extracted from paragraph text nodes
593
- body.links.push(item.attrs);
594
615
  }
616
+ // A link inside a sentence stays in the sentence: `links` holds the links that stand on
617
+ // their own line, which reach it as `link` elements. ⛔ From 2026-01-17 until 2026-10-31
618
+ // this also copied every link out of every paragraph, a fix for two links on consecutive
619
+ // lines that never checked the paragraph held only links. So a section drawing `links`
620
+ // drew a sentence's link a second time, and `links[0]` could be one from the prose.
595
621
  });
596
622
  }
597
623