@incremark/vue 0.2.2 → 0.2.4
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/ThemeProvider.vue.d.ts +25 -0
- package/dist/components/AutoScrollContainer.vue.d.ts +31 -0
- package/dist/components/Incremark.vue.d.ts +36 -0
- package/dist/components/IncremarkBlockquote.vue.d.ts +6 -0
- package/dist/components/IncremarkCode.vue.d.ts +23 -0
- package/dist/components/IncremarkContainer.vue.d.ts +18 -0
- package/dist/components/IncremarkDefault.vue.d.ts +6 -0
- package/dist/components/IncremarkFootnotes.vue.d.ts +2 -0
- package/dist/components/IncremarkHeading.vue.d.ts +6 -0
- package/dist/components/IncremarkHtmlElement.vue.d.ts +20 -0
- package/dist/components/IncremarkInline.vue.d.ts +6 -0
- package/dist/components/IncremarkList.vue.d.ts +6 -0
- package/dist/components/IncremarkMath.vue.d.ts +17 -0
- package/dist/components/IncremarkParagraph.vue.d.ts +6 -0
- package/dist/components/IncremarkRenderer.vue.d.ts +12 -0
- package/dist/components/IncremarkTable.vue.d.ts +6 -0
- package/dist/components/IncremarkThematicBreak.vue.d.ts +2 -0
- package/{src/components/index.ts → dist/components/index.d.ts} +16 -23
- package/dist/composables/index.d.ts +8 -0
- package/dist/composables/useBlockTransformer.d.ts +68 -0
- package/dist/composables/useDefinationsContext.d.ts +9 -0
- package/dist/composables/useDevTools.d.ts +18 -0
- package/dist/composables/useIncremark.d.ts +112 -0
- package/dist/composables/useProvideDefinations.d.ts +16 -0
- package/dist/composables/useStreamRenderer.d.ts +26 -0
- package/dist/composables/useTypewriter.d.ts +37 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +428 -222
- package/dist/index.js.map +1 -1
- package/dist/utils/cursor.d.ts +18 -0
- package/package.json +11 -13
- package/dist/index.css +0 -6
- package/dist/index.css.map +0 -1
- package/src/ThemeProvider.vue +0 -41
- package/src/components/AutoScrollContainer.vue +0 -164
- package/src/components/Incremark.vue +0 -131
- package/src/components/IncremarkBlockquote.vue +0 -18
- package/src/components/IncremarkCode.vue +0 -236
- package/src/components/IncremarkDefault.vue +0 -15
- package/src/components/IncremarkFootnotes.vue +0 -78
- package/src/components/IncremarkHeading.vue +0 -17
- package/src/components/IncremarkHtmlElement.vue +0 -127
- package/src/components/IncremarkInline.vue +0 -187
- package/src/components/IncremarkList.vue +0 -46
- package/src/components/IncremarkMath.vue +0 -105
- package/src/components/IncremarkParagraph.vue +0 -14
- package/src/components/IncremarkRenderer.vue +0 -50
- package/src/components/IncremarkTable.vue +0 -42
- package/src/components/IncremarkThematicBreak.vue +0 -8
- package/src/composables/index.ts +0 -11
- package/src/composables/useBlockTransformer.ts +0 -141
- package/src/composables/useDefinationsContext.ts +0 -16
- package/src/composables/useDevTools.ts +0 -54
- package/src/composables/useIncremark.ts +0 -238
- package/src/composables/useProvideDefinations.ts +0 -61
- package/src/composables/useStreamRenderer.ts +0 -55
- package/src/composables/useTypewriter.ts +0 -205
- package/src/index.ts +0 -78
- package/src/utils/cursor.ts +0 -46
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { ref, watch, computed, onUnmounted, type Ref, type ComputedRef } from 'vue'
|
|
2
|
-
import {
|
|
3
|
-
BlockTransformer,
|
|
4
|
-
createBlockTransformer,
|
|
5
|
-
type TransformerOptions,
|
|
6
|
-
type DisplayBlock,
|
|
7
|
-
type SourceBlock,
|
|
8
|
-
type AnimationEffect
|
|
9
|
-
} from '@incremark/core'
|
|
10
|
-
|
|
11
|
-
export interface UseBlockTransformerOptions extends Omit<TransformerOptions, 'onChange'> {}
|
|
12
|
-
|
|
13
|
-
export interface UseBlockTransformerReturn<T = unknown> {
|
|
14
|
-
/** 用于渲染的 display blocks */
|
|
15
|
-
displayBlocks: ComputedRef<DisplayBlock<T>[]>
|
|
16
|
-
/** 是否正在处理中 */
|
|
17
|
-
isProcessing: ComputedRef<boolean>
|
|
18
|
-
/** 是否已暂停 */
|
|
19
|
-
isPaused: ComputedRef<boolean>
|
|
20
|
-
/** 当前动画效果 */
|
|
21
|
-
effect: ComputedRef<AnimationEffect>
|
|
22
|
-
/** 跳过所有动画 */
|
|
23
|
-
skip: () => void
|
|
24
|
-
/** 重置状态 */
|
|
25
|
-
reset: () => void
|
|
26
|
-
/** 暂停动画 */
|
|
27
|
-
pause: () => void
|
|
28
|
-
/** 恢复动画 */
|
|
29
|
-
resume: () => void
|
|
30
|
-
/** 动态更新配置 */
|
|
31
|
-
setOptions: (options: Partial<Pick<TransformerOptions, 'charsPerTick' | 'tickInterval' | 'effect' | 'pauseOnHidden'>>) => void
|
|
32
|
-
/** transformer 实例(用于高级用法) */
|
|
33
|
-
transformer: BlockTransformer<T>
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Vue 3 Composable: Block Transformer
|
|
38
|
-
*
|
|
39
|
-
* 用于控制 blocks 的逐步显示(打字机效果)
|
|
40
|
-
* 作为解析器和渲染器之间的中间层
|
|
41
|
-
*
|
|
42
|
-
* 特性:
|
|
43
|
-
* - 使用 requestAnimationFrame 实现流畅动画
|
|
44
|
-
* - 支持随机步长 `charsPerTick: [1, 3]`
|
|
45
|
-
* - 支持动画效果 `effect: 'typing'`
|
|
46
|
-
* - 页面不可见时自动暂停
|
|
47
|
-
*
|
|
48
|
-
* @example
|
|
49
|
-
* ```vue
|
|
50
|
-
* <script setup>
|
|
51
|
-
* import { useIncremark, useBlockTransformer, defaultPlugins } from '@incremark/vue'
|
|
52
|
-
*
|
|
53
|
-
* const { blocks, completedBlocks, append, finalize } = useIncremark()
|
|
54
|
-
*
|
|
55
|
-
* // 使用 completedBlocks 作为输入(ID 稳定)
|
|
56
|
-
* const sourceBlocks = computed(() => completedBlocks.value.map(b => ({
|
|
57
|
-
* id: b.id,
|
|
58
|
-
* node: b.node,
|
|
59
|
-
* status: b.status
|
|
60
|
-
* })))
|
|
61
|
-
*
|
|
62
|
-
* // 添加打字机效果
|
|
63
|
-
* const { displayBlocks, isProcessing, skip, effect } = useBlockTransformer(sourceBlocks, {
|
|
64
|
-
* charsPerTick: [1, 3], // 随机步长
|
|
65
|
-
* tickInterval: 30,
|
|
66
|
-
* effect: 'typing', // 光标效果
|
|
67
|
-
* plugins: defaultPlugins
|
|
68
|
-
* })
|
|
69
|
-
* </script>
|
|
70
|
-
*
|
|
71
|
-
* <template>
|
|
72
|
-
* <Incremark :blocks="displayBlocks" :class="{ 'typing': effect === 'typing' }" />
|
|
73
|
-
* <button v-if="isProcessing" @click="skip">跳过</button>
|
|
74
|
-
* </template>
|
|
75
|
-
* ```
|
|
76
|
-
*/
|
|
77
|
-
export function useBlockTransformer<T = unknown>(
|
|
78
|
-
sourceBlocks: Ref<SourceBlock<T>[]> | ComputedRef<SourceBlock<T>[]>,
|
|
79
|
-
options: UseBlockTransformerOptions = {}
|
|
80
|
-
): UseBlockTransformerReturn<T> {
|
|
81
|
-
const displayBlocksRef = ref<DisplayBlock<T>[]>([])
|
|
82
|
-
const isProcessingRef = ref(false)
|
|
83
|
-
const isPausedRef = ref(false)
|
|
84
|
-
const effectRef = ref<AnimationEffect>(options.effect ?? 'none')
|
|
85
|
-
|
|
86
|
-
const transformer = createBlockTransformer<T>({
|
|
87
|
-
...options,
|
|
88
|
-
onChange: (blocks) => {
|
|
89
|
-
displayBlocksRef.value = blocks as DisplayBlock<T>[]
|
|
90
|
-
isProcessingRef.value = transformer.isProcessing()
|
|
91
|
-
isPausedRef.value = transformer.isPausedState()
|
|
92
|
-
}
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
// 监听源 blocks 变化
|
|
96
|
-
watch(
|
|
97
|
-
sourceBlocks,
|
|
98
|
-
(blocks) => {
|
|
99
|
-
// 推入新 blocks
|
|
100
|
-
transformer.push(blocks)
|
|
101
|
-
|
|
102
|
-
// 处理正在显示的 block 内容更新
|
|
103
|
-
const currentDisplaying = displayBlocksRef.value.find((b) => !b.isDisplayComplete)
|
|
104
|
-
if (currentDisplaying) {
|
|
105
|
-
const updated = blocks.find((b) => b.id === currentDisplaying.id)
|
|
106
|
-
if (updated) {
|
|
107
|
-
transformer.update(updated)
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
},
|
|
111
|
-
{ immediate: true, deep: true }
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
onUnmounted(() => {
|
|
115
|
-
transformer.destroy()
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
displayBlocks: computed(() => displayBlocksRef.value) as ComputedRef<DisplayBlock<T>[]>,
|
|
120
|
-
isProcessing: computed(() => isProcessingRef.value),
|
|
121
|
-
isPaused: computed(() => isPausedRef.value),
|
|
122
|
-
effect: computed(() => effectRef.value),
|
|
123
|
-
skip: () => transformer.skip(),
|
|
124
|
-
reset: () => transformer.reset(),
|
|
125
|
-
pause: () => {
|
|
126
|
-
transformer.pause()
|
|
127
|
-
isPausedRef.value = true
|
|
128
|
-
},
|
|
129
|
-
resume: () => {
|
|
130
|
-
transformer.resume()
|
|
131
|
-
isPausedRef.value = false
|
|
132
|
-
},
|
|
133
|
-
setOptions: (opts) => {
|
|
134
|
-
transformer.setOptions(opts)
|
|
135
|
-
if (opts.effect !== undefined) {
|
|
136
|
-
effectRef.value = opts.effect
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
transformer
|
|
140
|
-
}
|
|
141
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { inject } from 'vue'
|
|
2
|
-
import { definationsInjectionKey } from './useProvideDefinations'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* support definations and footnoteDefinitions
|
|
6
|
-
* @returns
|
|
7
|
-
*/
|
|
8
|
-
export function useDefinationsContext() {
|
|
9
|
-
const definationContext = inject(definationsInjectionKey);
|
|
10
|
-
|
|
11
|
-
if (!definationContext) {
|
|
12
|
-
throw new Error('definationContext not found');
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return definationContext;
|
|
16
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { onMounted, onUnmounted } from 'vue'
|
|
2
|
-
import { createDevTools, type DevToolsOptions } from '@incremark/devtools'
|
|
3
|
-
import type { UseIncremarkReturn } from './useIncremark'
|
|
4
|
-
|
|
5
|
-
export interface UseDevToolsOptions extends DevToolsOptions {}
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Vue 3 DevTools 一行接入
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```vue
|
|
12
|
-
* <script setup>
|
|
13
|
-
* import { useIncremark, useDevTools } from '@incremark/vue'
|
|
14
|
-
*
|
|
15
|
-
* const incremark = useIncremark()
|
|
16
|
-
* useDevTools(incremark) // 就这一行!
|
|
17
|
-
* </script>
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
export function useDevTools(
|
|
21
|
-
incremark: UseIncremarkReturn,
|
|
22
|
-
options: UseDevToolsOptions = {}
|
|
23
|
-
) {
|
|
24
|
-
const devtools = createDevTools(options)
|
|
25
|
-
|
|
26
|
-
// 设置 parser 的 onChange 回调
|
|
27
|
-
incremark.parser.setOnChange((state) => {
|
|
28
|
-
const blocks = [
|
|
29
|
-
...state.completedBlocks.map((b) => ({ ...b, stableId: b.id })),
|
|
30
|
-
...state.pendingBlocks.map((b, i) => ({ ...b, stableId: `pending-${i}` }))
|
|
31
|
-
]
|
|
32
|
-
|
|
33
|
-
devtools.update({
|
|
34
|
-
blocks,
|
|
35
|
-
completedBlocks: state.completedBlocks,
|
|
36
|
-
pendingBlocks: state.pendingBlocks,
|
|
37
|
-
markdown: state.markdown,
|
|
38
|
-
ast: state.ast,
|
|
39
|
-
isLoading: state.pendingBlocks.length > 0
|
|
40
|
-
})
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
onMounted(() => {
|
|
44
|
-
devtools.mount()
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
onUnmounted(() => {
|
|
48
|
-
devtools.unmount()
|
|
49
|
-
// 清理回调
|
|
50
|
-
incremark.parser.setOnChange(undefined)
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
return devtools
|
|
54
|
-
}
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
import { ref, shallowRef, computed, markRaw, type ComputedRef } from 'vue'
|
|
2
|
-
import {
|
|
3
|
-
createIncremarkParser,
|
|
4
|
-
type ParserOptions,
|
|
5
|
-
type ParsedBlock,
|
|
6
|
-
type IncrementalUpdate,
|
|
7
|
-
type Root,
|
|
8
|
-
type TransformerPlugin,
|
|
9
|
-
type AnimationEffect
|
|
10
|
-
} from '@incremark/core'
|
|
11
|
-
import { useProvideDefinations } from './useProvideDefinations'
|
|
12
|
-
import { useTypewriter } from './useTypewriter'
|
|
13
|
-
|
|
14
|
-
/** 打字机效果配置 */
|
|
15
|
-
export interface TypewriterOptions {
|
|
16
|
-
/** 是否启用打字机效果(可响应式切换) */
|
|
17
|
-
enabled?: boolean
|
|
18
|
-
/** 每次显示的字符数,可以是固定值或范围 [min, max] */
|
|
19
|
-
charsPerTick?: number | [number, number]
|
|
20
|
-
/** 更新间隔 (ms) */
|
|
21
|
-
tickInterval?: number
|
|
22
|
-
/** 动画效果: 'none' | 'fade-in' | 'typing' */
|
|
23
|
-
effect?: AnimationEffect
|
|
24
|
-
/** 光标字符(仅 typing 效果使用) */
|
|
25
|
-
cursor?: string
|
|
26
|
-
/** 页面不可见时暂停 */
|
|
27
|
-
pauseOnHidden?: boolean
|
|
28
|
-
/** 自定义插件 */
|
|
29
|
-
plugins?: TransformerPlugin[]
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface UseIncremarkOptions extends ParserOptions {
|
|
33
|
-
/** 打字机配置,传入即创建 transformer(可通过 enabled 控制是否启用) */
|
|
34
|
-
typewriter?: TypewriterOptions
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** 打字机控制对象 */
|
|
38
|
-
export interface TypewriterControls {
|
|
39
|
-
/** 是否启用(只读) */
|
|
40
|
-
enabled: ComputedRef<boolean>
|
|
41
|
-
/** 设置是否启用 */
|
|
42
|
-
setEnabled: (enabled: boolean) => void
|
|
43
|
-
/** 是否正在处理中 */
|
|
44
|
-
isProcessing: ComputedRef<boolean>
|
|
45
|
-
/** 是否已暂停 */
|
|
46
|
-
isPaused: ComputedRef<boolean>
|
|
47
|
-
/** 当前动画效果 */
|
|
48
|
-
effect: ComputedRef<AnimationEffect>
|
|
49
|
-
/** 跳过动画,直接显示全部 */
|
|
50
|
-
skip: () => void
|
|
51
|
-
/** 暂停动画 */
|
|
52
|
-
pause: () => void
|
|
53
|
-
/** 恢复动画 */
|
|
54
|
-
resume: () => void
|
|
55
|
-
/** 动态更新配置 */
|
|
56
|
-
setOptions: (options: Partial<TypewriterOptions>) => void
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** useIncremark 的返回类型 */
|
|
60
|
-
export type UseIncremarkReturn = ReturnType<typeof useIncremark>
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Vue 3 Composable: Incremark 流式 Markdown 解析器
|
|
64
|
-
*
|
|
65
|
-
* @example
|
|
66
|
-
* ```vue
|
|
67
|
-
* <script setup>
|
|
68
|
-
* import { useIncremark, Incremark } from '@incremark/vue'
|
|
69
|
-
*
|
|
70
|
-
* // 基础用法
|
|
71
|
-
* const { blocks, append, finalize } = useIncremark()
|
|
72
|
-
*
|
|
73
|
-
* // 启用打字机效果
|
|
74
|
-
* const { blocks, append, finalize, typewriter } = useIncremark({
|
|
75
|
-
* typewriter: {
|
|
76
|
-
* enabled: true, // 可响应式切换
|
|
77
|
-
* charsPerTick: [1, 3],
|
|
78
|
-
* tickInterval: 30,
|
|
79
|
-
* effect: 'typing',
|
|
80
|
-
* cursor: '|'
|
|
81
|
-
* }
|
|
82
|
-
* })
|
|
83
|
-
*
|
|
84
|
-
* // 动态切换打字机效果
|
|
85
|
-
* typewriter.enabled.value = false
|
|
86
|
-
* </script>
|
|
87
|
-
*
|
|
88
|
-
* <template>
|
|
89
|
-
* <Incremark :blocks="blocks" />
|
|
90
|
-
* <button v-if="typewriter.isProcessing.value" @click="typewriter.skip">跳过</button>
|
|
91
|
-
* </template>
|
|
92
|
-
* ```
|
|
93
|
-
*/
|
|
94
|
-
export function useIncremark(options: UseIncremarkOptions = {}) {
|
|
95
|
-
// 内部自动提供 definitions context
|
|
96
|
-
const { setDefinations, setFootnoteDefinitions, setFootnoteReferenceOrder } = useProvideDefinations()
|
|
97
|
-
|
|
98
|
-
// 解析器
|
|
99
|
-
const parser = createIncremarkParser({
|
|
100
|
-
...options,
|
|
101
|
-
onChange: (state) => {
|
|
102
|
-
setDefinations(state.definitions)
|
|
103
|
-
setFootnoteDefinitions(state.footnoteDefinitions)
|
|
104
|
-
// 调用用户提供的 onChange
|
|
105
|
-
options.onChange?.(state)
|
|
106
|
-
}
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
const completedBlocks = shallowRef<ParsedBlock[]>([])
|
|
110
|
-
const pendingBlocks = shallowRef<ParsedBlock[]>([])
|
|
111
|
-
const isLoading = ref(false)
|
|
112
|
-
const markdown = ref('')
|
|
113
|
-
const isFinalized = ref(false)
|
|
114
|
-
const footnoteReferenceOrder = ref<string[]>([])
|
|
115
|
-
|
|
116
|
-
// 使用 useTypewriter composable 管理打字机效果
|
|
117
|
-
const { blocks, typewriter, transformer } = useTypewriter({
|
|
118
|
-
typewriter: options.typewriter,
|
|
119
|
-
completedBlocks,
|
|
120
|
-
pendingBlocks
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
// AST
|
|
124
|
-
const ast = computed<Root>(() => ({
|
|
125
|
-
type: 'root',
|
|
126
|
-
children: [
|
|
127
|
-
...completedBlocks.value.map((b) => b.node),
|
|
128
|
-
...pendingBlocks.value.map((b) => b.node)
|
|
129
|
-
]
|
|
130
|
-
}))
|
|
131
|
-
|
|
132
|
-
function append(chunk: string): IncrementalUpdate {
|
|
133
|
-
isLoading.value = true
|
|
134
|
-
const update = parser.append(chunk)
|
|
135
|
-
|
|
136
|
-
markdown.value = parser.getBuffer()
|
|
137
|
-
|
|
138
|
-
if (update.completed.length > 0) {
|
|
139
|
-
completedBlocks.value = [
|
|
140
|
-
...completedBlocks.value,
|
|
141
|
-
...update.completed.map((b) => markRaw(b))
|
|
142
|
-
]
|
|
143
|
-
}
|
|
144
|
-
pendingBlocks.value = update.pending.map((b) => markRaw(b))
|
|
145
|
-
|
|
146
|
-
// 更新脚注引用顺序
|
|
147
|
-
footnoteReferenceOrder.value = update.footnoteReferenceOrder
|
|
148
|
-
setFootnoteReferenceOrder(update.footnoteReferenceOrder)
|
|
149
|
-
|
|
150
|
-
return update
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function finalize(): IncrementalUpdate {
|
|
154
|
-
const update = parser.finalize()
|
|
155
|
-
|
|
156
|
-
markdown.value = parser.getBuffer()
|
|
157
|
-
|
|
158
|
-
if (update.completed.length > 0) {
|
|
159
|
-
completedBlocks.value = [
|
|
160
|
-
...completedBlocks.value,
|
|
161
|
-
...update.completed.map((b) => markRaw(b))
|
|
162
|
-
]
|
|
163
|
-
}
|
|
164
|
-
pendingBlocks.value = []
|
|
165
|
-
isLoading.value = false
|
|
166
|
-
isFinalized.value = true
|
|
167
|
-
|
|
168
|
-
// 更新脚注引用顺序
|
|
169
|
-
footnoteReferenceOrder.value = update.footnoteReferenceOrder
|
|
170
|
-
setFootnoteReferenceOrder(update.footnoteReferenceOrder)
|
|
171
|
-
|
|
172
|
-
return update
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function abort(): IncrementalUpdate {
|
|
176
|
-
return finalize()
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function reset(): void {
|
|
180
|
-
parser.reset()
|
|
181
|
-
completedBlocks.value = []
|
|
182
|
-
pendingBlocks.value = []
|
|
183
|
-
markdown.value = ''
|
|
184
|
-
isLoading.value = false
|
|
185
|
-
isFinalized.value = false
|
|
186
|
-
footnoteReferenceOrder.value = []
|
|
187
|
-
|
|
188
|
-
// 重置 transformer
|
|
189
|
-
transformer?.reset()
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function render(content: string): IncrementalUpdate {
|
|
193
|
-
const update = parser.render(content)
|
|
194
|
-
|
|
195
|
-
markdown.value = parser.getBuffer()
|
|
196
|
-
completedBlocks.value = parser.getCompletedBlocks().map(b => markRaw(b))
|
|
197
|
-
pendingBlocks.value = []
|
|
198
|
-
isLoading.value = false
|
|
199
|
-
isFinalized.value = true
|
|
200
|
-
footnoteReferenceOrder.value = update.footnoteReferenceOrder
|
|
201
|
-
setFootnoteReferenceOrder(update.footnoteReferenceOrder)
|
|
202
|
-
|
|
203
|
-
return update
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
return {
|
|
207
|
-
/** 已收集的完整 Markdown 字符串 */
|
|
208
|
-
markdown,
|
|
209
|
-
/** 已完成的块列表 */
|
|
210
|
-
completedBlocks,
|
|
211
|
-
/** 待处理的块列表 */
|
|
212
|
-
pendingBlocks,
|
|
213
|
-
/** 当前完整的 AST */
|
|
214
|
-
ast,
|
|
215
|
-
/** 用于渲染的 blocks(根据打字机设置自动处理) */
|
|
216
|
-
blocks,
|
|
217
|
-
/** 是否正在加载 */
|
|
218
|
-
isLoading,
|
|
219
|
-
/** 是否已完成(finalize) */
|
|
220
|
-
isFinalized,
|
|
221
|
-
/** 脚注引用的出现顺序 */
|
|
222
|
-
footnoteReferenceOrder,
|
|
223
|
-
/** 追加内容 */
|
|
224
|
-
append,
|
|
225
|
-
/** 完成解析 */
|
|
226
|
-
finalize,
|
|
227
|
-
/** 强制中断 */
|
|
228
|
-
abort,
|
|
229
|
-
/** 重置解析器和打字机 */
|
|
230
|
-
reset,
|
|
231
|
-
/** 一次性渲染(reset + append + finalize) */
|
|
232
|
-
render,
|
|
233
|
-
/** 解析器实例 */
|
|
234
|
-
parser,
|
|
235
|
-
/** 打字机控制 */
|
|
236
|
-
typewriter
|
|
237
|
-
}
|
|
238
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { ref, provide, type InjectionKey, type Ref } from 'vue';
|
|
2
|
-
import type { Definition, FootnoteDefinition } from 'mdast';
|
|
3
|
-
|
|
4
|
-
export const definationsInjectionKey: InjectionKey<{
|
|
5
|
-
definations: Ref<Record<string, Definition>>
|
|
6
|
-
footnoteDefinitions: Ref<Record<string, FootnoteDefinition>>
|
|
7
|
-
footnoteReferenceOrder: Ref<string[]>
|
|
8
|
-
}> = Symbol('provideDefinations');
|
|
9
|
-
|
|
10
|
-
export function useProvideDefinations() {
|
|
11
|
-
const definations = ref<Record<string, Definition>>({});
|
|
12
|
-
const footnoteDefinitions = ref<Record<string, FootnoteDefinition>>({});
|
|
13
|
-
const footnoteReferenceOrder = ref<string[]>([]);
|
|
14
|
-
|
|
15
|
-
provide(definationsInjectionKey, {
|
|
16
|
-
definations,
|
|
17
|
-
footnoteDefinitions,
|
|
18
|
-
footnoteReferenceOrder
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
function setDefinations(definitions: Record<string, Definition>) {
|
|
22
|
-
definations.value = definitions;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function setFootnoteDefinitions(definitions: Record<string, FootnoteDefinition>) {
|
|
26
|
-
footnoteDefinitions.value = definitions;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function setFootnoteReferenceOrder(order: string[]) {
|
|
30
|
-
footnoteReferenceOrder.value = order;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function clearDefinations() {
|
|
34
|
-
definations.value = {};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function clearFootnoteDefinitions() {
|
|
38
|
-
footnoteDefinitions.value = {};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function clearFootnoteReferenceOrder() {
|
|
42
|
-
footnoteReferenceOrder.value = [];
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function clearAllDefinations() {
|
|
46
|
-
clearDefinations();
|
|
47
|
-
clearFootnoteDefinitions();
|
|
48
|
-
clearFootnoteReferenceOrder();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return {
|
|
52
|
-
setDefinations,
|
|
53
|
-
setFootnoteDefinitions,
|
|
54
|
-
setFootnoteReferenceOrder,
|
|
55
|
-
clearDefinations,
|
|
56
|
-
clearFootnoteDefinitions,
|
|
57
|
-
clearFootnoteReferenceOrder,
|
|
58
|
-
clearAllDefinations
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { ref, computed, type Ref, type ComputedRef } from 'vue'
|
|
2
|
-
import type { ParsedBlock } from '@incremark/core'
|
|
3
|
-
|
|
4
|
-
export interface BlockWithStableId extends ParsedBlock {
|
|
5
|
-
/** 稳定的渲染 ID(用于 Vue key) */
|
|
6
|
-
stableId: string
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface UseStreamRendererOptions {
|
|
10
|
-
/** 已完成的块 */
|
|
11
|
-
completedBlocks: Ref<ParsedBlock[]>
|
|
12
|
-
/** 待处理的块 */
|
|
13
|
-
pendingBlocks: Ref<ParsedBlock[]>
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface UseStreamRendererReturn {
|
|
17
|
-
/** 带稳定 ID 的已完成块 */
|
|
18
|
-
stableCompletedBlocks: ComputedRef<BlockWithStableId[]>
|
|
19
|
-
/** 带稳定 ID 的待处理块 */
|
|
20
|
-
stablePendingBlocks: ComputedRef<BlockWithStableId[]>
|
|
21
|
-
/** 所有带稳定 ID 的块 */
|
|
22
|
-
allStableBlocks: ComputedRef<BlockWithStableId[]>
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Vue 3 Composable: 流式渲染辅助
|
|
27
|
-
*
|
|
28
|
-
* 为块分配稳定的渲染 ID,确保 Vue 的虚拟 DOM 复用
|
|
29
|
-
*/
|
|
30
|
-
export function useStreamRenderer(options: UseStreamRendererOptions): UseStreamRendererReturn {
|
|
31
|
-
const { completedBlocks, pendingBlocks } = options
|
|
32
|
-
|
|
33
|
-
const stableCompletedBlocks = computed<BlockWithStableId[]>(() =>
|
|
34
|
-
completedBlocks.value.map((block) => ({
|
|
35
|
-
...block,
|
|
36
|
-
stableId: block.id
|
|
37
|
-
}))
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
const stablePendingBlocks = computed<BlockWithStableId[]>(() =>
|
|
41
|
-
pendingBlocks.value.map((block, index) => ({
|
|
42
|
-
...block,
|
|
43
|
-
stableId: `pending-${index}`
|
|
44
|
-
}))
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
const allStableBlocks = computed(() => [...stableCompletedBlocks.value, ...stablePendingBlocks.value])
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
stableCompletedBlocks,
|
|
51
|
-
stablePendingBlocks,
|
|
52
|
-
allStableBlocks
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|