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"),