@uniweb/semantic-parser 1.0.16 → 1.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/semantic-parser",
3
- "version": "1.0.16",
3
+ "version": "1.1.0",
4
4
  "description": "Semantic parser for ProseMirror/TipTap content structures",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -47,6 +47,14 @@ function getCodeBlockData(text, attrs) {
47
47
  return text;
48
48
  }
49
49
 
50
+ /**
51
+ * Check if an inline node is an icon.
52
+ * TipTap editor uses type "UniwebIcon"; markdown pipeline uses type "image" with role "icon".
53
+ */
54
+ function isIconNode(node) {
55
+ return node.type === "UniwebIcon" || (node.type === "image" && node.attrs?.role === "icon");
56
+ }
57
+
50
58
  /**
51
59
  * Process a ProseMirror/TipTap document into a flat sequence
52
60
  * @param {Object} doc ProseMirror document
@@ -287,17 +295,27 @@ function getTextContent(content, options = {}) {
287
295
  const spanMark = marks.find((mark) => mark.type === "span");
288
296
  const attrs = spanMark?.attrs || {};
289
297
  const attrParts = [];
298
+ const styleParts = [];
290
299
 
291
300
  if (attrs.class) attrParts.push(`class="${attrs.class}"`);
292
301
  if (attrs.id) attrParts.push(`id="${attrs.id}"`);
293
302
 
294
- // Add any other custom attributes (data-*, etc.)
295
303
  for (const [key, value] of Object.entries(attrs)) {
296
- if (key !== 'class' && key !== 'id') {
304
+ if (key === 'class' || key === 'id') continue;
305
+ // Convert color/bg to inline styles
306
+ if (key === 'color') {
307
+ styleParts.push(`color: ${value}`);
308
+ } else if (key === 'bg') {
309
+ styleParts.push(`background: ${value}`);
310
+ } else {
297
311
  attrParts.push(`${key}="${value}"`);
298
312
  }
299
313
  }
300
314
 
315
+ if (styleParts.length > 0) {
316
+ attrParts.push(`style="${styleParts.join('; ')}"`)
317
+ }
318
+
301
319
  const attrString = attrParts.length > 0 ? ` ${attrParts.join(' ')}` : '';
302
320
  styledText = `<span${attrString}>${styledText}</span>`;
303
321
  }
@@ -364,7 +382,7 @@ function processInlineElements(content) {
364
382
  const items = [];
365
383
 
366
384
  for (const item of content) {
367
- if (item.type === "UniwebIcon") {
385
+ if (isIconNode(item)) {
368
386
  items.push({
369
387
  type: "icon",
370
388
  attrs: parseUniwebIcon(item.attrs),
@@ -455,17 +473,23 @@ function parseDocumentBlock(itemAttrs) {
455
473
  }
456
474
 
457
475
  function parseUniwebIcon(itemAttrs) {
458
- let { svg, url, size, color, preserveColors, href, target } = itemAttrs || {};
459
-
460
- return {
461
- svg,
462
- url,
463
- size,
464
- color,
465
- preserveColors,
466
- href,
467
- target,
468
- };
476
+ const { svg, url, size, color, preserveColors, href, target, library, name } = itemAttrs || {};
477
+
478
+ // Build object with only defined fields — icon source varies:
479
+ // TipTap editor: svg/url (resolved inline)
480
+ // Markdown pipeline: library + name (resolved at runtime via CDN)
481
+ const icon = {};
482
+ if (svg) icon.svg = svg;
483
+ if (url) icon.url = url;
484
+ if (size) icon.size = size;
485
+ if (color) icon.color = color;
486
+ if (preserveColors) icon.preserveColors = preserveColors;
487
+ if (href) icon.href = href;
488
+ if (target) icon.target = target;
489
+ if (library) icon.library = library;
490
+ if (name) icon.name = name;
491
+
492
+ return icon;
469
493
  }
470
494
 
471
495
  function parseIconBlock(itemAttrs) {
@@ -581,7 +605,7 @@ function isLink(item) {
581
605
 
582
606
  // Filter out icons and whitespace to check for single link
583
607
  const textContent = originalContent.filter((c) => {
584
- if (c.type === "UniwebIcon") {
608
+ if (isIconNode(c)) {
585
609
  return false;
586
610
  } else if (c.type === "text") {
587
611
  return (c.text || "").trim() !== "";
@@ -607,7 +631,7 @@ function isLink(item) {
607
631
  let iconAfter = null;
608
632
 
609
633
  for (let i = 0; i < originalContent.length; i++) {
610
- if (originalContent[i].type === "UniwebIcon") {
634
+ if (isIconNode(originalContent[i])) {
611
635
  const iconAttrs = parseUniwebIcon(originalContent[i].attrs);
612
636
  if (i < linkIndex) {
613
637
  // Take the last icon before the link
@@ -657,7 +681,7 @@ function isOnlyLinks(item) {
657
681
 
658
682
  // Filter to get only significant content (no icons, no whitespace)
659
683
  const textContent = content.filter((c) => {
660
- if (c.type === "UniwebIcon") return false;
684
+ if (isIconNode(c)) return false;
661
685
  if (c.type === "text" && !(c.text || "").trim()) return false;
662
686
  return true;
663
687
  });
@@ -698,7 +722,7 @@ function isStyledLink(item) {
698
722
  if (!content.length) return false;
699
723
 
700
724
  content = content.filter((c) => {
701
- if (c.type === "UniwebIcon") {
725
+ if (isIconNode(c)) {
702
726
  return false;
703
727
  }
704
728