theseus/sidebar-preload.js

68 lines
3.3 KiB
JavaScript
Raw Normal View History

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
// Preload shared by every add-on sidebar panel. Exposes a small, safe
// surface to the add-on's HTML. Main-side handlers derive the add-on
// identity from the sender's URL (the panel is always loaded from
// somewhere inside <userData>/addons/<id>/), so a random page that
// happens to see the API shape can't touch another add-on's storage.
const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld("silentmode", {
storage: {
get: (key, fallback = null) => ipcRenderer.invoke("addon-storage-get", key, fallback),
set: (key, value) => ipcRenderer.invoke("addon-storage-set", key, value),
all: () => ipcRenderer.invoke("addon-storage-all"),
},
// Ask main which panel is currently visible — panels may want to
// suspend expensive work when hidden.
onVisibility: (cb) => ipcRenderer.on("sidebar-visibility", (_e, visible) => cb(!!visible)),
// Call into the add-on's activate() context: resolves with whatever the
// matching api.onMessage handler returned (or rejects with its error).
invoke: (msg, payload) => ipcRenderer.invoke("addon-msg", String(msg), payload),
// Events the add-on pushes via api.emit while this panel is open.
on: (msg, cb) => ipcRenderer.on("addon-event", (_e, name, payload) => { if (name === msg) cb(payload); }),
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
});
Ship Theseus 0.2.2 972f6209 (Extensions rename + draggable sidebar + session-proxy) Setup 972f6209639122f32f032d5f2f9fc5a4808e0d4a810f88ae38ec9a1275ac52ac Portable a9771f054ec36b9aff6ddee958cecf7e84cdacd4d7932aa8385c445aab4d29be User-visible rename: the Settings tab and its labels say "Extensions" now instead of "Add-ons". Internal identifiers (disabledAddons, the addons/ folder, IPC channels, capability strings) stay put — code churn wasn't worth it, and users only see the user-facing text. Draggable sidebar. sidebar-preload.js now injects a 5px grip strip along the LEFT edge of every panel document. mousedown+mousemove streams delta-x px to main via sidebar-drag IPC; main clamps to [200, 800] and debounces a save to settings.sidebarWidth. Width is restored on next launch. The default is still 340. Faint acid-green highlight on hover so the affordance is discoverable. New extension capability: session-proxy. An extension whose addon.json declares "session-proxy" gets api.setSessionProxy(rules) which routes to session.defaultSession.setProxy — the same primitive Tor already uses under the hood. Rules can be a string ("socks5://host:port") or an object matching Electron's setProxy shape; null clears. The capability is opt-in: an extension without the declaration gets an error if it tries to call setSessionProxy. This is the framework surface a private 3-VPS relay extension would build on (extension folder stays on the operator's disk only; nothing about it appears in the public build). Deployed: scp installers + manifest + tools/ + releases/ pages to VPS, sia-upload of both trees, verified HEAD 200 and manifest 0.2.2.
2026-08-31 16:00:34 +02:00
// Panel picker strip was here — a 32-px tab bar injected at the top of
// every panel to switch between registered extensions. Removed: the
// toolbar extension dock (per-extension buttons + puzzle dropdown at
// narrow widths) is the canonical switcher now, and doubling that inside
// the sidebar wasted vertical space and made narrow panels feel cramped.
Ship Theseus 0.2.3 3abaeff7 (new brand icon + multi-panel picker + Startpage default) Setup 3abaeff73afcecc9e4f05f749765e168a97a377890211dbae4345654c84ce2f1 Portable 71017563d6ba107cb25e24be240abcb78e34491d8f7aa42e095421edbc482a85 Brand: the red-N compass from theseus.x is now the taskbar / titlebar / File Explorer icon everywhere. Source SVG lives at site-theseus-x/assets/favicon.svg so brand + browser icon stay in sync. nsis/make-icons.mjs renders it to build/icon.png (512x512) and build/icon.ico (multi-resolution: 16/24/32/48/64/128/256). Wired into package.json: build.win.icon, build.nsis.installerIcon + uninstaller + header; the ico ships as an extraResource so main.js's BrowserWindow also uses it at runtime. Icons live in build/ which is gitignored - run `node nsis/make-icons.mjs` when the SVG changes. Sidebar: multi-panel picker strip. When 2+ extensions register sidebar panels, sidebar-preload.js injects a 32-px tab strip at the top of every panel's document. Click a tab -> ipcRenderer sidebar-open -> loadFile switch. Preload also injects box-sizing:border-box + a 33-px body padding so height:100% panels don't overflow. Solo-panel case is unchanged (strip only appears when panels.length >= 2). Search: Startpage is the new default. Both the default id and the enabled-list ordering put it first. DDG stays enabled by default too. Deploy: scp installers + manifest + tools/ + releases/ to VPS, sia-upload of both trees, verified HEAD 200 and manifest 0.2.3. Icons regenerated from theseus.x's favicon.svg; the .svg itself shipped in the 0.1.x window when the theseus.x site went live.
2026-08-31 16:32:16 +02:00
Ship Theseus 0.2.2 972f6209 (Extensions rename + draggable sidebar + session-proxy) Setup 972f6209639122f32f032d5f2f9fc5a4808e0d4a810f88ae38ec9a1275ac52ac Portable a9771f054ec36b9aff6ddee958cecf7e84cdacd4d7932aa8385c445aab4d29be User-visible rename: the Settings tab and its labels say "Extensions" now instead of "Add-ons". Internal identifiers (disabledAddons, the addons/ folder, IPC channels, capability strings) stay put — code churn wasn't worth it, and users only see the user-facing text. Draggable sidebar. sidebar-preload.js now injects a 5px grip strip along the LEFT edge of every panel document. mousedown+mousemove streams delta-x px to main via sidebar-drag IPC; main clamps to [200, 800] and debounces a save to settings.sidebarWidth. Width is restored on next launch. The default is still 340. Faint acid-green highlight on hover so the affordance is discoverable. New extension capability: session-proxy. An extension whose addon.json declares "session-proxy" gets api.setSessionProxy(rules) which routes to session.defaultSession.setProxy — the same primitive Tor already uses under the hood. Rules can be a string ("socks5://host:port") or an object matching Electron's setProxy shape; null clears. The capability is opt-in: an extension without the declaration gets an error if it tries to call setSessionProxy. This is the framework surface a private 3-VPS relay extension would build on (extension folder stays on the operator's disk only; nothing about it appears in the public build). Deployed: scp installers + manifest + tools/ + releases/ pages to VPS, sia-upload of both trees, verified HEAD 200 and manifest 0.2.2.
2026-08-31 16:00:34 +02:00
// Sidebar resize grip. Injected into every panel automatically so panel
// authors don't have to reinvent it. A thin strip along the LEFT edge
// (the boundary between the tab area and the sidebar) accepts mousedown
// and streams drag deltas to main until mouseup. Main clamps the width
// to [200, 800] and persists it in settings.sidebarWidth.
window.addEventListener("DOMContentLoaded", () => {
const grip = document.createElement("div");
grip.setAttribute("aria-label", "Resize sidebar");
grip.style.cssText = [
"position:fixed", "left:0", "top:0", "bottom:0",
"width:5px", "cursor:col-resize", "z-index:2147483647",
"background:transparent",
].join(";");
// A faint highlight on hover so the affordance is visible.
grip.addEventListener("mouseenter", () => { grip.style.background = "rgba(214,255,61,.20)"; });
grip.addEventListener("mouseleave", () => { if (!dragging) grip.style.background = "transparent"; });
document.body.appendChild(grip);
let dragging = false;
grip.addEventListener("mousedown", (e) => {
if (e.button !== 0) return;
e.preventDefault();
dragging = true;
document.body.style.userSelect = "none";
grip.style.background = "rgba(214,255,61,.35)";
});
window.addEventListener("mousemove", (e) => {
if (!dragging) return;
// Moving cursor LEFT = grow sidebar width. movementX is negative left.
if (e.movementX !== 0) ipcRenderer.invoke("sidebar-drag", -e.movementX);
});
const stop = () => {
if (!dragging) return;
dragging = false;
document.body.style.userSelect = "";
grip.style.background = "transparent";
};
window.addEventListener("mouseup", stop);
window.addEventListener("mouseleave", stop);
});