From b4cea7b83fd5b25864d135a59309375467a76650 Mon Sep 17 00:00:00 2001 From: Theseus Dev Date: Thu, 29 Oct 2026 14:46:29 +0200 Subject: [PATCH] Theseus 0.3.130: startpage sidebar fills the panel, searchbar stops auto-growing, Store Claude Desktop detected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Startpage sidebar is now a real left column that starts at the Theseus title's top edge and runs the full page height. The slot grid is 3 columns stretched to the full sidebar width, with enough rows to cover the sidebar vertically (count derived from the main column's height on each render, and re-derived on viewport resize). Empty cells sit as semi-transparent dashed outlines in the exact position a filled tile would occupy, so the grid reads as one uniform surface; any empty cell doubles as the "add link" surface and clicking it opens the Add-link modal with that cell pre-pinned, so new links land exactly where the click did instead of being pushed to the end. The dedicated "+" button is gone — the pin-location is the click target. A "Theseus v0.3.X — What's new" link appears under the search tip, pointing at silentmode.st/releases; version comes from a new getVersion exposed on window.home over the existing app-version IPC. Chrome toolbar: 0.3.128's "give the searchbox more room while typing" rule is removed — focusing the search field was pulling the drag handle with it, which made the URL / search ratio impossible to set by hand. The URL bar now caps at 75% of the pair's shared budget via a hard CSS max-width (so a wide urlBarWidthPx stored under the old 80% ceiling clamps on load without the user having to re-drag), and the drag floor matches at 25 % so the search field always keeps at least a quarter of the pair. Claude connector: bumped bundled-addons/claude-connector from 0.2.0 to 0.2.1. The clients.cjs Store-install detection and the panel-access work shipped in 0.3.129's bundled 0.2.0, but seedBundledAddons only overwrites the installed copy when the bundle is strictly newer, so every user with an existing 0.2.0 kept the stale pre-fix copy from %APPDATA%/Theseus/extensions/claude-connector/. The version bump surfaces the Store-install detection ("Claude Desktop not installed on this computer" was false on every Microsoft Store install) and the add-on-panel approvals split in the connector panel, both of which were already in-tree but gated behind the version check. --- PENDING.md | 82 ++++++- bundled-addons/claude-connector/addon.json | 2 +- chrome.html | 30 ++- home-preload.js | 4 + home.html | 268 ++++++++++++++------- package.json | 2 +- 6 files changed, 285 insertions(+), 103 deletions(-) diff --git a/PENDING.md b/PENDING.md index cf2aebc1..98acb1a7 100644 --- a/PENDING.md +++ b/PENDING.md @@ -8,7 +8,7 @@ what belongs to whom and coordinate. Naming convention: `YYYY-MM-DD:short-slug` for date-bounded work, or `parallel:` for work owned by a session that isn't the current one. -Last updated: 2026-10-28. +Last updated: 2026-10-29. --- @@ -16,7 +16,7 @@ Releases are no longer recorded here: the live version and hashes are in `../site/releases-manifest.json` and `https://dl.silentmode.st/releases-manifest.json`, and each release commit is `Theseus 0.3.x: ...` in git history. -## Staged for the next release (post-0.3.128) +## Staged for the next release (post-0.3.129) Queue of asks for the next cut. Items marked LANDED are edited in the working tree waiting for a bump + build. Items marked TODO still need the @@ -35,6 +35,84 @@ code change. is the only way through to installed users). `game.x` isn't resolving yet (no s3 pointer on-chain, no manifest); wait until the service is up before baking its description/favicon. +- **LANDED: Searchbox no longer auto-grows on focus; URL/search ratio + capped at 75/25.** Reverts the 0.3.128 "give the searchbox more room + while typing" rule — focusing the searchbox was pulling the drag handle + with it and making the ratio hard to set by hand. Changes in + [chrome.html](chrome.html): dropped the + `.urlsearch:has(.searchbox:focus-within) .searchbox { flex: 2 1 480px }` + rule and its `.14s` transition on `.searchbox`; added + `max-width: 75%` to `.urlwrap` so a stored `urlBarWidthPx` from the old + 80 % ceiling clamps on load without the user having to redrag; bumped + the drag `MIN_SHARE` 0.20 → 0.25 so new drags can't push the search + bar below 25 % of the shared budget. 0.3.128's typing-wider effect + stays off; the user owns the ratio again. +- **LANDED: Startpage sidebar runs the full page height alongside the + hero; slots fill the whole sidebar panel; empty cells double as the + "add link" surface.** [home.html](home.html): + - **Page layout restructured.** Hero + homepage are now wrapped in a + single `.page` grid (`grid-template-columns: minmax(240, 280) 1fr`) + with the sidebar ` +
+
+
+
+ + + + + + + +
+

