@uniweb/semantic-parser 1.4.1 → 1.5.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 +1 -1
- package/src/builders/doc.js +18 -0
- package/src/processors/groups.js +9 -13
- package/src/processors/sequence.js +80 -15
package/package.json
CHANGED
package/src/builders/doc.js
CHANGED
|
@@ -116,6 +116,18 @@ function imageBlock({ src, url, alt = '', caption = '', direction, role, width,
|
|
|
116
116
|
return { type: 'ImageBlock', attrs }
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
// A document is STORED as the image row with `role: 'pdf'` — the node the
|
|
120
|
+
// markdown reader writes and the parser reads as a `document` — so that is what
|
|
121
|
+
// this emits. `url` (the parser's spelling) or `src` (this builder's) in.
|
|
122
|
+
function documentNode({ url, src, alt = '', caption = '', preview, author, description }) {
|
|
123
|
+
const attrs = { src: url || src, alt, role: 'pdf' }
|
|
124
|
+
if (caption) attrs.caption = caption
|
|
125
|
+
if (preview) attrs.preview = preview
|
|
126
|
+
if (author) attrs.author = author
|
|
127
|
+
if (description) attrs.description = description
|
|
128
|
+
return { type: 'image', attrs }
|
|
129
|
+
}
|
|
130
|
+
|
|
119
131
|
function iconNode({ src, url, svg, library, name, size, color }) {
|
|
120
132
|
// UniwebIcon supports multiple source types.
|
|
121
133
|
//
|
|
@@ -293,6 +305,12 @@ function buildGroupNodes(group, titleLevel = 1, options = {}) {
|
|
|
293
305
|
}
|
|
294
306
|
}
|
|
295
307
|
|
|
308
|
+
if (group.documents) {
|
|
309
|
+
for (const doc of group.documents) {
|
|
310
|
+
nodes.push(documentNode(doc))
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
296
314
|
if (group.lists) {
|
|
297
315
|
for (const list of group.lists) {
|
|
298
316
|
const node = bulletList(list)
|
package/src/processors/groups.js
CHANGED
|
@@ -15,6 +15,7 @@ function flattenGroup(group) {
|
|
|
15
15
|
icons: group.body.icons || [],
|
|
16
16
|
lists: group.body.lists || [],
|
|
17
17
|
videos: group.body.videos || [],
|
|
18
|
+
documents: group.body.documents || [],
|
|
18
19
|
insets: group.body.insets || [],
|
|
19
20
|
snippets: group.body.snippets || [],
|
|
20
21
|
data: group.body.data || {},
|
|
@@ -303,6 +304,7 @@ function processGroupContent(elements, options = {}) {
|
|
|
303
304
|
images: [],
|
|
304
305
|
icons: [],
|
|
305
306
|
videos: [],
|
|
307
|
+
documents: [],
|
|
306
308
|
insets: [],
|
|
307
309
|
snippets: [],
|
|
308
310
|
paragraphs: [],
|
|
@@ -387,6 +389,10 @@ function processGroupContent(elements, options = {}) {
|
|
|
387
389
|
body.videos.push(preserveProps);
|
|
388
390
|
break;
|
|
389
391
|
|
|
392
|
+
case "document":
|
|
393
|
+
body.documents.push(preserveProps);
|
|
394
|
+
break;
|
|
395
|
+
|
|
390
396
|
case "link":
|
|
391
397
|
if (element.children && Array.isArray(element.children))
|
|
392
398
|
processInlineElements(element.children, body);
|
|
@@ -520,19 +526,6 @@ function processGroupContent(elements, options = {}) {
|
|
|
520
526
|
});
|
|
521
527
|
break;
|
|
522
528
|
|
|
523
|
-
case "document-group":
|
|
524
|
-
// Map documents to links with role=document
|
|
525
|
-
element.documents.forEach(doc => {
|
|
526
|
-
body.links.push({
|
|
527
|
-
href: doc.href || doc.downloadUrl || '',
|
|
528
|
-
label: doc.title || '',
|
|
529
|
-
role: 'document',
|
|
530
|
-
download: true,
|
|
531
|
-
preview: doc.coverImg,
|
|
532
|
-
fileType: doc.fileType,
|
|
533
|
-
});
|
|
534
|
-
});
|
|
535
|
-
break;
|
|
536
529
|
}
|
|
537
530
|
}
|
|
538
531
|
}
|
|
@@ -588,6 +581,9 @@ function processInlineElements(children, body) {
|
|
|
588
581
|
// `{role=video}` belongs in videos[] for the same reason a hoisted
|
|
589
582
|
// one does.
|
|
590
583
|
body.videos.push(item.attrs);
|
|
584
|
+
} else if (item.type === "document") {
|
|
585
|
+
// And an inline `{role=pdf}` in documents[].
|
|
586
|
+
body.documents.push(item.attrs);
|
|
591
587
|
} else if (item.type === "link") {
|
|
592
588
|
// Handle inline links extracted from paragraph text nodes
|
|
593
589
|
body.links.push(item.attrs);
|
|
@@ -210,7 +210,19 @@ function createSequenceElement(node, options = {}) {
|
|
|
210
210
|
case "image":
|
|
211
211
|
// `role` decides which content array this lands in — the contract
|
|
212
212
|
// published in docs/reference/content-structure.md ("Media Assets"):
|
|
213
|
-
// icon → icons, video → videos, everything else →
|
|
213
|
+
// icon → icons, video → videos, pdf → documents, everything else →
|
|
214
|
+
// images.
|
|
215
|
+
//
|
|
216
|
+
// A document is stored as this node, as a video is, and delivered as
|
|
217
|
+
// a kind of its own (`parseMarkdownDocument`). Until 2026-09-29 it
|
|
218
|
+
// landed in `images`, drawn as an image of the file, while the
|
|
219
|
+
// runtime had guaranteed an empty `content.documents` since January.
|
|
220
|
+
if (attrs?.role === "pdf") {
|
|
221
|
+
return {
|
|
222
|
+
type: "document",
|
|
223
|
+
attrs: parseMarkdownDocument(attrs, options),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
214
226
|
//
|
|
215
227
|
// The video branch was missing until 2026-07-30: every
|
|
216
228
|
// `{role=video}` node ran parseImgBlock, which does not carry
|
|
@@ -315,13 +327,16 @@ function createSequenceElement(node, options = {}) {
|
|
|
315
327
|
};
|
|
316
328
|
|
|
317
329
|
case "document-group":
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
330
|
+
// The editor's older container: one `document` element per document
|
|
331
|
+
// it holds, so a document is one kind of content whichever node
|
|
332
|
+
// wrote it (`documentFromEditorNode`). Until 2026-09-29 this was a
|
|
333
|
+
// `document-group` element whose documents became `content.links`.
|
|
334
|
+
return (node.content || [])
|
|
335
|
+
.filter((c) => c.type === "document")
|
|
336
|
+
.map((doc) => ({
|
|
337
|
+
type: "document",
|
|
338
|
+
attrs: documentFromEditorNode(doc.attrs),
|
|
339
|
+
}));
|
|
325
340
|
|
|
326
341
|
// The editor renamed this node to `StructuredContent` (2026-07-31,
|
|
327
342
|
// editor-internal). Both are accepted: the new name is what the editor
|
|
@@ -613,13 +628,15 @@ function processInlineElements(content, options = {}) {
|
|
|
613
628
|
// image silently deleted. groups.js has carried the receiving
|
|
614
629
|
// branch (and a comment describing exactly this failure) since it
|
|
615
630
|
// was written, but nothing ever emitted the item it reads.
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
attrs:
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
}
|
|
631
|
+
// The role partitions it as the block case does.
|
|
632
|
+
const role = item.attrs?.role;
|
|
633
|
+
if (role === "video") {
|
|
634
|
+
items.push({ type: "video", attrs: parseMarkdownVideo(item.attrs, options) });
|
|
635
|
+
} else if (role === "pdf") {
|
|
636
|
+
items.push({ type: "document", attrs: parseMarkdownDocument(item.attrs, options) });
|
|
637
|
+
} else {
|
|
638
|
+
items.push({ type: "image", attrs: parseImgBlock(item.attrs || {}, options) });
|
|
639
|
+
}
|
|
623
640
|
} else if (item.type === "math-inline" || item.type === "math_inline") {
|
|
624
641
|
items.push(item);
|
|
625
642
|
} else if (item.type === "inset_placeholder") {
|
|
@@ -802,6 +819,54 @@ function parseCardBlock(itemAttrs) {
|
|
|
802
819
|
};
|
|
803
820
|
}
|
|
804
821
|
|
|
822
|
+
// A document authored in markdown: `{role=pdf …}`.
|
|
823
|
+
//
|
|
824
|
+
// Read through `parseImgBlock`, which resolves the address and the preview's the
|
|
825
|
+
// way every image's are, and then narrowed to what a document IS: where the file
|
|
826
|
+
// is, what it is called, and the resource's own metadata. The image layout keys
|
|
827
|
+
// (direction, size, credit) describe a picture, not a file, and are left out.
|
|
828
|
+
function parseMarkdownDocument(itemAttrs, options) {
|
|
829
|
+
const { url, alt, caption, role, href, target, id, preview, author, description } =
|
|
830
|
+
parseImgBlock(itemAttrs || {}, options);
|
|
831
|
+
return {
|
|
832
|
+
url,
|
|
833
|
+
alt,
|
|
834
|
+
caption,
|
|
835
|
+
role,
|
|
836
|
+
...(href && { href }),
|
|
837
|
+
...(target && { target }),
|
|
838
|
+
...(id && { id }),
|
|
839
|
+
...(preview && { preview }),
|
|
840
|
+
...(author && { author }),
|
|
841
|
+
...(description && { description }),
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// The editor's older `document` node — its `title`, `coverImg` and `src` (or
|
|
846
|
+
// asset identifier) — read into the same shape a markdown document has.
|
|
847
|
+
function documentFromEditorNode(itemAttrs) {
|
|
848
|
+
const {
|
|
849
|
+
href,
|
|
850
|
+
downloadUrl,
|
|
851
|
+
title = "",
|
|
852
|
+
alt = "",
|
|
853
|
+
coverImg,
|
|
854
|
+
author,
|
|
855
|
+
description,
|
|
856
|
+
fileType,
|
|
857
|
+
} = parseDocumentBlock(itemAttrs || {});
|
|
858
|
+
return {
|
|
859
|
+
url: href || downloadUrl || "",
|
|
860
|
+
alt: alt || title,
|
|
861
|
+
caption: title,
|
|
862
|
+
role: "pdf",
|
|
863
|
+
...(coverImg && { preview: coverImg }),
|
|
864
|
+
...(author && { author }),
|
|
865
|
+
...(description && { description }),
|
|
866
|
+
...(fileType && { fileType }),
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
|
|
805
870
|
function parseDocumentBlock(itemAttrs) {
|
|
806
871
|
const { src, info = {}, coverImg = null, ...others } = itemAttrs;
|
|
807
872
|
|