49c3bcb72e5c

entry URL editor: + add current site button uses active tab URL, fills primary if empty else appends to additional URLs, dedupes (v2.16.4)

time
2026-05-01 03:27 UTC · 5 months ago
machine
koh2
parent
17914c240bb4
5 files manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/popup/App.tsx+7 −0src/popup/views/Entry.tsx+38 −0
manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach",4-  "version": "2.16.3",4+  "version": "2.16.4",55   "description": "Zero-knowledge password manager",66   "browser_specific_settings": {77     "gecko": {

view file

manifest.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach",4-  "version": "2.16.3",4+  "version": "2.16.4",55   "description": "Zero-knowledge password manager",66   "action": {77     "default_popup": "src/popup/index.html",

view file

package.json+1 −1
@@ -1,6 +1,6 @@11 {22   "name": "peach",3-  "version": "2.16.3",3+  "version": "2.16.4",44   "description": "Zero-knowledge password manager",55   "type": "module",66   "scripts": {

view file

src/popup/App.tsx+7 −0
@@ -148,6 +148,7 @@148148   const [restoreView, setRestoreView] = createSignal<"file" | null>(null);149149   const [pendingPasskey, setPendingPasskey] = createSignal<PendingPasskeyRequest | null>(null);150150   const [currentTabDomain, setCurrentTabDomain] = createSignal<string>("");151+  const [currentTabUrl, setCurrentTabUrl] = createSignal<string>("");151152   // Cached Pro status — synchronous for render paths. Background's152153   // GET_LICENSE is async, but render-time reads of isPro() must not block.153154   // Refreshed at boot, after activate/deactivate, and whenever the popup@@ -690,8 +691,13 @@690691       const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });691692       const url = tab?.url ?? tab?.pendingUrl ?? "";692693       setCurrentTabDomain(url ? (extractDomain(url) ?? "") : "");694+      // Skip extension/internal URLs — adding chrome://, about:, or695+      // chrome-extension:// to a vault entry is never what the user wants.696+      const isWebUrl = /^https?:\/\//i.test(url);697+      setCurrentTabUrl(isWebUrl ? url : "");693698     } catch {694699       setCurrentTabDomain("");700+      setCurrentTabUrl("");695701     }696702   }697703 @@ -1221,6 +1227,7 @@12211227               <div class="peach-content-frame" data-content-phase={contentPhase()} classList={{ navigating: contentTransitioning() }}>12221228                 <EntryView12231229                   entry={selectedEntry()}1230+                  currentTabUrl={currentTabUrl()}12241231                   onDelete={handleDelete}12251232                   onFieldSaved={() => void loadEntries()}12261233                   onEscape={() => { setView("list"); }}

view file

src/popup/views/Entry.tsx+38 −0
@@ -14,6 +14,12 @@1414 1515 interface EntryProps {1616   entry: VaultEntry | null;17+  /**18+   * Current active tab's full URL (https only). Empty string when unavailable19+   * — Capacitor, an extension page, or a chrome:///about: URL. The URL20+   * editor uses this to render an "add current site" shortcut.21+   */22+  currentTabUrl?: string;1723   onDelete: () => void;1824   /** Called after any inline save so App can reload entries */1925   onFieldSaved: () => void | Promise<void>;@@ -157,6 +163,26 @@157163     }158164   }159165 166+  // Compare URLs by string after trim — vault.ts has its own canonicalisation167+  // when saving, so the dedupe here is a UX-level "don't show the button if168+  // we'd just add a duplicate" rather than a security check.169+  function urlMatchesAny(candidate: string, primary: string, others: string[]): boolean {170+    const c = candidate.trim();171+    if (!c) return true;172+    if (primary.trim() === c) return true;173+    return others.some((u) => u.trim() === c);174+  }175+176+  function addCurrentSite(url: string) {177+    if (!url) return;178+    if (!draft().trim()) {179+      setDraft(url);180+      return;181+    }182+    if (urlMatchesAny(url, draft(), additionalUrlsDraft())) return;183+    setAdditionalUrlsDraft((arr) => [...arr, url]);184+  }185+160186   async function saveField() {161187     const field = editingField();162188     const entry = props.entry;@@ -563,6 +589,18 @@563589                         >564590                           + add another url565591                         </button>592+                        <Show when={props.currentTabUrl && !urlMatchesAny(props.currentTabUrl!, draft(), additionalUrlsDraft())}>593+                          <button594+                            class="peach-add-field-link peach-additional-urls-add peach-additional-urls-add-current"595+                            type="button"596+                            title={props.currentTabUrl}597+                            onMouseDown={(e) => e.preventDefault()}598+                            onClick={() => addCurrentSite(props.currentTabUrl!)}599+                            disabled={saving()}600+                          >601+                            + add current site602+                          </button>603+                        </Show>566604                       </div>567605                     </div>568606                   </Show>

view file