From eba2b2cd7257640e98305d60c6712a6f01fed809 Mon Sep 17 00:00:00 2001 From: Shirasawa <764798966@qq.com> Date: Tue, 31 Mar 2026 09:54:28 +0800 Subject: [PATCH] refactor(SVGPanZoom): Fix memory leaking - consolidate zoom lifecycle into PanzoomContainer (#23236) * refactor(SVGPanZoom): attach panzoom via use: action and remove unused parent bind:this * refactor: centralize panzoom in createPanzoomAction and align ImagePreview cleanup * refactor(panzoom): consolidate zoom lifecycle into PanzoomContainer and remove action-based wiring --- src/lib/components/chat/FileNav.svelte | 1 - .../chat/FileNav/FilePreview.svelte | 49 ++++++------------- .../components/common/FileItemModal.svelte | 26 ++-------- src/lib/components/common/ImagePreview.svelte | 40 ++++----------- .../components/common/PanzoomContainer.svelte | 33 +++++++++++++ src/lib/components/common/SVGPanZoom.svelte | 32 ++++-------- 6 files changed, 72 insertions(+), 109 deletions(-) create mode 100644 src/lib/components/common/PanzoomContainer.svelte diff --git a/src/lib/components/chat/FileNav.svelte b/src/lib/components/chat/FileNav.svelte index 988c92669c..2a14312d45 100644 --- a/src/lib/components/chat/FileNav.svelte +++ b/src/lib/components/chat/FileNav.svelte @@ -293,7 +293,6 @@ // ── File preview management ────────────────────────────────────────── const clearFilePreview = () => { fileContent = null; - filePreviewRef?.disposePanzoom(); if (fileImageUrl) { URL.revokeObjectURL(fileImageUrl); fileImageUrl = null; diff --git a/src/lib/components/chat/FileNav/FilePreview.svelte b/src/lib/components/chat/FileNav/FilePreview.svelte index 0a7250c3ed..0bfa4fe504 100644 --- a/src/lib/components/chat/FileNav/FilePreview.svelte +++ b/src/lib/components/chat/FileNav/FilePreview.svelte @@ -1,6 +1,5 @@
{:else if fileImageUrl !== null} -
+ {selectedFile?.split('/').pop()} -
+ {:else if fileVideoUrl !== null}
@@ -343,9 +323,10 @@
{:else if fileOfficeSlides !== null && fileOfficeSlides.length > 0}
-
-
+ {#if fileOfficeSlides.length > 1}
{ - pzInstance = panzoom(node, { - bounds: true, - boundsPadding: 0.1, - zoomSpeed: 0.065 - }); - }; - + let panzoomRef: PanzoomContainer; const resetImageView = () => { - if (pzInstance) { - pzInstance.moveTo(0, 0); - pzInstance.zoomAbs(0, 0, 1); - } + panzoomRef?.reset(); }; $: isPDF = @@ -266,10 +254,6 @@ if (item?.context === 'full') { enableFullContent = true; } - - return () => { - pzInstance?.dispose(); - }; }); @@ -445,7 +429,7 @@
-
+ {item?.name -
+
{:else if selectedTab === ''} {#if item?.file?.data} diff --git a/src/lib/components/common/ImagePreview.svelte b/src/lib/components/common/ImagePreview.svelte index 17d9a93a1e..9cb36dc160 100644 --- a/src/lib/components/common/ImagePreview.svelte +++ b/src/lib/components/common/ImagePreview.svelte @@ -1,10 +1,10 @@ @@ -181,14 +160,13 @@ -
+ -
+ {/if} diff --git a/src/lib/components/common/PanzoomContainer.svelte b/src/lib/components/common/PanzoomContainer.svelte new file mode 100644 index 0000000000..50aec0709d --- /dev/null +++ b/src/lib/components/common/PanzoomContainer.svelte @@ -0,0 +1,33 @@ + + +
+ +
diff --git a/src/lib/components/common/SVGPanZoom.svelte b/src/lib/components/common/SVGPanZoom.svelte index aaeb26dac9..307ddda21e 100644 --- a/src/lib/components/common/SVGPanZoom.svelte +++ b/src/lib/components/common/SVGPanZoom.svelte @@ -4,15 +4,14 @@ import { toast } from 'svelte-sonner'; - import panzoom, { type PanZoom } from 'panzoom'; import DOMPurify from 'dompurify'; - import { onMount, getContext } from 'svelte'; + import { getContext } from 'svelte'; const i18n = getContext('i18n'); import { copyToClipboard } from '$lib/utils'; - import DocumentDuplicate from '../icons/DocumentDuplicate.svelte'; + import PanzoomContainer from './PanzoomContainer.svelte'; import Tooltip from './Tooltip.svelte'; import Clipboard from '../icons/Clipboard.svelte'; import Reset from '../icons/Reset.svelte'; @@ -22,23 +21,9 @@ export let svg = ''; export let content = ''; - let instance: PanZoom; - - let sceneParentElement: HTMLElement; - let sceneElement: HTMLElement; - - $: if (sceneElement) { - instance = panzoom(sceneElement, { - bounds: true, - boundsPadding: 0.1, - - zoomSpeed: 0.065 - }); - } + let panzoomRef: PanzoomContainer; const resetPanZoomViewport = () => { - instance.moveTo(0, 0); - instance.zoomAbs(0, 0, 1); - console.log(instance.getTransform()); + panzoomRef?.reset(); }; const downloadAsSVG = () => { @@ -47,8 +32,11 @@ }; -
-
+
+ {@html DOMPurify.sanitize(svg, { USE_PROFILES: { svg: true, svgFilters: true }, // allow , , , etc. WHOLE_DOCUMENT: false, @@ -88,7 +76,7 @@ ], SANITIZE_DOM: true })} -
+ {#if content}