theseus/preload.js
Local Dev 5ea4515085 Ship Theseus 0.3.2 09331b2f (tab context menu + branded installer)
Setup    09331b2fd9ccf136e2183b7cd85354cfd56e2ed50260b7aadeed63c7ea450251
Portable 21752d0fc85fb39ec1e65192920461e9ae395a22d9a68abd27f12e638d0fdd07

Right-click a tab: floating context menu with Reload, Duplicate, Group
(submenu: None / Red / Orange / Yellow / Green / Cyan / Blue / Purple),
Add to Bookmarks, Mute (also Unmute; 🔇 shows next to the title when
muted), Close. Menus close on outside click or Escape.

Group state is per-tab. A grouped tab shows a colored dot before the
title and a matching 2-px accent stripe on the top edge, so a cluster
of same-group tabs reads visually. Palette is drawn from existing
provenance colors (err/warn/acid/srv/sia/blue).

Backend IPCs are all tab-scoped (not "active tab"): tab-reload,
tab-duplicate, tab-mute (toggle or explicit boolean), tab-group,
tab-bookmark. emitTabs payload gains muted, group, and url so the
menu can read current state.

Installer wizard branding: 164×314 sidebar BMP with the compass mark
centered + "Theseus / NAVIGATOR" wordmark under it, plus a 150×57
top-strip header with a mini compass on the right. Sharp can't write
BMP directly (only png/webp/etc), so nsis/make-icons.mjs renders raw
RGB via sharp and wraps it in a hand-rolled 24-bit uncompressed BMP
header. Uninstaller reuses the same sidebar.

Silent-install fix: nsis/installer.nsh's AriadnePageCreate now checks
IfSilent BEFORE touching nsDialogs::Create. In /S mode the flag is
zeroed and the function returns cleanly, so the installer no longer
hangs waiting for a page it will never draw. This is why 0.3.2 needed
two builds — the first hung on /S install; the fixed hash is the one
that ships.

Deployed: scp + sia-upload of both trees. Verified VPS hash matches
local 09331b2f. Fresh /S install to D:\Program Files\Theseus Navigator\
placed 0.3.2 with the correct HKCU Uninstall registry entry.
2026-08-31 19:30:30 +02:00

63 lines
3.9 KiB
JavaScript

const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld("theseus", {
navigate: (input) => ipcRenderer.invoke("navigate", input),
search: (q) => ipcRenderer.invoke("search", q),
newTab: () => ipcRenderer.invoke("new-tab"),
closeTab: (id) => ipcRenderer.invoke("close-tab", id),
switchTab: (id) => ipcRenderer.invoke("switch-tab", id),
moveTab: (id, targetId, place) => ipcRenderer.invoke("move-tab", id, targetId, place),
suggestAddress: (query, rect) => ipcRenderer.invoke("suggest-address", query, rect),
closeAddressPicker: () => ipcRenderer.invoke("close-address-picker"),
addressCursor: (dir) => ipcRenderer.invoke("address-cursor", dir),
togglePwFill: (rect) => ipcRenderer.invoke("toggle-pw-fill", rect),
onPwAvailability: (cb) => ipcRenderer.on("pw-availability", (_e, d) => cb(d)),
// Update chip: chrome subscribes to update-available; clicking the chip's
// download button opens the URL in the system browser; ✕ dismisses for
// the current session.
onUpdateAvailable: (cb) => ipcRenderer.on("update-available", (_e, d) => cb(d)),
openUpdateDownload: (url) => ipcRenderer.invoke("open-update-download", url),
installUpdateNow: () => ipcRenderer.invoke("install-update-now"),
dismissUpdate: () => ipcRenderer.invoke("dismiss-update"),
goHome: () => ipcRenderer.invoke("go-home"),
back: () => ipcRenderer.invoke("go-back"),
forward: () => ipcRenderer.invoke("go-forward"),
reload: (hard) => ipcRenderer.invoke("reload", !!hard),
stop: () => ipcRenderer.invoke("stop"),
toggleTor: () => ipcRenderer.invoke("toggle-tor"),
openSettings: () => ipcRenderer.invoke("open-settings"),
toggleSiteInfo: (rect) => ipcRenderer.invoke("toggle-site-info", rect),
switchToBcnr: () => ipcRenderer.invoke("switch-to-bcnr"),
setChromeHeight: (h) => ipcRenderer.invoke("set-chrome-height", h),
getSearchEngines: () => ipcRenderer.invoke("search-engines"),
setSearchEngine: (id) => ipcRenderer.invoke("set-search-engine", id),
addEngine: (eng) => ipcRenderer.invoke("add-engine", eng),
removeEngine: (id) => ipcRenderer.invoke("remove-engine", id),
toggleEnginePicker: (rect) => ipcRenderer.invoke("toggle-engine-picker", rect),
onEngines: (cb) => ipcRenderer.on("engines", (_e, d) => cb(d)),
getBookmarks: () => ipcRenderer.invoke("bookmarks-get"),
addBookmark: (bm) => ipcRenderer.invoke("bookmark-add", bm),
removeBookmark: (url) => ipcRenderer.invoke("bookmark-remove", url),
onBookmarks: (cb) => ipcRenderer.on("bookmarks", (_e, d) => cb(d)),
onNav: (cb) => ipcRenderer.on("nav", (_e, d) => cb(d)),
onTor: (cb) => ipcRenderer.on("tor", (_e, d) => cb(d)),
onTabs: (cb) => ipcRenderer.on("tabs", (_e, d) => cb(d)),
tabReload: (id) => ipcRenderer.invoke("tab-reload", id),
tabDuplicate: (id) => ipcRenderer.invoke("tab-duplicate", id),
tabMute: (id, on) => ipcRenderer.invoke("tab-mute", id, on),
tabGroup: (id, color) => ipcRenderer.invoke("tab-group", id, color),
tabBookmark: (id) => ipcRenderer.invoke("tab-bookmark", id),
onAddressPicked: (cb) => ipcRenderer.on("address-picked", (_e, url) => cb(url)),
onBcnrOffer: (cb) => ipcRenderer.on("bcnr-offer", (_e, d) => cb(d)),
// Collision-mode (BCNR ↔ ICANN) live switcher for the active tab
collisionSwitch: (arg) => ipcRenderer.invoke("collision-switch", arg),
collisionState: () => ipcRenderer.invoke("collision-state"),
// Downloads — the toolbar button subscribes to `downloads` to update its
// badge, and toggleDownloads opens/closes the floating panel.
getDownloads: () => ipcRenderer.invoke("downloads-get"),
toggleDownloads: (rect) => ipcRenderer.invoke("toggle-downloads", rect),
onDownloads: (cb) => ipcRenderer.on("downloads", (_e, d) => cb(d)),
// Add-on sidebar toggle in the toolbar.
toggleSidebar: () => ipcRenderer.invoke("sidebar-toggle"),
sidebarState: () => ipcRenderer.invoke("sidebar-state"),
onSidebarState: (cb) => ipcRenderer.on("sidebar-state", (_e, d) => cb(d)),
});