feat(settings): search across all settings (#453 phase 3) #467

Merged
jared merged 2 commits from feat/settings-search into lotus 2026-10-25 19:38:23 -04:00
Owner

Phase 3 of the settings audit #453. Fixes #463.

Stacked on #466 (phase 2), which is still in CI. Until #466 merges, this PR's diff also shows its commits. The new commit is 22d5aea5.

What it does

  • The search box sits at the top of the settings sidebar. Ctrl/Cmd+F focuses it while settings are open; it captures the key before the app's own message search.
  • Results replace the page list while you type. Each shows the setting's title and the page and section it's on, e.g. "Push to deafen" with "Calls & audio · Push to talk and hotkeys".
  • Picking a result, or pressing Enter for the top one, opens that page, scrolls to the setting and briefly outlines it. Esc clears the search.
  • Matching: every word must appear in the title, description or section, and title matches rank first. So "utm" finds "Strip tracking parameters from links" by its description.
  • Desktop-only options only show in the desktop app.

How the index works (no hand-kept list)

  • At first search, a lazy chunk loads the settings .tsx source as raw text (import.meta.glob(..., { query: '?raw' })). It isn't in the main bundle.
  • parseSettingsSource pulls each <SettingTile title=… description=…> and each section header out of it.
  • The page comes from the folder. For General.tsx, it comes from which section function the setting is in.
  • Options added later are found automatically. Only a brand-new section function in General.tsx needs a line in GENERAL_SECTIONS.
  • Dynamic titles (template strings with ${}, variables) are skipped.

Tested

  • New unit tests (3) run against the real settings source. They check that the right pages are found (Calls, Privacy, Appearance, Messages, Advanced, Notifications, Desktop-only), check ranking, hide desktop-only options in a browser, and cover the parser's attribute forms. 1,443 pass in total.
  • New Playwright settings-search.spec.ts covers:
    • Ctrl+F focusing the box;
    • "deafen" finding the setting on Calls & audio, with the page list hidden;
    • clicking a result opening the page with the setting in view;
    • "utm" plus Enter landing on Privacy & security;
    • the no-match message;
    • Esc clearing the search without closing settings.
  • 16/16 with security-nudge, your-data, a11y (axe + menu snapshot) and boot. Lint is at 29, unchanged.

🤖 Generated with Claude Code

https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5

Phase 3 of the settings audit #453. Fixes #463. **Stacked on #466** (phase 2), which is still in CI. Until #466 merges, this PR's diff also shows its commits. The new commit is `22d5aea5`. **What it does** - **The search box** sits at the top of the settings sidebar. **Ctrl/Cmd+F** focuses it while settings are open; it captures the key before the app's own message search. - **Results** replace the page list while you type. Each shows the setting's title and the page and section it's on, e.g. "Push to deafen" with "Calls & audio · Push to talk and hotkeys". - **Picking a result**, or pressing **Enter** for the top one, opens that page, scrolls to the setting and briefly outlines it. **Esc** clears the search. - **Matching:** every word must appear in the title, description or section, and title matches rank first. So "utm" finds "Strip tracking parameters from links" by its description. - **Desktop-only options** only show in the desktop app. **How the index works (no hand-kept list)** - At first search, a lazy chunk loads the settings `.tsx` source as raw text (`import.meta.glob(..., { query: '?raw' })`). It isn't in the main bundle. - `parseSettingsSource` pulls each `<SettingTile title=… description=…>` and each section header out of it. - The page comes from the folder. For `General.tsx`, it comes from which section function the setting is in. - Options added later are found automatically. Only a brand-new section function in `General.tsx` needs a line in `GENERAL_SECTIONS`. - Dynamic titles (template strings with `${}`, variables) are skipped. **Tested** - New unit tests (3) run against the real settings source. They check that the right pages are found (Calls, Privacy, Appearance, Messages, Advanced, Notifications, Desktop-only), check ranking, hide desktop-only options in a browser, and cover the parser's attribute forms. 1,443 pass in total. - New Playwright `settings-search.spec.ts` covers: - Ctrl+F focusing the box; - "deafen" finding the setting on Calls & audio, with the page list hidden; - clicking a result opening the page with the setting in view; - "utm" plus Enter landing on Privacy & security; - the no-match message; - Esc clearing the search without closing settings. - 16/16 with security-nudge, your-data, a11y (axe + menu snapshot) and boot. Lint is at 29, unchanged. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared added 4 commits 2026-10-25 12:36:45 -04:00
#459 General held 76 of ~110 settings (11 screens). Its sections now live
on pages named for what people come to do:
  Account, Appearance, Messages (incl. editor, date and time),
  Notifications, Calls & audio, Privacy & security (privacy options +
  the former Devices page), Data & storage (storage, sync, your data),
  Desktop (desktop app only), Emojis & stickers, Advanced (the former
  Developer Tools + "show hidden events" / "legacy username colour"),
  About.
The longest page is now ~3 screens (Appearance). Settings opens on
Account. Deep links keep working: "devices" lands on Privacy & security,
the old "general" on Appearance, the update notice on Desktop. Text that
pointed at the old page names is updated. The push-rule editor gets one
line explaining the Matrix terms. Remaining Title Case labels on Account
and Devices are sentence case.

#460 Chat background, seasonal theme and avatar decoration grids sit
behind one row: the current choice and a Change button (the first two
close after a choice; decorations keep their Save step).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
#461 Both link-preview switches move into Privacy. The two overlapping
receipt switches become one choice that maps exactly onto the existing
settings (no migration, every old combination behaves the same):
Everyone / Private read receipts / Hide my activity (also hides typing
and other people's receipts, as "Hide typing and read receipts" did).

#462 Calls & audio is grouped, the same in every mode: Joining,
Microphone and noise, Push to talk and hotkeys, Sounds, Quality,
Soundboard. No setting changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
fix(settings): visible radio buttons for the privacy and import choices
CI / Build & Quality Checks (push) Skipped
CI / Secret scan (gitleaks) (push) Skipped
CI / Playwright smoke (e2e) (push) Skipped
CI / Build & Quality Checks (pull_request) Successful in 3m22s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 8s
CI / Playwright smoke (e2e) (pull_request) Successful in 19m52s
b93e4bcef9
The app CSS hides native radio inputs, so the read-receipt choice and the
"keep mine / use the file's" import choice showed no selection. Use the
folds RadioButton.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
feat(settings): search across all settings (#463)
CI / Build & Quality Checks (push) Skipped
CI / Secret scan (gitleaks) (push) Skipped
CI / Playwright smoke (e2e) (push) Skipped
CI / Build & Quality Checks (pull_request) Successful in 3m44s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 16s
CI / Playwright smoke (e2e) (pull_request) Failing after 23m12s
22d5aea5e6
A search box at the top of the settings sidebar (Ctrl/Cmd+F while settings
are open). Results replace the page list while typing, showing the page and
section; picking one (or Enter for the top result) opens that page, scrolls
to the setting and briefly outlines it. Esc clears.

The index is built from the settings source itself (SettingTile titles and
descriptions, section headers), loaded as raw text in a lazy chunk on first
search, so options added later are found without a hand-kept list.
Desktop-only options only show in the desktop app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared scheduled this pull request to auto merge when all checks succeed 2026-10-25 12:36:52 -04:00
jared added 1 commit 2026-10-25 19:12:08 -04:00
fix(media): retry an image once when it raced the service worker
CI / Build & Quality Checks (push) Skipped
CI / Secret scan (gitleaks) (push) Skipped
CI / Playwright smoke (e2e) (push) Skipped
CI / Build & Quality Checks (pull_request) Successful in 3m57s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 10s
CI / Playwright smoke (e2e) (pull_request) Successful in 20m44s
02bd356a89
On a first visit (or right after an update) the service worker only takes
control when it activates. An image requested before then goes out without
the access token, fails, and showed "Retry". Now a failed image waits for the
worker to take control (or a moment) and retries once by itself; "Retry"
appears only if that fails too. Encrypted media is unchanged.

Also the cause of the flaky "#219 gallery lightbox" e2e test (1 in 4 locally,
10/10 after).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
Author
Owner

CI failure on the first run, now fixed in 02bd356a.

The failing test was the "#219 gallery lightbox" e2e test, which is unrelated to search. The test image showed its "Retry" overlay because the media download had failed.

Cause: on a first visit, or just after an update, the service worker only takes control of the page once it activates. An image requested before then goes out without the access token and fails. Every Playwright test starts in a fresh browser, and real users can see the same "Retry" on first load.

Fix: a failed image now waits for the worker to take control (or a moment) and retries once by itself. "Retry" only shows if that fails too. Encrypted media is unchanged.

Result: locally the test failed 1 run in 4 before, and passes 10/10 after. Unit tests: 1,443 pass.

🤖 Generated with Claude Code

**CI failure on the first run, now fixed in `02bd356a`.** The failing test was the "#219 gallery lightbox" e2e test, which is unrelated to search. The test image showed its "Retry" overlay because the media download had failed. **Cause:** on a first visit, or just after an update, the service worker only takes control of the page once it activates. An image requested before then goes out without the access token and fails. Every Playwright test starts in a fresh browser, and real users can see the same "Retry" on first load. **Fix:** a failed image now waits for the worker to take control (or a moment) and retries once by itself. "Retry" only shows if that fails too. Encrypted media is unchanged. **Result:** locally the test failed 1 run in 4 before, and passes 10/10 after. Unit tests: 1,443 pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jared merged commit 222927b558 into lotus 2026-10-25 19:38:23 -04:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/cinny#467