@incremark/svelte 0.2.3

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 (79) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +91 -0
  3. package/dist/ThemeProvider.svelte +52 -0
  4. package/dist/ThemeProvider.svelte.d.ts +21 -0
  5. package/dist/ThemeProvider.svelte.d.ts.map +1 -0
  6. package/dist/components/AutoScrollContainer.svelte +163 -0
  7. package/dist/components/AutoScrollContainer.svelte.d.ts +17 -0
  8. package/dist/components/AutoScrollContainer.svelte.d.ts.map +1 -0
  9. package/dist/components/Incremark.svelte +183 -0
  10. package/dist/components/Incremark.svelte.d.ts +24 -0
  11. package/dist/components/Incremark.svelte.d.ts.map +1 -0
  12. package/dist/components/IncremarkBlockquote.svelte +30 -0
  13. package/dist/components/IncremarkBlockquote.svelte.d.ts +12 -0
  14. package/dist/components/IncremarkBlockquote.svelte.d.ts.map +1 -0
  15. package/dist/components/IncremarkCode.svelte +275 -0
  16. package/dist/components/IncremarkCode.svelte.d.ts +18 -0
  17. package/dist/components/IncremarkCode.svelte.d.ts.map +1 -0
  18. package/dist/components/IncremarkDefault.svelte +24 -0
  19. package/dist/components/IncremarkDefault.svelte.d.ts +12 -0
  20. package/dist/components/IncremarkDefault.svelte.d.ts.map +1 -0
  21. package/dist/components/IncremarkFootnotes.svelte +84 -0
  22. package/dist/components/IncremarkFootnotes.svelte.d.ts +4 -0
  23. package/dist/components/IncremarkFootnotes.svelte.d.ts.map +1 -0
  24. package/dist/components/IncremarkHeading.svelte +35 -0
  25. package/dist/components/IncremarkHeading.svelte.d.ts +12 -0
  26. package/dist/components/IncremarkHeading.svelte.d.ts.map +1 -0
  27. package/dist/components/IncremarkHtmlElement.svelte +138 -0
  28. package/dist/components/IncremarkHtmlElement.svelte.d.ts +27 -0
  29. package/dist/components/IncremarkHtmlElement.svelte.d.ts.map +1 -0
  30. package/dist/components/IncremarkInline.svelte +233 -0
  31. package/dist/components/IncremarkInline.svelte.d.ts +13 -0
  32. package/dist/components/IncremarkInline.svelte.d.ts.map +1 -0
  33. package/dist/components/IncremarkList.svelte +85 -0
  34. package/dist/components/IncremarkList.svelte.d.ts +12 -0
  35. package/dist/components/IncremarkList.svelte.d.ts.map +1 -0
  36. package/dist/components/IncremarkMath.svelte +137 -0
  37. package/dist/components/IncremarkMath.svelte.d.ts +24 -0
  38. package/dist/components/IncremarkMath.svelte.d.ts.map +1 -0
  39. package/dist/components/IncremarkParagraph.svelte +24 -0
  40. package/dist/components/IncremarkParagraph.svelte.d.ts +12 -0
  41. package/dist/components/IncremarkParagraph.svelte.d.ts.map +1 -0
  42. package/dist/components/IncremarkRenderer.svelte +70 -0
  43. package/dist/components/IncremarkRenderer.svelte.d.ts +12 -0
  44. package/dist/components/IncremarkRenderer.svelte.d.ts.map +1 -0
  45. package/dist/components/IncremarkTable.svelte +79 -0
  46. package/dist/components/IncremarkTable.svelte.d.ts +12 -0
  47. package/dist/components/IncremarkTable.svelte.d.ts.map +1 -0
  48. package/dist/components/IncremarkThematicBreak.svelte +11 -0
  49. package/dist/components/IncremarkThematicBreak.svelte.d.ts +19 -0
  50. package/dist/components/IncremarkThematicBreak.svelte.d.ts.map +1 -0
  51. package/dist/components/index.d.ts +21 -0
  52. package/dist/components/index.d.ts.map +1 -0
  53. package/dist/components/index.js +19 -0
  54. package/dist/components/types.d.ts +18 -0
  55. package/dist/components/types.d.ts.map +1 -0
  56. package/dist/components/types.js +5 -0
  57. package/dist/context/definitionsContext.d.ts +64 -0
  58. package/dist/context/definitionsContext.d.ts.map +1 -0
  59. package/dist/context/definitionsContext.js +117 -0
  60. package/dist/index.d.ts +15 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +19 -0
  63. package/dist/stores/useBlockTransformer.d.ts +89 -0
  64. package/dist/stores/useBlockTransformer.d.ts.map +1 -0
  65. package/dist/stores/useBlockTransformer.js +110 -0
  66. package/dist/stores/useDevTools.d.ts +33 -0
  67. package/dist/stores/useDevTools.d.ts.map +1 -0
  68. package/dist/stores/useDevTools.js +53 -0
  69. package/dist/stores/useIncremark.d.ts +128 -0
  70. package/dist/stores/useIncremark.d.ts.map +1 -0
  71. package/dist/stores/useIncremark.js +177 -0
  72. package/dist/stores/useTypewriter.d.ts +42 -0
  73. package/dist/stores/useTypewriter.d.ts.map +1 -0
  74. package/dist/stores/useTypewriter.js +153 -0
  75. package/dist/svelte-runes.d.ts +32 -0
  76. package/dist/utils/cursor.d.ts +14 -0
  77. package/dist/utils/cursor.d.ts.map +1 -0
  78. package/dist/utils/cursor.js +36 -0
  79. package/package.json +70 -0
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @file useDevTools Store - DevTools 集成
3
+ * @description Svelte 5 DevTools 一行接入
4
+ */
5
+ import { type DevToolsOptions } from '@incremark/devtools';
6
+ import type { UseIncremarkReturn } from './useIncremark';
7
+ /**
8
+ * useDevTools 选项
9
+ */
10
+ export interface UseDevToolsOptions extends DevToolsOptions {
11
+ }
12
+ /**
13
+ * Svelte 5 DevTools 一行接入
14
+ *
15
+ * @description
16
+ * 在组件中调用此函数以启用 DevTools
17
+ *
18
+ * @param incremark - useIncremark 返回的对象
19
+ * @param options - DevTools 选项
20
+ * @returns DevTools 实例
21
+ *
22
+ * @example
23
+ * ```svelte
24
+ * <script>
25
+ * import { useIncremark, useDevTools } from '@incremark/svelte'
26
+ *
27
+ * const incremark = useIncremark()
28
+ * useDevTools(incremark) // 就这一行!
29
+ * </script>
30
+ * ```
31
+ */
32
+ export declare function useDevTools(incremark: UseIncremarkReturn, options?: UseDevToolsOptions): import("@incremark/devtools").IncremarkDevTools;
33
+ //# sourceMappingURL=useDevTools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDevTools.d.ts","sourceRoot":"","sources":["../../src/stores/useDevTools.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAC1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAExD;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,eAAe;CAAG;AAE9D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,kBAAkB,EAC7B,OAAO,GAAE,kBAAuB,mDAgCjC"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @file useDevTools Store - DevTools 集成
3
+ * @description Svelte 5 DevTools 一行接入
4
+ */
5
+ import { onMount, onDestroy } from 'svelte';
6
+ import { createDevTools } from '@incremark/devtools';
7
+ /**
8
+ * Svelte 5 DevTools 一行接入
9
+ *
10
+ * @description
11
+ * 在组件中调用此函数以启用 DevTools
12
+ *
13
+ * @param incremark - useIncremark 返回的对象
14
+ * @param options - DevTools 选项
15
+ * @returns DevTools 实例
16
+ *
17
+ * @example
18
+ * ```svelte
19
+ * <script>
20
+ * import { useIncremark, useDevTools } from '@incremark/svelte'
21
+ *
22
+ * const incremark = useIncremark()
23
+ * useDevTools(incremark) // 就这一行!
24
+ * </script>
25
+ * ```
26
+ */
27
+ export function useDevTools(incremark, options = {}) {
28
+ const devtools = createDevTools(options);
29
+ // 设置 parser 的 onChange 回调
30
+ incremark.parser.setOnChange((state) => {
31
+ const blocks = [
32
+ ...state.completedBlocks.map((b) => ({ ...b, stableId: b.id })),
33
+ ...state.pendingBlocks.map((b, i) => ({ ...b, stableId: `pending-${i}` }))
34
+ ];
35
+ devtools.update({
36
+ blocks,
37
+ completedBlocks: state.completedBlocks,
38
+ pendingBlocks: state.pendingBlocks,
39
+ markdown: state.markdown,
40
+ ast: state.ast,
41
+ isLoading: state.pendingBlocks.length > 0
42
+ });
43
+ });
44
+ onMount(() => {
45
+ devtools.mount();
46
+ });
47
+ onDestroy(() => {
48
+ devtools.unmount();
49
+ // 清理回调
50
+ incremark.parser.setOnChange(undefined);
51
+ });
52
+ return devtools;
53
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * @file useIncremark Store - 核心 Store
3
+ * @description Svelte 5 Store: Incremark 流式 Markdown 解析器
4
+ */
5
+ import { type Writable, type Readable } from 'svelte/store';
6
+ import { createIncremarkParser, type ParserOptions, type ParsedBlock, type IncrementalUpdate, type Root, type TransformerPlugin, type AnimationEffect } from '@incremark/core';
7
+ /**
8
+ * 打字机效果配置
9
+ */
10
+ export interface TypewriterOptions {
11
+ /** 是否启用打字机效果(可响应式切换) */
12
+ enabled?: boolean;
13
+ /** 每次显示的字符数,可以是固定值或范围 [min, max] */
14
+ charsPerTick?: number | [number, number];
15
+ /** 更新间隔 (ms) */
16
+ tickInterval?: number;
17
+ /** 动画效果: 'none' | 'fade-in' | 'typing' */
18
+ effect?: AnimationEffect;
19
+ /** 光标字符(仅 typing 效果使用) */
20
+ cursor?: string;
21
+ /** 页面不可见时暂停 */
22
+ pauseOnHidden?: boolean;
23
+ /** 自定义插件 */
24
+ plugins?: TransformerPlugin[];
25
+ }
26
+ /**
27
+ * useIncremark 选项
28
+ */
29
+ export interface UseIncremarkOptions extends ParserOptions {
30
+ /** 打字机配置,传入即创建 transformer(可通过 enabled 控制是否启用) */
31
+ typewriter?: TypewriterOptions;
32
+ }
33
+ /**
34
+ * 打字机控制对象
35
+ */
36
+ export interface TypewriterControls {
37
+ /** 是否启用(只读) */
38
+ enabled: Readable<boolean>;
39
+ /** 设置是否启用 */
40
+ setEnabled: (enabled: boolean) => void;
41
+ /** 是否正在处理中 */
42
+ isProcessing: Readable<boolean>;
43
+ /** 是否已暂停 */
44
+ isPaused: Readable<boolean>;
45
+ /** 当前动画效果 */
46
+ effect: Readable<AnimationEffect>;
47
+ /** 跳过动画,直接显示全部 */
48
+ skip: () => void;
49
+ /** 暂停动画 */
50
+ pause: () => void;
51
+ /** 恢复动画 */
52
+ resume: () => void;
53
+ /** 动态更新配置 */
54
+ setOptions: (options: Partial<TypewriterOptions>) => void;
55
+ }
56
+ /**
57
+ * useIncremark 返回值
58
+ */
59
+ export interface UseIncremarkReturn {
60
+ /** 已收集的完整 Markdown 字符串 */
61
+ markdown: Writable<string>;
62
+ /** 已完成的块列表 */
63
+ completedBlocks: Writable<ParsedBlock[]>;
64
+ /** 待处理的块列表 */
65
+ pendingBlocks: Writable<ParsedBlock[]>;
66
+ /** 当前完整的 AST */
67
+ ast: Readable<Root>;
68
+ /** 用于渲染的 blocks(根据打字机设置自动处理) */
69
+ blocks: Readable<Array<ParsedBlock & {
70
+ stableId: string;
71
+ }>>;
72
+ /** 是否正在加载 */
73
+ isLoading: Writable<boolean>;
74
+ /** 是否已完成(finalize) */
75
+ isFinalized: Writable<boolean>;
76
+ /** 脚注引用的出现顺序 */
77
+ footnoteReferenceOrder: Writable<string[]>;
78
+ /** 追加内容 */
79
+ append: (chunk: string) => IncrementalUpdate;
80
+ /** 完成解析 */
81
+ finalize: () => IncrementalUpdate;
82
+ /** 强制中断 */
83
+ abort: () => IncrementalUpdate;
84
+ /** 重置解析器和打字机 */
85
+ reset: () => void;
86
+ /** 一次性渲染(reset + append + finalize) */
87
+ render: (content: string) => IncrementalUpdate;
88
+ /** 解析器实例 */
89
+ parser: ReturnType<typeof createIncremarkParser>;
90
+ /** 打字机控制 */
91
+ typewriter: TypewriterControls;
92
+ }
93
+ /**
94
+ * Svelte 5 Store: Incremark 流式 Markdown 解析器
95
+ *
96
+ * @description
97
+ * 核心 store,管理解析器状态和操作
98
+ *
99
+ * @param options - 解析器选项
100
+ * @returns 解析器状态和控制对象
101
+ *
102
+ * @example
103
+ * ```svelte
104
+ * <script>
105
+ * import { useIncremark, Incremark } from '@incremark/svelte'
106
+ *
107
+ * // 基础用法
108
+ * const { blocks, append, finalize } = useIncremark()
109
+ *
110
+ * // 启用打字机效果
111
+ * const { blocks, append, finalize, typewriter } = useIncremark({
112
+ * typewriter: {
113
+ * enabled: true,
114
+ * charsPerTick: [1, 3],
115
+ * tickInterval: 30,
116
+ * effect: 'typing',
117
+ * cursor: '|'
118
+ * }
119
+ * })
120
+ * </script>
121
+ *
122
+ * <template>
123
+ * <Incremark blocks={$blocks} />
124
+ * </template>
125
+ * ```
126
+ */
127
+ export declare function useIncremark(options?: UseIncremarkOptions): UseIncremarkReturn;
128
+ //# sourceMappingURL=useIncremark.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useIncremark.d.ts","sourceRoot":"","sources":["../../src/stores/useIncremark.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,177 @@
1
+ /**
2
+ * @file useIncremark Store - 核心 Store
3
+ * @description Svelte 5 Store: Incremark 流式 Markdown 解析器
4
+ */
5
+ import { writable, derived } from 'svelte/store';
6
+ import { createIncremarkParser } from '@incremark/core';
7
+ import { setDefinitionsContext } from '../context/definitionsContext';
8
+ import { useTypewriter } from './useTypewriter';
9
+ /**
10
+ * Svelte 5 Store: Incremark 流式 Markdown 解析器
11
+ *
12
+ * @description
13
+ * 核心 store,管理解析器状态和操作
14
+ *
15
+ * @param options - 解析器选项
16
+ * @returns 解析器状态和控制对象
17
+ *
18
+ * @example
19
+ * ```svelte
20
+ * <script>
21
+ * import { useIncremark, Incremark } from '@incremark/svelte'
22
+ *
23
+ * // 基础用法
24
+ * const { blocks, append, finalize } = useIncremark()
25
+ *
26
+ * // 启用打字机效果
27
+ * const { blocks, append, finalize, typewriter } = useIncremark({
28
+ * typewriter: {
29
+ * enabled: true,
30
+ * charsPerTick: [1, 3],
31
+ * tickInterval: 30,
32
+ * effect: 'typing',
33
+ * cursor: '|'
34
+ * }
35
+ * })
36
+ * </script>
37
+ *
38
+ * <template>
39
+ * <Incremark blocks={$blocks} />
40
+ * </template>
41
+ * ```
42
+ */
43
+ export function useIncremark(options = {}) {
44
+ // 内部自动提供 definitions context
45
+ const { setDefinations, setFootnoteDefinitions, setFootnoteReferenceOrder } = setDefinitionsContext();
46
+ // 解析器
47
+ const parser = createIncremarkParser({
48
+ ...options,
49
+ onChange: (state) => {
50
+ setDefinations(state.definitions);
51
+ setFootnoteDefinitions(state.footnoteDefinitions);
52
+ // 调用用户提供的 onChange
53
+ options.onChange?.(state);
54
+ }
55
+ });
56
+ // 状态 stores
57
+ const completedBlocks = writable([]);
58
+ const pendingBlocks = writable([]);
59
+ const isLoading = writable(false);
60
+ const markdown = writable('');
61
+ const isFinalized = writable(false);
62
+ const footnoteReferenceOrder = writable([]);
63
+ // 使用 useTypewriter store 管理打字机效果
64
+ const { blocks, typewriter, transformer } = useTypewriter({
65
+ typewriter: options.typewriter,
66
+ completedBlocks,
67
+ pendingBlocks
68
+ });
69
+ // AST
70
+ const ast = derived([completedBlocks, pendingBlocks], ([$completedBlocks, $pendingBlocks]) => ({
71
+ type: 'root',
72
+ children: [
73
+ ...$completedBlocks.map((b) => b.node),
74
+ ...$pendingBlocks.map((b) => b.node)
75
+ ]
76
+ }));
77
+ /**
78
+ * 追加内容
79
+ *
80
+ * @param chunk - 要追加的 Markdown 文本块
81
+ * @returns 增量更新结果
82
+ */
83
+ function append(chunk) {
84
+ isLoading.set(true);
85
+ const update = parser.append(chunk);
86
+ markdown.set(parser.getBuffer());
87
+ if (update.completed.length > 0) {
88
+ completedBlocks.update((blocks) => [
89
+ ...blocks,
90
+ ...update.completed
91
+ ]);
92
+ }
93
+ pendingBlocks.set(update.pending);
94
+ // 更新脚注引用顺序
95
+ footnoteReferenceOrder.set(update.footnoteReferenceOrder);
96
+ setFootnoteReferenceOrder(update.footnoteReferenceOrder);
97
+ return update;
98
+ }
99
+ /**
100
+ * 完成解析
101
+ *
102
+ * @returns 增量更新结果
103
+ */
104
+ function finalize() {
105
+ const update = parser.finalize();
106
+ markdown.set(parser.getBuffer());
107
+ if (update.completed.length > 0) {
108
+ completedBlocks.update((blocks) => [
109
+ ...blocks,
110
+ ...update.completed
111
+ ]);
112
+ }
113
+ pendingBlocks.set([]);
114
+ isLoading.set(false);
115
+ isFinalized.set(true);
116
+ // 更新脚注引用顺序
117
+ footnoteReferenceOrder.set(update.footnoteReferenceOrder);
118
+ setFootnoteReferenceOrder(update.footnoteReferenceOrder);
119
+ return update;
120
+ }
121
+ /**
122
+ * 强制中断
123
+ *
124
+ * @returns 增量更新结果
125
+ */
126
+ function abort() {
127
+ return finalize();
128
+ }
129
+ /**
130
+ * 重置解析器和打字机
131
+ */
132
+ function reset() {
133
+ parser.reset();
134
+ completedBlocks.set([]);
135
+ pendingBlocks.set([]);
136
+ markdown.set('');
137
+ isLoading.set(false);
138
+ isFinalized.set(false);
139
+ footnoteReferenceOrder.set([]);
140
+ // 重置 transformer
141
+ transformer?.reset();
142
+ }
143
+ /**
144
+ * 一次性渲染(reset + append + finalize)
145
+ *
146
+ * @param content - 完整的 Markdown 内容
147
+ * @returns 增量更新结果
148
+ */
149
+ function render(content) {
150
+ const update = parser.render(content);
151
+ markdown.set(parser.getBuffer());
152
+ completedBlocks.set(parser.getCompletedBlocks());
153
+ pendingBlocks.set([]);
154
+ isLoading.set(false);
155
+ isFinalized.set(true);
156
+ footnoteReferenceOrder.set(update.footnoteReferenceOrder);
157
+ setFootnoteReferenceOrder(update.footnoteReferenceOrder);
158
+ return update;
159
+ }
160
+ return {
161
+ markdown,
162
+ completedBlocks,
163
+ pendingBlocks,
164
+ ast,
165
+ blocks,
166
+ isLoading,
167
+ isFinalized,
168
+ footnoteReferenceOrder,
169
+ append,
170
+ finalize,
171
+ abort,
172
+ reset,
173
+ render,
174
+ parser,
175
+ typewriter
176
+ };
177
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @file useTypewriter Store - 打字机效果管理
3
+ * @description 管理打字机效果的状态和控制逻辑,从 useIncremark 中拆分出来以简化代码
4
+ */
5
+ import { type Writable, type Readable } from 'svelte/store';
6
+ import { type RootContent, type ParsedBlock, type BlockTransformer } from '@incremark/core';
7
+ import type { TypewriterOptions, TypewriterControls } from './useIncremark';
8
+ /**
9
+ * useTypewriter 选项
10
+ */
11
+ export interface UseTypewriterOptions {
12
+ /** 打字机配置 */
13
+ typewriter?: TypewriterOptions;
14
+ /** 已完成的块列表 store */
15
+ completedBlocks: Writable<ParsedBlock[]>;
16
+ /** 待处理的块列表 store */
17
+ pendingBlocks: Writable<ParsedBlock[]>;
18
+ }
19
+ /**
20
+ * useTypewriter 返回值
21
+ */
22
+ export interface UseTypewriterReturn {
23
+ /** 用于渲染的 blocks(经过打字机处理或原始blocks) */
24
+ blocks: Readable<Array<ParsedBlock & {
25
+ stableId: string;
26
+ }>>;
27
+ /** 打字机控制对象 */
28
+ typewriter: TypewriterControls;
29
+ /** transformer 实例 */
30
+ transformer: BlockTransformer<RootContent> | null;
31
+ }
32
+ /**
33
+ * useTypewriter Store
34
+ *
35
+ * @description
36
+ * 管理打字机效果的所有状态和逻辑
37
+ *
38
+ * @param options - 打字机配置和数据
39
+ * @returns 打字机状态和控制对象
40
+ */
41
+ export declare function useTypewriter(options: UseTypewriterOptions): UseTypewriterReturn;
42
+ //# sourceMappingURL=useTypewriter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTypewriter.d.ts","sourceRoot":"","sources":["../../src/stores/useTypewriter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAqB,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC9E,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,WAAW,EAGhB,KAAK,gBAAgB,EACtB,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAG3E;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,YAAY;IACZ,UAAU,CAAC,EAAE,iBAAiB,CAAA;IAC9B,oBAAoB;IACpB,eAAe,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAA;IACxC,oBAAoB;IACpB,aAAa,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAA;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,qCAAqC;IACrC,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,WAAW,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAA;IAC3D,cAAc;IACd,UAAU,EAAE,kBAAkB,CAAA;IAC9B,qBAAqB;IACrB,WAAW,EAAE,gBAAgB,CAAC,WAAW,CAAC,GAAG,IAAI,CAAA;CAClD;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CAkKhF"}
@@ -0,0 +1,153 @@
1
+ /**
2
+ * @file useTypewriter Store - 打字机效果管理
3
+ * @description 管理打字机效果的状态和控制逻辑,从 useIncremark 中拆分出来以简化代码
4
+ */
5
+ import { writable, derived } from 'svelte/store';
6
+ import { createBlockTransformer, defaultPlugins } from '@incremark/core';
7
+ import { addCursorToNode } from '../utils/cursor';
8
+ /**
9
+ * useTypewriter Store
10
+ *
11
+ * @description
12
+ * 管理打字机效果的所有状态和逻辑
13
+ *
14
+ * @param options - 打字机配置和数据
15
+ * @returns 打字机状态和控制对象
16
+ */
17
+ export function useTypewriter(options) {
18
+ const { typewriter: typewriterConfig, completedBlocks, pendingBlocks } = options;
19
+ // 打字机状态 stores
20
+ const typewriterEnabled = writable(typewriterConfig?.enabled ?? !!typewriterConfig);
21
+ const displayBlocks = writable([]);
22
+ const isTypewriterProcessing = writable(false);
23
+ const isTypewriterPaused = writable(false);
24
+ const typewriterEffect = writable(typewriterConfig?.effect ?? 'none');
25
+ const typewriterCursor = writable(typewriterConfig?.cursor ?? '|');
26
+ // 创建 transformer(如果有 typewriter 配置)
27
+ let transformer = null;
28
+ if (typewriterConfig) {
29
+ const twOptions = typewriterConfig;
30
+ transformer = createBlockTransformer({
31
+ charsPerTick: twOptions.charsPerTick ?? [1, 3],
32
+ tickInterval: twOptions.tickInterval ?? 30,
33
+ effect: twOptions.effect ?? 'none',
34
+ pauseOnHidden: twOptions.pauseOnHidden ?? true,
35
+ plugins: twOptions.plugins ?? defaultPlugins,
36
+ onChange: (blocks) => {
37
+ displayBlocks.set(blocks);
38
+ isTypewriterProcessing.set(transformer?.isProcessing() ?? false);
39
+ isTypewriterPaused.set(transformer?.isPausedState() ?? false);
40
+ }
41
+ });
42
+ }
43
+ // 将 completedBlocks 转换为 SourceBlock 格式
44
+ const sourceBlocks = derived(completedBlocks, ($completedBlocks) => {
45
+ return $completedBlocks.map(block => ({
46
+ id: block.id,
47
+ node: block.node,
48
+ status: block.status
49
+ }));
50
+ });
51
+ // 监听 sourceBlocks 变化,推送给 transformer
52
+ if (transformer) {
53
+ let unsubscribe = null;
54
+ unsubscribe = sourceBlocks.subscribe((blocks) => {
55
+ transformer.push(blocks);
56
+ // 更新正在显示的 block
57
+ displayBlocks.update((displayBlocksValue) => {
58
+ const currentDisplaying = displayBlocksValue.find((b) => !b.isDisplayComplete);
59
+ if (currentDisplaying) {
60
+ const updated = blocks.find((b) => b.id === currentDisplaying.id);
61
+ if (updated) {
62
+ transformer.update(updated);
63
+ }
64
+ }
65
+ return displayBlocksValue;
66
+ });
67
+ });
68
+ }
69
+ // 原始 blocks(不经过打字机)
70
+ const rawBlocks = derived([completedBlocks, pendingBlocks], ([$completedBlocks, $pendingBlocks]) => {
71
+ const result = [];
72
+ for (const block of $completedBlocks) {
73
+ result.push({ ...block, stableId: block.id });
74
+ }
75
+ for (let i = 0; i < $pendingBlocks.length; i++) {
76
+ result.push({
77
+ ...$pendingBlocks[i],
78
+ stableId: `pending-${i}`
79
+ });
80
+ }
81
+ return result;
82
+ });
83
+ // 最终用于渲染的 blocks
84
+ const blocks = derived([typewriterEnabled, displayBlocks, rawBlocks, typewriterEffect, typewriterCursor], ([$enabled, $displayBlocks, $rawBlocks, $effect, $cursor]) => {
85
+ // 未启用打字机或没有 transformer:返回原始 blocks
86
+ if (!$enabled || !transformer) {
87
+ return $rawBlocks;
88
+ }
89
+ // 启用打字机:使用 displayBlocks
90
+ return $displayBlocks.map((db, index) => {
91
+ const isPending = !db.isDisplayComplete;
92
+ const isLastPending = isPending && index === $displayBlocks.length - 1;
93
+ // typing 效果时添加光标
94
+ let node = db.displayNode;
95
+ if ($effect === 'typing' && isLastPending) {
96
+ node = addCursorToNode(db.displayNode, $cursor);
97
+ }
98
+ return {
99
+ id: db.id,
100
+ stableId: db.id,
101
+ status: (db.isDisplayComplete ? 'completed' : 'pending'),
102
+ isLastPending,
103
+ node,
104
+ startOffset: 0,
105
+ endOffset: 0,
106
+ rawText: ''
107
+ };
108
+ });
109
+ });
110
+ // 打字机控制对象
111
+ const typewriterControls = {
112
+ enabled: derived(typewriterEnabled, ($enabled) => $enabled),
113
+ setEnabled: (value) => {
114
+ typewriterEnabled.set(value);
115
+ },
116
+ isProcessing: derived(isTypewriterProcessing, ($processing) => $processing),
117
+ isPaused: derived(isTypewriterPaused, ($paused) => $paused),
118
+ effect: derived(typewriterEffect, ($effect) => $effect),
119
+ skip: () => transformer?.skip(),
120
+ pause: () => {
121
+ transformer?.pause();
122
+ isTypewriterPaused.set(true);
123
+ },
124
+ resume: () => {
125
+ transformer?.resume();
126
+ isTypewriterPaused.set(false);
127
+ },
128
+ setOptions: (opts) => {
129
+ if (opts.enabled !== undefined) {
130
+ typewriterEnabled.set(opts.enabled);
131
+ }
132
+ if (opts.charsPerTick !== undefined || opts.tickInterval !== undefined || opts.effect !== undefined || opts.pauseOnHidden !== undefined) {
133
+ transformer?.setOptions({
134
+ charsPerTick: opts.charsPerTick,
135
+ tickInterval: opts.tickInterval,
136
+ effect: opts.effect,
137
+ pauseOnHidden: opts.pauseOnHidden
138
+ });
139
+ }
140
+ if (opts.effect !== undefined) {
141
+ typewriterEffect.set(opts.effect);
142
+ }
143
+ if (opts.cursor !== undefined) {
144
+ typewriterCursor.set(opts.cursor);
145
+ }
146
+ }
147
+ };
148
+ return {
149
+ blocks,
150
+ typewriter: typewriterControls,
151
+ transformer
152
+ };
153
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @file Svelte 5 Runes 类型声明
3
+ * @description 为 Svelte 5 的 runes 提供类型支持
4
+ */
5
+
6
+ declare global {
7
+ /**
8
+ * $props rune - 定义组件 props
9
+ */
10
+ function $props<T extends Record<string, any> = {}>(): T
11
+
12
+ /**
13
+ * $derived rune - 定义派生状态
14
+ */
15
+ function $derived<T>(expression: T): T
16
+ namespace $derived {
17
+ function by<T>(fn: () => T): T
18
+ }
19
+
20
+ /**
21
+ * $state rune - 定义响应式状态
22
+ */
23
+ function $state<T>(initial: T): T
24
+
25
+ /**
26
+ * $effect rune - 定义副作用
27
+ */
28
+ function $effect(fn: () => void | (() => void)): void
29
+ }
30
+
31
+ export {}
32
+
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @file Cursor Utils - 光标工具函数
3
+ * @description 用于在 AST 节点末尾添加光标的工具函数
4
+ */
5
+ import type { RootContent } from '@incremark/core';
6
+ /**
7
+ * 在节点末尾添加光标
8
+ *
9
+ * @param node - 要添加光标的节点
10
+ * @param cursor - 光标字符
11
+ * @returns 添加了光标的新节点
12
+ */
13
+ export declare function addCursorToNode(node: RootContent, cursor: string): RootContent;
14
+ //# sourceMappingURL=cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/utils/cursor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAElD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CA0B9E"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @file Cursor Utils - 光标工具函数
3
+ * @description 用于在 AST 节点末尾添加光标的工具函数
4
+ */
5
+ /**
6
+ * 在节点末尾添加光标
7
+ *
8
+ * @param node - 要添加光标的节点
9
+ * @param cursor - 光标字符
10
+ * @returns 添加了光标的新节点
11
+ */
12
+ export function addCursorToNode(node, cursor) {
13
+ const cloned = JSON.parse(JSON.stringify(node));
14
+ function addToLast(n) {
15
+ if (n.children && n.children.length > 0) {
16
+ for (let i = n.children.length - 1; i >= 0; i--) {
17
+ if (addToLast(n.children[i])) {
18
+ return true;
19
+ }
20
+ }
21
+ n.children.push({ type: 'text', value: cursor });
22
+ return true;
23
+ }
24
+ if (n.type === 'text' && typeof n.value === 'string') {
25
+ n.value += cursor;
26
+ return true;
27
+ }
28
+ if (typeof n.value === 'string') {
29
+ n.value += cursor;
30
+ return true;
31
+ }
32
+ return false;
33
+ }
34
+ addToLast(cloned);
35
+ return cloned;
36
+ }