The two .prettierrc bidirectional binding effects in LanguageRunner (language-runner.ts:452-507) are registered unconditionally (after the if (this.config.injectPrettierRc) config flag check at registration time). Inside each effect, the if (this.mode() !== "sandbox") return; if (this.effectiveSandboxLevel() !== "advanced") return; guards short-circuit when not in advanced sandbox mode — but the effect still RUNS on every signal change it reads (mode, effectiveSandboxLevel, files, AND prettierStore.options()).
In beginner mode, every visual-panel Prettier toggle re-fires the store→file effect, which immediately returns. Cost is O(1) per toggle and signals are fast, but multiplying by 9 options × N toggles × 2 effects adds up over a long session, especially on lower-end devices common among learners.
Suggested fix
Gate the effect creation behind a computed that becomes true only when injectPrettierRc && mode === "sandbox" && effectiveSandboxLevel === "advanced". Use Angular's effect() from inside another effect (or runInInjectionContext lazily) to only register the binding effects when that gate flips to true; tear them down when it flips to false.
Alternative (simpler): leave as-is. The cost is sub-microsecond per signal change and not measurable in practice. This issue exists primarily to flag the design decision so a future profiler-driven optimization knows the trade-off.
Owner pointers
libraries/tools/src/components/language-runner/language-runner.ts:432-508 (the two binding effects)
libraries/tools/src/components/language-runner/language-runner-prettierrc.spec.ts (the 12-spec test suite covering the seam — any restructure must keep these passing)
Related
Observation from code review of the Prettier integration + JS formatting options panel feature wave; not a bug.
## Observation (low-priority perf note)
The two `.prettierrc` bidirectional binding effects in `LanguageRunner` (`language-runner.ts:452-507`) are registered unconditionally (after the `if (this.config.injectPrettierRc)` config flag check at registration time). Inside each effect, the `if (this.mode() !== "sandbox") return; if (this.effectiveSandboxLevel() !== "advanced") return;` guards short-circuit when not in advanced sandbox mode — but the effect still RUNS on every signal change it reads (`mode`, `effectiveSandboxLevel`, `files`, AND `prettierStore.options()`).
In beginner mode, every visual-panel Prettier toggle re-fires the store→file effect, which immediately returns. Cost is O(1) per toggle and signals are fast, but multiplying by 9 options × N toggles × 2 effects adds up over a long session, especially on lower-end devices common among learners.
## Suggested fix
Gate the effect creation behind a `computed` that becomes true only when `injectPrettierRc && mode === "sandbox" && effectiveSandboxLevel === "advanced"`. Use Angular's `effect()` from inside another effect (or `runInInjectionContext` lazily) to only register the binding effects when that gate flips to true; tear them down when it flips to false.
Alternative (simpler): leave as-is. The cost is sub-microsecond per signal change and not measurable in practice. This issue exists primarily to flag the design decision so a future profiler-driven optimization knows the trade-off.
## Owner pointers
- `libraries/tools/src/components/language-runner/language-runner.ts:432-508` (the two binding effects)
- `libraries/tools/src/components/language-runner/language-runner-prettierrc.spec.ts` (the 12-spec test suite covering the seam — any restructure must keep these passing)
## Related
Observation from code review of the Prettier integration + JS formatting options panel feature wave; not a bug.
Investigated — the described per-toggle re-run does not actually occur, so no gating machinery is warranted. Angular effect() uses dynamic dependency tracking: an effect only re-runs when a signal it read during its last execution changes. Both .prettierrc binding effects read the cheap mode() / effectiveSandboxLevel() guards and early-return before ever reading prettierStore.toJson(). So in beginner/lesson mode they don't depend on the Prettier store at all, and a panel toggle does not re-fire them. The dependency is (re)established only while in advanced sandbox mode, which is exactly when we want it.
Added a comment in spikersoft-angular PR #56 documenting that the guard ordering is intentional for this reason. No functional change needed; implementing dynamic effect creation/teardown would be over-engineering for a cost that isn't incurred. Will close alongside PR #56 (which also fixes#81 in the same effect block). Reopen if you'd still prefer the explicit gate.
Investigated — the described per-toggle re-run does **not** actually occur, so no gating machinery is warranted. Angular `effect()` uses *dynamic* dependency tracking: an effect only re-runs when a signal it read during its **last** execution changes. Both `.prettierrc` binding effects read the cheap `mode()` / `effectiveSandboxLevel()` guards and early-return **before** ever reading `prettierStore.toJson()`. So in beginner/lesson mode they don't depend on the Prettier store at all, and a panel toggle does not re-fire them. The dependency is (re)established only while in advanced sandbox mode, which is exactly when we want it.
Added a comment in `spikersoft-angular` PR #56 documenting that the guard ordering is intentional for this reason. No functional change needed; implementing dynamic effect creation/teardown would be over-engineering for a cost that isn't incurred. Will close alongside PR #56 (which also fixes #81 in the same effect block). Reopen if you'd still prefer the explicit gate.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Observation (low-priority perf note)
The two
.prettierrcbidirectional binding effects inLanguageRunner(language-runner.ts:452-507) are registered unconditionally (after theif (this.config.injectPrettierRc)config flag check at registration time). Inside each effect, theif (this.mode() !== "sandbox") return; if (this.effectiveSandboxLevel() !== "advanced") return;guards short-circuit when not in advanced sandbox mode — but the effect still RUNS on every signal change it reads (mode,effectiveSandboxLevel,files, ANDprettierStore.options()).In beginner mode, every visual-panel Prettier toggle re-fires the store→file effect, which immediately returns. Cost is O(1) per toggle and signals are fast, but multiplying by 9 options × N toggles × 2 effects adds up over a long session, especially on lower-end devices common among learners.
Suggested fix
Gate the effect creation behind a
computedthat becomes true only wheninjectPrettierRc && mode === "sandbox" && effectiveSandboxLevel === "advanced". Use Angular'seffect()from inside another effect (orrunInInjectionContextlazily) to only register the binding effects when that gate flips to true; tear them down when it flips to false.Alternative (simpler): leave as-is. The cost is sub-microsecond per signal change and not measurable in practice. This issue exists primarily to flag the design decision so a future profiler-driven optimization knows the trade-off.
Owner pointers
libraries/tools/src/components/language-runner/language-runner.ts:432-508(the two binding effects)libraries/tools/src/components/language-runner/language-runner-prettierrc.spec.ts(the 12-spec test suite covering the seam — any restructure must keep these passing)Related
Observation from code review of the Prettier integration + JS formatting options panel feature wave; not a bug.
Investigated — the described per-toggle re-run does not actually occur, so no gating machinery is warranted. Angular
effect()uses dynamic dependency tracking: an effect only re-runs when a signal it read during its last execution changes. Both.prettierrcbinding effects read the cheapmode()/effectiveSandboxLevel()guards and early-return before ever readingprettierStore.toJson(). So in beginner/lesson mode they don't depend on the Prettier store at all, and a panel toggle does not re-fire them. The dependency is (re)established only while in advanced sandbox mode, which is exactly when we want it.Added a comment in
spikersoft-angularPR #56 documenting that the guard ordering is intentional for this reason. No functional change needed; implementing dynamic effect creation/teardown would be over-engineering for a cost that isn't incurred. Will close alongside PR #56 (which also fixes #81 in the same effect block). Reopen if you'd still prefer the explicit gate.