@incremark/svelte 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/components/AutoScrollContainer.svelte +2 -1
  2. package/dist/components/AutoScrollContainer.svelte.d.ts +2 -1
  3. package/dist/components/AutoScrollContainer.svelte.d.ts.map +1 -1
  4. package/dist/components/Incremark.svelte +32 -46
  5. package/dist/components/Incremark.svelte.d.ts +6 -1
  6. package/dist/components/Incremark.svelte.d.ts.map +1 -1
  7. package/dist/components/IncremarkCode.svelte +27 -2
  8. package/dist/components/IncremarkCode.svelte.d.ts +4 -0
  9. package/dist/components/IncremarkCode.svelte.d.ts.map +1 -1
  10. package/dist/components/IncremarkContainer.svelte +83 -0
  11. package/dist/components/IncremarkContainer.svelte.d.ts +22 -0
  12. package/dist/components/IncremarkContainer.svelte.d.ts.map +1 -0
  13. package/dist/components/IncremarkFootnotes.svelte +3 -12
  14. package/dist/components/IncremarkFootnotes.svelte.d.ts.map +1 -1
  15. package/dist/components/IncremarkInline.svelte +5 -22
  16. package/dist/components/IncremarkInline.svelte.d.ts.map +1 -1
  17. package/dist/components/IncremarkList.svelte +40 -15
  18. package/dist/components/IncremarkList.svelte.d.ts +1 -0
  19. package/dist/components/IncremarkList.svelte.d.ts.map +1 -1
  20. package/dist/components/IncremarkRenderer.svelte +37 -3
  21. package/dist/components/IncremarkRenderer.svelte.d.ts +5 -1
  22. package/dist/components/IncremarkRenderer.svelte.d.ts.map +1 -1
  23. package/dist/stores/useIncremark.d.ts +7 -0
  24. package/dist/stores/useIncremark.d.ts.map +1 -1
  25. package/dist/stores/useIncremark.js +6 -1
  26. package/dist/stores/useTypewriter.d.ts +2 -0
  27. package/dist/stores/useTypewriter.d.ts.map +1 -1
  28. package/dist/stores/useTypewriter.js +11 -1
  29. package/package.json +5 -5
@@ -5,11 +5,12 @@
5
5
 
6
6
  <script lang="ts">
7
7
  import { onMount, onDestroy } from 'svelte'
8
+ import type { HTMLAttributes } from 'svelte/elements';
8
9
 
9
10
  /**
10
11
  * 组件 Props
11
12
  */
12
- interface Props {
13
+ interface Props extends HTMLAttributes<HTMLDivElement>{
13
14
  /** 是否启用自动滚动 */
14
15
  enabled?: boolean
15
16
  /** 触发自动滚动的底部阈值(像素) */
@@ -1,7 +1,8 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
1
2
  /**
2
3
  * 组件 Props
3
4
  */
4
- interface Props {
5
+ interface Props extends HTMLAttributes<HTMLDivElement> {
5
6
  /** 是否启用自动滚动 */
6
7
  enabled?: boolean;
7
8
  /** 触发自动滚动的底部阈值(像素) */
@@ -1 +1 @@
1
- {"version":3,"file":"AutoScrollContainer.svelte.d.ts","sourceRoot":"","sources":["../../src/components/AutoScrollContainer.svelte.ts"],"names":[],"mappings":"AAME;;GAEG;AACH,UAAU,KAAK;IACb,eAAe;IACf,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,sBAAsB;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW;IACX,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,UAAU;IACV,QAAQ,CAAC,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAA;CACpC;AAmJH,QAAA,MAAM,mBAAmB,2CAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"AutoScrollContainer.svelte.d.ts","sourceRoot":"","sources":["../../src/components/AutoScrollContainer.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGpD;;GAEG;AACH,UAAU,KAAM,SAAQ,cAAc,CAAC,cAAc,CAAC;IACpD,eAAe;IACf,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,sBAAsB;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW;IACX,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,UAAU;IACV,QAAQ,CAAC,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAA;CACpC;AAoJH,QAAA,MAAM,mBAAmB,2CAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
@@ -4,8 +4,9 @@
4
4
  -->
5
5
 
6
6
  <script lang="ts">
7
+ import type { Component } from 'svelte'
7
8
  import type { Readable } from 'svelte/store'
8
- import type { ParsedBlock, RootContent } from '@incremark/core'
9
+ import type { RootContent } from '@incremark/core'
9
10
  import type { HTML } from 'mdast'
10
11
 
11
12
  import { getDefinitionsContext } from '../context/definitionsContext'
@@ -24,6 +25,7 @@
24
25
  import IncremarkHtmlElement from './IncremarkHtmlElement.svelte'
25
26
  import IncremarkDefault from './IncremarkDefault.svelte'
26
27
  import IncremarkFootnotes from './IncremarkFootnotes.svelte'
28
+ import IncremarkRenderer from './IncremarkRenderer.svelte'
27
29
 
28
30
  /**
29
31
  * 检查是否是 html 节点
@@ -40,6 +42,10 @@
40
42
  blocks?: BlockWithStableId[] | Readable<BlockWithStableId[]>
41
43
  /** 自定义组件映射,key 为节点类型 */
42
44
  components?: ComponentMap
45
+ /** 自定义容器组件映射,key 为容器名称(如 'warning', 'info') */
46
+ customContainers?: Record<string, Component<any>>
47
+ /** 自定义代码块组件映射,key 为代码语言名称(如 'echart', 'mermaid') */
48
+ customCodeBlocks?: Record<string, Component<any>>
43
49
  /** 待处理块的样式类名 */
44
50
  pendingClass?: string
45
51
  /** 已完成块的样式类名 */
@@ -53,28 +59,21 @@
53
59
  let {
54
60
  blocks = [],
55
61
  components = {},
62
+ customContainers = {},
63
+ customCodeBlocks = {},
56
64
  pendingClass = 'incremark-pending',
57
65
  completedClass = 'incremark-completed',
58
66
  showBlockStatus = false,
59
67
  incremark
60
68
  }: Props = $props()
61
69
 
62
- // context 获取 footnoteReferenceOrder(如果有的话)
63
- // 使用 $derived 来确保响应式
64
- const footnoteReferenceOrder = $derived.by(() => {
65
- try {
66
- const context = getDefinitionsContext()
67
- return context.footnoteReferenceOrder
68
- } catch {
69
- // 如果没有 context,返回 null
70
- return null
71
- }
72
- })
70
+ const context = getDefinitionsContext();
71
+ const footnoteReferenceOrder = $derived(context?.footnoteReferenceOrder ?? []);
73
72
 
74
- // 计算 isFinalized(当不使用 incremark 时)
75
- const actualIsFinalized = $derived.by(() => {
73
+ // 计算 isDisplayComplete(当不使用 incremark 时)
74
+ const actualIsDisplayComplete = $derived.by(() => {
76
75
  if (incremark) {
77
- // 如果提供了 incremark,在模板中直接使用 $incremark.isFinalized
76
+ // 如果提供了 incremark,在模板中直接使用 $incremark.isDisplayComplete
78
77
  return false
79
78
  }
80
79
  // 如果手动传入 blocks,自动判断是否所有 block 都是 completed
@@ -102,13 +101,6 @@
102
101
  ...components
103
102
  })
104
103
 
105
- /**
106
- * 获取组件
107
- */
108
- function getComponent(type: string): any {
109
- return mergedComponents[type] || components?.default || IncremarkDefault
110
- }
111
-
112
104
  // 处理 blocks(可能是 store 或数组)
113
105
  const blocksArray = $derived.by(() => {
114
106
  if (incremark) {
@@ -120,7 +112,7 @@
120
112
 
121
113
  // 提取 incremark 的 stores(如果存在)
122
114
  const incremarkBlocks = $derived.by(() => incremark?.blocks)
123
- const incremarkIsFinalized = $derived.by(() => incremark?.isFinalized)
115
+ const incremarkIsDisplayComplete = $derived.by(() => incremark?.isDisplayComplete)
124
116
  </script>
125
117
 
126
118
  <div class="incremark">
@@ -132,16 +124,13 @@
132
124
  <div
133
125
  class="incremark-block {block.status === 'completed' ? completedClass : pendingClass} {showBlockStatus ? 'incremark-show-status' : ''} {(block as BlockWithStableId).isLastPending ? 'incremark-last-pending' : ''}"
134
126
  >
135
- <!-- HTML 节点:渲染为代码块显示源代码 -->
136
- {#if isHtmlNode(block.node)}
137
- <pre class="incremark-html-code"><code>{block.node.value}</code></pre>
138
- {:else}
139
- <!-- 其他节点:使用对应组件 -->
140
- {@const Component = getComponent(block.node.type)}
141
- {#if Component}
142
- <Component node={block.node} />
143
- {/if}
144
- {/if}
127
+ <!-- 使用 IncremarkRenderer,传递 customContainers 和 customCodeBlocks -->
128
+ <IncremarkRenderer
129
+ node={block.node}
130
+ customContainers={customContainers}
131
+ customCodeBlocks={customCodeBlocks}
132
+ blockStatus={block.status}
133
+ />
145
134
  </div>
146
135
  {/if}
147
136
  {/each}
@@ -152,28 +141,25 @@
152
141
  <div
153
142
  class="incremark-block {block.status === 'completed' ? completedClass : pendingClass} {showBlockStatus ? 'incremark-show-status' : ''} {block.isLastPending ? 'incremark-last-pending' : ''}"
154
143
  >
155
- <!-- HTML 节点:渲染为代码块显示源代码 -->
156
- {#if isHtmlNode(block.node)}
157
- <pre class="incremark-html-code"><code>{block.node.value}</code></pre>
158
- {:else}
159
- <!-- 其他节点:使用对应组件 -->
160
- {@const Component = getComponent(block.node.type)}
161
- {#if Component}
162
- <Component node={block.node} />
163
- {/if}
164
- {/if}
144
+ <!-- 使用 IncremarkRenderer,传递 customContainers 和 customCodeBlocks -->
145
+ <IncremarkRenderer
146
+ node={block.node}
147
+ customContainers={customContainers}
148
+ customCodeBlocks={customCodeBlocks}
149
+ blockStatus={block.status}
150
+ />
165
151
  </div>
166
152
  {/if}
167
153
  {/each}
168
154
  {/if}
169
155
 
170
- <!-- 脚注列表(仅在 finalize 后显示) -->
171
- {#if incremark && incremarkIsFinalized && footnoteReferenceOrder && $incremarkIsFinalized}
156
+ <!-- 脚注列表(仅在内容完全显示后显示) -->
157
+ {#if incremark && incremarkIsDisplayComplete && footnoteReferenceOrder && $incremarkIsDisplayComplete}
172
158
  {@const footnoteOrder = $footnoteReferenceOrder ?? []}
173
159
  {#if footnoteOrder.length > 0}
174
160
  <IncremarkFootnotes />
175
161
  {/if}
176
- {:else if !incremark && actualIsFinalized && footnoteReferenceOrder}
162
+ {:else if !incremark && actualIsDisplayComplete && footnoteReferenceOrder}
177
163
  {@const footnoteOrder = $footnoteReferenceOrder ?? []}
178
164
  {#if footnoteOrder.length > 0}
179
165
  <IncremarkFootnotes />
@@ -1,3 +1,4 @@
1
+ import type { Component } from 'svelte';
1
2
  import type { Readable } from 'svelte/store';
2
3
  import type { UseIncremarkReturn } from '../stores/useIncremark';
3
4
  import type { ComponentMap, BlockWithStableId } from './types';
@@ -9,6 +10,10 @@ interface Props {
9
10
  blocks?: BlockWithStableId[] | Readable<BlockWithStableId[]>;
10
11
  /** 自定义组件映射,key 为节点类型 */
11
12
  components?: ComponentMap;
13
+ /** 自定义容器组件映射,key 为容器名称(如 'warning', 'info') */
14
+ customContainers?: Record<string, Component<any>>;
15
+ /** 自定义代码块组件映射,key 为代码语言名称(如 'echart', 'mermaid') */
16
+ customCodeBlocks?: Record<string, Component<any>>;
12
17
  /** 待处理块的样式类名 */
13
18
  pendingClass?: string;
14
19
  /** 已完成块的样式类名 */
@@ -18,7 +23,7 @@ interface Props {
18
23
  /** 可选:useIncremark 返回的对象(用于自动注入数据) */
19
24
  incremark?: UseIncremarkReturn;
20
25
  }
21
- declare const Incremark: import("svelte").Component<Props, {}, "">;
26
+ declare const Incremark: Component<Props, {}, "">;
22
27
  type Incremark = ReturnType<typeof Incremark>;
23
28
  export default Incremark;
24
29
  //# sourceMappingURL=Incremark.svelte.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Incremark.svelte.d.ts","sourceRoot":"","sources":["../../src/components/Incremark.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAK5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAe5D;;GAEG;AACH,UAAU,KAAK;IACb,wCAAwC;IACxC,MAAM,CAAC,EAAE,iBAAiB,EAAE,GAAG,QAAQ,CAAC,iBAAiB,EAAE,CAAC,CAAA;IAC5D,wBAAwB;IACxB,UAAU,CAAC,EAAE,YAAY,CAAA;IACzB,gBAAgB;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB;IAChB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,gBAAgB;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,sCAAsC;IACtC,SAAS,CAAC,EAAE,kBAAkB,CAAA;CAC/B;AAmKH,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"Incremark.svelte.d.ts","sourceRoot":"","sources":["../../src/components/Incremark.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AACvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAK5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAgB5D;;GAEG;AACH,UAAU,KAAK;IACb,wCAAwC;IACxC,MAAM,CAAC,EAAE,iBAAiB,EAAE,GAAG,QAAQ,CAAC,iBAAiB,EAAE,CAAC,CAAA;IAC5D,wBAAwB;IACxB,UAAU,CAAC,EAAE,YAAY,CAAA;IACzB,+CAA+C;IAC/C,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAA;IACjD,oDAAoD;IACpD,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAA;IACjD,gBAAgB;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB;IAChB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,gBAAgB;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,sCAAsC;IACtC,SAAS,CAAC,EAAE,kBAAkB,CAAA;CAC/B;AAuIH,QAAA,MAAM,SAAS,0BAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -19,13 +19,19 @@
19
19
  disableHighlight?: boolean
20
20
  /** Mermaid 渲染延迟(毫秒),用于流式输入时防抖 */
21
21
  mermaidDelay?: number
22
+ /** 自定义代码块组件映射,key 为代码语言名称 */
23
+ customCodeBlocks?: Record<string, any>
24
+ /** 块状态,用于判断是否使用自定义组件 */
25
+ blockStatus?: 'pending' | 'stable' | 'completed'
22
26
  }
23
27
 
24
28
  let {
25
29
  node,
26
30
  theme = 'github-dark',
27
31
  disableHighlight = false,
28
- mermaidDelay = 500
32
+ mermaidDelay = 500,
33
+ customCodeBlocks,
34
+ blockStatus = 'completed'
29
35
  }: Props = $props()
30
36
 
31
37
  // 状态
@@ -55,6 +61,18 @@
55
61
  const code = $derived(node.value)
56
62
  const isMermaid = $derived(language === 'mermaid')
57
63
 
64
+ // 检查是否有自定义代码块组件
65
+ const CustomCodeBlock = $derived.by(() => {
66
+ // 如果代码块还在 pending 状态,不使用自定义组件
67
+ if (blockStatus === 'pending') {
68
+ return null
69
+ }
70
+ return customCodeBlocks?.[language] || null
71
+ })
72
+
73
+ // 是否使用自定义组件
74
+ const useCustomComponent = $derived(!!CustomCodeBlock)
75
+
58
76
  /**
59
77
  * 切换 Mermaid 视图模式
60
78
  */
@@ -210,8 +228,15 @@
210
228
  })
211
229
  </script>
212
230
 
231
+ <!-- 自定义代码块组件 -->
232
+ {#if useCustomComponent && CustomCodeBlock}
233
+ <svelte:component
234
+ this={CustomCodeBlock}
235
+ codeStr={code}
236
+ lang={language}
237
+ />
213
238
  <!-- Mermaid 图表 -->
214
- {#if isMermaid}
239
+ {:else if isMermaid}
215
240
  <div class="incremark-mermaid">
216
241
  <div class="mermaid-header">
217
242
  <span class="language">MERMAID</span>
@@ -11,6 +11,10 @@ interface Props {
11
11
  disableHighlight?: boolean;
12
12
  /** Mermaid 渲染延迟(毫秒),用于流式输入时防抖 */
13
13
  mermaidDelay?: number;
14
+ /** 自定义代码块组件映射,key 为代码语言名称 */
15
+ customCodeBlocks?: Record<string, any>;
16
+ /** 块状态,用于判断是否使用自定义组件 */
17
+ blockStatus?: 'pending' | 'stable' | 'completed';
14
18
  }
15
19
  declare const IncremarkCode: import("svelte").Component<Props, {}, "">;
16
20
  type IncremarkCode = ReturnType<typeof IncremarkCode>;
@@ -1 +1 @@
1
- {"version":3,"file":"IncremarkCode.svelte.d.ts","sourceRoot":"","sources":["../../src/components/IncremarkCode.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAI/B;;GAEG;AACH,UAAU,KAAK;IACb,WAAW;IACX,IAAI,EAAE,IAAI,CAAA;IACV,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,eAAe;IACf,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,iCAAiC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAkQH,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"IncremarkCode.svelte.d.ts","sourceRoot":"","sources":["../../src/components/IncremarkCode.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAI/B;;GAEG;AACH,UAAU,KAAK;IACb,WAAW;IACX,IAAI,EAAE,IAAI,CAAA;IACV,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,eAAe;IACf,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,iCAAiC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6BAA6B;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACtC,wBAAwB;IACxB,WAAW,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAA;CACjD;AAmRH,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -0,0 +1,83 @@
1
+ <!--
2
+ @file IncremarkContainer.svelte - 容器组件
3
+ @description 用于渲染自定义容器节点
4
+ -->
5
+
6
+ <script lang="ts">
7
+ import type { RootContent } from 'mdast'
8
+ import IncremarkRenderer from './IncremarkRenderer.svelte'
9
+
10
+ /**
11
+ * 容器节点类型定义
12
+ * 根据 directive 解析后的结构
13
+ */
14
+ export interface ContainerNode {
15
+ type: 'containerDirective' | 'leafDirective' | 'textDirective'
16
+ name: string
17
+ attributes?: Record<string, string>
18
+ children?: RootContent[]
19
+ }
20
+
21
+ /**
22
+ * 组件 Props
23
+ */
24
+ interface Props {
25
+ node: ContainerNode
26
+ customContainers?: Record<string, any>
27
+ }
28
+
29
+ let { node, customContainers = {} }: Props = $props()
30
+
31
+ /**
32
+ * 解析 attributes 字符串为对象
33
+ * directive 的 attributes 可能是字符串格式,需要解析
34
+ */
35
+ function parseOptions(attributes?: Record<string, string>): Record<string, any> {
36
+ if (!attributes) return {}
37
+
38
+ const options: Record<string, any> = {}
39
+ for (const [key, value] of Object.entries(attributes)) {
40
+ // 尝试解析 JSON 值
41
+ try {
42
+ options[key] = JSON.parse(value)
43
+ } catch {
44
+ // 如果不是 JSON,直接使用字符串值
45
+ options[key] = value
46
+ }
47
+ }
48
+ return options
49
+ }
50
+
51
+ const containerName = $derived(node.name)
52
+ const options = $derived(parseOptions(node.attributes))
53
+ const CustomContainer = $derived(customContainers?.[containerName])
54
+
55
+ // 如果没有自定义容器组件,使用默认渲染
56
+ const hasCustomContainer = $derived(!!CustomContainer)
57
+ </script>
58
+
59
+ {#if hasCustomContainer && CustomContainer}
60
+ <!-- 如果有自定义容器组件,使用自定义组件 -->
61
+ <CustomContainer
62
+ name={containerName}
63
+ options={options}
64
+ >
65
+ {#if node.children && node.children.length > 0}
66
+ {#each node.children as child, index (index)}
67
+ <IncremarkRenderer node={child} />
68
+ {/each}
69
+ {/if}
70
+ </CustomContainer>
71
+ {:else}
72
+ <!-- 如果没有自定义容器组件,使用默认渲染 -->
73
+ <div class="incremark-container incremark-container-{containerName}">
74
+ {#if node.children && node.children.length > 0}
75
+ <div class="incremark-container-content">
76
+ {#each node.children as child, index (index)}
77
+ <IncremarkRenderer node={child} />
78
+ {/each}
79
+ </div>
80
+ {/if}
81
+ </div>
82
+ {/if}
83
+
@@ -0,0 +1,22 @@
1
+ import type { RootContent } from 'mdast';
2
+ /**
3
+ * 容器节点类型定义
4
+ * 根据 directive 解析后的结构
5
+ */
6
+ export interface ContainerNode {
7
+ type: 'containerDirective' | 'leafDirective' | 'textDirective';
8
+ name: string;
9
+ attributes?: Record<string, string>;
10
+ children?: RootContent[];
11
+ }
12
+ /**
13
+ * 组件 Props
14
+ */
15
+ interface Props {
16
+ node: ContainerNode;
17
+ customContainers?: Record<string, any>;
18
+ }
19
+ declare const IncremarkContainer: import("svelte").Component<Props, {}, "">;
20
+ type IncremarkContainer = ReturnType<typeof IncremarkContainer>;
21
+ export default IncremarkContainer;
22
+ //# sourceMappingURL=IncremarkContainer.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IncremarkContainer.svelte.d.ts","sourceRoot":"","sources":["../../src/components/IncremarkContainer.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAItC;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,oBAAoB,GAAG,eAAe,GAAG,eAAe,CAAA;IAC9D,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAA;CACzB;AAED;;GAEG;AACH,UAAU,KAAK;IACb,IAAI,EAAE,aAAa,CAAA;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CACvC;AAgEH,QAAA,MAAM,kBAAkB,2CAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
@@ -9,19 +9,10 @@
9
9
  import { getDefinitionsContext } from '../context/definitionsContext'
10
10
  import IncremarkRenderer from './IncremarkRenderer.svelte'
11
11
 
12
- // 从 context 获取数据
13
- let definations: Readable<Record<string, any>> | null = null
14
- let footnoteDefinitions: Readable<Record<string, FootnoteDefinition>> | null = null
15
- let footnoteReferenceOrder: Readable<string[]> | null = null
16
12
 
17
- try {
18
- const context = getDefinitionsContext()
19
- definations = context.definations
20
- footnoteDefinitions = context.footnoteDefinitions
21
- footnoteReferenceOrder = context.footnoteReferenceOrder
22
- } catch {
23
- // Context 不存在
24
- }
13
+ const context = getDefinitionsContext()
14
+ const footnoteDefinitions = $derived(context?.footnoteDefinitions ?? {});
15
+ const footnoteReferenceOrder = $derived(context?.footnoteReferenceOrder ?? []);
25
16
 
26
17
  /**
27
18
  * 按引用顺序排列的脚注列表
@@ -1 +1 @@
1
- {"version":3,"file":"IncremarkFootnotes.svelte.d.ts","sourceRoot":"","sources":["../../src/components/IncremarkFootnotes.svelte.ts"],"names":[],"mappings":"AAqFA,QAAA,MAAM,kBAAkB,2DAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"IncremarkFootnotes.svelte.d.ts","sourceRoot":"","sources":["../../src/components/IncremarkFootnotes.svelte.ts"],"names":[],"mappings":"AA4EA,QAAA,MAAM,kBAAkB,2DAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
@@ -4,7 +4,6 @@
4
4
  -->
5
5
 
6
6
  <script lang="ts">
7
- import type { Readable } from 'svelte/store'
8
7
  import type { PhrasingContent, ImageReference, LinkReference } from 'mdast'
9
8
  import type { TextChunk } from '@incremark/core'
10
9
  import {
@@ -72,25 +71,9 @@
72
71
 
73
72
  // 获取 definitions context(可能不存在)
74
73
  // 使用 $derived 来确保响应式
75
- const definations = $derived.by(() => {
76
- try {
77
- const context = getDefinitionsContext()
78
- return context.definations
79
- } catch {
80
- // Context 不存在,返回 null
81
- return null
82
- }
83
- })
84
-
85
- const footnoteDefinitions = $derived.by(() => {
86
- try {
87
- const context = getDefinitionsContext()
88
- return context.footnoteDefinitions
89
- } catch {
90
- // Context 不存在,返回 null
91
- return null
92
- }
93
- })
74
+ const context = getDefinitionsContext()
75
+ const definations = $derived(context?.definations ?? {});
76
+
94
77
 
95
78
  /**
96
79
  * 获取节点的 chunks(类型安全)
@@ -173,7 +156,7 @@
173
156
 
174
157
  <!-- 引用式图片(imageReference) -->
175
158
  {#if isImageReference(node)}
176
- {#if definations && $definations[node.identifier]}
159
+ {#if $definations && $definations[node.identifier]}
177
160
  <img
178
161
  class="incremark-image incremark-reference-image"
179
162
  src={$definations[node.identifier].url}
@@ -191,7 +174,7 @@
191
174
 
192
175
  <!-- 引用式链接(linkReference) -->
193
176
  {#if isLinkReference(node)}
194
- {#if definations && $definations[node.identifier]}
177
+ {#if $definations && $definations[node.identifier]}
195
178
  <a
196
179
  class="incremark-link incremark-reference-link"
197
180
  href={$definations[node.identifier].url}
@@ -1 +1 @@
1
- {"version":3,"file":"IncremarkInline.svelte.d.ts","sourceRoot":"","sources":["../../src/components/IncremarkInline.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAiC,MAAM,OAAO,CAAA;AAW3E,OAAO,eAAe,MAAM,0BAA0B,CAAC;AAGrD;;GAEG;AACH,UAAU,KAAK;IACb,aAAa;IACb,KAAK,EAAE,eAAe,EAAE,CAAA;CACzB;AAyMH,QAAA,MAAM,eAAe,2CAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"IncremarkInline.svelte.d.ts","sourceRoot":"","sources":["../../src/components/IncremarkInline.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAiC,MAAM,OAAO,CAAA;AAW3E,OAAO,eAAe,MAAM,0BAA0B,CAAC;AAGrD;;GAEG;AACH,UAAU,KAAK;IACb,aAAa;IACb,KAAK,EAAE,eAAe,EAAE,CAAA;CACzB;AAwLH,QAAA,MAAM,eAAe,2CAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
@@ -1,11 +1,12 @@
1
1
  <!--
2
2
  @file IncremarkList.svelte - 列表组件
3
- @description 渲染 Markdown 列表(有序列表和无序列表),支持任务列表
3
+ @description 渲染 Markdown 列表(有序列表和无序列表),支持任务列表和嵌套列表
4
4
  -->
5
5
 
6
6
  <script lang="ts">
7
- import type { List, ListItem, PhrasingContent } from 'mdast'
7
+ import type { List, ListItem, PhrasingContent, BlockContent } from 'mdast'
8
8
  import IncremarkInline from './IncremarkInline.svelte'
9
+ import IncremarkList from './IncremarkList.svelte';
9
10
 
10
11
  /**
11
12
  * 组件 Props
@@ -30,12 +31,12 @@
30
31
  )
31
32
 
32
33
  /**
33
- * 获取列表项内容
34
- *
34
+ * 获取列表项的内联内容(来自第一个 paragraph)
35
+ *
35
36
  * @param item - 列表项节点
36
37
  * @returns 行内内容数组
37
38
  */
38
- function getItemContent(item: ListItem): PhrasingContent[] {
39
+ function getItemInlineContent(item: ListItem): PhrasingContent[] {
39
40
  const firstChild = item.children[0]
40
41
  if (firstChild?.type === 'paragraph') {
41
42
  return firstChild.children as PhrasingContent[]
@@ -43,9 +44,26 @@
43
44
  return []
44
45
  }
45
46
 
47
+ /**
48
+ * 获取列表项的块级子节点(嵌套列表、代码块等)
49
+ * 排除第一个 paragraph,因为它已经被 getItemInlineContent 处理
50
+ *
51
+ * @param item - 列表项节点
52
+ * @returns 块级子节点数组
53
+ */
54
+ function getItemBlockChildren(item: ListItem): BlockContent[] {
55
+ return item.children.filter((child, index) => {
56
+ // 第一个 paragraph 已经被处理为内联内容
57
+ if (index === 0 && child.type === 'paragraph') {
58
+ return false
59
+ }
60
+ return true
61
+ }) as BlockContent[]
62
+ }
63
+
46
64
  /**
47
65
  * 判断列表项是否是任务项
48
- *
66
+ *
49
67
  * @param item - 列表项节点
50
68
  * @returns 是否是任务项
51
69
  */
@@ -54,30 +72,37 @@
54
72
  }
55
73
  </script>
56
74
 
57
- <svelte:element
58
- this={tag}
75
+ <svelte:element
76
+ this={tag}
59
77
  class="incremark-list"
60
78
  class:task-list={isTaskList}
61
79
  >
62
80
  {#each node.children as item, index (index)}
63
- <li
81
+ <li
64
82
  class="incremark-list-item"
65
83
  class:task-item={isTaskItem(item)}
66
84
  >
67
85
  {#if isTaskItem(item)}
68
86
  <label class="task-label">
69
- <input
70
- type="checkbox"
71
- checked={item.checked}
72
- disabled
87
+ <input
88
+ type="checkbox"
89
+ checked={item.checked}
90
+ disabled
73
91
  class="checkbox"
74
92
  />
75
93
  <span class="task-content">
76
- <IncremarkInline nodes={getItemContent(item)} />
94
+ <IncremarkInline nodes={getItemInlineContent(item)} />
77
95
  </span>
78
96
  </label>
79
97
  {:else}
80
- <IncremarkInline nodes={getItemContent(item)} />
98
+ <IncremarkInline nodes={getItemInlineContent(item)} />
99
+ <!-- 递归渲染嵌套列表和其他块级内容 -->
100
+ {#each getItemBlockChildren(item) as child, childIndex (childIndex)}
101
+ {#if child.type === 'list'}
102
+ <IncremarkList node={child} />
103
+ {/if}
104
+ <!-- 其他块级内容可以在这里扩展 -->
105
+ {/each}
81
106
  {/if}
82
107
  </li>
83
108
  {/each}
@@ -1,4 +1,5 @@
1
1
  import type { List } from 'mdast';
2
+ import IncremarkList from './IncremarkList.svelte';
2
3
  /**
3
4
  * 组件 Props
4
5
  */
@@ -1 +1 @@
1
- {"version":3,"file":"IncremarkList.svelte.d.ts","sourceRoot":"","sources":["../../src/components/IncremarkList.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAA6B,MAAM,OAAO,CAAA;AAI1D;;GAEG;AACH,UAAU,KAAK;IACb,WAAW;IACX,IAAI,EAAE,IAAI,CAAA;CACX;AAmEH,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"IncremarkList.svelte.d.ts","sourceRoot":"","sources":["../../src/components/IncremarkList.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAA2C,MAAM,OAAO,CAAA;AAE1E,OAAO,aAAa,MAAM,wBAAwB,CAAC;AAGjD;;GAEG;AACH,UAAU,KAAK;IACb,WAAW;IACX,IAAI,EAAE,IAAI,CAAA;CACX;AA4FH,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -15,16 +15,25 @@
15
15
  import IncremarkMath from './IncremarkMath.svelte'
16
16
  import IncremarkHtmlElement from './IncremarkHtmlElement.svelte'
17
17
  import IncremarkDefault from './IncremarkDefault.svelte'
18
+ import IncremarkContainer, { type ContainerNode } from './IncremarkContainer.svelte'
18
19
 
19
20
  /**
20
21
  * 组件 Props
21
22
  */
22
23
  interface Props {
23
24
  /** 要渲染的节点 */
24
- node: RootContent
25
+ node: RootContent | ContainerNode
26
+ customContainers?: Record<string, any>
27
+ customCodeBlocks?: Record<string, any>
28
+ blockStatus?: 'pending' | 'stable' | 'completed'
25
29
  }
26
30
 
27
- let { node }: Props = $props()
31
+ let {
32
+ node,
33
+ customContainers,
34
+ customCodeBlocks,
35
+ blockStatus
36
+ }: Props = $props()
28
37
 
29
38
  /**
30
39
  * 组件映射
@@ -52,14 +61,39 @@
52
61
  /**
53
62
  * 检查是否是 html 节点
54
63
  */
55
- function isHtmlNode(node: RootContent): node is HTML {
64
+ function isHtmlNode(node: RootContent | ContainerNode): node is HTML {
56
65
  return node.type === 'html'
57
66
  }
67
+
68
+ /**
69
+ * 检查是否是容器节点
70
+ */
71
+ function isContainerNode(node: RootContent | ContainerNode): node is ContainerNode {
72
+ return (node as any).type === 'containerDirective' ||
73
+ (node as any).type === 'leafDirective' ||
74
+ (node as any).type === 'textDirective'
75
+ }
58
76
  </script>
59
77
 
60
78
  <!-- HTML 节点:渲染为代码块显示源代码 -->
61
79
  {#if isHtmlNode(node)}
62
80
  <pre class="incremark-html-code"><code>{node.value}</code></pre>
81
+ <!-- 容器节点:使用容器组件,传递 customContainers -->
82
+ {:else if isContainerNode(node)}
83
+ <IncremarkContainer
84
+ node={node}
85
+ customContainers={customContainers}
86
+ />
87
+ <!-- 代码节点:特殊处理,传递 customCodeBlocks 和 blockStatus -->
88
+ {:else if node.type === 'code'}
89
+ {@const Component = getComponent('code')}
90
+ {#if Component}
91
+ <Component
92
+ node={node}
93
+ customCodeBlocks={customCodeBlocks}
94
+ blockStatus={blockStatus}
95
+ />
96
+ {/if}
63
97
  {:else}
64
98
  <!-- 其他节点:使用对应组件 -->
65
99
  {@const Component = getComponent(node.type)}
@@ -1,10 +1,14 @@
1
1
  import type { RootContent } from 'mdast';
2
+ import { type ContainerNode } from './IncremarkContainer.svelte';
2
3
  /**
3
4
  * 组件 Props
4
5
  */
5
6
  interface Props {
6
7
  /** 要渲染的节点 */
7
- node: RootContent;
8
+ node: RootContent | ContainerNode;
9
+ customContainers?: Record<string, any>;
10
+ customCodeBlocks?: Record<string, any>;
11
+ blockStatus?: 'pending' | 'stable' | 'completed';
8
12
  }
9
13
  declare const IncremarkRenderer: import("svelte").Component<Props, {}, "">;
10
14
  type IncremarkRenderer = ReturnType<typeof IncremarkRenderer>;
@@ -1 +1 @@
1
- {"version":3,"file":"IncremarkRenderer.svelte.d.ts","sourceRoot":"","sources":["../../src/components/IncremarkRenderer.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAQ,MAAM,OAAO,CAAA;AAa5C;;GAEG;AACH,UAAU,KAAK;IACb,aAAa;IACb,IAAI,EAAE,WAAW,CAAA;CAClB;AAgEH,QAAA,MAAM,iBAAiB,2CAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"IncremarkRenderer.svelte.d.ts","sourceRoot":"","sources":["../../src/components/IncremarkRenderer.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAQ,MAAM,OAAO,CAAA;AAW9C,OAA2B,EAAE,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAGnF;;GAEG;AACH,UAAU,KAAK;IACb,aAAa;IACb,IAAI,EAAE,WAAW,GAAG,aAAa,CAAA;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACtC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACtC,WAAW,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAA;CACjD;AAwFH,QAAA,MAAM,iBAAiB,2CAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
@@ -73,6 +73,13 @@ export interface UseIncremarkReturn {
73
73
  isLoading: Writable<boolean>;
74
74
  /** 是否已完成(finalize) */
75
75
  isFinalized: Writable<boolean>;
76
+ /**
77
+ * 内容是否完全显示完成
78
+ * - 无打字机:等于 isFinalized
79
+ * - 有打字机:isFinalized + 动画播放完成
80
+ * 适用于控制 footnote 等需要在内容完全显示后才出现的元素
81
+ */
82
+ isDisplayComplete: Readable<boolean>;
76
83
  /** 脚注引用的出现顺序 */
77
84
  footnoteReferenceOrder: Writable<string[]>;
78
85
  /** 追加内容 */
@@ -1 +1 @@
1
- {"version":3,"file":"useIncremark.d.ts","sourceRoot":"","sources":["../../src/stores/useIncremark.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useIncremark.d.ts","sourceRoot":"","sources":["../../src/stores/useIncremark.ts"],"names":[],"mappings":"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"}
@@ -61,11 +61,15 @@ export function useIncremark(options = {}) {
61
61
  const isFinalized = writable(false);
62
62
  const footnoteReferenceOrder = writable([]);
63
63
  // 使用 useTypewriter store 管理打字机效果
64
- const { blocks, typewriter, transformer } = useTypewriter({
64
+ const { blocks, typewriter, transformer, isAnimationComplete } = useTypewriter({
65
65
  typewriter: options.typewriter,
66
66
  completedBlocks,
67
67
  pendingBlocks
68
68
  });
69
+ // 内容是否完全显示完成
70
+ // 如果没有启用打字机:解析完成即显示完成
71
+ // 如果启用打字机:解析完成 + 动画完成
72
+ const isDisplayComplete = derived([isFinalized, isAnimationComplete], ([$isFinalized, $isAnimationComplete]) => $isFinalized && $isAnimationComplete);
69
73
  // AST
70
74
  const ast = derived([completedBlocks, pendingBlocks], ([$completedBlocks, $pendingBlocks]) => ({
71
75
  type: 'root',
@@ -165,6 +169,7 @@ export function useIncremark(options = {}) {
165
169
  blocks,
166
170
  isLoading,
167
171
  isFinalized,
172
+ isDisplayComplete,
168
173
  footnoteReferenceOrder,
169
174
  append,
170
175
  finalize,
@@ -28,6 +28,8 @@ export interface UseTypewriterReturn {
28
28
  typewriter: TypewriterControls;
29
29
  /** transformer 实例 */
30
30
  transformer: BlockTransformer<RootContent> | null;
31
+ /** 所有动画是否已完成(队列为空且没有正在处理的 block) */
32
+ isAnimationComplete: Readable<boolean>;
31
33
  }
32
34
  /**
33
35
  * useTypewriter Store
@@ -1 +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"}
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;IACjD,oCAAoC;IACpC,mBAAmB,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;CACvC;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CA4KhF"}
@@ -23,6 +23,7 @@ export function useTypewriter(options) {
23
23
  const isTypewriterPaused = writable(false);
24
24
  const typewriterEffect = writable(typewriterConfig?.effect ?? 'none');
25
25
  const typewriterCursor = writable(typewriterConfig?.cursor ?? '|');
26
+ const isAnimationComplete = writable(true); // 初始为 true(没有动画时视为完成)
26
27
  // 创建 transformer(如果有 typewriter 配置)
27
28
  let transformer = null;
28
29
  if (typewriterConfig) {
@@ -37,6 +38,14 @@ export function useTypewriter(options) {
37
38
  displayBlocks.set(blocks);
38
39
  isTypewriterProcessing.set(transformer?.isProcessing() ?? false);
39
40
  isTypewriterPaused.set(transformer?.isPausedState() ?? false);
41
+ // 有 blocks 正在处理时,动画未完成
42
+ if (transformer?.isProcessing()) {
43
+ isAnimationComplete.set(false);
44
+ }
45
+ },
46
+ onAllComplete: () => {
47
+ // 所有动画完成
48
+ isAnimationComplete.set(true);
40
49
  }
41
50
  });
42
51
  }
@@ -148,6 +157,7 @@ export function useTypewriter(options) {
148
157
  return {
149
158
  blocks,
150
159
  typewriter: typewriterControls,
151
- transformer
160
+ transformer,
161
+ isAnimationComplete: derived(isAnimationComplete, ($v) => $v)
152
162
  };
153
163
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@incremark/svelte",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "Incremark Svelte 5 集成",
5
5
  "type": "module",
6
6
  "svelte": "./dist/index.js",
@@ -24,10 +24,10 @@
24
24
  ],
25
25
  "dependencies": {
26
26
  "shiki": "^3.20.0",
27
- "@incremark/core": "0.2.3",
28
- "@incremark/theme": "0.2.3",
29
- "@incremark/shared": "0.2.3",
30
- "@incremark/devtools": "0.2.3"
27
+ "@incremark/core": "0.2.5",
28
+ "@incremark/devtools": "0.2.5",
29
+ "@incremark/shared": "0.2.5",
30
+ "@incremark/theme": "0.2.5"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "svelte": "^5.0.0",