markstream-svelte 2.0.12 → 2.0.14
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.
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { SvelteRenderableNode, SvelteRenderContext } from './shared/node-helpers'
|
|
3
|
+
import { createPanGesture } from 'markstream-core'
|
|
3
4
|
import { onDestroy, onMount, tick, untrack } from 'svelte'
|
|
4
5
|
import { useSafeI18n } from '../i18n/useSafeI18n'
|
|
5
6
|
import { getInfographic } from '../optional/infographic'
|
|
@@ -65,6 +66,9 @@
|
|
|
65
66
|
let modalOpen = $state(false)
|
|
66
67
|
let modalMarkup = $state('')
|
|
67
68
|
let zoom = $state(1)
|
|
69
|
+
let translateX = $state(0)
|
|
70
|
+
let translateY = $state(0)
|
|
71
|
+
let isDragging = $state(false)
|
|
68
72
|
let copyTimer: ReturnType<typeof setTimeout> | null = null
|
|
69
73
|
|
|
70
74
|
let source = $derived(getString((node as any)?.code))
|
|
@@ -82,10 +86,18 @@
|
|
|
82
86
|
let previewStyle = $derived([
|
|
83
87
|
`min-height: ${previewHeight}px`,
|
|
84
88
|
maxHeight && maxHeight !== 'none' ? `max-height: ${maxHeight}` : '',
|
|
89
|
+
`cursor: ${isDragging ? 'grabbing' : 'grab'}`,
|
|
85
90
|
].filter(Boolean).join('; '))
|
|
86
|
-
let transformStyle = $derived(`transform: scale(${zoom}); transform-origin: center center;`)
|
|
91
|
+
let transformStyle = $derived(`transform: translate(${translateX}px, ${translateY}px) scale(${zoom}); transform-origin: center center;`)
|
|
87
92
|
let shouldRender = $derived(!(resolvedLoading && !source.trim()))
|
|
88
93
|
|
|
94
|
+
// The diagram is scaled to fit the preview box, so at fit zoom nothing is
|
|
95
|
+
// off-screen and panning only shifts it inside the box. Touch therefore keeps
|
|
96
|
+
// scrolling the page at that zoom (a chat is mostly scrolling) and only claims
|
|
97
|
+
// the gesture once the diagram is zoomed in and actually overflows the box.
|
|
98
|
+
// The fullscreen modal always claims it.
|
|
99
|
+
let panSurfaceClaimsTouch = $derived(modalOpen || zoom > 1)
|
|
100
|
+
|
|
89
101
|
onMount(() => {
|
|
90
102
|
mounted = true
|
|
91
103
|
queueInfographicRender(true)
|
|
@@ -107,6 +119,8 @@
|
|
|
107
119
|
renderToken += 1
|
|
108
120
|
destroyInstance()
|
|
109
121
|
lastCompletedRenderSignature = ''
|
|
122
|
+
// A drag in progress must not keep its window listeners after unmount.
|
|
123
|
+
panGesture.stop()
|
|
110
124
|
if (copyTimer)
|
|
111
125
|
clearTimeout(copyTimer)
|
|
112
126
|
})
|
|
@@ -343,6 +357,39 @@
|
|
|
343
357
|
zoom = Math.max(0.5, Math.round((zoom - 0.1) * 10) / 10)
|
|
344
358
|
}
|
|
345
359
|
|
|
360
|
+
function resetZoom() {
|
|
361
|
+
zoom = 1
|
|
362
|
+
translateX = 0
|
|
363
|
+
translateY = 0
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Drag functionality. The gesture lifecycle (window listeners, pointer identity,
|
|
367
|
+
// lost releases) lives in markstream-core so every package pans identically.
|
|
368
|
+
const panGesture = createPanGesture({
|
|
369
|
+
getTranslate: () => ({ x: translateX, y: translateY }),
|
|
370
|
+
setTranslate: (next) => {
|
|
371
|
+
translateX = next.x
|
|
372
|
+
translateY = next.y
|
|
373
|
+
},
|
|
374
|
+
canStart: (event) => {
|
|
375
|
+
// Nothing to pan until the diagram exists. Without this the pending/error
|
|
376
|
+
// source panel the block renders in the preview would be claimed by the
|
|
377
|
+
// gesture, and `preventDefault` would take text selection away from it.
|
|
378
|
+
if (!hasPreview)
|
|
379
|
+
return false
|
|
380
|
+
|
|
381
|
+
// Touch pans only where the surface claims the gesture. Elsewhere the page
|
|
382
|
+
// keeps the swipe for scrolling, and panning along with it would shift the
|
|
383
|
+
// diagram by a few pixels before the browser takes the gesture over.
|
|
384
|
+
return event.pointerType !== 'touch' || panSurfaceClaimsTouch
|
|
385
|
+
},
|
|
386
|
+
onActiveChange: active => (isDragging = active),
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
function startDrag(event: PointerEvent) {
|
|
390
|
+
panGesture.start(event)
|
|
391
|
+
}
|
|
392
|
+
|
|
346
393
|
function showButtonTooltip(event: MouseEvent | FocusEvent, text: string) {
|
|
347
394
|
const target = event.currentTarget as HTMLElement | null
|
|
348
395
|
if (!target || (target instanceof HTMLButtonElement && target.disabled))
|
|
@@ -453,10 +500,11 @@
|
|
|
453
500
|
<button type="button" class="markstream-svelte-enhanced-block__action markstream-svelte-enhanced-block__action--icon infographic-action-btn" aria-label={t('common.zoomOut') || 'Zoom out'} onblur={() => hideTooltip()} onclick={zoomOut} onfocus={(event) => showButtonTooltip(event, t('common.zoomOut') || 'Zoom out')} onmouseleave={() => hideTooltip()} onmouseenter={(event) => showButtonTooltip(event, t('common.zoomOut') || 'Zoom out')}>
|
|
454
501
|
<svg aria-hidden="true" role="img" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21l-4.35-4.35M8 11h6"/></g></svg>
|
|
455
502
|
</button>
|
|
456
|
-
<button type="button" class="markstream-svelte-enhanced-block__action infographic-action-btn infographic-zoom-reset" onblur={() => hideTooltip()} onclick={
|
|
503
|
+
<button type="button" class="markstream-svelte-enhanced-block__action infographic-action-btn infographic-zoom-reset" onblur={() => hideTooltip()} onclick={resetZoom} 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>
|
|
457
504
|
</div>
|
|
458
505
|
{/if}
|
|
459
|
-
|
|
506
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
507
|
+
<div class="infographic-render" class:markstream-svelte-pan-touch={panSurfaceClaimsTouch} style={previewStyle} onpointerdown={startDrag}>
|
|
460
508
|
{#if !hasPreview && !renderError}
|
|
461
509
|
<pre class="infographic-source-code infographic-pending-source"><code>{source}</code></pre>
|
|
462
510
|
{/if}
|
|
@@ -480,13 +528,14 @@
|
|
|
480
528
|
<button type="button" class="markstream-svelte-enhanced-block__action markstream-svelte-enhanced-block__action--icon infographic-action-btn" aria-label={t('common.zoomOut') || 'Zoom out'} onclick={zoomOut}>
|
|
481
529
|
<svg aria-hidden="true" role="img" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21l-4.35-4.35M8 11h6"/></g></svg>
|
|
482
530
|
</button>
|
|
483
|
-
<button type="button" class="markstream-svelte-enhanced-block__action infographic-action-btn infographic-zoom-reset" aria-label={t('common.resetZoom') || 'Reset zoom'} onclick={
|
|
531
|
+
<button type="button" class="markstream-svelte-enhanced-block__action infographic-action-btn infographic-zoom-reset" aria-label={t('common.resetZoom') || 'Reset zoom'} onclick={resetZoom}>{Math.round(zoom * 100)}%</button>
|
|
484
532
|
<button type="button" class="markstream-svelte-enhanced-block__action markstream-svelte-enhanced-block__action--icon infographic-action-btn" aria-label={t('common.close') || 'Close'} onclick={closeModal}>
|
|
485
533
|
<svg aria-hidden="true" role="img" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 6L6 18M6 6l12 12"/></svg>
|
|
486
534
|
</button>
|
|
487
535
|
</div>
|
|
488
|
-
|
|
489
|
-
|
|
536
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
537
|
+
<div class="mermaid-modal-body" class:markstream-svelte-pan-touch={panSurfaceClaimsTouch} style={`cursor: ${isDragging ? 'grabbing' : 'grab'}`} onpointerdown={startDrag}>
|
|
538
|
+
<div class="mermaid-modal-content infographic-modal-content" style={`transform: translate(${translateX}px, ${translateY}px) scale(${zoom});`}>{@html modalMarkup}</div>
|
|
490
539
|
</div>
|
|
491
540
|
</div>
|
|
492
541
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { SvelteRenderableNode, SvelteRenderContext } from './shared/node-helpers'
|
|
3
|
+
import { createPanGesture } from 'markstream-core'
|
|
3
4
|
import { onMount, tick, untrack } from 'svelte'
|
|
4
5
|
import { toSafeMermaidSvgMarkup } from 'stream-markdown-parser'
|
|
5
6
|
import { useSafeI18n } from '../i18n/useSafeI18n'
|
|
@@ -77,6 +78,9 @@
|
|
|
77
78
|
let showSource = $state(false)
|
|
78
79
|
let modalOpen = $state(false)
|
|
79
80
|
let zoom = $state(1)
|
|
81
|
+
let translateX = $state(0)
|
|
82
|
+
let translateY = $state(0)
|
|
83
|
+
let isDragging = $state(false)
|
|
80
84
|
let blockHost: HTMLElement | null = $state(null)
|
|
81
85
|
let previewHost: HTMLElement | null = $state(null)
|
|
82
86
|
let modalHost: HTMLElement | null = $state(null)
|
|
@@ -100,11 +104,19 @@
|
|
|
100
104
|
let previewStyle = $derived([
|
|
101
105
|
`min-height: ${previewHeight}px`,
|
|
102
106
|
maxHeight && maxHeight !== 'none' ? `max-height: ${maxHeight}` : '',
|
|
103
|
-
`transform: scale(${zoom})`,
|
|
107
|
+
`transform: translate(${translateX}px, ${translateY}px) scale(${zoom})`,
|
|
108
|
+
`cursor: ${isDragging ? 'grabbing' : 'grab'}`,
|
|
104
109
|
].filter(Boolean).join('; '))
|
|
105
110
|
let shouldRender = $derived(!(resolvedLoading && !source.trim()))
|
|
106
111
|
let canUsePreview = $derived(Boolean(svgMarkup && !showSource))
|
|
107
112
|
|
|
113
|
+
// The diagram is scaled to fit the preview box, so at fit zoom nothing is
|
|
114
|
+
// off-screen and panning only shifts it inside the box. Touch therefore keeps
|
|
115
|
+
// scrolling the page at that zoom (a chat is mostly scrolling) and only claims
|
|
116
|
+
// the gesture once the diagram is zoomed in and actually overflows the box.
|
|
117
|
+
// The fullscreen modal always claims it.
|
|
118
|
+
let panSurfaceClaimsTouch = $derived(modalOpen || zoom > 1)
|
|
119
|
+
|
|
108
120
|
onMount(() => {
|
|
109
121
|
mounted = true
|
|
110
122
|
scheduleRender(true)
|
|
@@ -113,6 +125,8 @@
|
|
|
113
125
|
mounted = false
|
|
114
126
|
renderToken += 1
|
|
115
127
|
clearRenderTimer()
|
|
128
|
+
// A drag in progress must not keep its window listeners after unmount.
|
|
129
|
+
panGesture.stop()
|
|
116
130
|
if (copyTimer)
|
|
117
131
|
clearTimeout(copyTimer)
|
|
118
132
|
}
|
|
@@ -460,6 +474,39 @@
|
|
|
460
474
|
zoom = Math.max(0.5, Math.round((zoom - 0.1) * 10) / 10)
|
|
461
475
|
}
|
|
462
476
|
|
|
477
|
+
function resetZoom() {
|
|
478
|
+
zoom = 1
|
|
479
|
+
translateX = 0
|
|
480
|
+
translateY = 0
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Drag functionality. The gesture lifecycle (window listeners, pointer identity,
|
|
484
|
+
// lost releases) lives in markstream-core so every package pans identically.
|
|
485
|
+
const panGesture = createPanGesture({
|
|
486
|
+
getTranslate: () => ({ x: translateX, y: translateY }),
|
|
487
|
+
setTranslate: (next) => {
|
|
488
|
+
translateX = next.x
|
|
489
|
+
translateY = next.y
|
|
490
|
+
},
|
|
491
|
+
canStart: (event) => {
|
|
492
|
+
// Nothing to pan until the diagram exists. Without this the pending/error
|
|
493
|
+
// source panel the block renders in the preview would be claimed by the
|
|
494
|
+
// gesture, and `preventDefault` would take text selection away from it.
|
|
495
|
+
if (!svgMarkup)
|
|
496
|
+
return false
|
|
497
|
+
|
|
498
|
+
// Touch pans only where the surface claims the gesture. Elsewhere the page
|
|
499
|
+
// keeps the swipe for scrolling, and panning along with it would shift the
|
|
500
|
+
// diagram by a few pixels before the browser takes the gesture over.
|
|
501
|
+
return event.pointerType !== 'touch' || panSurfaceClaimsTouch
|
|
502
|
+
},
|
|
503
|
+
onActiveChange: active => (isDragging = active),
|
|
504
|
+
})
|
|
505
|
+
|
|
506
|
+
function startDrag(event: PointerEvent) {
|
|
507
|
+
panGesture.start(event)
|
|
508
|
+
}
|
|
509
|
+
|
|
463
510
|
function showButtonTooltip(event: MouseEvent | FocusEvent, text: string, placement: TooltipPlacement = 'top') {
|
|
464
511
|
const target = event.currentTarget as HTMLElement | null
|
|
465
512
|
if (!target || (target instanceof HTMLButtonElement && target.disabled))
|
|
@@ -568,10 +615,11 @@
|
|
|
568
615
|
<button type="button" class="mermaid-btn mermaid-action-btn mermaid-btn--icon" aria-label={t('common.zoomOut')} onblur={() => hideTooltip()} onclick={zoomOut} onfocus={(event) => showButtonTooltip(event, t('common.zoomOut') || 'Zoom out')} onmouseleave={() => hideTooltip()} onmouseenter={(event) => showButtonTooltip(event, t('common.zoomOut') || 'Zoom out')}>
|
|
569
616
|
<svg aria-hidden="true" role="img" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21l-4.35-4.35M8 11h6"/></g></svg>
|
|
570
617
|
</button>
|
|
571
|
-
<button type="button" class="mermaid-btn mermaid-action-btn mermaid-zoom-reset" aria-label={t('common.resetZoom')} onblur={() => hideTooltip()} onclick={
|
|
618
|
+
<button type="button" class="mermaid-btn mermaid-action-btn mermaid-zoom-reset" aria-label={t('common.resetZoom')} onblur={() => hideTooltip()} onclick={resetZoom} 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>
|
|
572
619
|
</div>
|
|
573
620
|
{/if}
|
|
574
|
-
|
|
621
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
622
|
+
<div bind:this={previewHost} class="mermaid-preview markstream-svelte-mermaid" class:markstream-svelte-pan-touch={panSurfaceClaimsTouch} style={previewStyle} onpointerdown={startDrag}>
|
|
575
623
|
{#if svgMarkup}
|
|
576
624
|
{@html svgMarkup}
|
|
577
625
|
{:else if renderError}
|
|
@@ -598,13 +646,14 @@
|
|
|
598
646
|
<button type="button" class="mermaid-btn mermaid-action-btn mermaid-btn--icon" aria-label={t('common.zoomOut')} onclick={zoomOut}>
|
|
599
647
|
<svg aria-hidden="true" role="img" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21l-4.35-4.35M8 11h6"/></g></svg>
|
|
600
648
|
</button>
|
|
601
|
-
<button type="button" class="mermaid-btn mermaid-action-btn mermaid-zoom-reset" aria-label={t('common.resetZoom')} onclick={
|
|
649
|
+
<button type="button" class="mermaid-btn mermaid-action-btn mermaid-zoom-reset" aria-label={t('common.resetZoom')} onclick={resetZoom}>{Math.round(zoom * 100)}%</button>
|
|
602
650
|
<button type="button" class="mermaid-btn mermaid-action-btn mermaid-btn--icon" aria-label={t('common.close')} onclick={closeModal}>
|
|
603
651
|
<svg aria-hidden="true" role="img" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 6L6 18M6 6l12 12"/></svg>
|
|
604
652
|
</button>
|
|
605
653
|
</div>
|
|
606
|
-
|
|
607
|
-
|
|
654
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
655
|
+
<div class="mermaid-modal-body" class:markstream-svelte-pan-touch={panSurfaceClaimsTouch} style={`cursor: ${isDragging ? 'grabbing' : 'grab'}`} onpointerdown={startDrag}>
|
|
656
|
+
<div bind:this={modalHost} class="mermaid-modal-content markstream-svelte-mermaid" style={`transform: translate(${translateX}px, ${translateY}px) scale(${zoom});`}>{@html svgMarkup}</div>
|
|
608
657
|
</div>
|
|
609
658
|
</div>
|
|
610
659
|
</div>
|
package/dist/index.css
CHANGED
|
@@ -1099,9 +1099,14 @@ body > div[id^="dmermaid-"] {
|
|
|
1099
1099
|
color: var(--code-fg);
|
|
1100
1100
|
}
|
|
1101
1101
|
|
|
1102
|
+
/* The pan transform is applied to `.mermaid-preview` itself, and a transformed
|
|
1103
|
+
element paints above the header it overlaps; the body is what clips the panned
|
|
1104
|
+
diagram back into the preview area. clip (not auto/hidden) means it is not a
|
|
1105
|
+
scroll container at all, so a touch swipe keeps scrolling the page. */
|
|
1102
1106
|
.markstream-svelte .mermaid-body {
|
|
1103
1107
|
position: relative;
|
|
1104
1108
|
padding: 0;
|
|
1109
|
+
overflow: clip;
|
|
1105
1110
|
}
|
|
1106
1111
|
|
|
1107
1112
|
.markstream-svelte .markstream-svelte-zoom-controls {
|
|
@@ -1128,7 +1133,11 @@ body > div[id^="dmermaid-"] {
|
|
|
1128
1133
|
.markstream-svelte .mermaid-preview {
|
|
1129
1134
|
width: 100%;
|
|
1130
1135
|
min-height: var(--ms-size-diagram-min-height);
|
|
1131
|
-
overflow:
|
|
1136
|
+
/* clip, not auto: an overflow:auto pan surface is a "user-scrollable" box,
|
|
1137
|
+
so a touch swipe latches onto it and, when nothing can scroll there, the
|
|
1138
|
+
page does not scroll either. clip is never a scroll container and clips
|
|
1139
|
+
the same (the diagram is scaled to fit). */
|
|
1140
|
+
overflow: clip;
|
|
1132
1141
|
display: flex;
|
|
1133
1142
|
align-items: center;
|
|
1134
1143
|
justify-content: center;
|
|
@@ -1141,6 +1150,14 @@ body > div[id^="dmermaid-"] {
|
|
|
1141
1150
|
height: auto;
|
|
1142
1151
|
}
|
|
1143
1152
|
|
|
1153
|
+
/* The pan surface takes the touch gesture instead of letting the page scroller
|
|
1154
|
+
claim it, which would fire pointercancel and stop the drag mid-gesture. The
|
|
1155
|
+
class is only bound while panning can reveal something (zoomed in, or in the
|
|
1156
|
+
fullscreen modal), so at fit zoom a swipe keeps scrolling the page. */
|
|
1157
|
+
.markstream-svelte .markstream-svelte-pan-touch {
|
|
1158
|
+
touch-action: none;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1144
1161
|
.markstream-svelte .mermaid-source {
|
|
1145
1162
|
margin: 0;
|
|
1146
1163
|
padding: var(--ms-inset-panel-body);
|
|
@@ -1401,6 +1418,10 @@ body > div[id^="dmermaid-"] {
|
|
|
1401
1418
|
.markstream-svelte .infographic-block-body {
|
|
1402
1419
|
padding: 0;
|
|
1403
1420
|
background: var(--diagram-bg);
|
|
1421
|
+
/* The pan transform is applied to .infographic-render itself, so the body is
|
|
1422
|
+
what clips the panned diagram back into the preview area. clip (not the
|
|
1423
|
+
base __body's auto) so it is not a scroll container a swipe could latch on. */
|
|
1424
|
+
overflow: clip;
|
|
1404
1425
|
}
|
|
1405
1426
|
|
|
1406
1427
|
.markstream-svelte .infographic-source {
|
|
@@ -2162,7 +2183,9 @@ body > div[id^="dmermaid-"] {
|
|
|
2162
2183
|
position: relative;
|
|
2163
2184
|
width: 100%;
|
|
2164
2185
|
min-height: var(--ms-size-diagram-min-height);
|
|
2165
|
-
|
|
2186
|
+
/* clip, not auto — same reason as .mermaid-preview: a scrollable pan surface
|
|
2187
|
+
would latch touch swipes and stop the page from scrolling. */
|
|
2188
|
+
overflow: clip;
|
|
2166
2189
|
text-align: center;
|
|
2167
2190
|
}
|
|
2168
2191
|
|
package/dist/index.px.css
CHANGED
|
@@ -1099,9 +1099,14 @@ body > div[id^="dmermaid-"] {
|
|
|
1099
1099
|
color: var(--code-fg);
|
|
1100
1100
|
}
|
|
1101
1101
|
|
|
1102
|
+
/* The pan transform is applied to `.mermaid-preview` itself, and a transformed
|
|
1103
|
+
element paints above the header it overlaps; the body is what clips the panned
|
|
1104
|
+
diagram back into the preview area. clip (not auto/hidden) means it is not a
|
|
1105
|
+
scroll container at all, so a touch swipe keeps scrolling the page. */
|
|
1102
1106
|
.markstream-svelte .mermaid-body {
|
|
1103
1107
|
position: relative;
|
|
1104
1108
|
padding: 0;
|
|
1109
|
+
overflow: clip;
|
|
1105
1110
|
}
|
|
1106
1111
|
|
|
1107
1112
|
.markstream-svelte .markstream-svelte-zoom-controls {
|
|
@@ -1128,7 +1133,11 @@ body > div[id^="dmermaid-"] {
|
|
|
1128
1133
|
.markstream-svelte .mermaid-preview {
|
|
1129
1134
|
width: 100%;
|
|
1130
1135
|
min-height: var(--ms-size-diagram-min-height);
|
|
1131
|
-
overflow:
|
|
1136
|
+
/* clip, not auto: an overflow:auto pan surface is a "user-scrollable" box,
|
|
1137
|
+
so a touch swipe latches onto it and, when nothing can scroll there, the
|
|
1138
|
+
page does not scroll either. clip is never a scroll container and clips
|
|
1139
|
+
the same (the diagram is scaled to fit). */
|
|
1140
|
+
overflow: clip;
|
|
1132
1141
|
display: flex;
|
|
1133
1142
|
align-items: center;
|
|
1134
1143
|
justify-content: center;
|
|
@@ -1141,6 +1150,14 @@ body > div[id^="dmermaid-"] {
|
|
|
1141
1150
|
height: auto;
|
|
1142
1151
|
}
|
|
1143
1152
|
|
|
1153
|
+
/* The pan surface takes the touch gesture instead of letting the page scroller
|
|
1154
|
+
claim it, which would fire pointercancel and stop the drag mid-gesture. The
|
|
1155
|
+
class is only bound while panning can reveal something (zoomed in, or in the
|
|
1156
|
+
fullscreen modal), so at fit zoom a swipe keeps scrolling the page. */
|
|
1157
|
+
.markstream-svelte .markstream-svelte-pan-touch {
|
|
1158
|
+
touch-action: none;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1144
1161
|
.markstream-svelte .mermaid-source {
|
|
1145
1162
|
margin: 0;
|
|
1146
1163
|
padding: var(--ms-inset-panel-body);
|
|
@@ -1401,6 +1418,10 @@ body > div[id^="dmermaid-"] {
|
|
|
1401
1418
|
.markstream-svelte .infographic-block-body {
|
|
1402
1419
|
padding: 0;
|
|
1403
1420
|
background: var(--diagram-bg);
|
|
1421
|
+
/* The pan transform is applied to .infographic-render itself, so the body is
|
|
1422
|
+
what clips the panned diagram back into the preview area. clip (not the
|
|
1423
|
+
base __body's auto) so it is not a scroll container a swipe could latch on. */
|
|
1424
|
+
overflow: clip;
|
|
1404
1425
|
}
|
|
1405
1426
|
|
|
1406
1427
|
.markstream-svelte .infographic-source {
|
|
@@ -2162,7 +2183,9 @@ body > div[id^="dmermaid-"] {
|
|
|
2162
2183
|
position: relative;
|
|
2163
2184
|
width: 100%;
|
|
2164
2185
|
min-height: var(--ms-size-diagram-min-height);
|
|
2165
|
-
|
|
2186
|
+
/* clip, not auto — same reason as .mermaid-preview: a scrollable pan surface
|
|
2187
|
+
would latch touch swipes and stop the page from scrolling. */
|
|
2188
|
+
overflow: clip;
|
|
2166
2189
|
text-align: center;
|
|
2167
2190
|
}
|
|
2168
2191
|
|
package/dist/index.tailwind.css
CHANGED
|
@@ -1099,9 +1099,14 @@ body > div[id^="dmermaid-"] {
|
|
|
1099
1099
|
color: var(--code-fg);
|
|
1100
1100
|
}
|
|
1101
1101
|
|
|
1102
|
+
/* The pan transform is applied to `.mermaid-preview` itself, and a transformed
|
|
1103
|
+
element paints above the header it overlaps; the body is what clips the panned
|
|
1104
|
+
diagram back into the preview area. clip (not auto/hidden) means it is not a
|
|
1105
|
+
scroll container at all, so a touch swipe keeps scrolling the page. */
|
|
1102
1106
|
.markstream-svelte .mermaid-body {
|
|
1103
1107
|
position: relative;
|
|
1104
1108
|
padding: 0;
|
|
1109
|
+
overflow: clip;
|
|
1105
1110
|
}
|
|
1106
1111
|
|
|
1107
1112
|
.markstream-svelte .markstream-svelte-zoom-controls {
|
|
@@ -1128,7 +1133,11 @@ body > div[id^="dmermaid-"] {
|
|
|
1128
1133
|
.markstream-svelte .mermaid-preview {
|
|
1129
1134
|
width: 100%;
|
|
1130
1135
|
min-height: var(--ms-size-diagram-min-height);
|
|
1131
|
-
overflow:
|
|
1136
|
+
/* clip, not auto: an overflow:auto pan surface is a "user-scrollable" box,
|
|
1137
|
+
so a touch swipe latches onto it and, when nothing can scroll there, the
|
|
1138
|
+
page does not scroll either. clip is never a scroll container and clips
|
|
1139
|
+
the same (the diagram is scaled to fit). */
|
|
1140
|
+
overflow: clip;
|
|
1132
1141
|
display: flex;
|
|
1133
1142
|
align-items: center;
|
|
1134
1143
|
justify-content: center;
|
|
@@ -1141,6 +1150,14 @@ body > div[id^="dmermaid-"] {
|
|
|
1141
1150
|
height: auto;
|
|
1142
1151
|
}
|
|
1143
1152
|
|
|
1153
|
+
/* The pan surface takes the touch gesture instead of letting the page scroller
|
|
1154
|
+
claim it, which would fire pointercancel and stop the drag mid-gesture. The
|
|
1155
|
+
class is only bound while panning can reveal something (zoomed in, or in the
|
|
1156
|
+
fullscreen modal), so at fit zoom a swipe keeps scrolling the page. */
|
|
1157
|
+
.markstream-svelte .markstream-svelte-pan-touch {
|
|
1158
|
+
touch-action: none;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1144
1161
|
.markstream-svelte .mermaid-source {
|
|
1145
1162
|
margin: 0;
|
|
1146
1163
|
padding: var(--ms-inset-panel-body);
|
|
@@ -1401,6 +1418,10 @@ body > div[id^="dmermaid-"] {
|
|
|
1401
1418
|
.markstream-svelte .infographic-block-body {
|
|
1402
1419
|
padding: 0;
|
|
1403
1420
|
background: var(--diagram-bg);
|
|
1421
|
+
/* The pan transform is applied to .infographic-render itself, so the body is
|
|
1422
|
+
what clips the panned diagram back into the preview area. clip (not the
|
|
1423
|
+
base __body's auto) so it is not a scroll container a swipe could latch on. */
|
|
1424
|
+
overflow: clip;
|
|
1404
1425
|
}
|
|
1405
1426
|
|
|
1406
1427
|
.markstream-svelte .infographic-source {
|
|
@@ -2162,7 +2183,9 @@ body > div[id^="dmermaid-"] {
|
|
|
2162
2183
|
position: relative;
|
|
2163
2184
|
width: 100%;
|
|
2164
2185
|
min-height: var(--ms-size-diagram-min-height);
|
|
2165
|
-
|
|
2186
|
+
/* clip, not auto — same reason as .mermaid-preview: a scrollable pan surface
|
|
2187
|
+
would latch touch swipes and stop the page from scrolling. */
|
|
2188
|
+
overflow: clip;
|
|
2166
2189
|
text-align: center;
|
|
2167
2190
|
}
|
|
2168
2191
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "markstream-svelte",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.0.14",
|
|
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.
|
|
104
|
-
"stream-markdown-parser": "1.2.
|
|
103
|
+
"markstream-core": "2.0.14",
|
|
104
|
+
"stream-markdown-parser": "1.2.17"
|
|
105
105
|
},
|
|
106
106
|
"devDependencies": {
|
|
107
107
|
"@sveltejs/package": "^2.5.8",
|