Amazon Wishlist Search
July 31, 2026 · View on GitHub
A userscript that adds a search box to Amazon's Add to List wishlist popover, so you can filter long wishlist menus by typing. Optionally keeps a "Previously selected" group of your most-used lists at the top.

This is the TypeScript source. Builds compile src/ into a single
*.user.js file that Tampermonkey / Violentmonkey / Greasemonkey installs.
Install (users)
Install a userscript manager (Tampermonkey, Violentmonkey, or Greasemonkey), then open:
The manager will offer to install it. After that it auto-updates: the
script's @updateURL/@downloadURL point at that always-latest release link,
so your manager picks up new releases automatically. The raw main branch is
for development and may contain unreleased changes.
Requirements
- Node.js 18+ (built/tested on Node 22)
- pnpm (pinned via the
packageManagerfield — runcorepack enableand pnpm will use the right version automatically) - A userscript manager (Tampermonkey, Violentmonkey, or Greasemonkey)
Setup
pnpm install
Build
pnpm build
This typechecks (tsc --noEmit) and then bundles with
vite-plugin-monkey, emitting:
dist/amazon-wishlist-search.user.js
For local testing, point your userscript manager at that file (drag it in, or
open it directly). The userscript metadata block (@name, @include,
@grant, …) is generated from the userscript section of
vite.config.ts — edit it there, not by hand.
Develop
pnpm dev
Starts Vite's dev server. vite-plugin-monkey serves an auto-installing
development userscript with hot-reload, so saving a .ts file updates the
running script in the browser. Use pnpm typecheck to typecheck without
building.
The dev server runs over HTTPS (via vite-plugin-mkcert). This is required
because HTTPS sites like Amazon block the injected dev script as mixed content
if it's served over HTTP. mkcert installs a locally-trusted CA on first run
(it may prompt for your password once), so the dev entry loads with no
certificate warning. Production builds are unaffected — mkcert only applies to
pnpm dev.
If pnpm dev still shows nothing on the page, you're likely looking at the
dev loader failing to reach the server. The reliable path is always to
pnpm build and install dist/amazon-wishlist-search.user.js directly — that
file is fully self-contained (no dev server) and behaves like any normal
userscript.
Test
pnpm test
Uses Vitest. pnpm test builds first (via the pretest
hook), then runs the suite in test/:
metadata.test.ts— asserts on the builtdist/*.user.js: that@versionmatchespackage.json, that@updateURL/@downloadURLpoint at the latest release asset (and not the rawmainbranch),@grant none, and the@includerules. This guards the auto-update wiring.release.test.ts— a release-integrity check (mirrors unwall): downloads the asset from the latest GitHub release, hashes it, and asserts the release notes publish that SHA-256. It is skipped unless run viapnpm test:release(CHECK_GITHUB_RELEASE=1).
Release & auto-update
Auto-update works through GitHub Releases: managers poll the @updateURL
permalink and update when @version increases. The userscript version comes
from package.json (vite-plugin-monkey reads it there), so there's a single
source of truth. To cut a release:
-
Bump the version and create the tag in one step:
pnpm version patch # or minor / major — updates package.json and tags vX.Y.Z git push --follow-tags -
The
releaseworkflow runs on the tag: it builds, runs tests, verifies the tag matches@version, computes the asset's SHA-256, and publishes a GitHub Release withamazon-wishlist-search.user.jsattached and the checksum in the notes.
Because the asset is attached at the stable
releases/latest/download/amazon-wishlist-search.user.js URL, every installed
user is updated automatically. dist/ is not committed — releases are built by
CI.
Project structure
src/
main.ts Entry point — wires everything up
config.ts CONFIG values and DOM SELECTORS
dom.ts Live-popover DOM helpers
regex.ts Regex parsing / escaping / compiling helpers
regex-state.ts Runtime on/off toggle for regex search (persisted)
frequencies.ts localStorage selection-frequency + blocklist tracking
frequent-section.ts "Previously selected" group + its inline controls
frequent-state.ts Runtime on/off toggle for the group (persisted)
icons.ts Inline SVG icons for the injected UI
result-count.ts "N results" notice element
search.ts Search + debounce + highlight logic
styles.ts Injected stylesheet for the input
inject.ts Builds and inserts the search UI (input + regex toggle)
observer.ts MutationObserver + ESC handling
log.ts Prefixed console logger
types/index.ts Shared interfaces and types
test/
metadata.test.ts Asserts built userscript metadata / auto-update wiring
release.test.ts Release SHA-256 integrity check (opt-in)
.github/workflows/ CI (test) and release automation
Configuration
Behaviour is controlled by CONFIG in src/config.ts:
debounce delay, max results shown, regex mode, and the frequent-lists group.
Every persisted value's localStorage key lives in STORAGE_KEYS in the same
file. When the frequent-lists group is enabled, a window.clearWishlistHistory()
helper is exposed in the console to reset it.
Managing the "Previously selected" group
The group can be managed inline — the controls stay hidden until you hover:
- Hover a row → an ✕ removes just that list from the group; a trash icon also blocks it from ever reappearing there.
- Hover the Previously selected label → an ✕ clears the whole group; a
toggle turns the feature off. While off, the label stays (struck through,
italic) with just the toggle — click it (or run
wishlistSearchFrequent(true)) to turn it back on.
wishlistSearchFrequent(true) // enable the group (persists across refreshes)
wishlistSearchFrequent(false) // disable it
wishlistSearchFrequent() // return the current state, unchanged
Regex search
A small icon inside the right edge of the search box toggles regex mode (persists across refreshes):
Txt(default) — your text is matched literally (case-insensitive).(.*)— your text is treated as a regular expression; a/pattern/flagsform is honoured too, and an incomplete pattern falls back to a literal match while you type.
CONFIG.regexSearches sets the default mode.
Debug logging
CONFIG.debug is the compile-time default, but you can toggle debug logging at
runtime from the browser console — the choice is saved to localStorage and
persists across refreshes (overriding the default):
wishlistSearchDebug(true) // enable verbose logging
wishlistSearchDebug(false) // disable it
wishlistSearchDebug() // print a selector/state snapshot (flag unchanged)
Every call also returns a snapshot of which selectors are currently matching, useful for diagnosing why the input might not appear.