@incremark/core 0.0.4 → 0.0.5
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/README.md +82 -1
- package/dist/index.d.ts +291 -2
- package/dist/index.js +450 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/index.ts +41 -0
- package/src/transformer/BlockTransformer.ts +472 -0
- package/src/transformer/index.ts +35 -0
- package/src/transformer/plugins.ts +113 -0
- package/src/transformer/styles.css +33 -0
- package/src/transformer/types.ts +113 -0
- package/src/transformer/utils.ts +85 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { RootContent } from 'mdast'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 源 Block 类型(来自解析器)
|
|
5
|
+
*/
|
|
6
|
+
export interface SourceBlock<T = unknown> {
|
|
7
|
+
/** 唯一标识 */
|
|
8
|
+
id: string
|
|
9
|
+
/** AST 节点 */
|
|
10
|
+
node: RootContent
|
|
11
|
+
/** 块状态 */
|
|
12
|
+
status: 'pending' | 'stable' | 'completed'
|
|
13
|
+
/** 用户自定义元数据 */
|
|
14
|
+
meta?: T
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 显示用的 Block(转换后)
|
|
19
|
+
*/
|
|
20
|
+
export interface DisplayBlock<T = unknown> extends SourceBlock<T> {
|
|
21
|
+
/** 用于显示的 AST 节点(可能是截断的) */
|
|
22
|
+
displayNode: RootContent
|
|
23
|
+
/** 显示进度 0-1 */
|
|
24
|
+
progress: number
|
|
25
|
+
/** 是否已完成显示 */
|
|
26
|
+
isDisplayComplete: boolean
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 动画效果类型
|
|
31
|
+
* - 'none': 无动画效果
|
|
32
|
+
* - 'typing': 打字机光标效果(需配合 CSS)
|
|
33
|
+
*/
|
|
34
|
+
export type AnimationEffect = 'none' | 'typing'
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Transformer 插件
|
|
38
|
+
*/
|
|
39
|
+
export interface TransformerPlugin {
|
|
40
|
+
/** 插件名称 */
|
|
41
|
+
name: string
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 判断是否处理此节点
|
|
45
|
+
* 返回 true 表示这个插件要处理此节点
|
|
46
|
+
*/
|
|
47
|
+
match?: (node: RootContent) => boolean
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 自定义字符数计算
|
|
51
|
+
* 返回 undefined 则使用默认逻辑
|
|
52
|
+
* 返回 0 表示立即显示(不参与逐字符效果)
|
|
53
|
+
*/
|
|
54
|
+
countChars?: (node: RootContent) => number | undefined
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 自定义截断逻辑
|
|
58
|
+
* @param node 原始节点
|
|
59
|
+
* @param displayedChars 当前应显示的字符数
|
|
60
|
+
* @param totalChars 该节点的总字符数
|
|
61
|
+
* @returns 截断后的节点,null 表示不显示
|
|
62
|
+
*/
|
|
63
|
+
sliceNode?: (
|
|
64
|
+
node: RootContent,
|
|
65
|
+
displayedChars: number,
|
|
66
|
+
totalChars: number
|
|
67
|
+
) => RootContent | null
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 节点显示完成时的回调
|
|
71
|
+
*/
|
|
72
|
+
onComplete?: (node: RootContent) => void
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Transformer 配置选项
|
|
77
|
+
*/
|
|
78
|
+
export interface TransformerOptions {
|
|
79
|
+
/**
|
|
80
|
+
* 每 tick 增加的字符数
|
|
81
|
+
* - number: 固定步长(默认 1)
|
|
82
|
+
* - [min, max]: 随机步长区间(更自然的打字效果)
|
|
83
|
+
*/
|
|
84
|
+
charsPerTick?: number | [number, number]
|
|
85
|
+
/** tick 间隔 (ms),默认 20 */
|
|
86
|
+
tickInterval?: number
|
|
87
|
+
/** 动画效果,默认 'none' */
|
|
88
|
+
effect?: AnimationEffect
|
|
89
|
+
/** 插件列表 */
|
|
90
|
+
plugins?: TransformerPlugin[]
|
|
91
|
+
/** 状态变化回调 */
|
|
92
|
+
onChange?: (displayBlocks: DisplayBlock[]) => void
|
|
93
|
+
/**
|
|
94
|
+
* 是否在页面不可见时自动暂停
|
|
95
|
+
* 默认 true,节省资源
|
|
96
|
+
*/
|
|
97
|
+
pauseOnHidden?: boolean
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Transformer 内部状态
|
|
102
|
+
*/
|
|
103
|
+
export interface TransformerState<T = unknown> {
|
|
104
|
+
/** 已完成显示的 blocks */
|
|
105
|
+
completedBlocks: SourceBlock<T>[]
|
|
106
|
+
/** 当前正在显示的 block */
|
|
107
|
+
currentBlock: SourceBlock<T> | null
|
|
108
|
+
/** 当前 block 已显示的字符数 */
|
|
109
|
+
currentProgress: number
|
|
110
|
+
/** 等待显示的 blocks */
|
|
111
|
+
pendingBlocks: SourceBlock<T>[]
|
|
112
|
+
}
|
|
113
|
+
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { RootContent } from 'mdast'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 计算 AST 节点的总字符数
|
|
5
|
+
*/
|
|
6
|
+
export function countChars(node: RootContent): number {
|
|
7
|
+
let count = 0
|
|
8
|
+
|
|
9
|
+
function traverse(n: any): void {
|
|
10
|
+
// 文本类节点
|
|
11
|
+
if (n.value && typeof n.value === 'string') {
|
|
12
|
+
count += n.value.length
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 容器节点,递归处理子节点
|
|
17
|
+
if (n.children && Array.isArray(n.children)) {
|
|
18
|
+
for (const child of n.children) {
|
|
19
|
+
traverse(child)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
traverse(node)
|
|
25
|
+
return count
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 截断 AST 节点,只保留前 maxChars 个字符
|
|
30
|
+
*
|
|
31
|
+
* @param node 原始节点
|
|
32
|
+
* @param maxChars 最大字符数
|
|
33
|
+
* @returns 截断后的节点,如果 maxChars <= 0 返回 null
|
|
34
|
+
*/
|
|
35
|
+
export function sliceAst(node: RootContent, maxChars: number): RootContent | null {
|
|
36
|
+
if (maxChars <= 0) return null
|
|
37
|
+
|
|
38
|
+
let remaining = maxChars
|
|
39
|
+
|
|
40
|
+
function process(n: any): any {
|
|
41
|
+
if (remaining <= 0) return null
|
|
42
|
+
|
|
43
|
+
// 文本类节点:截断 value
|
|
44
|
+
if (n.value && typeof n.value === 'string') {
|
|
45
|
+
const take = Math.min(n.value.length, remaining)
|
|
46
|
+
remaining -= take
|
|
47
|
+
if (take === 0) return null
|
|
48
|
+
return { ...n, value: n.value.slice(0, take) }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 容器节点:递归处理 children
|
|
52
|
+
if (n.children && Array.isArray(n.children)) {
|
|
53
|
+
const newChildren: any[] = []
|
|
54
|
+
for (const child of n.children) {
|
|
55
|
+
if (remaining <= 0) break
|
|
56
|
+
const processed = process(child)
|
|
57
|
+
if (processed) {
|
|
58
|
+
newChildren.push(processed)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// 如果没有 children 了,根据节点类型决定是否保留
|
|
62
|
+
if (newChildren.length === 0) {
|
|
63
|
+
// 对于某些容器节点,即使没有内容也应该保留结构
|
|
64
|
+
// 例如 list 节点如果没有 children 就不应该渲染
|
|
65
|
+
return null
|
|
66
|
+
}
|
|
67
|
+
return { ...n, children: newChildren }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 其他节点(如 thematicBreak, image):整体处理
|
|
71
|
+
// 算作 1 个字符的消耗
|
|
72
|
+
remaining -= 1
|
|
73
|
+
return { ...n }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return process(node)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 深拷贝 AST 节点
|
|
81
|
+
*/
|
|
82
|
+
export function cloneNode<T extends RootContent>(node: T): T {
|
|
83
|
+
return JSON.parse(JSON.stringify(node))
|
|
84
|
+
}
|
|
85
|
+
|