Revert Theseus 0.3.32 through 0.3.34 — back to 0.3.31
Bundled revert of six commits: 9bacdcb Ship 0.3.34 aef7864 0.3.34 UI (grey chrome, DnD bookmarks, chip colors) c2da8bb Ship 0.3.33 9b92224 fix(updater) verify SHA-256 4e932e0 Ship 0.3.32 4e695c4 fix(updater) --updated flag Reason: the real-install E2E test (turn N-1) showed silent update against the user's custom D:\Program Files\Theseus Navigator location returns exit 0 without upgrading anything — every flag combination tried (/S alone, --updated /S --force-run, /S /currentuser, /S /D=<install>) exits successfully and no files change. The 0.3.32 flag fix and 0.3.33 hash-verify fix were defensive improvements but did not address the actual "browser vanished" symptom, and 0.3.34 inherits the same broken silent-update path so its UI improvements never reach anyone still on 0.3.33+ with a non-default install path. Rolling manifest back to 0.3.31 so the update chip stops appearing on installs that can't complete the update. Users on the default %LOCALAPPDATA%\Programs\theseus-navigator location are unaffected — 0.3.31 is still their canonical shipped version. package.json reset to 0.3.31 explicitly (git revert stopped one bump short on the merged diff). Screenshot 0.3.0 (8ca5ac3) preserved — unrelated parallel work.
This commit is contained in:
parent
fa65ce5bab
commit
347e63de0c
4 changed files with 30 additions and 144 deletions
84
chrome.html
84
chrome.html
|
|
@ -13,12 +13,7 @@
|
||||||
}
|
}
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
:root {
|
:root {
|
||||||
/* Chrome strip is a soft neutral grey — flat white was too bright next
|
--bg:#e9ecf1; --surface:#ffffff; --surface2:#f1f3f7; --active:#dbe1ec;
|
||||||
to the browsed page, and pure #ffffff tabs disappeared into the
|
|
||||||
window title bar on light Windows themes. --bg tints the whole
|
|
||||||
top strip; --surface (inactive tab) sits a hair above it; --active
|
|
||||||
(foreground tab) pops a bit brighter so the current tab reads. */
|
|
||||||
--bg:#e6e8ec; --surface:#f2f4f7; --surface2:#eaecf0; --active:#ffffff;
|
|
||||||
--ink:#1a1f28; --mut:#3c4453; --dim:#697280; --faint:#98a1b0;
|
--ink:#1a1f28; --mut:#3c4453; --dim:#697280; --faint:#98a1b0;
|
||||||
--line:rgba(0,0,0,.18); --line2:rgba(0,0,0,.11); --hover:rgba(0,0,0,.06);
|
--line:rgba(0,0,0,.18); --line2:rgba(0,0,0,.11); --hover:rgba(0,0,0,.06);
|
||||||
/* Darker acid for light backgrounds — same hue family, ~5.5:1
|
/* Darker acid for light backgrounds — same hue family, ~5.5:1
|
||||||
|
|
@ -298,10 +293,8 @@
|
||||||
padding: 6px 12px; border: 1px solid color-mix(in srgb, var(--acid) 20%, transparent); border-radius: 8px; cursor: pointer; background: rgb(from var(--acid) r g b / .08); font-size: 13px; }
|
padding: 6px 12px; border: 1px solid color-mix(in srgb, var(--acid) 20%, transparent); border-radius: 8px; cursor: pointer; background: rgb(from var(--acid) r g b / .08); font-size: 13px; }
|
||||||
.logo:hover { background: rgb(from var(--acid) r g b / .16); }
|
.logo:hover { background: rgb(from var(--acid) r g b / .16); }
|
||||||
.logo .gear { font-size: 12px; opacity: .8; }
|
.logo .gear { font-size: 12px; opacity: .8; }
|
||||||
/* favorites bar — new-tab page only, single compact row. Chips are
|
/* favorites bar — new-tab page only, single compact row */
|
||||||
tight so many fit in the row; the whole strip is 22px tall (down
|
.bookmarks { display: flex; align-items: center; gap: 4px; padding: 3px 10px 5px; height: 26px; overflow: hidden; }
|
||||||
from 26) so the chrome view gains a bit of vertical space. */
|
|
||||||
.bookmarks { display: flex; align-items: center; gap: 3px; padding: 2px 10px 4px; height: 22px; overflow: hidden; }
|
|
||||||
/* Ctrl+F find-in-page bar. Sits below the address bar (above tabs body),
|
/* Ctrl+F find-in-page bar. Sits below the address bar (above tabs body),
|
||||||
~32px tall so it's part of the chrome view height like .tordisc / .bcnrbar.
|
~32px tall so it's part of the chrome view height like .tordisc / .bcnrbar.
|
||||||
syncHeight in the tabs handler picks it up automatically. */
|
syncHeight in the tabs handler picks it up automatically. */
|
||||||
|
|
@ -314,14 +307,11 @@
|
||||||
.findbar .findbtn { border: 1px solid var(--line); background: var(--surface2); color: var(--ink); border-radius: 6px;
|
.findbar .findbtn { border: 1px solid var(--line); background: var(--surface2); color: var(--ink); border-radius: 6px;
|
||||||
padding: 3px 8px; font-size: 13px; cursor: pointer; }
|
padding: 3px 8px; font-size: 13px; cursor: pointer; }
|
||||||
.findbar .findbtn:hover { background: var(--surface); border-color: color-mix(in srgb, var(--acid) 35%, transparent); }
|
.findbar .findbtn:hover { background: var(--surface); border-color: color-mix(in srgb, var(--acid) 35%, transparent); }
|
||||||
.bm { display: flex; align-items: center; gap: 5px; max-width: 130px; padding: 2px 7px; border-radius: 5px;
|
.bm { display: flex; align-items: center; gap: 6px; max-width: 180px; padding: 3px 9px; border-radius: 6px;
|
||||||
background: var(--surface2); border: 1px solid var(--hover); color: var(--mut); font-size: 11px; cursor: grab; white-space: nowrap; }
|
background: var(--surface2); border: 1px solid var(--hover); color: var(--mut); font-size: 12px; cursor: pointer; white-space: nowrap; }
|
||||||
.bm:hover { background: var(--active); color: var(--ink); }
|
.bm:hover { background: var(--active); color: var(--ink); }
|
||||||
.bm.dragging { opacity: .45; cursor: grabbing; }
|
.bm .bmfav { width: 14px; height: 14px; flex: none; object-fit: contain; border-radius: 2px; }
|
||||||
.bm.dropbefore { box-shadow: -2px 0 0 var(--acid); }
|
.bm .bt { overflow: hidden; text-overflow: ellipsis; }
|
||||||
.bm.dropafter { box-shadow: 2px 0 0 var(--acid); }
|
|
||||||
.bm .bmfav { width: 12px; height: 12px; flex: none; object-fit: contain; border-radius: 2px; }
|
|
||||||
.bm .bt { overflow: hidden; text-overflow: ellipsis; max-width: 96px; }
|
|
||||||
/* In-chrome prompt modal (window.prompt is disabled in Electron BrowserViews,
|
/* In-chrome prompt modal (window.prompt is disabled in Electron BrowserViews,
|
||||||
so bookmark rename etc. use this instead). */
|
so bookmark rename etc. use this instead). */
|
||||||
.promptmask { position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 10000;
|
.promptmask { position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 10000;
|
||||||
|
|
@ -354,21 +344,15 @@
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
.ctxmenu { background: #ffffff; border-color: rgba(0,0,0,.15); }
|
.ctxmenu { background: #ffffff; border-color: rgba(0,0,0,.15); }
|
||||||
.ctxmenu .mi:hover { background: rgba(0,0,0,.05); }
|
.ctxmenu .mi:hover { background: rgba(0,0,0,.05); }
|
||||||
/* The old light-mode chips used the BCH dark #253A49 as their fill.
|
/* BCH dark #253A49 as a light-mode accent so the toolbar isn't
|
||||||
At small font sizes on light Windows the white-on-dark-navy label
|
all-white-on-white. .logo (⛓ Theseus) becomes a dark chip; the
|
||||||
fell below legible, so both .logo (⛓ Theseus) and .tor switch to
|
download / update pill picks it up so both stand out. */
|
||||||
a light-grey chip with dark ink — same footprint, readable text.
|
.logo { background: #253A49; color: #F8FDFF; border-color: #253A49; }
|
||||||
Update pill picks up the same treatment. */
|
.logo:hover { background: #1a2c39; }
|
||||||
.logo { background: #eef1f5; color: #253A49; border-color: rgba(0,0,0,.14); }
|
.logo .gear { color: #F8FDFF; opacity: .85; }
|
||||||
.logo:hover { background: #e2e6ec; }
|
.upchip { background: #253A49; color: #F8FDFF; border-color: #253A49; }
|
||||||
.logo .gear { color: #253A49; opacity: .85; }
|
.upchip .upcore:hover { background: #1a2c39; }
|
||||||
.tor { background: #eef1f5; color: #253A49; border-color: rgba(0,0,0,.14); }
|
.upchip .updismiss { border-left-color: rgba(255,255,255,.20); color: #F8FDFF; }
|
||||||
.tor:hover { background: #e2e6ec; }
|
|
||||||
.tor.connecting { background: #fbe9b5; color: #5a3f00; border-color: rgba(0,0,0,.14); }
|
|
||||||
.tor.on { background: #e6d9f5; color: #3f1f6b; border-color: rgba(0,0,0,.14); }
|
|
||||||
.upchip { background: #eef1f5; color: #253A49; border-color: rgba(0,0,0,.14); }
|
|
||||||
.upchip .upcore:hover { background: #e2e6ec; }
|
|
||||||
.upchip .updismiss { border-left-color: rgba(0,0,0,.14); color: #253A49; }
|
|
||||||
}
|
}
|
||||||
.tordisc { font-size: 11.5px; color: #d9c7f2; background: #2a1c40; border-top: 1px solid #6b3fa055; padding: 5px 14px; }
|
.tordisc { font-size: 11.5px; color: #d9c7f2; background: #2a1c40; border-top: 1px solid #6b3fa055; padding: 5px 14px; }
|
||||||
.tordisc a { color: var(--acid); }
|
.tordisc a { color: var(--acid); }
|
||||||
|
|
@ -809,7 +793,7 @@
|
||||||
const fav = b.favicon
|
const fav = b.favicon
|
||||||
? `<img class="bmfav" src="${String(b.favicon).replace(/"/g,""")}" onerror="this.remove()">`
|
? `<img class="bmfav" src="${String(b.favicon).replace(/"/g,""")}" onerror="this.remove()">`
|
||||||
: `<span class="bmfav" style="display:inline-block"></span>`;
|
: `<span class="bmfav" style="display:inline-block"></span>`;
|
||||||
return `<div class="bm" draggable="true" data-i="${i}" data-url="${(b.url||"").replace(/"/g,""")}" title="${((b.title||b.url)+" — "+(b.url||"")).replace(/"/g,""")}">${fav}<span class="bt">${(b.title||b.url).replace(/</g,"<")}</span><span class="bx" data-x="${i}">✕</span></div>`;
|
return `<div class="bm" data-i="${i}" title="${(b.url||"").replace(/"/g,""")}">${fav}<span class="bt">${(b.title||b.url).replace(/</g,"<")}</span><span class="bx" data-x="${i}">✕</span></div>`;
|
||||||
}).join("")
|
}).join("")
|
||||||
: `<span class="bm-empty">No saved pages yet — click ☆ or right-click here to add one.</span>`;
|
: `<span class="bm-empty">No saved pages yet — click ☆ or right-click here to add one.</span>`;
|
||||||
box.querySelectorAll(".bm").forEach((el) => el.onclick = (e) => {
|
box.querySelectorAll(".bm").forEach((el) => el.onclick = (e) => {
|
||||||
|
|
@ -817,40 +801,6 @@
|
||||||
if (e.target.dataset.x !== undefined) T.removeBookmark(bookmarks[i].url);
|
if (e.target.dataset.x !== undefined) T.removeBookmark(bookmarks[i].url);
|
||||||
else T.navigate(bookmarks[i].url);
|
else T.navigate(bookmarks[i].url);
|
||||||
});
|
});
|
||||||
// Drag-reorder chips — same shape as the tab strip. Drop side is chosen
|
|
||||||
// by whether the pointer is on the left/right half of the target chip.
|
|
||||||
// dragUrl is the identity we hand to bookmark-move; renderBookmarks
|
|
||||||
// fires again after the reorder, which rebinds these handlers cleanly.
|
|
||||||
let dragUrl = null;
|
|
||||||
box.querySelectorAll(".bm").forEach((row) => {
|
|
||||||
row.addEventListener("dragstart", (e) => {
|
|
||||||
dragUrl = row.dataset.url;
|
|
||||||
try { e.dataTransfer.effectAllowed = "move"; e.dataTransfer.setData("text/plain", dragUrl); } catch {}
|
|
||||||
row.classList.add("dragging");
|
|
||||||
});
|
|
||||||
row.addEventListener("dragend", () => {
|
|
||||||
row.classList.remove("dragging");
|
|
||||||
box.querySelectorAll(".bm").forEach((r) => r.classList.remove("dropbefore", "dropafter"));
|
|
||||||
dragUrl = null;
|
|
||||||
});
|
|
||||||
row.addEventListener("dragover", (e) => {
|
|
||||||
if (!dragUrl || row.dataset.url === dragUrl) return;
|
|
||||||
e.preventDefault(); e.dataTransfer.dropEffect = "move";
|
|
||||||
const r = row.getBoundingClientRect();
|
|
||||||
const before = (e.clientX - r.left) < r.width / 2;
|
|
||||||
row.classList.toggle("dropbefore", before);
|
|
||||||
row.classList.toggle("dropafter", !before);
|
|
||||||
});
|
|
||||||
row.addEventListener("dragleave", () => row.classList.remove("dropbefore", "dropafter"));
|
|
||||||
row.addEventListener("drop", (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const targetUrl = row.dataset.url;
|
|
||||||
if (!dragUrl || dragUrl === targetUrl) return;
|
|
||||||
const r = row.getBoundingClientRect();
|
|
||||||
const before = (e.clientX - r.left) < r.width / 2;
|
|
||||||
T.moveBookmark && T.moveBookmark(dragUrl, targetUrl, before ? "before" : "after");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// Backfill: older bookmarks stored before favicon-support have no icon.
|
// Backfill: older bookmarks stored before favicon-support have no icon.
|
||||||
// If we're currently on that URL and have a favicon in hand, patch it in
|
// If we're currently on that URL and have a favicon in hand, patch it in
|
||||||
// once so the next render draws it.
|
// once so the next render draws it.
|
||||||
|
|
|
||||||
87
main.js
87
main.js
|
|
@ -1984,51 +1984,15 @@ function installDownloadTracker() {
|
||||||
emitUpdateAvailable();
|
emitUpdateAvailable();
|
||||||
});
|
});
|
||||||
item.once("done", (_ev, state) => {
|
item.once("done", (_ev, state) => {
|
||||||
if (state !== "completed") {
|
if (state === "completed") {
|
||||||
|
updateDownloadPath = item.getSavePath() || dst;
|
||||||
|
updateDownloadState = "ready";
|
||||||
|
console.log(`[update] silent fetch complete: ${updateDownloadPath}`);
|
||||||
|
} else {
|
||||||
updateDownloadState = "failed";
|
updateDownloadState = "failed";
|
||||||
console.warn(`[update] silent fetch ${state}`);
|
console.warn(`[update] silent fetch ${state}`);
|
||||||
emitUpdateAvailable();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// NEVER mark "ready" without verifying the file hashes to what the
|
emitUpdateAvailable();
|
||||||
// manifest promised. Electron's DownloadItem has been observed to
|
|
||||||
// fire done/completed on truncated payloads (bad Content-Length,
|
|
||||||
// CDN cache truncation, mid-stream TLS reset the runtime swallowed),
|
|
||||||
// and 0.3.31's in-app updater then spawned a half-file as setup —
|
|
||||||
// NSIS integrity check failed silently and the browser was gone.
|
|
||||||
const savedPath = item.getSavePath() || dst;
|
|
||||||
const expected = String(updateAvailable && updateAvailable.setupHash || "").toLowerCase();
|
|
||||||
if (!expected) {
|
|
||||||
updateDownloadState = "failed";
|
|
||||||
console.warn(`[update] no manifest hash for ${savedPath} — refusing to arm install`);
|
|
||||||
try { fs.unlinkSync(savedPath); } catch {}
|
|
||||||
emitUpdateAvailable();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const crypto = require("node:crypto");
|
|
||||||
const hash = crypto.createHash("sha256");
|
|
||||||
const rs = fs.createReadStream(savedPath);
|
|
||||||
rs.on("data", (c) => hash.update(c));
|
|
||||||
rs.once("error", (e) => {
|
|
||||||
updateDownloadState = "failed";
|
|
||||||
console.warn(`[update] hash read failed: ${e.message}`);
|
|
||||||
try { fs.unlinkSync(savedPath); } catch {}
|
|
||||||
emitUpdateAvailable();
|
|
||||||
});
|
|
||||||
rs.once("end", () => {
|
|
||||||
const got = hash.digest("hex").toLowerCase();
|
|
||||||
if (got !== expected) {
|
|
||||||
updateDownloadState = "failed";
|
|
||||||
console.warn(`[update] SHA-256 mismatch: got ${got}, want ${expected} — refusing to arm install`);
|
|
||||||
try { fs.unlinkSync(savedPath); } catch {}
|
|
||||||
emitUpdateAvailable();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
updateDownloadPath = savedPath;
|
|
||||||
updateDownloadState = "ready";
|
|
||||||
console.log(`[update] silent fetch complete + verified: ${savedPath}`);
|
|
||||||
emitUpdateAvailable();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -3109,26 +3073,15 @@ ipcMain.handle("recheck-update", async () => {
|
||||||
return { updateAvailable, currentVersion: app.getVersion() };
|
return { updateAvailable, currentVersion: app.getVersion() };
|
||||||
});
|
});
|
||||||
// One-click "Install & restart". Requires the silent pre-fetch to have
|
// One-click "Install & restart". Requires the silent pre-fetch to have
|
||||||
// finished (updateDownloadState === "ready").
|
// finished (updateDownloadState === "ready"). Launches the setup with /S
|
||||||
//
|
// (skips the wizard; our nsis/installer.nsh's Ariadne prompt is bypassed
|
||||||
// CRITICAL — spawning the setup with just ["/S"] uninstalls Theseus but
|
// too on upgrades because the Ariadne registry key is already present),
|
||||||
// then FAILS to reinstall on our config (oneClick:false + perMachine:false
|
// then quits Theseus so the installer can overwrite it. When the installer
|
||||||
// + allowToChangeInstallationDirectory:true). In wizard mode NSIS expects
|
// finishes, the user re-launches Theseus and lands on the new version.
|
||||||
// UI for the install-path picker, and in silent mode with the registry
|
|
||||||
// path wiped by the just-run uninstaller, it exits without installing.
|
|
||||||
// Users hit this in 0.3.31: browser gone, nothing left.
|
|
||||||
//
|
|
||||||
// Fix: pass --updated alongside /S. electron-builder's NSIS template
|
|
||||||
// treats --updated as "this is an auto-update, reuse the existing install
|
|
||||||
// directory from registry, don't re-run the uninstaller". --force-run
|
|
||||||
// tells it to relaunch the app after install so the user isn't stranded
|
|
||||||
// even if the wizard-mode path somehow trips again.
|
|
||||||
ipcMain.handle("install-update-now", () => {
|
ipcMain.handle("install-update-now", () => {
|
||||||
if (updateDownloadState !== "ready" || !updateDownloadPath) return false;
|
if (updateDownloadState !== "ready" || !updateDownloadPath) return false;
|
||||||
try {
|
try {
|
||||||
const p = spawn(updateDownloadPath,
|
const p = spawn(updateDownloadPath, ["/S"], { detached: true, stdio: "ignore" });
|
||||||
["--updated", "/S", "--force-run"],
|
|
||||||
{ detached: true, stdio: "ignore" });
|
|
||||||
p.unref();
|
p.unref();
|
||||||
} catch (e) { console.warn("update spawn failed:", e?.message); return false; }
|
} catch (e) { console.warn("update spawn failed:", e?.message); return false; }
|
||||||
// Give the child a moment to inherit our arguments before we exit.
|
// Give the child a moment to inherit our arguments before we exit.
|
||||||
|
|
@ -3772,22 +3725,6 @@ ipcMain.handle("bookmark-remove", (_e, url) => {
|
||||||
saveBookmarks(); emitBookmarks();
|
saveBookmarks(); emitBookmarks();
|
||||||
return bookmarks;
|
return bookmarks;
|
||||||
});
|
});
|
||||||
// Reorder: pull `fromUrl` out of the list and reinsert it before or after
|
|
||||||
// `targetUrl`. Renderer picks the side by which half of the target chip the
|
|
||||||
// pointer is on, same convention the tab strip uses. A missing entry or a
|
|
||||||
// self-drop is a no-op, so noisy drag events don't corrupt the list.
|
|
||||||
ipcMain.handle("bookmark-move", (_e, fromUrl, targetUrl, place) => {
|
|
||||||
if (typeof fromUrl !== "string" || typeof targetUrl !== "string" || fromUrl === targetUrl) return bookmarks;
|
|
||||||
const from = bookmarks.findIndex((b) => b.url === fromUrl);
|
|
||||||
if (from < 0) return bookmarks;
|
|
||||||
const [moved] = bookmarks.splice(from, 1);
|
|
||||||
let to = bookmarks.findIndex((b) => b.url === targetUrl);
|
|
||||||
if (to < 0) { bookmarks.splice(from, 0, moved); return bookmarks; }
|
|
||||||
if (place === "after") to += 1;
|
|
||||||
bookmarks.splice(to, 0, moved);
|
|
||||||
saveBookmarks(); emitBookmarks();
|
|
||||||
return bookmarks;
|
|
||||||
});
|
|
||||||
ipcMain.handle("settings-get", () => settings);
|
ipcMain.handle("settings-get", () => settings);
|
||||||
ipcMain.handle("settings-set", (_e, key, val) => {
|
ipcMain.handle("settings-set", (_e, key, val) => {
|
||||||
if (key in SETTINGS_DEFAULTS) { settings[key] = val; saveSettings(); }
|
if (key in SETTINGS_DEFAULTS) { settings[key] = val; saveSettings(); }
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "theseus-navigator",
|
"name": "theseus-navigator",
|
||||||
"version": "0.3.34",
|
"version": "0.3.31",
|
||||||
"description": "Theseus Navigator — a browser that follows the thread. By Silent Mode, a Deviant project.",
|
"description": "Theseus Navigator — a browser that follows the thread. By Silent Mode, a Deviant project.",
|
||||||
"author": "Silent Mode",
|
"author": "Silent Mode",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,6 @@ contextBridge.exposeInMainWorld("theseus", {
|
||||||
addBookmark: (bm) => ipcRenderer.invoke("bookmark-add", bm),
|
addBookmark: (bm) => ipcRenderer.invoke("bookmark-add", bm),
|
||||||
updateBookmark: (url, patch) => ipcRenderer.invoke("bookmark-update", url, patch),
|
updateBookmark: (url, patch) => ipcRenderer.invoke("bookmark-update", url, patch),
|
||||||
removeBookmark: (url) => ipcRenderer.invoke("bookmark-remove", url),
|
removeBookmark: (url) => ipcRenderer.invoke("bookmark-remove", url),
|
||||||
moveBookmark: (fromUrl, targetUrl, place) => ipcRenderer.invoke("bookmark-move", fromUrl, targetUrl, place),
|
|
||||||
onBookmarks: (cb) => ipcRenderer.on("bookmarks", (_e, d) => cb(d)),
|
onBookmarks: (cb) => ipcRenderer.on("bookmarks", (_e, d) => cb(d)),
|
||||||
onNav: (cb) => ipcRenderer.on("nav", (_e, d) => cb(d)),
|
onNav: (cb) => ipcRenderer.on("nav", (_e, d) => cb(d)),
|
||||||
onTor: (cb) => ipcRenderer.on("tor", (_e, d) => cb(d)),
|
onTor: (cb) => ipcRenderer.on("tor", (_e, d) => cb(d)),
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue