Theseus 0.3.112: AI picker chevron fixed, home polish, performance toggles
Three home-page tweaks: - Removed the YOUR LINKS and SILENT MODE section headings; the two panels now stand on their own without labels. - The + Add card cell now matches the real cards' size and shape so it reads as a peer of the grid rather than a short dashed strip. - Empty slot cells in the left grid are invisible by default and only reveal their dashed outline when you hover the strip or drag a link over them. The + add-link cell stays visible as the discoverable entry. AI engine picker: - The chevron badge on the AI button is now a plain span with the button role, not a nested <button> inside the main AI button. Chromium flattened the invalid nesting so the chevron's own click handler never fired; the picker now opens from the chevron and from keyboard Enter / Space as well as right-click. Performance (Settings > Performance > Startup): - Pithos ships with activation: on-demand; its control server for s3d no longer boots at launch. Wakes on first panel open or a pithos.sia navigation. - Hermes relay auto-restore (1.5 s after launch) is now gated by a new setting hermesAutoRestore (default on). Turn it off to skip background Nostr relay connections for users who don't live in Hermes. - New setting pithosAtLaunch (default off) mirrors the existing walletAtLaunch so users who do want Pithos ready at launch can opt back in. - The Lightest preset now also turns off pithosAtLaunch and hermesAutoRestore.
This commit is contained in:
parent
5829d43482
commit
9095a5ecf9
6 changed files with 68 additions and 13 deletions
|
|
@ -6,6 +6,7 @@
|
|||
"author": "Silent Mode",
|
||||
"icon": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NCA2NCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJnIiB4MT0iMCIgeTE9IjAiIHgyPSIwIiB5Mj0iMSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjM2RkYzk3Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMWU4ZjZhIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHBhdGggZD0iTTIyIDZoMjB2NWMwIDItMiAzLTIgNSA4IDMgMTQgMTEgMTQgMjEgMCAxMi05IDIxLTIyIDIxUzEwIDQ5IDEwIDM3YzAtMTAgNi0xOCAxNC0yMSAwLTItMi0zLTItNXoiIGZpbGw9InVybCgjZykiLz48cGF0aCBkPSJNMTQgMzBoMzZNMTMgNDBoMzgiIHN0cm9rZT0iIzBiMWYxOCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIG9wYWNpdHk9Ii41NSIvPjwvc3ZnPgo=",
|
||||
"main": "index.js",
|
||||
"activation": "on-demand",
|
||||
"capabilities": [
|
||||
"sidebar-panel",
|
||||
"vault-derive",
|
||||
|
|
|
|||
16
home.html
16
home.html
|
|
@ -107,9 +107,9 @@
|
|||
specific slot. Order matches the grid's cell index. Subtly visible
|
||||
so users see where they can drop; brighter on hover of the strip. */
|
||||
.slot-empty { display: block; width: 48px; height: 48px; margin: 0 auto;
|
||||
border-radius: 10px; border: 2px dashed rgb(from var(--line2) r g b / .55);
|
||||
border-radius: 10px; border: 2px dashed transparent;
|
||||
background: transparent; transition: border-color .12s, background .12s; }
|
||||
.slots-list:hover .slot-empty { border-color: var(--line2); }
|
||||
.slots-list:hover .slot-empty { border-color: rgb(from var(--line2) r g b / .55); }
|
||||
.slot-empty.drop-hot { border-color: var(--acid); background: rgb(from var(--acid) r g b / .06); }
|
||||
|
||||
/* RIGHT COLUMN — just the 3-column grid of Silent Mode cards; search
|
||||
|
|
@ -148,11 +148,15 @@
|
|||
}
|
||||
.card.drag-before::before { left: -8px; }
|
||||
.card.drag-after::after { right: -8px; }
|
||||
.card-add { display: flex; align-items: center; justify-content: center; min-height: 128px;
|
||||
border: 2px dashed var(--line2); border-radius: 14px; background: transparent;
|
||||
/* Matches .card's horizontal shape so a "+ Add card" cell sits in the
|
||||
grid as a peer of the real cards instead of a short dashed strip. */
|
||||
.card-add { display: flex; align-items: center; justify-content: center;
|
||||
gap: 14px; padding: 14px 16px; min-height: 92px;
|
||||
border: 2px dashed var(--line2); border-radius: 14px;
|
||||
background: rgba(20,26,36,.35); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
|
||||
color: var(--mut); font-size: 13.5px; cursor: pointer;
|
||||
transition: color .12s, border-color .12s, background .12s; }
|
||||
.card-add:hover { color: var(--acid); border-color: var(--acid); background: rgb(from var(--acid) r g b / .04); }
|
||||
.card-add:hover { color: var(--acid); border-color: var(--acid); background: rgb(from var(--acid) r g b / .06); }
|
||||
|
||||
/* Context menus. One for the background (edit actions), one for images
|
||||
(copy / edit / remove). Positioned at the pointer by JS. */
|
||||
|
|
@ -237,11 +241,9 @@
|
|||
|
||||
<div class="homepage">
|
||||
<div class="section left-column">
|
||||
<div class="section-title">Your links</div>
|
||||
<div class="slots-list" id="slotsList"></div>
|
||||
</div>
|
||||
<div class="section right-column">
|
||||
<div class="section-title">Silent Mode</div>
|
||||
<div class="cards-grid" id="cardsGrid"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
45
main.js
45
main.js
|
|
@ -549,6 +549,8 @@ const SETTINGS_DEFAULTS = {
|
|||
// extension's activate() runs at launch, before the window exists.
|
||||
extensionsOnDemand: true, // extensions whose manifest allows it start on first use
|
||||
walletAtLaunch: false, // start Aegis at launch even once it allows on-demand (no effect while it is startup-only)
|
||||
pithosAtLaunch: false, // start Pithos at launch (otherwise it wakes on first panel open or a pithos.sia navigation)
|
||||
hermesAutoRestore: true, // open your Nostr relays 1.5s after launch so Hermes messages arrive before you open the app (off = relays connect on first open)
|
||||
preloadMenus: true, // load address suggestions / link pill / site info after the first page (prewarmOverlays)
|
||||
addonsStartAtLaunch: [], // on-demand add-ons that asked (api.startAtLaunch) to start at launch anyway
|
||||
// Storage retention — nothing persists by default. Auto-clear on quit
|
||||
|
|
@ -3280,6 +3282,7 @@ function initAddons() {
|
|||
if (m.activation !== "on-demand") return "startup";
|
||||
if (settings.extensionsOnDemand === false) return "startup";
|
||||
if (m.id === "aegis" && settings.walletAtLaunch) return "startup";
|
||||
if (m.id === "pithos" && settings.pithosAtLaunch) return "startup";
|
||||
if (Array.isArray(settings.addonsStartAtLaunch) && settings.addonsStartAtLaunch.includes(m.id)) return "startup";
|
||||
return "on-demand";
|
||||
},
|
||||
|
|
@ -9705,6 +9708,7 @@ ipcMain.handle("settings-set", (_e, key, val) => {
|
|||
// leaves running add-ons alone; it applies from the next launch.
|
||||
if (addonHost && key === "extensionsOnDemand" && val === false) addonHost.activateAllDormant("setting").catch(() => {});
|
||||
if (addonHost && key === "walletAtLaunch" && val && addonHost.isDormant("aegis")) addonHost.ensureActive("aegis", "setting").catch(() => {});
|
||||
if (addonHost && key === "pithosAtLaunch" && val && addonHost.isDormant("pithos")) addonHost.ensureActive("pithos", "setting").catch(() => {});
|
||||
if (key === "preloadMenus" && val && chromeReadyDone) prewarmOverlays();
|
||||
if (key === "freezeBackgroundTabs") applyFreezeSetting();
|
||||
if (["timezoneMode", "timezoneValue", "languageMode", "languageSpoof", "languageValue",
|
||||
|
|
@ -10648,7 +10652,14 @@ async function hermesAutoRestore() {
|
|||
}
|
||||
// Fire a little after app ready so loadHermesLib's dynamic import and
|
||||
// safeStorage both exist. Non-blocking.
|
||||
app.whenReady().then(() => setTimeout(() => { void hermesAutoRestore(); }, 1500));
|
||||
app.whenReady().then(() => setTimeout(() => {
|
||||
// Gated so a user who doesn't want background relay traffic (laptop on
|
||||
// battery, metered connection, or they just don't use Hermes) can opt
|
||||
// out in Settings › Performance › Startup. Default on so first-run
|
||||
// users keep getting messages before they open the panel.
|
||||
if (settings.hermesAutoRestore === false) return;
|
||||
void hermesAutoRestore();
|
||||
}, 1500));
|
||||
|
||||
const hOk = (o = {}) => ({ ok: true, ...o });
|
||||
const hErr = (e) => ({ ok: false, err: String((e && e.message) || e) });
|
||||
|
|
@ -10729,17 +10740,44 @@ async function hermesReverseResolve(pkHex) {
|
|||
|
||||
// One receive subscription per relay. If the socket dies we resurrect it on a
|
||||
// backoff — a locked / logged-out state tears them all down cleanly.
|
||||
// Zombie-socket mitigation: ping every PING_MS, close + reconnect if no pong
|
||||
// within PONG_TIMEOUT_MS. Without this the TCP state can be "connected" while
|
||||
// the relay has silently stopped delivering, and new incoming kind:1059s only
|
||||
// arrive after the user's next outbound send forces a write-error and triggers
|
||||
// close → reconnect. Nasty invisible lag on long-lived Electron sessions.
|
||||
const HERMES_PING_MS = 30_000;
|
||||
const HERMES_PONG_TIMEOUT_MS = 10_000;
|
||||
async function hermesConnectRelay(url) {
|
||||
const H = await loadHermesLib();
|
||||
const state = { ws: null, ready: false, subId: "hermes-inbox" };
|
||||
const state = { ws: null, ready: false, subId: "hermes-inbox", pingTimer: null, pongTimer: null };
|
||||
const open = () => {
|
||||
if (!hermesState) return; // torn down while reconnecting
|
||||
const ws = new WebSocket(url);
|
||||
state.ws = ws;
|
||||
const clearPingers = () => {
|
||||
if (state.pingTimer) { clearInterval(state.pingTimer); state.pingTimer = null; }
|
||||
if (state.pongTimer) { clearTimeout(state.pongTimer); state.pongTimer = null; }
|
||||
};
|
||||
const schedulePing = () => {
|
||||
clearPingers();
|
||||
state.pingTimer = setInterval(() => {
|
||||
try { ws.ping(); } catch { return; }
|
||||
if (state.pongTimer) clearTimeout(state.pongTimer);
|
||||
state.pongTimer = setTimeout(() => {
|
||||
// No pong in time → the socket is zombie. Terminate forces an
|
||||
// immediate close event which drives the reconnect loop below.
|
||||
try { ws.terminate(); } catch {}
|
||||
}, HERMES_PONG_TIMEOUT_MS);
|
||||
}, HERMES_PING_MS);
|
||||
};
|
||||
ws.on("open", () => {
|
||||
state.ready = true;
|
||||
hermesEmitStatus();
|
||||
ws.send(JSON.stringify(["REQ", state.subId, { kinds: [1059], "#p": [hermesState.pkHex] }]));
|
||||
schedulePing();
|
||||
});
|
||||
ws.on("pong", () => {
|
||||
if (state.pongTimer) { clearTimeout(state.pongTimer); state.pongTimer = null; }
|
||||
});
|
||||
ws.on("message", async (buf) => {
|
||||
let msg; try { msg = JSON.parse(buf.toString()); } catch { return; }
|
||||
|
|
@ -10775,6 +10813,7 @@ async function hermesConnectRelay(url) {
|
|||
});
|
||||
ws.on("close", () => {
|
||||
state.ready = false;
|
||||
clearPingers();
|
||||
hermesEmitStatus();
|
||||
// Attempt reconnect if we're still supposed to be running.
|
||||
if (hermesState && hermesState.relays.get(url) === state) {
|
||||
|
|
@ -10790,6 +10829,8 @@ async function hermesConnectRelay(url) {
|
|||
function hermesTeardown() {
|
||||
if (!hermesState) return;
|
||||
for (const state of hermesState.relays.values()) {
|
||||
if (state.pingTimer) { try { clearInterval(state.pingTimer); } catch {} state.pingTimer = null; }
|
||||
if (state.pongTimer) { try { clearTimeout(state.pongTimer); } catch {} state.pongTimer = null; }
|
||||
try { state.ws?.close(1000); } catch {}
|
||||
}
|
||||
hermesState = null;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "theseus-navigator",
|
||||
"version": "0.3.111",
|
||||
"version": "0.3.112",
|
||||
"license": "MPL-2.0",
|
||||
"description": "Theseus Navigator — a browser that follows the thread. By Silent Mode, a Deviant project.",
|
||||
"author": "Silent Mode",
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@
|
|||
<div class="spacer"></div>
|
||||
<div class="icons" id="icons"></div>
|
||||
<div class="spacer"></div>
|
||||
<button class="icon llm" id="llmBtn" hidden><span class="glyph" id="llmGlyph">✨</span><span class="tt" id="llmTip">AI</span><button class="chev" id="llmChev" title="Pick AI engine">▾</button></button>
|
||||
<button class="icon llm" id="llmBtn" hidden><span class="glyph" id="llmGlyph">✨</span><span class="tt" id="llmTip">AI</span><span class="chev" id="llmChev" role="button" tabindex="0" aria-label="Pick AI engine" title="Pick AI engine">▾</span></button>
|
||||
<button class="icon gear" id="gearBtn" title="Sidebar settings"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg><span class="tt">Sidebar settings</span></button>
|
||||
</div>
|
||||
<script src="strip-icons.js"></script>
|
||||
|
|
@ -91,6 +91,9 @@
|
|||
const r = llmBtn.getBoundingClientRect();
|
||||
Q && Q.llmMenu && Q.llmMenu({ x: r.left, y: r.top, width: r.width, height: r.height });
|
||||
});
|
||||
llmChev.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter" || e.key === " ") { e.preventDefault(); e.stopPropagation(); const r = llmBtn.getBoundingClientRect(); Q && Q.llmMenu && Q.llmMenu({ x: r.left, y: r.top, width: r.width, height: r.height }); }
|
||||
});
|
||||
llmChev.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
const r = llmBtn.getBoundingClientRect();
|
||||
|
|
|
|||
|
|
@ -887,6 +887,14 @@
|
|||
<div class="txt"><div class="t">Start the wallet at launch</div><div class="d" id="walletAtLaunchHint">Aegis is ready the moment a site asks for it, at the cost of a slower start.</div></div>
|
||||
<label class="sw"><input type="checkbox" id="walletAtLaunch"><span class="track"><span class="knob"></span></span></label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="txt"><div class="t">Start Pithos at launch</div><div class="d">Pithos boots its control server for the Sia S3 gateway right away. Off: it wakes on first panel open or when you visit a pithos.sia page.</div></div>
|
||||
<label class="sw"><input type="checkbox" id="pithosAtLaunch"><span class="track"><span class="knob"></span></span></label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="txt"><div class="t">Connect Hermes relays at launch</div><div class="d">Open your Nostr relays shortly after launch so Hermes messages arrive before you open the panel. Off: relays connect the first time you open Messages.</div></div>
|
||||
<label class="sw"><input type="checkbox" id="hermesAutoRestore"><span class="track"><span class="knob"></span></span></label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="txt"><div class="t">Preload common menus</div><div class="d">After the first page loads, get address suggestions, the link preview and site info ready in the background so they open instantly. Off saves memory; each opens a moment slower the first time.</div></div>
|
||||
<label class="sw"><input type="checkbox" id="preloadMenus"><span class="track"><span class="knob"></span></span></label>
|
||||
|
|
@ -1416,7 +1424,7 @@
|
|||
}
|
||||
onDemand.addEventListener("change", syncWallet);
|
||||
$("lightStart").addEventListener("click", () => {
|
||||
const want = { extensionsOnDemand: true, walletAtLaunch: false, preloadMenus: false };
|
||||
const want = { extensionsOnDemand: true, walletAtLaunch: false, pithosAtLaunch: false, hermesAutoRestore: false, preloadMenus: false };
|
||||
for (const [k, v] of Object.entries(want)) { $(k).checked = v; C.set(k, v); }
|
||||
syncWallet();
|
||||
});
|
||||
|
|
@ -1424,7 +1432,7 @@
|
|||
// Running counts change as extensions start; refresh when the page is shown.
|
||||
document.addEventListener("visibilitychange", () => { if (!document.hidden) refresh(); });
|
||||
})();
|
||||
const TOGGLES = ["restoreSession", "backgroundThrottle", "freezeBackgroundTabs", "extensionsOnDemand", "walletAtLaunch", "preloadMenus", "blockCamera", "blockMicrophone", "hideMediaDevices", "blockScreenCapture", "gpc",
|
||||
const TOGGLES = ["restoreSession", "backgroundThrottle", "freezeBackgroundTabs", "extensionsOnDemand", "walletAtLaunch", "pithosAtLaunch", "hermesAutoRestore", "preloadMenus", "blockCamera", "blockMicrophone", "hideMediaDevices", "blockScreenCapture", "gpc",
|
||||
"clearCookiesOnQuit", "keepSignInsOnQuit", "clearCacheOnQuit", "clearStorageOnQuit", "clearHistoryOnQuit",
|
||||
"pwOfferSave", "pwOfferFill",
|
||||
"quickLinksShow", "translateAutoOffer",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue