diff --git a/packages/ui/src/components/internal/Root.svelte b/packages/ui/src/components/internal/Root.svelte index 118befe28c..0c59cac7ad 100644 --- a/packages/ui/src/components/internal/Root.svelte +++ b/packages/ui/src/components/internal/Root.svelte @@ -41,12 +41,23 @@ function handleWindowBlur (): void { updateAppFocused(false) } + function handleWindowBeforeUnload (): void { + // Many text inputs across the platform rely on the blur event to persist state, + // but they don’t account for cases where the tab is forcefully closed, navigated away from, or destroyed. + // Handling beforeunload for every input individually is impractical, + // so leveraging existing blur behavior is a more maintainable approach. + // While not foolproof—since most blur handlers are async and may not complete in time, it’s sufficient for most cases. + // A more robust solution, such as tracking pending async mutations to block tab unload, + // would require significant reworks across the board. + ;(document.activeElement as HTMLElement)?.blur?.() + } onMount(() => { document.addEventListener('visibilitychange', visibilityChangeHandler) window.addEventListener('wheel', handleWindowFocus) window.addEventListener('focus', handleWindowFocus) window.addEventListener('blur', handleWindowBlur) + window.addEventListener('beforeunload', handleWindowBeforeUnload) }) onDestroy(() => { @@ -54,6 +65,7 @@ window.removeEventListener('wheel', handleWindowFocus) window.removeEventListener('focus', handleWindowFocus) window.removeEventListener('blur', handleWindowBlur) + window.removeEventListener('beforeunload', handleWindowBeforeUnload) }) onDestroy(