Skip to content

Commit e85ae44

Browse files
kingpanther13claude
andcommitted
fix(settings-ui): preserve search filter across tool-toggle rebuilds
Reported: typing a query in the Tools tab search bar filtered the list correctly, but toggling a setting on a visible tool snapped the full list back into view — even though the search bar still showed the query. Root cause: ``render()`` (called after every toggle change via ``scheduleSave(); render();``) does ``container.innerHTML = ''`` and rebuilds the entire ``.tool`` DOM. The search filter applied the ``hidden`` class imperatively to the OLD nodes; the freshly-built nodes had none. The ``<input>`` element itself is outside the rebuilt container, so its value persists — producing the visible-query-but-no-filter mismatch the user reported. Fix: extract the filter into ``applyToolSearch()`` that reads the search input's current value directly and applies / clears the ``hidden`` class on every ``.tool`` node it finds. Wire it to the input's ``input`` event (was inline before) and also call it at the end of ``render()`` so any rebuild — toggle change, group-master toggle, future render triggers — re-applies the active filter against the new DOM. No regression risk on the no-search path: an empty query short- circuits ``!q`` to true and every tool stays visible, same outcome as before. Group auto-expansion when search has matches still works (q && visible branch unchanged). Not directly testable in the current JS test infra (the project bar is ``node --check`` syntax-only — no JSDOM, no DOM event simulation). Closing that gap is tracked in #1422; ``node --check`` on the rebuilt script confirms no syntax regression. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 9a108ef commit e85ae44

1 file changed

Lines changed: 18 additions & 3 deletions

File tree

src/ha_mcp/settings_ui.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,6 +1298,15 @@ def apply_tool_visibility(
12981298
`<span style="color:var(--success)">${enabledCount} enabled</span>` +
12991299
`<span style="color:var(--accent)">${pinnedCount} pinned</span>` +
13001300
`<span style="color:var(--danger)">${disabledCount} disabled</span>`;
1301+
1302+
// ``render()`` rebuilds the entire ``.tool`` DOM, so any
1303+
// ``hidden`` class previously applied by ``applyToolSearch`` is
1304+
// wiped. The search ``<input>`` is a separate element and keeps
1305+
// its value across the rebuild — re-apply the filter so the
1306+
// visible list matches what the user has typed. Otherwise
1307+
// toggling a setting on a filtered tool snaps the full list back
1308+
// even though the search box still shows the query.
1309+
applyToolSearch();
13011310
}
13021311
13031312
function scheduleSave() {
@@ -1331,8 +1340,12 @@ def apply_tool_visibility(
13311340
el.className = saved ? 'status saved' : 'status';
13321341
}
13331342
1334-
document.getElementById('search').addEventListener('input', (e) => {
1335-
const q = e.target.value.toLowerCase();
1343+
function applyToolSearch() {
1344+
// Read the current search query directly from the DOM rather than
1345+
// taking it as a parameter — ``render()`` calls this after rebuilding
1346+
// the tool DOM and needs to use whatever the user currently has
1347+
// typed without coordinating with the input event.
1348+
const q = (document.getElementById('search').value || '').toLowerCase();
13361349
document.querySelectorAll('.tool').forEach(el => {
13371350
const match = !q || el.dataset.name.includes(q) || el.dataset.title.includes(q);
13381351
el.classList.toggle('hidden', !match);
@@ -1346,7 +1359,9 @@ def apply_tool_visibility(
13461359
g.querySelector('.group-chevron').classList.add('open');
13471360
}
13481361
});
1349-
});
1362+
}
1363+
1364+
document.getElementById('search').addEventListener('input', applyToolSearch);
13501365
13511366
document.getElementById('restartBtn').addEventListener('click', restartAddon);
13521367

0 commit comments

Comments
 (0)