@uniweb/semantic-parser 1.2.0 → 1.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/semantic-parser",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "Semantic parser for ProseMirror/TipTap content structures",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -360,6 +360,11 @@ function createSequenceElement(node, options = {}) {
360
360
  .map((doc) => parseDocumentBlock(doc.attrs)) || [],
361
361
  };
362
362
 
363
+ // The editor renamed this node to `StructuredContent` (2026-07-31,
364
+ // editor-internal). Both are accepted: the new name is what the editor
365
+ // emits now, and the old one still arrives from documents authored
366
+ // before the rename, which the framework never rewrites.
367
+ case "StructuredContent":
363
368
  case "FormBlock":
364
369
  // Parse form data (can be JSON string or object)
365
370
  let formData = attrs?.data;
@@ -465,10 +470,23 @@ function getTextContent(content, options = {}) {
465
470
  }
466
471
  }
467
472
 
473
+ // Icons are emitted as positional markers that a renderer swaps for a real
474
+ // component (see renderInlineNode). The marker carries the icon's ordinal
475
+ // among the icons of THIS content array, which is the same order
476
+ // processInlineElements() puts them in `children` — so a consumer resolves
477
+ // one with `children.filter(c => c.type === 'icon')[index]`. Referencing by
478
+ // ordinal rather than serializing the attrs keeps an inline `svg` blob out
479
+ // of the text stream and leaves `children` the single source of truth.
480
+ const iconOrdinals = new Map();
481
+ let nextIcon = 0;
482
+ for (const node of content) {
483
+ if (isIconNode(node)) iconOrdinals.set(node, nextIcon++);
484
+ }
485
+
468
486
  return groups
469
487
  .reduce((prev, group) => {
470
488
  const inner = group.nodes.reduce(
471
- (acc, node) => acc + renderInlineNode(node),
489
+ (acc, node) => acc + renderInlineNode(node, iconOrdinals),
472
490
  "",
473
491
  );
474
492
  if (!inner && !group.link) return prev;
@@ -481,7 +499,7 @@ function getTextContent(content, options = {}) {
481
499
  * Render one inline node to HTML, applying every mark EXCEPT link — the
482
500
  * anchor is applied per group by getTextContent, since it may span nodes.
483
501
  */
484
- function renderInlineNode(curr) {
502
+ function renderInlineNode(curr, iconOrdinals) {
485
503
  return [curr]
486
504
  .reduce((prev, curr) => {
487
505
  const { type, marks = [], text } = curr;
@@ -588,6 +606,21 @@ function renderInlineNode(curr) {
588
606
  return prev + `<uniweb-inset data-ref-id="${refId}"></uniweb-inset>`;
589
607
  } else if (type === "hardBreak") {
590
608
  return prev + "<br>";
609
+ } else if (isIconNode(curr)) {
610
+ // An icon sitting IN the prose — `Click the ![](lu-save) button`.
611
+ // It rides as a positional marker for the same reason an inline
612
+ // inset does: the paragraph's text is an HTML string, and an
613
+ // icon needs a component (kit's <Icon>, which resolves the
614
+ // library+name against the icon CDN at render time).
615
+ //
616
+ // Until 2026-08-12 this fell through to the catch-all below and
617
+ // the icon vanished — the text kept a gap where it had been, the
618
+ // entry stayed in `children` and in `content.icons`, and nothing
619
+ // put it back. Every other inline atom (math, insets) already
620
+ // had a marker; this was the one that did not.
621
+ const index = iconOrdinals?.get(curr);
622
+ if (index === undefined) return prev;
623
+ return prev + `<uniweb-icon data-index="${index}"></uniweb-icon>`;
591
624
  } else {
592
625
  // console.warn(`unhandled text content type: ${type}`, curr);
593
626
  return prev;