mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-07 06:59:46 +00:00
perf(card): a floor switch stops re-querying the same subtrees (#694)
A floor switch replaces the whole stage, so the card's pointer-hover MutationObserver receives hundreds of records whose targets are the same few containers. Each record re-ran `matches` and a `.devlayer` subtree `querySelector` on its target, and kept doing so after the device layer had already been found. The batch logic moves to `deviceLayerMutated` in device-hit-owner.ts: a node is checked at most once per batch, the first hit ends the checks, and every added node still goes through `_syncPointerHoverSubtree` in record order. The card shrinks by 12 lines. The View stair layer read the card's `_model` getter once more for every navigable stair; the getter rebuilds the config fingerprint on each read. `renderLayer` now reads it once. `languageRenderGate` wrote `lang` on the host on every render. It now writes it only when the value differs (language switch, English fallback, a foreign value); an unchanged value is left alone. No behaviour changes: DOM, tooltips and pixels are the same. Unit tests count subtree queries per node, `_model` reads per render and `lang` writes; one mutant per change restores the old behaviour. Issue: #694 User-Visible: no Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
This commit is contained in:
@@ -130,6 +130,44 @@ test('locale render gate exposes the fallback language to assistive technology',
|
||||
assert.equal(host.attrs.get('lang'), 'en');
|
||||
});
|
||||
|
||||
// #694: a real element answers getAttribute; count only the `lang` writes.
|
||||
class AttributeHost extends FakeHost {
|
||||
langWrites = [];
|
||||
getAttribute(name) { return this.attrs.has(name) ? this.attrs.get(name) : null; }
|
||||
setAttribute(name, value) {
|
||||
if (name === 'lang') this.langWrites.push(value);
|
||||
super.setAttribute(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
test('#694 AC3 the locale render gate writes lang only when the value changes', async () => {
|
||||
const runtime = new LanguageRuntime([
|
||||
{ code: 'en', dictionary: {} },
|
||||
{ code: 'de', dictionary: {} },
|
||||
{ code: 'fr', loadDictionary: async () => { throw new Error('offline'); } },
|
||||
], 'build', () => {});
|
||||
const host = new AttributeHost();
|
||||
const renders = (code, times = 3) => {
|
||||
for (let index = 0; index < times; index += 1) languageRenderGate(host, runtime, code);
|
||||
};
|
||||
renders('en');
|
||||
assert.deepEqual(host.langWrites, ['en'], 'repeated renders keep the attribute that is already right');
|
||||
renders('de');
|
||||
assert.deepEqual(host.langWrites, ['en', 'de'], 'a language switch writes the new language once');
|
||||
assert.equal(languageRenderGate(host, runtime, 'fr'), 'warm');
|
||||
await runtime.ensure('fr');
|
||||
renders('fr');
|
||||
assert.deepEqual(host.langWrites, ['en', 'de', 'en'], 'the English fallback is written once');
|
||||
assert.equal(host.attrs.get('lang'), 'en');
|
||||
renders('de');
|
||||
renders(null);
|
||||
assert.deepEqual(host.langWrites, ['en', 'de', 'en', 'de'], 'no language code writes nothing');
|
||||
host.attrs.set('lang', 'ru');
|
||||
renders('de');
|
||||
assert.deepEqual(host.langWrites, ['en', 'de', 'en', 'de', 'de'], 'a foreign value is corrected');
|
||||
assert.equal(host.attrs.get('lang'), 'de');
|
||||
});
|
||||
|
||||
test('the production runtime is the tested class, not a handwritten twin (#354)', async () => {
|
||||
const registry = await import('../test-build/i18n/registry.js');
|
||||
assert.ok(
|
||||
|
||||
Reference in New Issue
Block a user