diff --git a/chrome.html b/chrome.html index fee3a5a..af1b1bf 100644 --- a/chrome.html +++ b/chrome.html @@ -147,13 +147,28 @@ /* address bar. flex:1 by default consumes remaining space; user can cap it via Settings > Appearance > Address bar size so the extension dock gets more room. min-width guards against squeezing the URL invisible. */ - .urlwrap { flex: 1 1 auto; min-width: 200px; display: flex; align-items: center; gap: 4px; background: var(--surface); border: 1px solid var(--line); + .urlwrap { position: relative; flex: 1 1 auto; min-width: 200px; display: flex; align-items: center; gap: 4px; background: var(--surface); border: 1px solid var(--line); border-radius: 999px; padding: 0 6px 0 4px; } - .bar[data-urlsize="medium"] .urlwrap { flex: 0 1 640px; } - .bar[data-urlsize="compact"] .urlwrap { flex: 0 1 400px; } + /* Capped URL bar: pin the trailing items (download / extensions / logo / + Theseus) to the RIGHT edge instead of packing them next to the URL bar + — a smaller address bar shouldn't slide everything leftward. margin- + right: auto on the URL bar consumes the slack. */ + .bar[data-urlsize="medium"] .urlwrap { flex: 0 1 640px; margin-right: auto; } + .bar[data-urlsize="compact"] .urlwrap { flex: 0 1 400px; margin-right: auto; } + .bar[data-urlwidth] .urlwrap { flex: 0 0 var(--urlbar-w, auto); margin-right: auto; } + .searchbox { position: relative; } .bar[data-searchsize="hidden"] .searchbox { display: none; } .bar[data-searchsize="compact"] .searchbox { width: 180px; } .bar[data-searchsize="wide"] .searchbox { width: 400px; } + .bar[data-searchwidth] .searchbox { width: var(--searchbox-w, 300px); } + /* Drag-to-resize handles: a thin invisible strip on the trailing edge of + .urlwrap and the leading edge of .searchbox. col-resize cursor makes + the affordance visible; a faint acid tint on hover confirms it's live. */ + .urldrag, .searchdrag { position: absolute; top: 0; bottom: 0; width: 6px; cursor: col-resize; z-index: 10; background: transparent; } + .urldrag { right: -3px; } + .searchdrag { left: -3px; } + .urldrag:hover, .searchdrag:hover, + .urldrag.dragging, .searchdrag.dragging { background: rgba(214,255,61,.35); } /* Adaptive toolbar. A ResizeObserver on .bar sets data-responsive: "0" wide — everything visible (default) "1" tight — auto-hide the search box, min-width for URL bar drops @@ -339,8 +354,10 @@ +
@@ -1197,15 +1214,66 @@ syncHeight(); // Toolbar sizing: URL bar + search box widths follow user preference. // Applied as data-attrs on .bar so CSS handles the layout swap. + // urlBarWidthPx / searchBoxWidthPx (integers > 0) come from the drag + // handles; when present they OVERRIDE the discrete size preset. Set to + // null (or 0) to restore preset behavior. function applyBarSizes(s) { if (!s) return; const bar = $("bar"); if (s.urlBarSize) bar.dataset.urlsize = s.urlBarSize; if (s.searchBoxSize) bar.dataset.searchsize = s.searchBoxSize; + const uw = Number(s.urlBarWidthPx) || 0; + if (uw > 0) { bar.dataset.urlwidth = "1"; bar.style.setProperty("--urlbar-w", uw + "px"); } + else { delete bar.dataset.urlwidth; bar.style.removeProperty("--urlbar-w"); } + const sw = Number(s.searchBoxWidthPx) || 0; + if (sw > 0) { bar.dataset.searchwidth = "1"; bar.style.setProperty("--searchbox-w", sw + "px"); } + else { delete bar.dataset.searchwidth; bar.style.removeProperty("--searchbox-w"); } syncHeight(); } T.getSettings && T.getSettings().then(applyBarSizes); T.onSettingsUpdate && T.onSettingsUpdate(applyBarSizes); + + // Drag handles: right edge of .urlwrap resizes the URL bar; left edge of + // .searchbox resizes the search box. Persists to settings.urlBarWidthPx / + // searchBoxWidthPx on release, which then wins over the size preset. + function makeDragHandle(handleEl, targetEl, opts) { + let startX = 0, startW = 0, dragging = false, lastW = 0; + handleEl.addEventListener("pointerdown", (e) => { + if (e.button !== 0) return; + dragging = true; + startX = e.clientX; + startW = targetEl.getBoundingClientRect().width; + handleEl.setPointerCapture(e.pointerId); + handleEl.classList.add("dragging"); + e.preventDefault(); + }); + handleEl.addEventListener("pointermove", (e) => { + if (!dragging) return; + const dx = e.clientX - startX; + // Left-edge handles (search) grow when dragged LEFT (dx < 0). + const w = Math.round(Math.max(opts.min, Math.min(opts.max, startW + (opts.reverseX ? -dx : dx)))); + lastW = w; + opts.applyLive(w); + }); + const stop = (e) => { + if (!dragging) return; + dragging = false; + handleEl.classList.remove("dragging"); + try { handleEl.releasePointerCapture(e.pointerId); } catch {} + if (lastW > 0 && T.setSetting) T.setSetting(opts.settingKey, lastW); + }; + handleEl.addEventListener("pointerup", stop); + handleEl.addEventListener("pointercancel", stop); + } + const barEl2 = $("bar"); + makeDragHandle($("urldrag"), $("bar").querySelector(".urlwrap"), { + min: 200, max: 1800, settingKey: "urlBarWidthPx", reverseX: false, + applyLive: (w) => { barEl2.dataset.urlwidth = "1"; barEl2.style.setProperty("--urlbar-w", w + "px"); }, + }); + makeDragHandle($("searchdrag"), $("bar").querySelector(".searchbox"), { + min: 120, max: 800, settingKey: "searchBoxWidthPx", reverseX: true, + applyLive: (w) => { barEl2.dataset.searchwidth = "1"; barEl2.style.setProperty("--searchbox-w", w + "px"); }, + }); // Self-heal: some menu-close paths (drag interruptions, focus flips) can // leave the chrome view taller than the natural body-scrollHeight. Every // 750 ms with no menu open, re-sync — main.js only re-layouts when the diff --git a/main.js b/main.js index 3b0c865..694bbc0 100644 --- a/main.js +++ b/main.js @@ -230,6 +230,10 @@ const SETTINGS_DEFAULTS = { // room to grow. The search box is fixed-width; hidden removes it. urlBarSize: "wide", // compact | medium | wide (default) searchBoxSize: "normal", // hidden | compact | normal (default) | wide + // Drag-set widths in pixels. Non-zero → override the corresponding size + // preset; zero/null → follow the preset. Set by the toolbar drag handles. + urlBarWidthPx: 0, + searchBoxWidthPx: 0, }; // Applying the theme via nativeTheme.themeSource makes prefers-color-scheme update // in every renderer (chrome, settings, popover, page views) with no per-view IPC. diff --git a/preload.js b/preload.js index 6efd216..6dadc11 100644 --- a/preload.js +++ b/preload.js @@ -26,6 +26,7 @@ contextBridge.exposeInMainWorld("theseus", { 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)), toggleSiteInfo: (rect) => ipcRenderer.invoke("toggle-site-info", rect), switchToBcnr: () => ipcRenderer.invoke("switch-to-bcnr"),