@incremark/react 0.2.5 → 0.2.7
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/dist/index.d.ts +105 -33
- package/dist/index.js +364 -167
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ParserOptions, AnimationEffect, TransformerPlugin, ParsedBlock, Root, IncrementalUpdate, IncremarkParser, SourceBlock, TransformerOptions, DisplayBlock, BlockTransformer
|
|
1
|
+
import { ParserOptions, AnimationEffect, TransformerPlugin, ParsedBlock, Root, IncrementalUpdate, IncremarkParser, SourceBlock, TransformerOptions, DisplayBlock, BlockTransformer } from '@incremark/core';
|
|
2
2
|
export { AnimationEffect, BlockTransformer, DisplayBlock, IncrementalUpdate, ParsedBlock, ParserOptions, Root, RootContent, SourceBlock, TransformerOptions, TransformerPlugin, TransformerState, allPlugins, cloneNode, codeBlockPlugin, countChars, createBlockTransformer, createPlugin, defaultPlugins, imagePlugin, mathPlugin, mermaidPlugin, sliceAst, thematicBreakPlugin } from '@incremark/core';
|
|
3
|
-
import React, { ReactNode } from 'react';
|
|
3
|
+
import React$1, { ReactNode, ComponentType } from 'react';
|
|
4
4
|
import { Definition, FootnoteDefinition, RootContent, PhrasingContent } from 'mdast';
|
|
5
5
|
import * as _incremark_devtools from '@incremark/devtools';
|
|
6
6
|
import { DevToolsOptions } from '@incremark/devtools';
|
|
@@ -69,7 +69,7 @@ interface DefinitionsProviderProps {
|
|
|
69
69
|
* }
|
|
70
70
|
* ```
|
|
71
71
|
*/
|
|
72
|
-
declare const DefinitionsProvider: React.FC<DefinitionsProviderProps>;
|
|
72
|
+
declare const DefinitionsProvider: React$1.FC<DefinitionsProviderProps>;
|
|
73
73
|
/**
|
|
74
74
|
* useDefinitions Hook
|
|
75
75
|
*
|
|
@@ -111,10 +111,9 @@ interface UseIncremarkOptions extends ParserOptions {
|
|
|
111
111
|
/** 打字机配置,传入即创建 transformer(可通过 enabled 控制是否启用) */
|
|
112
112
|
typewriter?: TypewriterOptions;
|
|
113
113
|
}
|
|
114
|
-
|
|
115
|
-
stableId: string;
|
|
114
|
+
type RenderableBlock = ParsedBlock & {
|
|
116
115
|
isLastPending?: boolean;
|
|
117
|
-
}
|
|
116
|
+
};
|
|
118
117
|
/** 打字机控制对象 */
|
|
119
118
|
interface TypewriterControls {
|
|
120
119
|
/** 是否启用 */
|
|
@@ -175,7 +174,7 @@ declare function useIncremark(options?: UseIncremarkOptions): {
|
|
|
175
174
|
/** 当前完整的 AST */
|
|
176
175
|
ast: Root;
|
|
177
176
|
/** 用于渲染的 blocks(根据打字机设置自动处理) */
|
|
178
|
-
blocks:
|
|
177
|
+
blocks: RenderableBlock[];
|
|
179
178
|
/** 是否正在加载 */
|
|
180
179
|
isLoading: boolean;
|
|
181
180
|
/** 是否已完成(finalize) */
|
|
@@ -294,32 +293,81 @@ interface UseBlockTransformerReturn<T = unknown> {
|
|
|
294
293
|
*/
|
|
295
294
|
declare function useBlockTransformer<T = unknown>(sourceBlocks: SourceBlock<T>[], options?: UseBlockTransformerOptions): UseBlockTransformerReturn<T>;
|
|
296
295
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
296
|
+
type ComponentMap = Partial<Record<string, ComponentType<{
|
|
297
|
+
node: any;
|
|
298
|
+
}>>>;
|
|
299
|
+
/**
|
|
300
|
+
* 代码块配置
|
|
301
|
+
*/
|
|
302
|
+
interface CodeBlockConfig$1 {
|
|
303
|
+
/** 是否从一开始就接管渲染,而不是等到 completed 状态 */
|
|
304
|
+
takeOver?: boolean;
|
|
305
|
+
}
|
|
306
|
+
interface IncremarkContentProps {
|
|
307
|
+
stream?: () => AsyncGenerator<string>;
|
|
308
|
+
content?: string;
|
|
309
|
+
components?: ComponentMap;
|
|
310
|
+
/** 自定义容器组件映射,key 为容器名称(如 'warning', 'info') */
|
|
311
|
+
customContainers?: Record<string, ComponentType<{
|
|
312
|
+
name: string;
|
|
313
|
+
options?: Record<string, any>;
|
|
314
|
+
children?: React.ReactNode;
|
|
315
|
+
}>>;
|
|
316
|
+
/** 自定义代码块组件映射,key 为代码语言名称(如 'echart', 'mermaid') */
|
|
317
|
+
customCodeBlocks?: Record<string, ComponentType<{
|
|
318
|
+
codeStr: string;
|
|
319
|
+
lang?: string;
|
|
320
|
+
completed?: boolean;
|
|
321
|
+
takeOver?: boolean;
|
|
322
|
+
}>>;
|
|
323
|
+
/** 代码块配置映射,key 为代码语言名称 */
|
|
324
|
+
codeBlockConfigs?: Record<string, CodeBlockConfig$1>;
|
|
325
|
+
isFinished?: boolean;
|
|
326
|
+
incremarkOptions?: UseIncremarkOptions;
|
|
327
|
+
pendingClass?: string;
|
|
328
|
+
showBlockStatus?: boolean;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* 代码块配置
|
|
333
|
+
*/
|
|
334
|
+
interface CodeBlockConfig {
|
|
335
|
+
/** 是否从一开始就接管渲染,而不是等到 completed 状态 */
|
|
336
|
+
takeOver?: boolean;
|
|
300
337
|
}
|
|
301
338
|
interface IncremarkProps {
|
|
302
339
|
/** 要渲染的块列表 */
|
|
303
|
-
blocks?:
|
|
340
|
+
blocks?: RenderableBlock[];
|
|
341
|
+
/** 内容是否完全显示完成(用于控制脚注等需要在内容完全显示后才出现的元素)
|
|
342
|
+
* 如果传入了 incremark,则会自动使用 incremark.isDisplayComplete,此 prop 被忽略 */
|
|
343
|
+
isDisplayComplete?: boolean;
|
|
304
344
|
/** 自定义组件映射 */
|
|
305
|
-
components?: Partial<Record<string, React.ComponentType<{
|
|
345
|
+
components?: Partial<Record<string, React$1.ComponentType<{
|
|
306
346
|
node: any;
|
|
307
347
|
}>>>;
|
|
308
348
|
/** 自定义容器组件映射,key 为容器名称(如 'warning', 'info') */
|
|
309
|
-
customContainers?: Record<string, React.ComponentType<{
|
|
349
|
+
customContainers?: Record<string, React$1.ComponentType<{
|
|
310
350
|
name: string;
|
|
311
351
|
options?: Record<string, any>;
|
|
312
|
-
children?: React.ReactNode;
|
|
352
|
+
children?: React$1.ReactNode;
|
|
313
353
|
}>>;
|
|
314
354
|
/** 自定义代码块组件映射,key 为代码语言名称(如 'echart', 'mermaid') */
|
|
315
|
-
customCodeBlocks?: Record<string, React.ComponentType<{
|
|
355
|
+
customCodeBlocks?: Record<string, React$1.ComponentType<{
|
|
316
356
|
codeStr: string;
|
|
317
|
-
lang?: string;
|
|
357
|
+
lang?: string | undefined;
|
|
358
|
+
completed?: boolean | undefined;
|
|
359
|
+
takeOver?: boolean | undefined;
|
|
318
360
|
}>>;
|
|
361
|
+
/** 代码块配置映射,key 为代码语言名称 */
|
|
362
|
+
codeBlockConfigs?: Record<string, CodeBlockConfig>;
|
|
319
363
|
/** 是否显示块状态(待处理块边框) */
|
|
320
364
|
showBlockStatus?: boolean;
|
|
321
365
|
/** 自定义类名 */
|
|
322
366
|
className?: string;
|
|
367
|
+
/** 待处理块的样式类名 */
|
|
368
|
+
pendingClass?: string;
|
|
369
|
+
/** 已完成块的样式类名 */
|
|
370
|
+
completedClass?: string;
|
|
323
371
|
/** 可选:useIncremark 返回的对象(用于自动提供 context) */
|
|
324
372
|
incremark?: UseIncremarkReturn;
|
|
325
373
|
}
|
|
@@ -337,7 +385,26 @@ interface IncremarkProps {
|
|
|
337
385
|
* }
|
|
338
386
|
* ```
|
|
339
387
|
*/
|
|
340
|
-
declare const Incremark: React.FC<IncremarkProps>;
|
|
388
|
+
declare const Incremark: React$1.FC<IncremarkProps>;
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* IncremarkContent 组件
|
|
392
|
+
*
|
|
393
|
+
* 提供开箱即用的 Markdown 渲染体验,自动处理流式内容和普通内容
|
|
394
|
+
*
|
|
395
|
+
* @example
|
|
396
|
+
* ```tsx
|
|
397
|
+
* // 普通内容
|
|
398
|
+
* <IncremarkContent content={markdownString} />
|
|
399
|
+
*
|
|
400
|
+
* // 流式内容
|
|
401
|
+
* <IncremarkContent stream={() => streamGenerator()} />
|
|
402
|
+
*
|
|
403
|
+
* // 增量更新内容
|
|
404
|
+
* <IncremarkContent content={partialContent} isFinished={false} />
|
|
405
|
+
* ```
|
|
406
|
+
*/
|
|
407
|
+
declare const IncremarkContent: React$1.FC<IncremarkContentProps>;
|
|
341
408
|
|
|
342
409
|
/**
|
|
343
410
|
* 容器节点类型定义
|
|
@@ -351,29 +418,34 @@ interface ContainerNode {
|
|
|
351
418
|
}
|
|
352
419
|
|
|
353
420
|
interface IncremarkRendererProps {
|
|
354
|
-
node: RootContent
|
|
355
|
-
components?: Partial<Record<string, React.ComponentType<{
|
|
356
|
-
node:
|
|
421
|
+
node: RootContent | ContainerNode;
|
|
422
|
+
components?: Partial<Record<string, React$1.ComponentType<{
|
|
423
|
+
node: any;
|
|
357
424
|
}>>>;
|
|
358
|
-
customContainers?: Record<string, React.ComponentType<{
|
|
425
|
+
customContainers?: Record<string, React$1.ComponentType<{
|
|
359
426
|
name: string;
|
|
360
427
|
options?: Record<string, any>;
|
|
361
|
-
children?:
|
|
428
|
+
children?: ReactNode;
|
|
362
429
|
}>>;
|
|
363
|
-
customCodeBlocks?: Record<string, React.ComponentType<{
|
|
430
|
+
customCodeBlocks?: Record<string, React$1.ComponentType<{
|
|
364
431
|
codeStr: string;
|
|
365
432
|
lang?: string;
|
|
433
|
+
completed?: boolean;
|
|
434
|
+
takeOver?: boolean;
|
|
366
435
|
}>>;
|
|
436
|
+
codeBlockConfigs?: Record<string, {
|
|
437
|
+
takeOver?: boolean;
|
|
438
|
+
}>;
|
|
367
439
|
blockStatus?: 'pending' | 'stable' | 'completed';
|
|
368
440
|
}
|
|
369
441
|
/**
|
|
370
442
|
* 渲染单个 AST 节点
|
|
371
443
|
*/
|
|
372
|
-
declare const IncremarkRenderer: React.FC<IncremarkRendererProps>;
|
|
444
|
+
declare const IncremarkRenderer: React$1.FC<IncremarkRendererProps>;
|
|
373
445
|
|
|
374
446
|
interface AutoScrollContainerProps {
|
|
375
447
|
/** 子元素 */
|
|
376
|
-
children: React.ReactNode;
|
|
448
|
+
children: React$1.ReactNode;
|
|
377
449
|
/** 是否启用自动滚动 */
|
|
378
450
|
enabled?: boolean;
|
|
379
451
|
/** 触发自动滚动的底部阈值(像素) */
|
|
@@ -381,7 +453,7 @@ interface AutoScrollContainerProps {
|
|
|
381
453
|
/** 滚动行为 */
|
|
382
454
|
behavior?: ScrollBehavior;
|
|
383
455
|
/** 容器样式 */
|
|
384
|
-
style?: React.CSSProperties;
|
|
456
|
+
style?: React$1.CSSProperties;
|
|
385
457
|
/** 容器类名 */
|
|
386
458
|
className?: string;
|
|
387
459
|
}
|
|
@@ -411,7 +483,7 @@ interface AutoScrollContainerRef {
|
|
|
411
483
|
* scrollRef.current?.scrollToBottom()
|
|
412
484
|
* ```
|
|
413
485
|
*/
|
|
414
|
-
declare const AutoScrollContainer: React.ForwardRefExoticComponent<AutoScrollContainerProps & React.RefAttributes<AutoScrollContainerRef>>;
|
|
486
|
+
declare const AutoScrollContainer: React$1.ForwardRefExoticComponent<AutoScrollContainerProps & React$1.RefAttributes<AutoScrollContainerRef>>;
|
|
415
487
|
|
|
416
488
|
interface IncremarkInlineProps {
|
|
417
489
|
nodes: PhrasingContent[];
|
|
@@ -431,7 +503,7 @@ interface IncremarkInlineProps {
|
|
|
431
503
|
*
|
|
432
504
|
* 注意:此组件与 Vue 版本保持完全一致的逻辑和结构
|
|
433
505
|
*/
|
|
434
|
-
declare const IncremarkInline: React.FC<IncremarkInlineProps>;
|
|
506
|
+
declare const IncremarkInline: React$1.FC<IncremarkInlineProps>;
|
|
435
507
|
|
|
436
508
|
/**
|
|
437
509
|
* HtmlElementNode 类型定义(与 @incremark/core 中的定义一致)
|
|
@@ -455,7 +527,7 @@ interface IncremarkHtmlElementProps {
|
|
|
455
527
|
*
|
|
456
528
|
* 渲染结构化的 HTML 元素节点
|
|
457
529
|
*/
|
|
458
|
-
declare const IncremarkHtmlElement: React.FC<IncremarkHtmlElementProps>;
|
|
530
|
+
declare const IncremarkHtmlElement: React$1.FC<IncremarkHtmlElementProps>;
|
|
459
531
|
|
|
460
532
|
/**
|
|
461
533
|
* 脚注列表组件
|
|
@@ -482,7 +554,7 @@ declare const IncremarkHtmlElement: React.FC<IncremarkHtmlElementProps>;
|
|
|
482
554
|
* <IncremarkFootnotes />
|
|
483
555
|
* ```
|
|
484
556
|
*/
|
|
485
|
-
declare const IncremarkFootnotes: React.FC;
|
|
557
|
+
declare const IncremarkFootnotes: React$1.FC;
|
|
486
558
|
|
|
487
559
|
/**
|
|
488
560
|
* @file IncremarkContainerProvider - Incremark 容器级 Provider
|
|
@@ -514,7 +586,7 @@ interface IncremarkContainerProviderProps {
|
|
|
514
586
|
* @param props - 组件参数
|
|
515
587
|
* @returns ReactElement
|
|
516
588
|
*/
|
|
517
|
-
declare const IncremarkContainerProvider: React.FC<IncremarkContainerProviderProps>;
|
|
589
|
+
declare const IncremarkContainerProvider: React$1.FC<IncremarkContainerProviderProps>;
|
|
518
590
|
|
|
519
591
|
interface ThemeProviderProps {
|
|
520
592
|
/** 主题配置,可以是:
|
|
@@ -546,6 +618,6 @@ interface ThemeProviderProps {
|
|
|
546
618
|
* </ThemeProvider>
|
|
547
619
|
* ```
|
|
548
620
|
*/
|
|
549
|
-
declare const ThemeProvider: React.FC<ThemeProviderProps>;
|
|
621
|
+
declare const ThemeProvider: React$1.FC<ThemeProviderProps>;
|
|
550
622
|
|
|
551
|
-
export { AutoScrollContainer, type AutoScrollContainerProps, type AutoScrollContainerRef, type DefinitionsContextValue, DefinitionsProvider, type DefinitionsProviderProps, type HtmlElementNode, Incremark, IncremarkContainerProvider, type IncremarkContainerProviderProps, IncremarkFootnotes, IncremarkHtmlElement, type IncremarkHtmlElementProps, IncremarkInline, type IncremarkInlineProps, type IncremarkProps, IncremarkRenderer, type IncremarkRendererProps, ThemeProvider, type ThemeProviderProps, type TypewriterControls, type TypewriterOptions, type UseBlockTransformerOptions, type UseBlockTransformerReturn, type UseDevToolsOptions, type UseIncremarkOptions, type UseIncremarkReturn, useBlockTransformer, useDefinitions, useDevTools, useIncremark };
|
|
623
|
+
export { AutoScrollContainer, type AutoScrollContainerProps, type AutoScrollContainerRef, type DefinitionsContextValue, DefinitionsProvider, type DefinitionsProviderProps, type HtmlElementNode, Incremark, IncremarkContainerProvider, type IncremarkContainerProviderProps, IncremarkContent, type IncremarkContentProps, IncremarkFootnotes, IncremarkHtmlElement, type IncremarkHtmlElementProps, IncremarkInline, type IncremarkInlineProps, type IncremarkProps, IncremarkRenderer, type IncremarkRendererProps, ThemeProvider, type ThemeProviderProps, type TypewriterControls, type TypewriterOptions, type UseBlockTransformerOptions, type UseBlockTransformerReturn, type UseDevToolsOptions, type UseIncremarkOptions, type UseIncremarkReturn, useBlockTransformer, useDefinitions, useDevTools, useIncremark };
|
package/dist/index.js
CHANGED
|
@@ -193,17 +193,7 @@ function useTypewriter(options) {
|
|
|
193
193
|
}, [sourceBlocks, transformer]);
|
|
194
194
|
const blocks = useMemo(() => {
|
|
195
195
|
if (!typewriterEnabled || !transformer) {
|
|
196
|
-
|
|
197
|
-
for (const block of completedBlocks) {
|
|
198
|
-
result.push({ ...block, stableId: block.id });
|
|
199
|
-
}
|
|
200
|
-
for (let i = 0; i < pendingBlocks.length; i++) {
|
|
201
|
-
result.push({
|
|
202
|
-
...pendingBlocks[i],
|
|
203
|
-
stableId: `pending-${i}`
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
return result;
|
|
196
|
+
return [...completedBlocks, ...pendingBlocks];
|
|
207
197
|
}
|
|
208
198
|
return displayBlocks.map((db, index) => {
|
|
209
199
|
const isPending = !db.isDisplayComplete;
|
|
@@ -220,7 +210,6 @@ function useTypewriter(options) {
|
|
|
220
210
|
}
|
|
221
211
|
const block = {
|
|
222
212
|
id: db.id,
|
|
223
|
-
stableId: db.id,
|
|
224
213
|
status: db.isDisplayComplete ? "completed" : "pending",
|
|
225
214
|
isLastPending,
|
|
226
215
|
node,
|
|
@@ -326,12 +315,34 @@ function useIncremark(options = {}) {
|
|
|
326
315
|
const [pendingBlocks, setPendingBlocks] = useState3([]);
|
|
327
316
|
const [isLoading, setIsLoading] = useState3(false);
|
|
328
317
|
const [isFinalized, setIsFinalized] = useState3(false);
|
|
318
|
+
const handleUpdate = useCallback3(
|
|
319
|
+
(update, isFinalize) => {
|
|
320
|
+
setMarkdown(parser.getBuffer());
|
|
321
|
+
if (update.completed.length > 0) {
|
|
322
|
+
setCompletedBlocks((prev) => [...prev, ...update.completed]);
|
|
323
|
+
}
|
|
324
|
+
setPendingBlocks(update.pending);
|
|
325
|
+
if (isFinalize) {
|
|
326
|
+
setIsLoading(false);
|
|
327
|
+
setIsFinalized(true);
|
|
328
|
+
} else {
|
|
329
|
+
setIsLoading(true);
|
|
330
|
+
}
|
|
331
|
+
setFootnoteReferenceOrder(update.footnoteReferenceOrder);
|
|
332
|
+
},
|
|
333
|
+
[parser, setFootnoteReferenceOrder]
|
|
334
|
+
);
|
|
329
335
|
const { blocks, typewriter, transformer, isAnimationComplete } = useTypewriter({
|
|
330
336
|
typewriter: options.typewriter,
|
|
331
337
|
completedBlocks,
|
|
332
338
|
pendingBlocks
|
|
333
339
|
});
|
|
334
|
-
const isDisplayComplete =
|
|
340
|
+
const isDisplayComplete = useMemo2(() => {
|
|
341
|
+
if (!options.typewriter || !typewriter.enabled) {
|
|
342
|
+
return isFinalized;
|
|
343
|
+
}
|
|
344
|
+
return isFinalized && isAnimationComplete;
|
|
345
|
+
}, [options.typewriter, typewriter.enabled, isFinalized, isAnimationComplete]);
|
|
335
346
|
const ast = useMemo2(
|
|
336
347
|
() => ({
|
|
337
348
|
type: "root",
|
|
@@ -341,30 +352,17 @@ function useIncremark(options = {}) {
|
|
|
341
352
|
);
|
|
342
353
|
const append = useCallback3(
|
|
343
354
|
(chunk) => {
|
|
344
|
-
setIsLoading(true);
|
|
345
355
|
const update = parser.append(chunk);
|
|
346
|
-
|
|
347
|
-
if (update.completed.length > 0) {
|
|
348
|
-
setCompletedBlocks((prev) => [...prev, ...update.completed]);
|
|
349
|
-
}
|
|
350
|
-
setPendingBlocks(update.pending);
|
|
351
|
-
setFootnoteReferenceOrder(update.footnoteReferenceOrder);
|
|
356
|
+
handleUpdate(update, false);
|
|
352
357
|
return update;
|
|
353
358
|
},
|
|
354
|
-
[parser,
|
|
359
|
+
[parser, handleUpdate]
|
|
355
360
|
);
|
|
356
361
|
const finalize = useCallback3(() => {
|
|
357
362
|
const update = parser.finalize();
|
|
358
|
-
|
|
359
|
-
if (update.completed.length > 0) {
|
|
360
|
-
setCompletedBlocks((prev) => [...prev, ...update.completed]);
|
|
361
|
-
}
|
|
362
|
-
setPendingBlocks([]);
|
|
363
|
-
setIsLoading(false);
|
|
364
|
-
setIsFinalized(true);
|
|
365
|
-
setFootnoteReferenceOrder(update.footnoteReferenceOrder);
|
|
363
|
+
handleUpdate(update, true);
|
|
366
364
|
return update;
|
|
367
|
-
}, [parser,
|
|
365
|
+
}, [parser, handleUpdate]);
|
|
368
366
|
const abort = useCallback3(() => {
|
|
369
367
|
return finalize();
|
|
370
368
|
}, [finalize]);
|
|
@@ -443,8 +441,8 @@ function useDevTools(incremark, options = {}) {
|
|
|
443
441
|
devtoolsRef.current = devtools;
|
|
444
442
|
incremark.parser.setOnChange((state) => {
|
|
445
443
|
const blocks = [
|
|
446
|
-
...state.completedBlocks
|
|
447
|
-
...state.pendingBlocks
|
|
444
|
+
...state.completedBlocks,
|
|
445
|
+
...state.pendingBlocks
|
|
448
446
|
];
|
|
449
447
|
devtools.update({
|
|
450
448
|
blocks,
|
|
@@ -538,9 +536,6 @@ function useBlockTransformer(sourceBlocks, options = {}) {
|
|
|
538
536
|
};
|
|
539
537
|
}
|
|
540
538
|
|
|
541
|
-
// src/components/IncremarkRenderer.tsx
|
|
542
|
-
import React6 from "react";
|
|
543
|
-
|
|
544
539
|
// src/components/IncremarkInline.tsx
|
|
545
540
|
import React3 from "react";
|
|
546
541
|
import {
|
|
@@ -838,37 +833,169 @@ var IncremarkParagraph = ({ node }) => {
|
|
|
838
833
|
};
|
|
839
834
|
|
|
840
835
|
// src/components/IncremarkCode.tsx
|
|
841
|
-
import
|
|
836
|
+
import React5, { useState as useState5, useEffect as useEffect4, useRef as useRef5, useCallback as useCallback5 } from "react";
|
|
837
|
+
|
|
838
|
+
// src/hooks/useShiki.ts
|
|
839
|
+
import React4 from "react";
|
|
840
|
+
var ShikiManager = class _ShikiManager {
|
|
841
|
+
static instance = null;
|
|
842
|
+
/** 存储 highlighter 实例,key 为主题名称 */
|
|
843
|
+
highlighters = /* @__PURE__ */ new Map();
|
|
844
|
+
constructor() {
|
|
845
|
+
}
|
|
846
|
+
static getInstance() {
|
|
847
|
+
if (!_ShikiManager.instance) {
|
|
848
|
+
_ShikiManager.instance = new _ShikiManager();
|
|
849
|
+
}
|
|
850
|
+
return _ShikiManager.instance;
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* 获取或创建 highlighter
|
|
854
|
+
* @param theme 主题名称
|
|
855
|
+
* @returns highlighter 实例
|
|
856
|
+
*/
|
|
857
|
+
async getHighlighter(theme) {
|
|
858
|
+
if (this.highlighters.has(theme)) {
|
|
859
|
+
return this.highlighters.get(theme);
|
|
860
|
+
}
|
|
861
|
+
const { createHighlighter } = await import("shiki");
|
|
862
|
+
const highlighter = await createHighlighter({
|
|
863
|
+
themes: [theme],
|
|
864
|
+
langs: []
|
|
865
|
+
});
|
|
866
|
+
const info = {
|
|
867
|
+
highlighter,
|
|
868
|
+
loadedLanguages: /* @__PURE__ */ new Set(),
|
|
869
|
+
loadedThemes: /* @__PURE__ */ new Set([theme])
|
|
870
|
+
};
|
|
871
|
+
this.highlighters.set(theme, info);
|
|
872
|
+
return info;
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* 加载语言(按需)
|
|
876
|
+
* @param theme 主题名称
|
|
877
|
+
* @param lang 语言名称
|
|
878
|
+
*/
|
|
879
|
+
async loadLanguage(theme, lang) {
|
|
880
|
+
const info = this.highlighters.get(theme);
|
|
881
|
+
if (!info || info.loadedLanguages.has(lang)) return;
|
|
882
|
+
try {
|
|
883
|
+
await info.highlighter.loadLanguage(lang);
|
|
884
|
+
info.loadedLanguages.add(lang);
|
|
885
|
+
} catch {
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* 加载主题(按需)
|
|
890
|
+
* @param theme 主题名称
|
|
891
|
+
*/
|
|
892
|
+
async loadTheme(theme) {
|
|
893
|
+
const info = this.highlighters.get(theme);
|
|
894
|
+
if (!info || info.loadedThemes.has(theme)) return;
|
|
895
|
+
try {
|
|
896
|
+
await info.highlighter.loadTheme(theme);
|
|
897
|
+
info.loadedThemes.add(theme);
|
|
898
|
+
} catch {
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* 高亮代码
|
|
903
|
+
* @param theme 主题名称
|
|
904
|
+
* @param code 代码内容
|
|
905
|
+
* @param lang 语言名称
|
|
906
|
+
* @param fallbackTheme 回退主题
|
|
907
|
+
* @returns 高亮后的 HTML
|
|
908
|
+
*/
|
|
909
|
+
async codeToHtml(theme, code, lang, fallbackTheme) {
|
|
910
|
+
const info = this.highlighters.get(theme);
|
|
911
|
+
if (!info) throw new Error("Highlighter not found");
|
|
912
|
+
const actualLang = info.loadedLanguages.has(lang) ? lang : "text";
|
|
913
|
+
const actualTheme = info.loadedThemes.has(theme) ? theme : fallbackTheme;
|
|
914
|
+
return info.highlighter.codeToHtml(code, {
|
|
915
|
+
lang: actualLang,
|
|
916
|
+
theme: actualTheme
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* 清理所有 highlighter(应用退出或需要重置时调用)
|
|
921
|
+
*/
|
|
922
|
+
disposeAll() {
|
|
923
|
+
for (const [, info] of this.highlighters) {
|
|
924
|
+
if (info.highlighter?.dispose) {
|
|
925
|
+
info.highlighter.dispose();
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
this.highlighters.clear();
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
var shikiManager = ShikiManager.getInstance();
|
|
932
|
+
function useShiki(theme) {
|
|
933
|
+
const [isHighlighting, setIsHighlighting] = React4.useState(false);
|
|
934
|
+
const highlighterInfoRef = React4.useRef(null);
|
|
935
|
+
const getHighlighter = React4.useCallback(async () => {
|
|
936
|
+
if (!highlighterInfoRef.current) {
|
|
937
|
+
highlighterInfoRef.current = await shikiManager.getHighlighter(theme);
|
|
938
|
+
}
|
|
939
|
+
return highlighterInfoRef.current;
|
|
940
|
+
}, [theme]);
|
|
941
|
+
const highlight = React4.useCallback(async (code, lang, fallbackTheme) => {
|
|
942
|
+
setIsHighlighting(true);
|
|
943
|
+
try {
|
|
944
|
+
const info = await getHighlighter();
|
|
945
|
+
if (!info.loadedLanguages.has(lang) && lang !== "text") {
|
|
946
|
+
await shikiManager.loadLanguage(theme, lang);
|
|
947
|
+
}
|
|
948
|
+
if (!info.loadedThemes.has(theme)) {
|
|
949
|
+
await shikiManager.loadTheme(theme);
|
|
950
|
+
}
|
|
951
|
+
return await shikiManager.codeToHtml(theme, code, lang, fallbackTheme);
|
|
952
|
+
} catch (e) {
|
|
953
|
+
throw e;
|
|
954
|
+
} finally {
|
|
955
|
+
setIsHighlighting(false);
|
|
956
|
+
}
|
|
957
|
+
}, [getHighlighter, theme]);
|
|
958
|
+
return {
|
|
959
|
+
isHighlighting,
|
|
960
|
+
highlight
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// src/components/IncremarkCode.tsx
|
|
842
965
|
import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
843
966
|
var IncremarkCode = ({
|
|
844
967
|
node,
|
|
845
968
|
theme = "github-dark",
|
|
969
|
+
fallbackTheme = "github-dark",
|
|
846
970
|
disableHighlight = false,
|
|
847
971
|
mermaidDelay = 500,
|
|
848
972
|
customCodeBlocks,
|
|
849
|
-
blockStatus = "completed"
|
|
973
|
+
blockStatus = "completed",
|
|
974
|
+
codeBlockConfigs
|
|
850
975
|
}) => {
|
|
851
976
|
const [copied, setCopied] = useState5(false);
|
|
852
977
|
const [highlightedHtml, setHighlightedHtml] = useState5("");
|
|
853
|
-
const [isHighlighting, setIsHighlighting] = useState5(false);
|
|
854
978
|
const [mermaidSvg, setMermaidSvg] = useState5("");
|
|
855
979
|
const [mermaidLoading, setMermaidLoading] = useState5(false);
|
|
856
980
|
const [mermaidViewMode, setMermaidViewMode] = useState5("preview");
|
|
857
981
|
const mermaidRef = useRef5(null);
|
|
858
|
-
const highlighterRef = useRef5(null);
|
|
859
982
|
const mermaidTimerRef = useRef5(null);
|
|
860
|
-
const loadedLanguagesRef = useRef5(/* @__PURE__ */ new Set());
|
|
861
|
-
const loadedThemesRef = useRef5(/* @__PURE__ */ new Set());
|
|
862
983
|
const language = node.lang || "text";
|
|
863
984
|
const code = node.value;
|
|
864
985
|
const isMermaid = language === "mermaid";
|
|
865
|
-
const
|
|
866
|
-
|
|
986
|
+
const { isHighlighting, highlight } = useShiki(theme);
|
|
987
|
+
const CustomCodeBlock = React5.useMemo(() => {
|
|
988
|
+
const component = customCodeBlocks?.[language];
|
|
989
|
+
if (!component) return null;
|
|
990
|
+
const config = codeBlockConfigs?.[language];
|
|
991
|
+
if (config?.takeOver) {
|
|
992
|
+
return component;
|
|
993
|
+
}
|
|
994
|
+
if (blockStatus !== "completed") {
|
|
867
995
|
return null;
|
|
868
996
|
}
|
|
869
|
-
return
|
|
870
|
-
}, [customCodeBlocks, language, blockStatus]);
|
|
871
|
-
const useCustomComponent = !!CustomCodeBlock;
|
|
997
|
+
return component;
|
|
998
|
+
}, [customCodeBlocks, language, blockStatus, codeBlockConfigs]);
|
|
872
999
|
const toggleMermaidView = useCallback5(() => {
|
|
873
1000
|
setMermaidViewMode((prev) => prev === "preview" ? "source" : "preview");
|
|
874
1001
|
}, []);
|
|
@@ -889,7 +1016,8 @@ var IncremarkCode = ({
|
|
|
889
1016
|
mermaidRef.current.initialize({
|
|
890
1017
|
startOnLoad: false,
|
|
891
1018
|
theme: "dark",
|
|
892
|
-
securityLevel: "loose"
|
|
1019
|
+
securityLevel: "loose",
|
|
1020
|
+
suppressErrorRendering: true
|
|
893
1021
|
});
|
|
894
1022
|
}
|
|
895
1023
|
const mermaid = mermaidRef.current;
|
|
@@ -912,7 +1040,7 @@ var IncremarkCode = ({
|
|
|
912
1040
|
doRenderMermaid();
|
|
913
1041
|
}, mermaidDelay);
|
|
914
1042
|
}, [isMermaid, code, mermaidDelay, doRenderMermaid]);
|
|
915
|
-
const
|
|
1043
|
+
const doHighlight = useCallback5(async () => {
|
|
916
1044
|
if (isMermaid) {
|
|
917
1045
|
scheduleRenderMermaid();
|
|
918
1046
|
return;
|
|
@@ -921,46 +1049,16 @@ var IncremarkCode = ({
|
|
|
921
1049
|
setHighlightedHtml("");
|
|
922
1050
|
return;
|
|
923
1051
|
}
|
|
924
|
-
setIsHighlighting(true);
|
|
925
1052
|
try {
|
|
926
|
-
|
|
927
|
-
const { createHighlighter } = await import("shiki");
|
|
928
|
-
highlighterRef.current = await createHighlighter({
|
|
929
|
-
themes: [theme],
|
|
930
|
-
langs: []
|
|
931
|
-
});
|
|
932
|
-
loadedThemesRef.current.add(theme);
|
|
933
|
-
}
|
|
934
|
-
const highlighter = highlighterRef.current;
|
|
935
|
-
const lang = language;
|
|
936
|
-
if (!loadedLanguagesRef.current.has(lang) && lang !== "text") {
|
|
937
|
-
try {
|
|
938
|
-
await highlighter.loadLanguage(lang);
|
|
939
|
-
loadedLanguagesRef.current.add(lang);
|
|
940
|
-
} catch {
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
if (!loadedThemesRef.current.has(theme)) {
|
|
944
|
-
try {
|
|
945
|
-
await highlighter.loadTheme(theme);
|
|
946
|
-
loadedThemesRef.current.add(theme);
|
|
947
|
-
} catch {
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
const html = highlighter.codeToHtml(code, {
|
|
951
|
-
lang: loadedLanguagesRef.current.has(lang) ? lang : "text",
|
|
952
|
-
theme: loadedThemesRef.current.has(theme) ? theme : "github-dark"
|
|
953
|
-
});
|
|
1053
|
+
const html = await highlight(code, language, fallbackTheme);
|
|
954
1054
|
setHighlightedHtml(html);
|
|
955
1055
|
} catch {
|
|
956
1056
|
setHighlightedHtml("");
|
|
957
|
-
} finally {
|
|
958
|
-
setIsHighlighting(false);
|
|
959
1057
|
}
|
|
960
|
-
}, [code, language,
|
|
1058
|
+
}, [code, language, fallbackTheme, disableHighlight, isMermaid, highlight, scheduleRenderMermaid]);
|
|
961
1059
|
useEffect4(() => {
|
|
962
|
-
|
|
963
|
-
}, [
|
|
1060
|
+
doHighlight();
|
|
1061
|
+
}, [doHighlight]);
|
|
964
1062
|
useEffect4(() => {
|
|
965
1063
|
return () => {
|
|
966
1064
|
if (mermaidTimerRef.current) {
|
|
@@ -968,8 +1066,17 @@ var IncremarkCode = ({
|
|
|
968
1066
|
}
|
|
969
1067
|
};
|
|
970
1068
|
}, []);
|
|
971
|
-
if (
|
|
972
|
-
|
|
1069
|
+
if (CustomCodeBlock) {
|
|
1070
|
+
const config = codeBlockConfigs?.[language];
|
|
1071
|
+
return /* @__PURE__ */ jsx6(
|
|
1072
|
+
CustomCodeBlock,
|
|
1073
|
+
{
|
|
1074
|
+
codeStr: code,
|
|
1075
|
+
lang: language,
|
|
1076
|
+
completed: blockStatus === "completed",
|
|
1077
|
+
takeOver: config?.takeOver
|
|
1078
|
+
}
|
|
1079
|
+
);
|
|
973
1080
|
}
|
|
974
1081
|
if (isMermaid) {
|
|
975
1082
|
return /* @__PURE__ */ jsxs2("div", { className: "incremark-mermaid", children: [
|
|
@@ -1002,6 +1109,7 @@ var IncremarkCode = ({
|
|
|
1002
1109
|
};
|
|
1003
1110
|
|
|
1004
1111
|
// src/components/IncremarkList.tsx
|
|
1112
|
+
import React6 from "react";
|
|
1005
1113
|
import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1006
1114
|
function getItemInlineContent(item) {
|
|
1007
1115
|
const firstChild = item.children[0];
|
|
@@ -1021,7 +1129,7 @@ function getItemBlockChildren(item) {
|
|
|
1021
1129
|
var IncremarkList = ({ node }) => {
|
|
1022
1130
|
const Tag = node.ordered ? "ol" : "ul";
|
|
1023
1131
|
const isTaskList = node.children?.some((item) => item.checked !== null && item.checked !== void 0);
|
|
1024
|
-
return /* @__PURE__ */ jsx7(Tag, { className: `incremark-list ${isTaskList ? "task-list" : ""}`, children: node.children?.map((item, index) => {
|
|
1132
|
+
return /* @__PURE__ */ jsx7(Tag, { className: `incremark-list ${isTaskList ? "task-list" : ""}`, start: node.start || void 0, children: node.children?.map((item, index) => {
|
|
1025
1133
|
const isTaskItem = item.checked !== null && item.checked !== void 0;
|
|
1026
1134
|
const inlineContent = getItemInlineContent(item);
|
|
1027
1135
|
const blockChildren = getItemBlockChildren(item);
|
|
@@ -1041,25 +1149,16 @@ var IncremarkList = ({ node }) => {
|
|
|
1041
1149
|
}
|
|
1042
1150
|
return /* @__PURE__ */ jsxs3("li", { className: "incremark-list-item", children: [
|
|
1043
1151
|
/* @__PURE__ */ jsx7(IncremarkInline, { nodes: inlineContent }),
|
|
1044
|
-
blockChildren.map((child, childIndex) => {
|
|
1045
|
-
if (child.type === "list") {
|
|
1046
|
-
return /* @__PURE__ */ jsx7(IncremarkList, { node: child }, childIndex);
|
|
1047
|
-
}
|
|
1048
|
-
return null;
|
|
1049
|
-
})
|
|
1152
|
+
blockChildren.map((child, childIndex) => /* @__PURE__ */ jsx7(React6.Fragment, { children: /* @__PURE__ */ jsx7(IncremarkRenderer, { node: child }) }, childIndex))
|
|
1050
1153
|
] }, index);
|
|
1051
1154
|
}) });
|
|
1052
1155
|
};
|
|
1053
1156
|
|
|
1054
1157
|
// src/components/IncremarkBlockquote.tsx
|
|
1158
|
+
import React7 from "react";
|
|
1055
1159
|
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1056
1160
|
var IncremarkBlockquote = ({ node }) => {
|
|
1057
|
-
return /* @__PURE__ */ jsx8("blockquote", { className: "incremark-blockquote", children: node.children.map((child, index) => {
|
|
1058
|
-
if (child.type === "paragraph") {
|
|
1059
|
-
return /* @__PURE__ */ jsx8(IncremarkParagraph, { node: child }, index);
|
|
1060
|
-
}
|
|
1061
|
-
return /* @__PURE__ */ jsx8("div", { className: "unknown-child", children: child.type }, index);
|
|
1062
|
-
}) });
|
|
1161
|
+
return /* @__PURE__ */ jsx8("blockquote", { className: "incremark-blockquote", children: node.children.map((child, index) => /* @__PURE__ */ jsx8(React7.Fragment, { children: /* @__PURE__ */ jsx8(IncremarkRenderer, { node: child }) }, index)) });
|
|
1063
1162
|
};
|
|
1064
1163
|
|
|
1065
1164
|
// src/components/IncremarkTable.tsx
|
|
@@ -1156,17 +1255,8 @@ var IncremarkMath = ({
|
|
|
1156
1255
|
return /* @__PURE__ */ jsx11("div", { className: "incremark-math-block", children: renderedHtml && !isLoading ? /* @__PURE__ */ jsx11("div", { className: "math-rendered", dangerouslySetInnerHTML: { __html: renderedHtml } }) : /* @__PURE__ */ jsx11("pre", { className: "math-source-block", children: /* @__PURE__ */ jsx11("code", { children: formula }) }) });
|
|
1157
1256
|
};
|
|
1158
1257
|
|
|
1159
|
-
// src/components/IncremarkDefault.tsx
|
|
1160
|
-
import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1161
|
-
var IncremarkDefault = ({ node }) => {
|
|
1162
|
-
return /* @__PURE__ */ jsxs5("div", { className: "incremark-default", children: [
|
|
1163
|
-
/* @__PURE__ */ jsx12("span", { className: "type-badge", children: node.type }),
|
|
1164
|
-
/* @__PURE__ */ jsx12("pre", { children: JSON.stringify(node, null, 2) })
|
|
1165
|
-
] });
|
|
1166
|
-
};
|
|
1167
|
-
|
|
1168
1258
|
// src/components/IncremarkContainer.tsx
|
|
1169
|
-
import { jsx as
|
|
1259
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
1170
1260
|
function parseOptions(attributes) {
|
|
1171
1261
|
if (!attributes) return {};
|
|
1172
1262
|
const options = {};
|
|
@@ -1184,52 +1274,57 @@ var IncremarkContainer = ({ node, customContainers }) => {
|
|
|
1184
1274
|
const options = parseOptions(node.attributes);
|
|
1185
1275
|
const CustomContainer = customContainers?.[containerName];
|
|
1186
1276
|
if (CustomContainer) {
|
|
1187
|
-
return /* @__PURE__ */
|
|
1277
|
+
return /* @__PURE__ */ jsx12(CustomContainer, { name: containerName, options, children: node.children?.map((child, index) => /* @__PURE__ */ jsx12(IncremarkRenderer, { node: child }, index)) });
|
|
1188
1278
|
}
|
|
1189
|
-
return /* @__PURE__ */
|
|
1279
|
+
return /* @__PURE__ */ jsx12("div", { className: `incremark-container incremark-container-${containerName}`, children: node.children && node.children.length > 0 && /* @__PURE__ */ jsx12("div", { className: "incremark-container-content", children: node.children.map((child, index) => /* @__PURE__ */ jsx12(IncremarkRenderer, { node: child }, index)) }) });
|
|
1190
1280
|
};
|
|
1191
1281
|
|
|
1192
|
-
// src/components/
|
|
1193
|
-
import {
|
|
1194
|
-
var
|
|
1195
|
-
return /* @__PURE__ */
|
|
1282
|
+
// src/components/IncremarkDefault.tsx
|
|
1283
|
+
import { jsx as jsx13, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1284
|
+
var IncremarkDefault = ({ node }) => {
|
|
1285
|
+
return /* @__PURE__ */ jsxs5("div", { className: "incremark-default", children: [
|
|
1286
|
+
/* @__PURE__ */ jsx13("span", { className: "type-badge", children: node.type }),
|
|
1287
|
+
/* @__PURE__ */ jsx13("pre", { children: JSON.stringify(node, null, 2) })
|
|
1288
|
+
] });
|
|
1196
1289
|
};
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
var DefaultBlockquote = ({ node }) => /* @__PURE__ */ jsx14(IncremarkBlockquote, { node });
|
|
1201
|
-
var DefaultTable = ({ node }) => /* @__PURE__ */ jsx14(IncremarkTable, { node });
|
|
1202
|
-
var DefaultThematicBreak = () => /* @__PURE__ */ jsx14(IncremarkThematicBreak, {});
|
|
1203
|
-
var DefaultMath = ({ node }) => /* @__PURE__ */ jsx14(IncremarkMath, { node });
|
|
1204
|
-
var DefaultHtmlElement = ({ node }) => /* @__PURE__ */ jsx14(IncremarkHtmlElement, { node });
|
|
1205
|
-
var DefaultDefault = ({ node }) => /* @__PURE__ */ jsx14(IncremarkDefault, { node });
|
|
1290
|
+
|
|
1291
|
+
// src/components/IncremarkRenderer.tsx
|
|
1292
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
1206
1293
|
var defaultComponents = {
|
|
1207
|
-
heading:
|
|
1208
|
-
paragraph:
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1294
|
+
heading: IncremarkHeading,
|
|
1295
|
+
paragraph: IncremarkParagraph,
|
|
1296
|
+
list: IncremarkList,
|
|
1297
|
+
blockquote: IncremarkBlockquote,
|
|
1298
|
+
table: IncremarkTable,
|
|
1299
|
+
thematicBreak: IncremarkThematicBreak,
|
|
1300
|
+
math: IncremarkMath,
|
|
1301
|
+
inlineMath: IncremarkMath,
|
|
1302
|
+
htmlElement: IncremarkHtmlElement,
|
|
1303
|
+
containerDirective: IncremarkContainer,
|
|
1304
|
+
leafDirective: IncremarkContainer,
|
|
1305
|
+
textDirective: IncremarkContainer
|
|
1218
1306
|
};
|
|
1219
1307
|
function isContainerNode(node) {
|
|
1220
1308
|
return node.type === "containerDirective" || node.type === "leafDirective" || node.type === "textDirective";
|
|
1221
1309
|
}
|
|
1310
|
+
function isHtmlNode2(node) {
|
|
1311
|
+
return node.type === "html";
|
|
1312
|
+
}
|
|
1313
|
+
function getComponent(type, userComponents) {
|
|
1314
|
+
return userComponents[type] || defaultComponents[type] || IncremarkDefault;
|
|
1315
|
+
}
|
|
1222
1316
|
var IncremarkRenderer = ({
|
|
1223
1317
|
node,
|
|
1224
1318
|
components = {},
|
|
1225
1319
|
customContainers,
|
|
1226
1320
|
customCodeBlocks,
|
|
1321
|
+
codeBlockConfigs,
|
|
1227
1322
|
blockStatus
|
|
1228
1323
|
}) => {
|
|
1229
1324
|
if (node.type === "footnoteDefinition") {
|
|
1230
1325
|
return null;
|
|
1231
1326
|
}
|
|
1232
|
-
if (node
|
|
1327
|
+
if (isHtmlNode2(node)) {
|
|
1233
1328
|
return /* @__PURE__ */ jsx14("pre", { className: "incremark-html-code", children: /* @__PURE__ */ jsx14("code", { children: node.value }) });
|
|
1234
1329
|
}
|
|
1235
1330
|
if (isContainerNode(node)) {
|
|
@@ -1247,21 +1342,21 @@ var IncremarkRenderer = ({
|
|
|
1247
1342
|
{
|
|
1248
1343
|
node,
|
|
1249
1344
|
customCodeBlocks,
|
|
1345
|
+
codeBlockConfigs,
|
|
1250
1346
|
blockStatus
|
|
1251
1347
|
}
|
|
1252
1348
|
);
|
|
1253
1349
|
}
|
|
1254
|
-
const
|
|
1255
|
-
const Component = mergedComponents[node.type] || DefaultDefault;
|
|
1350
|
+
const Component = getComponent(node.type, components);
|
|
1256
1351
|
return /* @__PURE__ */ jsx14(Component, { node });
|
|
1257
1352
|
};
|
|
1258
1353
|
|
|
1259
1354
|
// src/components/IncremarkFootnotes.tsx
|
|
1260
|
-
import
|
|
1261
|
-
import { jsx as jsx15, jsxs as
|
|
1355
|
+
import React9 from "react";
|
|
1356
|
+
import { jsx as jsx15, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1262
1357
|
var IncremarkFootnotes = () => {
|
|
1263
1358
|
const { footnoteDefinitions, footnoteReferenceOrder } = useDefinitions();
|
|
1264
|
-
const orderedFootnotes =
|
|
1359
|
+
const orderedFootnotes = React9.useMemo(() => {
|
|
1265
1360
|
return footnoteReferenceOrder.map((identifier) => ({
|
|
1266
1361
|
identifier,
|
|
1267
1362
|
definition: footnoteDefinitions[identifier]
|
|
@@ -1270,16 +1365,16 @@ var IncremarkFootnotes = () => {
|
|
|
1270
1365
|
if (orderedFootnotes.length === 0) {
|
|
1271
1366
|
return null;
|
|
1272
1367
|
}
|
|
1273
|
-
return /* @__PURE__ */
|
|
1368
|
+
return /* @__PURE__ */ jsxs6("section", { className: "incremark-footnotes", children: [
|
|
1274
1369
|
/* @__PURE__ */ jsx15("hr", { className: "incremark-footnotes-divider" }),
|
|
1275
|
-
/* @__PURE__ */ jsx15("ol", { className: "incremark-footnotes-list", children: orderedFootnotes.map((item, index) => /* @__PURE__ */
|
|
1370
|
+
/* @__PURE__ */ jsx15("ol", { className: "incremark-footnotes-list", children: orderedFootnotes.map((item, index) => /* @__PURE__ */ jsxs6(
|
|
1276
1371
|
"li",
|
|
1277
1372
|
{
|
|
1278
1373
|
id: `fn-${item.identifier}`,
|
|
1279
1374
|
className: "incremark-footnote-item",
|
|
1280
1375
|
children: [
|
|
1281
|
-
/* @__PURE__ */
|
|
1282
|
-
/* @__PURE__ */
|
|
1376
|
+
/* @__PURE__ */ jsxs6("div", { className: "incremark-footnote-content", children: [
|
|
1377
|
+
/* @__PURE__ */ jsxs6("span", { className: "incremark-footnote-number", children: [
|
|
1283
1378
|
index + 1,
|
|
1284
1379
|
"."
|
|
1285
1380
|
] }),
|
|
@@ -1308,15 +1403,18 @@ var IncremarkContainerProvider = ({ children, definitions }) => {
|
|
|
1308
1403
|
};
|
|
1309
1404
|
|
|
1310
1405
|
// src/components/Incremark.tsx
|
|
1311
|
-
import { jsx as jsx17, jsxs as
|
|
1406
|
+
import { jsx as jsx17, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1312
1407
|
var Incremark = (props) => {
|
|
1313
1408
|
const {
|
|
1314
1409
|
blocks: propsBlocks,
|
|
1410
|
+
isDisplayComplete: propsIsDisplayComplete = false,
|
|
1315
1411
|
components,
|
|
1316
1412
|
customContainers,
|
|
1317
1413
|
customCodeBlocks,
|
|
1318
1414
|
showBlockStatus = true,
|
|
1319
1415
|
className = "",
|
|
1416
|
+
pendingClass = "incremark-pending",
|
|
1417
|
+
completedClass = "incremark-completed",
|
|
1320
1418
|
incremark
|
|
1321
1419
|
} = props;
|
|
1322
1420
|
if (incremark) {
|
|
@@ -1330,12 +1428,14 @@ var Incremark = (props) => {
|
|
|
1330
1428
|
customCodeBlocks,
|
|
1331
1429
|
showBlockStatus,
|
|
1332
1430
|
className,
|
|
1431
|
+
pendingClass,
|
|
1432
|
+
completedClass,
|
|
1333
1433
|
isDisplayComplete: isDisplayComplete2
|
|
1334
1434
|
}
|
|
1335
1435
|
) });
|
|
1336
1436
|
}
|
|
1337
1437
|
const blocks = propsBlocks || [];
|
|
1338
|
-
const isDisplayComplete =
|
|
1438
|
+
const isDisplayComplete = propsIsDisplayComplete;
|
|
1339
1439
|
return /* @__PURE__ */ jsx17(
|
|
1340
1440
|
IncremarkInternal,
|
|
1341
1441
|
{
|
|
@@ -1345,6 +1445,8 @@ var Incremark = (props) => {
|
|
|
1345
1445
|
customCodeBlocks,
|
|
1346
1446
|
showBlockStatus,
|
|
1347
1447
|
className,
|
|
1448
|
+
pendingClass,
|
|
1449
|
+
completedClass,
|
|
1348
1450
|
isDisplayComplete
|
|
1349
1451
|
}
|
|
1350
1452
|
);
|
|
@@ -1354,11 +1456,14 @@ var IncremarkInternal = ({
|
|
|
1354
1456
|
components,
|
|
1355
1457
|
customContainers,
|
|
1356
1458
|
customCodeBlocks,
|
|
1459
|
+
codeBlockConfigs,
|
|
1357
1460
|
showBlockStatus,
|
|
1358
1461
|
className,
|
|
1462
|
+
pendingClass,
|
|
1463
|
+
completedClass,
|
|
1359
1464
|
isDisplayComplete
|
|
1360
1465
|
}) => {
|
|
1361
|
-
return /* @__PURE__ */
|
|
1466
|
+
return /* @__PURE__ */ jsxs7("div", { className: `incremark ${className}`, children: [
|
|
1362
1467
|
blocks.map((block) => {
|
|
1363
1468
|
if (block.node.type === "definition" || block.node.type === "footnoteDefinition") {
|
|
1364
1469
|
return null;
|
|
@@ -1366,7 +1471,7 @@ var IncremarkInternal = ({
|
|
|
1366
1471
|
const isPending = block.status === "pending";
|
|
1367
1472
|
const classes = [
|
|
1368
1473
|
"incremark-block",
|
|
1369
|
-
isPending ?
|
|
1474
|
+
isPending ? pendingClass : completedClass,
|
|
1370
1475
|
showBlockStatus && "incremark-show-status",
|
|
1371
1476
|
block.isLastPending && "incremark-last-pending"
|
|
1372
1477
|
].filter(Boolean).join(" ");
|
|
@@ -1377,24 +1482,115 @@ var IncremarkInternal = ({
|
|
|
1377
1482
|
components,
|
|
1378
1483
|
customContainers,
|
|
1379
1484
|
customCodeBlocks,
|
|
1485
|
+
codeBlockConfigs,
|
|
1380
1486
|
blockStatus: block.status
|
|
1381
1487
|
}
|
|
1382
|
-
) }, block.
|
|
1488
|
+
) }, block.id);
|
|
1383
1489
|
}),
|
|
1384
1490
|
isDisplayComplete && /* @__PURE__ */ jsx17(IncremarkFootnotes, {})
|
|
1385
1491
|
] });
|
|
1386
1492
|
};
|
|
1387
1493
|
|
|
1494
|
+
// src/components/IncremarkContent.tsx
|
|
1495
|
+
import { useEffect as useEffect6, useRef as useRef7, useMemo as useMemo3, useCallback as useCallback7 } from "react";
|
|
1496
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
1497
|
+
var IncremarkContent = (props) => {
|
|
1498
|
+
const {
|
|
1499
|
+
stream,
|
|
1500
|
+
content,
|
|
1501
|
+
components,
|
|
1502
|
+
customContainers,
|
|
1503
|
+
customCodeBlocks,
|
|
1504
|
+
codeBlockConfigs,
|
|
1505
|
+
isFinished = false,
|
|
1506
|
+
incremarkOptions,
|
|
1507
|
+
showBlockStatus,
|
|
1508
|
+
pendingClass
|
|
1509
|
+
} = props;
|
|
1510
|
+
const initialOptionsRef = useRef7({
|
|
1511
|
+
gfm: true,
|
|
1512
|
+
htmlTree: true,
|
|
1513
|
+
containers: true,
|
|
1514
|
+
math: true,
|
|
1515
|
+
...incremarkOptions
|
|
1516
|
+
});
|
|
1517
|
+
const { blocks, append, finalize, render, reset, isDisplayComplete, markdown, typewriter, _definitionsContextValue } = useIncremark(initialOptionsRef.current);
|
|
1518
|
+
useEffect6(() => {
|
|
1519
|
+
if (incremarkOptions?.typewriter) {
|
|
1520
|
+
typewriter.setOptions(incremarkOptions.typewriter);
|
|
1521
|
+
}
|
|
1522
|
+
}, [incremarkOptions?.typewriter, typewriter]);
|
|
1523
|
+
const isStreamMode = useMemo3(() => typeof stream === "function", [stream]);
|
|
1524
|
+
const prevContentRef = useRef7(void 0);
|
|
1525
|
+
const isStreamingRef = useRef7(false);
|
|
1526
|
+
const handleStreamInput = useCallback7(async () => {
|
|
1527
|
+
if (!stream || isStreamingRef.current) return;
|
|
1528
|
+
isStreamingRef.current = true;
|
|
1529
|
+
try {
|
|
1530
|
+
const streamGen = stream();
|
|
1531
|
+
for await (const chunk of streamGen) {
|
|
1532
|
+
append(chunk);
|
|
1533
|
+
}
|
|
1534
|
+
finalize();
|
|
1535
|
+
} catch (error) {
|
|
1536
|
+
console.error("Stream error: ", error);
|
|
1537
|
+
finalize();
|
|
1538
|
+
} finally {
|
|
1539
|
+
isStreamingRef.current = false;
|
|
1540
|
+
}
|
|
1541
|
+
}, [stream, append, finalize]);
|
|
1542
|
+
const handleContentInput = useCallback7((newContent, oldContent) => {
|
|
1543
|
+
if (!newContent) {
|
|
1544
|
+
if (oldContent) {
|
|
1545
|
+
reset();
|
|
1546
|
+
}
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1549
|
+
if (newContent?.startsWith(oldContent ?? "")) {
|
|
1550
|
+
const delta = newContent.slice((oldContent || "").length);
|
|
1551
|
+
append(delta);
|
|
1552
|
+
} else {
|
|
1553
|
+
render(newContent);
|
|
1554
|
+
}
|
|
1555
|
+
}, [append, render, reset]);
|
|
1556
|
+
useEffect6(() => {
|
|
1557
|
+
if (isStreamMode) {
|
|
1558
|
+
handleStreamInput();
|
|
1559
|
+
} else {
|
|
1560
|
+
handleContentInput(content, prevContentRef.current);
|
|
1561
|
+
}
|
|
1562
|
+
prevContentRef.current = content;
|
|
1563
|
+
}, [content, isStreamMode, handleStreamInput, handleContentInput]);
|
|
1564
|
+
useEffect6(() => {
|
|
1565
|
+
if (isFinished && content === markdown) {
|
|
1566
|
+
finalize();
|
|
1567
|
+
}
|
|
1568
|
+
}, [isFinished, content, markdown, finalize]);
|
|
1569
|
+
return /* @__PURE__ */ jsx18(IncremarkContainerProvider, { definitions: _definitionsContextValue, children: /* @__PURE__ */ jsx18(
|
|
1570
|
+
Incremark,
|
|
1571
|
+
{
|
|
1572
|
+
blocks,
|
|
1573
|
+
isDisplayComplete,
|
|
1574
|
+
showBlockStatus,
|
|
1575
|
+
pendingClass,
|
|
1576
|
+
components,
|
|
1577
|
+
customContainers,
|
|
1578
|
+
customCodeBlocks,
|
|
1579
|
+
codeBlockConfigs
|
|
1580
|
+
}
|
|
1581
|
+
) });
|
|
1582
|
+
};
|
|
1583
|
+
|
|
1388
1584
|
// src/components/AutoScrollContainer.tsx
|
|
1389
1585
|
import {
|
|
1390
|
-
useRef as
|
|
1391
|
-
useEffect as
|
|
1392
|
-
useCallback as
|
|
1586
|
+
useRef as useRef8,
|
|
1587
|
+
useEffect as useEffect7,
|
|
1588
|
+
useCallback as useCallback8,
|
|
1393
1589
|
useState as useState7,
|
|
1394
1590
|
forwardRef,
|
|
1395
1591
|
useImperativeHandle
|
|
1396
1592
|
} from "react";
|
|
1397
|
-
import { jsx as
|
|
1593
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
1398
1594
|
var AutoScrollContainer = forwardRef(
|
|
1399
1595
|
({
|
|
1400
1596
|
children,
|
|
@@ -1404,17 +1600,17 @@ var AutoScrollContainer = forwardRef(
|
|
|
1404
1600
|
style,
|
|
1405
1601
|
className
|
|
1406
1602
|
}, ref) => {
|
|
1407
|
-
const containerRef =
|
|
1603
|
+
const containerRef = useRef8(null);
|
|
1408
1604
|
const [isUserScrolledUp, setIsUserScrolledUp] = useState7(false);
|
|
1409
|
-
const lastScrollTopRef =
|
|
1410
|
-
const lastScrollHeightRef =
|
|
1411
|
-
const isNearBottom =
|
|
1605
|
+
const lastScrollTopRef = useRef8(0);
|
|
1606
|
+
const lastScrollHeightRef = useRef8(0);
|
|
1607
|
+
const isNearBottom = useCallback8(() => {
|
|
1412
1608
|
const container = containerRef.current;
|
|
1413
1609
|
if (!container) return true;
|
|
1414
1610
|
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
1415
1611
|
return scrollHeight - scrollTop - clientHeight <= threshold;
|
|
1416
1612
|
}, [threshold]);
|
|
1417
|
-
const scrollToBottom =
|
|
1613
|
+
const scrollToBottom = useCallback8(
|
|
1418
1614
|
(force = false) => {
|
|
1419
1615
|
const container = containerRef.current;
|
|
1420
1616
|
if (!container) return;
|
|
@@ -1426,12 +1622,12 @@ var AutoScrollContainer = forwardRef(
|
|
|
1426
1622
|
},
|
|
1427
1623
|
[isUserScrolledUp, behavior]
|
|
1428
1624
|
);
|
|
1429
|
-
const hasScrollbar =
|
|
1625
|
+
const hasScrollbar = useCallback8(() => {
|
|
1430
1626
|
const container = containerRef.current;
|
|
1431
1627
|
if (!container) return false;
|
|
1432
1628
|
return container.scrollHeight > container.clientHeight;
|
|
1433
1629
|
}, []);
|
|
1434
|
-
const handleScroll =
|
|
1630
|
+
const handleScroll = useCallback8(() => {
|
|
1435
1631
|
const container = containerRef.current;
|
|
1436
1632
|
if (!container) return;
|
|
1437
1633
|
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
@@ -1453,14 +1649,14 @@ var AutoScrollContainer = forwardRef(
|
|
|
1453
1649
|
lastScrollTopRef.current = scrollTop;
|
|
1454
1650
|
lastScrollHeightRef.current = scrollHeight;
|
|
1455
1651
|
}, [isNearBottom]);
|
|
1456
|
-
|
|
1652
|
+
useEffect7(() => {
|
|
1457
1653
|
const container = containerRef.current;
|
|
1458
1654
|
if (container) {
|
|
1459
1655
|
lastScrollTopRef.current = container.scrollTop;
|
|
1460
1656
|
lastScrollHeightRef.current = container.scrollHeight;
|
|
1461
1657
|
}
|
|
1462
1658
|
}, []);
|
|
1463
|
-
|
|
1659
|
+
useEffect7(() => {
|
|
1464
1660
|
const container = containerRef.current;
|
|
1465
1661
|
if (!container || !enabled) return;
|
|
1466
1662
|
const observer = new MutationObserver(() => {
|
|
@@ -1492,7 +1688,7 @@ var AutoScrollContainer = forwardRef(
|
|
|
1492
1688
|
}),
|
|
1493
1689
|
[scrollToBottom, isUserScrolledUp]
|
|
1494
1690
|
);
|
|
1495
|
-
return /* @__PURE__ */
|
|
1691
|
+
return /* @__PURE__ */ jsx19(
|
|
1496
1692
|
"div",
|
|
1497
1693
|
{
|
|
1498
1694
|
ref: containerRef,
|
|
@@ -1511,21 +1707,21 @@ var AutoScrollContainer = forwardRef(
|
|
|
1511
1707
|
AutoScrollContainer.displayName = "AutoScrollContainer";
|
|
1512
1708
|
|
|
1513
1709
|
// src/ThemeProvider.tsx
|
|
1514
|
-
import { useEffect as
|
|
1710
|
+
import { useEffect as useEffect8, useRef as useRef9 } from "react";
|
|
1515
1711
|
import { applyTheme } from "@incremark/theme";
|
|
1516
|
-
import { jsx as
|
|
1712
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
1517
1713
|
var ThemeProvider = ({
|
|
1518
1714
|
theme,
|
|
1519
1715
|
children,
|
|
1520
1716
|
className = ""
|
|
1521
1717
|
}) => {
|
|
1522
|
-
const containerRef =
|
|
1523
|
-
|
|
1718
|
+
const containerRef = useRef9(null);
|
|
1719
|
+
useEffect8(() => {
|
|
1524
1720
|
if (containerRef.current) {
|
|
1525
1721
|
applyTheme(containerRef.current, theme);
|
|
1526
1722
|
}
|
|
1527
1723
|
}, [theme]);
|
|
1528
|
-
return /* @__PURE__ */
|
|
1724
|
+
return /* @__PURE__ */ jsx20("div", { ref: containerRef, className: `incremark-theme-provider ${className}`.trim(), children });
|
|
1529
1725
|
};
|
|
1530
1726
|
|
|
1531
1727
|
// src/index.ts
|
|
@@ -1557,6 +1753,7 @@ export {
|
|
|
1557
1753
|
DefinitionsProvider,
|
|
1558
1754
|
Incremark,
|
|
1559
1755
|
IncremarkContainerProvider,
|
|
1756
|
+
IncremarkContent,
|
|
1560
1757
|
IncremarkFootnotes,
|
|
1561
1758
|
IncremarkHtmlElement,
|
|
1562
1759
|
IncremarkInline,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@incremark/react",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Incremark React integration - Incremental Markdown parser for AI streaming",
|
|
6
6
|
"type": "module",
|
|
@@ -21,13 +21,13 @@
|
|
|
21
21
|
"mermaid": "^10.0.0 || ^11.0.0",
|
|
22
22
|
"katex": "^0.16.0",
|
|
23
23
|
"react": ">=18.0.0",
|
|
24
|
-
"@incremark/core": "0.2.
|
|
24
|
+
"@incremark/core": "0.2.7"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"shiki": "^3.20.0",
|
|
28
|
-
"@incremark/
|
|
29
|
-
"@incremark/
|
|
30
|
-
"@incremark/
|
|
28
|
+
"@incremark/devtools": "0.2.7",
|
|
29
|
+
"@incremark/theme": "0.2.7",
|
|
30
|
+
"@incremark/shared": "0.2.7"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/mdast": "^4.0.0",
|