pd-markdown 2.0.3 → 2.0.4-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/package.json +7 -2
  2. package/packages/parser/dist/index.cjs +1 -1
  3. package/packages/parser/dist/index.mjs +4 -184
  4. package/packages/parser/dist/index.mjs.map +1 -1
  5. package/packages/parser/dist/plugins/transform/heading.mjs +39 -0
  6. package/packages/parser/dist/plugins/transform/heading.mjs.map +1 -0
  7. package/packages/parser/dist/plugins/transform/list.mjs +21 -0
  8. package/packages/parser/dist/plugins/transform/list.mjs.map +1 -0
  9. package/packages/parser/dist/plugins/transform/table.mjs +40 -0
  10. package/packages/parser/dist/plugins/transform/table.mjs.map +1 -0
  11. package/packages/parser/dist/processor.mjs +100 -0
  12. package/packages/parser/dist/processor.mjs.map +1 -0
  13. package/packages/web/dist/MarkdownRenderer-CflS5zy_.js +43 -0
  14. package/packages/web/dist/MarkdownRenderer-CflS5zy_.js.map +1 -0
  15. package/packages/web/dist/NodeRenderer-DTR-8m1G.js +225 -0
  16. package/packages/web/dist/NodeRenderer-DTR-8m1G.js.map +1 -0
  17. package/packages/web/dist/client.cjs +16 -0
  18. package/packages/web/dist/client.cjs.map +1 -0
  19. package/packages/web/dist/client.mjs +5 -0
  20. package/packages/web/dist/client.mjs.map +1 -0
  21. package/packages/web/dist/components/MarkdownRenderer.mjs +41 -0
  22. package/packages/web/dist/components/MarkdownRenderer.mjs.map +1 -0
  23. package/packages/web/dist/components/NodeRenderer.mjs +131 -0
  24. package/packages/web/dist/components/NodeRenderer.mjs.map +1 -0
  25. package/packages/web/dist/components/StreamMarkdownRenderer.mjs +145 -0
  26. package/packages/web/dist/components/StreamMarkdownRenderer.mjs.map +1 -0
  27. package/packages/web/dist/components/context.mjs +17 -0
  28. package/packages/web/dist/components/context.mjs.map +1 -0
  29. package/packages/web/dist/components/defaults/Blockquote.mjs +8 -0
  30. package/packages/web/dist/components/defaults/Blockquote.mjs.map +1 -0
  31. package/packages/web/dist/components/defaults/Code.mjs +12 -0
  32. package/packages/web/dist/components/defaults/Code.mjs.map +1 -0
  33. package/packages/web/dist/components/defaults/Heading.mjs +10 -0
  34. package/packages/web/dist/components/defaults/Heading.mjs.map +1 -0
  35. package/packages/web/dist/components/defaults/Image.mjs +8 -0
  36. package/packages/web/dist/components/defaults/Image.mjs.map +1 -0
  37. package/packages/web/dist/components/defaults/Link.mjs +10 -0
  38. package/packages/web/dist/components/defaults/Link.mjs.map +1 -0
  39. package/packages/web/dist/components/defaults/List.mjs +17 -0
  40. package/packages/web/dist/components/defaults/List.mjs.map +1 -0
  41. package/packages/web/dist/components/defaults/Paragraph.mjs +8 -0
  42. package/packages/web/dist/components/defaults/Paragraph.mjs.map +1 -0
  43. package/packages/web/dist/components/defaults/Table.mjs +21 -0
  44. package/packages/web/dist/components/defaults/Table.mjs.map +1 -0
  45. package/packages/web/dist/components/defaults/index.mjs +29 -0
  46. package/packages/web/dist/components/defaults/index.mjs.map +1 -0
  47. package/packages/web/dist/context-DR5sJXYw.js +86 -0
  48. package/packages/web/dist/context-DR5sJXYw.js.map +1 -0
  49. package/packages/web/dist/hooks/useMarkdown.mjs +31 -0
  50. package/packages/web/dist/hooks/useMarkdown.mjs.map +1 -0
  51. package/packages/web/dist/hooks/useStreamMarkdown.mjs +274 -0
  52. package/packages/web/dist/hooks/useStreamMarkdown.mjs.map +1 -0
  53. package/packages/web/dist/index.cjs +29 -712
  54. package/packages/web/dist/index.cjs.map +1 -1
  55. package/packages/web/dist/index.d.ts +28 -26
  56. package/packages/web/dist/index.mjs +15 -693
  57. package/packages/web/dist/index.mjs.map +1 -1
  58. package/packages/web/dist/server.cjs +25 -0
  59. package/packages/web/dist/server.cjs.map +1 -0
  60. package/packages/web/dist/server.mjs +12 -0
  61. package/packages/web/dist/server.mjs.map +1 -0
  62. package/packages/web/dist/useStreamMarkdown-CXM4Hrzx.js +417 -0
  63. package/packages/web/dist/useStreamMarkdown-CXM4Hrzx.js.map +1 -0
  64. package/packages/web/dist/useStreamMarkdown-DEOfH8Ve.js +459 -0
  65. package/packages/web/dist/useStreamMarkdown-DEOfH8Ve.js.map +1 -0
