markstream-svelte 0.0.1-beta.5 → 0.0.1

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 CHANGED
@@ -1,6 +1,21 @@
1
1
  # markstream-svelte
2
2
 
3
- Svelte 5-only renderer aligned with `markstream-vue`, `markstream-vue2`, and `markstream-react`. Svelte 4 is not supported.
3
+ Svelte 5 streaming Markdown renderer for AI chat, LLM token streams, SSE/WebSocket output, incomplete Markdown states, long documents, custom components, Mermaid, KaTeX, Monaco, D2, and Infographic.
4
+
5
+ ## When to use it
6
+
7
+ Use `markstream-svelte` when Markdown changes while users are reading it:
8
+ LLM output, SSE streams, WebSocket streams, AI chat messages, long generated answers,
9
+ progressive diagrams, math, or code blocks.
10
+
11
+ For normal chat streaming, start with the raw `content` string path. Use pre-parsed
12
+ `nodes` only when another part of your app already owns the parser or AST state.
13
+
14
+ ## Known limitations
15
+
16
+ - **Svelte 5 only.** Svelte 4 is not supported.
17
+ - This package is currently beta. Check npm and the [Svelte guide](https://markstream.simonhe.me/guide/svelte) for the latest API maturity.
18
+ - It is not the first choice for short static Markdown or apps that require a fully stable Svelte 4-compatible API.
4
19
 
5
20
  ## Install
6
21
 
@@ -8,7 +23,8 @@ Svelte 5-only renderer aligned with `markstream-vue`, `markstream-vue2`, and `ma
8
23
  pnpm add markstream-svelte svelte@^5
9
24
  ```
10
25
 
11
- Optional heavy renderers stay as peer dependencies, matching the Vue and React packages:
26
+ Optional heavy renderers stay as peer dependencies, matching the Vue and React packages.
27
+ Plain Markdown does not require these packages:
12
28
 
13
29
  ```bash
14
30
  pnpm add katex mermaid stream-monaco @terrastruct/d2 @antv/infographic
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { SvelteRenderableNode } from './shared/node-helpers'
3
+ import { sanitizeImageSrc } from 'stream-markdown-parser'
3
4
  import { useSafeI18n } from '../i18n/useSafeI18n'
4
5
  import { getString } from './shared/node-helpers'
5
6
 
@@ -21,38 +22,58 @@
21
22
 
22
23
  const { t } = useSafeI18n()
23
24
 
24
- let src = $derived(getString((node as any)?.src))
25
+ type ImageStage = 'primary' | 'fallback' | 'failed'
26
+
27
+ function resolveImageState(primarySrc: string, fallbackSrc: string, loading: boolean): { src: string; stage: ImageStage } {
28
+ if (loading || primarySrc)
29
+ return { src: primarySrc, stage: 'primary' }
30
+ if (fallbackSrc)
31
+ return { src: fallbackSrc, stage: 'fallback' }
32
+ return { src: '', stage: 'failed' }
33
+ }
34
+
35
+ let safeNodeSrc = $derived(sanitizeImageSrc((node as any)?.src))
36
+ let safeFallbackSrc = $derived(sanitizeImageSrc(fallbackSrc))
25
37
  let alt = $derived(getString((node as any)?.alt))
26
38
  let title = $derived(getString((node as any)?.title))
27
39
  let raw = $derived(getString((node as any)?.raw))
28
40
  let isLoading = $derived(Boolean((node as any)?.loading))
29
41
  let useEagerImagePath = $derived(!lazy)
30
42
 
31
- let initialSrc = untrack(() => getString((node as any)?.src))
32
- let previousSrc = $state(initialSrc)
33
- let currentSrc = $state(initialSrc)
43
+ let initialSafeNodeSrc = untrack(() => sanitizeImageSrc((node as any)?.src))
44
+ let initialSafeFallbackSrc = untrack(() => sanitizeImageSrc(fallbackSrc))
45
+ let initialIsLoading = untrack(() => Boolean((node as any)?.loading))
46
+ let initialImageState = resolveImageState(initialSafeNodeSrc, initialSafeFallbackSrc, initialIsLoading)
47
+ let previousSafeNodeSrc = $state(initialSafeNodeSrc)
48
+ let previousSafeFallbackSrc = $state(initialSafeFallbackSrc)
49
+ let previousIsLoading = $state(initialIsLoading)
50
+ let currentSrc = $state(initialImageState.src)
51
+ let imageStage = $state<ImageStage>(initialImageState.stage)
34
52
  let imageLoaded = $state(false)
35
- let hasError = $state(false)
36
- let fallbackTried = $state(false)
53
+ let hasError = $state(initialImageState.stage === 'failed')
37
54
 
38
55
  $effect.pre(() => {
39
- if (src !== previousSrc) {
40
- previousSrc = src
41
- currentSrc = src
56
+ if (safeNodeSrc !== previousSafeNodeSrc || safeFallbackSrc !== previousSafeFallbackSrc || isLoading !== previousIsLoading) {
57
+ previousSafeNodeSrc = safeNodeSrc
58
+ previousSafeFallbackSrc = safeFallbackSrc
59
+ previousIsLoading = isLoading
60
+ const next = resolveImageState(safeNodeSrc, safeFallbackSrc, isLoading)
61
+ currentSrc = next.src
62
+ imageStage = next.stage
42
63
  imageLoaded = false
43
- hasError = false
44
- fallbackTried = false
64
+ hasError = next.stage === 'failed'
45
65
  }
46
66
  })
47
67
 
48
68
  function handleImageError() {
49
- if (fallbackSrc && !fallbackTried) {
50
- fallbackTried = true
51
- currentSrc = fallbackSrc
69
+ if (imageStage === 'primary' && safeFallbackSrc && safeFallbackSrc !== currentSrc) {
70
+ currentSrc = safeFallbackSrc
71
+ imageStage = 'fallback'
52
72
  imageLoaded = false
53
73
  hasError = false
54
74
  return
55
75
  }
76
+ imageStage = 'failed'
56
77
  hasError = true
57
78
  imageLoaded = false
58
79
  }
@@ -69,7 +90,7 @@
69
90
  </script>
70
91
 
71
92
  <span class="image-node-container">
72
- {#if !isLoading && !hasError}
93
+ {#if !isLoading && !hasError && currentSrc}
73
94
  <img
74
95
  class="image-node__img"
75
96
  class:is-loaded={useEagerImagePath || imageLoaded}
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { SvelteRenderableNode, SvelteRenderContext } from './shared/node-helpers'
3
+ import { sanitizeHtmlAttrs, shouldOpenLinkInNewTab } from 'stream-markdown-parser'
3
4
  import { hideTooltip, showTooltipForAnchor } from '../tooltip/singletonTooltip'
4
5
  import RenderChildren from './RenderChildren.svelte'
5
6
  import { getNodeList, getString } from './shared/node-helpers'
@@ -13,11 +14,12 @@
13
14
 
14
15
  let { node, context, indexKey, showTooltip }: Props = $props();
15
16
 
16
- let href = $derived(getString((node as any)?.href));
17
+ let href = $derived(sanitizeHtmlAttrs({ href: getString((node as any)?.href) }, 'safe', 'a').href ?? '');
17
18
  let title = $derived(getString((node as any)?.title || href));
18
19
  let children = $derived(getNodeList((node as any)?.children));
19
20
  let tooltipEnabled = $derived(showTooltip ?? context?.showTooltips ?? true);
20
21
  let isHashLink = $derived(href.startsWith('#') && href.length > 1);
22
+ let openInNewTab = $derived(shouldOpenLinkInNewTab(href));
21
23
 
22
24
  function showLinkTooltip(event: MouseEvent | FocusEvent) {
23
25
  if (!tooltipEnabled || !title)
@@ -44,4 +46,4 @@
44
46
  }
45
47
  }
46
48
  </script>
47
- <a class:link-loading={Boolean((node as any)?.loading)} class="link-node" href={href || undefined} title={tooltipEnabled ? undefined : title} onblur={() => hideTooltip()} onclick={scrollToHashTarget} onfocus={showLinkTooltip} onmouseleave={() => hideTooltip()} onmouseenter={showLinkTooltip} target={isHashLink ? undefined : '_blank'} rel={isHashLink ? undefined : 'noreferrer noopener'}><span class="link-text-wrapper"><span class="link-text">{#if children.length}<RenderChildren nodes={children} context={context} prefix={String(indexKey ?? 'link') + '-link'} />{:else}{getString((node as any)?.text || href)}{/if}</span>{#if (node as any)?.loading}<span class="link-loading-indicator"></span>{/if}</span></a>
49
+ <a class:link-loading={Boolean((node as any)?.loading)} class="link-node" href={href || undefined} title={tooltipEnabled ? undefined : title} onblur={() => hideTooltip()} onclick={scrollToHashTarget} onfocus={showLinkTooltip} onmouseleave={() => hideTooltip()} onmouseenter={showLinkTooltip} target={openInNewTab ? '_blank' : undefined} rel={openInNewTab ? 'noreferrer noopener' : undefined}><span class="link-text-wrapper"><span class="link-text">{#if children.length}<RenderChildren nodes={children} context={context} prefix={String(indexKey ?? 'link') + '-link'} />{:else}{getString((node as any)?.text || href)}{/if}</span>{#if (node as any)?.loading}<span class="link-loading-indicator"></span>{/if}</span></a>
@@ -1,9 +1,9 @@
1
1
  <script lang="ts">
2
2
  import type { SvelteRenderableNode, SvelteRenderContext } from './shared/node-helpers'
3
- import { onMount, untrack } from 'svelte'
3
+ import { onMount, tick, untrack } from 'svelte'
4
+ import { toSafeMermaidSvgMarkup } from 'stream-markdown-parser'
4
5
  import { useSafeI18n } from '../i18n/useSafeI18n'
5
6
  import { getMermaid } from '../optional/mermaid'
6
- import { toSafeSvgMarkup } from '../sanitizeSvg'
7
7
  import { hideTooltip, showTooltipForAnchor, type TooltipPlacement } from '../tooltip/singletonTooltip'
8
8
  import { getLanguageIcon } from '../utils/languageIcon'
9
9
  import { canParseOffthread, findPrefixOffthread } from '../workers/mermaidWorkerClient'
@@ -33,6 +33,7 @@
33
33
  showCollapseButton?: boolean
34
34
  showZoomControls?: boolean
35
35
  isStrict?: boolean
36
+ enableMermaidInteractions?: boolean
36
37
  }
37
38
 
38
39
  let {
@@ -55,6 +56,7 @@
55
56
  showCollapseButton = true,
56
57
  showZoomControls = true,
57
58
  isStrict = true,
59
+ enableMermaidInteractions = false,
58
60
  }: Props = $props()
59
61
 
60
62
  const { t } = useSafeI18n()
@@ -75,8 +77,11 @@
75
77
  let showSource = $state(false)
76
78
  let modalOpen = $state(false)
77
79
  let zoom = $state(1)
80
+ let previewHost: HTMLElement | null = $state(null)
81
+ let modalHost: HTMLElement | null = $state(null)
78
82
  let renderTimer: ReturnType<typeof setTimeout> | null = $state(null)
79
83
  let copyTimer: ReturnType<typeof setTimeout> | null = $state(null)
84
+ let lastMermaidBindFunctions: ((element: Element) => unknown) | null = null
80
85
 
81
86
  let source = $derived(normalizeMermaidSource(getString((node as any)?.code)))
82
87
  let nodeLoading = $derived(typeof (node as any)?.loading === 'boolean' ? Boolean((node as any)?.loading) : true)
@@ -121,6 +126,11 @@
121
126
  }
122
127
  })
123
128
 
129
+ $effect(() => {
130
+ if (mounted && modalOpen && svgMarkup && modalHost && enableMermaidInteractions)
131
+ untrack(() => void tick().then(() => bindCurrentMermaidInteractions(modalHost)))
132
+ })
133
+
124
134
  function clearRenderTimer() {
125
135
  if (!renderTimer)
126
136
  return
@@ -239,10 +249,11 @@
239
249
  return
240
250
 
241
251
  const rawSvg = typeof rendered === 'string' ? rendered : rendered?.svg
242
- const safeSvg = isStrict ? toSafeSvgMarkup(rawSvg) : rawSvg
252
+ const safeSvg = toSafeMermaidSvgMarkup(rawSvg)
243
253
  if (!safeSvg)
244
254
  throw new Error('Mermaid rendered empty SVG.')
245
255
  svgMarkup = safeSvg
256
+ lastMermaidBindFunctions = typeof rendered === 'string' ? null : rendered?.bindFunctions ?? null
246
257
  renderError = ''
247
258
  lastProgressiveMissSignature = ''
248
259
  if (fullRender) {
@@ -250,8 +261,14 @@
250
261
  lastRenderedCode = normalized
251
262
  svgCache[theme] = safeSvg
252
263
  }
253
- if (typeof rendered !== 'string')
254
- rendered?.bindFunctions?.(document.createElement('div'))
264
+ if (enableMermaidInteractions && typeof rendered !== 'string') {
265
+ await tick()
266
+ if (mounted && token === renderToken) {
267
+ bindCurrentMermaidInteractions(previewHost)
268
+ if (modalOpen)
269
+ bindCurrentMermaidInteractions(modalHost)
270
+ }
271
+ }
255
272
  }
256
273
  catch (error) {
257
274
  if (token === renderToken) {
@@ -385,7 +402,7 @@
385
402
  }
386
403
 
387
404
  function getRenderSignature() {
388
- return `${source}\n${theme}\n${isStrict}\n${final}\n${progressivePreview}`
405
+ return `${source}\n${theme}\n${isStrict}\n${enableMermaidInteractions}\n${final}\n${progressivePreview}`
389
406
  }
390
407
 
391
408
  function withTimeout<T>(run: () => Promise<T>, timeoutMs: number) {
@@ -403,6 +420,15 @@
403
420
  })
404
421
  }
405
422
 
423
+ function bindCurrentMermaidInteractions(element: Element | null | undefined) {
424
+ if (!enableMermaidInteractions || !element?.querySelector('svg'))
425
+ return
426
+ try {
427
+ lastMermaidBindFunctions?.(element)
428
+ }
429
+ catch {}
430
+ }
431
+
406
432
  async function copy() {
407
433
  await copyTextToClipboard(source)
408
434
  context?.events?.onCopy?.(source)
@@ -543,7 +569,7 @@
543
569
  <button type="button" class="mermaid-btn mermaid-action-btn mermaid-zoom-reset" aria-label={t('common.resetZoom')} onblur={() => hideTooltip()} onclick={() => (zoom = 1)} onfocus={(event) => showButtonTooltip(event, t('common.resetZoom') || 'Reset zoom')} onmouseleave={() => hideTooltip()} onmouseenter={(event) => showButtonTooltip(event, t('common.resetZoom') || 'Reset zoom')}>{Math.round(zoom * 100)}%</button>
544
570
  </div>
545
571
  {/if}
546
- <div class="mermaid-preview markstream-svelte-mermaid" style={previewStyle}>
572
+ <div bind:this={previewHost} class="mermaid-preview markstream-svelte-mermaid" style={previewStyle}>
547
573
  {#if svgMarkup}
548
574
  {@html svgMarkup}
549
575
  {:else if renderError}
@@ -576,7 +602,7 @@
576
602
  </button>
577
603
  </div>
578
604
  <div class="mermaid-modal-body">
579
- <div class="mermaid-modal-content markstream-svelte-mermaid" style={`transform: scale(${zoom});`}>{@html svgMarkup}</div>
605
+ <div bind:this={modalHost} class="mermaid-modal-content markstream-svelte-mermaid" style={`transform: scale(${zoom});`}>{@html svgMarkup}</div>
580
606
  </div>
581
607
  </div>
582
608
  </div>
@@ -19,6 +19,7 @@ type Props = {
19
19
  showCollapseButton?: boolean;
20
20
  showZoomControls?: boolean;
21
21
  isStrict?: boolean;
22
+ enableMermaidInteractions?: boolean;
22
23
  };
23
24
  declare const MermaidBlockNode: import("svelte").Component<Props, {}, "">;
24
25
  type MermaidBlockNode = ReturnType<typeof MermaidBlockNode>;
@@ -84,8 +84,9 @@
84
84
  let renderBatchToken = 0
85
85
  const textStreamState = new Map<string, string>()
86
86
 
87
- const smoothStream = useSmoothMarkdownStream(smoothStreamingOptions)
88
- let hasMountedForSmoothStreaming = $state(typeof window === 'undefined' || smoothStreaming === true)
87
+ let hasMounted = $state(false)
88
+ const smoothStream = useSmoothMarkdownStream(() => smoothStreamingOptions)
89
+ const hasMountedForSmoothStreaming = $derived(smoothStreaming === true || hasMounted)
89
90
  const hasNodes = $derived(Array.isArray(nodes))
90
91
  const parentSmoothStreaming = getContext<SmoothStreamingContextValue | undefined>(
91
92
  SMOOTH_STREAMING_CONTEXT,
@@ -107,18 +108,6 @@
107
108
  const smoothStreamingEnabled = $derived(hasMountedForSmoothStreaming && smoothStreamingEligible)
108
109
  setContext(SMOOTH_STREAMING_CONTEXT, () => smoothStreamingEnabled)
109
110
 
110
- // Baseline sync: in auto mode with initial static content, ensure the smooth
111
- // stream source/visible is already synced before smooth streaming activates.
112
- // This prevents a blank flash when the mount gate opens and renderContent
113
- // switches from raw content to smoothStream.visible (which would be empty).
114
- if (
115
- smoothStreaming !== true
116
- && !Array.isArray(nodes)
117
- && content
118
- ) {
119
- smoothStream.reset(content)
120
- }
121
-
122
111
  const renderContent = $derived(smoothStreamingEnabled ? smoothStream.visible : (content ?? ''))
123
112
  const rawContent = $derived(content ?? '')
124
113
  const smoothSourceSynced = $derived(hasNodes || smoothStream.source === rawContent)
@@ -133,7 +122,7 @@
133
122
  })
134
123
 
135
124
  onMount(() => {
136
- hasMountedForSmoothStreaming = true
125
+ hasMounted = true
137
126
  })
138
127
 
139
128
  $effect(() => {
@@ -46,6 +46,7 @@ export type NodeRendererMermaidProps = Partial<{
46
46
  showCollapseButton: boolean;
47
47
  showZoomControls: boolean;
48
48
  isStrict: boolean;
49
+ enableMermaidInteractions: boolean;
49
50
  }> & Record<string, unknown>;
50
51
  export type NodeRendererD2Props = Partial<{
51
52
  maxHeight: string | null;
@@ -16,4 +16,4 @@ export interface SmoothMarkdownStreamControllerSvelte {
16
16
  pause: () => void;
17
17
  resume: () => void;
18
18
  }
19
- export declare function useSmoothMarkdownStream(options?: SmoothMarkdownStreamOptions): SmoothMarkdownStreamControllerSvelte;
19
+ export declare function useSmoothMarkdownStream(optionsOrGetter?: SmoothMarkdownStreamOptions | (() => SmoothMarkdownStreamOptions)): SmoothMarkdownStreamControllerSvelte;
@@ -1,6 +1,7 @@
1
1
  import { createSmoothMarkdownStream } from 'markstream-core';
2
2
  import { onDestroy } from 'svelte';
3
- export function useSmoothMarkdownStream(options = {}) {
3
+ export function useSmoothMarkdownStream(optionsOrGetter = {}) {
4
+ const options = typeof optionsOrGetter === 'function' ? optionsOrGetter() : optionsOrGetter;
4
5
  let source = $state('');
5
6
  let visible = $state('');
6
7
  let done = $state(false);
@@ -1,3 +1,4 @@
1
+ import { toSafeMermaidSvgMarkup } from 'stream-markdown-parser';
1
2
  import { getD2 } from './optional/d2';
2
3
  import { getInfographic } from './optional/infographic';
3
4
  import { getKatex } from './optional/katex';
@@ -212,7 +213,7 @@ async function renderKatexMarkup(source, displayMode) {
212
213
  return html;
213
214
  }
214
215
  async function renderMermaid(root, cleanupFns, options, isActive) {
215
- var _a, _b, _c, _d, _e, _f, _g;
216
+ var _a, _b, _c, _d, _e, _f, _g, _h;
216
217
  const strictMode = ((_a = options.mermaidProps) === null || _a === void 0 ? void 0 : _a.isStrict) !== false;
217
218
  const mermaid = await getMermaid({
218
219
  startOnLoad: false,
@@ -264,14 +265,18 @@ async function renderMermaid(root, cleanupFns, options, isActive) {
264
265
  const svg = typeof rendered === 'string' ? rendered : rendered === null || rendered === void 0 ? void 0 : rendered.svg;
265
266
  if (!svg)
266
267
  continue;
267
- const safeSvg = strictMode ? toSafeSvgMarkup(svg) : svg;
268
+ const safeSvg = toSafeMermaidSvgMarkup(svg);
268
269
  if (!safeSvg)
269
270
  continue;
270
271
  shell.body.innerHTML = safeSvg;
271
272
  shell.body.classList.add('markstream-svelte-mermaid');
272
273
  shell.wrapper.dataset.markstreamMermaid = '1';
273
- if (typeof rendered !== 'string')
274
- (_g = rendered === null || rendered === void 0 ? void 0 : rendered.bindFunctions) === null || _g === void 0 ? void 0 : _g.call(rendered, shell.body);
274
+ if (((_g = options.mermaidProps) === null || _g === void 0 ? void 0 : _g.enableMermaidInteractions) === true && typeof rendered !== 'string') {
275
+ try {
276
+ (_h = rendered === null || rendered === void 0 ? void 0 : rendered.bindFunctions) === null || _h === void 0 ? void 0 : _h.call(rendered, shell.body);
277
+ }
278
+ catch { }
279
+ }
275
280
  cleanupFns.push(() => {
276
281
  if (shell.wrapper.isConnected)
277
282
  shell.wrapper.replaceWith(originalPre.cloneNode(true));
@@ -1,4 +1,4 @@
1
- import { getMarkdown, isHtmlTagBlocked, isUnsafeHtmlUrl, NON_STRUCTURING_HTML_TAGS, normalizeCustomHtmlTagName, normalizeCustomHtmlTags, parseMarkdownToStructure, sanitizeHtmlAttrs, } from 'stream-markdown-parser';
1
+ import { getMarkdown, isHtmlTagBlocked, isUnsafeHtmlUrl, NON_STRUCTURING_HTML_TAGS, normalizeCustomHtmlTagName, normalizeCustomHtmlTags, parseMarkdownToStructure, sanitizeHtmlAttrs, sanitizeImageSrc, shouldOpenLinkInNewTab, } from 'stream-markdown-parser';
2
2
  import { hydrateCustomTagContent } from './hydrateCustomTagContent';
3
3
  import { sanitizeHtmlContent } from './sanitizeHtmlContent';
4
4
  const DEFAULT_CACHE_KEY = 'markstream-svelte-html';
@@ -224,8 +224,9 @@ function renderLinkNode(node, ctx) {
224
224
  ? renderNodesToHtml(getNodeList(node.children), ctx)
225
225
  : escapeHtml(getString(node.text || href));
226
226
  const titleAttr = title ? ` title="${escapeAttr(title)}"` : '';
227
- const hrefAttr = href && !isUnsafeHtmlUrl(href) ? ` href="${escapeAttr(href)}"` : '';
228
- const externalAttrs = href.startsWith('#') ? '' : ' target="_blank" rel="noreferrer noopener"';
227
+ const safeHref = href && !isUnsafeHtmlUrl(href) ? href : '';
228
+ const hrefAttr = safeHref ? ` href="${escapeAttr(safeHref)}"` : '';
229
+ const externalAttrs = shouldOpenLinkInNewTab(safeHref) ? ' target="_blank" rel="noreferrer noopener"' : '';
229
230
  return `<a${hrefAttr}${titleAttr}${externalAttrs}>${content}</a>`;
230
231
  }
231
232
  function renderMathInlineNode(node) {
@@ -237,7 +238,9 @@ function renderMathBlockNode(node) {
237
238
  return `<div class="markstream-nested-math-block"><pre class="markstream-nested-math-block__source"><code>${source}</code></pre><div class="markstream-nested-math-block__render" aria-hidden="true"></div></div>`;
238
239
  }
239
240
  function renderImageNode(node) {
240
- const src = getString(node.src);
241
+ const src = sanitizeImageSrc(node.src);
242
+ if (!src)
243
+ return '';
241
244
  const alt = getString(node.alt);
242
245
  const title = getString(node.title);
243
246
  const titleAttr = title ? ` title="${escapeAttr(title)}"` : '';
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "markstream-svelte",
3
3
  "type": "module",
4
- "version": "0.0.1-beta.5",
5
- "description": "Svelte Markdown renderer for Markstream, aligned with markstream-vue and markstream-react.",
4
+ "version": "0.0.1",
5
+ "description": "Svelte 5 and SvelteKit streaming Markdown renderer for AI chat, LLM token streams, SSE/WebSocket output, incomplete Markdown, Mermaid, KaTeX, Monaco-powered code blocks, and custom Svelte components.",
6
6
  "author": "Simon He",
7
7
  "license": "MIT",
8
- "homepage": "https://github.com/Simon-He95/markstream-vue#readme",
8
+ "homepage": "https://markstream.simonhe.me/frameworks/svelte",
9
9
  "repository": {
10
10
  "type": "git",
11
11
  "url": "git+https://github.com/Simon-He95/markstream-vue.git",
@@ -16,11 +16,40 @@
16
16
  },
17
17
  "keywords": [
18
18
  "svelte",
19
+ "svelte5",
20
+ "sveltekit",
19
21
  "markdown",
20
22
  "markdown-renderer",
23
+ "svelte-markdown",
24
+ "svelte-markdown-renderer",
25
+ "svelte5-markdown",
26
+ "sveltekit-markdown",
27
+ "svelte-streaming-markdown",
28
+ "svelte-ai-chat",
21
29
  "streaming-markdown",
22
- "markstream",
23
- "markstream-svelte"
30
+ "ai-markdown-renderer",
31
+ "ai-chat",
32
+ "llm",
33
+ "llm-markdown",
34
+ "llm-streaming",
35
+ "sse",
36
+ "sse-markdown",
37
+ "websocket",
38
+ "websocket-markdown",
39
+ "incomplete-markdown",
40
+ "mermaid",
41
+ "streaming-mermaid",
42
+ "katex",
43
+ "streaming-katex",
44
+ "streaming-code-blocks",
45
+ "d2",
46
+ "infographic",
47
+ "monaco-editor",
48
+ "large-documents",
49
+ "custom-components",
50
+ "beta",
51
+ "markstream-svelte",
52
+ "markstream"
24
53
  ],
25
54
  "sideEffects": [
26
55
  "**/*.css"
@@ -49,7 +78,7 @@
49
78
  "@terrastruct/d2": ">=0.1.33",
50
79
  "katex": ">=0.16.22",
51
80
  "mermaid": ">=11",
52
- "stream-monaco": ">=0.0.40",
81
+ "stream-monaco": ">=0.0.45",
53
82
  "svelte": ">=5 <6"
54
83
  },
55
84
  "peerDependenciesMeta": {
@@ -71,20 +100,20 @@
71
100
  },
72
101
  "dependencies": {
73
102
  "@floating-ui/dom": "^1.7.6",
74
- "markstream-core": "0.0.1",
75
- "stream-markdown-parser": "0.0.95"
103
+ "markstream-core": "1.0.3",
104
+ "stream-markdown-parser": "1.0.5"
76
105
  },
77
106
  "devDependencies": {
78
- "@sveltejs/package": "^2.5.7",
107
+ "@sveltejs/package": "^2.5.8",
79
108
  "@sveltejs/vite-plugin-svelte": "^6.2.4",
80
109
  "@types/node": "^18.19.130",
81
- "autoprefixer": "^10.4.27",
110
+ "autoprefixer": "^10.5.0",
82
111
  "postcss-cli": "^11.0.1",
83
- "svelte": "^5.55.5",
84
- "svelte-check": "^4.4.8",
112
+ "svelte": "^5.56.3",
113
+ "svelte-check": "^4.6.0",
85
114
  "tailwindcss": "^3.4.19",
86
115
  "typescript": "^5.9.3",
87
- "vite": "^7.3.1"
116
+ "vite": "^7.3.5"
88
117
  },
89
118
  "scripts": {
90
119
  "build": "pnpm --dir ../markstream-core build && svelte-package -i src -o dist && postcss src/index.css -o dist/index.css && cp dist/index.css dist/index.tailwind.css && node ../../scripts/generate-px-css.mjs",
@@ -92,6 +121,7 @@
92
121
  "preview": "vite preview",
93
122
  "typecheck": "svelte-check --tsconfig ./tsconfig.json",
94
123
  "check:core-published": "node ../../scripts/check-core-published.mjs --package-json package.json --core-package-json ../markstream-core/package.json",
95
- "release": "pnpm run check:core-published && bumpp --commit --no-tag --no-push && pnpm publish --access public && node ../../scripts/tag-package.mjs --package-json package.json --push"
124
+ "check:workspace-deps-published": "node ../../scripts/check-workspace-deps-published.mjs --package-json package.json",
125
+ "release": "pnpm run check:workspace-deps-published && bumpp --commit --no-tag --no-push && pnpm publish --access public && node ../../scripts/tag-package.mjs --package-json package.json --push"
96
126
  }
97
127
  }