From 668914354fdb91601f1e45b9017cddbe1c4c66d8 Mon Sep 17 00:00:00 2001 From: Local Dev Date: Mon, 28 Sep 2026 20:07:51 +0200 Subject: [PATCH] fix(theseus): the default-engine control in Settings shows the engine icons It was a native is text-only) */ + .esel{display:flex;align-items:center;gap:8px;background:#1b2330;color:var(--ink);border:1px solid var(--line);border-radius:8px;padding:6px 10px;font-size:13px;min-width:180px;cursor:pointer;user-select:none;outline:none} + .esel:focus-visible,.esel.open{border-color:#4b7bec} + .esel .eic,.eselmenu .eic{display:inline-flex;width:16px;height:16px;align-items:center;justify-content:center;flex:none} + .esel .eic .ei,.eselmenu .eic .ei{width:16px;height:16px;border-radius:3px} .esel .eic .es,.eselmenu .eic .es{font-size:14px;line-height:1} + .esel .enm{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} + .esel .caret{color:var(--mut);font-size:13px;line-height:1;flex:none} + .ctxmenu.eselmenu{min-width:200px;max-height:60vh;overflow-y:auto;padding:4px} + .ctxmenu.eselmenu .ehdr{margin:6px 8px 2px;padding:0} .ctxmenu.eselmenu .ehdr:first-child{margin-top:2px} + .ctxmenu.eselmenu .mi{display:flex;align-items:center;gap:8px;padding:6px 10px} + .ctxmenu.eselmenu .mi.cur{color:var(--acid-text)} .ctxmenu.eselmenu .mi.hl{background:#ffffff10} + .ctxmenu.eselmenu .mi .chk{margin-left:auto;font-size:12px} + @media (prefers-color-scheme: light){ .esel{background:#f1f5f8;color:#253A49} .ctxmenu.eselmenu .mi.hl{background:rgba(0,0,0,.05)} } /* right-click context menu for an engine row */ .ctxmenu{position:fixed;z-index:9999;background:#1c222c;border:1px solid var(--line);border-radius:8px; box-shadow:0 12px 34px #000c;padding:4px;min-width:180px;font-size:13px;color:var(--ink)} @@ -341,7 +354,7 @@

Pick what your address bar and the toolbar dropdown search with.

Default search engine
Used when you type into the address bar.
-
+
@@ -892,16 +905,18 @@ { key: "search", label: "Search engines" }, { key: "llm", label: "AI answer engines" }, ]; + // Default engine: our own dropdown over the ENABLED engines (the ones in the + // toolbar picker), grouped like it, each row with the engine's icon. + let selEngines = [], selCurrent = ""; + function paintDefaultEngine() { + const cur = selEngines.find((e) => e.id === selCurrent) || selEngines[0]; + sel.querySelector(".eic").innerHTML = cur ? engIcon(cur) : ""; + sel.querySelector(".enm").textContent = cur ? cur.name : ""; + } function renderEngines(d) { - // default engine: pick from the ENABLED engines (those shown in the dropdown) const enabled = d.engines.filter((e) => e.enabled); - // group the dropdown too, so search and LLM don't intermix - sel.innerHTML = ENGINE_KINDS.map(({ key, label }) => { - const opts = enabled.filter((e) => (e.kind || "search") === key) - .map((e) => ``).join(""); - return opts ? `${opts}` : ""; - }).join(""); - sel.value = d.current; + selEngines = enabled; selCurrent = d.current; + paintDefaultEngine(); // Main list = engines the user has INSTALLED. The toggle only flips // enabled/disabled — the row STAYS. Right-click a row → "Remove from // list" is what actually removes an engine (back to the catalog for @@ -1018,7 +1033,48 @@ document.addEventListener("keydown", esc); }, 0); } - sel.onchange = () => C.set("searchEngine", sel.value); + let selMenu = null; + function closeDefaultEngineMenu() { if (selMenu) { selMenu.remove(); selMenu = null; sel.classList.remove("open"); sel.setAttribute("aria-expanded", "false"); } } + function openDefaultEngineMenu() { + closeEngineMenu(); closeDefaultEngineMenu(); + const m = document.createElement("div"); + m.className = "ctxmenu eselmenu"; m.setAttribute("role", "listbox"); + m.innerHTML = ENGINE_KINDS.map(({ key, label }) => { + const rows = selEngines.filter((e) => (e.kind || "search") === key) + .map((e) => `
${engIcon(e)}${esc(e.name)}${e.id === selCurrent ? '✓' : ""}
`).join(""); + return rows ? `
${label}
${rows}` : ""; + }).join(""); + document.body.appendChild(m); + const r = sel.getBoundingClientRect(), mr = m.getBoundingClientRect(); + const vw = document.documentElement.clientWidth, vh = document.documentElement.clientHeight; + m.style.minWidth = r.width + "px"; + m.style.left = Math.max(6, Math.min(r.left, vw - mr.width - 6)) + "px"; + m.style.top = (r.bottom + 4 + mr.height > vh - 6 ? Math.max(6, r.top - 4 - mr.height) : r.bottom + 4) + "px"; + const items = [...m.querySelectorAll(".mi")]; + let hl = Math.max(0, items.findIndex((i) => i.dataset.id === selCurrent)); + const paintHl = () => { items.forEach((i, k) => i.classList.toggle("hl", k === hl)); items[hl]?.scrollIntoView({ block: "nearest" }); }; + const choose = (id) => { closeDefaultEngineMenu(); if (id && id !== selCurrent) { selCurrent = id; paintDefaultEngine(); C.set("searchEngine", id); } sel.focus(); }; + items.forEach((i, k) => { i.onmouseenter = () => { hl = k; paintHl(); }; i.onclick = () => choose(i.dataset.id); }); + paintHl(); + const key = (ev) => { + if (ev.key === "Escape") { ev.preventDefault(); closeDefaultEngineMenu(); sel.focus(); } + else if (ev.key === "ArrowDown") { ev.preventDefault(); hl = Math.min(items.length - 1, hl + 1); paintHl(); } + else if (ev.key === "ArrowUp") { ev.preventDefault(); hl = Math.max(0, hl - 1); paintHl(); } + else if (ev.key === "Enter" || ev.key === " ") { ev.preventDefault(); choose(items[hl]?.dataset.id); } + else if (ev.key === "Tab") closeDefaultEngineMenu(); + }; + const off = (ev) => { if (!m.contains(ev.target) && ev.target !== sel && !sel.contains(ev.target)) closeDefaultEngineMenu(); }; + const cleanup = () => { document.removeEventListener("mousedown", off); document.removeEventListener("keydown", key, true); window.removeEventListener("blur", cleanup); }; + document.addEventListener("mousedown", off); + document.addEventListener("keydown", key, true); + window.addEventListener("blur", closeDefaultEngineMenu, { once: true }); + const origRemove = m.remove.bind(m); m.remove = () => { cleanup(); origRemove(); }; + selMenu = m; sel.classList.add("open"); sel.setAttribute("aria-expanded", "true"); + } + sel.onclick = () => (selMenu ? closeDefaultEngineMenu() : openDefaultEngineMenu()); + sel.onkeydown = (ev) => { if (selMenu) return; if (ev.key === "Enter" || ev.key === " " || ev.key === "ArrowDown" || ev.key === "ArrowUp") { ev.preventDefault(); openDefaultEngineMenu(); } }; + // The toolbar picker changes the same setting; keep the control in step. + if (C.onSettingsUpdate) C.onSettingsUpdate((next) => { if (next && typeof next.searchEngine === "string" && next.searchEngine !== selCurrent) { selCurrent = next.searchEngine; paintDefaultEngine(); } }); // appearance (theme) — three visual cards: system | light | dark. Any // unrecognised saved value falls back to "system" (follow the OS). const th = document.getElementById("theme");