aa7bb5ef2536
feat: add type-to-search escape hatch from inline popup silver platter (v3.6.99)
- time
- 2026-05-27 16:03 UTC · 5 months ago
- machine
- koh2
- parent
- 827c7e4a4890
↓ peach-browser-aa7bb5ef.tar.gzatlas at this save
files changed5 files
manifest.firefox.json+1 −1manifest.json+1 −1package-lock.json+2 −2package.json+1 −1src/content/inject.ts+60 −5manifest.firefox.json+1 −1
@@ -2,7 +2,7 @@22 "_comment_audio_capture": "Firefox does not honor the Chrome 'audioCapture' optional_permission — getUserMedia in WebExtensions is gated solely by the runtime permission prompt. Microphone access for chirp pairing works from the popup under the existing '<all_urls>' host permission; no manifest entry is required or accepted here.",33 "manifest_version": 3,44 "name": "Peach",5- "version": "3.6.95",5+ "version": "3.6.99",66 "description": "Zero-knowledge password manager",77 "browser_specific_settings": {88 "gecko": {manifest.json+1 −1
@@ -1,7 +1,7 @@11 {22 "manifest_version": 3,33 "name": "Peach",4- "version": "3.6.95",4+ "version": "3.6.99",55 "description": "Zero-knowledge password manager",66 "action": {77 "default_popup": "src/popup/index.html",package-lock.json+2 −2
@@ -1,12 +1,12 @@11 {22 "name": "peach",3- "version": "3.6.98",3+ "version": "3.6.99",44 "lockfileVersion": 3,55 "requires": true,66 "packages": {77 "": {88 "name": "peach",9- "version": "3.6.98",9+ "version": "3.6.99",1010 "dependencies": {1111 "@bokuweb/zstd-wasm": "^0.0.27",1212 "@stablelib/blake2s": "^2.0.1",package.json+1 −1
@@ -1,6 +1,6 @@11 {22 "name": "peach",3- "version": "3.6.98",3+ "version": "3.6.99",44 "description": "Zero-knowledge password manager",55 "type": "module",66 "scripts": {src/content/inject.ts+60 −5
@@ -91,6 +91,10 @@9191 return `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"><path d="M6 1.5v9M1.5 6h9"/></svg>`;9292 }9393 94+function searchSvgStr(): string {95+ return `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"><circle cx="5.25" cy="5.25" r="3.5"/><path d="M7.9 7.9l2.35 2.35"/></svg>`;96+}97+9498 function peachIconSvg(): string {9599 // Peach body stays brand-peach regardless of `accentHex` — a sage or96100 // sky accent must not turn the autofill icon green or blue. Accent@@ -585,8 +589,13 @@585589 font-family: "IBM Plex Mono", monospace;586590 white-space: nowrap; overflow: hidden; text-overflow: ellipsis;587591 }588- .domain-add-btn {592+ .domain-header-actions {593+ display: inline-flex; align-items: center; gap: 5px;589594 flex-shrink: 0;595+ }596+ .domain-add-btn,597+ .domain-search-btn {598+ flex-shrink: 0;590599 width: 22px; height: 22px;591600 display: inline-flex; align-items: center; justify-content: center;592601 background: color-mix(in srgb, var(--peach-accent) 12%, transparent);@@ -598,12 +607,14 @@598607 font-family: "IBM Plex Mono", monospace;599608 font-size: 14px; line-height: 1;600609 }601- .domain-add-btn:hover {610+ .domain-add-btn:hover,611+ .domain-search-btn:hover {602612 background: color-mix(in srgb, var(--peach-accent) 22%, transparent);603613 border-color: color-mix(in srgb, var(--peach-accent) 48%, transparent);604614 transform: scale(1.06);605615 }606- .domain-add-btn:active { transform: scale(0.96); }616+ .domain-add-btn:active,617+ .domain-search-btn:active { transform: scale(0.96); }607618 608619 /* Search */609620 .search-wrap {@@ -1548,6 +1559,7 @@15481559 container.innerHTML = "";15491560 focusedIndex = -1;15501561 searchInput = null;1562+ currentMode = "list";15511563 15521564 const current = activeEntries();15531565 @@ -1671,7 +1683,12 @@16711683 16721684 // Focus search if present16731685 if (searchInput) {1674- requestAnimationFrame(() => searchInput?.focus());1686+ requestAnimationFrame(() => {1687+ searchInput?.focus();1688+ // Seed query (from type-to-search or domain-search-btn) lands at the1689+ // end so the next keystroke extends it, not overwrites.1690+ searchInput?.setSelectionRange(query.length, query.length);1691+ });16751692 }16761693 }16771694 @@ -1971,6 +1988,25 @@19711988 headText.append(eyebrow, domName);19721989 domHdr.appendChild(headText);19731990 if (!options.isSignup) {1991+ const actions = document.createElement("div");1992+ actions.className = "domain-header-actions";1993+1994+ // Search shortcut — same idea as type-to-search below, but visible so1995+ // the user can discover that another entry is reachable from a1996+ // single-match platter. Sits to the left of the create button.1997+ const searchBtn = document.createElement("button");1998+ searchBtn.className = "domain-search-btn";1999+ searchBtn.type = "button";2000+ searchBtn.setAttribute("aria-label", "Search for a different entry");2001+ searchBtn.title = "Search for a different entry";2002+ searchBtn.innerHTML = searchSvgStr();2003+ searchBtn.addEventListener("click", (e) => {2004+ e.stopPropagation();2005+ query = "";2006+ renderList();2007+ });2008+ actions.appendChild(searchBtn);2009+19742010 const addBtn = document.createElement("button");19752011 addBtn.className = "domain-add-btn";19762012 addBtn.type = "button";@@ -1978,7 +2014,9 @@19782014 addBtn.title = "Create new entry";19792015 addBtn.textContent = "+";19802016 addBtn.addEventListener("click", (e) => { e.stopPropagation(); void showCreateForm(); });1981- domHdr.appendChild(addBtn);2017+ actions.appendChild(addBtn);2018+2019+ domHdr.appendChild(actions);19822020 }19832021 container.appendChild(domHdr);19842022 @@ -2596,6 +2634,23 @@25962634 }25972635 25982636 document.addEventListener("keydown", handleKeyDown);2637+2638+ // Type-to-search from the silver platter. A printable single-character2639+ // keystroke (no modifier) seeds the search query and switches into the2640+ // list view, so the user can pivot to a different entry without first2641+ // clicking the search icon. Capture phase so the anchor input does not2642+ // receive the character before we hijack it.2643+ const silverPlatterTypeHandler = (e: KeyboardEvent) => {2644+ if (currentMode !== "silver-platter") return;2645+ if (e.ctrlKey || e.metaKey || e.altKey) return;2646+ if (e.key.length !== 1) return;2647+ e.preventDefault();2648+ e.stopImmediatePropagation();2649+ query = e.key;2650+ renderList();2651+ };2652+ document.addEventListener("keydown", silverPlatterTypeHandler, true);2653+ cleanupHooks.push(() => document.removeEventListener("keydown", silverPlatterTypeHandler, true));25992654 26002655 document.body.appendChild(host);26012656