Theseus Navigator

+
+

A browser that follows the thread. Names that live on decentralized blockchain.

+
+ + +
+

Tip: type hello.bch in the address bar above to open a decentralized site.

+ +
+
+
+
@@ -444,7 +481,7 @@ // hover of the grid. The "+" cell sits at the end so adding is always // one click away. const q = state.sections.q || []; - const slotCells = layoutSlots(q); + const slotCells = layoutSlots(q, minSlotsForFill()); const list = $("slotsList"); list.innerHTML = ""; slotCells.forEach((entry, cellIdx) => { @@ -466,15 +503,11 @@ } else { const empty = document.createElement("div"); empty.className = "slot-empty"; empty.dataset.cell = cellIdx; + empty.title = "Add link here"; wireEmptySlot(empty, cellIdx); list.appendChild(empty); } }); - const slotAdd = document.createElement("button"); - slotAdd.className = "slot-add"; slotAdd.textContent = "+"; slotAdd.title = "Add link"; - slotAdd.onclick = (e) => { e.preventDefault(); e.stopPropagation(); openModal("q", -1); }; - slotAdd.oncontextmenu = (e) => e.preventDefault(); - list.appendChild(slotAdd); // RIGHT — Silent Mode cards with sub-description under the title. const sm = state.sections.sm || []; @@ -502,11 +535,25 @@ $("ctxIconSizeVal").textContent = state.iconSize; } + // Compute how many slot rows fit the vertical space the sidebar covers. + // Sidebar height = main-col height (align-self: stretch on the aside), + // so basing the row count on main-col keeps the slot grid filling the + // whole sidebar panel instead of ending halfway down. Falls back to 24 + // (8 rows) when the layout hasn't been measured yet (first render). + function minSlotsForFill() { + const main = document.querySelector(".main-col"); + const h = main ? main.getBoundingClientRect().height : 0; + const rowH = 78; // .slot-empty height (68) + .slots-list row gap (10) + const rows = h > 0 ? Math.max(6, Math.floor(h / rowH)) : 8; + return rows * 3; + } // Lay out the q[] items into a fixed-slot grid: pinned items (with a // `slot: number`) go into that cell; unpinned items flow into the first // free cell, top-to-bottom. The grid always extends to at least one - // trailing empty row for drop space. - function layoutSlots(items, minSlots = 9) { + // trailing empty row for drop space. minSlots defaults to 18 (3 × 6) + // when nothing better is passed; render() passes minSlotsForFill() so + // the grid actually covers the whole sidebar height. + function layoutSlots(items, minSlots = 18) { const pinned = []; const unpinned = []; items.forEach((ic, i) => { const s = Number.isInteger(ic.slot) && ic.slot >= 0 ? ic.slot : null; @@ -618,6 +665,14 @@ if (!payload) return; pinItemToSlot(payload.sec, payload.i, cellIdx); }); + // Any empty cell doubles as the "add link" surface — click opens the + // link modal with this cell pre-pinned, so the user controls WHERE a + // new link lands (no dedicated + button needed). + el.addEventListener("click", (e) => { + e.preventDefault(); + openModal("q", -1, cellIdx); + }); + el.addEventListener("contextmenu", (e) => e.preventDefault()); } // Place the dragged item at `cellIdx` in the q grid, pinning it. @@ -839,8 +894,8 @@ // Add/edit modal — the same shape works for both left slots and right cards. let editKey = null; - function openModal(sec, i) { - editKey = { sec, i }; + function openModal(sec, i, pinSlot) { + editKey = { sec, i, pinSlot: Number.isInteger(pinSlot) ? pinSlot : null }; const isNew = i < 0; $("modalTitle").textContent = isNew ? "Add link" : "Edit link"; const c = isNew ? { title: "", url: "", sub: "", color: "#d6ff3d", letter: "" } @@ -869,8 +924,13 @@ letter: $("mLetter").value.trim() || null, }; const toSec = $("mSection").value; - if (!editKey || editKey.i < 0) state.sections[toSec].push(next); - else { + if (!editKey || editKey.i < 0) { + // Preserve the pinned cell index when the modal was opened from a + // specific empty slot — the new link lands exactly where the user + // clicked, not at the end. + if (editKey && editKey.pinSlot != null && toSec === "q") next.slot = editKey.pinSlot; + state.sections[toSec].push(next); + } else { const prev = state.sections[editKey.sec][editKey.i]; const merged = { ...prev, ...next, brand: prev.brand || null }; if (toSec === editKey.sec) state.sections[toSec][editKey.i] = merged; @@ -895,7 +955,7 @@ r.readAsDataURL(f); }; - // Search form (slot-add button is rendered inside slotsList per render()) + // Search form (empty cells themselves act as "add link" surfaces now; // Select-all on first click of the search field so a click lands with any // existing query ready to replace. Second click (already-focused) falls // through to native cursor placement. @@ -917,6 +977,22 @@ if (name) $("q").placeholder = "Search the web with " + name; }).catch(() => {}); } + // Running-version footer: "Theseus v0.3.X — What's new ↗". The link uses + // home.navigate so it opens release notes in this tab, same as any other + // link on the startpage. Hidden until the version resolves — kept off the + // page if the IPC isn't available (shouldn't happen in-app, but safe). + if (window.home && window.home.getVersion) { + window.home.getVersion().then((v) => { + if (!v) return; + const line = $("verLine"), a = $("verLink"); + a.textContent = "Theseus v" + v + " — What’s new ↗"; + a.addEventListener("click", (e) => { + e.preventDefault(); + window.home.navigate("https://silentmode.st/releases"); + }); + line.hidden = false; + }).catch(() => {}); + } // Container-level drop handlers accept cross-column drops onto empty space. wireContainer($("slotsList"), "q"); @@ -928,13 +1004,33 @@ let loaded = null; try { if (window.home && window.home.getCards) loaded = await window.home.getCards(); } catch {} state = ensureShape(loaded) || defaultState(); - applyBg(); render(); + applyBg(); + render(); + // The first render runs before the main column has laid out, so the + // row count comes from the fallback (24). Re-render once the layout + // settles so the slot grid fills the sidebar vertically. + requestAnimationFrame(() => render()); if (window.home && window.home.onCards) { window.home.onCards((data) => { const next = ensureShape(data); if (next) { state = next; applyBg(); render(); } }); } + // Resize the slot grid when the viewport changes so new rows appear + // (or drop off) to match the sidebar's new height. ResizeObserver on + // the main column catches both window resizes and content-driven + // height changes (e.g. new cards being added). + const main = document.querySelector(".main-col"); + if (main && window.ResizeObserver) { + let lastRows = -1, t = null; + const ro = new ResizeObserver(() => { + const rows = Math.floor(minSlotsForFill() / 3); + if (rows === lastRows) return; + lastRows = rows; + clearTimeout(t); t = setTimeout(() => render(), 80); + }); + ro.observe(main); + } })(); diff --git a/package.json b/package.json index e310ccd9..ec3d0c2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "theseus-navigator", - "version": "0.3.129", + "version": "0.3.130", "license": "MPL-2.0", "description": "Theseus Navigator — a browser that follows the thread. By Silent Mode, a Deviant project.", "author": "Silent Mode",