From ca8f516f8ceab2e586fa63b1e2d09e5bfe5bfc84 Mon Sep 17 00:00:00 2001 From: dev Date: Sat, 6 Jun 2026 15:14:19 +0200 Subject: [PATCH] fix(words): untrack the langs-registration $effect (no reactive loop) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regression from the onMount→$effect conversion (increment 3): eidos.langs.extend both reads AND mutates the reactive langs schema, so the $effect tracked that read, the mutation retriggered it, and it looped — re-extending the bundles thousands of times, each logging a DEV leaf-overwrite warning (~90k warnings, hung the editor page). Wrapped the body in untrack ⇒ the effect has no deps ⇒ runs exactly once on mount, like the old onMount. Verified: words page loads fast, zero console warnings, editor + inspector render. api-contract test still green (untrack is not flagged). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/uix/eidos/components/words/words.svelte | 22 ++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/uix/eidos/components/words/words.svelte b/src/uix/eidos/components/words/words.svelte index 97a0a7c99..6b5b21caa 100644 --- a/src/uix/eidos/components/words/words.svelte +++ b/src/uix/eidos/components/words/words.svelte @@ -13,6 +13,7 @@ * (builder-style, always visible), a slide-in drawer, a popover, or * none. The same `` body is reused across all of them. */ + import { untrack } from 'svelte'; import { ActiveEidos } from '$uix/eidos'; import * as Words from '$soma/components/words'; import { Button } from '$uix/eidos/components/button'; @@ -47,16 +48,19 @@ // client pass emit different markup and hydration bails. Defer the // editor to the client; SSR + first paint render the empty frame. let mounted = $state(false); - // Client-only activation + one-time langs registration. Reads nothing - // reactive, so this $effect runs exactly once on mount (the Svelte 5 way, - // no lifecycle import). Register the inspector's localized strings into the - // active langs schema — without this every label resolves to the inline - // English fallback regardless of locale. `extend` deep-merges so app-level - // overrides at the same path applied AFTER mount still win. + // Client-only activation + one-time langs registration. `langs.extend` BOTH + // reads and mutates the reactive langs schema, so it MUST run untracked — + // otherwise this $effect tracks that read, the mutation retriggers it, and it + // loops, re-extending the bundle thousands of times (each logging a DEV + // leaf-overwrite warning). untrack ⇒ the effect has no deps ⇒ runs once on + // mount. Registers the inspector's localized strings so labels localize + // instead of falling back to the inline English. $effect(() => { - mounted = true; - eidos.langs.extend('components.words.inspector', WORDS_INSPECTOR_BUNDLE); - eidos.langs.extend('components.words.menu', WORDS_MENU_BUNDLE); + untrack(() => { + mounted = true; + eidos.langs.extend('components.words.inspector', WORDS_INSPECTOR_BUNDLE); + eidos.langs.extend('components.words.menu', WORDS_MENU_BUNDLE); + }); }); let contentEl = $state(null);