Theseus: reorderable engine list + dropdown-or-type manual fingerprint fields
- Search engines can be reordered (▲/▼ per row); order persists in settings.engineOrder and drives the toolbar dropdown order. - Manual anti-fingerprinting values are now dropdown-or-type (datalist): timezone (common IANA zones), language (common locales), and a city picker for location that fills exact lat/lon — each still accepts free typing.
This commit is contained in:
parent
69c2afca81
commit
e284864d3c
3 changed files with 55 additions and 5 deletions
15
main.js
15
main.js
|
|
@ -51,7 +51,15 @@ function allEngines() {
|
|||
({ id, name: e.name, sym: e.sym, favicon: faviconUrl(e.fav), builtin: true, enabled: isEnabled(id) }));
|
||||
for (const c of settings.customEngines || [])
|
||||
list.push({ id: c.id, name: c.name, sym: c.sym || "🔍", favicon: customFavicon(c.url), builtin: false, enabled: true });
|
||||
return list;
|
||||
// Apply the user's custom order; ids not in engineOrder keep their natural order (stable sort).
|
||||
const order = settings.engineOrder || [];
|
||||
return list.slice().sort((a, b) => {
|
||||
const ia = order.indexOf(a.id), ib = order.indexOf(b.id);
|
||||
if (ia === -1 && ib === -1) return 0;
|
||||
if (ia === -1) return 1;
|
||||
if (ib === -1) return -1;
|
||||
return ia - ib;
|
||||
});
|
||||
}
|
||||
function enabledEnginesList() { return allEngines().filter((e) => e.enabled); }
|
||||
function engineUrl(id, q) {
|
||||
|
|
@ -124,6 +132,7 @@ const SETTINGS_DEFAULTS = {
|
|||
locationMode: "hide", locationRegion: "europe", locationLat: "40.7128", locationLon: "-74.0060", // spoof by region, or manual coords
|
||||
searchEngine: "duckduckgo",// default search engine (built-in id or a custom id)
|
||||
enabledEngines: DEFAULT_ENABLED.slice(), // which built-in engines show in the dropdown
|
||||
engineOrder: [], // user-defined display order of engine ids (empty = natural)
|
||||
customEngines: [], // user-added: [{ id, name, url-with-%s }]
|
||||
theme: "dark", // dark | light | system — drives prefers-color-scheme in all views
|
||||
};
|
||||
|
|
@ -801,6 +810,10 @@ ipcMain.handle("set-engine-enabled", (_e, id, on) => {
|
|||
}
|
||||
return { engines: allEngines(), current: settings.searchEngine };
|
||||
});
|
||||
ipcMain.handle("set-engine-order", (_e, ids) => {
|
||||
if (Array.isArray(ids)) { settings.engineOrder = ids.filter((x) => typeof x === "string"); saveSettings(); emitEngines(); }
|
||||
return { engines: allEngines(), current: settings.searchEngine };
|
||||
});
|
||||
// The custom dropdown overlay (real favicons).
|
||||
ipcMain.handle("toggle-engine-picker", (_e, rect) => {
|
||||
if (epVisible) return showEnginePicker(false);
|
||||
|
|
|
|||
|
|
@ -6,4 +6,5 @@ contextBridge.exposeInMainWorld("cfg", {
|
|||
addEngine: (eng) => ipcRenderer.invoke("add-engine", eng),
|
||||
removeEngine: (id) => ipcRenderer.invoke("remove-engine", id),
|
||||
setEngineEnabled: (id, on) => ipcRenderer.invoke("set-engine-enabled", id, on),
|
||||
setEngineOrder: (ids) => ipcRenderer.invoke("set-engine-order", ids),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@
|
|||
.eng .eic .ei{width:16px;height:16px;border-radius:3px} .eng .eic .es{font-size:14px}
|
||||
.eng .enm{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.eng .cx{cursor:pointer;color:var(--dim);border:none;background:transparent;font-size:13px} .eng .cx:hover{color:#f6768a}
|
||||
.eng .mv{display:flex;flex-direction:column;flex:none;gap:1px}
|
||||
.eng .mv button{border:none;background:transparent;color:var(--dim);cursor:pointer;font-size:8px;line-height:1;padding:1px 2px;border-radius:3px}
|
||||
.eng .mv button:hover{background:var(--line);color:var(--ink)}
|
||||
.sw.sm{width:38px;height:22px} .sw.sm .knob{width:15px;height:15px} .sw.sm input:checked + .track .knob{transform:translateX(16px)}
|
||||
.ceng .cn{font-weight:600} .ceng .cu{color:var(--dim);font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}
|
||||
.ceng .cx{cursor:pointer;color:#8b98a9;border:none;background:transparent;font-size:13px} .ceng .cx:hover{color:#f6768a}
|
||||
|
|
@ -145,7 +148,12 @@
|
|||
<div class="txt"><div class="t">Timezone</div><div class="d">What sites read via JavaScript (Intl / Date).</div></div>
|
||||
<div class="ctl">
|
||||
<select id="timezoneMode"><option value="show">Show real</option><option value="hide">Hide (UTC)</option><option value="spoof">Spoof (auto)</option><option value="manual">Manual…</option></select>
|
||||
<input id="timezoneValue" placeholder="e.g. Europe/Berlin" hidden>
|
||||
<input id="timezoneValue" list="tzList" placeholder="pick or type e.g. Europe/Berlin" hidden>
|
||||
<datalist id="tzList">
|
||||
<option value="UTC"><option value="Europe/London"><option value="Europe/Berlin"><option value="Europe/Paris"><option value="Europe/Moscow">
|
||||
<option value="America/New_York"><option value="America/Chicago"><option value="America/Los_Angeles"><option value="America/Sao_Paulo">
|
||||
<option value="Asia/Tokyo"><option value="Asia/Shanghai"><option value="Asia/Kolkata"><option value="Asia/Dubai"><option value="Australia/Sydney"><option value="Africa/Nairobi">
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
|
@ -164,7 +172,11 @@
|
|||
<option value="de-DE">German (Deutsch)</option>
|
||||
<option value="ja-JP">Japanese (日本語)</option>
|
||||
</select>
|
||||
<input id="languageValue" placeholder="e.g. it-IT" hidden>
|
||||
<input id="languageValue" list="langList" placeholder="pick or type e.g. it-IT" hidden>
|
||||
<datalist id="langList">
|
||||
<option value="en-US"><option value="en-GB"><option value="zh-CN"><option value="es-ES"><option value="hi-IN"><option value="ar">
|
||||
<option value="pt-BR"><option value="ru-RU"><option value="fr-FR"><option value="de-DE"><option value="ja-JP"><option value="it-IT"><option value="ko-KR"><option value="nl-NL"><option value="tr-TR">
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
|
@ -181,8 +193,13 @@
|
|||
<option value="australia">Australia</option>
|
||||
</select>
|
||||
<div class="coords" id="locationCoords" hidden>
|
||||
<input id="locationCity" list="cityList" placeholder="pick a city…" style="width:112px">
|
||||
<input id="locationLat" placeholder="lat"><input id="locationLon" placeholder="lon">
|
||||
</div>
|
||||
<datalist id="cityList">
|
||||
<option value="London"><option value="Berlin"><option value="Paris"><option value="Moscow"><option value="New York"><option value="Los Angeles">
|
||||
<option value="São Paulo"><option value="Tokyo"><option value="Shanghai"><option value="Mumbai"><option value="Dubai"><option value="Sydney"><option value="Nairobi"><option value="Singapore">
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note">These reduce tracking and hide your IP, but a custom browser can still be fingerprinted. For maximum anonymity, use the Tor Browser.</div>
|
||||
|
|
@ -216,14 +233,26 @@
|
|||
const enabled = d.engines.filter((e) => e.enabled);
|
||||
sel.innerHTML = enabled.map((e) => `<option value="${e.id}">${(e.sym ? e.sym + " " : "")}${esc(e.name)}</option>`).join("");
|
||||
sel.value = d.current;
|
||||
// unified checklist: built-in engines get an enable toggle; custom get a remove ✕
|
||||
// unified checklist: drag handle / up-down to reorder; built-in get an
|
||||
// enable toggle, custom get a remove ✕.
|
||||
const list = document.getElementById("engineList");
|
||||
list.innerHTML = d.engines.map((e) => `<div class="eng"><span class="eic">${engIcon(e)}</span><span class="enm">${esc(e.name)}</span>` +
|
||||
list.innerHTML = d.engines.map((e) => `<div class="eng" data-id="${e.id}">` +
|
||||
`<span class="mv"><button class="up" title="Move up">▲</button><button class="dn" title="Move down">▼</button></span>` +
|
||||
`<span class="eic">${engIcon(e)}</span><span class="enm">${esc(e.name)}</span>` +
|
||||
(e.builtin
|
||||
? `<label class="sw sm"><input type="checkbox" data-id="${e.id}" ${e.enabled ? "checked" : ""}><span class="track"><span class="knob"></span></span></label>`
|
||||
: `<button class="cx" data-id="${e.id}" title="Remove">✕</button>`) + `</div>`).join("");
|
||||
list.querySelectorAll('input[type="checkbox"]').forEach((cb) => cb.onchange = () => C.setEngineEnabled(cb.dataset.id, cb.checked).then(renderEngines));
|
||||
list.querySelectorAll(".cx").forEach((b) => b.onclick = () => C.removeEngine(b.dataset.id).then(renderEngines));
|
||||
const move = (id, dir) => {
|
||||
const ids = [...list.querySelectorAll(".eng")].map((el) => el.dataset.id);
|
||||
const i = ids.indexOf(id), j = dir === "up" ? i - 1 : i + 1;
|
||||
if (j < 0 || j >= ids.length) return;
|
||||
[ids[i], ids[j]] = [ids[j], ids[i]];
|
||||
C.setEngineOrder(ids).then(renderEngines);
|
||||
};
|
||||
list.querySelectorAll(".up").forEach((b) => b.onclick = () => move(b.closest(".eng").dataset.id, "up"));
|
||||
list.querySelectorAll(".dn").forEach((b) => b.onclick = () => move(b.closest(".eng").dataset.id, "down"));
|
||||
}
|
||||
sel.onchange = () => C.set("searchEngine", sel.value);
|
||||
// appearance (theme)
|
||||
|
|
@ -275,6 +304,13 @@
|
|||
locMode.value = s.locationMode || "show";
|
||||
applyLoc();
|
||||
locMode.addEventListener("change", () => { C.set("locationMode", locMode.value); applyLoc(); });
|
||||
// picking a city fills the exact lat/lon fields
|
||||
const CITY = { "London":[51.5074,-0.1278],"Berlin":[52.52,13.405],"Paris":[48.8566,2.3522],"Moscow":[55.7558,37.6173],"New York":[40.7128,-74.006],"Los Angeles":[34.0522,-118.2437],"São Paulo":[-23.5505,-46.6333],"Tokyo":[35.6762,139.6503],"Shanghai":[31.2304,121.4737],"Mumbai":[19.076,72.8777],"Dubai":[25.2048,55.2708],"Sydney":[-33.8688,151.2093],"Nairobi":[-1.2921,36.8219],"Singapore":[1.3521,103.8198] };
|
||||
document.getElementById("locationCity").addEventListener("change", (e) => {
|
||||
const c = CITY[e.target.value.trim()]; if (!c) return;
|
||||
const la = document.getElementById("locationLat"), lo = document.getElementById("locationLon");
|
||||
la.value = c[0]; lo.value = c[1]; C.set("locationLat", String(c[0])); C.set("locationLon", String(c[1]));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue