feat(settings): search settings from the header and the command palette (#1714)

* feat(settings): search settings from the header and the command palette

The header search on the Settings page was shown but disabled. It now
searches a shared index of all 56 settings rows by translated title,
description and English synonyms, replaces the section page with ranked
results, and opens a result by scrolling to, focusing and briefly
highlighting its row. Enter opens the best match, and the section
navigation shows per-section match counts.

The command palette gains a "Settings" group that lists the best six
matches for a non-empty query, so any setting is one Ctrl/Cmd+K away.
Rows hidden by the current form state fall back to the control that
reveals them; rows the runtime cannot render are never returned.

The index ships through a new @iptvnator/workspace/shell/util/settings-search
sub-entrypoint so it stays out of the eager bundle, and a registry spec
keeps it in step with the section templates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(settings): let search reveals win over pending input and gate embedded MPV rows

- A reveal (result click, command palette, Enter) now cancels a search
  keystroke still waiting for its debounce, so its q navigation can no
  longer supersede the reveal and leave the results open.
- Embedded MPV extra options and auto-reconnect require a lazily probed
  embedded MPV capability; frame copy also needs frameCopyAvailable, so
  search never offers a row the settings page cannot render.
- Keyboard users keep a focus-visible ring on the revealed row after the
  highlight fades.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(workspace): wait for palette probes without Promise.allSettled

The web tsconfig lib predates Promise.allSettled; use Promise.all over
rejection-safe probes instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: 4gray <fourgray@proton.me>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-09-27 07:42:53 +02:00
1 parent b87291e388
commit ea51cae3db
77 files changed
+3304 -316

No files matched your search

@@ -232,6 +232,9 @@ sub-entrypoints are `@iptvnator/shared/interfaces/ipc-commands` and
`@iptvnator/shared/interfaces` barrel pulls in `ngx-indexed-db`, which the
preload bundle must not carry, so the preload only type-imports the barrel
and value-imports those two dependency-free modules directly.
`@iptvnator/workspace/shell/util/settings-search` exists for the opposite
reason: the main `workspace-shell-util` barrel is imported eagerly, and the
settings search index must stay in the lazy settings and shell chunks.
For a buildable library that has a local `package.json`, its `name` must match
the scoped alias. Nx uses that package name when rewriting buildable dependency
+51 -3
View File
@@ -169,7 +169,8 @@ restarting playback; remote commands retain captured playback order. See the
Search is shell-owned and route-aware:
1. Disabled on settings routes.
1. On settings routes, searches the settings themselves (see
[Settings search](#settings-search)).
2. Enabled on sources routes.
3. Enabled for `/workspace/search`, which is the Electron-only routed
global-search view. `Ctrl/Cmd+F` in Electron opens this route and
@@ -214,8 +215,10 @@ Rail navigation is also shell-owned:
Command palette behavior is shell-owned but view-extensible:
1. The shell resolves commands into three groups in fixed order: current view,
this playlist, then global.
1. The shell resolves commands into groups in fixed order: current view,
this playlist, global, then settings. The settings group appears only for a
non-empty query and holds at most six settings matches (see
[Settings search](#settings-search)).
2. Shell-owned commands are derived from route context and current playlist
state; empty groups are omitted instead of rendering disabled placeholders.
3. Workspace features contribute current-view commands through
@@ -245,6 +248,51 @@ Command palette behavior is shell-owned but view-extensible:
is disabled. The new player setting applies to the next playback session; an
existing stream is not re-mounted.
### Settings search
Settings rows are searchable from the header search on `/workspace/settings`
and from the command palette. Both use the same index and ranking.
1. The index is `SETTINGS_SEARCH_ENTRIES` in
`libs/workspace/shell/util/src/lib/settings-search/`, published through the
`@iptvnator/workspace/shell/util/settings-search` sub-entrypoint. Eager
code imports the main shell util barrel, so the index stays out of it and
ships only in lazy chunks (the initial-bytes ratchet enforces this).
2. Each entry names its section, title and description translation keys,
untranslated synonyms (`keywords`), runtime `requires`, and an optional
`fallbackId`. Section definitions (`SETTINGS_SECTION_DEFINITIONS`) are the
single source for the settings navigation too.
3. Every titled `.setting-item` in the section templates carries
`data-setting-id`. `settings-search-registry.spec.ts` fails when a row, id,
title key or description key drifts from the index, so a new settings row
must be added to the index in the same change.
4. `SettingsSearchService.search()` matches the translated title and
description of the current language plus the keywords; every query token
must match (AND), and a label prefix outranks a word start, which outranks
an inner match. Rows whose `requires` the runtime lacks are never returned.
Embedded MPV rows depend on a lazy support probe
(`ensureEmbeddedMpvSupportLoaded()`), run when the settings page or the
command palette opens, never from shell bootstrap; frame copy also needs
`frameCopyAvailable`, matching the settings page gate.
5. Settings routes use `local-filter` search mode, so the term lives in `q`.
While `q` is set, the settings page shows ranked results in place of the
section page and the settings context panel shows per-section match
counts, muting sections without matches. The search box is shown on
settings even when no playlist exists.
6. Choosing a result, pressing `Enter` in the header search (best match), or
picking a settings command in the palette calls `reveal()`: it navigates
to the section page without `q` (which clears the box) and the page
scrolls to, focuses, and briefly highlights the row once the form is
hydrated. A row hidden by the current form state falls back to its
`fallbackId`, the control that makes it appear. A reveal must win over the
typed term: `WorkspaceShellSearchSyncService` drops a keystroke still
waiting for its debounce through `onReveal()`, and Enter does not apply
the term first, because either `q` sync navigation would supersede the
reveal navigation. Keyboard users keep a `:focus-visible` ring on the row
after the highlight fades.
7. `Ctrl/Cmd+F` on settings focuses the header search instead of opening
global search.
Keyboard shortcut help is shell-owned:
1. `WorkspaceKeyboardShortcutsService` is provided by `WorkspaceShellComponent`.