@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.
Files changed (76) hide show
  1. package/.eslintrc.js +5 -0
  2. package/README.md +265 -0
  3. package/build/MarkdownRenderer.d.ts +12 -0
  4. package/build/MarkdownRenderer.d.ts.map +1 -0
  5. package/build/MarkdownRenderer.js +165 -0
  6. package/build/MarkdownRenderer.js.map +1 -0
  7. package/build/MarkdownTextInput.d.ts +10 -0
  8. package/build/MarkdownTextInput.d.ts.map +1 -0
  9. package/build/MarkdownTextInput.js +233 -0
  10. package/build/MarkdownTextInput.js.map +1 -0
  11. package/build/MarkdownToolbar.d.ts +11 -0
  12. package/build/MarkdownToolbar.d.ts.map +1 -0
  13. package/build/MarkdownToolbar.js +98 -0
  14. package/build/MarkdownToolbar.js.map +1 -0
  15. package/build/index.d.ts +14 -0
  16. package/build/index.d.ts.map +1 -0
  17. package/build/index.js +11 -0
  18. package/build/index.js.map +1 -0
  19. package/build/markdownCore.types.d.ts +321 -0
  20. package/build/markdownCore.types.d.ts.map +1 -0
  21. package/build/markdownCore.types.js +2 -0
  22. package/build/markdownCore.types.js.map +1 -0
  23. package/build/markdownHighlight.d.ts +31 -0
  24. package/build/markdownHighlight.d.ts.map +1 -0
  25. package/build/markdownHighlight.js +378 -0
  26. package/build/markdownHighlight.js.map +1 -0
  27. package/build/markdownHighlight.types.d.ts +48 -0
  28. package/build/markdownHighlight.types.d.ts.map +1 -0
  29. package/build/markdownHighlight.types.js +9 -0
  30. package/build/markdownHighlight.types.js.map +1 -0
  31. package/build/markdownParser.d.ts +16 -0
  32. package/build/markdownParser.d.ts.map +1 -0
  33. package/build/markdownParser.js +309 -0
  34. package/build/markdownParser.js.map +1 -0
  35. package/build/markdownRendererDefaults.d.ts +113 -0
  36. package/build/markdownRendererDefaults.d.ts.map +1 -0
  37. package/build/markdownRendererDefaults.js +174 -0
  38. package/build/markdownRendererDefaults.js.map +1 -0
  39. package/build/markdownSegment.types.d.ts +22 -0
  40. package/build/markdownSegment.types.d.ts.map +1 -0
  41. package/build/markdownSegment.types.js +2 -0
  42. package/build/markdownSegment.types.js.map +1 -0
  43. package/build/markdownSegmentDefaults.d.ts +43 -0
  44. package/build/markdownSegmentDefaults.d.ts.map +1 -0
  45. package/build/markdownSegmentDefaults.js +176 -0
  46. package/build/markdownSegmentDefaults.js.map +1 -0
  47. package/build/markdownSyntaxUtils.d.ts +58 -0
  48. package/build/markdownSyntaxUtils.d.ts.map +1 -0
  49. package/build/markdownSyntaxUtils.js +98 -0
  50. package/build/markdownSyntaxUtils.js.map +1 -0
  51. package/build/markdownToolbarActions.d.ts +12 -0
  52. package/build/markdownToolbarActions.d.ts.map +1 -0
  53. package/build/markdownToolbarActions.js +212 -0
  54. package/build/markdownToolbarActions.js.map +1 -0
  55. package/build/useMarkdownEditor.d.ts +10 -0
  56. package/build/useMarkdownEditor.d.ts.map +1 -0
  57. package/build/useMarkdownEditor.js +219 -0
  58. package/build/useMarkdownEditor.js.map +1 -0
  59. package/jest.config.js +10 -0
  60. package/package.json +45 -0
  61. package/src/MarkdownRenderer.tsx +240 -0
  62. package/src/MarkdownTextInput.tsx +263 -0
  63. package/src/MarkdownToolbar.tsx +126 -0
  64. package/src/index.ts +31 -0
  65. package/src/markdownCore.types.ts +405 -0
  66. package/src/markdownHighlight.ts +413 -0
  67. package/src/markdownHighlight.types.ts +75 -0
  68. package/src/markdownParser.ts +345 -0
  69. package/src/markdownRendererDefaults.tsx +207 -0
  70. package/src/markdownSegment.types.ts +24 -0
  71. package/src/markdownSegmentDefaults.tsx +208 -0
  72. package/src/markdownSyntaxUtils.ts +139 -0
  73. package/src/markdownToolbarActions.ts +296 -0
  74. package/src/useMarkdownEditor.ts +265 -0
  75. package/tsconfig.json +9 -0
  76. package/tsconfig.test.json +8 -0
