From 71cb450cbdd92bbb1aaedc43e9d4eae1e5206234 Mon Sep 17 00:00:00 2001
From: Fuwn <contact@fuwn.net>
Date: Sat, 18 Apr 2026 09:14:37 +0000
Subject: fix(tooltip): remove body-appended tooltip node on destroy

LinkedTooltip appends its tooltip div directly to document.body in
createTooltip and only removes it from hideTooltip. If the component
was destroyed while the tooltip was visible (e.g. parent navigated
mid-hover), the div leaked and stayed in the DOM forever.

Add an onDestroy handler that clears the pending hide/debounce timers
and, if the tooltip div is still parented to document.body, removes
it. Guarding on parentNode avoids double-removal when hideTooltip has
already run.
---
 src/lib/Tooltip/LinkedTooltip.svelte | 12 ++++++++++++
 1 file changed, 12 insertions(+)

(limited to 'src/lib/Tooltip/LinkedTooltip.svelte')

diff --git a/src/lib/Tooltip/LinkedTooltip.svelte b/src/lib/Tooltip/LinkedTooltip.svelte
index 82147536..b3fc6ae2 100644
--- a/src/lib/Tooltip/LinkedTooltip.svelte
+++ b/src/lib/Tooltip/LinkedTooltip.svelte
@@ -1,6 +1,7 @@
 <script lang="ts">
 import tooltipPosition from "$stores/tooltipPosition";
 import { fade } from "svelte/transition";
+import { onDestroy } from "svelte";
 
 export let id: string | undefined = undefined;
 export let pin: string | undefined = undefined;
@@ -20,6 +21,17 @@ let hideTimeout: number | null = null;
 let debounceTimer: number | null = null;
 let opacity = 0;
 
+onDestroy(() => {
+	if (hideTimeout !== null) clearTimeout(hideTimeout);
+
+	if (debounceTimer !== null) clearTimeout(debounceTimer);
+
+	if (tooltipDiv && tooltipDiv.parentNode === document.body) {
+		document.body.removeChild(tooltipDiv);
+		tooltipDiv = null;
+	}
+});
+
 const createTooltip = () => {
 	if (!tooltipDiv) {
 		tooltipDiv = document.createElement("div");
-- 
cgit v1.2.3

