From a2fb3d180f66d7dc29df4023edf66b67ea34cf75 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 8 Jul 2026 16:11:11 +0000 Subject: [PATCH] Serve the settings search index from the server as JSON Move index building off the client so there is no client-side HTML fetching or DOM parsing (resolves Codacy's variable-fetch and DOMParser flags on the read-only, same-origin index build). - Add GET /v3/settings/search-index (pages_v3.py): renders the settings partials server-side and extracts each field's anchor id, key, label, tooltip, and section with a small stdlib HTMLParser, then caches the result keyed on the installed-plugin set. Parsing the rendered HTML keeps anchor ids identical to the live DOM, so the index cannot drift. - settings-search.js: buildIndex() now does a single fetch of the literal endpoint + .json(); removed the per-partial fetch loop, DOMParser, scanDoc, CORE_TABS, and the plugin-id allowlist. Search, keyboard nav, navigation, and the per-tab filter are unchanged. Net: fewer requests and no client-side HTML parsing. Verified with a new endpoint test in test_web_settings_ui.py (12 pass) and the headless-Chromium test (tooltip, filter, search navigate + flash all green). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_014gZxznuxw8L92FUMBN3Nqz --- test/test_web_settings_ui.py | 25 +++ web_interface/blueprints/pages_v3.py | 160 +++++++++++++++++- web_interface/static/v3/js/settings-search.js | 98 ++--------- 3 files changed, 202 insertions(+), 81 deletions(-) diff --git a/test/test_web_settings_ui.py b/test/test_web_settings_ui.py index 657f5c54..9ae4c1cb 100644 --- a/test/test_web_settings_ui.py +++ b/test/test_web_settings_ui.py @@ -115,3 +115,28 @@ def test_display_tooltip_carries_rich_text(client): body = client.get("/v3/partials/display").get_data(as_text=True) assert 'id="setting-display-brightness"' in body assert "Recommended:" in body # rich detail authored into a tooltip + + +def test_search_index_endpoint(client): + """The server-built search index powers the global settings search.""" + resp = client.get("/v3/settings/search-index") + assert resp.status_code == 200 + data = resp.get_json() + assert isinstance(data, dict) and isinstance(data.get("fields"), list) + fields = data["fields"] + assert len(fields) >= 40, "expected the core settings fields to be indexed" + + by_id = {f["anchorId"]: f for f in fields} + # Representative fields across tabs must be present with usable text. + for anchor in ("setting-general-timezone", "setting-display-brightness", + "setting-wifi-password"): + assert anchor in by_id, f"{anchor} missing from search index" + entry = by_id[anchor] + assert entry["label"], f"{anchor} has no label" + assert entry["help"], f"{anchor} has no tooltip help" + assert entry["tab"] and entry["tabLabel"] + + # Every entry must carry a non-empty label and a stable anchor id. + assert all(f["label"] and f["anchorId"].startswith("setting-") for f in fields) + # Section context is captured for grouped fields (e.g. Display hardware). + assert by_id["setting-display-brightness"]["section"] == "Hardware Configuration" diff --git a/web_interface/blueprints/pages_v3.py b/web_interface/blueprints/pages_v3.py index b011480a..1966f7ea 100644 --- a/web_interface/blueprints/pages_v3.py +++ b/web_interface/blueprints/pages_v3.py @@ -1,6 +1,7 @@ -from flask import Blueprint, render_template, flash +from flask import Blueprint, render_template, flash, jsonify from jinja2 import TemplateNotFound from markupsafe import escape +from html.parser import HTMLParser import json import logging import os @@ -22,6 +23,114 @@ plugin_store_manager = None pages_v3 = Blueprint('pages_v3', __name__) + +class _SettingsIndexParser(HTMLParser): + """Extract searchable settings fields from a rendered partial's HTML. + + Captures one entry per ``
``: the + anchor id, ``data-setting-key``, the field's ``