0.3.38: brand-green scrollbars everywhere + captureTab widens the viewport when sidebar shrinks the tab
Two changes shipped together (main.js touched by both this session and a
parallel session in different regions):
Scrollbars — from the "empty white space should be grey, thumb should
be Bitcoin Cash green" ask:
* new SCROLLBAR_CSS constant + styleScrollbars(wc) helper injects the
theme on every dom-ready
* thumb #0AC18E (BCH primary), track rgba(120,130,150,0.18) subtle
neutral grey so it works on both dark and light surfaces without
hardcoding either; 6px radius, 2px inset via background-clip:padding-box
* modern scrollbar-color on <html> for Chromium 121+; ::-webkit- rules
with !important as the fallback / override for sites that theme
their own scrollbars — scrollbar-width intentionally left alone so
a page that hides scrollbars entirely keeps that behaviour
* hooked into every wc we own: createTab, chrome, popover, enginePicker,
downloadsPop, addressPicker, pwFillPop, linkStatus, sidebar (so every
add-on panel like Aegis picks it up), approvalPop
* fires once immediately if the wc is already past dom-ready when we
attach — fixed views load fast during startup, we'd otherwise miss
captureTab full-page — from the parallel session's screenshot work:
* before Page.captureScreenshot with captureBeyondViewport we now
override Emulation.setDeviceMetricsOverride to the window's full
content width so an open sidebar (or other on-screen chrome that
narrowed the tab view) doesn't clip the shot — capture comes back at
the page's natural full width, not the visible width
* attach the debugger for the call if it isn't attached, detach on
return; clear the metrics override in finally so the tab returns to
its normal layout regardless of success
This commit is contained in:
parent
e36d2b361c
commit
d7d4e7eb4e
2 changed files with 85 additions and 6 deletions
82
main.js
82
main.js
|
|
@ -1610,10 +1610,41 @@ function initAddons() {
|
||||||
}
|
}
|
||||||
if (mode === "full") {
|
if (mode === "full") {
|
||||||
// Page.captureScreenshot with captureBeyondViewport does the whole
|
// Page.captureScreenshot with captureBeyondViewport does the whole
|
||||||
// scrollable page in one shot, no setBounds gymnastics needed.
|
// scrollable page. Before we shoot, force the layout viewport to the
|
||||||
const r = await cdpCapture({ full: true });
|
// window's full content width via Emulation.setDeviceMetricsOverride
|
||||||
console.log(`[addons] [${addonId}] captureTab full ${r.width}x${r.height}`);
|
// so an open sidebar (or any other on-screen chrome that narrowed
|
||||||
return { dataUrl: r.dataUrl, width: r.width, height: r.height, host, format };
|
// the tab view) doesn't clip the capture — the shot always comes
|
||||||
|
// back at the page's natural full width, not the visible width.
|
||||||
|
const wasAttached = wc.debugger.isAttached();
|
||||||
|
if (!wasAttached) {
|
||||||
|
try { wc.debugger.attach("1.3"); }
|
||||||
|
catch (e) { if (!/already attached/i.test(String(e?.message))) throw e; }
|
||||||
|
}
|
||||||
|
let overrode = false;
|
||||||
|
try {
|
||||||
|
const winW = (win?.getContentBounds()?.width) || 0;
|
||||||
|
const tabB = t.view.getBounds();
|
||||||
|
const need = winW > tabB.width + 24 ? winW : 0;
|
||||||
|
if (need > 0) {
|
||||||
|
// dsf 0 = "let Chromium keep the real device scale factor".
|
||||||
|
// mobile false, deviceScaleFactor 0 keeps typography sane;
|
||||||
|
// height 0 tells CDP "use the current viewport height".
|
||||||
|
await wc.debugger.sendCommand("Emulation.setDeviceMetricsOverride", {
|
||||||
|
width: need, height: 0, deviceScaleFactor: 0, mobile: false,
|
||||||
|
});
|
||||||
|
overrode = true;
|
||||||
|
// A frame or two so the reflow settles before we snapshot.
|
||||||
|
await new Promise((r) => setTimeout(r, 250));
|
||||||
|
}
|
||||||
|
const r = await cdpCapture({ full: true });
|
||||||
|
console.log(`[addons] [${addonId}] captureTab full ${r.width}x${r.height}${overrode ? ` (viewport widened to ${need}px)` : ""}`);
|
||||||
|
return { dataUrl: r.dataUrl, width: r.width, height: r.height, host, format };
|
||||||
|
} finally {
|
||||||
|
if (overrode) {
|
||||||
|
try { await wc.debugger.sendCommand("Emulation.clearDeviceMetricsOverride"); } catch {}
|
||||||
|
}
|
||||||
|
if (!wasAttached) { try { wc.debugger.detach(); } catch {} }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (mode === "region") {
|
if (mode === "region") {
|
||||||
const src = String(opts?.overlaySource || "");
|
const src = String(opts?.overlaySource || "");
|
||||||
|
|
@ -2177,6 +2208,34 @@ function loadErrorPage(t, id, { url, code, desc }) {
|
||||||
if (id === activeId) pushNav(t.prov);
|
if (id === activeId) pushNav(t.prov);
|
||||||
emitTabs();
|
emitTabs();
|
||||||
}
|
}
|
||||||
|
// Scrollbar theme injected into every webContents we own — tabs, chrome,
|
||||||
|
// sidebar, all the floating popovers, and every add-on panel host. Track
|
||||||
|
// picks up a subtle neutral grey (works on both dark and light surfaces
|
||||||
|
// without hardcoding either); thumb is the BCH primary #0AC18E so every
|
||||||
|
// scroll surface reads as Silent Mode's. `scrollbar-color` is the modern
|
||||||
|
// standard (Chromium ≥ 121); the ::-webkit- fallback gives us fine control
|
||||||
|
// over width, radius and hover state on older engines. `!important` on the
|
||||||
|
// track / thumb wins over per-page overrides so the branding stays visible
|
||||||
|
// even on sites that theme their own scrollbars — but we deliberately don't
|
||||||
|
// force `scrollbar-width` so a page that has hidden its scrollbars entirely
|
||||||
|
// keeps that behaviour.
|
||||||
|
const SCROLLBAR_CSS = `
|
||||||
|
html { scrollbar-color: #0AC18E rgba(120,130,150,0.18); }
|
||||||
|
::-webkit-scrollbar { width: 12px; height: 12px; background: rgba(120,130,150,0.18) !important; }
|
||||||
|
::-webkit-scrollbar-track { background: rgba(120,130,150,0.18) !important; }
|
||||||
|
::-webkit-scrollbar-thumb { background: #0AC18E !important; border-radius: 6px;
|
||||||
|
border: 2px solid transparent; background-clip: padding-box !important; }
|
||||||
|
::-webkit-scrollbar-thumb:hover { background: #14e0a5 !important; background-clip: padding-box !important; }
|
||||||
|
::-webkit-scrollbar-corner { background: transparent !important; }
|
||||||
|
`;
|
||||||
|
function styleScrollbars(wc) {
|
||||||
|
if (!wc) return;
|
||||||
|
const inject = () => { try { wc.insertCSS(SCROLLBAR_CSS); } catch {} };
|
||||||
|
wc.on("dom-ready", inject);
|
||||||
|
// For a wc that's already past dom-ready when we attach (fixed views load
|
||||||
|
// fast during startup), fire once explicitly.
|
||||||
|
try { if (!wc.isLoading()) inject(); } catch {}
|
||||||
|
}
|
||||||
function createTab(initial, opts = {}) {
|
function createTab(initial, opts = {}) {
|
||||||
const id = ++tabSeq;
|
const id = ++tabSeq;
|
||||||
// Non-settings tabs get home-preload so the built-in home page can round-
|
// Non-settings tabs get home-preload so the built-in home page can round-
|
||||||
|
|
@ -2197,6 +2256,7 @@ function createTab(initial, opts = {}) {
|
||||||
// mode users don't get a dark stub while a page paints.
|
// mode users don't get a dark stub while a page paints.
|
||||||
try { view.setBackgroundColor(nativeTheme.shouldUseDarkColors ? "#0b0e14" : "#ffffff"); } catch {}
|
try { view.setBackgroundColor(nativeTheme.shouldUseDarkColors ? "#0b0e14" : "#ffffff"); } catch {}
|
||||||
const wc = view.webContents;
|
const wc = view.webContents;
|
||||||
|
styleScrollbars(wc);
|
||||||
try { wc.setWebRTCIPHandlingPolicy(webrtcPolicy()); } catch {}
|
try { wc.setWebRTCIPHandlingPolicy(webrtcPolicy()); } catch {}
|
||||||
try { wc.setBackgroundThrottling(settings.backgroundThrottle); } catch {}
|
try { wc.setBackgroundThrottling(settings.backgroundThrottle); } catch {}
|
||||||
applyFingerprint(wc);
|
applyFingerprint(wc);
|
||||||
|
|
@ -2398,52 +2458,64 @@ function createWindow() {
|
||||||
});
|
});
|
||||||
chrome = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "preload.js") } });
|
chrome = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "preload.js") } });
|
||||||
win.contentView.addChildView(chrome);
|
win.contentView.addChildView(chrome);
|
||||||
|
styleScrollbars(chrome.webContents);
|
||||||
chrome.webContents.loadFile("chrome.html");
|
chrome.webContents.loadFile("chrome.html");
|
||||||
// Floating site-info overlay (hidden until the address-bar badge is clicked).
|
// Floating site-info overlay (hidden until the address-bar badge is clicked).
|
||||||
popover = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "popover-preload.js") } });
|
popover = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "popover-preload.js") } });
|
||||||
try { popover.setBackgroundColor("#00000000"); } catch {}
|
try { popover.setBackgroundColor("#00000000"); } catch {}
|
||||||
win.contentView.addChildView(popover);
|
win.contentView.addChildView(popover);
|
||||||
|
styleScrollbars(popover.webContents);
|
||||||
popover.webContents.loadFile("popover.html");
|
popover.webContents.loadFile("popover.html");
|
||||||
popover.setVisible(false);
|
popover.setVisible(false);
|
||||||
// Floating engine-picker overlay (custom dropdown with real favicons).
|
// Floating engine-picker overlay (custom dropdown with real favicons).
|
||||||
enginePicker = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "engine-picker-preload.js") } });
|
enginePicker = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "engine-picker-preload.js") } });
|
||||||
try { enginePicker.setBackgroundColor("#00000000"); } catch {}
|
try { enginePicker.setBackgroundColor("#00000000"); } catch {}
|
||||||
win.contentView.addChildView(enginePicker);
|
win.contentView.addChildView(enginePicker);
|
||||||
|
styleScrollbars(enginePicker.webContents);
|
||||||
enginePicker.webContents.loadFile("engine-picker.html");
|
enginePicker.webContents.loadFile("engine-picker.html");
|
||||||
enginePicker.setVisible(false);
|
enginePicker.setVisible(false);
|
||||||
// Floating downloads panel — shows active + recent downloads.
|
// Floating downloads panel — shows active + recent downloads.
|
||||||
downloadsPop = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "downloads-preload.js") } });
|
downloadsPop = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "downloads-preload.js") } });
|
||||||
try { downloadsPop.setBackgroundColor("#00000000"); } catch {}
|
try { downloadsPop.setBackgroundColor("#00000000"); } catch {}
|
||||||
win.contentView.addChildView(downloadsPop);
|
win.contentView.addChildView(downloadsPop);
|
||||||
|
styleScrollbars(downloadsPop.webContents);
|
||||||
downloadsPop.webContents.loadFile("downloads.html");
|
downloadsPop.webContents.loadFile("downloads.html");
|
||||||
downloadsPop.setVisible(false);
|
downloadsPop.setVisible(false);
|
||||||
// Floating address-bar suggestions dropdown.
|
// Floating address-bar suggestions dropdown.
|
||||||
addressPicker = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "address-picker-preload.js") } });
|
addressPicker = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "address-picker-preload.js") } });
|
||||||
try { addressPicker.setBackgroundColor("#00000000"); } catch {}
|
try { addressPicker.setBackgroundColor("#00000000"); } catch {}
|
||||||
win.contentView.addChildView(addressPicker);
|
win.contentView.addChildView(addressPicker);
|
||||||
|
styleScrollbars(addressPicker.webContents);
|
||||||
addressPicker.webContents.loadFile("address-picker.html");
|
addressPicker.webContents.loadFile("address-picker.html");
|
||||||
addressPicker.setVisible(false);
|
addressPicker.setVisible(false);
|
||||||
// Floating password-fill picker.
|
// Floating password-fill picker.
|
||||||
pwFillPop = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "pw-fill-preload.js") } });
|
pwFillPop = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "pw-fill-preload.js") } });
|
||||||
try { pwFillPop.setBackgroundColor("#00000000"); } catch {}
|
try { pwFillPop.setBackgroundColor("#00000000"); } catch {}
|
||||||
win.contentView.addChildView(pwFillPop);
|
win.contentView.addChildView(pwFillPop);
|
||||||
|
styleScrollbars(pwFillPop.webContents);
|
||||||
pwFillPop.webContents.loadFile("pw-fill.html");
|
pwFillPop.webContents.loadFile("pw-fill.html");
|
||||||
pwFillPop.setVisible(false);
|
pwFillPop.setVisible(false);
|
||||||
// Link-hover status pill (bottom-left of window).
|
// Link-hover status pill (bottom-left of window).
|
||||||
linkStatus = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "link-status-preload.js") } });
|
linkStatus = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "link-status-preload.js") } });
|
||||||
try { linkStatus.setBackgroundColor("#00000000"); } catch {}
|
try { linkStatus.setBackgroundColor("#00000000"); } catch {}
|
||||||
win.contentView.addChildView(linkStatus);
|
win.contentView.addChildView(linkStatus);
|
||||||
|
styleScrollbars(linkStatus.webContents);
|
||||||
linkStatus.webContents.loadFile("link-status.html");
|
linkStatus.webContents.loadFile("link-status.html");
|
||||||
linkStatus.setVisible(false);
|
linkStatus.setVisible(false);
|
||||||
// Add-on sidebar host. Doesn't loadFile until an add-on panel is opened.
|
// Add-on sidebar host. Doesn't loadFile until an add-on panel is opened —
|
||||||
|
// styleScrollbars hooks dom-ready, which fires per navigation, so every
|
||||||
|
// panel loaded into this view (Aegis, Screenshot, etc.) picks up the
|
||||||
|
// brand scrollbar the moment its DOM is ready.
|
||||||
sidebar = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "sidebar-preload.js") } });
|
sidebar = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "sidebar-preload.js") } });
|
||||||
win.contentView.addChildView(sidebar);
|
win.contentView.addChildView(sidebar);
|
||||||
|
styleScrollbars(sidebar.webContents);
|
||||||
sidebar.setVisible(false);
|
sidebar.setVisible(false);
|
||||||
// Add-on approval overlay (approval-modal capability). Transparent view
|
// Add-on approval overlay (approval-modal capability). Transparent view
|
||||||
// over the tab area, loaded once, shown per request.
|
// over the tab area, loaded once, shown per request.
|
||||||
approvalPop = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "approval-preload.js") } });
|
approvalPop = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "approval-preload.js") } });
|
||||||
try { approvalPop.setBackgroundColor("#00000000"); } catch {}
|
try { approvalPop.setBackgroundColor("#00000000"); } catch {}
|
||||||
win.contentView.addChildView(approvalPop);
|
win.contentView.addChildView(approvalPop);
|
||||||
|
styleScrollbars(approvalPop.webContents);
|
||||||
approvalPop.webContents.loadFile("approval.html");
|
approvalPop.webContents.loadFile("approval.html");
|
||||||
approvalPop.setVisible(false);
|
approvalPop.setVisible(false);
|
||||||
chrome.webContents.once("did-finish-load", () => {
|
chrome.webContents.once("did-finish-load", () => {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "theseus-navigator",
|
"name": "theseus-navigator",
|
||||||
"version": "0.3.37",
|
"version": "0.3.38",
|
||||||
"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",
|
||||||
|
|
@ -11,15 +11,22 @@
|
||||||
"dist": "electron-builder --win nsis portable"
|
"dist": "electron-builder --win nsis portable"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@bch-wc2/interfaces": "^0.0.16",
|
||||||
|
"@bitauth/libauth": "^3.1.0-next.8",
|
||||||
"@bitcoinerlab/secp256k1": "^1.2.0",
|
"@bitcoinerlab/secp256k1": "^1.2.0",
|
||||||
"@noble/curves": "^2.0.1",
|
"@noble/curves": "^2.0.1",
|
||||||
"@noble/hashes": "^2.0.1",
|
"@noble/hashes": "^2.0.1",
|
||||||
"@scure/bip32": "^2.0.1",
|
"@scure/bip32": "^2.0.1",
|
||||||
|
"@wizardconnect/core": "^0.2.4",
|
||||||
|
"@wizardconnect/wallet": "^0.2.3",
|
||||||
"bip32": "^4.0.0",
|
"bip32": "^4.0.0",
|
||||||
"bip39": "^3.1.0",
|
"bip39": "^3.1.0",
|
||||||
"bitcoinjs-lib": "^6.1.7",
|
"bitcoinjs-lib": "^6.1.7",
|
||||||
"ecpair": "^2.1.0",
|
"ecpair": "^2.1.0",
|
||||||
|
"eventemitter3": "^5.0.1",
|
||||||
"fetch-socks": "^1.3.3",
|
"fetch-socks": "^1.3.3",
|
||||||
|
"isomorphic-ws": "^5.0.0",
|
||||||
|
"lossless-json": "^4.0.2",
|
||||||
"nostr-tools": "^2.10.4",
|
"nostr-tools": "^2.10.4",
|
||||||
"psl": "^1.15.0",
|
"psl": "^1.15.0",
|
||||||
"socks-proxy-agent": "^10.1.0",
|
"socks-proxy-agent": "^10.1.0",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue