@domenico-esposito/react-native-markdown-editor 0.1.1
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/.eslintrc.js +5 -0
- package/README.md +265 -0
- package/build/MarkdownRenderer.d.ts +12 -0
- package/build/MarkdownRenderer.d.ts.map +1 -0
- package/build/MarkdownRenderer.js +165 -0
- package/build/MarkdownRenderer.js.map +1 -0
- package/build/MarkdownTextInput.d.ts +10 -0
- package/build/MarkdownTextInput.d.ts.map +1 -0
- package/build/MarkdownTextInput.js +233 -0
- package/build/MarkdownTextInput.js.map +1 -0
- package/build/MarkdownToolbar.d.ts +11 -0
- package/build/MarkdownToolbar.d.ts.map +1 -0
- package/build/MarkdownToolbar.js +98 -0
- package/build/MarkdownToolbar.js.map +1 -0
- package/build/index.d.ts +14 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +11 -0
- package/build/index.js.map +1 -0
- package/build/markdownCore.types.d.ts +321 -0
- package/build/markdownCore.types.d.ts.map +1 -0
- package/build/markdownCore.types.js +2 -0
- package/build/markdownCore.types.js.map +1 -0
- package/build/markdownHighlight.d.ts +31 -0
- package/build/markdownHighlight.d.ts.map +1 -0
- package/build/markdownHighlight.js +378 -0
- package/build/markdownHighlight.js.map +1 -0
- package/build/markdownHighlight.types.d.ts +48 -0
- package/build/markdownHighlight.types.d.ts.map +1 -0
- package/build/markdownHighlight.types.js +9 -0
- package/build/markdownHighlight.types.js.map +1 -0
- package/build/markdownParser.d.ts +16 -0
- package/build/markdownParser.d.ts.map +1 -0
- package/build/markdownParser.js +309 -0
- package/build/markdownParser.js.map +1 -0
- package/build/markdownRendererDefaults.d.ts +113 -0
- package/build/markdownRendererDefaults.d.ts.map +1 -0
- package/build/markdownRendererDefaults.js +174 -0
- package/build/markdownRendererDefaults.js.map +1 -0
- package/build/markdownSegment.types.d.ts +22 -0
- package/build/markdownSegment.types.d.ts.map +1 -0
- package/build/markdownSegment.types.js +2 -0
- package/build/markdownSegment.types.js.map +1 -0
- package/build/markdownSegmentDefaults.d.ts +43 -0
- package/build/markdownSegmentDefaults.d.ts.map +1 -0
- package/build/markdownSegmentDefaults.js +176 -0
- package/build/markdownSegmentDefaults.js.map +1 -0
- package/build/markdownSyntaxUtils.d.ts +58 -0
- package/build/markdownSyntaxUtils.d.ts.map +1 -0
- package/build/markdownSyntaxUtils.js +98 -0
- package/build/markdownSyntaxUtils.js.map +1 -0
- package/build/markdownToolbarActions.d.ts +12 -0
- package/build/markdownToolbarActions.d.ts.map +1 -0
- package/build/markdownToolbarActions.js +212 -0
- package/build/markdownToolbarActions.js.map +1 -0
- package/build/useMarkdownEditor.d.ts +10 -0
- package/build/useMarkdownEditor.d.ts.map +1 -0
- package/build/useMarkdownEditor.js +219 -0
- package/build/useMarkdownEditor.js.map +1 -0
- package/jest.config.js +10 -0
- package/package.json +45 -0
- package/src/MarkdownRenderer.tsx +240 -0
- package/src/MarkdownTextInput.tsx +263 -0
- package/src/MarkdownToolbar.tsx +126 -0
- package/src/index.ts +31 -0
- package/src/markdownCore.types.ts +405 -0
- package/src/markdownHighlight.ts +413 -0
- package/src/markdownHighlight.types.ts +75 -0
- package/src/markdownParser.ts +345 -0
- package/src/markdownRendererDefaults.tsx +207 -0
- package/src/markdownSegment.types.ts +24 -0
- package/src/markdownSegmentDefaults.tsx +208 -0
- package/src/markdownSyntaxUtils.ts +139 -0
- package/src/markdownToolbarActions.ts +296 -0
- package/src/useMarkdownEditor.ts +265 -0
- package/tsconfig.json +9 -0
- package/tsconfig.test.json +8 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Syntax highlighting module for the live preview editor.
|
|
3
|
+
* Converts raw markdown text into an array of semantic segments,
|
|
4
|
+
* preserving markdown delimiters as visible elements.
|
|
5
|
+
*
|
|
6
|
+
* Unlike the parser in markdownParser.ts (which produces an AST for rendering),
|
|
7
|
+
* this module produces flat {text, type, meta?} segments optimized to be
|
|
8
|
+
* rendered as children of a TextInput.
|
|
9
|
+
*
|
|
10
|
+
* Complexity: O(n) where n is text length.
|
|
11
|
+
*/
|
|
12
|
+
import { findUnescapedToken, parseImageSourceAndTitle, isEscaped, isMarkdownFeatureEnabled, isHeadingLevelEnabled } from './markdownSyntaxUtils';
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Regex patterns for block recognition
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
const HEADING_RE = /^(#{1,6})\s+(.*)/;
|
|
17
|
+
const BLOCKQUOTE_RE = /^(>\s?)(.*)/;
|
|
18
|
+
const HORIZONTAL_RULE_RE = /^ {0,3}([-*_])[ \t]*(?:\1[ \t]*){2,}$/;
|
|
19
|
+
const UNORDERED_LIST_RE = /^(\s*[-*+]\s+)(.*)/;
|
|
20
|
+
const ORDERED_LIST_RE = /^(\s*\d+\.\s+)(.*)/;
|
|
21
|
+
const CODE_FENCE_RE = /^```/;
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Public API
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
/**
|
|
26
|
+
* Converts markdown text into an array of semantic segments.
|
|
27
|
+
*
|
|
28
|
+
* Parsing happens line by line:
|
|
29
|
+
* 1. Identifies block type (heading, list, quote, code block)
|
|
30
|
+
* 2. Tags block markers (e.g. #, >, -) with dedicated segment types
|
|
31
|
+
* 3. Analyzes inline content (bold, italic, code, link)
|
|
32
|
+
* 4. Produces semantic segments rendered via segment components
|
|
33
|
+
*
|
|
34
|
+
* @param markdown - Raw markdown text
|
|
35
|
+
* @param features - Optional list of enabled features. When provided,
|
|
36
|
+
* only the corresponding syntax is highlighted; disabled
|
|
37
|
+
* syntax is treated as plain text.
|
|
38
|
+
* @returns Array of semantic segments
|
|
39
|
+
*/
|
|
40
|
+
export function highlightMarkdown(markdown, features) {
|
|
41
|
+
const lines = markdown.split('\n');
|
|
42
|
+
const segments = [];
|
|
43
|
+
let inCodeBlock = false;
|
|
44
|
+
let prevLineMeta;
|
|
45
|
+
for (let i = 0; i < lines.length; i++) {
|
|
46
|
+
if (i > 0) {
|
|
47
|
+
// Newline inherits previous heading context so Android renders
|
|
48
|
+
// the line break with the heading's larger lineHeight.
|
|
49
|
+
if (prevLineMeta?.lineContext === 'heading') {
|
|
50
|
+
segments.push({ text: '\n', type: 'heading', meta: prevLineMeta });
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
segments.push({ text: '\n', type: 'text' });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
prevLineMeta = undefined;
|
|
57
|
+
const line = lines[i] ?? '';
|
|
58
|
+
// Code fence: open/close code blocks
|
|
59
|
+
if (isMarkdownFeatureEnabled(features, 'codeBlock') && CODE_FENCE_RE.test(line)) {
|
|
60
|
+
segments.push({
|
|
61
|
+
text: line,
|
|
62
|
+
type: 'delimiter',
|
|
63
|
+
meta: { lineContext: 'codeFence' },
|
|
64
|
+
});
|
|
65
|
+
inCodeBlock = !inCodeBlock;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
// Inside a code block
|
|
69
|
+
if (inCodeBlock) {
|
|
70
|
+
segments.push({
|
|
71
|
+
text: line,
|
|
72
|
+
type: 'codeBlock',
|
|
73
|
+
});
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
// Heading: # Title
|
|
77
|
+
const headingMatch = line.match(HEADING_RE);
|
|
78
|
+
if (headingMatch && !isEscaped(line, 0) && isHeadingLevelEnabled(features, headingMatch[1].length)) {
|
|
79
|
+
const level = headingMatch[1].length;
|
|
80
|
+
const headingMeta = { lineContext: 'heading', headingLevel: String(level) };
|
|
81
|
+
prevLineMeta = headingMeta;
|
|
82
|
+
segments.push({
|
|
83
|
+
text: `${headingMatch[1]} `,
|
|
84
|
+
type: 'delimiter',
|
|
85
|
+
meta: headingMeta,
|
|
86
|
+
});
|
|
87
|
+
appendInlineSegments(segments, headingMatch[2] ?? '', {
|
|
88
|
+
baseTextType: 'heading',
|
|
89
|
+
meta: headingMeta,
|
|
90
|
+
features,
|
|
91
|
+
});
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
// Horizontal rule: ---, ***, ___
|
|
95
|
+
if (isMarkdownFeatureEnabled(features, 'divider') && HORIZONTAL_RULE_RE.test(line)) {
|
|
96
|
+
segments.push({
|
|
97
|
+
text: line,
|
|
98
|
+
type: 'horizontalRule',
|
|
99
|
+
});
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
// Blockquote: > quote
|
|
103
|
+
if (isMarkdownFeatureEnabled(features, 'quote')) {
|
|
104
|
+
const quoteMatch = line.match(BLOCKQUOTE_RE);
|
|
105
|
+
if (quoteMatch) {
|
|
106
|
+
const quoteMeta = { lineContext: 'quote' };
|
|
107
|
+
segments.push({
|
|
108
|
+
text: quoteMatch[1] ?? '',
|
|
109
|
+
type: 'quoteMarker',
|
|
110
|
+
meta: quoteMeta,
|
|
111
|
+
});
|
|
112
|
+
appendInlineSegments(segments, quoteMatch[2] ?? '', {
|
|
113
|
+
baseTextType: 'quote',
|
|
114
|
+
meta: quoteMeta,
|
|
115
|
+
features,
|
|
116
|
+
});
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Unordered list: - item
|
|
121
|
+
if (isMarkdownFeatureEnabled(features, 'unorderedList')) {
|
|
122
|
+
const ulMatch = line.match(UNORDERED_LIST_RE);
|
|
123
|
+
if (ulMatch) {
|
|
124
|
+
segments.push({
|
|
125
|
+
text: ulMatch[1] ?? '',
|
|
126
|
+
type: 'listMarker',
|
|
127
|
+
});
|
|
128
|
+
appendInlineSegments(segments, ulMatch[2] ?? '', { features });
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Ordered list: 1. item
|
|
133
|
+
if (isMarkdownFeatureEnabled(features, 'orderedList')) {
|
|
134
|
+
const olMatch = line.match(ORDERED_LIST_RE);
|
|
135
|
+
if (olMatch) {
|
|
136
|
+
segments.push({
|
|
137
|
+
text: olMatch[1] ?? '',
|
|
138
|
+
type: 'listMarker',
|
|
139
|
+
});
|
|
140
|
+
appendInlineSegments(segments, olMatch[2] ?? '', { features });
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Simple text line: inline parsing only
|
|
145
|
+
appendInlineSegments(segments, line, { features });
|
|
146
|
+
}
|
|
147
|
+
return segments;
|
|
148
|
+
}
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Segment composition functions
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
/**
|
|
153
|
+
* Analyzes inline text and adds semantic segments to the array.
|
|
154
|
+
*/
|
|
155
|
+
function appendInlineSegments(segments, text, options = {}) {
|
|
156
|
+
const inlineSegs = parseInlineHighlight(text, undefined, options.features);
|
|
157
|
+
for (const seg of inlineSegs) {
|
|
158
|
+
const type = resolveInlineSegmentType(seg, options.baseTextType ?? 'text');
|
|
159
|
+
const mergedMeta = options.meta || seg.meta ? { ...(options.meta ?? {}), ...(seg.meta ?? {}) } : undefined;
|
|
160
|
+
segments.push({
|
|
161
|
+
text: seg.text,
|
|
162
|
+
type,
|
|
163
|
+
...(mergedMeta ? { meta: mergedMeta } : {}),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Maps a raw inline segment to its public semantic HighlightSegmentType.
|
|
169
|
+
*/
|
|
170
|
+
function resolveInlineSegmentType(seg, baseTextType) {
|
|
171
|
+
switch (seg.type) {
|
|
172
|
+
case 'delimiter':
|
|
173
|
+
return 'delimiter';
|
|
174
|
+
case 'code':
|
|
175
|
+
return 'code';
|
|
176
|
+
case 'link-label':
|
|
177
|
+
return 'link';
|
|
178
|
+
case 'link-url':
|
|
179
|
+
return 'linkUrl';
|
|
180
|
+
case 'image-alt':
|
|
181
|
+
return 'image';
|
|
182
|
+
case 'text':
|
|
183
|
+
default:
|
|
184
|
+
if (seg.bold)
|
|
185
|
+
return 'bold';
|
|
186
|
+
if (seg.italic)
|
|
187
|
+
return 'italic';
|
|
188
|
+
if (seg.strikethrough)
|
|
189
|
+
return 'strikethrough';
|
|
190
|
+
return baseTextType;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
// Inline parser
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
/**
|
|
197
|
+
* Inline parser that preserves markdown delimiters and tracks
|
|
198
|
+
* formatting context (bold, italic) recursively.
|
|
199
|
+
*
|
|
200
|
+
* Unlike parseMarkdownInline in markdownParser.ts:
|
|
201
|
+
* - Delimiters (**, _, `, etc.) are kept as 'delimiter' segments
|
|
202
|
+
* - Bold/italic context is propagated to nested levels
|
|
203
|
+
* - Produces flat segments (not a tree) for direct rendering
|
|
204
|
+
*
|
|
205
|
+
* @param content - Inline text to analyze
|
|
206
|
+
* @param context - Formatting context inherited from upper level
|
|
207
|
+
*/
|
|
208
|
+
function parseInlineHighlight(content, context = { bold: false, italic: false, strikethrough: false }, features) {
|
|
209
|
+
const segments = [];
|
|
210
|
+
let cursor = 0;
|
|
211
|
+
/**
|
|
212
|
+
* Adds text to segments array, merging adjacent segments
|
|
213
|
+
* of same type to reduce number of Text nodes in rendering.
|
|
214
|
+
*/
|
|
215
|
+
const appendText = (value, type = 'text') => {
|
|
216
|
+
if (!value)
|
|
217
|
+
return;
|
|
218
|
+
const last = segments[segments.length - 1];
|
|
219
|
+
// Merge only if type and formatting context match
|
|
220
|
+
if (last && last.type === type && last.bold === context.bold && last.italic === context.italic && last.strikethrough === context.strikethrough) {
|
|
221
|
+
last.text += value;
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
segments.push({
|
|
225
|
+
text: value,
|
|
226
|
+
type,
|
|
227
|
+
bold: context.bold,
|
|
228
|
+
italic: context.italic,
|
|
229
|
+
strikethrough: context.strikethrough,
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
while (cursor < content.length) {
|
|
233
|
+
// Escape: \* → * literal
|
|
234
|
+
if (content[cursor] === '\\' && cursor + 1 < content.length) {
|
|
235
|
+
// Keep the backslash visible as a delimiter to preserve character count
|
|
236
|
+
// parity with the input value (required for correct cursor positioning).
|
|
237
|
+
segments.push({ text: '\\', type: 'delimiter', bold: false, italic: false, strikethrough: false });
|
|
238
|
+
appendText(content[cursor + 1] ?? '');
|
|
239
|
+
cursor += 2;
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
// Inline code: `code`
|
|
243
|
+
if (isMarkdownFeatureEnabled(features, 'code') && content[cursor] === '`') {
|
|
244
|
+
const closeIdx = findUnescapedToken(content, '`', cursor + 1);
|
|
245
|
+
if (closeIdx !== -1) {
|
|
246
|
+
// Backticks are delimiters, content is code
|
|
247
|
+
segments.push({ text: '`', type: 'delimiter', bold: false, italic: false, strikethrough: false });
|
|
248
|
+
segments.push({
|
|
249
|
+
text: content.slice(cursor + 1, closeIdx),
|
|
250
|
+
type: 'code',
|
|
251
|
+
bold: false,
|
|
252
|
+
italic: false,
|
|
253
|
+
strikethrough: false,
|
|
254
|
+
});
|
|
255
|
+
segments.push({ text: '`', type: 'delimiter', bold: false, italic: false, strikethrough: false });
|
|
256
|
+
cursor = closeIdx + 1;
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// Bold: **text** or __text__
|
|
261
|
+
if (isMarkdownFeatureEnabled(features, 'bold') && (content.startsWith('**', cursor) || content.startsWith('__', cursor))) {
|
|
262
|
+
const marker = content.slice(cursor, cursor + 2);
|
|
263
|
+
const closeIdx = findUnescapedToken(content, marker, cursor + 2);
|
|
264
|
+
if (closeIdx !== -1) {
|
|
265
|
+
segments.push({ text: marker, type: 'delimiter', bold: false, italic: false, strikethrough: false });
|
|
266
|
+
// Recursion with bold context active: inner content
|
|
267
|
+
// inherits italic state from upper level
|
|
268
|
+
const inner = parseInlineHighlight(content.slice(cursor + 2, closeIdx), { ...context, bold: true }, features);
|
|
269
|
+
segments.push(...inner);
|
|
270
|
+
segments.push({ text: marker, type: 'delimiter', bold: false, italic: false, strikethrough: false });
|
|
271
|
+
cursor = closeIdx + 2;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// Strikethrough: ~~text~~
|
|
276
|
+
if (isMarkdownFeatureEnabled(features, 'strikethrough') && content.startsWith('~~', cursor)) {
|
|
277
|
+
const closeIdx = findUnescapedToken(content, '~~', cursor + 2);
|
|
278
|
+
if (closeIdx !== -1) {
|
|
279
|
+
segments.push({ text: '~~', type: 'delimiter', bold: false, italic: false, strikethrough: false });
|
|
280
|
+
const inner = parseInlineHighlight(content.slice(cursor + 2, closeIdx), { ...context, strikethrough: true }, features);
|
|
281
|
+
segments.push(...inner);
|
|
282
|
+
segments.push({ text: '~~', type: 'delimiter', bold: false, italic: false, strikethrough: false });
|
|
283
|
+
cursor = closeIdx + 2;
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
// Italic: *text* or _text_
|
|
288
|
+
if (isMarkdownFeatureEnabled(features, 'italic') && (content[cursor] === '*' || content[cursor] === '_')) {
|
|
289
|
+
const marker = content[cursor];
|
|
290
|
+
const closeIdx = findUnescapedToken(content, marker, cursor + 1);
|
|
291
|
+
if (closeIdx !== -1) {
|
|
292
|
+
segments.push({ text: marker, type: 'delimiter', bold: false, italic: false, strikethrough: false });
|
|
293
|
+
// Recursion with italic context active: inner content
|
|
294
|
+
// inherits bold state from upper level
|
|
295
|
+
const inner = parseInlineHighlight(content.slice(cursor + 1, closeIdx), { ...context, italic: true }, features);
|
|
296
|
+
segments.push(...inner);
|
|
297
|
+
segments.push({ text: marker, type: 'delimiter', bold: false, italic: false, strikethrough: false });
|
|
298
|
+
cursor = closeIdx + 1;
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// Image:  or 
|
|
303
|
+
if (content[cursor] === '!' && content[cursor + 1] === '[') {
|
|
304
|
+
if (isMarkdownFeatureEnabled(features, 'image')) {
|
|
305
|
+
const altClose = findUnescapedToken(content, ']', cursor + 2);
|
|
306
|
+
if (altClose !== -1 && content[altClose + 1] === '(') {
|
|
307
|
+
const srcClose = findUnescapedToken(content, ')', altClose + 2);
|
|
308
|
+
if (srcClose !== -1) {
|
|
309
|
+
const fullText = content.slice(cursor, srcClose + 1);
|
|
310
|
+
const alt = content.slice(cursor + 2, altClose);
|
|
311
|
+
const rawUrl = content.slice(altClose + 2, srcClose).trim();
|
|
312
|
+
const { src, title } = parseImageSourceAndTitle(rawUrl, { unescapeSrc: false });
|
|
313
|
+
segments.push({
|
|
314
|
+
text: fullText,
|
|
315
|
+
type: 'image-alt',
|
|
316
|
+
bold: context.bold,
|
|
317
|
+
italic: context.italic,
|
|
318
|
+
strikethrough: context.strikethrough,
|
|
319
|
+
meta: { src, alt, ...(title ? { title } : {}) },
|
|
320
|
+
});
|
|
321
|
+
cursor = srcClose + 1;
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
// Image disabled: consume "!" as plain text and skip ahead so
|
|
328
|
+
// the link parser does not pick up "[alt](url)" as a link.
|
|
329
|
+
const altClose = findUnescapedToken(content, ']', cursor + 2);
|
|
330
|
+
if (altClose !== -1 && content[altClose + 1] === '(') {
|
|
331
|
+
const srcClose = findUnescapedToken(content, ')', altClose + 2);
|
|
332
|
+
if (srcClose !== -1) {
|
|
333
|
+
appendText(content.slice(cursor, srcClose + 1));
|
|
334
|
+
cursor = srcClose + 1;
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
appendText('!');
|
|
339
|
+
cursor += 1;
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
// Link: [label](url)
|
|
344
|
+
if (content[cursor] === '[') {
|
|
345
|
+
const labelClose = findUnescapedToken(content, ']', cursor + 1);
|
|
346
|
+
if (labelClose !== -1 && content[labelClose + 1] === '(') {
|
|
347
|
+
const hrefClose = findUnescapedToken(content, ')', labelClose + 2);
|
|
348
|
+
if (hrefClose !== -1) {
|
|
349
|
+
// Delimiters [] and () are tagged separately from label/url
|
|
350
|
+
segments.push({ text: '[', type: 'delimiter', bold: context.bold, italic: context.italic, strikethrough: context.strikethrough });
|
|
351
|
+
segments.push({
|
|
352
|
+
text: content.slice(cursor + 1, labelClose),
|
|
353
|
+
type: 'link-label',
|
|
354
|
+
bold: context.bold,
|
|
355
|
+
italic: context.italic,
|
|
356
|
+
strikethrough: context.strikethrough,
|
|
357
|
+
});
|
|
358
|
+
segments.push({ text: '](', type: 'delimiter', bold: context.bold, italic: context.italic, strikethrough: context.strikethrough });
|
|
359
|
+
segments.push({
|
|
360
|
+
text: content.slice(labelClose + 2, hrefClose),
|
|
361
|
+
type: 'link-url',
|
|
362
|
+
bold: context.bold,
|
|
363
|
+
italic: context.italic,
|
|
364
|
+
strikethrough: context.strikethrough,
|
|
365
|
+
});
|
|
366
|
+
segments.push({ text: ')', type: 'delimiter', bold: context.bold, italic: context.italic, strikethrough: context.strikethrough });
|
|
367
|
+
cursor = hrefClose + 1;
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
// Simple text character
|
|
373
|
+
appendText(content[cursor] ?? '');
|
|
374
|
+
cursor += 1;
|
|
375
|
+
}
|
|
376
|
+
return segments;
|
|
377
|
+
}
|
|
378
|
+
//# sourceMappingURL=markdownHighlight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdownHighlight.js","sourceRoot":"","sources":["../src/markdownHighlight.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Syntax highlighting module for the live preview editor.\n * Converts raw markdown text into an array of semantic segments,\n * preserving markdown delimiters as visible elements.\n *\n * Unlike the parser in markdownParser.ts (which produces an AST for rendering),\n * this module produces flat {text, type, meta?} segments optimized to be\n * rendered as children of a TextInput.\n *\n * Complexity: O(n) where n is text length.\n */\n\nimport { findUnescapedToken, parseImageSourceAndTitle, isEscaped, isMarkdownFeatureEnabled, isHeadingLevelEnabled } from './markdownSyntaxUtils';\nimport type { HighlightSegmentType, HighlightSegment, InlineSegmentType, InlineContext, RawInlineSegment } from './markdownHighlight.types';\nimport type { MarkdownToolbarAction } from './markdownCore.types';\n\nexport type { HighlightSegmentType, HighlightSegment } from './markdownHighlight.types';\n\n// ---------------------------------------------------------------------------\n// Regex patterns for block recognition\n// ---------------------------------------------------------------------------\n\nconst HEADING_RE = /^(#{1,6})\\s+(.*)/;\nconst BLOCKQUOTE_RE = /^(>\\s?)(.*)/;\nconst HORIZONTAL_RULE_RE = /^ {0,3}([-*_])[ \\t]*(?:\\1[ \\t]*){2,}$/;\nconst UNORDERED_LIST_RE = /^(\\s*[-*+]\\s+)(.*)/;\nconst ORDERED_LIST_RE = /^(\\s*\\d+\\.\\s+)(.*)/;\nconst CODE_FENCE_RE = /^```/;\n\ntype AppendInlineOptions = {\n\tbaseTextType?: Extract<HighlightSegmentType, 'text' | 'heading' | 'quote'>;\n\tmeta?: Record<string, string>;\n\tfeatures?: MarkdownToolbarAction[];\n};\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Converts markdown text into an array of semantic segments.\n *\n * Parsing happens line by line:\n * 1. Identifies block type (heading, list, quote, code block)\n * 2. Tags block markers (e.g. #, >, -) with dedicated segment types\n * 3. Analyzes inline content (bold, italic, code, link)\n * 4. Produces semantic segments rendered via segment components\n *\n * @param markdown - Raw markdown text\n * @param features - Optional list of enabled features. When provided,\n * only the corresponding syntax is highlighted; disabled\n * syntax is treated as plain text.\n * @returns Array of semantic segments\n */\nexport function highlightMarkdown(markdown: string, features?: MarkdownToolbarAction[]): HighlightSegment[] {\n\tconst lines = markdown.split('\\n');\n\tconst segments: HighlightSegment[] = [];\n\n\tlet inCodeBlock = false;\n\tlet prevLineMeta: Record<string, string> | undefined;\n\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tif (i > 0) {\n\t\t\t// Newline inherits previous heading context so Android renders\n\t\t\t// the line break with the heading's larger lineHeight.\n\t\t\tif (prevLineMeta?.lineContext === 'heading') {\n\t\t\t\tsegments.push({ text: '\\n', type: 'heading', meta: prevLineMeta });\n\t\t\t} else {\n\t\t\t\tsegments.push({ text: '\\n', type: 'text' });\n\t\t\t}\n\t\t}\n\t\tprevLineMeta = undefined;\n\n\t\tconst line = lines[i] ?? '';\n\n\t\t// Code fence: open/close code blocks\n\t\tif (isMarkdownFeatureEnabled(features, 'codeBlock') && CODE_FENCE_RE.test(line)) {\n\t\t\tsegments.push({\n\t\t\t\ttext: line,\n\t\t\t\ttype: 'delimiter',\n\t\t\t\tmeta: { lineContext: 'codeFence' },\n\t\t\t});\n\t\t\tinCodeBlock = !inCodeBlock;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Inside a code block\n\t\tif (inCodeBlock) {\n\t\t\tsegments.push({\n\t\t\t\ttext: line,\n\t\t\t\ttype: 'codeBlock',\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Heading: # Title\n\t\tconst headingMatch = line.match(HEADING_RE);\n\t\tif (headingMatch && !isEscaped(line, 0) && isHeadingLevelEnabled(features, headingMatch[1]!.length)) {\n\t\t\tconst level = headingMatch[1]!.length;\n\t\t\tconst headingMeta = { lineContext: 'heading', headingLevel: String(level) };\n\t\t\tprevLineMeta = headingMeta;\n\t\t\tsegments.push({\n\t\t\t\ttext: `${headingMatch[1]} `,\n\t\t\t\ttype: 'delimiter',\n\t\t\t\tmeta: headingMeta,\n\t\t\t});\n\t\t\tappendInlineSegments(segments, headingMatch[2] ?? '', {\n\t\t\t\tbaseTextType: 'heading',\n\t\t\t\tmeta: headingMeta,\n\t\t\t\tfeatures,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Horizontal rule: ---, ***, ___\n\t\tif (isMarkdownFeatureEnabled(features, 'divider') && HORIZONTAL_RULE_RE.test(line)) {\n\t\t\tsegments.push({\n\t\t\t\ttext: line,\n\t\t\t\ttype: 'horizontalRule',\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Blockquote: > quote\n\t\tif (isMarkdownFeatureEnabled(features, 'quote')) {\n\t\t\tconst quoteMatch = line.match(BLOCKQUOTE_RE);\n\t\t\tif (quoteMatch) {\n\t\t\t\tconst quoteMeta = { lineContext: 'quote' };\n\t\t\t\tsegments.push({\n\t\t\t\t\ttext: quoteMatch[1] ?? '',\n\t\t\t\t\ttype: 'quoteMarker',\n\t\t\t\t\tmeta: quoteMeta,\n\t\t\t\t});\n\t\t\t\tappendInlineSegments(segments, quoteMatch[2] ?? '', {\n\t\t\t\t\tbaseTextType: 'quote',\n\t\t\t\t\tmeta: quoteMeta,\n\t\t\t\t\tfeatures,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Unordered list: - item\n\t\tif (isMarkdownFeatureEnabled(features, 'unorderedList')) {\n\t\t\tconst ulMatch = line.match(UNORDERED_LIST_RE);\n\t\t\tif (ulMatch) {\n\t\t\t\tsegments.push({\n\t\t\t\t\ttext: ulMatch[1] ?? '',\n\t\t\t\t\ttype: 'listMarker',\n\t\t\t\t});\n\t\t\t\tappendInlineSegments(segments, ulMatch[2] ?? '', { features });\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Ordered list: 1. item\n\t\tif (isMarkdownFeatureEnabled(features, 'orderedList')) {\n\t\t\tconst olMatch = line.match(ORDERED_LIST_RE);\n\t\t\tif (olMatch) {\n\t\t\t\tsegments.push({\n\t\t\t\t\ttext: olMatch[1] ?? '',\n\t\t\t\t\ttype: 'listMarker',\n\t\t\t\t});\n\t\t\t\tappendInlineSegments(segments, olMatch[2] ?? '', { features });\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Simple text line: inline parsing only\n\t\tappendInlineSegments(segments, line, { features });\n\t}\n\n\treturn segments;\n}\n\n// ---------------------------------------------------------------------------\n// Segment composition functions\n// ---------------------------------------------------------------------------\n\n/**\n * Analyzes inline text and adds semantic segments to the array.\n */\nfunction appendInlineSegments(segments: HighlightSegment[], text: string, options: AppendInlineOptions = {}): void {\n\tconst inlineSegs = parseInlineHighlight(text, undefined, options.features);\n\tfor (const seg of inlineSegs) {\n\t\tconst type = resolveInlineSegmentType(seg, options.baseTextType ?? 'text');\n\t\tconst mergedMeta = options.meta || seg.meta ? { ...(options.meta ?? {}), ...(seg.meta ?? {}) } : undefined;\n\t\tsegments.push({\n\t\t\ttext: seg.text,\n\t\t\ttype,\n\t\t\t...(mergedMeta ? { meta: mergedMeta } : {}),\n\t\t});\n\t}\n}\n\n/**\n * Maps a raw inline segment to its public semantic HighlightSegmentType.\n */\nfunction resolveInlineSegmentType(seg: RawInlineSegment, baseTextType: Extract<HighlightSegmentType, 'text' | 'heading' | 'quote'>): HighlightSegmentType {\n\tswitch (seg.type) {\n\t\tcase 'delimiter':\n\t\t\treturn 'delimiter';\n\t\tcase 'code':\n\t\t\treturn 'code';\n\t\tcase 'link-label':\n\t\t\treturn 'link';\n\t\tcase 'link-url':\n\t\t\treturn 'linkUrl';\n\t\tcase 'image-alt':\n\t\t\treturn 'image';\n\t\tcase 'text':\n\t\tdefault:\n\t\t\tif (seg.bold) return 'bold';\n\t\t\tif (seg.italic) return 'italic';\n\t\t\tif (seg.strikethrough) return 'strikethrough';\n\t\t\treturn baseTextType;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Inline parser\n// ---------------------------------------------------------------------------\n\n/**\n * Inline parser that preserves markdown delimiters and tracks\n * formatting context (bold, italic) recursively.\n *\n * Unlike parseMarkdownInline in markdownParser.ts:\n * - Delimiters (**, _, `, etc.) are kept as 'delimiter' segments\n * - Bold/italic context is propagated to nested levels\n * - Produces flat segments (not a tree) for direct rendering\n *\n * @param content - Inline text to analyze\n * @param context - Formatting context inherited from upper level\n */\nfunction parseInlineHighlight(content: string, context: InlineContext = { bold: false, italic: false, strikethrough: false }, features?: MarkdownToolbarAction[]): RawInlineSegment[] {\n\tconst segments: RawInlineSegment[] = [];\n\tlet cursor = 0;\n\n\t/**\n\t * Adds text to segments array, merging adjacent segments\n\t * of same type to reduce number of Text nodes in rendering.\n\t */\n\tconst appendText = (value: string, type: InlineSegmentType = 'text') => {\n\t\tif (!value) return;\n\t\tconst last = segments[segments.length - 1];\n\t\t// Merge only if type and formatting context match\n\t\tif (last && last.type === type && last.bold === context.bold && last.italic === context.italic && last.strikethrough === context.strikethrough) {\n\t\t\tlast.text += value;\n\t\t\treturn;\n\t\t}\n\t\tsegments.push({\n\t\t\ttext: value,\n\t\t\ttype,\n\t\t\tbold: context.bold,\n\t\t\titalic: context.italic,\n\t\t\tstrikethrough: context.strikethrough,\n\t\t});\n\t};\n\n\twhile (cursor < content.length) {\n\t\t// Escape: \\* → * literal\n\t\tif (content[cursor] === '\\\\' && cursor + 1 < content.length) {\n\t\t\t// Keep the backslash visible as a delimiter to preserve character count\n\t\t\t// parity with the input value (required for correct cursor positioning).\n\t\t\tsegments.push({ text: '\\\\', type: 'delimiter', bold: false, italic: false, strikethrough: false });\n\t\t\tappendText(content[cursor + 1] ?? '');\n\t\t\tcursor += 2;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Inline code: `code`\n\t\tif (isMarkdownFeatureEnabled(features, 'code') && content[cursor] === '`') {\n\t\t\tconst closeIdx = findUnescapedToken(content, '`', cursor + 1);\n\t\t\tif (closeIdx !== -1) {\n\t\t\t\t// Backticks are delimiters, content is code\n\t\t\t\tsegments.push({ text: '`', type: 'delimiter', bold: false, italic: false, strikethrough: false });\n\t\t\t\tsegments.push({\n\t\t\t\t\ttext: content.slice(cursor + 1, closeIdx),\n\t\t\t\t\ttype: 'code',\n\t\t\t\t\tbold: false,\n\t\t\t\t\titalic: false,\n\t\t\t\t\tstrikethrough: false,\n\t\t\t\t});\n\t\t\t\tsegments.push({ text: '`', type: 'delimiter', bold: false, italic: false, strikethrough: false });\n\t\t\t\tcursor = closeIdx + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Bold: **text** or __text__\n\t\tif (isMarkdownFeatureEnabled(features, 'bold') && (content.startsWith('**', cursor) || content.startsWith('__', cursor))) {\n\t\t\tconst marker = content.slice(cursor, cursor + 2);\n\t\t\tconst closeIdx = findUnescapedToken(content, marker, cursor + 2);\n\t\t\tif (closeIdx !== -1) {\n\t\t\t\tsegments.push({ text: marker, type: 'delimiter', bold: false, italic: false, strikethrough: false });\n\t\t\t\t// Recursion with bold context active: inner content\n\t\t\t\t// inherits italic state from upper level\n\t\t\t\tconst inner = parseInlineHighlight(content.slice(cursor + 2, closeIdx), { ...context, bold: true }, features);\n\t\t\t\tsegments.push(...inner);\n\t\t\t\tsegments.push({ text: marker, type: 'delimiter', bold: false, italic: false, strikethrough: false });\n\t\t\t\tcursor = closeIdx + 2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Strikethrough: ~~text~~\n\t\tif (isMarkdownFeatureEnabled(features, 'strikethrough') && content.startsWith('~~', cursor)) {\n\t\t\tconst closeIdx = findUnescapedToken(content, '~~', cursor + 2);\n\t\t\tif (closeIdx !== -1) {\n\t\t\t\tsegments.push({ text: '~~', type: 'delimiter', bold: false, italic: false, strikethrough: false });\n\t\t\t\tconst inner = parseInlineHighlight(content.slice(cursor + 2, closeIdx), { ...context, strikethrough: true }, features);\n\t\t\t\tsegments.push(...inner);\n\t\t\t\tsegments.push({ text: '~~', type: 'delimiter', bold: false, italic: false, strikethrough: false });\n\t\t\t\tcursor = closeIdx + 2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Italic: *text* or _text_\n\t\tif (isMarkdownFeatureEnabled(features, 'italic') && (content[cursor] === '*' || content[cursor] === '_')) {\n\t\t\tconst marker = content[cursor]!;\n\t\t\tconst closeIdx = findUnescapedToken(content, marker, cursor + 1);\n\t\t\tif (closeIdx !== -1) {\n\t\t\t\tsegments.push({ text: marker, type: 'delimiter', bold: false, italic: false, strikethrough: false });\n\t\t\t\t// Recursion with italic context active: inner content\n\t\t\t\t// inherits bold state from upper level\n\t\t\t\tconst inner = parseInlineHighlight(content.slice(cursor + 1, closeIdx), { ...context, italic: true }, features);\n\t\t\t\tsegments.push(...inner);\n\t\t\t\tsegments.push({ text: marker, type: 'delimiter', bold: false, italic: false, strikethrough: false });\n\t\t\t\tcursor = closeIdx + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Image:  or \n\t\tif (content[cursor] === '!' && content[cursor + 1] === '[') {\n\t\t\tif (isMarkdownFeatureEnabled(features, 'image')) {\n\t\t\t\tconst altClose = findUnescapedToken(content, ']', cursor + 2);\n\t\t\t\tif (altClose !== -1 && content[altClose + 1] === '(') {\n\t\t\t\t\tconst srcClose = findUnescapedToken(content, ')', altClose + 2);\n\t\t\t\t\tif (srcClose !== -1) {\n\t\t\t\t\t\tconst fullText = content.slice(cursor, srcClose + 1);\n\t\t\t\t\t\tconst alt = content.slice(cursor + 2, altClose);\n\t\t\t\t\t\tconst rawUrl = content.slice(altClose + 2, srcClose).trim();\n\t\t\t\t\t\tconst { src, title } = parseImageSourceAndTitle(rawUrl, { unescapeSrc: false });\n\t\t\t\t\t\tsegments.push({\n\t\t\t\t\t\t\ttext: fullText,\n\t\t\t\t\t\t\ttype: 'image-alt',\n\t\t\t\t\t\t\tbold: context.bold,\n\t\t\t\t\t\t\titalic: context.italic,\n\t\t\t\t\t\t\tstrikethrough: context.strikethrough,\n\t\t\t\t\t\t\tmeta: { src, alt, ...(title ? { title } : {}) },\n\t\t\t\t\t\t});\n\t\t\t\t\t\tcursor = srcClose + 1;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Image disabled: consume \"!\" as plain text and skip ahead so\n\t\t\t\t// the link parser does not pick up \"[alt](url)\" as a link.\n\t\t\t\tconst altClose = findUnescapedToken(content, ']', cursor + 2);\n\t\t\t\tif (altClose !== -1 && content[altClose + 1] === '(') {\n\t\t\t\t\tconst srcClose = findUnescapedToken(content, ')', altClose + 2);\n\t\t\t\t\tif (srcClose !== -1) {\n\t\t\t\t\t\tappendText(content.slice(cursor, srcClose + 1));\n\t\t\t\t\t\tcursor = srcClose + 1;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tappendText('!');\n\t\t\t\tcursor += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Link: [label](url)\n\t\tif (content[cursor] === '[') {\n\t\t\tconst labelClose = findUnescapedToken(content, ']', cursor + 1);\n\t\t\tif (labelClose !== -1 && content[labelClose + 1] === '(') {\n\t\t\t\tconst hrefClose = findUnescapedToken(content, ')', labelClose + 2);\n\t\t\t\tif (hrefClose !== -1) {\n\t\t\t\t\t// Delimiters [] and () are tagged separately from label/url\n\t\t\t\t\tsegments.push({ text: '[', type: 'delimiter', bold: context.bold, italic: context.italic, strikethrough: context.strikethrough });\n\t\t\t\t\tsegments.push({\n\t\t\t\t\t\ttext: content.slice(cursor + 1, labelClose),\n\t\t\t\t\t\ttype: 'link-label',\n\t\t\t\t\t\tbold: context.bold,\n\t\t\t\t\t\titalic: context.italic,\n\t\t\t\t\t\tstrikethrough: context.strikethrough,\n\t\t\t\t\t});\n\t\t\t\t\tsegments.push({ text: '](', type: 'delimiter', bold: context.bold, italic: context.italic, strikethrough: context.strikethrough });\n\t\t\t\t\tsegments.push({\n\t\t\t\t\t\ttext: content.slice(labelClose + 2, hrefClose),\n\t\t\t\t\t\ttype: 'link-url',\n\t\t\t\t\t\tbold: context.bold,\n\t\t\t\t\t\titalic: context.italic,\n\t\t\t\t\t\tstrikethrough: context.strikethrough,\n\t\t\t\t\t});\n\t\t\t\t\tsegments.push({ text: ')', type: 'delimiter', bold: context.bold, italic: context.italic, strikethrough: context.strikethrough });\n\t\t\t\t\tcursor = hrefClose + 1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Simple text character\n\t\tappendText(content[cursor] ?? '');\n\t\tcursor += 1;\n\t}\n\n\treturn segments;\n}\n"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the markdown syntax-highlighting module.
|
|
3
|
+
*
|
|
4
|
+
* Public types (`HighlightSegmentType`, `HighlightSegment`) are re-exported
|
|
5
|
+
* from `index.ts` for consumers who provide custom segment components.
|
|
6
|
+
* Internal types are used only within `markdownHighlight.ts`.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Semantic type of a highlight segment.
|
|
10
|
+
* Exposed to consumers so they can provide custom render components per type.
|
|
11
|
+
*/
|
|
12
|
+
export type HighlightSegmentType = 'text' | 'delimiter' | 'heading' | 'bold' | 'italic' | 'strikethrough' | 'code' | 'codeBlock' | 'link' | 'linkUrl' | 'image' | 'quote' | 'quoteMarker' | 'listMarker' | 'horizontalRule';
|
|
13
|
+
/** Semantic highlight segment, ready for rendering via segment components. */
|
|
14
|
+
export type HighlightSegment = {
|
|
15
|
+
text: string;
|
|
16
|
+
type: HighlightSegmentType;
|
|
17
|
+
/** Optional metadata (e.g. image src, alt, title). */
|
|
18
|
+
meta?: Record<string, string>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Semantic type of an inline segment.
|
|
22
|
+
* Internally used to map inline tokens to public segment types.
|
|
23
|
+
*/
|
|
24
|
+
export type InlineSegmentType = 'text' | 'delimiter' | 'code' | 'link-label' | 'link-url' | 'image-alt';
|
|
25
|
+
/**
|
|
26
|
+
* Inline formatting context, propagated recursively
|
|
27
|
+
* through nesting levels (e.g. bold inside italic).
|
|
28
|
+
*/
|
|
29
|
+
export type InlineContext = {
|
|
30
|
+
bold: boolean;
|
|
31
|
+
italic: boolean;
|
|
32
|
+
strikethrough: boolean;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Intermediate segment produced by the inline parser.
|
|
36
|
+
* Contains both semantic type and formatting context,
|
|
37
|
+
* which will be combined to produce the final semantic segment.
|
|
38
|
+
*/
|
|
39
|
+
export type RawInlineSegment = {
|
|
40
|
+
text: string;
|
|
41
|
+
type: InlineSegmentType;
|
|
42
|
+
bold: boolean;
|
|
43
|
+
italic: boolean;
|
|
44
|
+
strikethrough: boolean;
|
|
45
|
+
/** Optional metadata for image segments. */
|
|
46
|
+
meta?: Record<string, string>;
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=markdownHighlight.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdownHighlight.types.d.ts","sourceRoot":"","sources":["../src/markdownHighlight.types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAC7B,MAAM,GACN,WAAW,GACX,SAAS,GACT,MAAM,GACN,QAAQ,GACR,eAAe,GACf,MAAM,GACN,WAAW,GACX,MAAM,GACN,SAAS,GACT,OAAO,GACP,OAAO,GACP,aAAa,GACb,YAAY,GACZ,gBAAgB,CAAC;AAEpB,8EAA8E;AAC9E,MAAM,MAAM,gBAAgB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,oBAAoB,CAAC;IAC3B,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B,CAAC;AAMF;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,YAAY,GAAG,UAAU,GAAG,WAAW,CAAC;AAExG;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;IACvB,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the markdown syntax-highlighting module.
|
|
3
|
+
*
|
|
4
|
+
* Public types (`HighlightSegmentType`, `HighlightSegment`) are re-exported
|
|
5
|
+
* from `index.ts` for consumers who provide custom segment components.
|
|
6
|
+
* Internal types are used only within `markdownHighlight.ts`.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=markdownHighlight.types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdownHighlight.types.js","sourceRoot":"","sources":["../src/markdownHighlight.types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG","sourcesContent":["/**\n * Type definitions for the markdown syntax-highlighting module.\n *\n * Public types (`HighlightSegmentType`, `HighlightSegment`) are re-exported\n * from `index.ts` for consumers who provide custom segment components.\n * Internal types are used only within `markdownHighlight.ts`.\n */\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Semantic type of a highlight segment.\n * Exposed to consumers so they can provide custom render components per type.\n */\nexport type HighlightSegmentType =\n\t| 'text'\n\t| 'delimiter'\n\t| 'heading'\n\t| 'bold'\n\t| 'italic'\n\t| 'strikethrough'\n\t| 'code'\n\t| 'codeBlock'\n\t| 'link'\n\t| 'linkUrl'\n\t| 'image'\n\t| 'quote'\n\t| 'quoteMarker'\n\t| 'listMarker'\n\t| 'horizontalRule';\n\n/** Semantic highlight segment, ready for rendering via segment components. */\nexport type HighlightSegment = {\n\ttext: string;\n\ttype: HighlightSegmentType;\n\t/** Optional metadata (e.g. image src, alt, title). */\n\tmeta?: Record<string, string>;\n};\n\n// ---------------------------------------------------------------------------\n// Internal types (used only inside markdownHighlight.ts)\n// ---------------------------------------------------------------------------\n\n/**\n * Semantic type of an inline segment.\n * Internally used to map inline tokens to public segment types.\n */\nexport type InlineSegmentType = 'text' | 'delimiter' | 'code' | 'link-label' | 'link-url' | 'image-alt';\n\n/**\n * Inline formatting context, propagated recursively\n * through nesting levels (e.g. bold inside italic).\n */\nexport type InlineContext = {\n\tbold: boolean;\n\titalic: boolean;\n\tstrikethrough: boolean;\n};\n\n/**\n * Intermediate segment produced by the inline parser.\n * Contains both semantic type and formatting context,\n * which will be combined to produce the final semantic segment.\n */\nexport type RawInlineSegment = {\n\ttext: string;\n\ttype: InlineSegmentType;\n\tbold: boolean;\n\titalic: boolean;\n\tstrikethrough: boolean;\n\t/** Optional metadata for image segments. */\n\tmeta?: Record<string, string>;\n};\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { MarkdownBlockNode, MarkdownInlineNode, MarkdownToolbarAction } from './markdownCore.types';
|
|
2
|
+
/**
|
|
3
|
+
* Parses markdown text into block nodes.
|
|
4
|
+
*
|
|
5
|
+
* Parsing is line-oriented: block-level constructs are identified first,
|
|
6
|
+
* then text fragments are delegated to the inline parser.
|
|
7
|
+
*/
|
|
8
|
+
export declare function parseMarkdown(markdown: string, features?: MarkdownToolbarAction[]): MarkdownBlockNode[];
|
|
9
|
+
/**
|
|
10
|
+
* Parses inline markdown tokens inside a block.
|
|
11
|
+
*
|
|
12
|
+
* The parser scans left-to-right and uses recursion for nested emphasis
|
|
13
|
+
* and link labels.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseMarkdownInline(content: string, features?: MarkdownToolbarAction[]): MarkdownInlineNode[];
|
|
16
|
+
//# sourceMappingURL=markdownParser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdownParser.d.ts","sourceRoot":"","sources":["../src/markdownParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAmBzG;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,qBAAqB,EAAE,GAAG,iBAAiB,EAAE,CAsHvG;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,qBAAqB,EAAE,GAAG,kBAAkB,EAAE,CA2I7G"}
|