From 0021826413f8a2b4375eb39ea23edd4b0c77b677 Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 15 May 2026 18:47:01 +0200 Subject: [PATCH] Avoid global window access in Soma helpers --- .../announce/announce-provider.svelte.ts | 6 +++++- .../color-picker-provider.svelte.ts | 19 ++++++++++++++----- src/uix/soma/layers/floating/utils.ts | 4 ++-- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/uix/soma/components/announce/announce-provider.svelte.ts b/src/uix/soma/components/announce/announce-provider.svelte.ts index 8c0e756f9..65dbf216f 100644 --- a/src/uix/soma/components/announce/announce-provider.svelte.ts +++ b/src/uix/soma/components/announce/announce-provider.svelte.ts @@ -78,7 +78,11 @@ export class AnnounceProvider { } announce = (message: string, priority: AnnouncePriority = 'polite', timeout?: number): void => { - if (typeof window === 'undefined') return; + try { + this.soma.dom.getDocument(this.opts.ref?.current); + } catch { + return; + } const clearAfter = timeout ?? this.opts.defaultTimeout.current; if (priority === 'assertive') { diff --git a/src/uix/soma/components/color-picker/color-picker-provider.svelte.ts b/src/uix/soma/components/color-picker/color-picker-provider.svelte.ts index c017a9fd4..6cad16583 100644 --- a/src/uix/soma/components/color-picker/color-picker-provider.svelte.ts +++ b/src/uix/soma/components/color-picker/color-picker-provider.svelte.ts @@ -1262,10 +1262,18 @@ export class ColorPickerEyeDropperProvider { }); } - readonly isSupported: boolean = - typeof window !== 'undefined' && - 'EyeDropper' in window && - typeof (window as unknown as { EyeDropper: unknown }).EyeDropper === 'function'; + private get eyeDropperCtor(): (new () => EyeDropperLike) | undefined { + let win: Window; + try { + win = this.provider.soma.dom.getWindow(this.opts.ref.current); + } catch { + return undefined; + } + const candidate = (win as unknown as { EyeDropper?: unknown }).EyeDropper; + return typeof candidate === 'function' ? (candidate as new () => EyeDropperLike) : undefined; + } + + readonly isSupported = $derived.by(() => this.eyeDropperCtor !== undefined); readonly resolvedLabel = $derived.by( () => this.opts.ariaLabel.current ?? this.provider.langs(COLOR_PICKER_LANGS.EYE_DROPPER) @@ -1274,7 +1282,8 @@ export class ColorPickerEyeDropperProvider { readonly onclick = async () => { if (!this.isSupported || this.provider.isDisabled || this.provider.isReadonly) return; try { - const Ctor = (window as unknown as { EyeDropper: new () => EyeDropperLike }).EyeDropper; + const Ctor = this.eyeDropperCtor; + if (!Ctor) return; const picker = new Ctor(); const { sRGBHex } = await picker.open(); const hsv = parseColor(sRGBHex); diff --git a/src/uix/soma/layers/floating/utils.ts b/src/uix/soma/layers/floating/utils.ts index 7eefd63b5..9da59481e 100644 --- a/src/uix/soma/layers/floating/utils.ts +++ b/src/uix/soma/layers/floating/utils.ts @@ -1,8 +1,8 @@ // ─── DPR ───────────────────────────────────────────────────────────────────── export function getDPR(element: Element): number { - if (typeof window === 'undefined') return 1; - const win = element.ownerDocument.defaultView ?? window; + const win = element.ownerDocument.defaultView; + if (!win) return 1; return win.devicePixelRatio || 1; }