svelte-streamdown 0.0.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 (98) hide show
  1. package/README.md +58 -0
  2. package/dist/Block.svelte +34 -0
  3. package/dist/Block.svelte.d.ts +6 -0
  4. package/dist/Elements/A.svelte +48 -0
  5. package/dist/Elements/A.svelte.d.ts +4 -0
  6. package/dist/Elements/Alert.svelte +78 -0
  7. package/dist/Elements/Alert.svelte.d.ts +4 -0
  8. package/dist/Elements/Blockquote.svelte +26 -0
  9. package/dist/Elements/Blockquote.svelte.d.ts +4 -0
  10. package/dist/Elements/Code.svelte +123 -0
  11. package/dist/Elements/Code.svelte.d.ts +4 -0
  12. package/dist/Elements/Del.svelte +24 -0
  13. package/dist/Elements/Del.svelte.d.ts +4 -0
  14. package/dist/Elements/Element.svelte +122 -0
  15. package/dist/Elements/Element.svelte.d.ts +12 -0
  16. package/dist/Elements/Em.svelte +23 -0
  17. package/dist/Elements/Em.svelte.d.ts +4 -0
  18. package/dist/Elements/H1.svelte +23 -0
  19. package/dist/Elements/H1.svelte.d.ts +4 -0
  20. package/dist/Elements/H2.svelte +23 -0
  21. package/dist/Elements/H2.svelte.d.ts +4 -0
  22. package/dist/Elements/H3.svelte +23 -0
  23. package/dist/Elements/H3.svelte.d.ts +4 -0
  24. package/dist/Elements/H4.svelte +23 -0
  25. package/dist/Elements/H4.svelte.d.ts +4 -0
  26. package/dist/Elements/H5.svelte +23 -0
  27. package/dist/Elements/H5.svelte.d.ts +4 -0
  28. package/dist/Elements/H6.svelte +23 -0
  29. package/dist/Elements/H6.svelte.d.ts +4 -0
  30. package/dist/Elements/Hr.svelte +20 -0
  31. package/dist/Elements/Hr.svelte.d.ts +4 -0
  32. package/dist/Elements/Image.svelte +49 -0
  33. package/dist/Elements/Image.svelte.d.ts +4 -0
  34. package/dist/Elements/InlineCode.svelte +23 -0
  35. package/dist/Elements/InlineCode.svelte.d.ts +4 -0
  36. package/dist/Elements/Li.svelte +23 -0
  37. package/dist/Elements/Li.svelte.d.ts +4 -0
  38. package/dist/Elements/Math.svelte +73 -0
  39. package/dist/Elements/Math.svelte.d.ts +5 -0
  40. package/dist/Elements/Mermaid.svelte +80 -0
  41. package/dist/Elements/Mermaid.svelte.d.ts +4 -0
  42. package/dist/Elements/Ol.svelte +23 -0
  43. package/dist/Elements/Ol.svelte.d.ts +4 -0
  44. package/dist/Elements/P.svelte +23 -0
  45. package/dist/Elements/P.svelte.d.ts +4 -0
  46. package/dist/Elements/Slot.svelte +19 -0
  47. package/dist/Elements/Slot.svelte.d.ts +9 -0
  48. package/dist/Elements/Strong.svelte +23 -0
  49. package/dist/Elements/Strong.svelte.d.ts +4 -0
  50. package/dist/Elements/Sub.svelte +23 -0
  51. package/dist/Elements/Sub.svelte.d.ts +4 -0
  52. package/dist/Elements/Sup.svelte +23 -0
  53. package/dist/Elements/Sup.svelte.d.ts +4 -0
  54. package/dist/Elements/Table.svelte +25 -0
  55. package/dist/Elements/Table.svelte.d.ts +4 -0
  56. package/dist/Elements/Tbody.svelte +23 -0
  57. package/dist/Elements/Tbody.svelte.d.ts +4 -0
  58. package/dist/Elements/Td.svelte +23 -0
  59. package/dist/Elements/Td.svelte.d.ts +4 -0
  60. package/dist/Elements/Th.svelte +23 -0
  61. package/dist/Elements/Th.svelte.d.ts +4 -0
  62. package/dist/Elements/Thead.svelte +23 -0
  63. package/dist/Elements/Thead.svelte.d.ts +4 -0
  64. package/dist/Elements/Tr.svelte +23 -0
  65. package/dist/Elements/Tr.svelte.d.ts +4 -0
  66. package/dist/Elements/Ul.svelte +23 -0
  67. package/dist/Elements/Ul.svelte.d.ts +4 -0
  68. package/dist/Elements/element.d.ts +8 -0
  69. package/dist/Elements/element.js +1 -0
  70. package/dist/Elements/index.d.ts +34 -0
  71. package/dist/Elements/index.js +34 -0
  72. package/dist/Streamdown.d.ts +83 -0
  73. package/dist/Streamdown.js +1 -0
  74. package/dist/Streamdown.svelte +128 -0
  75. package/dist/Streamdown.svelte.d.ts +18 -0
  76. package/dist/hightlighter.svelte.d.ts +25 -0
  77. package/dist/hightlighter.svelte.js +413 -0
  78. package/dist/index.d.ts +2 -0
  79. package/dist/index.js +2 -0
  80. package/dist/theme.d.ts +247 -0
  81. package/dist/theme.js +138 -0
  82. package/dist/utils/alerts.d.ts +12 -0
  83. package/dist/utils/alerts.js +207 -0
  84. package/dist/utils/bind.d.ts +1 -0
  85. package/dist/utils/bind.js +6 -0
  86. package/dist/utils/copy.svelte.d.ts +7 -0
  87. package/dist/utils/copy.svelte.js +35 -0
  88. package/dist/utils/parse-blocks.d.ts +1 -0
  89. package/dist/utils/parse-blocks.js +40 -0
  90. package/dist/utils/parse-incomplete-markdown.d.ts +1 -0
  91. package/dist/utils/parse-incomplete-markdown.js +442 -0
  92. package/dist/utils/parse.d.ts +3 -0
  93. package/dist/utils/parse.js +144 -0
  94. package/dist/utils/save.d.ts +1 -0
  95. package/dist/utils/save.js +11 -0
  96. package/dist/utils/url.d.ts +2 -0
  97. package/dist/utils/url.js +71 -0
  98. package/package.json +92 -0
