@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.
Files changed (59) hide show
  1. package/dist/ThemeProvider.vue.d.ts +25 -0
  2. package/dist/components/AutoScrollContainer.vue.d.ts +31 -0
  3. package/dist/components/Incremark.vue.d.ts +36 -0
  4. package/dist/components/IncremarkBlockquote.vue.d.ts +6 -0
  5. package/dist/components/IncremarkCode.vue.d.ts +23 -0
  6. package/dist/components/IncremarkContainer.vue.d.ts +18 -0
  7. package/dist/components/IncremarkDefault.vue.d.ts +6 -0
  8. package/dist/components/IncremarkFootnotes.vue.d.ts +2 -0
  9. package/dist/components/IncremarkHeading.vue.d.ts +6 -0
  10. package/dist/components/IncremarkHtmlElement.vue.d.ts +20 -0
  11. package/dist/components/IncremarkInline.vue.d.ts +6 -0
  12. package/dist/components/IncremarkList.vue.d.ts +6 -0
  13. package/dist/components/IncremarkMath.vue.d.ts +17 -0
  14. package/dist/components/IncremarkParagraph.vue.d.ts +6 -0
  15. package/dist/components/IncremarkRenderer.vue.d.ts +12 -0
  16. package/dist/components/IncremarkTable.vue.d.ts +6 -0
  17. package/dist/components/IncremarkThematicBreak.vue.d.ts +2 -0
  18. package/{src/components/index.ts → dist/components/index.d.ts} +16 -23
  19. package/dist/composables/index.d.ts +8 -0
  20. package/dist/composables/useBlockTransformer.d.ts +68 -0
  21. package/dist/composables/useDefinationsContext.d.ts +9 -0
  22. package/dist/composables/useDevTools.d.ts +18 -0
  23. package/dist/composables/useIncremark.d.ts +112 -0
  24. package/dist/composables/useProvideDefinations.d.ts +16 -0
  25. package/dist/composables/useStreamRenderer.d.ts +26 -0
  26. package/dist/composables/useTypewriter.d.ts +37 -0
  27. package/dist/index.d.ts +10 -0
  28. package/dist/index.js +428 -222
  29. package/dist/index.js.map +1 -1
  30. package/dist/utils/cursor.d.ts +18 -0
  31. package/package.json +11 -13
  32. package/dist/index.css +0 -6
  33. package/dist/index.css.map +0 -1
  34. package/src/ThemeProvider.vue +0 -41
  35. package/src/components/AutoScrollContainer.vue +0 -164
  36. package/src/components/Incremark.vue +0 -131
  37. package/src/components/IncremarkBlockquote.vue +0 -18
  38. package/src/components/IncremarkCode.vue +0 -236
  39. package/src/components/IncremarkDefault.vue +0 -15
  40. package/src/components/IncremarkFootnotes.vue +0 -78
  41. package/src/components/IncremarkHeading.vue +0 -17
  42. package/src/components/IncremarkHtmlElement.vue +0 -127
  43. package/src/components/IncremarkInline.vue +0 -187
  44. package/src/components/IncremarkList.vue +0 -46
  45. package/src/components/IncremarkMath.vue +0 -105
  46. package/src/components/IncremarkParagraph.vue +0 -14
  47. package/src/components/IncremarkRenderer.vue +0 -50
  48. package/src/components/IncremarkTable.vue +0 -42
  49. package/src/components/IncremarkThematicBreak.vue +0 -8
  50. package/src/composables/index.ts +0 -11
  51. package/src/composables/useBlockTransformer.ts +0 -141
  52. package/src/composables/useDefinationsContext.ts +0 -16
  53. package/src/composables/useDevTools.ts +0 -54
  54. package/src/composables/useIncremark.ts +0 -238
  55. package/src/composables/useProvideDefinations.ts +0 -61
  56. package/src/composables/useStreamRenderer.ts +0 -55
  57. package/src/composables/useTypewriter.ts +0 -205
  58. package/src/index.ts +0 -78
  59. 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
-