@incremark/svelte 0.3.0 → 0.3.2
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/components/CachedCodeRenderer.svelte +120 -0
- package/dist/components/CachedCodeRenderer.svelte.d.ts +10 -0
- package/dist/components/CachedCodeRenderer.svelte.d.ts.map +1 -0
- package/dist/components/ConfigProvider.svelte +5 -4
- package/dist/components/ConfigProvider.svelte.d.ts +1 -1
- package/dist/components/ConfigProvider.svelte.d.ts.map +1 -1
- package/dist/components/Incremark.svelte +10 -37
- package/dist/components/Incremark.svelte.d.ts +1 -1
- package/dist/components/Incremark.svelte.d.ts.map +1 -1
- package/dist/components/IncremarkCode.svelte +8 -6
- package/dist/components/IncremarkCode.svelte.d.ts.map +1 -1
- package/dist/components/IncremarkCodeDefault.svelte +60 -31
- package/dist/components/IncremarkCodeDefault.svelte.d.ts +2 -0
- package/dist/components/IncremarkCodeDefault.svelte.d.ts.map +1 -1
- package/dist/components/IncremarkContent.svelte +44 -18
- package/dist/components/IncremarkContent.svelte.d.ts.map +1 -1
- package/dist/components/IncremarkFootnotes.svelte +3 -6
- package/dist/components/IncremarkFootnotes.svelte.d.ts.map +1 -1
- package/dist/components/IncremarkInline.svelte +9 -10
- package/dist/components/IncremarkInline.svelte.d.ts.map +1 -1
- package/dist/components/types.d.ts +1 -1
- package/dist/components/types.d.ts.map +1 -1
- package/dist/context/{definitionsContext.d.ts → definitionsContext.svelte.d.ts} +13 -11
- package/dist/context/definitionsContext.svelte.d.ts.map +1 -0
- package/dist/context/{definitionsContext.js → definitionsContext.svelte.js} +18 -15
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/stores/{useBlockTransformer.d.ts → useBlockTransformer.svelte.d.ts} +33 -35
- package/dist/stores/useBlockTransformer.svelte.d.ts.map +1 -0
- package/dist/stores/useBlockTransformer.svelte.js +106 -0
- package/dist/stores/useDevTools.svelte.d.ts +1 -1
- package/dist/stores/useDevTools.svelte.d.ts.map +1 -1
- package/dist/stores/{useIncremark.d.ts → useIncremark.svelte.d.ts} +35 -36
- package/dist/stores/useIncremark.svelte.d.ts.map +1 -0
- package/dist/stores/useIncremark.svelte.js +226 -0
- package/dist/stores/useLocale.svelte.d.ts.map +1 -1
- package/dist/stores/useLocale.svelte.js +3 -2
- package/dist/stores/useShiki.svelte.d.ts +26 -0
- package/dist/stores/useShiki.svelte.d.ts.map +1 -1
- package/dist/stores/useShiki.svelte.js +27 -0
- package/dist/stores/useTypewriter.svelte.d.ts +49 -0
- package/dist/stores/useTypewriter.svelte.d.ts.map +1 -0
- package/dist/stores/useTypewriter.svelte.js +212 -0
- package/dist/utils/cursor.d.ts.map +1 -1
- package/dist/utils/cursor.js +8 -0
- package/package.json +9 -6
- package/dist/context/definitionsContext.d.ts.map +0 -1
- package/dist/stores/useBlockTransformer.d.ts.map +0 -1
- package/dist/stores/useBlockTransformer.js +0 -110
- package/dist/stores/useIncremark.d.ts.map +0 -1
- package/dist/stores/useIncremark.js +0 -189
- package/dist/stores/useTypewriter.d.ts +0 -44
- package/dist/stores/useTypewriter.d.ts.map +0 -1
- package/dist/stores/useTypewriter.js +0 -163
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIncremark.svelte.d.ts","sourceRoot":"","sources":["../../src/stores/useIncremark.svelte.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,qBAAqB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,IAAI,EACT,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACrB,MAAM,iBAAiB,CAAA;AAIxB;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,wBAAwB;IACxB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,oCAAoC;IACpC,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxC,gBAAgB;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,eAAe,CAAA;IACxB,0BAA0B;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,eAAe;IACf,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,YAAY;IACZ,OAAO,CAAC,EAAE,iBAAiB,EAAE,CAAA;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,sBAAsB;IACjE,kDAAkD;IAClD,UAAU,CAAC,EAAE,iBAAiB,CAAA;CAC/B;AAGD,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AAEvE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,sBAAsB;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,aAAa;IACb,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC,cAAc;IACd,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAA;IAC9B,YAAY;IACZ,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,aAAa;IACb,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAA;IAChC,kBAAkB;IAClB,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,WAAW;IACX,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,WAAW;IACX,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,aAAa;IACb,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAA;CAC1D;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,kCAAkC;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,sBAAsB;IACtB,QAAQ,CAAC,eAAe,EAAE,WAAW,EAAE,CAAA;IACvC,sBAAsB;IACtB,QAAQ,CAAC,aAAa,EAAE,WAAW,EAAE,CAAA;IACrC,wBAAwB;IACxB,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAA;IAClB,uCAAuC;IACvC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC,CAAA;IACvC,qBAAqB;IACrB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,8BAA8B;IAC9B,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAA;IACnC,wBAAwB;IACxB,QAAQ,CAAC,sBAAsB,EAAE,MAAM,EAAE,CAAA;IACzC,WAAW;IACX,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,iBAAiB,CAAA;IAC5C,WAAW;IACX,QAAQ,EAAE,MAAM,iBAAiB,CAAA;IACjC,WAAW;IACX,KAAK,EAAE,MAAM,iBAAiB,CAAA;IAC9B,gBAAgB;IAChB,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,uCAAuC;IACvC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,iBAAiB,CAAA;IAC9C,oCAAoC;IACpC,aAAa,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,sBAAsB,CAAC,KAAK,IAAI,CAAA;IACjE,YAAY;IACZ,MAAM,EAAE,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAA;IAChD,YAAY;IACZ,UAAU,EAAE,kBAAkB,CAAA;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,kBAAkB,CAgNlF"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file useIncremark - 核心 Composable
|
|
3
|
+
* @description Svelte 5 Composable: Incremark 流式 Markdown 解析器(使用 runes 语法)
|
|
4
|
+
*/
|
|
5
|
+
import { createIncremarkParser } from '@incremark/core';
|
|
6
|
+
import { setDefinitionsContext } from '../context/definitionsContext.svelte.ts';
|
|
7
|
+
import { useTypewriter } from './useTypewriter.svelte.ts';
|
|
8
|
+
/**
|
|
9
|
+
* Svelte 5 Composable: Incremark 流式 Markdown 解析器
|
|
10
|
+
*
|
|
11
|
+
* @description
|
|
12
|
+
* 核心 composable,管理解析器状态和操作,使用 Svelte 5 runes 语法
|
|
13
|
+
*
|
|
14
|
+
* @param options - 解析器选项
|
|
15
|
+
* @returns 解析器状态和控制对象
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```svelte
|
|
19
|
+
* <script>
|
|
20
|
+
* import { useIncremark, Incremark } from '@incremark/svelte'
|
|
21
|
+
*
|
|
22
|
+
* // 基础用法
|
|
23
|
+
* const incremark = useIncremark()
|
|
24
|
+
*
|
|
25
|
+
* // 启用打字机效果
|
|
26
|
+
* const incremark = useIncremark({
|
|
27
|
+
* typewriter: {
|
|
28
|
+
* enabled: true,
|
|
29
|
+
* charsPerTick: [1, 3],
|
|
30
|
+
* tickInterval: 30,
|
|
31
|
+
* effect: 'typing',
|
|
32
|
+
* cursor: '|'
|
|
33
|
+
* }
|
|
34
|
+
* })
|
|
35
|
+
* </script>
|
|
36
|
+
*
|
|
37
|
+
* <Incremark blocks={incremark.blocks} />
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function useIncremark(options = {}) {
|
|
41
|
+
// 内部自动提供 definitions context
|
|
42
|
+
const { setDefinations, setFootnoteDefinitions, setFootnoteReferenceOrder } = setDefinitionsContext();
|
|
43
|
+
// 解析器
|
|
44
|
+
const parser = createIncremarkParser({
|
|
45
|
+
...options,
|
|
46
|
+
onChange: (state) => {
|
|
47
|
+
setDefinations(state.definitions);
|
|
48
|
+
setFootnoteDefinitions(state.footnoteDefinitions);
|
|
49
|
+
// 调用用户提供的 onChange
|
|
50
|
+
options.onChange?.(state);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
// 状态(使用 $state runes)
|
|
54
|
+
let completedBlocksState = $state([]);
|
|
55
|
+
let pendingBlocksState = $state([]);
|
|
56
|
+
let isLoadingState = $state(false);
|
|
57
|
+
let markdownState = $state('');
|
|
58
|
+
let isFinalizedState = $state(false);
|
|
59
|
+
let footnoteReferenceOrderState = $state([]);
|
|
60
|
+
let astState = $state({
|
|
61
|
+
type: 'root',
|
|
62
|
+
children: []
|
|
63
|
+
});
|
|
64
|
+
/**
|
|
65
|
+
* 处理解析器更新结果(统一 append 和 finalize 的更新逻辑)
|
|
66
|
+
*/
|
|
67
|
+
function handleUpdate(update, isFinalize) {
|
|
68
|
+
markdownState = parser.getBuffer();
|
|
69
|
+
// 处理被更新的 blocks(需要移除的旧 blocks)
|
|
70
|
+
if (update.updated.length > 0) {
|
|
71
|
+
const idsToRemove = new Set(update.updated.map((b) => b.id));
|
|
72
|
+
completedBlocksState = completedBlocksState.filter((b) => !idsToRemove.has(b.id));
|
|
73
|
+
}
|
|
74
|
+
if (update.completed.length > 0) {
|
|
75
|
+
completedBlocksState = [...completedBlocksState, ...update.completed];
|
|
76
|
+
}
|
|
77
|
+
pendingBlocksState = update.pending;
|
|
78
|
+
if (isFinalize) {
|
|
79
|
+
// 如果还有 pending blocks,则将它们添加到 completed blocks 中
|
|
80
|
+
if (update.pending.length > 0) {
|
|
81
|
+
completedBlocksState = [...completedBlocksState, ...update.pending];
|
|
82
|
+
pendingBlocksState = [];
|
|
83
|
+
}
|
|
84
|
+
isLoadingState = false;
|
|
85
|
+
isFinalizedState = true;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
isLoadingState = true;
|
|
89
|
+
}
|
|
90
|
+
// 更新脚注引用顺序(解析器的完整顺序)
|
|
91
|
+
footnoteReferenceOrderState = update.footnoteReferenceOrder;
|
|
92
|
+
}
|
|
93
|
+
// 使用 useTypewriter composable 管理打字机效果
|
|
94
|
+
const typewriterResult = useTypewriter({
|
|
95
|
+
typewriter: options.typewriter,
|
|
96
|
+
getCompletedBlocks: () => completedBlocksState,
|
|
97
|
+
getPendingBlocks: () => pendingBlocksState
|
|
98
|
+
});
|
|
99
|
+
const { getBlocks: getTypewriterBlocks, typewriter, transformer } = typewriterResult;
|
|
100
|
+
// 派生状态:用于渲染的 blocks
|
|
101
|
+
const blocksGetter = $derived.by(() => {
|
|
102
|
+
// 如果没有启用打字机,直接返回原始 blocks
|
|
103
|
+
if (!options.typewriter || !typewriter.enabled) {
|
|
104
|
+
return [
|
|
105
|
+
...completedBlocksState,
|
|
106
|
+
...pendingBlocksState.map((b, i) => ({
|
|
107
|
+
...b,
|
|
108
|
+
isLastPending: i === pendingBlocksState.length - 1
|
|
109
|
+
}))
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
// 否则返回打字机处理的 blocks
|
|
113
|
+
return getTypewriterBlocks();
|
|
114
|
+
});
|
|
115
|
+
// 派生状态:内容是否完全显示完成
|
|
116
|
+
const isDisplayCompleteGetter = $derived.by(() => {
|
|
117
|
+
// 没有配置打字机,或者打字机未启用:只需判断是否 finalized
|
|
118
|
+
if (!options.typewriter || !typewriter.enabled) {
|
|
119
|
+
return isFinalizedState;
|
|
120
|
+
}
|
|
121
|
+
// 启用了打字机:需要 finalize + 动画完成
|
|
122
|
+
return isFinalizedState && typewriterResult.isAnimationComplete;
|
|
123
|
+
});
|
|
124
|
+
// 派生状态:脚注引用顺序
|
|
125
|
+
// 与 Vue 版本对齐:确保脚注只在引用所在的 block 动画完成后才显示
|
|
126
|
+
const displayedFootnoteOrder = $derived.by(() => {
|
|
127
|
+
// 没有配置打字机,或者打字机未启用:使用解析器的顺序
|
|
128
|
+
if (!options.typewriter || !typewriter.enabled) {
|
|
129
|
+
return footnoteReferenceOrderState;
|
|
130
|
+
}
|
|
131
|
+
// 启用了打字机:从打字机获取脚注顺序(通过 getter 访问派生状态)
|
|
132
|
+
// 注意:动画进行中时会返回空数组,这是正确的行为(隐藏脚注)
|
|
133
|
+
return typewriterResult.displayedFootnoteReferenceOrder;
|
|
134
|
+
});
|
|
135
|
+
// 监听脚注顺序变化,更新 context
|
|
136
|
+
$effect(() => {
|
|
137
|
+
setFootnoteReferenceOrder(displayedFootnoteOrder);
|
|
138
|
+
});
|
|
139
|
+
/**
|
|
140
|
+
* 追加内容
|
|
141
|
+
*/
|
|
142
|
+
function append(chunk) {
|
|
143
|
+
const update = parser.append(chunk);
|
|
144
|
+
astState = update.ast;
|
|
145
|
+
handleUpdate(update, false);
|
|
146
|
+
return update;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* 完成解析
|
|
150
|
+
*/
|
|
151
|
+
function finalize() {
|
|
152
|
+
const update = parser.finalize();
|
|
153
|
+
handleUpdate(update, true);
|
|
154
|
+
return update;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* 强制中断
|
|
158
|
+
*/
|
|
159
|
+
function abort() {
|
|
160
|
+
return finalize();
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* 重置解析器和打字机
|
|
164
|
+
*/
|
|
165
|
+
function reset() {
|
|
166
|
+
parser.reset();
|
|
167
|
+
completedBlocksState = [];
|
|
168
|
+
pendingBlocksState = [];
|
|
169
|
+
markdownState = '';
|
|
170
|
+
isLoadingState = false;
|
|
171
|
+
isFinalizedState = false;
|
|
172
|
+
footnoteReferenceOrderState = [];
|
|
173
|
+
astState = { type: 'root', children: [] };
|
|
174
|
+
// 重置 transformer
|
|
175
|
+
transformer?.reset();
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* 一次性渲染(reset + append + finalize)
|
|
179
|
+
*/
|
|
180
|
+
function render(content) {
|
|
181
|
+
const update = parser.render(content);
|
|
182
|
+
markdownState = parser.getBuffer();
|
|
183
|
+
completedBlocksState = parser.getCompletedBlocks();
|
|
184
|
+
pendingBlocksState = [];
|
|
185
|
+
isLoadingState = false;
|
|
186
|
+
isFinalizedState = true;
|
|
187
|
+
// render 是一次性渲染,不经过打字机,直接设置脚注顺序
|
|
188
|
+
footnoteReferenceOrderState = update.footnoteReferenceOrder;
|
|
189
|
+
setFootnoteReferenceOrder(update.footnoteReferenceOrder);
|
|
190
|
+
return update;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* 更新解析器配置(动态更新,不需要重建 parser 实例)
|
|
194
|
+
*/
|
|
195
|
+
function updateOptions(newOptions) {
|
|
196
|
+
parser.updateOptions(newOptions);
|
|
197
|
+
// 同步状态
|
|
198
|
+
completedBlocksState = [];
|
|
199
|
+
pendingBlocksState = [];
|
|
200
|
+
markdownState = '';
|
|
201
|
+
isLoadingState = false;
|
|
202
|
+
isFinalizedState = false;
|
|
203
|
+
footnoteReferenceOrderState = [];
|
|
204
|
+
astState = { type: 'root', children: [] };
|
|
205
|
+
transformer?.reset();
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
get markdown() { return markdownState; },
|
|
209
|
+
get completedBlocks() { return completedBlocksState; },
|
|
210
|
+
get pendingBlocks() { return pendingBlocksState; },
|
|
211
|
+
get ast() { return astState; },
|
|
212
|
+
get blocks() { return blocksGetter; },
|
|
213
|
+
get isLoading() { return isLoadingState; },
|
|
214
|
+
get isFinalized() { return isFinalizedState; },
|
|
215
|
+
get isDisplayComplete() { return isDisplayCompleteGetter; },
|
|
216
|
+
get footnoteReferenceOrder() { return footnoteReferenceOrderState; },
|
|
217
|
+
append,
|
|
218
|
+
finalize,
|
|
219
|
+
abort,
|
|
220
|
+
reset,
|
|
221
|
+
render,
|
|
222
|
+
updateOptions,
|
|
223
|
+
parser,
|
|
224
|
+
typewriter
|
|
225
|
+
};
|
|
226
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLocale.svelte.d.ts","sourceRoot":"","sources":["../../src/stores/useLocale.svelte.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAKxD;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,6BAA6B;IAC7B,SAAS,EAAE,MAAM,eAAe,CAAA;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,WAAW;IACX,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,eAAe,QAG7D;AAED;;;GAGG;AACH,wBAAgB,SAAS,IAAI,eAAe,
|
|
1
|
+
{"version":3,"file":"useLocale.svelte.d.ts","sourceRoot":"","sources":["../../src/stores/useLocale.svelte.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAKxD;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,6BAA6B;IAC7B,SAAS,EAAE,MAAM,eAAe,CAAA;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,WAAW;IACX,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,eAAe,QAG7D;AAED;;;GAGG;AACH,wBAAgB,SAAS,IAAI,eAAe,CAkB3C"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { setContext, getContext } from 'svelte';
|
|
2
|
-
import {
|
|
2
|
+
import { zhCN } from '@incremark/shared';
|
|
3
3
|
const LOCALE_KEY = Symbol('incremark-locale');
|
|
4
4
|
/**
|
|
5
5
|
* 提供 locale context
|
|
@@ -18,7 +18,8 @@ export function provideLocale(getLocale) {
|
|
|
18
18
|
export function useLocale() {
|
|
19
19
|
const context = getContext(LOCALE_KEY);
|
|
20
20
|
// 获取当前 locale(支持响应式更新)
|
|
21
|
-
|
|
21
|
+
// 使用 getContext 的默认值,如果没有 provider 则使用中文
|
|
22
|
+
const getLocale = context?.getLocale ?? (() => zhCN);
|
|
22
23
|
const t = (key) => {
|
|
23
24
|
const locale = getLocale();
|
|
24
25
|
const keys = key.split('.');
|
|
@@ -1,9 +1,35 @@
|
|
|
1
|
+
import type { HighlighterGeneric, BundledLanguage, BundledTheme } from 'shiki';
|
|
2
|
+
interface HighlighterInfo {
|
|
3
|
+
highlighter: HighlighterGeneric<BundledLanguage, BundledTheme>;
|
|
4
|
+
loadedLanguages: Set<BundledLanguage>;
|
|
5
|
+
loadedThemes: Set<BundledTheme>;
|
|
6
|
+
}
|
|
7
|
+
export type { HighlighterInfo };
|
|
8
|
+
declare class ShikiManager {
|
|
9
|
+
private static instance;
|
|
10
|
+
private highlighters;
|
|
11
|
+
private constructor();
|
|
12
|
+
static getInstance(): ShikiManager;
|
|
13
|
+
getHighlighter(theme: BundledTheme): Promise<HighlighterInfo>;
|
|
14
|
+
loadLanguage(theme: BundledTheme, lang: BundledLanguage): Promise<void>;
|
|
15
|
+
loadTheme(theme: BundledTheme): Promise<void>;
|
|
16
|
+
codeToHtml(theme: BundledTheme, code: string, lang: BundledLanguage, fallbackTheme: BundledTheme): Promise<string>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 获取 ShikiManager 单例(延迟初始化)
|
|
20
|
+
* 避免模块加载时立即创建实例,支持 SSR
|
|
21
|
+
*/
|
|
22
|
+
declare function getShikiManager(): ShikiManager;
|
|
23
|
+
export { getShikiManager, ShikiManager };
|
|
1
24
|
/**
|
|
2
25
|
* 使用 Shiki Highlighter
|
|
3
26
|
* @param themeGetter 传入一个返回主题字符串的函数,例如 () => theme
|
|
4
27
|
*/
|
|
5
28
|
export declare function useShiki(themeGetter: () => string): {
|
|
6
29
|
readonly isHighlighting: boolean;
|
|
30
|
+
readonly isReady: boolean;
|
|
31
|
+
readonly highlighterInfo: HighlighterInfo | null;
|
|
32
|
+
initHighlighter: () => Promise<void>;
|
|
7
33
|
highlight: (code: string, lang: string, fallbackTheme: string) => Promise<string>;
|
|
8
34
|
};
|
|
9
35
|
//# sourceMappingURL=useShiki.svelte.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useShiki.svelte.d.ts","sourceRoot":"","sources":["../../src/stores/useShiki.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useShiki.svelte.d.ts","sourceRoot":"","sources":["../../src/stores/useShiki.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AAG9E,UAAU,eAAe;IACvB,WAAW,EAAE,kBAAkB,CAAC,eAAe,EAAE,YAAY,CAAC,CAAA;IAC9D,eAAe,EAAE,GAAG,CAAC,eAAe,CAAC,CAAA;IACrC,YAAY,EAAE,GAAG,CAAC,YAAY,CAAC,CAAA;CAChC;AAED,YAAY,EAAE,eAAe,EAAE,CAAA;AAG/B,cAAM,YAAY;IAChB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA4B;IACnD,OAAO,CAAC,YAAY,CAAqC;IAEzD,OAAO;IAEP,MAAM,CAAC,WAAW,IAAI,YAAY;IAO5B,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IAqB7D,YAAY,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IASvE,SAAS,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAS7C,UAAU,CACd,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,eAAe,EACrB,aAAa,EAAE,YAAY,GAC1B,OAAO,CAAC,MAAM,CAAC;CASnB;AAMD;;;GAGG;AACH,iBAAS,eAAe,IAAI,YAAY,CAKvC;AAED,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,CAAA;AAIxC;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM;;;;2BASd,OAAO,CAAC,IAAI,CAAC;sBAiBhB,MAAM,QAAQ,MAAM,iBAAiB,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;EAwC7F"}
|
|
@@ -69,6 +69,7 @@ function getShikiManager() {
|
|
|
69
69
|
}
|
|
70
70
|
return shikiManagerInstance;
|
|
71
71
|
}
|
|
72
|
+
export { getShikiManager, ShikiManager };
|
|
72
73
|
// ============ Svelte 5 Composable ============
|
|
73
74
|
/**
|
|
74
75
|
* 使用 Shiki Highlighter
|
|
@@ -77,6 +78,25 @@ function getShikiManager() {
|
|
|
77
78
|
export function useShiki(themeGetter) {
|
|
78
79
|
// 使用 Svelte 5 的原生响应式状态
|
|
79
80
|
let isHighlighting = $state(false);
|
|
81
|
+
let isReady = $state(false);
|
|
82
|
+
let highlighterInfo = $state(null);
|
|
83
|
+
/**
|
|
84
|
+
* 初始化 highlighter(预加载)
|
|
85
|
+
*/
|
|
86
|
+
async function initHighlighter() {
|
|
87
|
+
if (isReady)
|
|
88
|
+
return;
|
|
89
|
+
try {
|
|
90
|
+
const currentTheme = themeGetter();
|
|
91
|
+
const info = await getShikiManager().getHighlighter(currentTheme);
|
|
92
|
+
highlighterInfo = info;
|
|
93
|
+
isReady = true;
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
console.warn('Failed to initialize Shiki highlighter:', e);
|
|
97
|
+
throw e;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
80
100
|
/**
|
|
81
101
|
* 高亮代码
|
|
82
102
|
*/
|
|
@@ -88,6 +108,10 @@ export function useShiki(themeGetter) {
|
|
|
88
108
|
try {
|
|
89
109
|
const manager = getShikiManager();
|
|
90
110
|
const info = await manager.getHighlighter(currentTheme);
|
|
111
|
+
if (!highlighterInfo) {
|
|
112
|
+
highlighterInfo = info;
|
|
113
|
+
isReady = true;
|
|
114
|
+
}
|
|
91
115
|
// 按需加载语言
|
|
92
116
|
if (!info.loadedLanguages.has(lang) && lang !== 'text') {
|
|
93
117
|
await manager.loadLanguage(currentTheme, lang);
|
|
@@ -105,6 +129,9 @@ export function useShiki(themeGetter) {
|
|
|
105
129
|
return {
|
|
106
130
|
// 使用 getter 暴露只读状态,保持 UI 响应
|
|
107
131
|
get isHighlighting() { return isHighlighting; },
|
|
132
|
+
get isReady() { return isReady; },
|
|
133
|
+
get highlighterInfo() { return highlighterInfo; },
|
|
134
|
+
initHighlighter,
|
|
108
135
|
highlight
|
|
109
136
|
};
|
|
110
137
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file useTypewriter Composable - 打字机效果管理
|
|
3
|
+
* @description 管理打字机效果的状态和控制逻辑,使用 Svelte 5 runes
|
|
4
|
+
*/
|
|
5
|
+
import { type RootContent, type ParsedBlock, type BlockTransformer } from '@incremark/core';
|
|
6
|
+
import type { TypewriterOptions, TypewriterControls } from './useIncremark.svelte.ts';
|
|
7
|
+
/**
|
|
8
|
+
* useTypewriter 选项
|
|
9
|
+
*/
|
|
10
|
+
export interface UseTypewriterOptions {
|
|
11
|
+
/** 打字机配置 */
|
|
12
|
+
typewriter?: TypewriterOptions;
|
|
13
|
+
/** 获取已完成的块列表(使用 getter 函数实现响应式) */
|
|
14
|
+
getCompletedBlocks: () => ParsedBlock[];
|
|
15
|
+
/** 获取待处理的块列表(使用 getter 函数实现响应式) */
|
|
16
|
+
getPendingBlocks: () => ParsedBlock[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* useTypewriter 返回值
|
|
20
|
+
*/
|
|
21
|
+
export interface UseTypewriterReturn {
|
|
22
|
+
/** 获取用于渲染的 blocks(经过打字机处理或原始blocks) */
|
|
23
|
+
getBlocks: () => Array<ParsedBlock & {
|
|
24
|
+
isLastPending?: boolean;
|
|
25
|
+
}>;
|
|
26
|
+
/** 打字机控制对象 */
|
|
27
|
+
typewriter: TypewriterControls;
|
|
28
|
+
/** transformer 实例 */
|
|
29
|
+
transformer: BlockTransformer<RootContent> | null;
|
|
30
|
+
/** 所有动画是否已完成(getter) */
|
|
31
|
+
readonly isAnimationComplete: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* 脚注引用顺序(所有动画完成后才返回)(getter)
|
|
34
|
+
* 用于控制脚注的显示时机:只有当所有 blocks 的打字机动画都完成后才显示脚注
|
|
35
|
+
* 动画进行中时返回空数组
|
|
36
|
+
*/
|
|
37
|
+
readonly displayedFootnoteReferenceOrder: string[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* useTypewriter Composable
|
|
41
|
+
*
|
|
42
|
+
* @description
|
|
43
|
+
* 管理打字机效果的所有状态和逻辑,使用 Svelte 5 runes
|
|
44
|
+
*
|
|
45
|
+
* @param options - 打字机配置和数据
|
|
46
|
+
* @returns 打字机状态和控制对象
|
|
47
|
+
*/
|
|
48
|
+
export declare function useTypewriter(options: UseTypewriterOptions): UseTypewriterReturn;
|
|
49
|
+
//# sourceMappingURL=useTypewriter.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTypewriter.svelte.d.ts","sourceRoot":"","sources":["../../src/stores/useTypewriter.svelte.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,WAAW,EAGhB,KAAK,gBAAgB,EACtB,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAGrF;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,YAAY;IACZ,UAAU,CAAC,EAAE,iBAAiB,CAAA;IAC9B,mCAAmC;IACnC,kBAAkB,EAAE,MAAM,WAAW,EAAE,CAAA;IACvC,mCAAmC;IACnC,gBAAgB,EAAE,MAAM,WAAW,EAAE,CAAA;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,uCAAuC;IACvC,SAAS,EAAE,MAAM,KAAK,CAAC,WAAW,GAAG;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IACjE,cAAc;IACd,UAAU,EAAE,kBAAkB,CAAA;IAC9B,qBAAqB;IACrB,WAAW,EAAE,gBAAgB,CAAC,WAAW,CAAC,GAAG,IAAI,CAAA;IACjD,wBAAwB;IACxB,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAA;IACrC;;;;OAIG;IACH,QAAQ,CAAC,+BAA+B,EAAE,MAAM,EAAE,CAAA;CACnD;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CAwNhF"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file useTypewriter Composable - 打字机效果管理
|
|
3
|
+
* @description 管理打字机效果的状态和控制逻辑,使用 Svelte 5 runes
|
|
4
|
+
*/
|
|
5
|
+
import { createBlockTransformer, defaultPlugins, mathPlugin, collectFootnoteReferences } from '@incremark/core';
|
|
6
|
+
import { addCursorToNode } from '../utils/cursor';
|
|
7
|
+
/**
|
|
8
|
+
* useTypewriter Composable
|
|
9
|
+
*
|
|
10
|
+
* @description
|
|
11
|
+
* 管理打字机效果的所有状态和逻辑,使用 Svelte 5 runes
|
|
12
|
+
*
|
|
13
|
+
* @param options - 打字机配置和数据
|
|
14
|
+
* @returns 打字机状态和控制对象
|
|
15
|
+
*/
|
|
16
|
+
export function useTypewriter(options) {
|
|
17
|
+
const { typewriter: typewriterConfig, getCompletedBlocks, getPendingBlocks } = options;
|
|
18
|
+
// 打字机状态(使用 $state runes)
|
|
19
|
+
let typewriterEnabled = $state(typewriterConfig?.enabled ?? !!typewriterConfig);
|
|
20
|
+
let displayBlocks = $state([]);
|
|
21
|
+
let isTypewriterProcessing = $state(false);
|
|
22
|
+
let isTypewriterPaused = $state(false);
|
|
23
|
+
let typewriterEffect = $state(typewriterConfig?.effect ?? 'none');
|
|
24
|
+
let typewriterCursor = $state(typewriterConfig?.cursor ?? '|');
|
|
25
|
+
let isAnimationComplete = $state(true); // 初始为 true(没有动画时视为完成)
|
|
26
|
+
// 创建 transformer(如果有 typewriter 配置)
|
|
27
|
+
// 在初始化时立即创建,而不是延迟创建
|
|
28
|
+
let transformer = null;
|
|
29
|
+
if (typewriterConfig) {
|
|
30
|
+
transformer = createBlockTransformer({
|
|
31
|
+
charsPerTick: typewriterConfig.charsPerTick ?? [1, 3],
|
|
32
|
+
tickInterval: typewriterConfig.tickInterval ?? 30,
|
|
33
|
+
effect: typewriterConfig.effect ?? 'none',
|
|
34
|
+
pauseOnHidden: typewriterConfig.pauseOnHidden ?? true,
|
|
35
|
+
// 默认插件 + 数学公式插件(数学公式应该整体显示,不参与打字机逐字符效果)
|
|
36
|
+
plugins: typewriterConfig.plugins ?? [...defaultPlugins, mathPlugin],
|
|
37
|
+
onChange: (blocks) => {
|
|
38
|
+
displayBlocks = blocks;
|
|
39
|
+
isTypewriterProcessing = transformer?.isProcessing() ?? false;
|
|
40
|
+
isTypewriterPaused = transformer?.isPausedState() ?? false;
|
|
41
|
+
},
|
|
42
|
+
onAllComplete: () => {
|
|
43
|
+
// 所有动画完成
|
|
44
|
+
isAnimationComplete = true;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
// 监听配置变化(同步 Vue 的 watch 逻辑)
|
|
49
|
+
$effect(() => {
|
|
50
|
+
const config = typewriterConfig;
|
|
51
|
+
if (!config || !transformer)
|
|
52
|
+
return;
|
|
53
|
+
// 更新本地状态
|
|
54
|
+
if (config.enabled !== undefined) {
|
|
55
|
+
typewriterEnabled = config.enabled;
|
|
56
|
+
}
|
|
57
|
+
if (config.effect !== undefined) {
|
|
58
|
+
typewriterEffect = config.effect;
|
|
59
|
+
}
|
|
60
|
+
if (config.cursor !== undefined) {
|
|
61
|
+
typewriterCursor = config.cursor;
|
|
62
|
+
}
|
|
63
|
+
// 更新 transformer 配置
|
|
64
|
+
transformer.setOptions({
|
|
65
|
+
charsPerTick: config.charsPerTick,
|
|
66
|
+
tickInterval: config.tickInterval,
|
|
67
|
+
effect: config.effect,
|
|
68
|
+
pauseOnHidden: config.pauseOnHidden
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
// 监听 blocks 变化,推送给 transformer
|
|
72
|
+
// transformer.push() 会自动检测并更新已存在 blocks 的内容变化
|
|
73
|
+
$effect(() => {
|
|
74
|
+
// 只有在 transformer 存在时才推送 blocks
|
|
75
|
+
if (!transformer)
|
|
76
|
+
return;
|
|
77
|
+
// 通过 getter 函数获取最新的 blocks(这样可以跟踪响应式变化)
|
|
78
|
+
const completedBlocks = getCompletedBlocks();
|
|
79
|
+
const pendingBlocks = getPendingBlocks();
|
|
80
|
+
// 直接传递原始 block 引用
|
|
81
|
+
// ParsedBlock 的结构(id, node, status)已经兼容 SourceBlock
|
|
82
|
+
const allBlocks = [...completedBlocks, ...pendingBlocks];
|
|
83
|
+
transformer.push(allBlocks);
|
|
84
|
+
});
|
|
85
|
+
// 获取原始 blocks(不经过打字机)
|
|
86
|
+
function getRawBlocks() {
|
|
87
|
+
const completedBlocks = getCompletedBlocks();
|
|
88
|
+
const pendingBlocks = getPendingBlocks();
|
|
89
|
+
const result = [];
|
|
90
|
+
for (const block of completedBlocks) {
|
|
91
|
+
result.push(block);
|
|
92
|
+
}
|
|
93
|
+
for (let i = 0; i < pendingBlocks.length; i++) {
|
|
94
|
+
const isLastPending = i === pendingBlocks.length - 1;
|
|
95
|
+
result.push({
|
|
96
|
+
...pendingBlocks[i],
|
|
97
|
+
isLastPending
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
// 获取最终用于渲染的 blocks
|
|
103
|
+
function getBlocks() {
|
|
104
|
+
// 未启用打字机或没有 transformer:返回原始 blocks
|
|
105
|
+
if (!typewriterEnabled || !transformer) {
|
|
106
|
+
return getRawBlocks();
|
|
107
|
+
}
|
|
108
|
+
// 启用打字机:使用 displayBlocks state
|
|
109
|
+
return displayBlocks.map((db, index) => {
|
|
110
|
+
const isPending = !db.isDisplayComplete;
|
|
111
|
+
const isLastPending = isPending && index === displayBlocks.length - 1;
|
|
112
|
+
// typing 效果时添加光标
|
|
113
|
+
let node = db.displayNode;
|
|
114
|
+
if (typewriterEffect === 'typing' && isLastPending) {
|
|
115
|
+
node = addCursorToNode(db.displayNode, typewriterCursor);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
id: db.id,
|
|
119
|
+
status: db.status,
|
|
120
|
+
isLastPending,
|
|
121
|
+
node,
|
|
122
|
+
startOffset: 0,
|
|
123
|
+
endOffset: 0,
|
|
124
|
+
rawText: ''
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 派生状态:脚注引用顺序(所有动画完成后才返回)
|
|
130
|
+
* 用于控制脚注的显示时机:只有当所有 blocks 的打字机动画都完成后才显示脚注
|
|
131
|
+
*/
|
|
132
|
+
const displayedFootnoteReferenceOrder = $derived.by(() => {
|
|
133
|
+
// 未启用打字机:返回所有脚注引用(从原始 blocks 中提取)
|
|
134
|
+
if (!typewriterEnabled || !transformer) {
|
|
135
|
+
const references = [];
|
|
136
|
+
const seen = new Set();
|
|
137
|
+
for (const block of getRawBlocks()) {
|
|
138
|
+
const blockRefs = collectFootnoteReferences(block.node);
|
|
139
|
+
for (const ref of blockRefs) {
|
|
140
|
+
if (!seen.has(ref)) {
|
|
141
|
+
seen.add(ref);
|
|
142
|
+
references.push(ref);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return references;
|
|
147
|
+
}
|
|
148
|
+
// 启用打字机:只有所有动画完成后才返回脚注引用
|
|
149
|
+
// 如果还有动画在进行中,返回空数组
|
|
150
|
+
if (!isAnimationComplete) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
// 所有动画完成,返回全部脚注引用
|
|
154
|
+
const references = [];
|
|
155
|
+
const seen = new Set();
|
|
156
|
+
for (const db of displayBlocks) {
|
|
157
|
+
const blockRefs = collectFootnoteReferences(db.displayNode);
|
|
158
|
+
for (const ref of blockRefs) {
|
|
159
|
+
if (!seen.has(ref)) {
|
|
160
|
+
seen.add(ref);
|
|
161
|
+
references.push(ref);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return references;
|
|
166
|
+
});
|
|
167
|
+
// 打字机控制对象
|
|
168
|
+
const typewriterControls = {
|
|
169
|
+
get enabled() { return typewriterEnabled; },
|
|
170
|
+
setEnabled: (value) => {
|
|
171
|
+
typewriterEnabled = value;
|
|
172
|
+
},
|
|
173
|
+
get isProcessing() { return isTypewriterProcessing; },
|
|
174
|
+
get isPaused() { return isTypewriterPaused; },
|
|
175
|
+
get effect() { return typewriterEffect; },
|
|
176
|
+
skip: () => transformer?.skip(),
|
|
177
|
+
pause: () => {
|
|
178
|
+
transformer?.pause();
|
|
179
|
+
isTypewriterPaused = true;
|
|
180
|
+
},
|
|
181
|
+
resume: () => {
|
|
182
|
+
transformer?.resume();
|
|
183
|
+
isTypewriterPaused = false;
|
|
184
|
+
},
|
|
185
|
+
setOptions: (opts) => {
|
|
186
|
+
if (opts.enabled !== undefined) {
|
|
187
|
+
typewriterEnabled = opts.enabled;
|
|
188
|
+
}
|
|
189
|
+
if (opts.charsPerTick !== undefined || opts.tickInterval !== undefined || opts.effect !== undefined || opts.pauseOnHidden !== undefined) {
|
|
190
|
+
transformer?.setOptions({
|
|
191
|
+
charsPerTick: opts.charsPerTick,
|
|
192
|
+
tickInterval: opts.tickInterval,
|
|
193
|
+
effect: opts.effect,
|
|
194
|
+
pauseOnHidden: opts.pauseOnHidden
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
if (opts.effect !== undefined) {
|
|
198
|
+
typewriterEffect = opts.effect;
|
|
199
|
+
}
|
|
200
|
+
if (opts.cursor !== undefined) {
|
|
201
|
+
typewriterCursor = opts.cursor;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
return {
|
|
206
|
+
getBlocks,
|
|
207
|
+
typewriter: typewriterControls,
|
|
208
|
+
transformer,
|
|
209
|
+
get isAnimationComplete() { return isAnimationComplete; },
|
|
210
|
+
get displayedFootnoteReferenceOrder() { return displayedFootnoteReferenceOrder; }
|
|
211
|
+
};
|
|
212
|
+
}
|
|
@@ -1 +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,
|
|
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,CAoC9E"}
|
package/dist/utils/cursor.js
CHANGED
|
@@ -10,8 +10,16 @@
|
|
|
10
10
|
* @returns 添加了光标的新节点
|
|
11
11
|
*/
|
|
12
12
|
export function addCursorToNode(node, cursor) {
|
|
13
|
+
// 代码块不应该添加光标(会破坏代码高亮)
|
|
14
|
+
if (node.type === 'code') {
|
|
15
|
+
return node;
|
|
16
|
+
}
|
|
13
17
|
const cloned = JSON.parse(JSON.stringify(node));
|
|
14
18
|
function addToLast(n) {
|
|
19
|
+
// 跳过代码块
|
|
20
|
+
if (n.type === 'code') {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
15
23
|
if (n.children && n.children.length > 0) {
|
|
16
24
|
for (let i = n.children.length - 1; i >= 0; i--) {
|
|
17
25
|
if (addToLast(n.children[i])) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@incremark/svelte",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "High-performance streaming markdown renderer for Svelte 5 ecosystem.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"svelte": "./dist/index.js",
|
|
@@ -23,12 +23,14 @@
|
|
|
23
23
|
"dist"
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
|
+
"@antfu/utils": "^9.3.0",
|
|
26
27
|
"shiki": "^3.20.0",
|
|
27
|
-
"
|
|
28
|
-
"@incremark/
|
|
29
|
-
"@incremark/icons": "0.3.
|
|
30
|
-
"@incremark/shared": "0.3.
|
|
31
|
-
"@incremark/theme": "0.3.
|
|
28
|
+
"shiki-stream": "^0.1.4",
|
|
29
|
+
"@incremark/core": "0.3.2",
|
|
30
|
+
"@incremark/icons": "0.3.2",
|
|
31
|
+
"@incremark/shared": "0.3.2",
|
|
32
|
+
"@incremark/theme": "0.3.2",
|
|
33
|
+
"@incremark/devtools": "0.3.2"
|
|
32
34
|
},
|
|
33
35
|
"peerDependencies": {
|
|
34
36
|
"svelte": "^5.0.0",
|
|
@@ -44,6 +46,7 @@
|
|
|
44
46
|
}
|
|
45
47
|
},
|
|
46
48
|
"devDependencies": {
|
|
49
|
+
"@shikijs/core": "^3.21.0",
|
|
47
50
|
"@types/mdast": "^4.0.0",
|
|
48
51
|
"@sveltejs/package": "^2.4.0",
|
|
49
52
|
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|