Skip to content

feat(#3850): add searchable settings with section navigation and field highlight#3936

Open
rodboev wants to merge 3 commits into
nesquena:masterfrom
rodboev:pr/settings-search
Open

feat(#3850): add searchable settings with section navigation and field highlight#3936
rodboev wants to merge 3 commits into
nesquena:masterfrom
rodboev:pr/settings-search

Conversation

@rodboev

@rodboev rodboev commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Thinking Path

  • Settings has seven sections and dozens of fields; users who remember a label but not its section have to click through tabs one by one — there is no find-in-page equivalent.
  • The navigation target already exists: switchSettingsSection() at panels.js:6068 activates a pane and handles lazy loading for Providers/Plugins; all we need is to wire a search input to it.
  • The highlight pattern is already established by _highlightQuestionRow() in ui.js:494 — a CSS animation class added then removed after 1.8s using a box-shadow pulse on var(--focus-ring). The same pattern applies here to .settings-field.
  • The tricky case is Providers and Plugins, which are lazy-loaded by loadProvidersPanel() / loadPluginsPanel(). The spec says to index from live DOM after eagerly loading both panes on first search focus, which is the right call: it keeps the index always current and avoids maintaining a parallel i18n-key allowlist.

What Changed

  • static/index.html: add #settingsSearch input and #settingsSearchResults dropdown inside #settingsMenu before the first section button (line 351)
  • static/panels.js: add _buildSettingsIndex(), filterSettings(), _navigateToSettingsField(), _highlightSettingsField(); invalidate _settingsIndex in _beginSettingsPanelSession(); register outside-click dismissal
  • static/i18n.js: add settings_search_placeholder, settings_search_no_results
  • static/style.css: add .settings-search-results, .settings-search-result, .settings-search-section/arrow/label/empty, .settings-field-highlight animation

Why It Matters

Users can now find any setting by name without knowing its section, making the settings panel usable as a control center rather than a tab-navigation puzzle — especially useful for power users managing many settings across sections.

Verification

$env:PYTHONUTF8 = '1'
..\hermes-agent\venv\Scripts\python.exe -m pytest tests/test_3850_settings_search.py -v --timeout=60
..\hermes-agent\venv\Scripts\python.exe -m pytest tests/ -v --timeout=60

Manual: open Settings, type "theme" in search input, verify results show "Appearance › Theme"; click result, verify Appearance pane opens and theme field scrolls into view with pulse animation.

Risks / Follow-ups

  • Index is built from live DOM; fields added by plugins after panel open won't appear until the panel is closed and re-opened (acceptable for the first cut)
  • Results are capped at 12 to keep the dropdown manageable; can be made pageable in a follow-up
  • The scrollIntoView call uses requestAnimationFrame to wait for pane activation; an additional frame delay may be needed if lazy-loaded panes take longer than one frame to render their fields
  • No keyboard navigation in the results dropdown in this PR (arrow keys, Enter); can be added as an enhancement

Model Used

Claude Opus 4.6 via Claude Code CLI

@greptile-apps

greptile-apps Bot commented Jun 10, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a search input to the Settings panel that filters across all sections, navigates to the matching pane, scrolls the field into view, and pulses a highlight animation. The async index build, session-invalidation guard, and DOM re-resolution logic are well-thought-out — but the index query uses .settings-field which is absent from the dynamically-built Providers and Plugins panes, so those sections' settings are silently excluded and the eager panel loads are wasted network I/O.

  • Search UI: #settingsSearch input + #settingsSearchResults dropdown added to #settingsMenu; dropdown is correctly nested inside the .settings-search positioned wrapper.
  • Index lifecycle: _buildSettingsIndex() is memoized per session via a promise ref; _beginSettingsPanelSession() resets both _settingsIndex and _settingsIndexPromise, and the IIFE uses a promise-identity check to prevent stale writes across session boundaries.
  • Navigation & highlight: _resolveSettingsField() re-resolves the target by i18n key in the live DOM so post-index pane re-renders don't break scroll/highlight; _highlightSettingsField() mirrors the existing _highlightQuestionRow pattern.

Confidence Score: 4/5

Safe to merge for the five static sections; Providers and Plugins settings will not appear in search results despite the eager loading.

The session-safety logic, stale-query guard, and DOM re-resolution are all correct. The one real defect is that _buildSettingsIndex queries .settings-field elements in the Providers and Plugins panes, but those panes are built entirely with provider-card-field / provider-card DOM nodes — not .settings-field — so zero entries are ever indexed from them. Every first search triggers two unnecessary network fetches (one per panel) that produce no index entries, and the most commonly changed settings (API keys, model configs) remain unsearchable.

static/panels.js — _buildSettingsIndex() and _buildProviderCard()/_buildPluginCard() to reconcile the selector mismatch

Important Files Changed

Filename Overview
static/panels.js Adds _buildSettingsIndex, filterSettings, _navigateToSettingsField, _resolveSettingsField, _highlightSettingsField. Core logic for session safety and rAF scroll is sound, but _buildSettingsIndex eagerly loads Provider/Plugin panes that have no .settings-field elements, making the eager loads wasted I/O and leaving those sections unsearchable.
static/index.html Adds search input and results dropdown inside .settings-search wrapper; #settingsSearchResults is correctly nested inside the positioned wrapper so absolute positioning resolves properly.
static/i18n.js Adds settings_search_placeholder and settings_search_no_results to all 13 locales; translations look appropriate.
static/style.css Adds dropdown positioning, result item styles, and settings-field-pulse animation; position:relative on .settings-search is redundant (.sidebar-search already sets it) but harmless.
tests/test_3850_settings_search.py Static text-search regression tests that verify all new functions, CSS classes, and i18n keys are present; structurally sound and matches the implementation.

Sequence Diagram

sequenceDiagram
    participant U as User
    participant SI as settingsSearch input
    participant FS as filterSettings()
    participant BI as _buildSettingsIndex()
    participant LP as loadProvidersPanel / loadPluginsPanel
    participant DOM as Live DOM
    participant NF as _navigateToSettingsField()

    U->>SI: types query (oninput)
    SI->>FS: filterSettings(query)
    FS->>FS: "seq = ++_settingsSearchSeq"
    FS->>BI: await _buildSettingsIndex()
    alt first search this session
        BI->>LP: await Promise.all([load…])
        LP-->>DOM: rebuild pane HTML (network fetch)
        BI->>DOM: querySelectorAll('.settings-field') per pane
        DOM-->>BI: index entries (Providers/Plugins: 0 results ⚠️)
        BI-->>FS: index ready
    else index already built
        BI-->>FS: return immediately
    end
    FS->>FS: seq check (drop stale queries)
    FS->>FS: render up to 12 results in dropdown
    U->>FS: clicks a result
    FS->>NF: _navigateToSettingsField(entry)
    NF->>NF: "switchSettingsSection(sectionKey, {skipLazyLoad:true})"
    NF->>NF: requestAnimationFrame → _resolveSettingsField(entry)
    NF->>DOM: querySelector label[data-i18n].closest('.settings-field')
    NF->>NF: scrollIntoView + _highlightSettingsField(el)
Loading

Reviews (4): Last reviewed commit: "fix(#3850): resolve search targets in th..." | Re-trigger Greptile

Comment thread static/panels.js
Comment thread static/style.css
Comment thread static/panels.js
@rodboev rodboev force-pushed the pr/settings-search branch from a5495ca to 691910f Compare June 10, 2026 16:58
Comment thread static/panels.js Outdated
Comment thread static/panels.js
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant