toolbar 30% ratio floor + placeholder-safe search + brand map for .x names

Toolbar drag handle now clamps both bars to at least 30 % of the
.urlsearch budget (URL: 30 %–70 %, search fills the rest). The
existing absolute mins (URL 220 px, search bumped from 140 → 180 px
so the 'Search' placeholder always fits) still apply — the tighter of
absolute vs 30 %-of-container wins at any width. .urlsearch also gets
margin-right: 10 px so the search bar has visible breathing room from
the trailing dock (Downloads / extensions / ⛓ Theseus).

Bookmark brand-case now uses a canonical map for multi-word Silent Mode
names so all-caps sources come out correctly cased: SILENTMODE.X →
SilentMode.X, silentmode.x → SilentMode.X, coinspectrum.x →
CoinSpectrum.X. Single-word brands (Theseus, Sirius, Deviant, Aegis,
Ariadne, Argus, Hermes, Prometheus, Hephaestus, Helios, Atlas,
Katalogos, Game, Poutakidis, Syskypo) are in the same map for
consistency. Unknown names fall back to Title-case (foo.x → Foo.X)
— the ALL-CAPS preserve rule is gone, so GAME.X → Game.X now,
matching the user's ask.
This commit is contained in:
Local Dev 2026-09-09 02:25:12 +02:00
parent dcbe4d55f9
commit c64e81a959
2 changed files with 39 additions and 14 deletions

View file

@ -166,10 +166,16 @@
and vice versa) without moving anything else in the toolbar. When the and vice versa) without moving anything else in the toolbar. When the
window is too narrow for both, the search bar hides (see the window is too narrow for both, the search bar hides (see the
data-responsive rules below); the URL bar keeps working alone. */ data-responsive rules below); the URL bar keeps working alone. */
.urlsearch { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 6px; } /* margin-right creates a bit of breathing room between the search bar
and the trailing dock (Downloads, extensions, ⛓ Theseus) so they
never look like they collide. */
.urlsearch { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 6px; margin-right: 10px; }
.urlwrap { position: relative; flex: 0 1 var(--urlbar-w, 60%); min-width: 220px; display: flex; align-items: center; gap: 4px; background: var(--surface); border: 1px solid var(--line); .urlwrap { position: relative; flex: 0 1 var(--urlbar-w, 60%); min-width: 220px; display: flex; align-items: center; gap: 4px; background: var(--surface); border: 1px solid var(--line);
border-radius: 999px; padding: 0 6px 0 4px; } border-radius: 999px; padding: 0 6px 0 4px; }
.searchbox { flex: 1 1 0; min-width: 140px; } /* Search-box floor bumped so the "Search" placeholder always fits inside
the input — dropping below ~180px in normal font sizes was where the
placeholder text started getting clipped. */
.searchbox { flex: 1 1 0; min-width: 180px; }
/* Single drag handle sitting between the two — a thin 6px column with a /* Single drag handle sitting between the two — a thin 6px column with a
col-resize cursor. Zero-sum semantics: dragging right grows the URL col-resize cursor. Zero-sum semantics: dragging right grows the URL
bar and shrinks the search bar; dragging left does the inverse. */ bar and shrinks the search bar; dragging left does the inverse. */
@ -811,17 +817,24 @@
// that's surrounded by whitespace. The full title still shows on hover // that's surrounded by whitespace. The full title still shows on hover
// (tooltip below) so the descriptor isn't actually lost. // (tooltip below) so the descriptor isn't actually lost.
// //
// Brand-case normalisation for Silent Mode's .x TLD family: user's // Brand-case normalisation for Silent Mode's .x TLD family: sites save
// bookmark titles come in whatever case the site set (theseus.x saves // their title in whatever case they set (theseus.x, GAME.X, silentmode.x)
// as "theseus.x", game.x as "GAME.X"). Uniform on the chip: <Name>.X, // — the chip renders them uniform as <Name>.X. Titles are Title-case by
// but names that were already all-caps ("GAME") keep their form so // default; multi-word brand names (SilentMode, CoinSpectrum) have their
// they don't accidentally lose emphasis to Title-case. // canonical form in BRAND_CASE_X so they don't collapse to "Silentmode".
const BRAND_CASE_X = {
silentmode: "SilentMode", coinspectrum: "CoinSpectrum",
theseus: "Theseus", sirius: "Sirius", deviant: "Deviant", aegis: "Aegis",
ariadne: "Ariadne", argus: "Argus", hermes: "Hermes",
prometheus: "Prometheus", hephaestus: "Hephaestus", helios: "Helios",
atlas: "Atlas", katalogos: "Katalogos", game: "Game",
poutakidis: "Poutakidis", syskypo: "Syskypo",
};
function bookmarkBrandCase(label) { function bookmarkBrandCase(label) {
const m = label.match(/^([a-zA-Z][a-zA-Z0-9-]*)\.x$/i); const m = label.match(/^([a-zA-Z][a-zA-Z0-9-]*)\.x$/i);
if (!m) return label; if (!m) return label;
const name = m[1]; const key = m[1].toLowerCase();
const isAllCaps = name === name.toUpperCase() && /[A-Z]/.test(name); const cased = BRAND_CASE_X[key] || (key.charAt(0).toUpperCase() + key.slice(1));
const cased = isAllCaps ? name : (name.charAt(0).toUpperCase() + name.slice(1).toLowerCase());
return cased + ".X"; return cased + ".X";
} }
function bookmarkShortLabel(title, url) { function bookmarkShortLabel(title, url) {
@ -1476,7 +1489,14 @@
const handleEl = $("bardrag"); const handleEl = $("bardrag");
const urlWrapEl = barEl2.querySelector(".urlwrap"); const urlWrapEl = barEl2.querySelector(".urlwrap");
const urlsearchEl = $("urlsearch"); const urlsearchEl = $("urlsearch");
const MIN_URL = 220, MIN_SEARCH = 140, HANDLE_W = 6, GAP = 6; // Absolute minimums track the CSS min-widths so the drag can't push either
// bar below the size where its content stops fitting. On top of that we
// enforce a 30 % share floor for both bars: the URL bar can never take more
// than 70 % of the combined budget, and the search bar can never be less
// than 30 %. Combined with the CSS min-widths, the tighter of the two rules
// wins at any given container width.
const MIN_URL_PX = 220, MIN_SEARCH_PX = 180, HANDLE_W = 6, GAP = 6;
const MIN_SHARE = 0.30;
let dragging = false, startX = 0, startW = 0, lastW = 0; let dragging = false, startX = 0, startW = 0, lastW = 0;
handleEl.addEventListener("pointerdown", (e) => { handleEl.addEventListener("pointerdown", (e) => {
if (e.button !== 0) return; if (e.button !== 0) return;
@ -1490,8 +1510,13 @@
handleEl.addEventListener("pointermove", (e) => { handleEl.addEventListener("pointermove", (e) => {
if (!dragging) return; if (!dragging) return;
const total = urlsearchEl.getBoundingClientRect().width; const total = urlsearchEl.getBoundingClientRect().width;
const maxUrl = Math.max(MIN_URL, total - HANDLE_W - GAP * 2 - MIN_SEARCH); const minUrl = Math.max(MIN_URL_PX, Math.round(total * MIN_SHARE));
const w = Math.round(Math.max(MIN_URL, Math.min(maxUrl, startW + (e.clientX - startX)))); const maxUrl = Math.min(
total - HANDLE_W - GAP * 2 - Math.max(MIN_SEARCH_PX, Math.round(total * MIN_SHARE)),
Math.round(total * (1 - MIN_SHARE))
);
const clampedMax = Math.max(minUrl, maxUrl);
const w = Math.round(Math.max(minUrl, Math.min(clampedMax, startW + (e.clientX - startX))));
lastW = w; lastW = w;
barEl2.style.setProperty("--urlbar-w", w + "px"); barEl2.style.setProperty("--urlbar-w", w + "px");
if (typeof updateExtCollapse === "function") updateExtCollapse(); if (typeof updateExtCollapse === "function") updateExtCollapse();

View file

@ -1,6 +1,6 @@
{ {
"name": "theseus-navigator", "name": "theseus-navigator",
"version": "0.3.42", "version": "0.3.43",
"description": "Theseus Navigator — a browser that follows the thread. By Silent Mode, a Deviant project.", "description": "Theseus Navigator — a browser that follows the thread. By Silent Mode, a Deviant project.",
"author": "Silent Mode", "author": "Silent Mode",
"main": "main.js", "main": "main.js",