Bundle
@doiiarx/dsh-settings-search-plugin
Search field for the DSH Web Settings panel: independent candidate list over the content column, click to navigate to the owning section and focus the setting
- Source
- DoiiarX
- License
- MIT
- Updated
- Updated 7 days ago
Readme
# dsh-settings-search-plugin
English | [中文](README.zh.md)
A browser-only plugin that adds a search field to the DSH Web Settings panel. Typing shows an independent candidate list over the content column; clicking a candidate navigates to the owning section and, for item-level rows, focuses and highlights the setting. Registration is optional — sections are indexed from the slot ledger and from their rendered DOM, so third-party settings pages are searchable without any plugin changes.
## How the index is built
Searchable rows come from three sources, in priority order:
1. **Slot ledger (declarative, always complete)** — every `settings.section` registration is a tab-level candidate, whether its plugin registers search metadata or not and whether the user ever visited the section. The ledger is read through the injected `slots` service, so any plugin that contributes a settings page appears in search immediately.
2. **Registration surface (optional enhancement)** — a settings plugin may call `window.__DSH_SETTINGS_SEARCH__.register(sectionId, spec)` with item-level rows, rich keywords, and `data-settings-item` anchors for precise jump-and-focus. Registration is an opt-in; sections that skip it still get tab-level search from the ledger.
3. **Rendered-DOM scan (visited sections)** — the section observer scans the active section's DOM (`label > strong + small`, `[class*="title"]/[class*="heading"]` groups, headings, and short leaf text) and caches item-level candidates per section. This covers non-registering plugins once the user opens their page; until then only the tab name is searchable.
Candidate rows render as buttons carrying the setting name, an optional description, and a badge with the owning section's label. Clicking an item navigates to its section, scrolls the setting into view, and flashes an outline around it; anchored (registered) rows resolve by `data-settings-item`, scanned rows fall back to matching the setting title text.
## What it reads and writes
The plugin reads the `slots` service for the ledger and observes the settings dialog DOM. It writes nothing on the wire: search state, the registry surface, and the candidate overlay are all page-local. The registration surface is a plain object on `globalThis` (`__DSH_SETTINGS_SEARCH__`) so settings plugins may register before this plugin's bundle mounts; whichever loads first creates the holder, and `register` always writes the shared map.
The dialog and section containers are located by structure (`nav`, `nav`'s next sibling, its last child), not by class — the DSH client uses CSS Modules with hashed names, so class selectors are unreliable. The nav label is read from the button's longest span text to exclude SVG path data.
## Registration reference
```ts
// In a settings plugin's client apply():
window.__DSH_SETTINGS_SEARCH__?.register('local-shell-tools', {
label: 'Shell',
keywords: 'shell 终端 命令 cmd bash powershell pwsh 语言环境',
items: [
{ id: 'bashLang', label: '语言环境', desc: '写入 LANG 与 LC_ALL', keywords: '语言 locale lang 环境' },
],
})
```
The registry holder is created lazily by either side, so the optional chain is safe even when this plugin is absent or loads later. `items[].id` should match a `data-settings-item` attribute on the rendered setting's container for anchored focus.
## Model Experience
None, as a presentation-only plugin: search candidates and navigation are browser UI and never reach a model request. The plugin contributes no system-prompt prose and no tool schemas.
#### KV Cache effect
No invalidation. The plugin runs entirely in the browser and changes no model request shape, so it never affects an established prefix.
## Known Limitations and Deferred Work
- **Item-level search for never-visited, non-registering sections** — the ledger supplies tab-level rows for every section, but item-level candidates come from the registration surface or the rendered DOM. A section the user has never opened and whose plugin never registers exposes only its tab name; full item coverage for such sections would require reading each plugin's locale dictionaries or a `searchItems` channel in `slots.register`, both deferred.
- **Candidate text is normalized, not stemmed** — matching is case- and whitespace-normalized substring inclusion; pinyin and fuzzy recall are not implemented.
- **Overlay is a page-local absolute-positioned layer** — it tracks the settings panel's header and nav widths at install time; a theme that changes those dimensions after the dialog mounts needs a panel re-open to re-measure.
## Installation
Copy the block below to your DSH agent and it will finish the install:
```text
Install the settings-search plugin (@doiiarx/dsh-settings-search-plugin):
1. Clone the repo:
git clone https://github.com/DoiiarX/dsh-settings-search-plugin
cd dsh-settings-search-plugin
2. Install dependencies: pnpm install
(the built lib/ output is already committed; peer deps @deepseek-ai/cordis
and @deepseek-ai/dsh-client-ui-slots come from the harness runtime.)
3. Mount into the web profile: edit $HOME/.dsh/profiles/web/package.json,
add "@doiiarx/dsh-settings-search-plugin": "link:<absolute-path-to-this-dir>"
under dependencies, and "@doiiarx/dsh-settings-search-plugin" under
dsh.profile.bundles.
4. Run pnpm install in the profile directory.
5. Restart the web process and verify the Settings panel shows a search field.
```
This plugin is browser-only (no settings namespace, no model tools), so it
needs no WEB_SETTINGS_NAMESPACES entry and no host rebuild.
Install
dsh plugin --profile web add github:DoiiarX/dsh-settings-search-plugin
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install doiiarx-dsh-settings-search-plugin from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.