Theseus 0.3.70: fully-lazy session restore, quick-links strip reordered
Session restore no longer loads any page on launch. In 0.3.63 the strip was built from saved titles + favicons and only the previously-active tab's URL was navigated at startup, so a 20-tab session cost one renderer load instead of twenty — but that one load is still a real page, often the heaviest one in the whole session, and it fought every other startup task for the main thread while the window sat not-responding. Now every restored tab, the previously-active one included, comes up dormant: zero page renderers at launch, no page starts loading until the user asks for a specific tab (clicks the chip, hits reload, types in the URL bar). The previously-active tab stays highlighted in the strip so one click brings it back; the content area sits with the tab's own background colour until that click. RAM-at- launch is now just the chrome, the overlays and the strip — a 500 MB saved page never materialises as a renderer the user did not even ask to see. A pending tab that is navigated explicitly (URL bar, link, search) drops its saved URL at the top of navigateTab, so a later reload or chip click can't snap it back. Quick-links strip default set is now Telegram, WhatsApp, X, YouTube — in that order. Messenger and Spotify are out of the default; users who want them can still add them via Settings › General › Quick links. Existing installs whose list still matches the previous untouched default (same six ids in the same order) migrate on next launch; any customisation (reorder, add, remove) is left alone.
This commit is contained in:
parent
f2ff48b977
commit
c2ec0f0d02
2 changed files with 42 additions and 20 deletions
60
main.js
60
main.js
|
|
@ -522,12 +522,10 @@ const SETTINGS_DEFAULTS = {
|
||||||
// service shortcuts; click opens the URL in a new tab. Toggle via settings.
|
// service shortcuts; click opens the URL in a new tab. Toggle via settings.
|
||||||
quickLinksShow: true,
|
quickLinksShow: true,
|
||||||
quickLinks: [
|
quickLinks: [
|
||||||
{ id: "messenger", url: "https://www.messenger.com/", title: "Messenger" },
|
|
||||||
{ id: "whatsapp", url: "https://web.whatsapp.com/", title: "WhatsApp" },
|
|
||||||
{ id: "telegram", url: "https://web.telegram.org/k/", title: "Telegram" },
|
{ id: "telegram", url: "https://web.telegram.org/k/", title: "Telegram" },
|
||||||
|
{ id: "whatsapp", url: "https://web.whatsapp.com/", title: "WhatsApp" },
|
||||||
{ id: "x", url: "https://x.com/", title: "X" },
|
{ id: "x", url: "https://x.com/", title: "X" },
|
||||||
{ id: "youtube", url: "https://www.youtube.com/", title: "YouTube" },
|
{ id: "youtube", url: "https://www.youtube.com/", title: "YouTube" },
|
||||||
{ id: "spotify", url: "https://open.spotify.com/", title: "Spotify" },
|
|
||||||
],
|
],
|
||||||
// Toolbar bar sizes. The URL bar is flex:1 by default so it eats all
|
// Toolbar bar sizes. The URL bar is flex:1 by default so it eats all
|
||||||
// remaining space; `compact`/`medium` cap it so the extension dock has
|
// remaining space; `compact`/`medium` cap it so the extension dock has
|
||||||
|
|
@ -580,6 +578,17 @@ function loadSettings() {
|
||||||
settings.searchEngine = enabled.find((x) => !isFrozen(x)) || DEFAULT_ENABLED[0];
|
settings.searchEngine = enabled.find((x) => !isFrozen(x)) || DEFAULT_ENABLED[0];
|
||||||
}
|
}
|
||||||
} catch (e) { console.warn("engine normalize:", e?.message); }
|
} catch (e) { console.warn("engine normalize:", e?.message); }
|
||||||
|
// Quick-links default set changed in 0.3.70: drop Messenger + Spotify and
|
||||||
|
// reorder to Telegram, WhatsApp, X, YouTube. Users who kept the previous
|
||||||
|
// untouched default (same 6 ids, same order) move to the new set; any
|
||||||
|
// customisation (reorder, add, remove) is left alone.
|
||||||
|
try {
|
||||||
|
const PRIOR = ["messenger", "whatsapp", "telegram", "x", "youtube", "spotify"];
|
||||||
|
const have = Array.isArray(settings.quickLinks) ? settings.quickLinks.map((L) => String(L?.id || "")) : [];
|
||||||
|
if (have.length === PRIOR.length && have.every((id, i) => id === PRIOR[i])) {
|
||||||
|
settings.quickLinks = SETTINGS_DEFAULTS.quickLinks.map((L) => ({ ...L }));
|
||||||
|
}
|
||||||
|
} catch (e) { console.warn("quickLinks migrate:", e?.message); }
|
||||||
}
|
}
|
||||||
function saveSettings() {
|
function saveSettings() {
|
||||||
try { fs.writeFileSync(settingsFile(), JSON.stringify(settings, null, 2)); } catch (e) { console.error("settings save failed:", e.message); }
|
try { fs.writeFileSync(settingsFile(), JSON.stringify(settings, null, 2)); } catch (e) { console.error("settings save failed:", e.message); }
|
||||||
|
|
@ -1272,26 +1281,35 @@ function loadSession() {
|
||||||
return { tabs: urls.map((url) => ({ url, title: "", favicon: null })), active };
|
return { tabs: urls.map((url) => ({ url, title: "", favicon: null })), active };
|
||||||
} catch { return empty; }
|
} catch { return empty; }
|
||||||
}
|
}
|
||||||
// Session restore, LAZY: only the active tab actually loads a page at startup.
|
// Session restore, FULLY LAZY: nothing navigates at launch. Every restored
|
||||||
// Every other restored tab is a dormant WebContentsView in the strip — title
|
// tab — including the one that was active at close — comes up as a dormant
|
||||||
// and favicon from the saved session — and only navigates when the user clicks
|
// WebContentsView in the strip, painted from the saved title + favicon, and
|
||||||
// it (setActive consumes `pending`). This drops N renderer loads at startup
|
// only triggers a page load when the user asks for it specifically (clicks
|
||||||
// to 1, so launch time is roughly flat whether the user has 2 or 25 tabs.
|
// the chip, hits reload, types in the URL bar). This drops the whole startup
|
||||||
|
// renderer load to zero page renderers (chrome.html + the overlays + the
|
||||||
|
// strip only), so launch is near-flat whether the session has 2 or 50 tabs
|
||||||
|
// and the RAM-at-launch footprint is just the chrome plus the strip —
|
||||||
|
// a saved heavy page doesn't come back as a 500 MB renderer the user
|
||||||
|
// didn't even ask to see. The previously active tab stays highlighted in
|
||||||
|
// the strip so the user can one-click it back; its content area sits with
|
||||||
|
// the tab's solid background until that happens.
|
||||||
function restoreTabs() {
|
function restoreTabs() {
|
||||||
const saved = settings.restoreSession ? loadSession() : { tabs: [], active: 0 };
|
const saved = settings.restoreSession ? loadSession() : { tabs: [], active: 0 };
|
||||||
if (!saved.tabs.length) { createTab(); return; }
|
if (!saved.tabs.length) { createTab(); return; }
|
||||||
const activeMeta = saved.tabs[saved.active] || saved.tabs[0];
|
|
||||||
createTab(activeMeta.url);
|
|
||||||
// Build the rest as lazy. Insert at their saved slot so the strip order
|
|
||||||
// matches the save.
|
|
||||||
for (let i = 0; i < saved.tabs.length; i++) {
|
for (let i = 0; i < saved.tabs.length; i++) {
|
||||||
if (i === saved.active) continue;
|
try { createTab(null, { background: true, pending: saved.tabs[i] }); }
|
||||||
try {
|
catch (e) { console.warn("session restore: tab failed:", e?.message); }
|
||||||
createTab(null, { background: true, pending: saved.tabs[i] });
|
}
|
||||||
// createTab appends; move it into position.
|
// Pick the saved active slot as the chip the strip highlights at launch.
|
||||||
const t = tabs.pop();
|
// A plain setActive() would materialise it (defeating the point of fully-
|
||||||
tabs.splice(Math.min(i, tabs.length), 0, t);
|
// lazy restore), so apply activeId + visibility by hand — the user
|
||||||
} catch (e) { console.warn("session restore: tab failed:", e?.message); }
|
// clicking that same chip is what triggers the first navigation.
|
||||||
|
const idx = Math.min(Math.max(0, saved.active | 0), tabs.length - 1);
|
||||||
|
const target = tabs[idx];
|
||||||
|
if (target) {
|
||||||
|
activeId = target.id;
|
||||||
|
try { target.view.setVisible(true); } catch {}
|
||||||
|
for (const t of tabs) if (t.id !== activeId) { try { t.view.setVisible(false); } catch {} }
|
||||||
}
|
}
|
||||||
emitTabs();
|
emitTabs();
|
||||||
}
|
}
|
||||||
|
|
@ -4089,6 +4107,10 @@ function createWindow() {
|
||||||
|
|
||||||
async function navigateTab(id, input) {
|
async function navigateTab(id, input) {
|
||||||
const t = tabById(id); if (!t) return;
|
const t = tabById(id); if (!t) return;
|
||||||
|
// A pending tab the user navigates explicitly (URL bar, link follow,
|
||||||
|
// search) has outgrown its saved URL — otherwise a later reload or chip
|
||||||
|
// click would snap it back to that stale URL via materializePending.
|
||||||
|
if (t.pending) t.pending = null;
|
||||||
let q = String(input).trim();
|
let q = String(input).trim();
|
||||||
if (!q) return;
|
if (!q) return;
|
||||||
// theseus://extensions/install/<id> typed or pasted into the address bar.
|
// theseus://extensions/install/<id> typed or pasted into the address bar.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "theseus-navigator",
|
"name": "theseus-navigator",
|
||||||
"version": "0.3.69",
|
"version": "0.3.70",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"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",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue