@incremark/svelte 0.2.6 → 0.3.0
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/README.en.md +274 -0
- package/dist/ThemeProvider.svelte +4 -1
- package/dist/ThemeProvider.svelte.d.ts.map +1 -1
- package/dist/components/AutoScrollContainer.svelte +18 -18
- package/dist/components/ConfigProvider.svelte +18 -0
- package/dist/components/ConfigProvider.svelte.d.ts +9 -0
- package/dist/components/ConfigProvider.svelte.d.ts.map +1 -0
- package/dist/components/Incremark.svelte +63 -72
- package/dist/components/Incremark.svelte.d.ts +5 -7
- package/dist/components/Incremark.svelte.d.ts.map +1 -1
- package/dist/components/IncremarkCode.svelte +28 -251
- package/dist/components/IncremarkCode.svelte.d.ts +4 -1
- package/dist/components/IncremarkCode.svelte.d.ts.map +1 -1
- package/dist/components/IncremarkCodeDefault.svelte +115 -0
- package/dist/components/IncremarkCodeDefault.svelte.d.ts +18 -0
- package/dist/components/IncremarkCodeDefault.svelte.d.ts.map +1 -0
- package/dist/components/IncremarkCodeMermaid.svelte +148 -0
- package/dist/components/IncremarkCodeMermaid.svelte.d.ts +14 -0
- package/dist/components/IncremarkCodeMermaid.svelte.d.ts.map +1 -0
- package/dist/components/IncremarkContent.svelte +115 -0
- package/dist/components/IncremarkContent.svelte.d.ts +5 -0
- package/dist/components/IncremarkContent.svelte.d.ts.map +1 -0
- package/dist/components/IncremarkFootnotes.svelte +4 -8
- package/dist/components/IncremarkFootnotes.svelte.d.ts.map +1 -1
- package/dist/components/IncremarkInline.svelte +3 -3
- package/dist/components/IncremarkList.svelte +1 -0
- package/dist/components/IncremarkRenderer.svelte +4 -0
- package/dist/components/IncremarkRenderer.svelte.d.ts +2 -0
- package/dist/components/IncremarkRenderer.svelte.d.ts.map +1 -1
- package/dist/components/IncremarkTable.svelte +6 -9
- package/dist/components/IncremarkTable.svelte.d.ts.map +1 -1
- package/dist/components/SvgIcon.svelte +25 -0
- package/dist/components/SvgIcon.svelte.d.ts +12 -0
- package/dist/components/SvgIcon.svelte.d.ts.map +1 -0
- package/dist/components/index.d.ts +2 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -0
- package/dist/components/types.d.ts +29 -3
- package/dist/components/types.d.ts.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/stores/{useDevTools.d.ts → useDevTools.svelte.d.ts} +1 -1
- package/dist/stores/useDevTools.svelte.d.ts.map +1 -0
- package/dist/stores/{useDevTools.js → useDevTools.svelte.js} +10 -9
- package/dist/stores/useIncremark.d.ts +4 -4
- package/dist/stores/useIncremark.d.ts.map +1 -1
- package/dist/stores/useLocale.svelte.d.ts +29 -0
- package/dist/stores/useLocale.svelte.d.ts.map +1 -0
- package/dist/stores/useLocale.svelte.js +32 -0
- package/dist/stores/useShiki.svelte.d.ts +9 -0
- package/dist/stores/useShiki.svelte.d.ts.map +1 -0
- package/dist/stores/useShiki.svelte.js +110 -0
- package/dist/stores/useTypewriter.d.ts +0 -1
- package/dist/stores/useTypewriter.d.ts.map +1 -1
- package/dist/stores/useTypewriter.js +1 -3
- package/package.json +13 -7
- package/dist/stores/useDevTools.d.ts.map +0 -1
package/README.en.md
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# @incremark/svelte
|
|
2
|
+
|
|
3
|
+
Svelte 5 integration library for Incremark, providing high-performance streaming Markdown rendering components.
|
|
4
|
+
|
|
5
|
+
**[🇨🇳 中文](./README.md)** | 🇺🇸 English
|
|
6
|
+
|
|
7
|
+
## Core Advantages
|
|
8
|
+
|
|
9
|
+
- 📦 **Out of the Box** - Provides `IncremarkContent` component and `useIncremark` store
|
|
10
|
+
- ⚡ **Extreme Performance** - Incremental parsing with O(n) complexity, dual-engine support
|
|
11
|
+
- ⌨️ **Typewriter Effect** - Built-in animation effects (fade-in, typing)
|
|
12
|
+
- 🎨 **Highly Customizable** - Custom components, code blocks, containers
|
|
13
|
+
- 🎯 **Svelte 5 Runes** - Uses the latest Svelte 5 syntax
|
|
14
|
+
- 📜 **Auto Scroll** - Built-in AutoScrollContainer component
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add @incremark/core @incremark/svelte
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
### Recommended: IncremarkContent Component
|
|
25
|
+
|
|
26
|
+
```svelte
|
|
27
|
+
<script lang="ts">
|
|
28
|
+
import { IncremarkContent } from '@incremark/svelte'
|
|
29
|
+
import '@incremark/svelte/style.css'
|
|
30
|
+
|
|
31
|
+
let content = $state('')
|
|
32
|
+
let isFinished = $state(false)
|
|
33
|
+
|
|
34
|
+
// Handle AI streaming output
|
|
35
|
+
async function handleStream(stream: AsyncIterable<string>) {
|
|
36
|
+
content = ''
|
|
37
|
+
isFinished = false
|
|
38
|
+
|
|
39
|
+
for await (const chunk of stream) {
|
|
40
|
+
content += chunk
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
isFinished = true
|
|
44
|
+
}
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<button onclick={() => handleStream(stream)}>Start</button>
|
|
48
|
+
<IncremarkContent
|
|
49
|
+
{content}
|
|
50
|
+
{isFinished}
|
|
51
|
+
incremarkOptions={{
|
|
52
|
+
gfm: true,
|
|
53
|
+
math: true,
|
|
54
|
+
containers: true,
|
|
55
|
+
htmlTree: true
|
|
56
|
+
}}
|
|
57
|
+
/>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Advanced: useIncremark Store
|
|
61
|
+
|
|
62
|
+
```svelte
|
|
63
|
+
<script lang="ts">
|
|
64
|
+
import { useIncremark, Incremark } from '@incremark/svelte'
|
|
65
|
+
import '@incremark/svelte/style.css'
|
|
66
|
+
|
|
67
|
+
const { blocks, append, finalize, reset } = useIncremark({
|
|
68
|
+
gfm: true,
|
|
69
|
+
math: true
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
async function handleStream(stream: AsyncIterable<string>) {
|
|
73
|
+
reset()
|
|
74
|
+
for await (const chunk of stream) {
|
|
75
|
+
append(chunk)
|
|
76
|
+
}
|
|
77
|
+
finalize()
|
|
78
|
+
}
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<button onclick={() => handleStream(stream)}>Start</button>
|
|
82
|
+
<Incremark blocks={$blocks} />
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## IncremarkContent Component
|
|
86
|
+
|
|
87
|
+
Declarative all-in-one component, recommended for most scenarios.
|
|
88
|
+
|
|
89
|
+
### Props
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
interface IncremarkContentProps {
|
|
93
|
+
// Input (choose one)
|
|
94
|
+
content?: string // Accumulated Markdown string
|
|
95
|
+
stream?: () => AsyncGenerator<string> // Async generator function
|
|
96
|
+
|
|
97
|
+
// Status
|
|
98
|
+
isFinished?: boolean // Stream finished flag (required for content mode)
|
|
99
|
+
|
|
100
|
+
// Configuration
|
|
101
|
+
incremarkOptions?: {
|
|
102
|
+
gfm?: boolean // GFM support
|
|
103
|
+
math?: boolean // Math formulas
|
|
104
|
+
htmlTree?: boolean // HTML structured parsing
|
|
105
|
+
containers?: boolean // ::: container syntax
|
|
106
|
+
typewriter?: { // Typewriter effect
|
|
107
|
+
enabled?: boolean
|
|
108
|
+
charsPerTick?: number | [number, number]
|
|
109
|
+
tickInterval?: number
|
|
110
|
+
effect?: 'none' | 'fade-in' | 'typing'
|
|
111
|
+
cursor?: string
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Custom rendering
|
|
116
|
+
components?: ComponentMap // Custom components
|
|
117
|
+
customContainers?: Record<string, Component> // Custom containers
|
|
118
|
+
customCodeBlocks?: Record<string, Component> // Custom code blocks
|
|
119
|
+
codeBlockConfigs?: Record<string, CodeBlockConfig>
|
|
120
|
+
|
|
121
|
+
// Styling
|
|
122
|
+
showBlockStatus?: boolean // Show block status border
|
|
123
|
+
pendingClass?: string // CSS class for pending blocks
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Example: Enable Typewriter Effect
|
|
128
|
+
|
|
129
|
+
```svelte
|
|
130
|
+
<IncremarkContent
|
|
131
|
+
{content}
|
|
132
|
+
{isFinished}
|
|
133
|
+
incremarkOptions={{
|
|
134
|
+
gfm: true,
|
|
135
|
+
typewriter: {
|
|
136
|
+
enabled: true,
|
|
137
|
+
charsPerTick: [1, 3],
|
|
138
|
+
tickInterval: 30,
|
|
139
|
+
effect: 'fade-in'
|
|
140
|
+
}
|
|
141
|
+
}}
|
|
142
|
+
/>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Example: Custom Components
|
|
146
|
+
|
|
147
|
+
```svelte
|
|
148
|
+
<script lang="ts">
|
|
149
|
+
import CustomHeading from './CustomHeading.svelte'
|
|
150
|
+
import WarningContainer from './WarningContainer.svelte'
|
|
151
|
+
import EchartsCodeBlock from './EchartsCodeBlock.svelte'
|
|
152
|
+
</script>
|
|
153
|
+
|
|
154
|
+
<IncremarkContent
|
|
155
|
+
{content}
|
|
156
|
+
{isFinished}
|
|
157
|
+
components={{ heading: CustomHeading }}
|
|
158
|
+
customContainers={{ warning: WarningContainer }}
|
|
159
|
+
customCodeBlocks={{ echarts: EchartsCodeBlock }}
|
|
160
|
+
codeBlockConfigs={{ echarts: { takeOver: true } }}
|
|
161
|
+
/>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Theme System
|
|
165
|
+
|
|
166
|
+
```svelte
|
|
167
|
+
<script lang="ts">
|
|
168
|
+
import { ThemeProvider, IncremarkContent } from '@incremark/svelte'
|
|
169
|
+
</script>
|
|
170
|
+
|
|
171
|
+
<!-- Built-in theme -->
|
|
172
|
+
<ThemeProvider theme="dark">
|
|
173
|
+
<IncremarkContent {content} {isFinished} />
|
|
174
|
+
</ThemeProvider>
|
|
175
|
+
|
|
176
|
+
<!-- Custom theme -->
|
|
177
|
+
<ThemeProvider theme={{ color: { brand: { primary: '#8b5cf6' } } }}>
|
|
178
|
+
<IncremarkContent {content} {isFinished} />
|
|
179
|
+
</ThemeProvider>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Auto Scroll
|
|
183
|
+
|
|
184
|
+
```svelte
|
|
185
|
+
<script lang="ts">
|
|
186
|
+
import { AutoScrollContainer, IncremarkContent } from '@incremark/svelte'
|
|
187
|
+
|
|
188
|
+
let scrollContainer: { scrollToBottom: () => void }
|
|
189
|
+
let autoScrollEnabled = $state(true)
|
|
190
|
+
</script>
|
|
191
|
+
|
|
192
|
+
<AutoScrollContainer
|
|
193
|
+
bind:this={scrollContainer}
|
|
194
|
+
enabled={autoScrollEnabled}
|
|
195
|
+
threshold={50}
|
|
196
|
+
behavior="smooth"
|
|
197
|
+
>
|
|
198
|
+
<IncremarkContent {content} {isFinished} />
|
|
199
|
+
</AutoScrollContainer>
|
|
200
|
+
|
|
201
|
+
<button onclick={() => scrollContainer?.scrollToBottom()}>
|
|
202
|
+
Scroll to Bottom
|
|
203
|
+
</button>
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## useIncremark API
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
const {
|
|
210
|
+
// State (Svelte stores)
|
|
211
|
+
markdown, // Writable<string> - Complete Markdown
|
|
212
|
+
blocks, // Readable<Block[]> - All blocks
|
|
213
|
+
completedBlocks, // Writable<Block[]> - Completed blocks
|
|
214
|
+
pendingBlocks, // Writable<Block[]> - Pending blocks
|
|
215
|
+
isLoading, // Writable<boolean> - Is loading
|
|
216
|
+
isDisplayComplete, // Readable<boolean> - Is display complete
|
|
217
|
+
|
|
218
|
+
// Methods
|
|
219
|
+
append, // (chunk: string) => IncrementalUpdate
|
|
220
|
+
finalize, // () => IncrementalUpdate
|
|
221
|
+
reset, // () => void
|
|
222
|
+
render, // (content: string) => IncrementalUpdate
|
|
223
|
+
|
|
224
|
+
// Typewriter controls
|
|
225
|
+
typewriter: {
|
|
226
|
+
enabled, // Writable<boolean> - Is enabled
|
|
227
|
+
isProcessing, // Readable<boolean> - Is processing
|
|
228
|
+
skip, // () => void - Skip animation
|
|
229
|
+
setOptions // (options) => void - Update config
|
|
230
|
+
}
|
|
231
|
+
} = useIncremark(options)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Svelte 5 Runes Syntax
|
|
235
|
+
|
|
236
|
+
This library uses Svelte 5 Runes syntax:
|
|
237
|
+
|
|
238
|
+
```svelte
|
|
239
|
+
<script lang="ts">
|
|
240
|
+
import { IncremarkContent } from '@incremark/svelte'
|
|
241
|
+
|
|
242
|
+
// Use $state for reactive state
|
|
243
|
+
let content = $state('')
|
|
244
|
+
let isFinished = $state(false)
|
|
245
|
+
|
|
246
|
+
// Use $derived for computed values
|
|
247
|
+
let charCount = $derived(content.length)
|
|
248
|
+
</script>
|
|
249
|
+
|
|
250
|
+
<IncremarkContent {content} {isFinished} />
|
|
251
|
+
<p>Character count: {charCount}</p>
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Math Formula Support
|
|
255
|
+
|
|
256
|
+
Built-in support, just enable `math: true`:
|
|
257
|
+
|
|
258
|
+
```svelte
|
|
259
|
+
<IncremarkContent
|
|
260
|
+
{content}
|
|
261
|
+
{isFinished}
|
|
262
|
+
incremarkOptions={{ math: true }}
|
|
263
|
+
/>
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Import KaTeX styles:
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
import 'katex/dist/katex.min.css'
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## License
|
|
273
|
+
|
|
274
|
+
MIT
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
<script lang="ts">
|
|
7
7
|
import type { DesignTokens } from '@incremark/theme'
|
|
8
8
|
import { applyTheme } from '@incremark/theme'
|
|
9
|
+
import { isBrowser } from '@incremark/shared'
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* 组件 Props
|
|
@@ -37,7 +38,9 @@
|
|
|
37
38
|
// 在 Svelte 5 中,$effect 会自动追踪在 effect 内部访问的响应式值
|
|
38
39
|
// 直接访问 theme prop 和 containerRef,确保都被追踪
|
|
39
40
|
$effect(() => {
|
|
40
|
-
//
|
|
41
|
+
// SSR 环境检查
|
|
42
|
+
if (!isBrowser()) return
|
|
43
|
+
|
|
41
44
|
if (containerRef) {
|
|
42
45
|
applyTheme(containerRef, theme)
|
|
43
46
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeProvider.svelte.d.ts","sourceRoot":"","sources":["../src/ThemeProvider.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"ThemeProvider.svelte.d.ts","sourceRoot":"","sources":["../src/ThemeProvider.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAKlD;;GAEG;AACH,UAAU,KAAK;IACb;;;;;OAKG;IACH,KAAK,EAAE,SAAS,GAAG,MAAM,GAAG,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAChE,iBAAiB;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU;IACV,QAAQ,CAAC,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAA;CACpC;AAwCH,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
-->
|
|
5
5
|
|
|
6
6
|
<script lang="ts">
|
|
7
|
-
import { onMount, onDestroy } from 'svelte'
|
|
7
|
+
import { onMount, onDestroy, tick } from 'svelte'
|
|
8
8
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -117,24 +117,24 @@
|
|
|
117
117
|
lastScrollTop = containerRef.scrollTop
|
|
118
118
|
lastScrollHeight = containerRef.scrollHeight
|
|
119
119
|
|
|
120
|
-
observer = new MutationObserver(() => {
|
|
120
|
+
observer = new MutationObserver(async () => {
|
|
121
121
|
// 使用 tick 等待 DOM 更新
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
122
|
+
await tick()
|
|
123
|
+
|
|
124
|
+
if (!containerRef) return
|
|
125
|
+
|
|
126
|
+
// 如果没有滚动条,重置状态
|
|
127
|
+
if (!hasScrollbar()) {
|
|
128
|
+
isUserScrolledUp = false
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 更新 scrollHeight 记录(内容变化后)
|
|
132
|
+
lastScrollHeight = containerRef.scrollHeight
|
|
133
|
+
|
|
134
|
+
// 自动滚动
|
|
135
|
+
if (enabled && !isUserScrolledUp) {
|
|
136
|
+
scrollToBottom()
|
|
137
|
+
}
|
|
138
138
|
})
|
|
139
139
|
|
|
140
140
|
observer.observe(containerRef, {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { IncremarkLocale } from '@incremark/svelte'
|
|
3
|
+
import { en } from '../index'
|
|
4
|
+
import { provideLocale } from '../stores/useLocale.svelte'
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: IncremarkLocale
|
|
8
|
+
children: import('svelte').Snippet
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { locale = en, children }: Props = $props()
|
|
12
|
+
|
|
13
|
+
// 在组件初始化时同步设置 context
|
|
14
|
+
// 使用函数返回 locale 以支持响应式更新
|
|
15
|
+
provideLocale(() => locale)
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
{@render children()}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IncremarkLocale } from '@incremark/svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
locale?: IncremarkLocale;
|
|
4
|
+
children: import('svelte').Snippet;
|
|
5
|
+
}
|
|
6
|
+
declare const ConfigProvider: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type ConfigProvider = ReturnType<typeof ConfigProvider>;
|
|
8
|
+
export default ConfigProvider;
|
|
9
|
+
//# sourceMappingURL=ConfigProvider.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigProvider.svelte.d.ts","sourceRoot":"","sources":["../../src/components/ConfigProvider.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAKtD,UAAU,KAAK;IACb,MAAM,CAAC,EAAE,eAAe,CAAA;IACxB,QAAQ,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAA;CACnC;AAkBH,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -5,33 +5,23 @@
|
|
|
5
5
|
|
|
6
6
|
<script lang="ts">
|
|
7
7
|
import type { Component } from 'svelte'
|
|
8
|
-
import type { Readable } from 'svelte/store'
|
|
9
8
|
import type { RootContent, ParsedBlock } from '@incremark/core'
|
|
10
|
-
import type { HTML } from 'mdast'
|
|
11
9
|
|
|
12
10
|
import { getDefinitionsContext } from '../context/definitionsContext'
|
|
13
11
|
import type { UseIncremarkReturn } from '../stores/useIncremark'
|
|
14
|
-
import type { ComponentMap,
|
|
12
|
+
import type { ComponentMap, RenderableBlock } from './types'
|
|
15
13
|
|
|
16
14
|
// 导入组件
|
|
17
15
|
import IncremarkFootnotes from './IncremarkFootnotes.svelte'
|
|
18
16
|
import IncremarkRenderer from './IncremarkRenderer.svelte'
|
|
19
17
|
|
|
20
|
-
/**
|
|
21
|
-
* 检查是否是 html 节点
|
|
22
|
-
*/
|
|
23
|
-
function isHtmlNode(node: RootContent): node is HTML {
|
|
24
|
-
return node.type === 'html'
|
|
25
|
-
}
|
|
26
|
-
|
|
27
18
|
/**
|
|
28
19
|
* 组件 Props
|
|
29
20
|
*/
|
|
30
21
|
interface Props {
|
|
31
|
-
/**
|
|
32
|
-
blocks?:
|
|
33
|
-
/**
|
|
34
|
-
* 如果传入了 incremark,则会自动使用 incremark.isDisplayComplete,此 prop 被忽略 */
|
|
22
|
+
/** 要渲染的块列表数组 */
|
|
23
|
+
blocks?: RenderableBlock[]
|
|
24
|
+
/** 内容是否完全显示完成 */
|
|
35
25
|
isDisplayComplete?: boolean
|
|
36
26
|
/** 自定义组件映射,key 为节点类型 */
|
|
37
27
|
components?: ComponentMap
|
|
@@ -47,7 +37,7 @@
|
|
|
47
37
|
completedClass?: string
|
|
48
38
|
/** 是否显示块状态边框 */
|
|
49
39
|
showBlockStatus?: boolean
|
|
50
|
-
/** 可选:useIncremark
|
|
40
|
+
/** 可选:useIncremark 返回的对象(用于自动注入数据,优先级高于 blocks/isDisplayComplete) */
|
|
51
41
|
incremark?: UseIncremarkReturn
|
|
52
42
|
}
|
|
53
43
|
|
|
@@ -64,69 +54,70 @@
|
|
|
64
54
|
incremark
|
|
65
55
|
}: Props = $props()
|
|
66
56
|
|
|
67
|
-
const context = getDefinitionsContext()
|
|
68
|
-
|
|
57
|
+
const context = getDefinitionsContext()
|
|
58
|
+
// 解构 store 以便使用 $ 语法订阅
|
|
59
|
+
const { footnoteReferenceOrder } = context
|
|
60
|
+
|
|
61
|
+
// 获取 incremark 的 stores(使用 getter 访问以保持响应性)
|
|
62
|
+
const blocksStore = $derived(incremark?.blocks)
|
|
63
|
+
const displayCompleteStore = $derived(incremark?.isDisplayComplete)
|
|
64
|
+
|
|
65
|
+
// 订阅 stores 的值
|
|
66
|
+
let blocksFromStore = $state<RenderableBlock[]>([])
|
|
67
|
+
let displayCompleteFromStore = $state(false)
|
|
68
|
+
|
|
69
|
+
// 使用 effect 订阅 blocks store
|
|
70
|
+
$effect(() => {
|
|
71
|
+
if (blocksStore) {
|
|
72
|
+
const unsubscribe = blocksStore.subscribe((value) => {
|
|
73
|
+
blocksFromStore = value
|
|
74
|
+
})
|
|
75
|
+
return unsubscribe
|
|
76
|
+
}
|
|
77
|
+
})
|
|
69
78
|
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
// 使用 effect 订阅 displayComplete store
|
|
80
|
+
$effect(() => {
|
|
81
|
+
if (displayCompleteStore) {
|
|
82
|
+
const unsubscribe = displayCompleteStore.subscribe((value) => {
|
|
83
|
+
displayCompleteFromStore = value
|
|
84
|
+
})
|
|
85
|
+
return unsubscribe
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// 计算最终要渲染的 blocks
|
|
90
|
+
const renderBlocks = $derived<RenderableBlock[]>(
|
|
91
|
+
blocksStore ? blocksFromStore : (blocks ?? [])
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
// 计算是否显示完成
|
|
95
|
+
const displayComplete = $derived(
|
|
96
|
+
displayCompleteStore ? displayCompleteFromStore : isDisplayComplete
|
|
97
|
+
)
|
|
77
98
|
</script>
|
|
78
99
|
|
|
79
100
|
<div class="incremark">
|
|
80
101
|
<!-- 主要内容块 -->
|
|
81
|
-
{#
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
{/if}
|
|
98
|
-
{/each}
|
|
99
|
-
{:else}
|
|
100
|
-
<!-- 使用传入的 blocks 数组 -->
|
|
101
|
-
{#each (Array.isArray(blocks) ? blocks : []) as block (block.stableId)}
|
|
102
|
-
{#if (block as ParsedBlock).node.type !== 'definition' && (block as ParsedBlock).node.type !== 'footnoteDefinition'}
|
|
103
|
-
<div
|
|
104
|
-
class="incremark-block {(block as ParsedBlock).status === 'completed' ? completedClass : pendingClass} {showBlockStatus ? 'incremark-show-status' : ''} {block.isLastPending ? 'incremark-last-pending' : ''}"
|
|
105
|
-
>
|
|
106
|
-
<!-- 使用 IncremarkRenderer,传递 customContainers 和 customCodeBlocks -->
|
|
107
|
-
<IncremarkRenderer
|
|
108
|
-
node={(block as ParsedBlock).node}
|
|
109
|
-
customContainers={customContainers}
|
|
110
|
-
customCodeBlocks={customCodeBlocks}
|
|
111
|
-
codeBlockConfigs={codeBlockConfigs}
|
|
112
|
-
blockStatus={(block as ParsedBlock).status}
|
|
113
|
-
/>
|
|
114
|
-
</div>
|
|
115
|
-
{/if}
|
|
116
|
-
{/each}
|
|
117
|
-
{/if}
|
|
102
|
+
{#each renderBlocks as block (block.id)}
|
|
103
|
+
{#if block.node.type !== 'definition' && block.node.type !== 'footnoteDefinition'}
|
|
104
|
+
<div
|
|
105
|
+
class="incremark-block {block.status === 'completed' ? completedClass : pendingClass} {showBlockStatus ? 'incremark-show-status' : ''} {block.isLastPending ? 'incremark-last-pending' : ''}"
|
|
106
|
+
>
|
|
107
|
+
<IncremarkRenderer
|
|
108
|
+
node={block.node}
|
|
109
|
+
{components}
|
|
110
|
+
customContainers={customContainers}
|
|
111
|
+
customCodeBlocks={customCodeBlocks}
|
|
112
|
+
codeBlockConfigs={codeBlockConfigs}
|
|
113
|
+
blockStatus={block.status}
|
|
114
|
+
/>
|
|
115
|
+
</div>
|
|
116
|
+
{/if}
|
|
117
|
+
{/each}
|
|
118
118
|
|
|
119
119
|
<!-- 脚注列表(仅在内容完全显示后显示) -->
|
|
120
|
-
{#if
|
|
121
|
-
|
|
122
|
-
{#if footnoteOrder.length > 0}
|
|
123
|
-
<IncremarkFootnotes />
|
|
124
|
-
{/if}
|
|
125
|
-
{:else if !incremark && isDisplayComplete && $footnoteReferenceOrder}
|
|
126
|
-
{@const footnoteOrder = $footnoteReferenceOrder ?? []}
|
|
127
|
-
{#if footnoteOrder.length > 0}
|
|
128
|
-
<IncremarkFootnotes />
|
|
129
|
-
{/if}
|
|
120
|
+
{#if displayComplete && $footnoteReferenceOrder.length > 0}
|
|
121
|
+
<IncremarkFootnotes />
|
|
130
122
|
{/if}
|
|
131
123
|
</div>
|
|
132
|
-
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import type { Component } from 'svelte';
|
|
2
|
-
import type { Readable } from 'svelte/store';
|
|
3
2
|
import type { UseIncremarkReturn } from '../stores/useIncremark';
|
|
4
|
-
import type { ComponentMap,
|
|
3
|
+
import type { ComponentMap, RenderableBlock } from './types';
|
|
5
4
|
/**
|
|
6
5
|
* 组件 Props
|
|
7
6
|
*/
|
|
8
7
|
interface Props {
|
|
9
|
-
/**
|
|
10
|
-
blocks?:
|
|
11
|
-
/**
|
|
12
|
-
* 如果传入了 incremark,则会自动使用 incremark.isDisplayComplete,此 prop 被忽略 */
|
|
8
|
+
/** 要渲染的块列表数组 */
|
|
9
|
+
blocks?: RenderableBlock[];
|
|
10
|
+
/** 内容是否完全显示完成 */
|
|
13
11
|
isDisplayComplete?: boolean;
|
|
14
12
|
/** 自定义组件映射,key 为节点类型 */
|
|
15
13
|
components?: ComponentMap;
|
|
@@ -27,7 +25,7 @@ interface Props {
|
|
|
27
25
|
completedClass?: string;
|
|
28
26
|
/** 是否显示块状态边框 */
|
|
29
27
|
showBlockStatus?: boolean;
|
|
30
|
-
/** 可选:useIncremark
|
|
28
|
+
/** 可选:useIncremark 返回的对象(用于自动注入数据,优先级高于 blocks/isDisplayComplete) */
|
|
31
29
|
incremark?: UseIncremarkReturn;
|
|
32
30
|
}
|
|
33
31
|
declare const Incremark: Component<Props, {}, "">;
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AAIvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAM1D;;GAEG;AACH,UAAU,KAAK;IACb,gBAAgB;IAChB,MAAM,CAAC,EAAE,eAAe,EAAE,CAAA;IAC1B,iBAAiB;IACjB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,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,0BAA0B;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IACzD,gBAAgB;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB;IAChB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,gBAAgB;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,qEAAqE;IACrE,SAAS,CAAC,EAAE,kBAAkB,CAAA;CAC/B;AA0FH,QAAA,MAAM,SAAS,0BAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
|