dsh-models-filter
A DeepSeek Harness profile bundle that adds a filter input at the top of the
model-list pane of the composer model menu. The root Model/Effort pane and
every other part of the menu stay exactly as shipped.
[!CAUTION]
This plugin has been fully vibe-coded with GLM-5.3-FLASH (Ox Alpha).
It was a simple exercise to try out the LLM during its free test phase.
(Everything came straight from the LLM — code, comments, readme, etc.)
I don't have any experience writing DSH plugins, so I can't vouch for the code quality.
[!WARNING]
I'm not a big fan of how the CSS classes are used inside the plugin, especially the const C = {} map, because in future versions of DSH the class hash prefix may change and this will break the plugin.
So this plugin is actually dependent on DSH's 0.1.1-rc.2 version.
[!NOTE]
Post on X (with a short video showing the plugin in action)
Install
From a local checkout:
dsh plugin --profile web add ./dsh-models-filter
Or straight from GitHub (any pnpm-compatible spec works):
dsh plugin --profile web add gh:<user>/dsh-models-filter
# or:
dsh plugin --profile web add git+https://github.com/<user>/dsh-models-filter.git
Then restart the profile (dsh web) so the new bundle layer composes in.
lib/ artifacts are committed, so installs never need a build step and no
pnpm allowBuilds entry.
Uninstall with dsh plugin --profile web remove dsh-models-filter, or disable
just this row without removing the package by adding to the profile's own
cordis.patch.yml:
- id: models-filter
disabled: true
What you get
- Opening the menu shows the unchanged Model / Effort cells — no filter there.
- Entering Model shows the list pane with the filter row on top,
autofocused; typing filters case-insensitively by model name, id,
description, or provider name/id; emptied groups drop out;
× or Esc
clears and restores the full list in place.
- Combobox keyboard navigation:
ArrowDown/ArrowUp move a highlight
across the visible rows (wrapping at the ends) while focus stays in the
filter input — the highlight rides aria-activedescendant. Enter selects
the highlighted row; Escape cascades clear query → back a pane → close.
Typing resets the highlight to the first visible row.
- Two-tier emphasis: the pre-selected row carries a stronger layered fill
so it reads clearly in both themes; mouse hover keeps the lighter shipped
hover fill and never moves the pre-selection.
- Deterministic scrolling: wrapping to the first row snaps to top;
otherwise the pre-selected row is revealed minimally, always fully below its
sticky group title; already-visible rows cause no scroll.
- Selections flow through the shipped shared per-session
ModelDirectory
(ctx.modelDirectories), so /model, the composer trigger, selection state,
and the composer's model-unavailable block stay exactly in sync.
How it respects the existing design
- The menu renders with the shipped stylesheet's own class names
(
_7KE1Ra_*, content-hash classes from the deployment's
dsh-client-ui-model-selection build). Trigger, Model/Effort cells, provider
group titles, options, selected check, loading/error/warning strips: all are
the product's real markup and real styles. A copy of the shipped stylesheet
is kept in shipped.css for provenance.
- The only new CSS rules are the
dshmf-* classes for the added row and
its highlight state, written from the same design tokens and idioms: 10px
control radius, caption tone placeholder, subtle borders, hover fill, and the
trigger's 2px focus ring. They ship as one deduped
<style data-plugin-css> tag owned by the plugin fiber.
- All menu strings come from the shipped
model locale namespace; the filter
row registers its own tiny models-filter namespace (en + zh).
Package layout
| File | Purpose |
|---|
package.json | The dsh manifest: bundle.patch + client declaration. |
cordis.patch.yml | Bundle patch layer: inserts the discovery host row. |
lib/index.js | Host half (GENERATED from src/index.ts) — a deliberate no-op marker plugin (see below). |
lib/client.js | Browser half (GENERATED from src/client.ts) — committed lazy-CJS bundle served at /plugins/dsh-models-filter/client.js. |
src/client.ts | Readable TypeScript source of the client half: the factory body plus the type declarations the build erases. |
src/index.ts | TypeScript source of the host half. |
scripts/build.mjs | Transpiles src/*.ts into the committed lib/*.js (types erased, comments kept). |
tsconfig.json | Strict tsc --noEmit configuration for pnpm typecheck. |
shipped.css | Extracted shipped stylesheet kept for class-fidelity provenance. |
How it wires into a profile
Three declarations make the package a real bundle rather than a plain
dependency:
"dsh": { "bundle": { "patch": "./cordis.patch.yml" } } —
dsh plugin add reconciles installed dependencies against
dsh.profile.bundles; declaring a patch is what turns this package into a
profile layer instead of printing the "declares no dsh.bundle" warning.
- The patch inserts one loader row (
id: models-filter,
name: dsh-models-filter). The web client-module scan discovers browser
bundles by walking activated loader entries and matching each entry's name
against installed packages declaring dsh.client — without this row the
client half would never join the boot graph. The host half itself is a
no-op marker; all behavior lives client-side over services the web surface
already mounts (slots, locale, modelDirectories, sessions).
"dsh": { "client": { "platform": "web", ... } } + exports["./client"]
— once the entry activates, the scan resolves this manifest and adds the
bundle to the window.__DSH_BOOT__ graph. The vendored client Loader then
imports it like any shipped client plugin: the factory runs once,
returning { inject, apply }; Cordis fiber governance (service waiting,
stop/update disposal) applies unchanged.
- The seat is shadowed at
priority: -1, never replaced. The composer's
model seat is a single slot that admits one registration per priority —
the shipped dsh-client-ui-model-selection seat holds priority 0, so this
bundle registers one priority lower (lower renders) and the shipped entry
stays registered but dormant. Unloading or disabling this plugin disposes
its -1 entry and the shipped seat takes back over with no gap.
Development
Sources are TypeScript; the committed lib/ artifacts are generated from them
by transpilation (type annotations erased, comments and runtime code kept
verbatim), which is why installs never run a build and the package has zero
runtime dependencies.
pnpm install # runtime deps: none — devDependencies (typescript) serve build/typecheck only
pnpm typecheck # tsc --noEmit over src/ (strict)
pnpm build # regenerate lib/client.js + lib/index.js after editing src/*.ts
link:-style installs (add ./dsh-models-filter) symlink the checkout, so a
rebuild plus a profile restart picks edits up immediately. Commit regenerated
lib/ files before publishing.
Known deviation
On a rejected selection the shipped component raises a transient Toast; this
plugin leaves the menu open instead. Load errors keep the shipped inline Retry
strip.
License
MIT
Author
@odelbos (with GLM-5.3-FLASH)