diff --git a/static/index.html b/static/index.html
index 13e92827..e5209e71 100644
--- a/static/index.html
+++ b/static/index.html
@@ -1293,7 +1293,7 @@
Automatically re-generates the session title based on the latest exchange, keeping it relevant as the conversation evolves. Requires an LLM title generation model to be configured.
-
+
Busy input mode
Queue follow-up
diff --git a/static/panels.js b/static/panels.js
index 397ce14f..6c7d11ce 100644
--- a/static/panels.js
+++ b/static/panels.js
@@ -6953,7 +6953,14 @@ async function _buildSettingsIndex() {
if (!labelEl) return;
const i18nKey = labelEl.dataset ? labelEl.dataset.i18n : undefined;
const label = (i18nKey && t(i18nKey)) || labelEl.textContent.trim();
- if (label) index.push({ label, sectionKey, i18nKey, el: field });
+ if (label) {
+ const searchBlob = [label, field.textContent, field.dataset ? field.dataset.settingsSearch : '']
+ .filter(Boolean)
+ .join(' ')
+ .replace(/\s+/g, ' ')
+ .trim();
+ index.push({ label, searchBlob, sectionKey, i18nKey, el: field });
+ }
});
if (sectionKey === 'providers') {
pane.querySelectorAll('.provider-card').forEach(card => {
@@ -7001,7 +7008,7 @@ async function filterSettings(query) {
help: t('settings_tab_help') || 'Help',
};
const matches = (_settingsIndex || []).filter(entry =>
- entry.label.toLowerCase().includes(q)
+ (entry.searchBlob || entry.label).toLowerCase().includes(q)
);
if (!matches.length) {
resultsEl.innerHTML = `${esc(t('settings_search_no_results') || 'No settings found.')}
`;
diff --git a/tests/test_3850_settings_search.py b/tests/test_3850_settings_search.py
index ea963cbb..e2415d13 100644
--- a/tests/test_3850_settings_search.py
+++ b/tests/test_3850_settings_search.py
@@ -42,17 +42,25 @@ class TestSettingsSearch:
assert "function _buildSettingsIndex()" in PANELS_JS, (
"panels.js must contain _buildSettingsIndex() function"
)
- assert "settingsPaneConversation" in PANELS_JS[PANELS_JS.find("function _buildSettingsIndex()"):], (
+ body = PANELS_JS[PANELS_JS.find("function _buildSettingsIndex()"):]
+ assert "settingsPaneConversation" in body, (
"_buildSettingsIndex must reference settingsPaneConversation pane"
)
+ assert "searchBlob" in body, (
+ "_buildSettingsIndex must store a searchBlob for settings fields"
+ )
def test_panels_js_has_filter_settings_function(self):
"""panels.js must contain filterSettings function."""
assert "function filterSettings(query)" in PANELS_JS, (
"panels.js must contain filterSettings(query) function"
)
- assert "toLowerCase().includes(q)" in PANELS_JS, (
- "filterSettings must do case-insensitive substring matching"
+ body = PANELS_JS[PANELS_JS.find("function filterSettings(query)"):]
+ assert "(entry.searchBlob || entry.label).toLowerCase().includes(q)" in body, (
+ "filterSettings must do case-insensitive substring matching over the search blob"
+ )
+ assert "esc(m.label)" in body, (
+ "filterSettings must keep rendering the visible label text"
)
def test_panels_js_has_navigate_to_field_function(self):
diff --git a/tests/test_5148_settings_search_busy_input_terms.py b/tests/test_5148_settings_search_busy_input_terms.py
new file mode 100644
index 00000000..647eb9d4
--- /dev/null
+++ b/tests/test_5148_settings_search_busy_input_terms.py
@@ -0,0 +1,47 @@
+"""Regression coverage for busy-input settings search terms."""
+from pathlib import Path
+
+INDEX_HTML = (Path(__file__).parent.parent / "static" / "index.html").read_text(encoding="utf-8")
+PANELS_JS = (Path(__file__).parent.parent / "static" / "panels.js").read_text(encoding="utf-8")
+
+
+class TestBusyInputSettingsSearchTerms:
+ """Busy-input settings search must cover option, descriptor, and supplemental text."""
+
+ def test_busy_input_field_has_supplemental_search_terms(self):
+ """The busy-input field must expose the supplemental terms from HTML."""
+ assert 'data-settings-search="message default while running queue interrupt steer"' in INDEX_HTML, (
+ "busy-input field must expose supplemental search terms in data-settings-search"
+ )
+ assert "Busy input mode" in INDEX_HTML, (
+ "busy-input field label must still be present in the HTML"
+ )
+
+ def test_settings_index_uses_field_text_for_busy_input(self):
+ """The index builder must include option and descriptor text in searchBlob."""
+ idx = PANELS_JS.find("function _buildSettingsIndex()")
+ assert idx >= 0, "_buildSettingsIndex not found"
+ body = PANELS_JS[idx:idx + 3200]
+ assert "searchBlob" in body, "_buildSettingsIndex must build a searchBlob"
+ assert "field.textContent" in body, "_buildSettingsIndex must include field text in searchBlob"
+ assert "field.dataset ? field.dataset.settingsSearch : ''" in body, (
+ "_buildSettingsIndex must include supplemental search terms in searchBlob"
+ )
+
+ def test_filter_settings_uses_search_blob_and_keeps_label_rendering(self):
+ """filterSettings must match searchBlob, render labels, and keep the cap."""
+ idx = PANELS_JS.find("function filterSettings(query)")
+ assert idx >= 0, "filterSettings not found"
+ body = PANELS_JS[idx:]
+ assert "(entry.searchBlob || entry.label).toLowerCase().includes(q)" in body, (
+ "filterSettings must search the richer blob instead of label-only text"
+ )
+ assert "esc(m.label)" in body, (
+ "filterSettings must keep rendering the visible label"
+ )
+ assert ".slice(0, 12)" in body, (
+ "filterSettings must keep the existing 12-result cap"
+ )
+ assert ".sort(" not in body, (
+ "filterSettings must not introduce ranking or reordering"
+ )