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.
This commit is contained in:
parent
ee0548fec9
commit
49dde73c6b
4 changed files with 116 additions and 5 deletions
13
main.js
13
main.js
|
|
@ -83,7 +83,7 @@ const SEARCH_ENGINES = {
|
|||
};
|
||||
// Engines enabled by default (shown in the toolbar dropdown). The rest are in the
|
||||
// catalog and can be turned on from Settings. Custom + detected engines are always on.
|
||||
const DEFAULT_ENABLED = ["duckduckgo", "google", "brave", "bing", "startpage"];
|
||||
const DEFAULT_ENABLED = ["startpage", "duckduckgo", "google", "brave", "bing"];
|
||||
// DuckDuckGo's icon service reliably returns a favicon for ANY domain from one
|
||||
// privacy-respecting host — far more robust than guessing /favicon.ico per site.
|
||||
const faviconUrl = (domain) => (domain ? `https://icons.duckduckgo.com/ip3/${domain}.ico` : null);
|
||||
|
|
@ -203,7 +203,7 @@ const SETTINGS_DEFAULTS = {
|
|||
timezoneMode: "show", timezoneValue: "Europe/Berlin", // IANA zone for manual
|
||||
languageMode: "show", languageSpoof: "en-US", languageValue: "en-US", // spoof = top-10 pick, manual = free text
|
||||
locationMode: "hide", locationRegion: "europe", locationLat: "40.7128", locationLon: "-74.0060", // spoof by region, or manual coords
|
||||
searchEngine: "duckduckgo",// default search engine (built-in id or a custom id)
|
||||
searchEngine: "startpage",// default search engine (built-in id or a custom id)
|
||||
installedEngines: DEFAULT_ENABLED.slice(), // built-in engines added to the user's list (visible in Settings)
|
||||
enabledEngines: DEFAULT_ENABLED.slice(), // subset that's currently toggled on (shown in the toolbar dropdown)
|
||||
engineOrder: [], // user-defined display order of engine ids (empty = natural)
|
||||
|
|
@ -1759,7 +1759,14 @@ function closeTab(id) {
|
|||
}
|
||||
|
||||
function createWindow() {
|
||||
win = new BrowserWindow({ width: 1220, height: 840, title: "Theseus Navigator", backgroundColor: "#0f1420" });
|
||||
win = new BrowserWindow({
|
||||
width: 1220, height: 840, title: "Theseus Navigator", backgroundColor: "#0f1420",
|
||||
// Taskbar / titlebar icon. Packaged builds ship build/icon.ico as
|
||||
// extraResource; dev reads the source file directly.
|
||||
icon: app.isPackaged
|
||||
? path.join(process.resourcesPath, "icon.ico")
|
||||
: path.join(__dirname, "build", "icon.ico"),
|
||||
});
|
||||
chrome = new WebContentsView({ webPreferences: { preload: path.join(__dirname, "preload.js") } });
|
||||
win.contentView.addChildView(chrome);
|
||||
chrome.webContents.loadFile("chrome.html");
|
||||
|
|
|
|||
48
nsis/make-icons.mjs
Normal file
48
nsis/make-icons.mjs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// One-shot script: render site-theseus-x/assets/favicon.svg to the icon
|
||||
// files electron-builder wants — a 512x512 PNG (main icon) and a multi-
|
||||
// resolution .ico (Windows taskbar / installer / shortcut icon).
|
||||
//
|
||||
// Not in the runtime app bundle; run manually when the brand changes:
|
||||
// node nsis/make-icons.mjs
|
||||
// Outputs build/icon.png and build/icon.ico (both gitignored regenerated).
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import sharp from "sharp";
|
||||
import pngToIco from "png-to-ico";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
// SVG source lives in the theseus.x standalone site so the browser icon
|
||||
// and the site brand stay in lockstep.
|
||||
const SVG = path.join(__dirname, "..", "..", "site-theseus-x", "assets", "favicon.svg");
|
||||
// Icons drop into build/, which is gitignored (regenerated artifacts).
|
||||
// electron-builder picks them up from there via package.json.
|
||||
const OUT_DIR = path.join(__dirname, "..", "build");
|
||||
const PNG_512 = path.join(OUT_DIR, "icon.png");
|
||||
const ICO = path.join(OUT_DIR, "icon.ico");
|
||||
const TMP_DIR = path.join(OUT_DIR, ".icon-tmp");
|
||||
const ICO_SIZES = [16, 24, 32, 48, 64, 128, 256];
|
||||
|
||||
if (!fs.existsSync(SVG)) throw new Error("SVG not found at " + SVG);
|
||||
console.log("source:", SVG);
|
||||
|
||||
// Main 512x512 PNG for macOS / linux / electron-builder auto-derived Windows.
|
||||
await sharp(SVG).resize(512, 512).png().toFile(PNG_512);
|
||||
console.log("wrote", PNG_512);
|
||||
|
||||
// Write individual sized PNGs to a temp dir, then feed their paths to
|
||||
// png-to-ico so it preserves each as a distinct image inside the .ico.
|
||||
fs.mkdirSync(TMP_DIR, { recursive: true });
|
||||
const paths = [];
|
||||
for (const size of ICO_SIZES) {
|
||||
const p = path.join(TMP_DIR, `icon-${size}.png`);
|
||||
await sharp(SVG).resize(size, size).png().toFile(p);
|
||||
paths.push(p);
|
||||
}
|
||||
const ico = await pngToIco(paths);
|
||||
fs.writeFileSync(ICO, ico);
|
||||
console.log("wrote", ICO, `(${ICO_SIZES.length} sizes)`);
|
||||
|
||||
// Clean up the temp PNGs.
|
||||
for (const p of paths) fs.unlinkSync(p);
|
||||
fs.rmdirSync(TMP_DIR);
|
||||
12
package.json
12
package.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "theseus-navigator",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"description": "Theseus Navigator — a browser that follows the thread. By Silent Mode, a Deviant project.",
|
||||
"author": "Silent Mode",
|
||||
"main": "main.js",
|
||||
|
|
@ -97,9 +97,14 @@
|
|||
{
|
||||
"from": "bundled-addons",
|
||||
"to": "bundled-addons"
|
||||
},
|
||||
{
|
||||
"from": "build/icon.ico",
|
||||
"to": "icon.ico"
|
||||
}
|
||||
],
|
||||
"win": {
|
||||
"icon": "build/icon.ico",
|
||||
"target": [
|
||||
"nsis",
|
||||
"portable"
|
||||
|
|
@ -110,7 +115,10 @@
|
|||
"perMachine": false,
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"artifactName": "TheseusNavigator-Setup-${version}.${ext}",
|
||||
"include": "nsis/installer.nsh"
|
||||
"include": "nsis/installer.nsh",
|
||||
"installerIcon": "build/icon.ico",
|
||||
"uninstallerIcon": "build/icon.ico",
|
||||
"installerHeaderIcon": "build/icon.ico"
|
||||
},
|
||||
"portable": {
|
||||
"artifactName": "TheseusNavigator-${version}-portable.${ext}"
|
||||
|
|
|
|||
|
|
@ -15,12 +15,60 @@ contextBridge.exposeInMainWorld("silentmode", {
|
|||
onVisibility: (cb) => ipcRenderer.on("sidebar-visibility", (_e, visible) => cb(!!visible)),
|
||||
});
|
||||
|
||||
// -- Panel picker strip -----------------------------------------------------
|
||||
// Injected at the top of every panel document so the user can switch between
|
||||
// registered extension panels without a settings round-trip. Only appears
|
||||
// when 2+ panels exist. Main pushes the panel list here via IPC on the
|
||||
// initial sidebar-state event.
|
||||
async function installPickerStrip() {
|
||||
let state;
|
||||
try { state = await ipcRenderer.invoke("sidebar-state"); } catch { return; }
|
||||
const panels = (state && state.panels) || [];
|
||||
if (panels.length < 2) return;
|
||||
const strip = document.createElement("div");
|
||||
strip.setAttribute("aria-label", "Extension panels");
|
||||
strip.style.cssText = [
|
||||
"position:fixed", "top:0", "left:5px", "right:0", "height:32px",
|
||||
"display:flex", "align-items:stretch", "gap:1px",
|
||||
"background:rgba(11,14,20,.85)", "backdrop-filter:blur(8px)",
|
||||
"border-bottom:1px solid rgba(255,255,255,.10)",
|
||||
"z-index:2147483646", "user-select:none",
|
||||
"font:12px/1 system-ui,-apple-system,Segoe UI,Roboto,sans-serif",
|
||||
].join(";");
|
||||
const active = state.active;
|
||||
for (const p of panels) {
|
||||
const tab = document.createElement("button");
|
||||
tab.type = "button";
|
||||
const isActive = p.panelId === active;
|
||||
tab.style.cssText = [
|
||||
"flex:1", "min-width:0", "padding:0 10px", "border:0",
|
||||
"background:" + (isActive ? "rgba(214,255,61,.10)" : "transparent"),
|
||||
"color:" + (isActive ? "#d6ff3d" : "#8b98a9"),
|
||||
"cursor:pointer",
|
||||
"display:flex", "align-items:center", "gap:6px", "justify-content:center",
|
||||
"overflow:hidden", "text-overflow:ellipsis", "white-space:nowrap",
|
||||
"font:inherit",
|
||||
].join(";");
|
||||
tab.innerHTML = '<span>' + (p.icon || "🧩") + '</span><span>' + p.title.replace(/</g, "<") + '</span>';
|
||||
tab.addEventListener("click", () => ipcRenderer.invoke("sidebar-open", p.panelId));
|
||||
strip.appendChild(tab);
|
||||
}
|
||||
document.body.appendChild(strip);
|
||||
// Push the panel body down so the 32-px strip doesn't overlap the content.
|
||||
// box-sizing:border-box keeps 100%-tall panels from overflowing when we
|
||||
// add the padding.
|
||||
const style = document.createElement("style");
|
||||
style.textContent = "html,body{box-sizing:border-box}body{padding-top:33px!important}";
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
// 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", () => {
|
||||
installPickerStrip();
|
||||
const grip = document.createElement("div");
|
||||
grip.setAttribute("aria-label", "Resize sidebar");
|
||||
grip.style.cssText = [
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue