boxwood 2.17.0 → 2.18.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/.claude/settings.local.json +14 -0
- package/package.json +1 -1
- package/ui/markdown/index.js +3 -3
- package/ui/prose/README.md +134 -16
- package/ui/prose/index.js +36 -5
- package/ui/prose/utilities/processConditionals.js +1 -1
- package/ui/prose/utilities/processLoops.js +70 -9
- package/ui/prose/utilities/protectCode.js +111 -0
- package/ui/prose/utilities/stripComments.js +19 -0
- package/ui/prose/utilities/validate.js +304 -0
- package/ui/{prose/utilities → utilities}/parseCustomTag.js +4 -3
- package/ui/{prose/utilities → utilities}/replaceVariables.js +180 -3
- package/ui/markdown/utilities/parseCustomTag.js +0 -218
- package/ui/markdown/utilities/replaceVariables.js +0 -115
- package/ui/prose/utilities/brackets.js +0 -21
- package/ui/prose/utilities/convertNodes.js +0 -193
- package/ui/prose/utilities/format.js +0 -184
- package/ui/prose/utilities/params.js +0 -29
- package/ui/prose/utilities/parseBlock.js +0 -284
- /package/ui/{markdown/utilities → utilities}/brackets.js +0 -0
- /package/ui/{markdown/utilities → utilities}/convertNodes.js +0 -0
- /package/ui/{markdown/utilities → utilities}/format.js +0 -0
- /package/ui/{markdown/utilities → utilities}/params.js +0 -0
- /package/ui/{markdown/utilities → utilities}/parseBlock.js +0 -0
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parse a custom component tag from markdown
|
|
3
|
-
* Supports both <Component attr="value"> and <Component attr={variable}>
|
|
4
|
-
* @param {string} line - The line to parse
|
|
5
|
-
* @param {Object} customComponents - Map of component names to functions
|
|
6
|
-
* @returns {Object|null} - Parsed tag info or null if not a custom tag
|
|
7
|
-
*/
|
|
8
|
-
function parseCustomTag(line, customComponents) {
|
|
9
|
-
if (!customComponents || typeof customComponents !== "object") {
|
|
10
|
-
return null
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const trimmed = line.trim()
|
|
14
|
-
|
|
15
|
-
// Check for single-line tag: <tag>content</tag> or <tag attr="value">content</tag>
|
|
16
|
-
const singleLineMatch = trimmed.match(
|
|
17
|
-
/^<([A-Za-z][A-Za-z0-9-]*)(\s+[^>]*)?>(.+?)<\/\1>\s*$/,
|
|
18
|
-
)
|
|
19
|
-
if (singleLineMatch) {
|
|
20
|
-
const [, tagName, attributesStr, content] = singleLineMatch
|
|
21
|
-
const component = customComponents[tagName]
|
|
22
|
-
if (component) {
|
|
23
|
-
const attributes = parseAttributes(attributesStr || "")
|
|
24
|
-
return {
|
|
25
|
-
type: "custom-component-single-line",
|
|
26
|
-
tagName,
|
|
27
|
-
component,
|
|
28
|
-
attributes,
|
|
29
|
-
content,
|
|
30
|
-
selfClosing: false,
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Check for self-closing tag: <ComponentName ... /> (space before / is optional)
|
|
36
|
-
const selfClosingMatch = trimmed.match(
|
|
37
|
-
/^<([A-Za-z][A-Za-z0-9-]*)(\s+[^>]*)?\s*\/>\s*$/,
|
|
38
|
-
)
|
|
39
|
-
if (selfClosingMatch) {
|
|
40
|
-
const [, tagName, attributesStr] = selfClosingMatch
|
|
41
|
-
const component = customComponents[tagName]
|
|
42
|
-
if (component) {
|
|
43
|
-
const attributes = parseAttributes(attributesStr || "")
|
|
44
|
-
return {
|
|
45
|
-
type: "custom-component",
|
|
46
|
-
tagName,
|
|
47
|
-
component,
|
|
48
|
-
attributes,
|
|
49
|
-
selfClosing: true,
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Check for opening tag: <ComponentName ...>
|
|
55
|
-
const openTagMatch = trimmed.match(
|
|
56
|
-
/^<([A-Za-z][A-Za-z0-9-]*)(\s+[^>]*)?>\s*$/,
|
|
57
|
-
)
|
|
58
|
-
if (openTagMatch) {
|
|
59
|
-
const [, tagName, attributesStr] = openTagMatch
|
|
60
|
-
const component = customComponents[tagName]
|
|
61
|
-
if (component) {
|
|
62
|
-
const attributes = parseAttributes(attributesStr || "")
|
|
63
|
-
return {
|
|
64
|
-
type: "custom-component-open",
|
|
65
|
-
tagName,
|
|
66
|
-
component,
|
|
67
|
-
attributes,
|
|
68
|
-
selfClosing: false,
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Check for closing tag: </ComponentName>
|
|
74
|
-
const closeTagMatch = trimmed.match(/^<\/([A-Za-z][A-Za-z0-9-]*)>\s*$/)
|
|
75
|
-
if (closeTagMatch) {
|
|
76
|
-
const [, tagName] = closeTagMatch
|
|
77
|
-
const component = customComponents[tagName]
|
|
78
|
-
if (component) {
|
|
79
|
-
return {
|
|
80
|
-
type: "custom-component-close",
|
|
81
|
-
tagName,
|
|
82
|
-
component,
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return null
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Parse attributes from a tag string
|
|
92
|
-
* Supports: attr="value", attr='value', attr={variable}, attr
|
|
93
|
-
* @param {string} attributesStr - The attributes string
|
|
94
|
-
* @returns {Object} - Parsed attributes
|
|
95
|
-
*/
|
|
96
|
-
function parseAttributes(attributesStr) {
|
|
97
|
-
const attributes = {}
|
|
98
|
-
if (!attributesStr || !attributesStr.trim()) {
|
|
99
|
-
return attributes
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const str = attributesStr.trim()
|
|
103
|
-
let i = 0
|
|
104
|
-
|
|
105
|
-
while (i < str.length) {
|
|
106
|
-
// Skip whitespace
|
|
107
|
-
while (i < str.length && /\s/.test(str[i])) {
|
|
108
|
-
i++
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (i >= str.length) break
|
|
112
|
-
|
|
113
|
-
// Parse attribute name
|
|
114
|
-
let nameStart = i
|
|
115
|
-
while (i < str.length && /[a-zA-Z0-9-_:]/.test(str[i])) {
|
|
116
|
-
i++
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const name = str.substring(nameStart, i)
|
|
120
|
-
if (!name) break
|
|
121
|
-
|
|
122
|
-
// Skip whitespace after name
|
|
123
|
-
while (i < str.length && /\s/.test(str[i])) {
|
|
124
|
-
i++
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Check for =
|
|
128
|
-
if (i >= str.length || str[i] !== "=") {
|
|
129
|
-
// Boolean attribute
|
|
130
|
-
attributes[name] = true
|
|
131
|
-
continue
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
i++ // Skip =
|
|
135
|
-
|
|
136
|
-
// Skip whitespace after =
|
|
137
|
-
while (i < str.length && /\s/.test(str[i])) {
|
|
138
|
-
i++
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (i >= str.length) break
|
|
142
|
-
|
|
143
|
-
// Parse value
|
|
144
|
-
const quote = str[i]
|
|
145
|
-
|
|
146
|
-
if (quote === '"' || quote === "'") {
|
|
147
|
-
// Quoted string
|
|
148
|
-
i++ // Skip opening quote
|
|
149
|
-
let value = ""
|
|
150
|
-
while (i < str.length && str[i] !== quote) {
|
|
151
|
-
if (str[i] === "\\" && i + 1 < str.length) {
|
|
152
|
-
// Escaped character - add the next character literally
|
|
153
|
-
value += str[i + 1]
|
|
154
|
-
i += 2
|
|
155
|
-
} else {
|
|
156
|
-
value += str[i]
|
|
157
|
-
i++
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
attributes[name] = value
|
|
161
|
-
if (i < str.length) i++ // Skip closing quote
|
|
162
|
-
} else if (str[i] === "{") {
|
|
163
|
-
// Variable reference
|
|
164
|
-
i++ // Skip {
|
|
165
|
-
const valueStart = i
|
|
166
|
-
while (i < str.length && str[i] !== "}") {
|
|
167
|
-
i++
|
|
168
|
-
}
|
|
169
|
-
const variableName = str.substring(valueStart, i).trim()
|
|
170
|
-
attributes[name] = { __variable: variableName }
|
|
171
|
-
if (i < str.length) i++ // Skip }
|
|
172
|
-
} else {
|
|
173
|
-
// Unquoted value (until space or end)
|
|
174
|
-
const valueStart = i
|
|
175
|
-
while (i < str.length && !/\s/.test(str[i])) {
|
|
176
|
-
i++
|
|
177
|
-
}
|
|
178
|
-
const value = str.substring(valueStart, i)
|
|
179
|
-
attributes[name] = value
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return attributes
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Resolve attributes by replacing variable references with actual values
|
|
188
|
-
* @param {Object} attributes - Attributes with possible variable references
|
|
189
|
-
* @param {Object} data - Data object containing variable values
|
|
190
|
-
* @returns {Object} - Resolved attributes
|
|
191
|
-
*/
|
|
192
|
-
function resolveAttributes(attributes, data) {
|
|
193
|
-
if (!attributes || typeof attributes !== "object") {
|
|
194
|
-
return {}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const resolved = {}
|
|
198
|
-
for (const [key, value] of Object.entries(attributes)) {
|
|
199
|
-
if (value && typeof value === "object" && value.__variable) {
|
|
200
|
-
// Resolve variable
|
|
201
|
-
const variableName = value.__variable
|
|
202
|
-
resolved[key] =
|
|
203
|
-
data && data[variableName] !== undefined
|
|
204
|
-
? data[variableName]
|
|
205
|
-
: undefined
|
|
206
|
-
} else {
|
|
207
|
-
resolved[key] = value
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return resolved
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
module.exports = {
|
|
215
|
-
parseCustomTag,
|
|
216
|
-
parseAttributes,
|
|
217
|
-
resolveAttributes,
|
|
218
|
-
}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Resolve a path like "images[0].src" or "user.name" from a data object
|
|
3
|
-
* @param {Object} data - The data object to resolve the path from
|
|
4
|
-
* @param {string} path - The path to resolve (e.g., "images[0].src", "user.name")
|
|
5
|
-
* @returns {*} - The resolved value or undefined
|
|
6
|
-
*/
|
|
7
|
-
function resolvePath(data, path) {
|
|
8
|
-
// Handle simple variable names (backwards compatibility)
|
|
9
|
-
if (!/[.\[]/.test(path)) {
|
|
10
|
-
return data[path]
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Split the path into parts, handling both dot notation and bracket notation
|
|
14
|
-
// e.g., "images[0].src" -> ["images", "0", "src"]
|
|
15
|
-
const parts = path
|
|
16
|
-
.replace(/\[(\d+)\]/g, ".$1") // Convert [0] to .0
|
|
17
|
-
.split(".")
|
|
18
|
-
.filter(Boolean)
|
|
19
|
-
|
|
20
|
-
let current = data
|
|
21
|
-
for (const part of parts) {
|
|
22
|
-
if (current === null || current === undefined) {
|
|
23
|
-
return undefined
|
|
24
|
-
}
|
|
25
|
-
current = current[part]
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return current
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Replace {variableName} placeholders in text with actual values from data
|
|
33
|
-
* Supports:
|
|
34
|
-
* - Simple variables: {name}
|
|
35
|
-
* - Array indexing: {images[0]}
|
|
36
|
-
* - Property access: {user.name}
|
|
37
|
-
* - Combined: {images[0].src}
|
|
38
|
-
* @param {string} text - Text containing variable placeholders
|
|
39
|
-
* @param {Object} data - Data object with variable values
|
|
40
|
-
* @returns {string|Array} - Text with variables replaced, or array if mixed content
|
|
41
|
-
*/
|
|
42
|
-
function replaceVariables(text, data) {
|
|
43
|
-
if (!text || typeof text !== "string") {
|
|
44
|
-
return text
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (!data || typeof data !== "object") {
|
|
48
|
-
return text
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Check if text contains any variables
|
|
52
|
-
if (!text.includes("{") || !text.includes("}")) {
|
|
53
|
-
return text
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const result = []
|
|
57
|
-
let i = 0
|
|
58
|
-
let lastIndex = 0
|
|
59
|
-
|
|
60
|
-
while (i < text.length) {
|
|
61
|
-
if (text[i] === "\\" && text[i + 1] === "{") {
|
|
62
|
-
// Escaped opening brace
|
|
63
|
-
result.push(text.substring(lastIndex, i))
|
|
64
|
-
result.push("{")
|
|
65
|
-
i += 2
|
|
66
|
-
lastIndex = i
|
|
67
|
-
continue
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (text[i] === "{") {
|
|
71
|
-
const closeIndex = text.indexOf("}", i + 1)
|
|
72
|
-
|
|
73
|
-
if (closeIndex !== -1) {
|
|
74
|
-
// Found a variable placeholder
|
|
75
|
-
const variablePath = text.substring(i + 1, closeIndex).trim()
|
|
76
|
-
|
|
77
|
-
if (variablePath) {
|
|
78
|
-
// Add text before the variable
|
|
79
|
-
if (i > lastIndex) {
|
|
80
|
-
result.push(text.substring(lastIndex, i))
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Resolve the variable value (supports paths like "images[0].src")
|
|
84
|
-
const value = resolvePath(data, variablePath)
|
|
85
|
-
if (value !== undefined && value !== null) {
|
|
86
|
-
result.push(String(value))
|
|
87
|
-
} else {
|
|
88
|
-
// Variable not found, keep the placeholder
|
|
89
|
-
result.push(text.substring(i, closeIndex + 1))
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
i = closeIndex + 1
|
|
93
|
-
lastIndex = i
|
|
94
|
-
continue
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
i++
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Add remaining text
|
|
103
|
-
if (lastIndex < text.length) {
|
|
104
|
-
result.push(text.substring(lastIndex))
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// If no substitutions were made, return original text
|
|
108
|
-
if (result.length === 0) {
|
|
109
|
-
return text
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return result.join("")
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
module.exports = { replaceVariables }
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// Find the matching closing bracket, accounting for nested brackets
|
|
2
|
-
function findMatchingBracket(text, startPos) {
|
|
3
|
-
let depth = 1
|
|
4
|
-
let i = startPos + 1
|
|
5
|
-
|
|
6
|
-
while (i < text.length && depth > 0) {
|
|
7
|
-
if (text[i] === "[" && text[i - 1] !== "\\") {
|
|
8
|
-
depth++
|
|
9
|
-
} else if (text[i] === "]" && text[i - 1] !== "\\") {
|
|
10
|
-
depth--
|
|
11
|
-
if (depth === 0) {
|
|
12
|
-
return i
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
i++
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return -1
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
module.exports = { findMatchingBracket }
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
const { resolveAttributes } = require("./parseCustomTag")
|
|
2
|
-
const { replaceVariables } = require("./replaceVariables")
|
|
3
|
-
const { format } = require("./format")
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Recursively parses nested lists with proper indentation handling
|
|
7
|
-
*/
|
|
8
|
-
function parseList(
|
|
9
|
-
items,
|
|
10
|
-
startIndex,
|
|
11
|
-
parentIndent,
|
|
12
|
-
htmlParams,
|
|
13
|
-
data,
|
|
14
|
-
allComponents,
|
|
15
|
-
) {
|
|
16
|
-
const list = []
|
|
17
|
-
let currentIndex = startIndex
|
|
18
|
-
const parentListType = items[startIndex].list
|
|
19
|
-
|
|
20
|
-
while (currentIndex < items.length) {
|
|
21
|
-
const item = items[currentIndex]
|
|
22
|
-
|
|
23
|
-
// Stop conditions
|
|
24
|
-
if (item.type !== "li" || item.indent < parentIndent) {
|
|
25
|
-
break
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (item.indent === parentIndent && item.list !== parentListType) {
|
|
29
|
-
break
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (item.indent > parentIndent) {
|
|
33
|
-
break
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Process current list item
|
|
37
|
-
const processedContent = replaceVariables(item.content, data)
|
|
38
|
-
const content = [format(processedContent, allComponents)]
|
|
39
|
-
currentIndex++
|
|
40
|
-
|
|
41
|
-
// Check for nested lists
|
|
42
|
-
const nextItem = items[currentIndex]
|
|
43
|
-
if (nextItem?.type === "li" && nextItem.indent > item.indent) {
|
|
44
|
-
const nestedResult = parseList(
|
|
45
|
-
items,
|
|
46
|
-
currentIndex,
|
|
47
|
-
nextItem.indent,
|
|
48
|
-
htmlParams,
|
|
49
|
-
data,
|
|
50
|
-
allComponents,
|
|
51
|
-
)
|
|
52
|
-
content.push(nestedResult.list)
|
|
53
|
-
currentIndex = nestedResult.nextIndex
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
list.push(allComponents.li(htmlParams, content))
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const listElement =
|
|
60
|
-
parentListType === "ul"
|
|
61
|
-
? allComponents.ul(htmlParams, list)
|
|
62
|
-
: allComponents.ol(htmlParams, list)
|
|
63
|
-
|
|
64
|
-
return { list: listElement, nextIndex: currentIndex }
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Processes a custom component item into a node
|
|
69
|
-
*/
|
|
70
|
-
function processCustomComponentItem(item, params, data, markdownRenderer) {
|
|
71
|
-
const resolvedAttrs = resolveAttributes(item.attributes, data)
|
|
72
|
-
|
|
73
|
-
if (item.selfClosing) {
|
|
74
|
-
return item.component(resolvedAttrs)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (item.singleLine) {
|
|
78
|
-
return item.component(resolvedAttrs, item.content)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Multi-line component with children - recursively process markdown
|
|
82
|
-
const childContent = item.content
|
|
83
|
-
? markdownRenderer(params, item.content)
|
|
84
|
-
: []
|
|
85
|
-
return item.component(resolvedAttrs, childContent)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Processes blockquote items into a single blockquote node
|
|
90
|
-
*/
|
|
91
|
-
function processBlockquotes(
|
|
92
|
-
items,
|
|
93
|
-
startIndex,
|
|
94
|
-
htmlParams,
|
|
95
|
-
data,
|
|
96
|
-
allComponents,
|
|
97
|
-
) {
|
|
98
|
-
const lines = []
|
|
99
|
-
let i = startIndex
|
|
100
|
-
|
|
101
|
-
while (i < items.length && items[i].type === "blockquote") {
|
|
102
|
-
lines.push(items[i].content)
|
|
103
|
-
i++
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const content = replaceVariables(lines.join("\n"), data)
|
|
107
|
-
const blockquote = allComponents.blockquote(
|
|
108
|
-
htmlParams,
|
|
109
|
-
allComponents.p(htmlParams, format(content, allComponents)),
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
return { node: blockquote, nextIndex: i }
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Converts parsed items into final node tree
|
|
117
|
-
*/
|
|
118
|
-
function convertItemsToNodes(
|
|
119
|
-
items,
|
|
120
|
-
params,
|
|
121
|
-
htmlParams,
|
|
122
|
-
data,
|
|
123
|
-
allComponents,
|
|
124
|
-
markdownRenderer,
|
|
125
|
-
) {
|
|
126
|
-
const result = []
|
|
127
|
-
let i = 0
|
|
128
|
-
|
|
129
|
-
while (i < items.length) {
|
|
130
|
-
const item = items[i]
|
|
131
|
-
|
|
132
|
-
if (item.type === "custom-component") {
|
|
133
|
-
result.push(
|
|
134
|
-
processCustomComponentItem(item, params, data, markdownRenderer),
|
|
135
|
-
)
|
|
136
|
-
i++
|
|
137
|
-
} else if (item.type === "li") {
|
|
138
|
-
const listResult = parseList(
|
|
139
|
-
items,
|
|
140
|
-
i,
|
|
141
|
-
item.indent,
|
|
142
|
-
htmlParams,
|
|
143
|
-
data,
|
|
144
|
-
allComponents,
|
|
145
|
-
)
|
|
146
|
-
result.push(listResult.list)
|
|
147
|
-
i = listResult.nextIndex
|
|
148
|
-
} else if (item.type === "blockquote") {
|
|
149
|
-
const { node, nextIndex } = processBlockquotes(
|
|
150
|
-
items,
|
|
151
|
-
i,
|
|
152
|
-
htmlParams,
|
|
153
|
-
data,
|
|
154
|
-
allComponents,
|
|
155
|
-
)
|
|
156
|
-
result.push(node)
|
|
157
|
-
i = nextIndex
|
|
158
|
-
} else if (item.type === "hr") {
|
|
159
|
-
result.push(allComponents.hr(htmlParams))
|
|
160
|
-
i++
|
|
161
|
-
} else if (item.type === "pre") {
|
|
162
|
-
// Code blocks - wrap in <pre><code>
|
|
163
|
-
const codeParams = item.language
|
|
164
|
-
? { class: `language-${item.language}` }
|
|
165
|
-
: {}
|
|
166
|
-
result.push(
|
|
167
|
-
allComponents.pre(
|
|
168
|
-
htmlParams,
|
|
169
|
-
allComponents.code(codeParams, item.content),
|
|
170
|
-
),
|
|
171
|
-
)
|
|
172
|
-
i++
|
|
173
|
-
} else {
|
|
174
|
-
// Regular block elements (h1-h6, p, etc.)
|
|
175
|
-
const { type, content } = item
|
|
176
|
-
const Component = allComponents[type] || allComponents.p
|
|
177
|
-
const processedContent = replaceVariables(content, data)
|
|
178
|
-
result.push(
|
|
179
|
-
Component(htmlParams, format(processedContent, allComponents)),
|
|
180
|
-
)
|
|
181
|
-
i++
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return result
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
module.exports = {
|
|
189
|
-
parseList,
|
|
190
|
-
processCustomComponentItem,
|
|
191
|
-
processBlockquotes,
|
|
192
|
-
convertItemsToNodes,
|
|
193
|
-
}
|