theseus/preload.js

93 lines
6.1 KiB
JavaScript
Raw Normal View History

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),
Theseus UX batch: tabs, address history, password autofill MVP, home cards Six user-visible improvements + supporting infra, all uncommitted from the earlier session-in-progress state. Ships together in one release. Chrome / tabs - Same-size tabs: flex 1 1 0 with max 200px, min 60px. Container gets overflow: hidden so many tabs shrink evenly instead of scrolling out. - Drag-and-drop tab reordering. HTML5 drag events on each .tab; drop side chosen by pointer x within target (Chrome UX). New move-tab IPC splices the tabs array + re-emits. Address bar - Persistent history at userData/history.json capped at 500 LRU. Ranked by host-prefix > url-prefix > contains > title-contains > recency. - Floating suggestions dropdown (addressPicker WebContentsView) anchored under the URL bar. Debounced 80ms input; ArrowUp/Down forward to the picker via address-cursor IPC; Enter fires goURL; blur closes after 160ms so click-through registers. New files address-picker.html + address-picker-preload.js. Cleared by existing clearHistoryOnQuit. Password autofill (A.2 MVP) - Green key chip in the address bar appears when the vault is UNLOCKED and the active tab's host has matching credentials (exact hostname match for phase 1; eTLD+1 upgrade queued as A.2.5). - Click chip → floating picker of usernames. Click a match → main.js runs a small script in the active tab: finds first visible input[type=password]:not([disabled]), walks the same form for a visible text/email/tel/url/search input whose name/id/autocomplete matches /username|user|email|login|account|id/, fills both via the native value setter + dispatches input/change so React/Vue-controlled inputs update. New files pw-fill.html + pw-fill-preload.js. - emitPwAvailability fires from pushNav + vault setup/unlock/lock so the chip's visibility + count stays accurate. Bookmarks bar - Right-click context menu on the favorites bar. On empty area: "Add current page" (or "Remove current page" if already saved). On a specific bookmark: "Open", "Edit title…" (prompt), "Remove", plus the add/remove-current entry. Uses a shared .ctxmenu style mirroring the settings ctxmenu (dark/light aware). - Empty-state text updated to mention right-click. Home page - Larger responsive card grid: auto-fill minmax(260-280px, 1fr) with breakpoints at 600/900/1200. Cards have a subtitle line, a colored badge (on-chain / Sia / server / custom), and edit affordances that reveal only in Edit mode. - User-editable set: Edit toggle reveals per-card ✎/✕ + a dashed "+ Add card" tile. Modal for add/edit with title / URL / subtitle / badge. Reset-to-defaults button. - Persisted at userData/home-cards.json. New home-preload.js exposes window.home = { getCards, setCards, resetCards, navigate }. IPC handlers in main.js validate sender.getURL() matches our own home.html — third-party pages see the API shape via the preload but can't act on the user's local cards. - Fallback set of 2 cards renders when window.home is unavailable (e.g. opening home.html directly outside Electron for preview) so the grid is never blank. Docs - TheseusNavigator/ROADMAP-identity-wallet.md — the phased plan for the two independent strands (password manager A.2/3, browser wallet B.1-6). Committed earlier this session; re-listed here for context. - TheseusNavigator/SESSION-PROMPT-identity-wallet.md — pastable kickoff for the next session picking up either strand. Files added to build.files: address-picker.html, address-picker-preload.js, pw-fill.html, pw-fill-preload.js, home-preload.js.
2026-08-17 02:17:12 +02:00
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)),
Theseus 0.0.4: cheap in-app update-check chip Bumps version so the chip actually surfaces itself on 0.0.3 installs (the version-newer check requires a strict semver bump — same-version rebuilds don't trigger the chip). From this release on, whenever the manifest names a newer Theseus, users get a one-click download. Mechanism - main.js checkForUpdate() fetches https://dl.silentmode.st/releases- manifest.json on startup (5s timeout, cache: no-store) + every 6h. Finds the theseus-navigator release, compares version to app.getVersion() with a numeric a.b.c comparator that handles "0.10.0 > 0.9.9" correctly. - On a match → stores { version, setupUrl, portableUrl, setupHash, portableHash, date } and emits update-available to chrome. Cleared after the user upgrades + relaunches (same-version → null). - Re-emits on chrome's did-finish-load in case the fetch beats the chrome view. Chip UI (chrome.html) - Acid-yellow pill between the downloads button and the Tor toggle: "↓ Update to X.Y.Z" + a ✕. Main body opens setupUrl in the system browser via shell.openExternal (origin-validated to https://dl.silentmode.st/ or https://silentmode.st/). ✕ dismisses for the current session — you'll see it again next launch if still behind. Trust anchor - No signing / no cryptographic verification of the download in this phase. releases.silentmode.bch publishes the SAME manifest URL, so users who want to verify can cross-check the manifest hash against what BCNR returns. The proper auto-updater with signature checks is the follow-on to this cheap version. Non-goals in phase 1 - No delta downloads; the user clicks and gets a full installer. - No auto-install; download → user runs the installer themselves. - No "check now" button in Settings; the periodic timer suffices. - No portable-vs-installed detection; the chip prefers setupUrl (the installer upgrades in place). Right-click for portable is future work.
2026-08-28 20:21:44 +02:00
// 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),
Ship Theseus 0.3.1 fe59105d (one-click updates: silent prefetch + install-and-restart) Setup fe59105d2e99a41b7000caeb86601a8e1675846d193e92204034669f5b368d60 Portable 1b6eda55b53894cf9889548116c7b6100888fb160edc84cc1592bb79f9d95b53 The update flow no longer asks the user to click Download. When checkForUpdate detects a newer version, autoDownloadUpdate() kicks off session.defaultSession.downloadURL against the setup URL immediately. will-download recognises the update URL and routes the file to a fixed %TEMP% path (bypassing the visible downloads panel entirely), streams updateDownloadReceived/Total into the chip via emitUpdateAvailable, and flips updateDownloadState to "ready" when the transfer finishes. Chip states: idle first render before the fetch starts — clickable to trigger the manual download (kept as a fallback). downloading "↓ 42% — 0.3.2" — no click, just progress. ready "✓ Install 0.3.2 & restart" — one click. failed fall back to the pre-0.3.1 explicit-download click. install-update-now IPC: spawns the cached setup with /S (detached, stdio ignored), then app.quit() 400ms later so the installer can overwrite the running exe. Our nsis/installer.nsh detects an existing Ariadne install via the HKLM registry and skips its Ariadne prompt on upgrades, so the /S run is fully unattended. The one-click flow eliminates two long-standing sources of confusion: - "Download opens a different browser" — Theseus's default session fetches the installer itself, not a URL handoff to shell. - "Update requires multiple wizard clicks" — /S skips them. Extensions aren't touched by this. The framework lives in addons-host.js + sidebar-preload.js; add-ons themselves live in %APPDATA%\Theseus Navigator\addons\<id>\ and are a separate layer. New extensions ship by drop-a-folder, no browser release required. Deployed: scp + sia-upload, verified 200 + 0.3.1 in the manifest.
2026-08-31 18:47:50 +02:00
installUpdateNow: () => ipcRenderer.invoke("install-update-now"),
Theseus 0.0.4: cheap in-app update-check chip Bumps version so the chip actually surfaces itself on 0.0.3 installs (the version-newer check requires a strict semver bump — same-version rebuilds don't trigger the chip). From this release on, whenever the manifest names a newer Theseus, users get a one-click download. Mechanism - main.js checkForUpdate() fetches https://dl.silentmode.st/releases- manifest.json on startup (5s timeout, cache: no-store) + every 6h. Finds the theseus-navigator release, compares version to app.getVersion() with a numeric a.b.c comparator that handles "0.10.0 > 0.9.9" correctly. - On a match → stores { version, setupUrl, portableUrl, setupHash, portableHash, date } and emits update-available to chrome. Cleared after the user upgrades + relaunches (same-version → null). - Re-emits on chrome's did-finish-load in case the fetch beats the chrome view. Chip UI (chrome.html) - Acid-yellow pill between the downloads button and the Tor toggle: "↓ Update to X.Y.Z" + a ✕. Main body opens setupUrl in the system browser via shell.openExternal (origin-validated to https://dl.silentmode.st/ or https://silentmode.st/). ✕ dismisses for the current session — you'll see it again next launch if still behind. Trust anchor - No signing / no cryptographic verification of the download in this phase. releases.silentmode.bch publishes the SAME manifest URL, so users who want to verify can cross-check the manifest hash against what BCNR returns. The proper auto-updater with signature checks is the follow-on to this cheap version. Non-goals in phase 1 - No delta downloads; the user clicks and gets a full installer. - No auto-install; download → user runs the installer themselves. - No "check now" button in Settings; the periodic timer suffices. - No portable-vs-installed detection; the chip prefers setupUrl (the installer upgrades in place). Right-click for portable is future work.
2026-08-28 20:21:44 +02:00
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"),
getSettings: () => ipcRenderer.invoke("settings-get"),
setSetting: (key, val) => ipcRenderer.invoke("settings-set", key, val),
onSettingsUpdate: (cb) => ipcRenderer.on("settings-update", (_e, d) => cb(d)),
// Find-in-page. main.js fires 'find-open' on Ctrl+F; chrome renderer
// owns the bar UI and drives findInPage / stopFindInPage via these
// wrappers. Match count / active ordinal comes back through onFindResult.
onFindOpen: (cb) => ipcRenderer.on("find-open", () => cb()),
findInPage: (query, opts) => ipcRenderer.invoke("find-in-page", query, opts || {}),
findStop: () => ipcRenderer.invoke("find-stop"),
onFindResult: (cb) => ipcRenderer.on("find-result", (_e, d) => cb(d)),
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),
updateBookmark: (url, patch) => ipcRenderer.invoke("bookmark-update", url, patch),
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)),
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)),
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
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),
tabGroupToggle: (color) => ipcRenderer.invoke("tab-group-toggle", color),
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
tabBookmark: (id) => ipcRenderer.invoke("tab-bookmark", id),
Ship Theseus 0.0.8: window.bcnr dApp API + eTLD+1 permission origins Merges a parallel session's work with the multi-source BNS story from 0.0.7. The dApp side (parallel session) -------------------------------- * bcnr-preload.js — installs `window.bcnr` on every page via contextBridge. Read-only surface: resolveName(name), isRegistered(name), getBcnrTlds(), getRecordVersion(name), plus getPermissionOrigin() for diagnostics. All Promises; a missing name returns null (not throw). No signing, no wallet unlock — that surface is designed but deliberately out of scope for 0.0.8 (see TheseusNavigator/DESIGN-integrated-wallet.md). * bcnr-origin.js — pure function that computes the eTLD+1 permission origin for a URL. ICANN suffixes via `psl` (same PSL Chromium uses, handles .co.uk / .github.io / etc); BNS names key off the on-chain TLD list so foo.wallet becomes a public suffix as soon as `wallet` appears there. Match browser cookie / MetaMask semantics: a grant on pay.merchant.com covers account.merchant.com but not evil.com. * dev/bcnr-selftest.js, dev/origin-selftest.mjs — self-tests, no I/O. * main.js wires bcnr-preload.js into session.defaultSession.setPreloads() so it runs BEFORE per-WebContentsView preloads; adds bcnr:* IPC handlers. * preload.js + chrome.html — small hooks so the shell picks up window.bcnr the same way regular content does. * package.json — psl dep, bcnr-preload.js/bcnr-origin.js in `files`. Also included ------------- * AriadneResolver/mobile/.../UpdateCheck.java — in-app update-check for the Android app; already active in the shipped 0.11 APK (build.ps1 -Recurse picked it up), formalising the source now. * TheseusNavigator/snapshots/bns-name-snapshot.json — refreshed bundled starter (73 beacon txs, root c37b8596…c54e414ba). * Site pages + manifest updated to point at 0.0.8. TheseusNavigator-Setup-0.0.8.exe 95.4 MB 21939743eafdfe8742a6b7c4b987bd2782384d7bc41289cb80a7e08019dc9f02 TheseusNavigator-0.0.8-portable.exe 92.7 MB 2aa429fe39dc0fa4ac040fc6d6eb31b0f890c8a83175c49fcb50f052c480d39d
2026-08-31 01:38:55 +02:00
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, open on a specific panel, close, or subscribe
// to state (visibility + which panel is active + the panel list). The
// extension dock in chrome.html renders one button per panel and calls
// openSidebar / closeSidebar accordingly.
Theseus: add-on framework MVP + Notepad reference add-on New subsystem for extending Theseus with folders on disk. Each add-on lives at <userData>/addons/<id>/ with an addon.json manifest and a CommonJS entry that exports activate(api). Nothing about a private add-on ships in the public installer - drop the folder, restart, it's live. Bundled reference add-ons ride in the packaged app under resources/bundled-addons/ and are seeded into <userData>/addons/ on first boot; the framework treats seeded and drop-in add-ons the same. Files: - addons-host.js Loader + api.registerSidebarPanel() + per- addon storage on <userData>/addons-data/. Kept at the CommonJS-scoped top level (lib/ is ESM-scoped via its own package.json). - sidebar-preload.js Runs in every sidebar panel. Exposes window.silentmode.storage.{get,set,all} + onVisibility. Main-side handlers derive the add-on id from the sender file:// URL, so a panel can only touch its own store. - bundled-addons/notepad/ Reference add-on: addon.json, index.js, note.html. Autosaving textarea with char / word count. main.js: - Extension point: sidebar-panel. One right-anchored WebContentsView (SIDEBAR_W=340) hosts the current panel; layout() shrinks the tab views by the sidebar width when visible. First registered panel wins for MVP; picker for multiple panels lands later. - initAddons() at app.whenReady(): seedBundledAddons, then AddonHost.discoverAndActivate. - IPC surface: sidebar-toggle / sidebar-open / sidebar-close / sidebar-state, addons-list / addons-set-enabled / addons-reveal / addons-open-dir / addons-reload, and origin-gated addon-storage-get/set/all. - Settings gains `disabledAddons: []` — off-toggled ids persist and the loader honours them without a restart (discoverAndActivate runs again on toggle). chrome.html: toolbar sidebar-toggle button, hidden until at least one add-on has registered a sidebar panel. settings.html: new "Add-ons" section under privacy. Lists installed add-ons with icon / name / version / description / capabilities; per-add-on enable/disable toggle + Show folder button; page-level Reload and Open add-ons folder buttons; warning note about the trust model. package.json: build.files gains sidebar-preload.js + addons-host.js. extraResources gains bundled-addons/ so the packaged app carries the reference notepad for the first-boot seed. Verified: `npm start` boots, addons-host discovers the notepad, activates it, registers one sidebar panel. Log confirms "1 installed, 1 enabled, 1 sidebar panels". Actual sidebar rendering + notepad UI need clicked-through validation on a real install. Not shipped yet - deploy still blocked on the fail2ban VPS SSH ban. Ships as 0.2.0 once SSH clears (this is a new subsystem, not a fix).
2026-08-31 13:51:08 +02:00
toggleSidebar: () => ipcRenderer.invoke("sidebar-toggle"),
openSidebar: (panelId) => ipcRenderer.invoke("sidebar-open", panelId),
closeSidebar: () => ipcRenderer.invoke("sidebar-close"),
Theseus: add-on framework MVP + Notepad reference add-on New subsystem for extending Theseus with folders on disk. Each add-on lives at <userData>/addons/<id>/ with an addon.json manifest and a CommonJS entry that exports activate(api). Nothing about a private add-on ships in the public installer - drop the folder, restart, it's live. Bundled reference add-ons ride in the packaged app under resources/bundled-addons/ and are seeded into <userData>/addons/ on first boot; the framework treats seeded and drop-in add-ons the same. Files: - addons-host.js Loader + api.registerSidebarPanel() + per- addon storage on <userData>/addons-data/. Kept at the CommonJS-scoped top level (lib/ is ESM-scoped via its own package.json). - sidebar-preload.js Runs in every sidebar panel. Exposes window.silentmode.storage.{get,set,all} + onVisibility. Main-side handlers derive the add-on id from the sender file:// URL, so a panel can only touch its own store. - bundled-addons/notepad/ Reference add-on: addon.json, index.js, note.html. Autosaving textarea with char / word count. main.js: - Extension point: sidebar-panel. One right-anchored WebContentsView (SIDEBAR_W=340) hosts the current panel; layout() shrinks the tab views by the sidebar width when visible. First registered panel wins for MVP; picker for multiple panels lands later. - initAddons() at app.whenReady(): seedBundledAddons, then AddonHost.discoverAndActivate. - IPC surface: sidebar-toggle / sidebar-open / sidebar-close / sidebar-state, addons-list / addons-set-enabled / addons-reveal / addons-open-dir / addons-reload, and origin-gated addon-storage-get/set/all. - Settings gains `disabledAddons: []` — off-toggled ids persist and the loader honours them without a restart (discoverAndActivate runs again on toggle). chrome.html: toolbar sidebar-toggle button, hidden until at least one add-on has registered a sidebar panel. settings.html: new "Add-ons" section under privacy. Lists installed add-ons with icon / name / version / description / capabilities; per-add-on enable/disable toggle + Show folder button; page-level Reload and Open add-ons folder buttons; warning note about the trust model. package.json: build.files gains sidebar-preload.js + addons-host.js. extraResources gains bundled-addons/ so the packaged app carries the reference notepad for the first-boot seed. Verified: `npm start` boots, addons-host discovers the notepad, activates it, registers one sidebar panel. Log confirms "1 installed, 1 enabled, 1 sidebar panels". Actual sidebar rendering + notepad UI need clicked-through validation on a real install. Not shipped yet - deploy still blocked on the fail2ban VPS SSH ban. Ships as 0.2.0 once SSH clears (this is a new subsystem, not a fix).
2026-08-31 13:51:08 +02:00
sidebarState: () => ipcRenderer.invoke("sidebar-state"),
onSidebarState: (cb) => ipcRenderer.on("sidebar-state", (_e, d) => cb(d)),
// Toolbar-menu (dropdown from an add-on's dock icon): dispatch the picked
// item id to the add-on's "menu-select" handler.
addonMenuSelect: (addonId, itemId) => ipcRenderer.invoke("addon-menu-select", addonId, itemId),
// Pop the dock-icon dropdown as a NATIVE menu. Renderer-side DOM popovers
// get clipped by chrome.html's own WebContentsView height (CHROME_H) and
// then covered by the tab view below it — a native Menu.popup escapes that
// layering entirely. Pass the button's viewport-rect so main can anchor.
toolbarMenuPopup: (addonId, rect) => ipcRenderer.invoke("toolbar-menu-popup", addonId, rect),
// Chrome listens so it can drop the "active" tint when the native menu
// dismisses (Esc, outside click, item picked — main fires all three).
onToolbarMenuClosed: (cb) => ipcRenderer.on("toolbar-menu-closed", () => cb()),
});