package/dist/theme.js ADDED
@@ -0,0 +1,138 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+ export const cn = (...inputs) => twMerge(clsx(inputs));
4
+ export const theme = {
5
+ a: {
6
+ base: 'text-blue-600 font-medium underline',
7
+ blocked: 'text-gray-500'
8
+ },
9
+ h1: {
10
+ base: 'mt-6 mb-2 text-3xl font-semibold'
11
+ },
12
+ h2: {
13
+ base: 'mt-6 mb-2 text-2xl font-semibold'
14
+ },
15
+ h3: {
16
+ base: 'mt-6 mb-2 text-xl font-semibold'
17
+ },
18
+ h4: {
19
+ base: 'mt-6 mb-2 text-lg font-semibold'
20
+ },
21
+ h5: {
22
+ base: 'mt-6 mb-2 text-base font-semibold'
23
+ },
24
+ h6: {
25
+ base: 'mt-6 mb-2 text-sm font-semibold'
26
+ },
27
+ p: {
28
+ base: ''
29
+ },
30
+ ul: {
31
+ base: 'ml-4 list-outside list-disc whitespace-normal'
32
+ },
33
+ ol: {
34
+ base: 'ml-4 list-outside list-decimal whitespace-normal'
35
+ },
36
+ li: {
37
+ base: 'py-1'
38
+ },
39
+ code: {
40
+ base: 'bg-gray-100 rounded p-2 font-mono text-sm',
41
+ container: 'my-4 w-full overflow-hidden rounded-xl border border-gray-200 flex flex-col',
42
+ header: 'flex items-center justify-between bg-gray-100/80 p-2 pb-0 text-gray-600 text-xs',
43
+ button: 'cursor-pointer p-1 text-gray-600 transition-all hover:text-gray-900 size-6 p-1',
44
+ language: 'ml-1 font-mono lowercase',
45
+ skeleton: 'rounded-md font-mono text-transparent bg-gray-200 whitespace-nowrap inline-block',
46
+ pre: 'overflow-x-auto font-mono p-0 bg-gray-100/40'
47
+ },
48
+ inlineCode: {
49
+ base: 'bg-gray-100 rounded px-1.5 py-0.5 font-mono text-sm'
50
+ },
51
+ img: {
52
+ container: 'group relative my-4 mx-auto w-fit block',
53
+ base: 'max-w-full rounded-lg',
54
+ downloadButton: 'absolute right-2 bottom-2 flex h-8 w-8 cursor-pointer items-center justify-center rounded-md border border-gray-200 bg-white/90 shadow-sm backdrop-blur-sm transition-all duration-200 hover:bg-white opacity-0 group-hover:opacity-100'
55
+ },
56
+ pre: {
57
+ base: 'overflow-x-auto font-mono text-xs p-4 bg-gray-100/40'
58
+ },
59
+ blockquote: {
60
+ base: 'border-gray-600/30 text-gray-600 my-4 border-l-4 pl-4 italic'
61
+ },
62
+ alert: {
63
+ base: ' relative my-4 border-l-4 p-4',
64
+ title: 'text-sm font-semibold flex items-center gap-2 mb-2 capitalize',
65
+ icon: 'size-5',
66
+ note: '[&>[data-alert-title]]:text-blue-600 border-blue-600 stroke-blue-600',
67
+ tip: '[&>[data-alert-title]]:text-green-600 border-green-600 stroke-green-600',
68
+ warning: '[&>[data-alert-title]]:text-yellow-600 border-yellow-600 stroke-yellow-600',
69
+ caution: '[&>[data-alert-title]]:text-red-600 border-red-600 stroke-red-600',
70
+ important: '[&>[data-alert-title]]:text-purple-600 border-purple-600 stroke-purple-600'
71
+ },
72
+ table: {
73
+ base: 'border-gray-200 w-full border-collapse border',
74
+ container: 'my-4 flex flex-col space-y-2'
75
+ },
76
+ thead: {
77
+ base: 'bg-gray-100/80'
78
+ },
79
+ tbody: {
80
+ base: 'divide-gray-200 bg-gray-100/40 divide-y'
81
+ },
82
+ tr: {
83
+ base: 'border-gray-200 border-b'
84
+ },
85
+ th: {
86
+ base: 'px-4 py-2 text-left text-sm font-semibold whitespace-nowrap'
87
+ },
88
+ td: {
89
+ base: 'px-4 py-2 text-sm'
90
+ },
91
+ sup: {
92
+ base: 'text-sm'
93
+ },
94
+ sub: {
95
+ base: 'text-sm'
96
+ },
97
+ hr: {
98
+ base: 'border-gray-200 my-6'
99
+ },
100
+ strong: {
101
+ base: 'font-semibold'
102
+ },
103
+ mermaid: {
104
+ base: 'group relative my-4 h-auto rounded-xl border border-gray-200 p-4',
105
+ downloadButton: 'cursor-pointer p-1 text-gray-600 transition-all hover:text-gray-900'
106
+ },
107
+ math: {
108
+ base: ''
109
+ },
110
+ inlineMath: {
111
+ base: ''
112
+ },
113
+ em: {
114
+ base: 'font-italic'
115
+ },
116
+ ins: {
117
+ base: 'underline'
118
+ },
119
+ del: {
120
+ base: 'line-through'
121
+ },
122
+ small: {
123
+ base: 'text-sm'
124
+ }
125
+ };
126
+ export const mergeTheme = (customTheme) => {
127
+ if (!customTheme)
128
+ return theme;
129
+ const baseTheme = { ...theme };
130
+ for (const key in customTheme) {
131
+ for (const subKey in customTheme[key]) {
132
+ Object.assign(baseTheme[key], {
133
+ [subKey]: cn(baseTheme[key], customTheme[key])
134
+ });
135
+ }
136
+ }
137
+ return baseTheme;
138
+ };
@@ -0,0 +1,12 @@
1
+ import type { Root, ElementContent } from 'hast';
2
+ export interface IAlert {
3
+ keyword: string;
4
+ }
5
+ export type DefaultBuildType = (alertOptions: IAlert, originalChildren: ElementContent[]) => ElementContent | null;
6
+ export interface IOptions {
7
+ alerts: IAlert[];
8
+ supportLegacy?: boolean;
9
+ build?: DefaultBuildType;
10
+ }
11
+ export declare const rehypeGithubAlerts: (options: IOptions) => (tree: Root) => void;
12
+ export declare const defaultBuild: DefaultBuildType;
@@ -0,0 +1,207 @@
1
+ import { SKIP, visit } from 'unist-util-visit';
2
+ import { isElement } from 'hast-util-is-element';
3
+ let internalOptions;
4
+ export const rehypeGithubAlerts = (options) => {
5
+ const defaultOptions = {
6
+ supportLegacy: false,
7
+ alerts: [
8
+ {
9
+ keyword: 'NOTE'
10
+ },
11
+ {
12
+ keyword: 'IMPORTANT'
13
+ },
14
+ {
15
+ keyword: 'WARNING'
16
+ },
17
+ {
18
+ keyword: 'TIP'
19
+ },
20
+ {
21
+ keyword: 'CAUTION'
22
+ }
23
+ ]
24
+ };
25
+ internalOptions = Object.assign({}, defaultOptions, options);
26
+ return (tree) => {
27
+ visit(tree, 'element', (node, index, parent) => {
28
+ create(node, index, parent);
29
+ });
30
+ };
31
+ };
32
+ const create = (node, index, parent) => {
33
+ // check if main element is a blockquote
34
+ if (node.tagName !== 'blockquote') {
35
+ return [SKIP];
36
+ }
37
+ // make sure the blockquote is not empty
38
+ if (node.children.length < 1) {
39
+ return [SKIP];
40
+ }
41
+ // find the first paragraph inside of the blockquote
42
+ const firstParagraph = node.children.find((child) => {
43
+ return child.type === 'element' && child.tagName === 'p';
44
+ });
45
+ // typescript type guard
46
+ // make sure the first paragraph is a valid element
47
+ if (!isElement(firstParagraph)) {
48
+ return [SKIP];
49
+ }
50
+ // try to find the alert type
51
+ const headerData = extractHeaderData(firstParagraph);
52
+ // typescript type guard
53
+ if (headerData === null) {
54
+ return [SKIP];
55
+ }
56
+ // if the first line contains more than the type
57
+ // drop out of rendering as alert, this is what
58
+ // GitHub does (as of Mar. 2024)
59
+ if (headerData.rest.trim() !== '') {
60
+ if (!headerData.rest.startsWith('\n') && !headerData.rest.startsWith('\r')) {
61
+ return [SKIP];
62
+ }
63
+ }
64
+ // try to find options matching the alert keyword
65
+ const alertOptions = getAlertOptions(headerData.alertType);
66
+ if (alertOptions === null) {
67
+ return [SKIP];
68
+ }
69
+ // make sure we have parent element and an index
70
+ // or we won't be able to replace the blockquote
71
+ // with the new alert element
72
+ if (!parent || typeof index !== 'number') {
73
+ return [SKIP];
74
+ }
75
+ // use a build to convert the blockquote into an alert
76
+ const build = internalOptions.build ?? defaultBuild;
77
+ const alertBodyChildren = [];
78
+ // for alerts the blockquote first element is always
79
+ // a paragraph but it can have more children than just
80
+ // the alert type text node
81
+ const remainingFirstParagraphChildren = firstParagraph.children.slice(1, firstParagraph.children.length);
82
+ const newFirstParagraphChildren = [];
83
+ // remove the first line break from rest if there is one
84
+ const rest = headerData.rest.replace(/^(\r\n|\r|\n)/, '');
85
+ // if the rest is empty and the first paragraph has no children
86
+ // this is a special github case (as of Mar. 2024)
87
+ // where the alert is only the type (no alert body)
88
+ // in this case github keeps the original blockquote
89
+ if (rest === '' && remainingFirstParagraphChildren.length === 0 && node.children.length < 4) {
90
+ return [SKIP];
91
+ }
92
+ if (remainingFirstParagraphChildren.length > 0) {
93
+ // if the alert type has a hard line break we remove it
94
+ // to not start the alert with a blank line
95
+ // meaning we start the slice at 2 to not take
96
+ // the br element and new line text nodes
97
+ if (remainingFirstParagraphChildren[0].type === 'element' &&
98
+ remainingFirstParagraphChildren[0].tagName === 'br') {
99
+ const remainingChildrenWithoutLineBreak = remainingFirstParagraphChildren.slice(2, firstParagraph.children.length);
100
+ newFirstParagraphChildren.push(...remainingChildrenWithoutLineBreak);
101
+ }
102
+ else {
103
+ // if the first line of the blockquote has no hard line break
104
+ // after the alert type but some text, then both the type
105
+ // and the text will be in a single text node
106
+ // headerData rest contains the remaining text without the alert type
107
+ if (rest !== '') {
108
+ const restAsTextNode = {
109
+ type: 'text',
110
+ value: rest
111
+ };
112
+ remainingFirstParagraphChildren.unshift(restAsTextNode);
113
+ }
114
+ // if no hard line break (br) take all the remaining
115
+ // and add them to new paragraph to mimic the initial structure
116
+ newFirstParagraphChildren.push(...remainingFirstParagraphChildren);
117
+ }
118
+ }
119
+ else {
120
+ if (rest !== '') {
121
+ const restAsTextNode = {
122
+ type: 'text',
123
+ value: rest
124
+ };
125
+ newFirstParagraphChildren.push(restAsTextNode);
126
+ }
127
+ }
128
+ if (newFirstParagraphChildren.length > 0) {
129
+ const paragraphElement = {
130
+ type: 'element',
131
+ tagName: 'p',
132
+ properties: {},
133
+ children: newFirstParagraphChildren
134
+ };
135
+ alertBodyChildren.push(paragraphElement);
136
+ }
137
+ // outside of the first paragraph there may also be children
138
+ // we add them too back into the alert body
139
+ if (node.children.length > 2) {
140
+ alertBodyChildren.push(...node.children.slice(2, node.children.length));
141
+ }
142
+ const alertElement = build(alertOptions, alertBodyChildren);
143
+ // replace the original blockquote with the
144
+ // new alert element and its children
145
+ if (alertElement !== null) {
146
+ parent.children[index] = alertElement;
147
+ }
148
+ return [SKIP];
149
+ };
150
+ export const defaultBuild = (alertOptions, originalChildren) => {
151
+ // Filter out any title paragraphs that might contain icons or titles
152
+ const filteredChildren = originalChildren.filter((child) => {
153
+ if (child.type === 'element' && child.tagName === 'p') {
154
+ const className = child.properties?.className;
155
+ // Remove paragraphs with markdown-alert-title class
156
+ if (className?.includes('markdown-alert-title')) {
157
+ return false;
158
+ }
159
+ }
160
+ return true;
161
+ });
162
+ const alert = {
163
+ type: 'element',
164
+ tagName: 'alert',
165
+ properties: {
166
+ className: ['markdown-alert', `markdown-alert-${alertOptions.keyword.toLowerCase()}`]
167
+ },
168
+ children: filteredChildren
169
+ };
170
+ return alert;
171
+ };
172
+ const extractHeaderData = (paragraph) => {
173
+ const header = paragraph.children[0];
174
+ let alertType;
175
+ let rest = '';
176
+ if (internalOptions.supportLegacy) {
177
+ if (header.type === 'element' && header.tagName === 'strong') {
178
+ if (header.children[0].type === 'text') {
179
+ alertType = header.children[0].value;
180
+ }
181
+ }
182
+ }
183
+ if (header.type === 'text') {
184
+ const match = /\[!(.*?)\]/.exec(header.value);
185
+ if (!match?.input) {
186
+ return null;
187
+ }
188
+ if (match.input.length > match[0].length) {
189
+ // if in markdown there are no two spaces at the end
190
+ // then in html there will be no line break
191
+ // this means in the first line there will be more
192
+ // content than just the alert type
193
+ rest = match.input.replace(match[0], '');
194
+ }
195
+ alertType = match[1];
196
+ }
197
+ if (typeof alertType === 'undefined') {
198
+ return null;
199
+ }
200
+ return { alertType, rest };
201
+ };
202
+ const getAlertOptions = (alertType) => {
203
+ const alertOptions = internalOptions.alerts.find((alert) => {
204
+ return alertType.toUpperCase() === alert.keyword.toUpperCase();
205
+ });
206
+ return alertOptions ?? null;
207
+ };
@@ -0,0 +1 @@
1
+ export declare const bind: (ref: any, props: Record<string, any>) => void;
@@ -0,0 +1,6 @@
1
+ export const bind = (ref, props) => {
2
+ const descriptors = Object.getOwnPropertyDescriptors(props);
3
+ for (const key in descriptors) {
4
+ Object.defineProperty(ref, key, descriptors[key]);
5
+ }
6
+ };
@@ -0,0 +1,7 @@
1
+ export declare const useCopy: (opts: {
2
+ content: string;
3
+ timeout?: number;
4
+ }) => {
5
+ readonly isCopied: boolean;
6
+ copy: () => Promise<void>;
7
+ };
@@ -0,0 +1,35 @@
1
+ import { onDestroy } from 'svelte';
2
+ export const useCopy = (opts) => {
3
+ let isCopied = $state(false);
4
+ let timeoutId;
5
+ const copy = async () => {
6
+ if (typeof window === 'undefined' || !navigator?.clipboard?.writeText) {
7
+ console.error('Clipboard API not available');
8
+ return;
9
+ }
10
+ try {
11
+ if (!isCopied) {
12
+ await navigator.clipboard.writeText(opts.content);
13
+ isCopied = true;
14
+ if (timeoutId)
15
+ clearTimeout(timeoutId);
16
+ timeoutId = window.setTimeout(() => {
17
+ isCopied = false;
18
+ }, opts.timeout ?? 2000);
19
+ }
20
+ }
21
+ catch (error) {
22
+ console.error('Failed to copy to clipboard:', error);
23
+ }
24
+ };
25
+ onDestroy(() => {
26
+ if (timeoutId)
27
+ clearTimeout(timeoutId);
28
+ });
29
+ return {
30
+ get isCopied() {
31
+ return isCopied;
32
+ },
33
+ copy
34
+ };
35
+ };
@@ -0,0 +1 @@
1
+ export declare const parseMarkdownIntoBlocks: (markdown: string) => string[];
@@ -0,0 +1,40 @@
1
+ import { marked } from 'marked';
2
+ export const parseMarkdownIntoBlocks = (markdown) => {
3
+ const tokens = marked.use({ gfm: true }).lexer(markdown);
4
+ const blocks = tokens.map((token) => token.raw);
5
+ // Post-process to merge consecutive blocks that are part of the same math block
6
+ const mergedBlocks = [];
7
+ for (let i = 0; i < blocks.length; i++) {
8
+ const currentBlock = blocks[i];
9
+ // Check if this is a standalone $$ that might be a closing delimiter
10
+ if (currentBlock.trim() === '$$' && mergedBlocks.length > 0) {
11
+ const previousBlock = mergedBlocks.at(-1);
12
+ // Check if the previous block starts with $$ but doesn't end with $$
13
+ const prevStartsWith$$ = previousBlock?.trimStart().startsWith('$$');
14
+ const prevDollarCount = (previousBlock?.match(/\$\$/g) || []).length;
15
+ // If previous block has odd number of $$ and starts with $$, merge them
16
+ if (prevStartsWith$$ && prevDollarCount % 2 === 1) {
17
+ mergedBlocks[mergedBlocks.length - 1] = previousBlock + currentBlock;
18
+ continue;
19
+ }
20
+ }
21
+ // Check if current block ends with $$ and previous block started with $$ but didn't close
22
+ if (mergedBlocks.length > 0 && currentBlock.trimEnd().endsWith('$$')) {
23
+ const previousBlock = mergedBlocks.at(-1);
24
+ const prevStartsWith$$ = previousBlock?.trimStart().startsWith('$$');
25
+ const prevDollarCount = (previousBlock?.match(/\$\$/g) || []).length;
26
+ const currDollarCount = (currentBlock.match(/\$\$/g) || []).length;
27
+ // If previous block has unclosed math (odd $$) and current block ends with $$
28
+ // AND current block doesn't start with $$, it's likely a continuation
29
+ if (prevStartsWith$$ &&
30
+ prevDollarCount % 2 === 1 &&
31
+ !currentBlock.trimStart().startsWith('$$') &&
32
+ currDollarCount === 1) {
33
+ mergedBlocks[mergedBlocks.length - 1] = previousBlock + currentBlock;
34
+ continue;
35
+ }
36
+ }
37
+ mergedBlocks.push(currentBlock);
38
+ }
39
+ return mergedBlocks;
40
+ };
@@ -0,0 +1 @@
1
+ export declare const parseIncompleteMarkdown: (text: string) => string;