@@ -1,715 +1,32 @@
1
1
  'use strict';
2
2
 
3
- var jsxRuntime = require('react/jsx-runtime');
4
- var pdMarkdownParser = require('pd-markdown/parser');
5
- var react = require('react');
6
-
7
- /**
8
- * Context for passing configuration down the component tree
9
- */
10
- const MarkdownContext = react.createContext({
11
- components: {},
12
- });
13
- /**
14
- * Hook to access markdown context
15
- */
16
- function useMarkdownContext() {
17
- return react.useContext(MarkdownContext);
18
- }
19
-
20
- const Heading = ({ node, children }) => {
21
- const Tag = `h${node.depth}`;
22
- const id = node.data && 'id' in node.data ? node.data.id : undefined;
23
- return jsxRuntime.jsx(Tag, { id: id, children: children });
24
- };
25
-
26
- const Paragraph = ({ children }) => {
27
- return jsxRuntime.jsx("p", { children: children });
28
- };
29
-
30
- const List = ({ node, children }) => {
31
- const Tag = node.ordered ? 'ol' : 'ul';
32
- const start = node.ordered && node.start != null && node.start !== 1 ? node.start : undefined;
33
- return jsxRuntime.jsx(Tag, { start: start, children: children });
34
- };
35
- const ListItem = ({ node, children }) => {
36
- // Handle task list items
37
- if (typeof node.checked === 'boolean') {
38
- return (jsxRuntime.jsxs("li", { className: "task-list-item", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: node.checked, readOnly: true }), jsxRuntime.jsx("span", { children: children })] }));
39
- }
40
- return jsxRuntime.jsx("li", { children: children });
41
- };
42
-
43
- const Table = ({ children }) => {
44
- return (jsxRuntime.jsx("table", { children: children }));
45
- };
46
- const TableRow = ({ children, isHeader }) => {
47
- if (isHeader) {
48
- return (jsxRuntime.jsx("thead", { children: jsxRuntime.jsx("tr", { children: children }) }));
49
- }
50
- return jsxRuntime.jsx("tr", { children: children });
51
- };
52
- const TableCell = ({ node, children }) => {
53
- const isHeader = node.data?.isHeader;
54
- const align = node.data?.align;
55
- const Tag = isHeader ? 'th' : 'td';
56
- const style = align ? { textAlign: align } : undefined;
57
- return jsxRuntime.jsx(Tag, { style: style, children: children });
58
- };
59
-
60
- const Code = ({ node }) => {
61
- const className = node.lang ? `language-${node.lang}` : undefined;
62
- return (jsxRuntime.jsx("pre", { children: jsxRuntime.jsx("code", { className: className, children: node.value }) }));
63
- };
64
- const InlineCodeComponent = ({ node }) => {
65
- return jsxRuntime.jsx("code", { children: node.value });
66
- };
67
-
68
- const Link = ({ node, children }) => {
69
- // Basic security: prevent javascript: URLs
70
- const href = node.url?.startsWith('javascript:') ? '#' : node.url;
71
- return (jsxRuntime.jsx("a", { href: href, title: node.title || undefined, children: children }));
72
- };
73
-
74
- const Image = ({ node }) => {
75
- return jsxRuntime.jsx("img", { src: node.url, alt: node.alt || '', title: node.title || undefined });
76
- };
77
-
78
- const Blockquote = ({ children }) => {
79
- return jsxRuntime.jsx("blockquote", { children: children });
80
- };
81
-
82
- /**
83
- * Default component map
84
- */
85
- const defaultComponents = {
86
- heading: Heading,
87
- paragraph: Paragraph,
88
- list: List,
89
- listItem: ListItem,
90
- table: Table,
91
- tableRow: TableRow,
92
- tableCell: TableCell,
93
- code: Code,
94
- inlineCode: InlineCodeComponent,
95
- link: Link,
96
- image: Image,
97
- blockquote: Blockquote,
98
- };
99
-
100
- /**
101
- * Recursive node renderer that renders AST nodes to React elements
102
- */
103
- const NodeRenderer = ({ node }) => {
104
- const { components } = useMarkdownContext();
105
- // Get the component for this node type
106
- const getComponent = (type) => {
107
- return components[type] || defaultComponents[type];
108
- };
109
- // Render children nodes
110
- const renderChildren = (children) => {
111
- return children.map((child, index) => (jsxRuntime.jsx(NodeRenderer, { node: child }, index)));
112
- };
113
- // Render phrasing content (inline elements)
114
- const renderPhrasingContent = (children) => {
115
- return children.map((child, index) => {
116
- switch (child.type) {
117
- case 'text':
118
- return child.value;
119
- case 'strong':
120
- return jsxRuntime.jsx("strong", { children: renderPhrasingContent(child.children) }, index);
121
- case 'emphasis':
122
- return jsxRuntime.jsx("em", { children: renderPhrasingContent(child.children) }, index);
123
- case 'delete':
124
- return jsxRuntime.jsx("del", { children: renderPhrasingContent(child.children) }, index);
125
- case 'inlineCode': {
126
- const InlineCode = getComponent('inlineCode');
127
- return InlineCode ? jsxRuntime.jsx(InlineCode, { node: child }, index) : jsxRuntime.jsx("code", { children: child.value }, index);
128
- }
129
- case 'link': {
130
- const Link = getComponent('link');
131
- return Link ? (jsxRuntime.jsx(Link, { node: child, children: renderPhrasingContent(child.children) }, index)) : (jsxRuntime.jsx("a", { href: child.url, children: renderPhrasingContent(child.children) }, index));
132
- }
133
- case 'image': {
134
- const Image = getComponent('image');
135
- return Image ? jsxRuntime.jsx(Image, { node: child }, index) : jsxRuntime.jsx("img", { src: child.url, alt: child.alt || '' }, index);
136
- }
137
- case 'break':
138
- return jsxRuntime.jsx("br", {}, index);
139
- case 'html':
140
- // For safety, render HTML as text in React
141
- return child.value;
142
- default:
143
- // For unknown inline types, try to render as text if possible
144
- if ('value' in child && typeof child.value === 'string') {
145
- return child.value;
146
- }
147
- if ('children' in child) {
148
- return renderPhrasingContent(child.children);
149
- }
150
- return null;
151
- }
152
- });
153
- };
154
- // Handle different node types
155
- switch (node.type) {
156
- case 'root':
157
- return jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderChildren(node.children) });
158
- case 'heading': {
159
- const Heading = getComponent('heading');
160
- return Heading ? (jsxRuntime.jsx(Heading, { node: node, children: renderPhrasingContent(node.children) })) : null;
161
- }
162
- case 'paragraph': {
163
- const Paragraph = getComponent('paragraph');
164
- return Paragraph ? (jsxRuntime.jsx(Paragraph, { node: node, children: renderPhrasingContent(node.children) })) : null;
165
- }
166
- case 'list': {
167
- const List = getComponent('list');
168
- return List ? (jsxRuntime.jsx(List, { node: node, children: renderChildren(node.children) })) : null;
169
- }
170
- case 'listItem': {
171
- const ListItem = getComponent('listItem');
172
- const children = node.children.map((child, index) => {
173
- // Unwrap single paragraph in list item
174
- if (child.type === 'paragraph' && node.children.length === 1) {
175
- return renderPhrasingContent(child.children);
176
- }
177
- return jsxRuntime.jsx(NodeRenderer, { node: child }, index);
178
- });
179
- return ListItem ? jsxRuntime.jsx(ListItem, { node: node, children: children }) : null;
180
- }
181
- case 'table': {
182
- const Table = getComponent('table');
183
- const TableRow = getComponent('tableRow');
184
- if (!Table || !TableRow)
185
- return null;
186
- const [headerRow, ...bodyRows] = node.children;
187
- return (jsxRuntime.jsxs(Table, { node: node, children: [headerRow && (jsxRuntime.jsx(TableRow, { node: headerRow, isHeader: true, children: headerRow.children.map((cell, index) => (jsxRuntime.jsx(NodeRenderer, { node: cell }, index))) })), bodyRows.length > 0 && (jsxRuntime.jsx("tbody", { children: bodyRows.map((row, rowIndex) => (jsxRuntime.jsx(TableRow, { node: row, children: row.children.map((cell, cellIndex) => (jsxRuntime.jsx(NodeRenderer, { node: cell }, cellIndex))) }, rowIndex))) }))] }));
188
- }
189
- case 'tableCell': {
190
- const TableCell = getComponent('tableCell');
191
- return TableCell ? (jsxRuntime.jsx(TableCell, { node: node, children: renderPhrasingContent(node.children) })) : null;
192
- }
193
- case 'code': {
194
- const Code = getComponent('code');
195
- return Code ? jsxRuntime.jsx(Code, { node: node }) : null;
196
- }
197
- case 'blockquote': {
198
- const Blockquote = getComponent('blockquote');
199
- return Blockquote ? (jsxRuntime.jsx(Blockquote, { node: node, children: renderChildren(node.children) })) : null;
200
- }
201
- case 'thematicBreak':
202
- return jsxRuntime.jsx("hr", {});
203
- case 'html':
204
- // For safety, don't render raw HTML by default
205
- return null;
206
- case 'yaml':
207
- // Frontmatter shouldn't be rendered
208
- return null;
209
- default: {
210
- // Try to find a custom component for unknown types
211
- const CustomComponent = getComponent(node.type);
212
- if (CustomComponent) {
213
- const children = 'children' in node
214
- ? renderChildren(node.children)
215
- : undefined;
216
- return jsxRuntime.jsx(CustomComponent, { node: node, children: children });
217
- }
218
- // Fallback: try to render children if available
219
- if ('children' in node) {
220
- return jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderChildren(node.children) });
221
- }
222
- return null;
223
- }
224
- }
225
- };
226
-
227
- // Singleton parser for client-side use
228
- let defaultParser = null;
229
- function getParser$2(options) {
230
- if (options) {
231
- return pdMarkdownParser.createParser(options);
232
- }
233
- if (!defaultParser) {
234
- defaultParser = pdMarkdownParser.createParser();
235
- }
236
- return defaultParser;
237
- }
238
- /**
239
- * Main markdown renderer component
240
- *
241
- * Supports both client-side and server-side rendering:
242
- * - Pass `source` for automatic parsing
243
- * - Pass `ast` for pre-parsed content (SSR optimization)
244
- */
245
- const MarkdownRenderer = ({ source, ast, components = {}, className, style, parserOptions, }) => {
246
- // Use provided AST or parse source
247
- let tree;
248
- if (ast) {
249
- tree = ast;
250
- }
251
- else if (source) {
252
- const parser = getParser$2(parserOptions);
253
- tree = parser.parse(source);
254
- }
255
- else {
256
- // No content provided
257
- return null;
258
- }
259
- return (jsxRuntime.jsx(MarkdownContext.Provider, { value: { components }, children: jsxRuntime.jsx("div", { className: className, style: style, children: jsxRuntime.jsx(NodeRenderer, { node: tree }) }) }));
260
- };
261
-
262
- // ─── Styles ───────────────────────────────────────────────────────────
263
- const cursorKeyframes = `
264
- @keyframes pd-md-cursor-blink {
265
- 0%, 100% { opacity: 1; }
266
- 50% { opacity: 0; }
267
- }
268
-
269
- @keyframes pd-md-fade-in {
270
- from { opacity: 0; transform: translateY(4px); }
271
- to { opacity: 1; transform: translateY(0); }
272
- }
273
- `;
274
- const cursorStyle = {
275
- display: 'inline-block',
276
- width: '2px',
277
- height: '1.1em',
278
- backgroundColor: 'currentColor',
279
- marginLeft: '2px',
280
- verticalAlign: 'text-bottom',
281
- animation: 'pd-md-cursor-blink 1s step-end infinite',
282
- };
283
- // ─── Internal Parsed AST Hook ─────────────────────────────────────────
284
- function useParsedAst(source, options) {
285
- const parserRef = react.useRef(null);
286
- const optionsRef = react.useRef(options);
287
- if (!parserRef.current ||
288
- JSON.stringify(optionsRef.current) !== JSON.stringify(options)) {
289
- parserRef.current = pdMarkdownParser.createParser(options);
290
- optionsRef.current = options;
291
- }
292
- return react.useMemo(() => {
293
- try {
294
- return parserRef.current.parse(source);
295
- }
296
- catch {
297
- return { type: 'root', children: [] };
298
- }
299
- }, [source]);
300
- }
301
- // ─── Cursor Component ─────────────────────────────────────────────────
302
- const StreamCursor = ({ customElement }) => {
303
- if (customElement) {
304
- return jsxRuntime.jsx(jsxRuntime.Fragment, { children: customElement });
305
- }
306
- return jsxRuntime.jsx("span", { style: cursorStyle, "aria-hidden": "true", "data-streaming-cursor": true });
307
- };
308
- // ─── Main Component ──────────────────────────────────────────────────
309
- /**
310
- * StreamMarkdownRenderer — A streaming-aware markdown renderer.
311
- *
312
- * This component is designed for rendering AI-generated streaming markdown.
313
- * It shows a blinking cursor at the end while content is being streamed,
314
- * and optionally animates new content blocks as they appear.
315
- *
316
- * Usage patterns:
317
- *
318
- * 1. **With `useStreamMarkdown` hook** (recommended):
319
- * ```tsx
320
- * const stream = useStreamMarkdown()
321
- * // ... consume stream
322
- * <StreamMarkdownRenderer
323
- * source={stream.source}
324
- * ast={stream.ast}
325
- * isStreaming={stream.isStreaming}
326
- * />
327
- * ```
328
- *
329
- * 2. **Standalone** (pass source, let it parse internally):
330
- * ```tsx
331
- * <StreamMarkdownRenderer
332
- * source={accumulatedText}
333
- * isStreaming={isLoading}
334
- * />
335
- * ```
336
- */
337
- const StreamMarkdownRenderer = ({ source, isStreaming = false, ast: externalAst, components = {}, className, style, parserOptions, showCursor = true, cursorElement, animationClassName, enableAnimation = true, }) => {
338
- // Use external AST if provided, otherwise parse internally
339
- const internalAst = useParsedAst(externalAst ? '' : source, parserOptions);
340
- const ast = externalAst || internalAst;
341
- // Track previous child count for animation
342
- const prevChildCountRef = react.useRef(0);
343
- const [animatingIndices, setAnimatingIndices] = react.useState(new Set());
344
- // Detect new blocks for animation
345
- react.useEffect(() => {
346
- const currentCount = ast.children.length;
347
- const prevCount = prevChildCountRef.current;
348
- if (enableAnimation &&
349
- isStreaming &&
350
- currentCount > prevCount &&
351
- prevCount > 0) {
352
- const newIndices = new Set();
353
- for (let i = prevCount; i < currentCount; i++) {
354
- newIndices.add(i);
355
- }
356
- setAnimatingIndices(newIndices);
357
- // Clear animation flags after animation completes
358
- const timer = setTimeout(() => {
359
- setAnimatingIndices(new Set());
360
- }, 300);
361
- prevChildCountRef.current = currentCount;
362
- return () => clearTimeout(timer);
363
- }
364
- prevChildCountRef.current = currentCount;
365
- }, [ast.children.length, enableAnimation, isStreaming]);
366
- // Auto-scroll ref
367
- const containerRef = react.useRef(null);
368
- // Inject keyframe styles
369
- react.useEffect(() => {
370
- const styleId = 'pd-md-stream-styles';
371
- if (!document.getElementById(styleId)) {
372
- const styleEl = document.createElement('style');
373
- styleEl.id = styleId;
374
- styleEl.textContent = cursorKeyframes;
375
- document.head.appendChild(styleEl);
376
- }
377
- }, []);
378
- if (!source && !externalAst) {
379
- return null;
380
- }
381
- const wrapperStyle = {
382
- ...style,
383
- position: 'relative',
384
- };
385
- return (jsxRuntime.jsx(MarkdownContext.Provider, { value: { components }, children: jsxRuntime.jsxs("div", { ref: containerRef, className: className, style: wrapperStyle, children: [ast.children.map((child, index) => {
386
- const isNewBlock = animatingIndices.has(index);
387
- const isLastBlock = index === ast.children.length - 1;
388
- const blockStyle = enableAnimation && isNewBlock
389
- ? {
390
- animation: 'pd-md-fade-in 0.3s ease-out forwards',
391
- }
392
- : {};
393
- const blockClassName = isNewBlock
394
- ? animationClassName || undefined
395
- : undefined;
396
- return (jsxRuntime.jsxs("div", { style: blockStyle, className: blockClassName, "data-stream-block": isLastBlock && isStreaming ? 'active' : undefined, children: [jsxRuntime.jsx(NodeRenderer, { node: child }), showCursor && isStreaming && isLastBlock && (jsxRuntime.jsx(StreamCursor, { customElement: cursorElement }))] }, index));
397
- }), showCursor && isStreaming && ast.children.length === 0 && (jsxRuntime.jsx(StreamCursor, { customElement: cursorElement }))] }) }));
398
- };
399
-
400
- // Cached parser instance
401
- let cachedParser$1 = null;
402
- function getParser$1(options) {
403
- if (options) {
404
- return pdMarkdownParser.createParser(options);
405
- }
406
- if (!cachedParser$1) {
407
- cachedParser$1 = pdMarkdownParser.createParser();
408
- }
409
- return cachedParser$1;
410
- }
411
- /**
412
- * Hook for parsing markdown on the client side
413
- *
414
- * @param source - Markdown source string
415
- * @param options - Parser options
416
- * @returns Parsed AST
417
- */
418
- function useMarkdown(source, options) {
419
- const ast = react.useMemo(() => {
420
- const parser = getParser$1(options);
421
- return parser.parse(source);
422
- }, [source, options]);
423
- return ast;
424
- }
425
-
426
- // Singleton parser cache
427
- let cachedParser = null;
428
- let cachedParserOptions;
429
- function getParser(options) {
430
- if (options !== cachedParserOptions ||
431
- (options && JSON.stringify(options) !== JSON.stringify(cachedParserOptions))) {
432
- cachedParser = pdMarkdownParser.createParser(options);
433
- cachedParserOptions = options;
434
- }
435
- if (!cachedParser) {
436
- cachedParser = pdMarkdownParser.createParser(options);
437
- cachedParserOptions = options;
438
- }
439
- return cachedParser;
440
- }
441
- const EMPTY_AST = { type: 'root', children: [] };
442
- /**
443
- * Hook for streaming markdown rendering.
444
- *
445
- * Provides a simple API to progressively append markdown text,
446
- * automatically re-parsing into an AST that can be rendered
447
- * by the MarkdownRenderer.
448
- *
449
- * Supports multiple consumption methods:
450
- * - Manual `append()` + `done()` calls
451
- * - `consume(readableStream)` for ReadableStream<string>
452
- * - `consumeIterator(asyncIterable)` for async iterators
453
- * - `consumeResponse(response)` for SSE/fetch responses
454
- */
455
- function useStreamMarkdown(options = {}) {
456
- const { parserOptions, onStart, onChunk, onDone, onError, parseDebounceMs = 50, } = options;
457
- const [state, setState] = react.useState({
458
- source: '',
459
- ast: EMPTY_AST,
460
- isStreaming: false,
461
- isDone: false,
462
- error: null,
463
- });
464
- const sourceRef = react.useRef('');
465
- const isStreamingRef = react.useRef(false);
466
- const parseTimerRef = react.useRef(null);
467
- const abortControllerRef = react.useRef(null);
468
- // Parse the current source and update AST
469
- const parseSource = react.useCallback((source) => {
470
- try {
471
- const parser = getParser(parserOptions);
472
- const ast = parser.parse(source);
473
- setState((prev) => ({ ...prev, source, ast }));
474
- }
475
- catch {
476
- // If parsing fails (e.g., incomplete markdown), keep previous AST
477
- setState((prev) => ({ ...prev, source }));
478
- }
479
- }, [parserOptions]);
480
- // Debounced parse
481
- const debouncedParse = react.useCallback((source) => {
482
- if (parseTimerRef.current) {
483
- clearTimeout(parseTimerRef.current);
484
- }
485
- parseTimerRef.current = setTimeout(() => {
486
- parseSource(source);
487
- }, parseDebounceMs);
488
- }, [parseSource, parseDebounceMs]);
489
- // Append a chunk
490
- const append = react.useCallback((chunk) => {
491
- if (!isStreamingRef.current) {
492
- isStreamingRef.current = true;
493
- setState((prev) => ({
494
- ...prev,
495
- isStreaming: true,
496
- isDone: false,
497
- error: null,
498
- }));
499
- onStart?.();
500
- }
501
- sourceRef.current += chunk;
502
- const currentSource = sourceRef.current;
503
- onChunk?.(chunk, currentSource);
504
- debouncedParse(currentSource);
505
- }, [debouncedParse, onStart, onChunk]);
506
- // Signal completion
507
- const done = react.useCallback(() => {
508
- if (parseTimerRef.current) {
509
- clearTimeout(parseTimerRef.current);
510
- }
511
- // Final parse with complete source
512
- const finalSource = sourceRef.current;
513
- parseSource(finalSource);
514
- isStreamingRef.current = false;
515
- setState((prev) => ({
516
- ...prev,
517
- isStreaming: false,
518
- isDone: true,
519
- }));
520
- onDone?.(finalSource);
521
- }, [parseSource, onDone]);
522
- // Reset state
523
- const reset = react.useCallback(() => {
524
- if (parseTimerRef.current) {
525
- clearTimeout(parseTimerRef.current);
526
- }
527
- if (abortControllerRef.current) {
528
- abortControllerRef.current.abort();
529
- }
530
- sourceRef.current = '';
531
- isStreamingRef.current = false;
532
- setState({
533
- source: '',
534
- ast: EMPTY_AST,
535
- isStreaming: false,
536
- isDone: false,
537
- error: null,
538
- });
539
- }, []);
540
- // Consume a ReadableStream<string>
541
- const consume = react.useCallback(async (stream) => {
542
- reset();
543
- const reader = stream.getReader();
544
- const controller = new AbortController();
545
- abortControllerRef.current = controller;
546
- try {
547
- while (true) {
548
- if (controller.signal.aborted)
549
- break;
550
- const { done: readerDone, value } = await reader.read();
551
- if (readerDone)
552
- break;
553
- if (value)
554
- append(value);
555
- }
556
- if (!controller.signal.aborted)
557
- done();
558
- }
559
- catch (err) {
560
- const error = err instanceof Error ? err : new Error(String(err));
561
- setState((prev) => ({ ...prev, error, isStreaming: false }));
562
- onError?.(error);
563
- }
564
- finally {
565
- reader.releaseLock();
566
- }
567
- }, [reset, append, done, onError]);
568
- // Consume an async iterator
569
- const consumeIterator = react.useCallback(async (iterator) => {
570
- reset();
571
- const controller = new AbortController();
572
- abortControllerRef.current = controller;
573
- try {
574
- for await (const chunk of iterator) {
575
- if (controller.signal.aborted)
576
- break;
577
- append(chunk);
578
- }
579
- if (!controller.signal.aborted)
580
- done();
581
- }
582
- catch (err) {
583
- const error = err instanceof Error ? err : new Error(String(err));
584
- setState((prev) => ({ ...prev, error, isStreaming: false }));
585
- onError?.(error);
586
- }
587
- }, [reset, append, done, onError]);
588
- // Consume a fetch Response (SSE format)
589
- const consumeResponse = react.useCallback(async (response, opts) => {
590
- if (!response.body) {
591
- throw new Error('Response has no body');
592
- }
593
- if (!response.ok) {
594
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
595
- }
596
- reset();
597
- const controller = new AbortController();
598
- abortControllerRef.current = controller;
599
- const reader = response.body.getReader();
600
- const decoder = new TextDecoder();
601
- const extractContent = opts?.extractContent ??
602
- ((data) => {
603
- // Default: try to extract from SSE "data: ..." lines
604
- // or OpenAI-style streaming content
605
- try {
606
- const parsed = JSON.parse(data);
607
- // OpenAI format
608
- if (parsed.choices?.[0]?.delta?.content !== undefined) {
609
- return parsed.choices[0].delta.content;
610
- }
611
- // Generic content field
612
- if (typeof parsed.content === 'string') {
613
- return parsed.content;
614
- }
615
- if (typeof parsed.text === 'string') {
616
- return parsed.text;
617
- }
618
- }
619
- catch {
620
- // Not JSON, return as-is
621
- }
622
- return data;
623
- });
624
- try {
625
- let buffer = '';
626
- while (true) {
627
- if (controller.signal.aborted)
628
- break;
629
- const { done: readerDone, value } = await reader.read();
630
- if (readerDone)
631
- break;
632
- buffer += decoder.decode(value, { stream: true });
633
- const lines = buffer.split('\n');
634
- buffer = lines.pop() || ''; // Keep incomplete line in buffer
635
- for (const line of lines) {
636
- const trimmed = line.trim();
637
- if (!trimmed)
638
- continue;
639
- if (trimmed === 'data: [DONE]')
640
- continue;
641
- let data = trimmed;
642
- if (trimmed.startsWith('data: ')) {
643
- data = trimmed.slice(6);
644
- }
645
- const content = extractContent(data);
646
- if (content !== null && content !== '') {
647
- append(content);
648
- }
649
- }
650
- }
651
- // Process remaining buffer
652
- if (buffer.trim()) {
653
- const data = buffer.trim().startsWith('data: ')
654
- ? buffer.trim().slice(6)
655
- : buffer.trim();
656
- const content = extractContent(data);
657
- if (content !== null && content !== '') {
658
- append(content);
659
- }
660
- }
661
- if (!controller.signal.aborted)
662
- done();
663
- }
664
- catch (err) {
665
- const error = err instanceof Error ? err : new Error(String(err));
666
- setState((prev) => ({ ...prev, error, isStreaming: false }));
667
- onError?.(error);
668
- }
669
- finally {
670
- reader.releaseLock();
671
- }
672
- }, [reset, append, done, onError]);
673
- // Cleanup on unmount
674
- react.useEffect(() => {
675
- return () => {
676
- if (parseTimerRef.current) {
677
- clearTimeout(parseTimerRef.current);
678
- }
679
- if (abortControllerRef.current) {
680
- abortControllerRef.current.abort();
681
- }
682
- };
683
- }, []);
684
- return {
685
- ...state,
686
- append,
687
- done,
688
- reset,
689
- consume,
690
- consumeIterator,
691
- consumeResponse,
692
- };
693
- }
694
-
695
- exports.Blockquote = Blockquote;
696
- exports.Code = Code;
697
- exports.Heading = Heading;
698
- exports.Image = Image;
699
- exports.InlineCodeComponent = InlineCodeComponent;
700
- exports.Link = Link;
701
- exports.List = List;
702
- exports.ListItem = ListItem;
703
- exports.MarkdownContext = MarkdownContext;
704
- exports.MarkdownRenderer = MarkdownRenderer;
705
- exports.NodeRenderer = NodeRenderer;
706
- exports.Paragraph = Paragraph;
707
- exports.StreamMarkdownRenderer = StreamMarkdownRenderer;
708
- exports.Table = Table;
709
- exports.TableCell = TableCell;
710
- exports.TableRow = TableRow;
711
- exports.defaultComponents = defaultComponents;
712
- exports.useMarkdown = useMarkdown;
713
- exports.useMarkdownContext = useMarkdownContext;
714
- exports.useStreamMarkdown = useStreamMarkdown;
3
+ var NodeRenderer = require('./NodeRenderer-DTR-8m1G.js');
4
+ var useStreamMarkdown = require('./useStreamMarkdown-DEOfH8Ve.js');
5
+ var MarkdownRenderer = require('./MarkdownRenderer-CflS5zy_.js');
6
+ require('react/jsx-runtime');
7
+ require('react');
8
+ require('pd-markdown-parser');
9
+
10
+
11
+
12
+ exports.Blockquote = NodeRenderer.Blockquote;
13
+ exports.Code = NodeRenderer.Code;
14
+ exports.Heading = NodeRenderer.Heading;
15
+ exports.Image = NodeRenderer.Image;
16
+ exports.InlineCodeComponent = NodeRenderer.InlineCodeComponent;
17
+ exports.Link = NodeRenderer.Link;
18
+ exports.List = NodeRenderer.List;
19
+ exports.ListItem = NodeRenderer.ListItem;
20
+ exports.NodeRenderer = NodeRenderer.NodeRenderer;
21
+ exports.Paragraph = NodeRenderer.Paragraph;
22
+ exports.Table = NodeRenderer.Table;
23
+ exports.TableCell = NodeRenderer.TableCell;
24
+ exports.TableRow = NodeRenderer.TableRow;
25
+ exports.defaultComponents = NodeRenderer.defaultComponents;
26
+ exports.MarkdownContext = useStreamMarkdown.MarkdownContext;
27
+ exports.StreamMarkdownRenderer = useStreamMarkdown.StreamMarkdownRenderer;
28
+ exports.useMarkdown = useStreamMarkdown.useMarkdown;
29
+ exports.useMarkdownContext = useStreamMarkdown.useMarkdownContext;
30
+ exports.useStreamMarkdown = useStreamMarkdown.useStreamMarkdown;
31
+ exports.MarkdownRenderer = MarkdownRenderer.MarkdownRenderer;
715
32
  //# sourceMappingURL=index.cjs.map