markstream-svelte 2.0.6 → 2.0.8

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.
@@ -47,7 +47,7 @@
47
47
  let {
48
48
  node,
49
49
  context = undefined,
50
- maxHeight = '500px',
50
+ maxHeight = undefined,
51
51
  loading = undefined,
52
52
  isDark = undefined,
53
53
  themeId = undefined,
@@ -80,7 +80,12 @@
80
80
  let resolvedIsDark = $derived(isDark ?? context?.isDark ?? false)
81
81
  let shouldRender = $derived(!(resolvedLoading && !source.trim()))
82
82
  let showSourceFallback = $derived(showSource || !svgMarkup)
83
- let renderStyle = $derived(maxHeight && maxHeight !== 'none' ? `max-height: ${maxHeight}` : '')
83
+ // Expose the same cap to the stylesheet (via --ms-d2-render-max-height)
84
+ // so the root svg can scale down proportionally to the container's
85
+ // max-height; see the .d2-svg svg rule in index.css.
86
+ let renderStyle = $derived(maxHeight
87
+ ? `max-height: ${maxHeight}; --ms-d2-render-max-height: ${maxHeight}`
88
+ : '')
84
89
 
85
90
  onMount(() => {
86
91
  mounted = true
@@ -5,7 +5,7 @@
5
5
  import { getInfographic } from '../optional/infographic'
6
6
  import { toSafeSvgMarkup } from '../sanitizeSvg'
7
7
  import { hideTooltip, showTooltipForAnchor } from '../tooltip/singletonTooltip'
8
- import { clampPreviewHeight, estimateInfographicPreviewHeight, parsePositiveNumber } from './shared/diagram-height'
8
+ import { clampPreviewHeight, estimateInfographicPreviewHeight, INFOGRAPHIC_PREVIEW_MIN_HEIGHT, parsePositiveNumber, resolveDiagramMinPreviewHeight } from './shared/diagram-height'
9
9
  import { clearElement, copyTextToClipboard, downloadSvgMarkup } from './shared/rich-block-helpers'
10
10
  import { getString } from './shared/node-helpers'
11
11
 
@@ -45,6 +45,7 @@
45
45
  const infographicIcon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true"><path fill="#5b8ff9" d="M5 4.25A2.25 2.25 0 0 1 7.25 2h2.5A2.25 2.25 0 0 1 12 4.25v2.5A2.25 2.25 0 0 1 9.75 9h-2.5A2.25 2.25 0 0 1 5 6.75z"/><path fill="#61d9a6" d="M12 17.25A2.25 2.25 0 0 1 14.25 15h2.5A2.25 2.25 0 0 1 19 17.25v2.5A2.25 2.25 0 0 1 16.75 22h-2.5A2.25 2.25 0 0 1 12 19.75z"/><path fill="#f6bd16" d="M12 4.25A2.25 2.25 0 0 1 14.25 2h2.5A2.25 2.25 0 0 1 19 4.25v2.5A2.25 2.25 0 0 1 16.75 9h-2.5A2.25 2.25 0 0 1 12 6.75z"/><path fill="#5ad8a6" d="M7.5 10.75h1.75a6.25 6.25 0 0 1 6.25 6.25v.25h-2V17a4.25 4.25 0 0 0-4.25-4.25H7.5z"/><path fill="#7262fd" d="M15.5 11.1l3.75 2.16v4.33l-3.75 2.16l-3.75-2.16v-4.33z"/></svg>'
46
46
 
47
47
  let renderHost: HTMLDivElement | null = $state(null)
48
+ let blockHost: HTMLElement | null = $state(null)
48
49
  let mounted = $state(false)
49
50
  let renderToken = $state(0)
50
51
  let lastCompletedRenderSignature = $state('')
@@ -75,7 +76,7 @@
75
76
  let maxPreviewHeight = $derived(maxHeight === 'none' ? null : parsePositiveNumber(maxHeight))
76
77
  let previewHeight = $derived(clampPreviewHeight(
77
78
  parsePositiveNumber(estimatedPreviewHeightPx) ?? estimateInfographicPreviewHeight(source),
78
- 320,
79
+ resolveDiagramMinPreviewHeight(blockHost, INFOGRAPHIC_PREVIEW_MIN_HEIGHT),
79
80
  maxPreviewHeight ?? undefined,
80
81
  ))
81
82
  let previewStyle = $derived([
@@ -379,6 +380,7 @@
379
380
 
380
381
  {#if shouldRender}
381
382
  <div
383
+ bind:this={blockHost}
382
384
  class:dark={resolvedIsDark}
383
385
  class:is-rendering={rendering || resolvedLoading}
384
386
  class="markstream-svelte-enhanced-block markstream-svelte-enhanced-block--infographic"
@@ -7,7 +7,7 @@
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'
10
- import { clampPreviewHeight, estimateMermaidPreviewHeight, getMermaidDiagramKind, parsePositiveNumber } from './shared/diagram-height'
10
+ import { clampPreviewHeight, estimateMermaidPreviewHeight, getMermaidDiagramKind, MERMAID_PREVIEW_MIN_HEIGHT, parsePositiveNumber, resolveDiagramMinPreviewHeight } from './shared/diagram-height'
11
11
  import { copyTextToClipboard, downloadSvgMarkup } from './shared/rich-block-helpers'
12
12
  import { getString } from './shared/node-helpers'
13
13
 
@@ -77,6 +77,7 @@
77
77
  let showSource = $state(false)
78
78
  let modalOpen = $state(false)
79
79
  let zoom = $state(1)
80
+ let blockHost: HTMLElement | null = $state(null)
80
81
  let previewHost: HTMLElement | null = $state(null)
81
82
  let modalHost: HTMLElement | null = $state(null)
82
83
  let renderTimer: ReturnType<typeof setTimeout> | null = $state(null)
@@ -93,7 +94,7 @@
93
94
  let maxPreviewHeight = $derived(maxHeight === 'none' ? null : parsePositiveNumber(maxHeight))
94
95
  let previewHeight = $derived(clampPreviewHeight(
95
96
  parsePositiveNumber(estimatedPreviewHeightPx) ?? estimateMermaidPreviewHeight(source),
96
- undefined,
97
+ resolveDiagramMinPreviewHeight(blockHost, MERMAID_PREVIEW_MIN_HEIGHT),
97
98
  maxPreviewHeight ?? undefined,
98
99
  ))
99
100
  let previewStyle = $derived([
@@ -496,6 +497,7 @@
496
497
 
497
498
  {#if shouldRender}
498
499
  <div
500
+ bind:this={blockHost}
499
501
  class:dark={resolvedIsDark}
500
502
  class:is-rendering={resolvedLoading || rendering}
501
503
  class="mermaid-block"
@@ -7,3 +7,10 @@ export declare function getMermaidDiagramKind(code: string): string;
7
7
  export declare function estimateMermaidPreviewHeight(code: string): number;
8
8
  export declare function estimateInfographicPreviewHeight(code: string): number;
9
9
  export declare function clampPreviewHeight(height: number, minHeight?: number, maxHeight?: number | null): number;
10
+ /**
11
+ * Resolve the diagram preview minimum height from the CSS token
12
+ * (`--ms-size-diagram-min-height`), falling back to the provided default.
13
+ * SSR-safe: without a mounted container (Svelte SSR) the token is unreadable,
14
+ * so the fallback applies instead.
15
+ */
16
+ export declare function resolveDiagramMinPreviewHeight(container: HTMLElement | null | undefined, fallback: number): number;
@@ -50,3 +50,16 @@ export function clampPreviewHeight(height, minHeight = MERMAID_PREVIEW_MIN_HEIGH
50
50
  ? Math.max(minHeight, height)
51
51
  : Math.min(Math.max(minHeight, height), maxHeight);
52
52
  }
53
+ /**
54
+ * Resolve the diagram preview minimum height from the CSS token
55
+ * (`--ms-size-diagram-min-height`), falling back to the provided default.
56
+ * SSR-safe: without a mounted container (Svelte SSR) the token is unreadable,
57
+ * so the fallback applies instead.
58
+ */
59
+ export function resolveDiagramMinPreviewHeight(container, fallback) {
60
+ var _a;
61
+ const raw = container
62
+ ? getComputedStyle(container).getPropertyValue('--ms-size-diagram-min-height').trim()
63
+ : '';
64
+ return (_a = parsePositiveNumber(raw)) !== null && _a !== void 0 ? _a : fallback;
65
+ }
package/dist/index.css CHANGED
@@ -67,6 +67,7 @@ markstream-svelte {
67
67
  --ms-flow-diagram-y: 1.5em;
68
68
  --ms-size-image-max-width: 24rem;
69
69
  --ms-size-diagram-min-height: 360px;
70
+ --ms-size-code-max-height: 500px;
70
71
  --ms-duration-standard: 180ms;
71
72
  --ms-duration-fast: 120ms;
72
73
  --ms-duration-emphasis: 220ms;
@@ -2119,17 +2120,37 @@ body > div[id^="dmermaid-"] {
2119
2120
  white-space: inherit;
2120
2121
  }
2121
2122
 
2123
+ /* Center the rendered diagram like the mermaid/infographic blocks: a
2124
+ column flex container with margin:auto children stays centered while
2125
+ remaining scroll-safe when content overflows. */
2122
2126
  .markstream-svelte .d2-render {
2123
2127
  max-height: var(--ms-size-code-max-height);
2124
2128
  overflow: auto;
2129
+ display: flex;
2130
+ flex-direction: column;
2125
2131
  }
2126
2132
 
2127
- .markstream-svelte .d2-svg svg,
2128
- .markstream-svelte .infographic-render svg {
2133
+ /* margin: auto (not justify-content: center) keeps overflow scrolling safe. */
2134
+ .markstream-svelte .d2-svg {
2135
+ margin: auto;
2136
+ width: 100%;
2137
+ }
2138
+
2139
+ .markstream-svelte .d2-render .d2-error {
2140
+ margin-left: auto;
2141
+ margin-right: auto;
2142
+ }
2143
+
2144
+ .markstream-svelte .d2-svg svg.markstream-d2-root-svg {
2145
+ width: 100%;
2129
2146
  max-width: 100%;
2130
2147
  height: auto;
2148
+ /* Match the container cap so tall diagrams scale down to fit, mirroring
2149
+ the mermaid block's max-height behavior. The inline maxHeight prop
2150
+ exposes its value as --ms-d2-render-max-height so the svg cap always
2151
+ tracks the effective container cap. The fallback is the shared token. */
2152
+ max-height: var(--ms-d2-render-max-height, var(--ms-size-code-max-height));
2131
2153
  display: block;
2132
- margin: 0 auto;
2133
2154
  }
2134
2155
 
2135
2156
  .markstream-svelte .d2-error {
@@ -2140,7 +2161,7 @@ body > div[id^="dmermaid-"] {
2140
2161
  .markstream-svelte .infographic-render {
2141
2162
  position: relative;
2142
2163
  width: 100%;
2143
- min-height: 320px;
2164
+ min-height: var(--ms-size-diagram-min-height);
2144
2165
  overflow: auto;
2145
2166
  text-align: center;
2146
2167
  }
@@ -2154,6 +2175,25 @@ body > div[id^="dmermaid-"] {
2154
2175
  text-align: left;
2155
2176
  }
2156
2177
 
2178
+ /* The render host mirrors the mermaid/vue diagram layers: an inset-0
2179
+ layer with a definite height (the inline min-height estimate on
2180
+ .infographic-render) that flex-centers the rendered diagram. */
2157
2181
  .markstream-svelte .infographic-render > div {
2182
+ position: absolute;
2183
+ inset: 0;
2184
+ display: flex;
2185
+ align-items: center;
2186
+ justify-content: center;
2158
2187
  transition: transform 120ms ease;
2159
2188
  }
2189
+
2190
+ /* Keep the diagram fully visible and centered like the mermaid block:
2191
+ scale down proportionally inside the preview area. Scoped to the
2192
+ preview container only; the fullscreen modal clone (portaled to body
2193
+ without this wrapper) keeps its own sizing. */
2194
+ .markstream-svelte .infographic-render svg {
2195
+ max-width: 100%;
2196
+ max-height: 100%;
2197
+ width: auto;
2198
+ height: auto;
2199
+ }
package/dist/index.px.css CHANGED
@@ -67,6 +67,7 @@ markstream-svelte {
67
67
  --ms-flow-diagram-y: 1.5em;
68
68
  --ms-size-image-max-width: 384px;
69
69
  --ms-size-diagram-min-height: 360px;
70
+ --ms-size-code-max-height: 500px;
70
71
  --ms-duration-standard: 180ms;
71
72
  --ms-duration-fast: 120ms;
72
73
  --ms-duration-emphasis: 220ms;
@@ -2119,17 +2120,37 @@ body > div[id^="dmermaid-"] {
2119
2120
  white-space: inherit;
2120
2121
  }
2121
2122
 
2123
+ /* Center the rendered diagram like the mermaid/infographic blocks: a
2124
+ column flex container with margin:auto children stays centered while
2125
+ remaining scroll-safe when content overflows. */
2122
2126
  .markstream-svelte .d2-render {
2123
2127
  max-height: var(--ms-size-code-max-height);
2124
2128
  overflow: auto;
2129
+ display: flex;
2130
+ flex-direction: column;
2125
2131
  }
2126
2132
 
2127
- .markstream-svelte .d2-svg svg,
2128
- .markstream-svelte .infographic-render svg {
2133
+ /* margin: auto (not justify-content: center) keeps overflow scrolling safe. */
2134
+ .markstream-svelte .d2-svg {
2135
+ margin: auto;
2136
+ width: 100%;
2137
+ }
2138
+
2139
+ .markstream-svelte .d2-render .d2-error {
2140
+ margin-left: auto;
2141
+ margin-right: auto;
2142
+ }
2143
+
2144
+ .markstream-svelte .d2-svg svg.markstream-d2-root-svg {
2145
+ width: 100%;
2129
2146
  max-width: 100%;
2130
2147
  height: auto;
2148
+ /* Match the container cap so tall diagrams scale down to fit, mirroring
2149
+ the mermaid block's max-height behavior. The inline maxHeight prop
2150
+ exposes its value as --ms-d2-render-max-height so the svg cap always
2151
+ tracks the effective container cap. The fallback is the shared token. */
2152
+ max-height: var(--ms-d2-render-max-height, var(--ms-size-code-max-height));
2131
2153
  display: block;
2132
- margin: 0 auto;
2133
2154
  }
2134
2155
 
2135
2156
  .markstream-svelte .d2-error {
@@ -2140,7 +2161,7 @@ body > div[id^="dmermaid-"] {
2140
2161
  .markstream-svelte .infographic-render {
2141
2162
  position: relative;
2142
2163
  width: 100%;
2143
- min-height: 320px;
2164
+ min-height: var(--ms-size-diagram-min-height);
2144
2165
  overflow: auto;
2145
2166
  text-align: center;
2146
2167
  }
@@ -2154,6 +2175,25 @@ body > div[id^="dmermaid-"] {
2154
2175
  text-align: left;
2155
2176
  }
2156
2177
 
2178
+ /* The render host mirrors the mermaid/vue diagram layers: an inset-0
2179
+ layer with a definite height (the inline min-height estimate on
2180
+ .infographic-render) that flex-centers the rendered diagram. */
2157
2181
  .markstream-svelte .infographic-render > div {
2182
+ position: absolute;
2183
+ inset: 0;
2184
+ display: flex;
2185
+ align-items: center;
2186
+ justify-content: center;
2158
2187
  transition: transform 120ms ease;
2159
2188
  }
2189
+
2190
+ /* Keep the diagram fully visible and centered like the mermaid block:
2191
+ scale down proportionally inside the preview area. Scoped to the
2192
+ preview container only; the fullscreen modal clone (portaled to body
2193
+ without this wrapper) keeps its own sizing. */
2194
+ .markstream-svelte .infographic-render svg {
2195
+ max-width: 100%;
2196
+ max-height: 100%;
2197
+ width: auto;
2198
+ height: auto;
2199
+ }
@@ -67,6 +67,7 @@ markstream-svelte {
67
67
  --ms-flow-diagram-y: 1.5em;
68
68
  --ms-size-image-max-width: 24rem;
69
69
  --ms-size-diagram-min-height: 360px;
70
+ --ms-size-code-max-height: 500px;
70
71
  --ms-duration-standard: 180ms;
71
72
  --ms-duration-fast: 120ms;
72
73
  --ms-duration-emphasis: 220ms;
@@ -2119,17 +2120,37 @@ body > div[id^="dmermaid-"] {
2119
2120
  white-space: inherit;
2120
2121
  }
2121
2122
 
2123
+ /* Center the rendered diagram like the mermaid/infographic blocks: a
2124
+ column flex container with margin:auto children stays centered while
2125
+ remaining scroll-safe when content overflows. */
2122
2126
  .markstream-svelte .d2-render {
2123
2127
  max-height: var(--ms-size-code-max-height);
2124
2128
  overflow: auto;
2129
+ display: flex;
2130
+ flex-direction: column;
2125
2131
  }
2126
2132
 
2127
- .markstream-svelte .d2-svg svg,
2128
- .markstream-svelte .infographic-render svg {
2133
+ /* margin: auto (not justify-content: center) keeps overflow scrolling safe. */
2134
+ .markstream-svelte .d2-svg {
2135
+ margin: auto;
2136
+ width: 100%;
2137
+ }
2138
+
2139
+ .markstream-svelte .d2-render .d2-error {
2140
+ margin-left: auto;
2141
+ margin-right: auto;
2142
+ }
2143
+
2144
+ .markstream-svelte .d2-svg svg.markstream-d2-root-svg {
2145
+ width: 100%;
2129
2146
  max-width: 100%;
2130
2147
  height: auto;
2148
+ /* Match the container cap so tall diagrams scale down to fit, mirroring
2149
+ the mermaid block's max-height behavior. The inline maxHeight prop
2150
+ exposes its value as --ms-d2-render-max-height so the svg cap always
2151
+ tracks the effective container cap. The fallback is the shared token. */
2152
+ max-height: var(--ms-d2-render-max-height, var(--ms-size-code-max-height));
2131
2153
  display: block;
2132
- margin: 0 auto;
2133
2154
  }
2134
2155
 
2135
2156
  .markstream-svelte .d2-error {
@@ -2140,7 +2161,7 @@ body > div[id^="dmermaid-"] {
2140
2161
  .markstream-svelte .infographic-render {
2141
2162
  position: relative;
2142
2163
  width: 100%;
2143
- min-height: 320px;
2164
+ min-height: var(--ms-size-diagram-min-height);
2144
2165
  overflow: auto;
2145
2166
  text-align: center;
2146
2167
  }
@@ -2154,6 +2175,25 @@ body > div[id^="dmermaid-"] {
2154
2175
  text-align: left;
2155
2176
  }
2156
2177
 
2178
+ /* The render host mirrors the mermaid/vue diagram layers: an inset-0
2179
+ layer with a definite height (the inline min-height estimate on
2180
+ .infographic-render) that flex-centers the rendered diagram. */
2157
2181
  .markstream-svelte .infographic-render > div {
2182
+ position: absolute;
2183
+ inset: 0;
2184
+ display: flex;
2185
+ align-items: center;
2186
+ justify-content: center;
2158
2187
  transition: transform 120ms ease;
2159
2188
  }
2189
+
2190
+ /* Keep the diagram fully visible and centered like the mermaid block:
2191
+ scale down proportionally inside the preview area. Scoped to the
2192
+ preview container only; the fullscreen modal clone (portaled to body
2193
+ without this wrapper) keeps its own sizing. */
2194
+ .markstream-svelte .infographic-render svg {
2195
+ max-width: 100%;
2196
+ max-height: 100%;
2197
+ width: auto;
2198
+ height: auto;
2199
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "markstream-svelte",
3
3
  "type": "module",
4
- "version": "2.0.6",
4
+ "version": "2.0.8",
5
5
  "description": "Svelte 5 and SvelteKit streaming Markdown renderer for AI chat, LLM token streams, SSE/WebSocket output, incomplete Markdown, Mermaid, KaTeX, stream-diffs powered code blocks, and custom Svelte components.",
6
6
  "author": "Simon He",
7
7
  "license": "MIT",
@@ -100,8 +100,8 @@
100
100
  },
101
101
  "dependencies": {
102
102
  "@floating-ui/dom": "^1.8.0",
103
- "markstream-core": "2.0.6",
104
- "stream-markdown-parser": "1.2.12"
103
+ "markstream-core": "2.0.8",
104
+ "stream-markdown-parser": "1.2.14"
105
105
  },
106
106
  "devDependencies": {
107
107
  "@sveltejs/package": "^2.5.8",