@@ -0,0 +1,309 @@
1
+ import { findUnescapedToken, parseImageSourceAndTitle, unescapeMarkdown, isEscaped, isMarkdownFeatureEnabled, isHeadingLevelEnabled } from './markdownSyntaxUtils';
2
+ // ---------------------------------------------------------------------------
3
+ // Regex patterns for block recognition
4
+ // ---------------------------------------------------------------------------
5
+ const FENCE_PATTERN = /^```([\w-]+)?\s*$/;
6
+ const FENCE_CLOSE_PATTERN = /^```\s*$/;
7
+ const HEADING_PATTERN = /^(#{1,6})\s+(.+)$/;
8
+ const BLOCKQUOTE_PATTERN = /^>\s?(.*)$/;
9
+ const HORIZONTAL_RULE_PATTERN = /^ {0,3}([-*_])[ \t]*(?:\1[ \t]*){2,}$/;
10
+ const UNORDERED_LIST_PATTERN = /^\s*[-*+]\s+(.*)$/;
11
+ const ORDERED_LIST_PATTERN = /^\s*\d+\.\s+(.*)$/;
12
+ // ---------------------------------------------------------------------------
13
+ // Public API
14
+ // ---------------------------------------------------------------------------
15
+ /**
16
+ * Parses markdown text into block nodes.
17
+ *
18
+ * Parsing is line-oriented: block-level constructs are identified first,
19
+ * then text fragments are delegated to the inline parser.
20
+ */
21
+ export function parseMarkdown(markdown, features) {
22
+ const lines = markdown.replace(/\r\n?/g, '\n').split('\n');
23
+ const blocks = [];
24
+ let cursor = 0;
25
+ while (cursor < lines.length) {
26
+ const line = lines[cursor] ?? '';
27
+ if (line.trim().length === 0) {
28
+ blocks.push({ type: 'spacer' });
29
+ cursor += 1;
30
+ continue;
31
+ }
32
+ // Fenced code block: ```lang ... ```
33
+ if (isMarkdownFeatureEnabled(features, 'codeBlock')) {
34
+ const fenceMatch = line.match(FENCE_PATTERN);
35
+ if (fenceMatch) {
36
+ const closingFenceIndex = findClosingFence(lines, cursor + 1);
37
+ if (closingFenceIndex !== -1) {
38
+ blocks.push({
39
+ type: 'codeBlock',
40
+ language: fenceMatch[1],
41
+ content: lines.slice(cursor + 1, closingFenceIndex).join('\n'),
42
+ });
43
+ cursor = closingFenceIndex + 1;
44
+ continue;
45
+ }
46
+ }
47
+ }
48
+ // Heading: # ... ######
49
+ const headingMatch = line.match(HEADING_PATTERN);
50
+ if (headingMatch && !isEscaped(line, 0) && isHeadingLevelEnabled(features, headingMatch[1].length)) {
51
+ const level = headingMatch[1].length;
52
+ blocks.push({
53
+ type: 'heading',
54
+ level,
55
+ children: parseMarkdownInline(headingMatch[2], features),
56
+ });
57
+ cursor += 1;
58
+ continue;
59
+ }
60
+ // Horizontal rule: ---, ***, ___
61
+ if (isMarkdownFeatureEnabled(features, 'divider') && HORIZONTAL_RULE_PATTERN.test(line)) {
62
+ blocks.push({ type: 'horizontalRule' });
63
+ cursor += 1;
64
+ continue;
65
+ }
66
+ // Blockquote: > quote
67
+ if (isMarkdownFeatureEnabled(features, 'quote') && BLOCKQUOTE_PATTERN.test(line)) {
68
+ const quotedLines = [];
69
+ while (cursor < lines.length) {
70
+ const quoteMatch = lines[cursor]?.match(BLOCKQUOTE_PATTERN);
71
+ if (!quoteMatch) {
72
+ break;
73
+ }
74
+ quotedLines.push(quoteMatch[1]);
75
+ cursor += 1;
76
+ }
77
+ blocks.push({
78
+ type: 'blockquote',
79
+ children: parseMarkdownInline(quotedLines.join('\n'), features),
80
+ });
81
+ continue;
82
+ }
83
+ // List: bullet (-, *, +) or ordered (1.)
84
+ if ((isMarkdownFeatureEnabled(features, 'unorderedList') && UNORDERED_LIST_PATTERN.test(line)) ||
85
+ (isMarkdownFeatureEnabled(features, 'orderedList') && ORDERED_LIST_PATTERN.test(line))) {
86
+ const ordered = ORDERED_LIST_PATTERN.test(line);
87
+ const itemPattern = ordered ? ORDERED_LIST_PATTERN : UNORDERED_LIST_PATTERN;
88
+ const items = [];
89
+ while (cursor < lines.length) {
90
+ const itemMatch = lines[cursor]?.match(itemPattern);
91
+ if (!itemMatch) {
92
+ break;
93
+ }
94
+ items.push(parseMarkdownInline(itemMatch[1], features));
95
+ cursor += 1;
96
+ }
97
+ blocks.push({
98
+ type: 'list',
99
+ ordered,
100
+ items,
101
+ });
102
+ continue;
103
+ }
104
+ // Paragraph: consecutive non-empty lines until next block start
105
+ const paragraphLines = [];
106
+ while (cursor < lines.length) {
107
+ const currentLine = lines[cursor] ?? '';
108
+ if (currentLine.trim().length === 0 || isBlockStart(lines, cursor, features)) {
109
+ break;
110
+ }
111
+ paragraphLines.push(currentLine);
112
+ cursor += 1;
113
+ }
114
+ if (paragraphLines.length > 0) {
115
+ blocks.push({
116
+ type: 'paragraph',
117
+ children: parseMarkdownInline(paragraphLines.join('\n'), features),
118
+ });
119
+ continue;
120
+ }
121
+ cursor += 1;
122
+ }
123
+ return blocks;
124
+ }
125
+ /**
126
+ * Parses inline markdown tokens inside a block.
127
+ *
128
+ * The parser scans left-to-right and uses recursion for nested emphasis
129
+ * and link labels.
130
+ */
131
+ export function parseMarkdownInline(content, features) {
132
+ const nodes = [];
133
+ let cursor = 0;
134
+ const appendText = (value) => {
135
+ if (!value) {
136
+ return;
137
+ }
138
+ const lastNode = nodes[nodes.length - 1];
139
+ if (lastNode?.type === 'text') {
140
+ lastNode.content += value;
141
+ return;
142
+ }
143
+ // Keep adjacent plain text in a single node to reduce fragmentation.
144
+ nodes.push({ type: 'text', content: value });
145
+ };
146
+ while (cursor < content.length) {
147
+ // Escape: \* -> literal *
148
+ if (content[cursor] === '\\' && cursor + 1 < content.length) {
149
+ appendText(content[cursor + 1] ?? '');
150
+ cursor += 2;
151
+ continue;
152
+ }
153
+ // Inline code: `code`
154
+ if (isMarkdownFeatureEnabled(features, 'code') && content[cursor] === '`') {
155
+ const closingIndex = findUnescapedToken(content, '`', cursor + 1);
156
+ if (closingIndex !== -1) {
157
+ nodes.push({
158
+ type: 'code',
159
+ content: unescapeMarkdown(content.slice(cursor + 1, closingIndex)),
160
+ });
161
+ cursor = closingIndex + 1;
162
+ continue;
163
+ }
164
+ }
165
+ // Bold: **text** or __text__
166
+ if (isMarkdownFeatureEnabled(features, 'bold') && (content.startsWith('**', cursor) || content.startsWith('__', cursor))) {
167
+ const marker = content.slice(cursor, cursor + 2);
168
+ const closingIndex = findUnescapedToken(content, marker, cursor + marker.length);
169
+ if (closingIndex !== -1) {
170
+ nodes.push({
171
+ type: 'bold',
172
+ children: parseMarkdownInline(content.slice(cursor + marker.length, closingIndex), features),
173
+ });
174
+ cursor = closingIndex + marker.length;
175
+ continue;
176
+ }
177
+ }
178
+ // Strikethrough: ~~text~~
179
+ if (isMarkdownFeatureEnabled(features, 'strikethrough') && content.startsWith('~~', cursor)) {
180
+ const closingIndex = findUnescapedToken(content, '~~', cursor + 2);
181
+ if (closingIndex !== -1) {
182
+ nodes.push({
183
+ type: 'strikethrough',
184
+ children: parseMarkdownInline(content.slice(cursor + 2, closingIndex), features),
185
+ });
186
+ cursor = closingIndex + 2;
187
+ continue;
188
+ }
189
+ }
190
+ // Italic: *text* or _text_
191
+ if (isMarkdownFeatureEnabled(features, 'italic') && (content[cursor] === '*' || content[cursor] === '_')) {
192
+ const marker = content[cursor];
193
+ const closingIndex = findUnescapedToken(content, marker, cursor + 1);
194
+ if (closingIndex !== -1) {
195
+ nodes.push({
196
+ type: 'italic',
197
+ children: parseMarkdownInline(content.slice(cursor + 1, closingIndex), features),
198
+ });
199
+ cursor = closingIndex + 1;
200
+ continue;
201
+ }
202
+ }
203
+ // Image: ![alt](url) or ![alt](url "title")
204
+ if (content[cursor] === '!' && content[cursor + 1] === '[') {
205
+ if (isMarkdownFeatureEnabled(features, 'image')) {
206
+ const altClosingIndex = findUnescapedToken(content, ']', cursor + 2);
207
+ const srcOpenIndex = altClosingIndex + 1;
208
+ if (altClosingIndex !== -1 && content[srcOpenIndex] === '(') {
209
+ const srcClosingIndex = findUnescapedToken(content, ')', srcOpenIndex + 1);
210
+ if (srcClosingIndex !== -1) {
211
+ const alt = unescapeMarkdown(content.slice(cursor + 2, altClosingIndex));
212
+ const raw = content.slice(srcOpenIndex + 1, srcClosingIndex);
213
+ const { src, title } = parseImageSourceAndTitle(raw);
214
+ nodes.push({ type: 'image', src, alt, title });
215
+ cursor = srcClosingIndex + 1;
216
+ continue;
217
+ }
218
+ }
219
+ }
220
+ else {
221
+ // Image disabled: consume the whole "![alt](url)" as plain text
222
+ // so the link parser does not pick up "[alt](url)" as a link.
223
+ const altClosingIndex = findUnescapedToken(content, ']', cursor + 2);
224
+ if (altClosingIndex !== -1 && content[altClosingIndex + 1] === '(') {
225
+ const srcClosingIndex = findUnescapedToken(content, ')', altClosingIndex + 2);
226
+ if (srcClosingIndex !== -1) {
227
+ appendText(content.slice(cursor, srcClosingIndex + 1));
228
+ cursor = srcClosingIndex + 1;
229
+ continue;
230
+ }
231
+ }
232
+ appendText('!');
233
+ cursor += 1;
234
+ continue;
235
+ }
236
+ }
237
+ // Link: [label](url)
238
+ if (content[cursor] === '[') {
239
+ const labelClosingIndex = findUnescapedToken(content, ']', cursor + 1);
240
+ const linkOpenIndex = labelClosingIndex + 1;
241
+ if (labelClosingIndex !== -1 && content[linkOpenIndex] === '(') {
242
+ const hrefClosingIndex = findUnescapedToken(content, ')', linkOpenIndex + 1);
243
+ if (hrefClosingIndex !== -1) {
244
+ nodes.push({
245
+ type: 'link',
246
+ href: unescapeMarkdown(content.slice(linkOpenIndex + 1, hrefClosingIndex)),
247
+ children: parseMarkdownInline(content.slice(cursor + 1, labelClosingIndex), features),
248
+ });
249
+ cursor = hrefClosingIndex + 1;
250
+ continue;
251
+ }
252
+ }
253
+ }
254
+ // Simple text character.
255
+ appendText(content[cursor] ?? '');
256
+ cursor += 1;
257
+ }
258
+ return nodes;
259
+ }
260
+ // ---------------------------------------------------------------------------
261
+ // Internal helpers
262
+ // ---------------------------------------------------------------------------
263
+ /**
264
+ * Determines whether the line at `index` begins a new block-level construct
265
+ * (heading, rule, quote, list, or fenced code block).
266
+ *
267
+ * Used by the paragraph collector to know when to stop consuming lines.
268
+ */
269
+ function isBlockStart(lines, index, features) {
270
+ const line = lines[index] ?? '';
271
+ if (line.trim().length === 0) {
272
+ return false;
273
+ }
274
+ if (isMarkdownFeatureEnabled(features, 'heading') && HEADING_PATTERN.test(line)) {
275
+ const m = line.match(HEADING_PATTERN);
276
+ if (m && isHeadingLevelEnabled(features, m[1].length)) {
277
+ return true;
278
+ }
279
+ }
280
+ if (isMarkdownFeatureEnabled(features, 'divider') && HORIZONTAL_RULE_PATTERN.test(line)) {
281
+ return true;
282
+ }
283
+ if (isMarkdownFeatureEnabled(features, 'quote') && BLOCKQUOTE_PATTERN.test(line)) {
284
+ return true;
285
+ }
286
+ if ((isMarkdownFeatureEnabled(features, 'unorderedList') && UNORDERED_LIST_PATTERN.test(line)) ||
287
+ (isMarkdownFeatureEnabled(features, 'orderedList') && ORDERED_LIST_PATTERN.test(line))) {
288
+ return true;
289
+ }
290
+ if (isMarkdownFeatureEnabled(features, 'codeBlock') && FENCE_PATTERN.test(line)) {
291
+ // Avoid treating an unmatched opening fence as a block boundary.
292
+ return findClosingFence(lines, index + 1) !== -1;
293
+ }
294
+ return false;
295
+ }
296
+ /**
297
+ * Scans forward from `startIndex` looking for a closing code fence (` ``` `).
298
+ *
299
+ * @returns The line index of the closing fence, or `-1` if none is found.
300
+ */
301
+ function findClosingFence(lines, startIndex) {
302
+ for (let index = startIndex; index < lines.length; index += 1) {
303
+ if (FENCE_CLOSE_PATTERN.test(lines[index] ?? '')) {
304
+ return index;
305
+ }
306
+ }
307
+ return -1;
308
+ }
309
+ //# sourceMappingURL=markdownParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdownParser.js","sourceRoot":"","sources":["../src/markdownParser.ts"],"names":[],"mappings":"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","sourcesContent":["import type { MarkdownBlockNode, MarkdownInlineNode, MarkdownToolbarAction } from './markdownCore.types';\nimport { findUnescapedToken, parseImageSourceAndTitle, unescapeMarkdown, isEscaped, isMarkdownFeatureEnabled, isHeadingLevelEnabled } from './markdownSyntaxUtils';\n\n// ---------------------------------------------------------------------------\n// Regex patterns for block recognition\n// ---------------------------------------------------------------------------\n\nconst FENCE_PATTERN = /^```([\\w-]+)?\\s*$/;\nconst FENCE_CLOSE_PATTERN = /^```\\s*$/;\nconst HEADING_PATTERN = /^(#{1,6})\\s+(.+)$/;\nconst BLOCKQUOTE_PATTERN = /^>\\s?(.*)$/;\nconst HORIZONTAL_RULE_PATTERN = /^ {0,3}([-*_])[ \\t]*(?:\\1[ \\t]*){2,}$/;\nconst UNORDERED_LIST_PATTERN = /^\\s*[-*+]\\s+(.*)$/;\nconst ORDERED_LIST_PATTERN = /^\\s*\\d+\\.\\s+(.*)$/;\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Parses markdown text into block nodes.\n *\n * Parsing is line-oriented: block-level constructs are identified first,\n * then text fragments are delegated to the inline parser.\n */\nexport function parseMarkdown(markdown: string, features?: MarkdownToolbarAction[]): MarkdownBlockNode[] {\n\tconst lines = markdown.replace(/\\r\\n?/g, '\\n').split('\\n');\n\tconst blocks: MarkdownBlockNode[] = [];\n\tlet cursor = 0;\n\n\twhile (cursor < lines.length) {\n\t\tconst line = lines[cursor] ?? '';\n\n\t\tif (line.trim().length === 0) {\n\t\t\tblocks.push({ type: 'spacer' });\n\t\t\tcursor += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Fenced code block: ```lang ... ```\n\t\tif (isMarkdownFeatureEnabled(features, 'codeBlock')) {\n\t\t\tconst fenceMatch = line.match(FENCE_PATTERN);\n\t\t\tif (fenceMatch) {\n\t\t\t\tconst closingFenceIndex = findClosingFence(lines, cursor + 1);\n\t\t\t\tif (closingFenceIndex !== -1) {\n\t\t\t\t\tblocks.push({\n\t\t\t\t\t\ttype: 'codeBlock',\n\t\t\t\t\t\tlanguage: fenceMatch[1],\n\t\t\t\t\t\tcontent: lines.slice(cursor + 1, closingFenceIndex).join('\\n'),\n\t\t\t\t\t});\n\t\t\t\t\tcursor = closingFenceIndex + 1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Heading: # ... ######\n\t\tconst headingMatch = line.match(HEADING_PATTERN);\n\t\tif (headingMatch && !isEscaped(line, 0) && isHeadingLevelEnabled(features, headingMatch[1].length)) {\n\t\t\tconst level = headingMatch[1].length as 1 | 2 | 3 | 4 | 5 | 6;\n\t\t\tblocks.push({\n\t\t\t\ttype: 'heading',\n\t\t\t\tlevel,\n\t\t\t\tchildren: parseMarkdownInline(headingMatch[2], features),\n\t\t\t});\n\t\t\tcursor += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Horizontal rule: ---, ***, ___\n\t\tif (isMarkdownFeatureEnabled(features, 'divider') && HORIZONTAL_RULE_PATTERN.test(line)) {\n\t\t\tblocks.push({ type: 'horizontalRule' });\n\t\t\tcursor += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Blockquote: > quote\n\t\tif (isMarkdownFeatureEnabled(features, 'quote') && BLOCKQUOTE_PATTERN.test(line)) {\n\t\t\tconst quotedLines: string[] = [];\n\t\t\twhile (cursor < lines.length) {\n\t\t\t\tconst quoteMatch = lines[cursor]?.match(BLOCKQUOTE_PATTERN);\n\t\t\t\tif (!quoteMatch) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tquotedLines.push(quoteMatch[1]);\n\t\t\t\tcursor += 1;\n\t\t\t}\n\t\t\tblocks.push({\n\t\t\t\ttype: 'blockquote',\n\t\t\t\tchildren: parseMarkdownInline(quotedLines.join('\\n'), features),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// List: bullet (-, *, +) or ordered (1.)\n\t\tif (\n\t\t\t(isMarkdownFeatureEnabled(features, 'unorderedList') && UNORDERED_LIST_PATTERN.test(line)) ||\n\t\t\t(isMarkdownFeatureEnabled(features, 'orderedList') && ORDERED_LIST_PATTERN.test(line))\n\t\t) {\n\t\t\tconst ordered = ORDERED_LIST_PATTERN.test(line);\n\t\t\tconst itemPattern = ordered ? ORDERED_LIST_PATTERN : UNORDERED_LIST_PATTERN;\n\t\t\tconst items: MarkdownInlineNode[][] = [];\n\n\t\t\twhile (cursor < lines.length) {\n\t\t\t\tconst itemMatch = lines[cursor]?.match(itemPattern);\n\t\t\t\tif (!itemMatch) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\titems.push(parseMarkdownInline(itemMatch[1], features));\n\t\t\t\tcursor += 1;\n\t\t\t}\n\n\t\t\tblocks.push({\n\t\t\t\ttype: 'list',\n\t\t\t\tordered,\n\t\t\t\titems,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Paragraph: consecutive non-empty lines until next block start\n\t\tconst paragraphLines: string[] = [];\n\t\twhile (cursor < lines.length) {\n\t\t\tconst currentLine = lines[cursor] ?? '';\n\t\t\tif (currentLine.trim().length === 0 || isBlockStart(lines, cursor, features)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tparagraphLines.push(currentLine);\n\t\t\tcursor += 1;\n\t\t}\n\n\t\tif (paragraphLines.length > 0) {\n\t\t\tblocks.push({\n\t\t\t\ttype: 'paragraph',\n\t\t\t\tchildren: parseMarkdownInline(paragraphLines.join('\\n'), features),\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tcursor += 1;\n\t}\n\n\treturn blocks;\n}\n\n/**\n * Parses inline markdown tokens inside a block.\n *\n * The parser scans left-to-right and uses recursion for nested emphasis\n * and link labels.\n */\nexport function parseMarkdownInline(content: string, features?: MarkdownToolbarAction[]): MarkdownInlineNode[] {\n\tconst nodes: MarkdownInlineNode[] = [];\n\tlet cursor = 0;\n\n\tconst appendText = (value: string) => {\n\t\tif (!value) {\n\t\t\treturn;\n\t\t}\n\t\tconst lastNode = nodes[nodes.length - 1];\n\t\tif (lastNode?.type === 'text') {\n\t\t\tlastNode.content += value;\n\t\t\treturn;\n\t\t}\n\t\t// Keep adjacent plain text in a single node to reduce fragmentation.\n\t\tnodes.push({ type: 'text', content: value });\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\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 closingIndex = findUnescapedToken(content, '`', cursor + 1);\n\t\t\tif (closingIndex !== -1) {\n\t\t\t\tnodes.push({\n\t\t\t\t\ttype: 'code',\n\t\t\t\t\tcontent: unescapeMarkdown(content.slice(cursor + 1, closingIndex)),\n\t\t\t\t});\n\t\t\t\tcursor = closingIndex + 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 closingIndex = findUnescapedToken(content, marker, cursor + marker.length);\n\t\t\tif (closingIndex !== -1) {\n\t\t\t\tnodes.push({\n\t\t\t\t\ttype: 'bold',\n\t\t\t\t\tchildren: parseMarkdownInline(content.slice(cursor + marker.length, closingIndex), features),\n\t\t\t\t});\n\t\t\t\tcursor = closingIndex + marker.length;\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 closingIndex = findUnescapedToken(content, '~~', cursor + 2);\n\t\t\tif (closingIndex !== -1) {\n\t\t\t\tnodes.push({\n\t\t\t\t\ttype: 'strikethrough',\n\t\t\t\t\tchildren: parseMarkdownInline(content.slice(cursor + 2, closingIndex), features),\n\t\t\t\t});\n\t\t\t\tcursor = closingIndex + 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 closingIndex = findUnescapedToken(content, marker, cursor + 1);\n\t\t\tif (closingIndex !== -1) {\n\t\t\t\tnodes.push({\n\t\t\t\t\ttype: 'italic',\n\t\t\t\t\tchildren: parseMarkdownInline(content.slice(cursor + 1, closingIndex), features),\n\t\t\t\t});\n\t\t\t\tcursor = closingIndex + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t// Image: ![alt](url) or ![alt](url \"title\")\n\t\tif (content[cursor] === '!' && content[cursor + 1] === '[') {\n\t\t\tif (isMarkdownFeatureEnabled(features, 'image')) {\n\t\t\t\tconst altClosingIndex = findUnescapedToken(content, ']', cursor + 2);\n\t\t\t\tconst srcOpenIndex = altClosingIndex + 1;\n\n\t\t\t\tif (altClosingIndex !== -1 && content[srcOpenIndex] === '(') {\n\t\t\t\t\tconst srcClosingIndex = findUnescapedToken(content, ')', srcOpenIndex + 1);\n\t\t\t\t\tif (srcClosingIndex !== -1) {\n\t\t\t\t\t\tconst alt = unescapeMarkdown(content.slice(cursor + 2, altClosingIndex));\n\t\t\t\t\t\tconst raw = content.slice(srcOpenIndex + 1, srcClosingIndex);\n\t\t\t\t\t\tconst { src, title } = parseImageSourceAndTitle(raw);\n\t\t\t\t\t\tnodes.push({ type: 'image', src, alt, title });\n\t\t\t\t\t\tcursor = srcClosingIndex + 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 the whole \"![alt](url)\" as plain text\n\t\t\t\t// so the link parser does not pick up \"[alt](url)\" as a link.\n\t\t\t\tconst altClosingIndex = findUnescapedToken(content, ']', cursor + 2);\n\t\t\t\tif (altClosingIndex !== -1 && content[altClosingIndex + 1] === '(') {\n\t\t\t\t\tconst srcClosingIndex = findUnescapedToken(content, ')', altClosingIndex + 2);\n\t\t\t\t\tif (srcClosingIndex !== -1) {\n\t\t\t\t\t\tappendText(content.slice(cursor, srcClosingIndex + 1));\n\t\t\t\t\t\tcursor = srcClosingIndex + 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 labelClosingIndex = findUnescapedToken(content, ']', cursor + 1);\n\t\t\tconst linkOpenIndex = labelClosingIndex + 1;\n\n\t\t\tif (labelClosingIndex !== -1 && content[linkOpenIndex] === '(') {\n\t\t\t\tconst hrefClosingIndex = findUnescapedToken(content, ')', linkOpenIndex + 1);\n\t\t\t\tif (hrefClosingIndex !== -1) {\n\t\t\t\t\tnodes.push({\n\t\t\t\t\t\ttype: 'link',\n\t\t\t\t\t\thref: unescapeMarkdown(content.slice(linkOpenIndex + 1, hrefClosingIndex)),\n\t\t\t\t\t\tchildren: parseMarkdownInline(content.slice(cursor + 1, labelClosingIndex), features),\n\t\t\t\t\t});\n\t\t\t\t\tcursor = hrefClosingIndex + 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 nodes;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Determines whether the line at `index` begins a new block-level construct\n * (heading, rule, quote, list, or fenced code block).\n *\n * Used by the paragraph collector to know when to stop consuming lines.\n */\nfunction isBlockStart(lines: string[], index: number, features?: MarkdownToolbarAction[]): boolean {\n\tconst line = lines[index] ?? '';\n\tif (line.trim().length === 0) {\n\t\treturn false;\n\t}\n\tif (isMarkdownFeatureEnabled(features, 'heading') && HEADING_PATTERN.test(line)) {\n\t\tconst m = line.match(HEADING_PATTERN);\n\t\tif (m && isHeadingLevelEnabled(features, m[1].length)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\tif (isMarkdownFeatureEnabled(features, 'divider') && HORIZONTAL_RULE_PATTERN.test(line)) {\n\t\treturn true;\n\t}\n\tif (isMarkdownFeatureEnabled(features, 'quote') && BLOCKQUOTE_PATTERN.test(line)) {\n\t\treturn true;\n\t}\n\tif (\n\t\t(isMarkdownFeatureEnabled(features, 'unorderedList') && UNORDERED_LIST_PATTERN.test(line)) ||\n\t\t(isMarkdownFeatureEnabled(features, 'orderedList') && ORDERED_LIST_PATTERN.test(line))\n\t) {\n\t\treturn true;\n\t}\n\tif (isMarkdownFeatureEnabled(features, 'codeBlock') && FENCE_PATTERN.test(line)) {\n\t\t// Avoid treating an unmatched opening fence as a block boundary.\n\t\treturn findClosingFence(lines, index + 1) !== -1;\n\t}\n\treturn false;\n}\n\n/**\n * Scans forward from `startIndex` looking for a closing code fence (` ``` `).\n *\n * @returns The line index of the closing fence, or `-1` if none is found.\n */\nfunction findClosingFence(lines: string[], startIndex: number): number {\n\tfor (let index = startIndex; index < lines.length; index += 1) {\n\t\tif (FENCE_CLOSE_PATTERN.test(lines[index] ?? '')) {\n\t\t\treturn index;\n\t\t}\n\t}\n\treturn -1;\n}\n"]}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Default component implementations and styles for {@link MarkdownRenderer}.
3
+ *
4
+ * Each markdown tag has a corresponding default component that provides
5
+ * basic visual styling. Consumers can override any subset via the
6
+ * `components` prop on `MarkdownRenderer`.
7
+ */
8
+ import type { MarkdownComponentMap } from './markdownCore.types';
9
+ /** Complete map of default markdown rendering components. */
10
+ export declare const DEFAULT_COMPONENTS: MarkdownComponentMap;
11
+ export declare const styles: {
12
+ root: {
13
+ gap: number;
14
+ };
15
+ paragraph: {
16
+ color: string;
17
+ lineHeight: number;
18
+ };
19
+ heading1: {
20
+ fontSize: number;
21
+ fontWeight: "700";
22
+ };
23
+ heading2: {
24
+ fontSize: number;
25
+ fontWeight: "700";
26
+ };
27
+ heading3: {
28
+ fontSize: number;
29
+ fontWeight: "700";
30
+ };
31
+ heading4: {
32
+ fontSize: number;
33
+ fontWeight: "700";
34
+ };
35
+ heading5: {
36
+ fontSize: number;
37
+ fontWeight: "700";
38
+ };
39
+ heading6: {
40
+ fontSize: number;
41
+ fontWeight: "700";
42
+ };
43
+ bold: {
44
+ fontWeight: "700";
45
+ };
46
+ italic: {
47
+ fontStyle: "italic";
48
+ };
49
+ strikethrough: {
50
+ textDecorationLine: "line-through";
51
+ };
52
+ inlineCode: {
53
+ fontFamily: string;
54
+ backgroundColor: string;
55
+ paddingHorizontal: number;
56
+ borderRadius: number;
57
+ };
58
+ codeBlock: {
59
+ backgroundColor: string;
60
+ borderRadius: number;
61
+ padding: number;
62
+ };
63
+ codeBlockLanguage: {
64
+ alignSelf: "flex-start";
65
+ fontSize: number;
66
+ fontWeight: "700";
67
+ backgroundColor: string;
68
+ borderRadius: number;
69
+ paddingHorizontal: number;
70
+ paddingVertical: number;
71
+ marginBottom: number;
72
+ };
73
+ codeBlockText: {
74
+ fontFamily: string;
75
+ };
76
+ blockquote: {
77
+ borderLeftWidth: number;
78
+ borderLeftColor: string;
79
+ paddingLeft: number;
80
+ };
81
+ horizontalRule: {
82
+ borderBottomWidth: number;
83
+ borderBottomColor: string;
84
+ marginVertical: number;
85
+ };
86
+ list: {
87
+ gap: number;
88
+ };
89
+ listItem: {
90
+ flexDirection: "row";
91
+ };
92
+ link: {
93
+ color: string;
94
+ textDecorationLine: "underline";
95
+ };
96
+ image: {
97
+ width: "100%";
98
+ height: number;
99
+ };
100
+ imageContainer: {
101
+ alignItems: "center";
102
+ };
103
+ imageAlt: {
104
+ fontSize: number;
105
+ color: string;
106
+ marginTop: number;
107
+ textAlign: "center";
108
+ };
109
+ spacer: {
110
+ height: number;
111
+ };
112
+ };
113
+ //# sourceMappingURL=markdownRendererDefaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdownRendererDefaults.d.ts","sourceRoot":"","sources":["../src/markdownRendererDefaults.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAgEjE,6DAA6D;AAC7D,eAAO,MAAM,kBAAkB,EAAE,oBAuBhC,CAAC;AAMF,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqGjB,CAAC"}
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Default component implementations and styles for {@link MarkdownRenderer}.
3
+ *
4
+ * Each markdown tag has a corresponding default component that provides
5
+ * basic visual styling. Consumers can override any subset via the
6
+ * `components` prop on `MarkdownRenderer`.
7
+ */
8
+ import * as React from 'react';
9
+ import { Image, StyleSheet, Text, View } from 'react-native';
10
+ // ---------------------------------------------------------------------------
11
+ // Default components
12
+ // ---------------------------------------------------------------------------
13
+ const DefaultRoot = ({ children, style }) => <View style={[styles.root, style]}>{children}</View>;
14
+ const DefaultParagraph = ({ children }) => <Text style={styles.paragraph}>{children}</Text>;
15
+ const DefaultText = ({ children }) => <Text>{children}</Text>;
16
+ const DefaultHeading1 = ({ children }) => <Text style={styles.heading1}>{children}</Text>;
17
+ const DefaultHeading2 = ({ children }) => <Text style={styles.heading2}>{children}</Text>;
18
+ const DefaultHeading3 = ({ children }) => <Text style={styles.heading3}>{children}</Text>;
19
+ const DefaultHeading4 = ({ children }) => <Text style={styles.heading4}>{children}</Text>;
20
+ const DefaultHeading5 = ({ children }) => <Text style={styles.heading5}>{children}</Text>;
21
+ const DefaultHeading6 = ({ children }) => <Text style={styles.heading6}>{children}</Text>;
22
+ const DefaultBold = ({ children }) => <Text style={styles.bold}>{children}</Text>;
23
+ const DefaultItalic = ({ children }) => <Text style={styles.italic}>{children}</Text>;
24
+ const DefaultStrikethrough = ({ children }) => <Text style={styles.strikethrough}>{children}</Text>;
25
+ const DefaultInlineCode = ({ children }) => <Text style={styles.inlineCode}>{children}</Text>;
26
+ const DefaultCodeBlock = ({ text, language }) => (<View style={styles.codeBlock}>
27
+ <Text style={styles.codeBlockLanguage}>{language?.trim() || 'Code'}</Text>
28
+ <Text style={styles.codeBlockText}>{text}</Text>
29
+ </View>);
30
+ const DefaultBlockquote = ({ children }) => <View style={styles.blockquote}>{children}</View>;
31
+ const DefaultHorizontalRule = () => <View style={styles.horizontalRule}/>;
32
+ const DefaultSpacer = () => <View style={styles.spacer}/>;
33
+ const DefaultUnorderedList = ({ children }) => <View style={styles.list}>{children}</View>;
34
+ const DefaultOrderedList = ({ children }) => <View style={styles.list}>{children}</View>;
35
+ const DefaultListItem = ({ children }) => <View style={styles.listItem}>{children}</View>;
36
+ const DefaultLink = ({ children }) => <Text style={styles.link}>{children}</Text>;
37
+ const DefaultImage = ({ src, alt, title }) => (<View style={styles.imageContainer}>
38
+ <Image source={{ uri: src }} style={styles.image} resizeMode="contain" accessibilityLabel={alt ?? title}/>
39
+ {alt ? <Text style={styles.imageAlt}>{alt}</Text> : null}
40
+ </View>);
41
+ // ---------------------------------------------------------------------------
42
+ // Component map
43
+ // ---------------------------------------------------------------------------
44
+ /** Complete map of default markdown rendering components. */
45
+ export const DEFAULT_COMPONENTS = {
46
+ root: DefaultRoot,
47
+ paragraph: DefaultParagraph,
48
+ text: DefaultText,
49
+ heading1: DefaultHeading1,
50
+ heading2: DefaultHeading2,
51
+ heading3: DefaultHeading3,
52
+ heading4: DefaultHeading4,
53
+ heading5: DefaultHeading5,
54
+ heading6: DefaultHeading6,
55
+ bold: DefaultBold,
56
+ italic: DefaultItalic,
57
+ strikethrough: DefaultStrikethrough,
58
+ inlineCode: DefaultInlineCode,
59
+ codeBlock: DefaultCodeBlock,
60
+ blockquote: DefaultBlockquote,
61
+ horizontalRule: DefaultHorizontalRule,
62
+ unorderedList: DefaultUnorderedList,
63
+ orderedList: DefaultOrderedList,
64
+ listItem: DefaultListItem,
65
+ link: DefaultLink,
66
+ image: DefaultImage,
67
+ spacer: DefaultSpacer,
68
+ };
69
+ // ---------------------------------------------------------------------------
70
+ // Styles
71
+ // ---------------------------------------------------------------------------
72
+ export const styles = StyleSheet.create({
73
+ root: {
74
+ gap: 8,
75
+ },
76
+ paragraph: {
77
+ color: '#1f1f1f',
78
+ lineHeight: 22,
79
+ },
80
+ heading1: {
81
+ fontSize: 30,
82
+ fontWeight: '700',
83
+ },
84
+ heading2: {
85
+ fontSize: 26,
86
+ fontWeight: '700',
87
+ },
88
+ heading3: {
89
+ fontSize: 22,
90
+ fontWeight: '700',
91
+ },
92
+ heading4: {
93
+ fontSize: 20,
94
+ fontWeight: '700',
95
+ },
96
+ heading5: {
97
+ fontSize: 18,
98
+ fontWeight: '700',
99
+ },
100
+ heading6: {
101
+ fontSize: 16,
102
+ fontWeight: '700',
103
+ },
104
+ bold: {
105
+ fontWeight: '700',
106
+ },
107
+ italic: {
108
+ fontStyle: 'italic',
109
+ },
110
+ strikethrough: {
111
+ textDecorationLine: 'line-through',
112
+ },
113
+ inlineCode: {
114
+ fontFamily: 'Courier',
115
+ backgroundColor: '#f1f1f1',
116
+ paddingHorizontal: 4,
117
+ borderRadius: 4,
118
+ },
119
+ codeBlock: {
120
+ backgroundColor: '#f1f1f1',
121
+ borderRadius: 8,
122
+ padding: 12,
123
+ },
124
+ codeBlockLanguage: {
125
+ alignSelf: 'flex-start',
126
+ fontSize: 11,
127
+ fontWeight: '700',
128
+ backgroundColor: '#e2e2e2',
129
+ borderRadius: 4,
130
+ paddingHorizontal: 6,
131
+ paddingVertical: 2,
132
+ marginBottom: 8,
133
+ },
134
+ codeBlockText: {
135
+ fontFamily: 'Courier',
136
+ },
137
+ blockquote: {
138
+ borderLeftWidth: 3,
139
+ borderLeftColor: '#acacac',
140
+ paddingLeft: 10,
141
+ },
142
+ horizontalRule: {
143
+ borderBottomWidth: 1,
144
+ borderBottomColor: '#d0d0d0',
145
+ marginVertical: 8,
146
+ },
147
+ list: {
148
+ gap: 4,
149
+ },
150
+ listItem: {
151
+ flexDirection: 'row',
152
+ },
153
+ link: {
154
+ color: '#2d5eff',
155
+ textDecorationLine: 'underline',
156
+ },
157
+ image: {
158
+ width: '100%',
159
+ height: 200,
160
+ },
161
+ imageContainer: {
162
+ alignItems: 'center',
163
+ },
164
+ imageAlt: {
165
+ fontSize: 12,
166
+ color: '#6b6b6b',
167
+ marginTop: 4,
168
+ textAlign: 'center',
169
+ },
170
+ spacer: {
171
+ height: 8,
172
+ },
173
+ });
174
+ //# sourceMappingURL=markdownRendererDefaults.js.map