mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-02 17:28:05 +00:00
Address CodeRabbit review on settings search/tooltips
- pages_v3: include Durations tab in the search index so setting-durations-* fields are actually indexed - test_web_settings_ui: assert setting-durations-clock is present in the search-index endpoint response - settings-search.js: on index fetch failure, reset buildPromise instead of caching an empty (truthy) index so search can retry - settings-search.js: filterScope returns null (not document) when no tab container matches, and the caller guards, so the per-tab filter can't hide fields across unrelated tabs - settings-search.js: refresh the stale header comment to describe the server-side JSON index flow - app.css: cap #settings-search-results height with overflow-y so the dropdown scrolls instead of overflowing small screens Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gZxznuxw8L92FUMBN3Nqz
This commit is contained in:
@@ -129,7 +129,7 @@ def test_search_index_endpoint(client):
|
|||||||
by_id = {f["anchorId"]: f for f in fields}
|
by_id = {f["anchorId"]: f for f in fields}
|
||||||
# Representative fields across tabs must be present with usable text.
|
# Representative fields across tabs must be present with usable text.
|
||||||
for anchor in ("setting-general-timezone", "setting-display-brightness",
|
for anchor in ("setting-general-timezone", "setting-display-brightness",
|
||||||
"setting-wifi-password"):
|
"setting-wifi-password", "setting-durations-clock"):
|
||||||
assert anchor in by_id, f"{anchor} missing from search index"
|
assert anchor in by_id, f"{anchor} missing from search index"
|
||||||
entry = by_id[anchor]
|
entry = by_id[anchor]
|
||||||
assert entry["label"], f"{anchor} has no label"
|
assert entry["label"], f"{anchor} has no label"
|
||||||
|
|||||||
@@ -232,6 +232,7 @@ def settings_search_index():
|
|||||||
core_tabs = [
|
core_tabs = [
|
||||||
('general', 'General', _load_general_partial),
|
('general', 'General', _load_general_partial),
|
||||||
('display', 'Display', _load_display_partial),
|
('display', 'Display', _load_display_partial),
|
||||||
|
('durations', 'Durations', _load_durations_partial),
|
||||||
('schedule', 'Schedule', _load_schedule_partial),
|
('schedule', 'Schedule', _load_schedule_partial),
|
||||||
('wifi', 'WiFi', _load_wifi_partial),
|
('wifi', 'WiFi', _load_wifi_partial),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -838,6 +838,8 @@ button.bg-white {
|
|||||||
box-shadow: var(--shadow-lg);
|
box-shadow: var(--shadow-lg);
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
padding: 0.25rem;
|
padding: 0.25rem;
|
||||||
|
max-height: min(60vh, 24rem);
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ssr-group {
|
.ssr-group {
|
||||||
|
|||||||
@@ -6,15 +6,16 @@
|
|||||||
* .help-tip[data-tooltip]), so it can never drift from what is rendered:
|
* .help-tip[data-tooltip]), so it can never drift from what is rendered:
|
||||||
*
|
*
|
||||||
* 1. Global search (header box): finds settings across ALL tabs, even ones
|
* 1. Global search (header box): finds settings across ALL tabs, even ones
|
||||||
* not yet opened, by fetching each tab's partial once and scanning it.
|
* not yet opened, by fetching a single server-side JSON index
|
||||||
|
* (/v3/settings/search-index) built from all rendered partials.
|
||||||
* Clicking a result switches to the tab, waits for the field to load,
|
* Clicking a result switches to the tab, waits for the field to load,
|
||||||
* then scrolls to and flashes it.
|
* then scrolls to and flashes it.
|
||||||
* 2. Per-tab filter (the .settings-filter box under a partial title):
|
* 2. Per-tab filter (the .settings-filter box under a partial title):
|
||||||
* hides non-matching fields on the current tab. Delegated, so it keeps
|
* hides non-matching fields on the current tab. Delegated, so it keeps
|
||||||
* working across HTMX swaps.
|
* working across HTMX swaps.
|
||||||
*
|
*
|
||||||
* No backend endpoint is required — plugins are enumerated from
|
* The server owns index generation (including plugin enumeration) and caches
|
||||||
* window.installedPlugins and their config partials are fetched the same way.
|
* it per installed-plugin set, so the client makes exactly one JSON request.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
@@ -64,8 +65,10 @@
|
|||||||
return fields;
|
return fields;
|
||||||
})
|
})
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
window._settingsIndex = [];
|
// Don't cache the failure: clear the in-flight promise so a
|
||||||
return window._settingsIndex;
|
// later call can retry after a transient fetch error.
|
||||||
|
buildPromise = null;
|
||||||
|
return [];
|
||||||
});
|
});
|
||||||
return buildPromise;
|
return buildPromise;
|
||||||
}
|
}
|
||||||
@@ -309,10 +312,12 @@
|
|||||||
// --- Per-tab filter (delegated) -------------------------------------------
|
// --- Per-tab filter (delegated) -------------------------------------------
|
||||||
|
|
||||||
function filterScope(input) {
|
function filterScope(input) {
|
||||||
|
// Return the nearest tab/content container, or null — never `document`,
|
||||||
|
// which would let the filter hide setting fields across unrelated tabs.
|
||||||
return input.closest('.plugin-config-tab') ||
|
return input.closest('.plugin-config-tab') ||
|
||||||
input.closest('[id$="-content"]') ||
|
input.closest('[id$="-content"]') ||
|
||||||
input.closest('.bg-white') ||
|
input.closest('.bg-white') ||
|
||||||
document;
|
null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function fieldHay(fg) {
|
function fieldHay(fg) {
|
||||||
@@ -364,7 +369,8 @@
|
|||||||
document.addEventListener('input', function (e) {
|
document.addEventListener('input', function (e) {
|
||||||
var box = e.target.closest ? e.target.closest('.settings-filter') : null;
|
var box = e.target.closest ? e.target.closest('.settings-filter') : null;
|
||||||
if (!box) return;
|
if (!box) return;
|
||||||
applyTabFilter(filterScope(box), box.value);
|
var scope = filterScope(box);
|
||||||
|
if (scope) applyTabFilter(scope, box.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- Boot -----------------------------------------------------------------
|
// --- Boot -----------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user