@uniweb/semantic-parser 1.1.8 → 1.1.10
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/AGENTS.md +5 -2
- package/README.md +47 -17
- package/package.json +1 -1
- package/src/processors/groups.js +14 -13
- package/src/processors/sequence.js +12 -2
package/AGENTS.md
CHANGED
|
@@ -95,12 +95,15 @@ The `data` object holds all structured content:
|
|
|
95
95
|
data: {
|
|
96
96
|
"nav-links": [...], // From ```yaml:nav-links
|
|
97
97
|
"config": {...}, // From ```yaml:config
|
|
98
|
-
"
|
|
98
|
+
"stats": [...], // From FormBlock (activeSchemaId='stats') or ```yaml:stats
|
|
99
99
|
"person": [...], // From card-group with cardType="person"
|
|
100
100
|
"event": [...] // From card-group with cardType="event"
|
|
101
101
|
}
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
+
FormBlock data is routed to `data[activeSchemaId]`. A legacy
|
|
105
|
+
FormBlock without a `schemaId` still lands at `data.form`.
|
|
106
|
+
|
|
104
107
|
### Main Content Identification
|
|
105
108
|
|
|
106
109
|
The `identifyMainContent()` function (src/processors/groups.js:282) determines if the first group should be treated as main content:
|
|
@@ -121,7 +124,7 @@ The sequence processor identifies several special element types by inspecting pa
|
|
|
121
124
|
|
|
122
125
|
Editor-specific nodes are mapped to standard entities:
|
|
123
126
|
- `button` node → `links[]` with `role: "button"` and `variant` attribute
|
|
124
|
-
- `FormBlock` → `data.form`
|
|
127
|
+
- `FormBlock` → `data[activeSchemaId]` (fallback: `data.form` when no schemaId)
|
|
125
128
|
- `card-group` → `data[cardType]` arrays (e.g., `data.person`, `data.event`)
|
|
126
129
|
- `document-group` → `links[]` with `role: "document"` and `download: true`
|
|
127
130
|
|
package/README.md
CHANGED
|
@@ -77,13 +77,18 @@ result = {
|
|
|
77
77
|
icons: [],
|
|
78
78
|
lists: [],
|
|
79
79
|
quotes: [],
|
|
80
|
+
insets: [], // Inline @Component references — { refId }
|
|
81
|
+
snippets: [], // Fenced code blocks — { language, code }
|
|
80
82
|
data: {}, // Structured data (tagged data blocks, forms, cards)
|
|
81
83
|
headings: [], // Headings after subtitle, in document order
|
|
82
84
|
|
|
83
85
|
// Additional content groups (from headings after content)
|
|
86
|
+
// Each item has the SAME flat structure as the top level — title,
|
|
87
|
+
// pretitle, subtitle, paragraphs, links, images, icons, lists,
|
|
88
|
+
// snippets, data, etc. Use this for cards, features, FAQ entries.
|
|
84
89
|
items: [
|
|
85
|
-
{ title: "Feature 1", paragraphs: [...], links: [...] },
|
|
86
|
-
{ title: "Feature 2", paragraphs: [...], links: [...] }
|
|
90
|
+
{ title: "Feature 1", paragraphs: [...], links: [...], /* ...full shape */ },
|
|
91
|
+
{ title: "Feature 2", paragraphs: [...], links: [...], /* ...full shape */ }
|
|
87
92
|
],
|
|
88
93
|
|
|
89
94
|
// Ordered sequence for document-order rendering
|
|
@@ -144,28 +149,53 @@ sequence.forEach(element => {
|
|
|
144
149
|
|
|
145
150
|
## Content Grouping
|
|
146
151
|
|
|
147
|
-
The parser
|
|
152
|
+
The parser interprets heading levels **relatively**, not absolutely. There is no requirement that a section start with H1 — what matters is the relationship *between* headings (which is more important, which is deeper, which comes after body content). Starting at H1 is natural and recommended, but the same structure works starting at H2 or H3 — every rule below is about relative levels.
|
|
148
153
|
|
|
149
|
-
###
|
|
154
|
+
### How groups are formed
|
|
150
155
|
|
|
151
|
-
|
|
152
|
-
- A heading follows content
|
|
153
|
-
- Multiple H1s appear (no main content created)
|
|
154
|
-
- The heading level indicates a new section
|
|
156
|
+
A new group is started whenever a heading appears after non-heading content (paragraphs, images, links, lists, etc.). A horizontal rule (`---`) explicitly closes the current group.
|
|
155
157
|
|
|
156
|
-
|
|
158
|
+
When a group begins with one or more headings, the parser consumes a contiguous **heading block** following these rules:
|
|
157
159
|
|
|
158
|
-
**
|
|
159
|
-
-
|
|
160
|
-
-
|
|
161
|
-
- H6 before H5 → pretitle ✅
|
|
162
|
-
- H4 before H2 → pretitle ✅
|
|
160
|
+
1. **Adjacent deeper** — the next heading is exactly one level deeper (e.g., H1→H2, H2→H3). It becomes the `subtitle`. Skipping levels (H1→H3) breaks the block; the deeper heading starts a new group as an item.
|
|
161
|
+
2. **Pretitle promotion** — if the very first heading is followed by a *more important* (lower-numbered) heading, the first one becomes `pretitle` and the next becomes `title`. This only applies at the start of the block.
|
|
162
|
+
3. **Same-level continuation** — consecutive headings at the same level merge into an array (multi-line title or subtitle). This stops once a deeper level has been reached: after going deeper, a same-level heading starts a new group instead of merging.
|
|
163
163
|
|
|
164
|
-
|
|
164
|
+
Anything else (going back up after going deeper, or any other gap) breaks the block.
|
|
165
165
|
|
|
166
|
-
###
|
|
166
|
+
### Main content vs items
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
Once groups are split, the parser decides whether the first group is the section's main content or whether *all* groups should be treated as items. The first group becomes main content when **either**:
|
|
169
|
+
|
|
170
|
+
- It has no heading at all (body content appearing before the first heading), **or**
|
|
171
|
+
- Its heading level is more important (lower-numbered) than the second group's heading level.
|
|
172
|
+
|
|
173
|
+
Otherwise, every group becomes an item and the top-level header fields stay empty. For example, a document that starts with `## Card A` followed by `## Card B` produces two items and no main title — because the first group is not more important than the second. The same is true for two H1s in a row, or two H3s; the rule is about *relative* importance, not a specific level.
|
|
174
|
+
|
|
175
|
+
This is the mechanism that creates repeating content groups (cards, features, FAQ entries) without any extra configuration.
|
|
176
|
+
|
|
177
|
+
### Pretitle detection
|
|
178
|
+
|
|
179
|
+
A heading followed by a more important heading at the start of a group is detected as `pretitle`:
|
|
180
|
+
|
|
181
|
+
- H3 before H1 → pretitle
|
|
182
|
+
- H2 before H1 → pretitle
|
|
183
|
+
- H6 before H5 → pretitle
|
|
184
|
+
- H4 before H2 → pretitle
|
|
185
|
+
|
|
186
|
+
Pretitle is detected only between the first two headings of a heading block (before the title is set).
|
|
187
|
+
|
|
188
|
+
### Banner image
|
|
189
|
+
|
|
190
|
+
If a section's very first element is an image (or an image followed immediately by a heading), the parser keeps the image and the following heading in the same group rather than splitting them. This lets a leading image act as a banner for the section's title without becoming an unrelated item.
|
|
191
|
+
|
|
192
|
+
### Divider-based grouping
|
|
193
|
+
|
|
194
|
+
A horizontal rule (`---`) explicitly closes the current group and starts a new one. Dividers compose with the heading rules above — they don't replace them — and are useful in three situations:
|
|
195
|
+
|
|
196
|
+
- **Resolving ambiguity.** When the heading rules would group content one way but you want it grouped another way. Most common case: forcing items without a subtitle. Without a divider, `# Our Stats` followed by `## 15,000+` makes `## 15,000+` the subtitle (one level deeper). Adding `---` between them closes the title group so `## 15,000+` becomes `items[0].title` instead.
|
|
197
|
+
- **Forcing splits within same-level runs.** Same-level headings that would otherwise merge into a multi-line title/subtitle become separate groups when separated by a divider.
|
|
198
|
+
- **Personal preference.** Even when the heading rules would already produce the desired structure, authors can use `---` as an explicit visual separator between groups. It never changes a structure that's already correct — it just makes the boundary obvious in the markdown source.
|
|
169
199
|
|
|
170
200
|
## Text Formatting
|
|
171
201
|
|
package/package.json
CHANGED
package/src/processors/groups.js
CHANGED
|
@@ -364,17 +364,10 @@ function processGroupContent(elements) {
|
|
|
364
364
|
break;
|
|
365
365
|
|
|
366
366
|
case "codeBlock":
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
} else {
|
|
372
|
-
// Untagged code block — collect as a snippet
|
|
373
|
-
body.snippets.push({
|
|
374
|
-
language: element.attrs?.language || '',
|
|
375
|
-
code: typeof element.text === 'string' ? element.text : '',
|
|
376
|
-
});
|
|
377
|
-
}
|
|
367
|
+
body.snippets.push({
|
|
368
|
+
language: element.attrs?.language || '',
|
|
369
|
+
code: typeof element.text === 'string' ? element.text : '',
|
|
370
|
+
});
|
|
378
371
|
break;
|
|
379
372
|
|
|
380
373
|
case "inset":
|
|
@@ -382,8 +375,16 @@ function processGroupContent(elements) {
|
|
|
382
375
|
break;
|
|
383
376
|
|
|
384
377
|
case "form":
|
|
385
|
-
//
|
|
386
|
-
|
|
378
|
+
// Route FormBlock data by the schema id it targets, so it
|
|
379
|
+
// shares a namespace with tagged markdown data blocks
|
|
380
|
+
// (```yaml:<id>``` also lands at data[<id>]). When no
|
|
381
|
+
// schemaId is present (legacy content), fall back to the
|
|
382
|
+
// literal key "form" — eventual deprecation.
|
|
383
|
+
{
|
|
384
|
+
const payload = element.data || element.attrs;
|
|
385
|
+
const key = element.schemaId || "form";
|
|
386
|
+
body.data[key] = payload;
|
|
387
|
+
}
|
|
387
388
|
break;
|
|
388
389
|
|
|
389
390
|
case "card-group":
|
|
@@ -145,13 +145,22 @@ function createSequenceElement(node, options = {}) {
|
|
|
145
145
|
tag: attrs.tag,
|
|
146
146
|
};
|
|
147
147
|
|
|
148
|
-
case "codeBlock":
|
|
148
|
+
case "codeBlock": {
|
|
149
149
|
const codeText = getTextContent(content, options);
|
|
150
|
+
// Tagged code blocks are semantically data blocks, not code
|
|
151
|
+
if (attrs?.tag) {
|
|
152
|
+
return {
|
|
153
|
+
type: "dataBlock",
|
|
154
|
+
tag: attrs.tag,
|
|
155
|
+
data: getCodeBlockData(codeText, attrs),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
150
158
|
return {
|
|
151
159
|
type: "codeBlock",
|
|
152
|
-
text:
|
|
160
|
+
text: codeText,
|
|
153
161
|
attrs,
|
|
154
162
|
};
|
|
163
|
+
}
|
|
155
164
|
|
|
156
165
|
case "inset_placeholder":
|
|
157
166
|
return {
|
|
@@ -232,6 +241,7 @@ function createSequenceElement(node, options = {}) {
|
|
|
232
241
|
|
|
233
242
|
return {
|
|
234
243
|
type: "form",
|
|
244
|
+
schemaId: attrs?.activeSchemaId || null,
|
|
235
245
|
data: formData,
|
|
236
246
|
attrs,
|
|
237
247
|
};
|