@humanspeak/svelte-markdown 1.0.5 → 1.1.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.md +42 -0
- package/dist/SvelteMarkdown.svelte +56 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types.d.ts +14 -0
- package/dist/utils/incremental-parser.d.ts +65 -0
- package/dist/utils/incremental-parser.js +75 -0
- package/dist/utils/parse-and-cache.d.ts +18 -0
- package/dist/utils/parse-and-cache.js +8 -1
- package/package.json +16 -16
package/README.md
CHANGED
|
@@ -27,6 +27,7 @@ A powerful, customizable markdown renderer for Svelte with TypeScript support. B
|
|
|
27
27
|
- 🧪 Comprehensive test coverage (vitest and playwright)
|
|
28
28
|
- 🧩 First-class marked extensions support via `extensions` prop (e.g., KaTeX math, alerts)
|
|
29
29
|
- ⚡ Intelligent token caching (50-200x faster re-renders)
|
|
30
|
+
- 📡 LLM streaming mode with incremental rendering (~1.6ms avg per update)
|
|
30
31
|
- 🖼️ Smart image lazy loading with fade-in animation
|
|
31
32
|
|
|
32
33
|
## Installation
|
|
@@ -678,6 +679,46 @@ Images automatically lazy load using native `loading="lazy"` and IntersectionObs
|
|
|
678
679
|
<SvelteMarkdown source={markdown} {renderers} />
|
|
679
680
|
```
|
|
680
681
|
|
|
682
|
+
### LLM Streaming
|
|
683
|
+
|
|
684
|
+
For real-time rendering of AI responses from ChatGPT, Claude, Gemini, and other LLMs, enable the `streaming` prop. This uses a smart diff algorithm that re-parses the full source for correctness but only updates changed DOM nodes, keeping render times constant regardless of document size.
|
|
685
|
+
|
|
686
|
+
```svelte
|
|
687
|
+
<script lang="ts">
|
|
688
|
+
import SvelteMarkdown from '@humanspeak/svelte-markdown'
|
|
689
|
+
|
|
690
|
+
let source = $state('')
|
|
691
|
+
|
|
692
|
+
async function streamResponse() {
|
|
693
|
+
const response = await fetch('/api/chat', { method: 'POST', body: '...' })
|
|
694
|
+
const reader = response.body.getReader()
|
|
695
|
+
const decoder = new TextDecoder()
|
|
696
|
+
|
|
697
|
+
while (true) {
|
|
698
|
+
const { done, value } = await reader.read()
|
|
699
|
+
if (done) break
|
|
700
|
+
source += decoder.decode(value, { stream: true })
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
</script>
|
|
704
|
+
|
|
705
|
+
<SvelteMarkdown {source} streaming={true} />
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
**Performance** (measured at 100 characters/sec, character mode):
|
|
709
|
+
|
|
710
|
+
| Metric | Standard Mode | Streaming Mode |
|
|
711
|
+
| -------------- | :-----------: | :------------: |
|
|
712
|
+
| Average render | ~3.6ms | ~1.6ms |
|
|
713
|
+
| Peak render | ~21ms | ~10ms |
|
|
714
|
+
| Dropped frames | 0 | 0 |
|
|
715
|
+
|
|
716
|
+
When `streaming` is `false` (default), existing behavior is unchanged. The `streaming` prop skips cache lookups (always a miss during streaming) and uses in-place token array mutation so Svelte only re-renders components for tokens that actually changed.
|
|
717
|
+
|
|
718
|
+
**Note:** `streaming` is automatically disabled when async extensions (e.g., `markedMermaid`) are used. A console warning is logged in this case.
|
|
719
|
+
|
|
720
|
+
See the [full streaming documentation](https://markdown.svelte.page/docs/advanced/llm-streaming) and [interactive demo](https://markdown.svelte.page/examples/llm-streaming).
|
|
721
|
+
|
|
681
722
|
## Available Renderers
|
|
682
723
|
|
|
683
724
|
- `text` - Text within other elements
|
|
@@ -764,6 +805,7 @@ The component emits a `parsed` event when tokens are calculated:
|
|
|
764
805
|
| Prop | Type | Description |
|
|
765
806
|
| ---------- | ----------------------- | ------------------------------------------------ |
|
|
766
807
|
| source | `string \| Token[]` | Markdown content or pre-parsed tokens |
|
|
808
|
+
| streaming | `boolean` | Enable incremental rendering for LLM streaming |
|
|
767
809
|
| renderers | `Partial<Renderers>` | Custom component overrides |
|
|
768
810
|
| options | `SvelteMarkdownOptions` | Marked parser configuration |
|
|
769
811
|
| isInline | `boolean` | Toggle inline parsing mode |
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
|
|
52
52
|
import Parser from './Parser.svelte'
|
|
53
53
|
import { type SvelteMarkdownProps } from './types.js'
|
|
54
|
+
import { IncrementalParser } from './utils/incremental-parser.js'
|
|
54
55
|
import {
|
|
55
56
|
defaultOptions,
|
|
56
57
|
defaultRenderers,
|
|
@@ -67,6 +68,7 @@
|
|
|
67
68
|
|
|
68
69
|
const {
|
|
69
70
|
source = [],
|
|
71
|
+
streaming = false,
|
|
70
72
|
renderers = {},
|
|
71
73
|
options = {},
|
|
72
74
|
isInline = false,
|
|
@@ -93,9 +95,59 @@
|
|
|
93
95
|
// Detect if any extension requires async processing
|
|
94
96
|
const hasAsyncExtension = $derived(extensions.some((ext) => ext.async === true))
|
|
95
97
|
|
|
96
|
-
//
|
|
98
|
+
// Streaming mode: full re-parse + smart in-place diff
|
|
99
|
+
let incrementalParser: IncrementalParser | undefined
|
|
100
|
+
let lastOptionsKey = ''
|
|
101
|
+
let streamTokens = $state<Token[]>([])
|
|
102
|
+
|
|
103
|
+
$effect(() => {
|
|
104
|
+
if (!streaming || hasAsyncExtension) {
|
|
105
|
+
if (incrementalParser) {
|
|
106
|
+
incrementalParser = undefined
|
|
107
|
+
lastOptionsKey = ''
|
|
108
|
+
}
|
|
109
|
+
if (streaming && hasAsyncExtension) {
|
|
110
|
+
console.warn(
|
|
111
|
+
'[svelte-markdown] streaming prop is ignored when async extensions are used. ' +
|
|
112
|
+
'Remove async extensions or set streaming={false} to silence this warning.'
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Read combinedOptions unconditionally so Svelte tracks it as a dependency
|
|
119
|
+
const currentOptions = combinedOptions
|
|
120
|
+
const optionsKey = JSON.stringify(currentOptions)
|
|
121
|
+
|
|
122
|
+
if (Array.isArray(source)) {
|
|
123
|
+
streamTokens = source as Token[]
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (source === '') {
|
|
128
|
+
if (incrementalParser) incrementalParser.reset()
|
|
129
|
+
streamTokens.length = 0
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Recreate parser only when options actually change
|
|
134
|
+
if (!incrementalParser || lastOptionsKey !== optionsKey) {
|
|
135
|
+
incrementalParser = new IncrementalParser(currentOptions)
|
|
136
|
+
lastOptionsKey = optionsKey
|
|
137
|
+
}
|
|
138
|
+
const { tokens: newTokens, divergeAt } = incrementalParser.update(source as string)
|
|
139
|
+
|
|
140
|
+
// In-place update: only touch changed/appended indices
|
|
141
|
+
for (let i = divergeAt; i < newTokens.length; i++) {
|
|
142
|
+
streamTokens[i] = newTokens[i]
|
|
143
|
+
}
|
|
144
|
+
streamTokens.length = newTokens.length
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
// Synchronous token derivation (default fast path — non-streaming)
|
|
97
148
|
const syncTokens = $derived.by(() => {
|
|
98
149
|
if (hasAsyncExtension) return undefined
|
|
150
|
+
if (streaming) return undefined
|
|
99
151
|
|
|
100
152
|
// Pre-parsed tokens - skip caching and parsing
|
|
101
153
|
if (Array.isArray(source)) {
|
|
@@ -107,7 +159,7 @@
|
|
|
107
159
|
return []
|
|
108
160
|
}
|
|
109
161
|
|
|
110
|
-
//
|
|
162
|
+
// Standard mode - full parse with caching
|
|
111
163
|
return parseAndCacheTokens(source as string, combinedOptions, isInline)
|
|
112
164
|
}) satisfies Token[] | TokensList | undefined
|
|
113
165
|
|
|
@@ -149,8 +201,8 @@
|
|
|
149
201
|
})
|
|
150
202
|
})
|
|
151
203
|
|
|
152
|
-
// Unified tokens:
|
|
153
|
-
const tokens = $derived(hasAsyncExtension ? asyncTokens : syncTokens)
|
|
204
|
+
// Unified tokens: streaming > sync > async
|
|
205
|
+
const tokens = $derived(streaming ? streamTokens : hasAsyncExtension ? asyncTokens : syncTokens)
|
|
154
206
|
|
|
155
207
|
$effect(() => {
|
|
156
208
|
if (!tokens) return
|
package/dist/index.d.ts
CHANGED
|
@@ -58,6 +58,7 @@ export { htmlRendererKeysInternal as htmlRendererKeys, rendererKeysInternal as r
|
|
|
58
58
|
* - `tokenCache` — shared singleton `TokenCache` instance
|
|
59
59
|
*/
|
|
60
60
|
export { MemoryCache } from './utils/cache.js';
|
|
61
|
+
export { IncrementalParser, type IncrementalUpdateResult } from './utils/incremental-parser.js';
|
|
61
62
|
export { TokenCache, tokenCache } from './utils/token-cache.js';
|
|
62
63
|
/** Re-exported `MarkedExtension` type for the `extensions` prop. */
|
|
63
64
|
export type { MarkedExtension } from 'marked';
|
package/dist/index.js
CHANGED
|
@@ -57,4 +57,5 @@ export { htmlRendererKeysInternal as htmlRendererKeys, rendererKeysInternal as r
|
|
|
57
57
|
* - `tokenCache` — shared singleton `TokenCache` instance
|
|
58
58
|
*/
|
|
59
59
|
export { MemoryCache } from './utils/cache.js';
|
|
60
|
+
export { IncrementalParser } from './utils/incremental-parser.js';
|
|
60
61
|
export { TokenCache, tokenCache } from './utils/token-cache.js';
|
package/dist/types.d.ts
CHANGED
|
@@ -190,6 +190,20 @@ export type SvelteMarkdownProps<T extends Renderers = Renderers> = {
|
|
|
190
190
|
* @defaultValue `false`
|
|
191
191
|
*/
|
|
192
192
|
isInline?: boolean;
|
|
193
|
+
/**
|
|
194
|
+
* Enables optimized rendering for LLM streaming scenarios.
|
|
195
|
+
*
|
|
196
|
+
* When `true`, the component performs a full re-parse on each source
|
|
197
|
+
* update but diffs the resulting tokens against the previous parse.
|
|
198
|
+
* Only changed or appended tokens trigger DOM updates, keeping render
|
|
199
|
+
* cost proportional to the change rather than the full document size.
|
|
200
|
+
*
|
|
201
|
+
* Use this when appending tokens to `source` in a streaming fashion
|
|
202
|
+
* (e.g., ChatGPT/Claude SSE responses).
|
|
203
|
+
*
|
|
204
|
+
* @defaultValue `false`
|
|
205
|
+
*/
|
|
206
|
+
streaming?: boolean;
|
|
193
207
|
/**
|
|
194
208
|
* Callback invoked after the source has been parsed into tokens.
|
|
195
209
|
*
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Incremental Markdown Parser for Streaming
|
|
3
|
+
*
|
|
4
|
+
* Optimizes streaming scenarios (LLM token-by-token updates) by performing
|
|
5
|
+
* a full re-parse but diffing the result against the previous token array.
|
|
6
|
+
* Only changed/appended tokens are returned as updates, allowing Svelte to
|
|
7
|
+
* skip re-rendering unchanged components.
|
|
8
|
+
*
|
|
9
|
+
* @module incremental-parser
|
|
10
|
+
*/
|
|
11
|
+
import type { SvelteMarkdownOptions } from '../types.js';
|
|
12
|
+
import type { Token } from './markdown-parser.js';
|
|
13
|
+
/**
|
|
14
|
+
* Result of an incremental parse update.
|
|
15
|
+
*/
|
|
16
|
+
export interface IncrementalUpdateResult {
|
|
17
|
+
/** The full new token array */
|
|
18
|
+
tokens: Token[];
|
|
19
|
+
/** Index of the first token that differs from the previous parse */
|
|
20
|
+
divergeAt: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Streaming-optimized parser that performs full re-parses but diffs results
|
|
24
|
+
* against the previous token array to minimize DOM updates.
|
|
25
|
+
*
|
|
26
|
+
* For append-only streaming (typical LLM use case), most tokens are identical
|
|
27
|
+
* between updates. By comparing `raw` strings, we identify which tokens changed
|
|
28
|
+
* so Svelte can skip re-rendering unchanged components.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const parser = new IncrementalParser({ gfm: true })
|
|
33
|
+
*
|
|
34
|
+
* // First update — all tokens are "new"
|
|
35
|
+
* const r1 = parser.update('# Hello')
|
|
36
|
+
* // r1.divergeAt === 0
|
|
37
|
+
*
|
|
38
|
+
* // Second update — heading unchanged, paragraph appended
|
|
39
|
+
* const r2 = parser.update('# Hello\n\nWorld')
|
|
40
|
+
* // r2.divergeAt === 1 (heading at index 0 unchanged)
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare class IncrementalParser {
|
|
44
|
+
/** Previous parse result for diffing */
|
|
45
|
+
private prevTokens;
|
|
46
|
+
/** Parser options passed to the Marked lexer */
|
|
47
|
+
private options;
|
|
48
|
+
/**
|
|
49
|
+
* Creates a new incremental parser instance.
|
|
50
|
+
*
|
|
51
|
+
* @param options - Svelte markdown parser options forwarded to Marked's Lexer
|
|
52
|
+
*/
|
|
53
|
+
constructor(options: SvelteMarkdownOptions);
|
|
54
|
+
/**
|
|
55
|
+
* Parses the full source and diffs against the previous result.
|
|
56
|
+
*
|
|
57
|
+
* @param source - The full accumulated markdown source string
|
|
58
|
+
* @returns The new tokens and the index where they diverge from the previous parse
|
|
59
|
+
*/
|
|
60
|
+
update: (source: string) => IncrementalUpdateResult;
|
|
61
|
+
/**
|
|
62
|
+
* Resets the parser state. Call this when starting a new stream.
|
|
63
|
+
*/
|
|
64
|
+
reset: () => void;
|
|
65
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Incremental Markdown Parser for Streaming
|
|
3
|
+
*
|
|
4
|
+
* Optimizes streaming scenarios (LLM token-by-token updates) by performing
|
|
5
|
+
* a full re-parse but diffing the result against the previous token array.
|
|
6
|
+
* Only changed/appended tokens are returned as updates, allowing Svelte to
|
|
7
|
+
* skip re-rendering unchanged components.
|
|
8
|
+
*
|
|
9
|
+
* @module incremental-parser
|
|
10
|
+
*/
|
|
11
|
+
import { lexAndClean } from './parse-and-cache.js';
|
|
12
|
+
/**
|
|
13
|
+
* Streaming-optimized parser that performs full re-parses but diffs results
|
|
14
|
+
* against the previous token array to minimize DOM updates.
|
|
15
|
+
*
|
|
16
|
+
* For append-only streaming (typical LLM use case), most tokens are identical
|
|
17
|
+
* between updates. By comparing `raw` strings, we identify which tokens changed
|
|
18
|
+
* so Svelte can skip re-rendering unchanged components.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const parser = new IncrementalParser({ gfm: true })
|
|
23
|
+
*
|
|
24
|
+
* // First update — all tokens are "new"
|
|
25
|
+
* const r1 = parser.update('# Hello')
|
|
26
|
+
* // r1.divergeAt === 0
|
|
27
|
+
*
|
|
28
|
+
* // Second update — heading unchanged, paragraph appended
|
|
29
|
+
* const r2 = parser.update('# Hello\n\nWorld')
|
|
30
|
+
* // r2.divergeAt === 1 (heading at index 0 unchanged)
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class IncrementalParser {
|
|
34
|
+
/** Previous parse result for diffing */
|
|
35
|
+
prevTokens = [];
|
|
36
|
+
/** Parser options passed to the Marked lexer */
|
|
37
|
+
options;
|
|
38
|
+
/**
|
|
39
|
+
* Creates a new incremental parser instance.
|
|
40
|
+
*
|
|
41
|
+
* @param options - Svelte markdown parser options forwarded to Marked's Lexer
|
|
42
|
+
*/
|
|
43
|
+
constructor(options) {
|
|
44
|
+
this.options = options;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Parses the full source and diffs against the previous result.
|
|
48
|
+
*
|
|
49
|
+
* @param source - The full accumulated markdown source string
|
|
50
|
+
* @returns The new tokens and the index where they diverge from the previous parse
|
|
51
|
+
*/
|
|
52
|
+
update = (source) => {
|
|
53
|
+
const newTokens = lexAndClean(source, this.options, false);
|
|
54
|
+
// Apply walkTokens if configured
|
|
55
|
+
if (typeof this.options.walkTokens === 'function') {
|
|
56
|
+
newTokens.forEach(this.options.walkTokens);
|
|
57
|
+
}
|
|
58
|
+
// Find first divergence point by comparing raw strings
|
|
59
|
+
let divergeAt = 0;
|
|
60
|
+
const minLen = Math.min(this.prevTokens.length, newTokens.length);
|
|
61
|
+
while (divergeAt < minLen) {
|
|
62
|
+
if (this.prevTokens[divergeAt].raw !== newTokens[divergeAt].raw)
|
|
63
|
+
break;
|
|
64
|
+
divergeAt++;
|
|
65
|
+
}
|
|
66
|
+
this.prevTokens = newTokens;
|
|
67
|
+
return { tokens: newTokens, divergeAt };
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Resets the parser state. Call this when starting a new stream.
|
|
71
|
+
*/
|
|
72
|
+
reset = () => {
|
|
73
|
+
this.prevTokens = [];
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -8,6 +8,24 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { SvelteMarkdownOptions } from '../types.js';
|
|
10
10
|
import type { Token, TokensList } from './markdown-parser.js';
|
|
11
|
+
/**
|
|
12
|
+
* Lexes markdown source and cleans the resulting tokens. Shared by sync and async paths.
|
|
13
|
+
*
|
|
14
|
+
* @param source - Raw markdown string to lex
|
|
15
|
+
* @param options - Parser options forwarded to the Marked lexer
|
|
16
|
+
* @param isInline - When true, uses inline tokenization (no block elements)
|
|
17
|
+
* @returns Cleaned token array with HTML tokens properly nested
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { lexAndClean } from './parse-and-cache.js'
|
|
22
|
+
*
|
|
23
|
+
* const tokens = lexAndClean('# Hello **world**', { gfm: true }, false)
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
export declare const lexAndClean: (source: string, options: SvelteMarkdownOptions, isInline: boolean) => Token[];
|
|
11
29
|
/**
|
|
12
30
|
* Parses markdown source with caching (synchronous path).
|
|
13
31
|
* Checks cache first, parses on miss, stores result, and returns tokens.
|
|
@@ -17,9 +17,16 @@ import { Lexer, Marked } from 'marked';
|
|
|
17
17
|
* @param isInline - When true, uses inline tokenization (no block elements)
|
|
18
18
|
* @returns Cleaned token array with HTML tokens properly nested
|
|
19
19
|
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* import { lexAndClean } from './parse-and-cache.js'
|
|
23
|
+
*
|
|
24
|
+
* const tokens = lexAndClean('# Hello **world**', { gfm: true }, false)
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
20
27
|
* @internal
|
|
21
28
|
*/
|
|
22
|
-
const lexAndClean = (source, options, isInline) => {
|
|
29
|
+
export const lexAndClean = (source, options, isInline) => {
|
|
23
30
|
const lexer = new Lexer(options);
|
|
24
31
|
const parsedTokens = isInline ? lexer.inlineTokens(source) : lexer.lex(source);
|
|
25
32
|
return shrinkHtmlTokens(parsedTokens);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanspeak/svelte-markdown",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Fast, customizable markdown renderer for Svelte with built-in caching, TypeScript support, and Svelte 5 runes",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"svelte",
|
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
"dependencies": {
|
|
70
70
|
"@humanspeak/memory-cache": "^1.0.6",
|
|
71
71
|
"github-slugger": "^2.0.0",
|
|
72
|
-
"htmlparser2": "^
|
|
73
|
-
"marked": "^17.0.
|
|
72
|
+
"htmlparser2": "^12.0.0",
|
|
73
|
+
"marked": "^17.0.5"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@eslint/compat": "^2.0.3",
|
|
@@ -86,32 +86,32 @@
|
|
|
86
86
|
"@testing-library/user-event": "^14.6.1",
|
|
87
87
|
"@types/katex": "^0.16.8",
|
|
88
88
|
"@types/node": "^25.5.0",
|
|
89
|
-
"@typescript-eslint/eslint-plugin": "^8.57.
|
|
90
|
-
"@typescript-eslint/parser": "^8.57.
|
|
91
|
-
"@vitest/coverage-v8": "^4.1.
|
|
92
|
-
"eslint": "^10.0
|
|
89
|
+
"@typescript-eslint/eslint-plugin": "^8.57.2",
|
|
90
|
+
"@typescript-eslint/parser": "^8.57.2",
|
|
91
|
+
"@vitest/coverage-v8": "^4.1.1",
|
|
92
|
+
"eslint": "^10.1.0",
|
|
93
93
|
"eslint-config-prettier": "^10.1.8",
|
|
94
94
|
"eslint-plugin-import": "^2.32.0",
|
|
95
|
-
"eslint-plugin-svelte": "^3.
|
|
95
|
+
"eslint-plugin-svelte": "^3.16.0",
|
|
96
96
|
"eslint-plugin-unused-imports": "^4.4.1",
|
|
97
97
|
"globals": "^17.4.0",
|
|
98
98
|
"husky": "^9.1.7",
|
|
99
|
-
"jsdom": "^29.0.
|
|
100
|
-
"katex": "^0.16.
|
|
99
|
+
"jsdom": "^29.0.1",
|
|
100
|
+
"katex": "^0.16.41",
|
|
101
101
|
"marked-katex-extension": "^5.1.7",
|
|
102
102
|
"mermaid": "^11.13.0",
|
|
103
|
-
"mprocs": "^0.
|
|
103
|
+
"mprocs": "^0.9.2",
|
|
104
104
|
"prettier": "^3.8.1",
|
|
105
105
|
"prettier-plugin-organize-imports": "^4.3.0",
|
|
106
106
|
"prettier-plugin-svelte": "^3.5.1",
|
|
107
107
|
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
108
108
|
"publint": "^0.3.18",
|
|
109
|
-
"svelte": "^5.
|
|
109
|
+
"svelte": "^5.55.0",
|
|
110
110
|
"svelte-check": "^4.4.5",
|
|
111
|
-
"typescript": "^
|
|
112
|
-
"typescript-eslint": "^8.57.
|
|
113
|
-
"vite": "^8.0.
|
|
114
|
-
"vitest": "^4.1.
|
|
111
|
+
"typescript": "^6.0.2",
|
|
112
|
+
"typescript-eslint": "^8.57.2",
|
|
113
|
+
"vite": "^8.0.2",
|
|
114
|
+
"vitest": "^4.1.1"
|
|
115
115
|
},
|
|
116
116
|
"peerDependencies": {
|
|
117
117
|
"mermaid": ">=10.0.0",
|