Two chips carried the same word in two shapes — a globe (Accept-Language)
and a translate chip (chip lights when page lang differs) — both labelled
"RU" at the same time for a Russian user. The chip for translation is
gone. The globe menu now covers both: a "Translate this page from X to Y"
item appears at the top when the loaded page is in another supported
language, flipping to "Show original" while a translation is on screen.
The chip's own code still shows the user's language (EN, RU, …); its
tooltip switches to "Translated to <X>. Menu: Show original." when a
translation is up, so the one chip reads the whole state.
With "Translate automatically" on, Theseus translates in place on
did-finish-load the first time it sees a supported source + target
mismatch for the active tab — no chip-click needed. A `_tr.autoTried`
latch keeps it to one attempt per document (a failing backend doesn't
retry on every reflow), and the latch resets on did-start-navigation so
the next page gets a fresh shot. The setting copy in Settings › Language
now says "Translate automatically" instead of "Offer to translate", so
the switch's label matches the behaviour.
The picker (both in Settings and in the globe menu) still lists every
language in WEBSITE_LANGUAGE_QUICK, but entries whose base code isn't
on the translator backend (en, es, fr, de, el, ru today) are shown
greyed out with "— translator coming later", and "Other… (Accept-Language
only, no translation)" is explicit about what free-form tags buy you.
The menu is a roadmap, not a lie: a user picking one of the greyed
entries sets Accept-Language and nothing else surprises them.
The Website-language setting only tells servers what the user prefers via
Accept-Language — many static sites (including names on BCDN) serve one
language and ignore it, so e.g. hello.bch loads in English for every user,
in every language. This adds a translator that converts the page's visible
text in place, so a Lithuanian user reads hello.bch in Lithuanian without
asking the server for anything.
The URL-bar grows a translate chip next to the website-language globe. The
chip lights up when the page's declared `<html lang>` differs from the
user's preferred language. Click it once to translate in place; click again
to revert — originals are kept in a renderer-local state slot and swapped
back without a reload. Right-click opens the chip menu (change target /
translator settings).
The engine lives behind a swappable adapter in main — this ships with the
LibreTranslate backend (POST /translate with {q, source, target, format}).
The endpoint defaults to the LibreTranslate public tier but is settable in
Settings › General › Translate pages, so a user with a self-hosted
LibreTranslate (or Silent Mode's own translate.silentmode.st once it is
up) swaps it there without a code change. On-device Bergamot (the WASM
engine Firefox Translations uses) will plug into the same adapter in a
later release — same contract (array of texts in, array of translations
out), the chip and revert path are already engine-agnostic.
The fetch goes through session.defaultSession.fetch so Tor and add-on
proxy rules apply uniformly, chunks the batch at ~3.8 KB per POST so a
large page spreads across several requests, times each one out at 45 s,
and reports a failure to the chip's tooltip so a dead endpoint reads as
such and not as a silent no-op. The injected walker skips SCRIPT / STYLE
/ CODE / PRE / NOSCRIPT / TEXTAREA and contentEditable subtrees, keeps a
reference to each text node and the original text, and reverts by
restoring from that pair.
A tab you switch away from is frozen (page lifecycle "frozen": no JS,
timers, network callbacks or media) one second later and thawed the moment
it is shown again. Right-click a tab → "Keep running in background" exempts
it (music, calls, dashboards); the strip marks it ▶. Dormant restored tabs
and tabs waiting on a page dialog are never frozen. Settings › Performance ›
"Stop tabs in the background" (on by default) turns it off. Freezing uses
the per-tab debugger applyFingerprint already keeps attached; Chromium only
freezes hidden pages.
The downloads, site-info and engine-picker popups close when focus moves
elsewhere in the window or to another app; the toolbar click that caused
that does not reopen them. Focus only moves into a popup while the window
is active — focusing it from the background blurs the window and closed the
popup it had just opened.
Also fixes a bug in the lazy overlays: isLoading() is still true while
did-finish-load is delivered, so a first show waited out the 4 s timeout
before appearing. Finished loads are now recorded explicitly.
- Resolved names are re-resolved when a newer index lands and evicted when
they drop out of it; an edited ip/s3/tls record, a transfer or an expiry
used to keep serving the old target until restart. The signed-DNS A
fallback follows its 30 s TTL instead of the first answer it ever saw.
- A cross-host navigation to a host the warm index knows is unregistered is
left to Chromium: replaying it via loadURL turned form POSTs (OAuth
form_post, SAML, 3-D Secure) into bodyless GETs. The site badge follows
navigations Chromium makes on its own.
- A background tab's alert/confirm no longer pulls its tab to the front; it
waits, marked in the tab strip, until the user switches to it. Dialogs in
other windows use the async box, so they no longer freeze every tab.
- Messages resolves sender keys from the browser's own index (one map per
index generation) instead of a full chain walk per unknown sender; the
dedupe set is bounded.
- Ariadne uninstall reads HKLM only and runs nothing but unins###.exe from
Program Files, elevated directly rather than via cmd /c.
- Tor and an add-on proxy no longer wipe each other's settings: Tor wins
while on, the add-on's rules come back when it goes off.
- Profile migration copies beside the target and renames it into place;
a failed copy keeps the old, complete profile instead of a partial one.
- Reload/DevTools/zoom shortcuts in app and link windows act on that window;
Ctrl+B stays with web pages (bold) and toggles the sidebar elsewhere.
- quickPanel comment corrected: it shares the default session on purpose.
Intl.DisplayNames.of("en-US") returns "American English", which spells out a
distinction the picker doesn't make — one row per language, with English the
UK original. Pass the base code to Intl so the chip tooltip, the "Automatic
(…)" label and the Settings hint all read as the plain language name
(English, Russian, Portuguese, Chinese) regardless of which regional variant
the OS or the saved setting happens to be.
Privacy › Location is three modes now: Show real, Hide, Manual. Manual reveals a
50-country dropdown whose pick becomes the coordinates navigator.geolocation
returns to pages — country-capital granularity, no regions or free-form cities.
Old profiles on the retired "Spoof (region)" auto-migrate to Manual + the
region's representative country on first open, so nothing breaks.
VPN row in Privacy stops opening the wrong add-on: the sidebar now no-ops on a
specific panelId that isn't registered (used to silently substitute panels[0],
which surfaced Aegis whenever the VPN add-on was disabled), and the row hides
itself when vpn:main isn't in the sidebar panel list.
Language picker (globe chip menu + Settings › General › Website language) drops
the BCP-47 tag from every visible label — the tag surfaces only as the 2-letter
chip in the URL bar once picked. "English" is the UK original; the US variant
row is retired (same 2-letter chip, ~same text). Ukrainian dropped from the
quick list too. "Automatic" reads as the OS language name (Intl.DisplayNames)
instead of a raw en-US style tag.
A globe chip next to the URL-bar star shows the language sites see you in
(Accept-Language + navigator.language) — "AUTO" while following the OS locale,
the two-letter code once you pin one. Click opens a 23-language menu; the same
setting has a friendly row at the top of Settings › General. Both write to the
existing languageMode/languageValue and stay in sync with the Anti-fingerprinting
Language row through a settings-update broadcast (settings.html and chrome.html
both react live).
Settings › Plug-ins is now two compact rows — one per plug-in — with the on/off
toggle on the right and the update controls beside it. Clicking a plug-in's title
opens its own sub-page (plugins/ariadne, plugins/aegis) with the full description
and the Uninstall button, so the main list stays scannable and dangerous actions
stop travelling with the everyday ones. The Ariadne toggle and its sub-page
mirror the same scheduled-task state.
Each chip was a fixed 150 px, so the newest ones ran out of the row
and out of sight. Chips now take the full width while there is room
and shrink together as more are saved, down to an icon and a few
letters, before the row overflows.
0.3.56 was cut from the Aegis line (WizardConnect auto-detection, Aegis
0.8.x, PDF Editor and VPN updates) on top of 0.3.55; 0.3.57 carries that
plus the install-as-app feature and the two main-process crash fixes.
Electron ships Chromium's renderer without the browser-side web-app
install machinery, so beforeinstallprompt never fires and every site's
own "Install our app" chip (coin-spectrum.com's, for one) stays hidden
in Theseus. The browser side now exists:
- webapps.js reads a page's <link rel="manifest">, accepts it when it
names an app with a standalone-style display mode and a start_url on
the page's origin, and records the descriptor on the tab.
- The address bar shows an install chip for such pages (filled once the
app is installed: click then opens or removes it); the page context
menu carries the same entry.
- Pages get a synthetic beforeinstallprompt whose prompt() routes to the
Theseus install dialog and resolves userChoice like Chrome, and an
appinstalled event afterwards, so sites' own chips appear and work.
- Installing stores the app under <userData>/webapps/, wraps the
manifest icon into an .ico, writes a Start Menu (optionally desktop)
shortcut that launches Theseus with --app=<start_url>, and opens the
app in a chromeless window with its own taskbar identity. The window
shares the session, BCNR resolution, fingerprint and add-on bridges
with tabs; popups and "open in Theseus" go to the browser window,
Alt+arrows / F5 / Ctrl+R cover navigation without a toolbar.
- Theseus takes the single-instance lock so a shortcut launch lands in
the running browser (second-instance) instead of a second profile
owner; launched cold, --app= opens only the app window and a later
plain launch brings the browser window back.
syncHeight queued its measurement in requestAnimationFrame, and frames
stop whenever the window is occluded — so a prompt closing, or a row
hiding, while another window covered Theseus left the chrome at its
old height until the window came back. A zero timeout coalesces the
same way and always runs. The open-prompt allowance now lives in
syncHeight itself, so any re-measure while a prompt is up keeps its
room, and the growth helper simply defers to it.
The chrome view is exactly as tall as its rows, and a fixed-position
prompt mask adds nothing to the height it reports, so any prompt taller
than the toolbar — the new two-field favorites form lost its title and
buttons — was clipped at the view's edge. The view now grows to fit the
open prompt and shrinks back when it closes.
The favorites row's right-click menu could only save the current page.
"Add a page…" opens a two-field form: the address (scheme optional,
must be a real host — Chromium's parser happily percent-encodes junk
like "not a url" into a bogus bookmark, so hosts are checked) and a
title that fills itself from the address, brand-cased like the chips,
until you edit it. Duplicates are refused; the site's /favicon.ico is
used for the chip.
Updates published after launch never showed: the add-on channel was
checked once, 30 s after boot, and staged copies only applied on the
next launch with nothing telling the user. On 2026-09-22 Aegis 0.8.3
and VPN 0.1.3 landed minutes after the app's only check and stayed
invisible through manual scans made earlier and a restart made before
they were published.
Now: the check repeats every 4 hours; every check (boot, timer, manual,
add-on-driven) reports what is staged to the chrome, which shows a chip
for staged extensions; clicking it, or the "Update to vX" button that
appears on the extension's row and detail in Settings, promotes the
staged folder over the installed one and rebuilds the add-on host, so
the new version runs without a restart. Plug-ins (Aegis) are excluded
from the chip and the hot swap — a wallet updates from its own panel
and applies on the next launch.
Also from the same review: the new-tab button follows the last tab and
parks after the scroll arrow only when the strip overflows; Tor sits
left of the Aegis chip; plug-ins no longer appear in Settings ›
Extensions (they have Plug-ins); the extension detail view has a
labelled Back button, a close button and a Check now button; the
promotion helper returns what it promoted and accepts a filter.
The extension buttons sat in registration order with no way to change
it, hide one, or switch an add-on off without opening Settings. Buttons
are now draggable (drop side follows the pointer, same feedback as tabs
and bookmark chips) and the order is persisted per profile. Right-click
opens a native menu: open/close the panel, move left/right, hide from
the toolbar, turn the add-on off, and Manage extensions; the dock's own
right-click offers Show hidden. Main owns the prefs (dockOrder,
dockHidden) and the actions, so the chrome only renders. Add-on
rediscovery now pushes a fresh dock state to the chrome, which it never
did before — turning an add-on off or installing one from a page
updates the toolbar at once.
Opening a PDF cost four clicks across two screens: the dock icon, a dropdown
with a single entry in it, an editor with an empty drop zone, and finally the
Open button that produced a file browser. Three of those were the program
asking the user to confirm what they had already said by clicking a PDF icon.
The dock item now raises the file browser itself. The dialog is native and
raised from the add-on's Node side, because a file:// page cannot open one
without a user gesture of its own and a freshly-opened tab has none to spend.
The chosen file goes through the same scratch handoff a right-clicked link
already used, so the editor opens with the document in it rather than with an
invitation to find one. Cancelling still lands in the empty editor, which is
where drag-and-drop works and is probably still where someone who changed
their mind about the file wants to be.
Second click removed in the chrome: a toolbar menu holding exactly one item is
not a menu, it is a button wearing a dropdown, so it now dispatches directly.
Falls through to the popup if the renderer has no addonMenuSelect.
Not yet exercised end to end — TheseusNavigator/node_modules is empty while
another session reinstalls it, so the CDP suites cannot boot Electron. Both
files parse; run scratchpad/verify-pdf-editor/drive.mjs once the tree is back.
The Tor chip was the widest control in the toolbar and competed with
the address bar for space. It now sits on the favorites row beside the
Aegis chip, under the Theseus menu, at the same chip height — the
network-level switch next to the wallet, both always visible. State
tints (off / connecting / on) are unchanged.
Aegis sat among the extension buttons and drifted as add-ons were
installed or removed. First-class Silent Mode components (manifest
category "plugin") now get a labelled chip at the right end of the
favorites row, directly below the Theseus menu — always visible, never
collapsed into the extensions overflow. The add-on host tags each
sidebar panel and toolbar menu with the category so the chrome can
split the two docks; the extensions dock keeps its behaviour.
Tabs keep a readable minimum width (76px). Once they overflow, the row
scrolls: the earliest tabs slide out on the left and an arrow at each end
moves the row by 60% of its width, disabled at its end of travel and
hidden while everything fits. The wheel scrolls the row too, the selected
tab is brought into view when the selection changes (never while the user
is scrolling), and the + button stays outside the row. Moves are immediate
rather than animated: the chrome view has no smooth scrolling, and frame
callbacks stop while the window is occluded, which stranded a frame-driven
slide at its start.
With many tabs open, the last ones slid under the minimise / maximise /
close overlay: the strip reserved that space as padding, and padding does
not stop overflowing flex items. The tabs now live in their own row that
clips at its own edge, shrink down to icon + close before anything is
hidden, and the row scrolls sideways with the wheel once they hit that
minimum, keeping the selected tab in view. The + button sits outside the
row so it stays reachable no matter how many tabs are open.
Holding Back or Forward for 450 ms (or right-clicking it) pops a native
menu of the tab's history entries in that direction — nearest first, up to
15, titled with the page title and host — and picking one jumps straight
to it. A hold swallows the click that would otherwise fire on release, so
a long press never also goes back one page.
- Address bar: the chrome view keeps document.activeElement on the URL
input after the user clicks into the page (focus moved to the tab's own
view), and the "don't clobber typed text" guard then froze the bar until
something blurred the field. The guard now requires real focus
(document.hasFocus()), and the field is blurred when the view loses focus.
- Tab strip: rebuilt with innerHTML on every tabs event, which recreated
every favicon <img> — a blink on the other tabs whenever one tab loaded,
reloaded or changed title — and dropped drag state mid-gesture. Elements
are now keyed by tab id (chips by group colour), updated in place, and
moved into order; the strip is never rebuilt.
- Settings › Extensions links to theseus.x/extensions. That page now has a
card per bundled add-on with the current signed version, tarball and
hash read from each add-on's updates.json at load (it still claimed
Screenshot 0.2.4 while the channel serves 0.6.5).
Three tab-strip changes from use:
- A tab opened from a link (target=_blank, middle-click, the context menu,
Duplicate) now goes right after the tab it came from — and after any
siblings that tab already opened — instead of at the end of the strip.
The + button, session restore and add-on requests still append.
- The selected tab gets an accent stripe and outline on top of its brighter
fill; with a dozen same-size tabs the fill alone was easy to lose. A
grouped tab keeps its group colour on the stripe.
- On Windows the tab row is the title bar: the native frame is hidden, the
minimise/maximise/close buttons are drawn as an overlay over the chrome
(colours follow the theme), the row is a drag region with every control
in it opted out, and 140px (or the overlay's real width when the API is
exposed) is kept clear on the right. The page gains the old title bar's
height. Other platforms keep the native frame.
A typed or pasted path such as D:\Dev\x\page.html has no dotted host, so
the URL-vs-search heuristic handed it to the search engine. Paths (drive,
UNC, file://, and absolute/~ on POSIX) now load as file:// URLs before the
heuristic runs. Local-file tabs keep their file:// URL in the address bar
(normally suppressed because our own home/error pages are file://), show a
"Local file" badge, and hide the registry button since no name resolution
is involved. A missing file lands on the error page with a matching badge.
- Per-tab page zoom on Chrome's ladder (25–500 %) via setZoomFactor, so
Chromium keys it per host: every tab on a site shares the level and it
persists across navigations and restarts. Ctrl +/=/numpad+ in,
Ctrl -/numpad- out, Ctrl 0 reset, Ctrl+wheel via zoom-changed. A
percentage chip appears in the address bar when a tab isn't at 100 %;
clicking it resets. Settings and add-on tabs never zoom.
- Address bar / search bar drag ratio floor lowered from 30 % to 20 %,
so the split runs 80/20 to 20/80 (pixel floors still apply).
- The collapsed extension-dock button and its dropdown printed a data:
URI icon as text ("data:image/svg+xml…"). One addonIconHtml() renderer
now serves the dock buttons, the collapsed button and the dropdown.
- Link-status pill: it measured its own width inside a view already
capped at 100 px, so it could never grow and long hrefs were cut short.
An off-screen twin now reports the natural width; main caps it to the
tab area (never under the sidebar) and the pill ellipsises past that.
- Address bar at narrow widths: the URL input's intrinsic minimum width
pushed the registry chips and the star out past the bar. #url now has
min-width: 0 and the trailing controls are fixed-size flex items.
- The BCDN/ICANN segmented chips are replaced by one Ariadne's Thread
icon (spiral + tail) at the end of the bar: acid when served from BCDN,
blue for ICANN, caret when the name exists on both. Click opens a
native menu (registry-menu-popup): switch registry, remember per name /
per TLD, forget choices, collision policy, and a jump to the Plug-ins
settings section. Reuses the existing switch / remember / policy paths
(collision-switch body extracted to switchRegistry, open-settings to
openSettingsTab). preload's openSettings now forwards a section slug.
Editor:
- Crop tool restored — drag to select, marquee sits with a dashed acid
border and a dimmed backdrop for the area you'll discard, then the
topbar shows Apply crop / Cancel. Applying trims #base to the rect,
resets undo (dimensions changed), and drops back into the select tool.
Enter / Esc keyboard shortcuts while a crop is pending.
- Blur / mosaic redaction tool back — drag a rectangle, editor
downsamples that region of #base to ~12-block granularity and paints
the blocks back nearest-neighbour. Commits directly (no confirm step).
- Sidebar-window controls (Back, name, Copy, Save, Sound, Maximize)
reflow: Back + name on the left, Copy + Save + Sound + Maximize on
the right so the "put the sidebar back to normal size" affordance
lives where users expect it. Toolbar's drawing tools stay centred.
- Back arrow icon swapped from a chevron to a proper flat arrow
(line + arrowhead), matching the new browser back/forward glyphs.
Sounds — modeled on Firefox Screenshots' feedback rather than beeps:
- Shutter is now a real photoshoot click: two mirror-slaps built from a
band-passed noise burst (metallic ping) plus a very short square-wave
thud each. Sounds like a camera, not a beep.
- Copy is a two-chirp "printer feed" — filtered noise burst on top of a
sine chirp per beat, staccato ascending pair. Same shape Firefox Easy
Screenshot uses for "copied to clipboard".
- Save keeps its ascending triad; Discard keeps its descending pair;
new small ascending pair for Apply crop.
Chrome:
- Browser Back / Forward chevrons (M10 3 L5 8 L10 13 — two segments
meeting at a point, no shaft) replaced with straight-arrow glyphs
(line + arrowhead). Reads as a navigation arrow, not an angle bracket.
Bundled but not shipped separately — parent session OTA-signs and pushes.
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.
Bookmark chip labels now normalise the .x TLD family to <Name>.X on
render:
theseus.x -> Theseus.X
deviant.x -> Deviant.X
Sirius.x -> Sirius.X
foo-bar.x -> Foo-bar.X
Names that were already ALL-CAPS keep their form so the visual weight
carries through:
GAME.X -> GAME.X (unchanged)
SILENTMODE.X -> SILENTMODE.X (unchanged)
Non-.x titles are untouched (CoinSpectrum, navigate.st, etc.). The
transformation runs after the descriptor trim, so titles like
'theseus.x — the browser…' also come out 'Theseus.X'.
Two visible fixes from the same 2026-09-09 screenshot:
Right-click a tab was building a DOM menu and then growing the chrome
view height so it would fit under the tabstrip. That opened a
visible gap between the toolbar and the tab body while the menu was
up. Now the tab context menu goes through a new IPC
"tab-context-menu-popup" (main.js) that pops an OS-native Menu at
the click point, floating above every WebContentsView — no layout
change, no gap. Preload exposes tabContextMenuPopup(id, {x, y});
chrome.html's tab contextmenu handler now calls it directly and the
DOM openTabContextMenu / openGroupSubmenu / growChromeForMenu path
is bypassed for tabs. (The bookmark bar's own ctxmenu still uses
the DOM path — its short 2-3-row menus don't grow chrome enough
to be visible.)
Bookmark chips were too small (130px max-width, 11px text, 22px row).
Now every chip is a fixed 150px × 28px cell so the row reads as a
uniform grid, 12.5px text, 14px favicon. Labels drop the descriptor:
"GAME.X — Bitcoin Cash game platform" renders as "GAME.X". The
trimmer splits on the first em-dash / en-dash / hyphen that's
surrounded by whitespace, so single-word titles and hyphenated
compound names ("Foo-Bar" with no spaces) come through intact. Full
title still shows on hover.
The two independent drag handles (right edge of URL bar, left edge of
search bar) are replaced with ONE handle sitting between them, wrapped
in a new .urlsearch flex container that owns the URL-bar-plus-search-bar
budget between the nav buttons on the left and the trailing dock on the
right.
Zero-sum semantics: dragging the handle right grows the URL bar and
shrinks the search bar; dragging left inverts. Nothing else in the
toolbar moves — nav buttons, Downloads, extension dock and the
⛓ Theseus logo all sit outside .urlsearch's flex parent.
When the window shrinks below the responsive breakpoint the search
bar (and the drag handle) hide as before, and the URL bar grows to
fill the .urlsearch budget on its own (added flex: 1 1 auto in the
data-responsive rules for .urlwrap).
Verified static rendering: url=700px → search=157px, url=300px →
search=539px, url=default → 60/40 split, narrow window → search
hidden and url fills.
Old settings (urlBarSize / searchBoxSize / searchBoxWidthPx) are
ignored by chrome.html but kept in main's SETTINGS_DEFAULTS so an
older chrome.html could still read them on a downgrade. Only
urlBarWidthPx is used going forward.
Same 0.3.31 version, new binary. Rebuilds the shipped 0.3.31 with the
salvageable content from the reverted 0.3.32-0.3.34 track:
chrome.html
- light-mode chrome strip: --bg #e6e8ec, inactive tab #f2f4f7,
active tab #ffffff. Fixes the "tabs disappear into the light
Windows title bar" report.
- bookmark chips shrunk: 130px max-width, 11px text, 12px favicon,
22px row (was 26). ~40% more chips fit in the same width.
- bookmark chips draggable with the tab-strip's left/right-half
drop convention; new .dropbefore/.dropafter accent.
- light-mode .tor + .logo + .upchip chips: from illegible white-
on-#253A49 (at 12-13px) to #eef1f5 with #253A49 ink. Both readable
now. .tor.connecting/.on keep amber/purple hue in light fills.
main.js
- will-download update handler now streams the saved setup .exe
through crypto.createHash("sha256"), compares to the manifest's
updateAvailable.setupHash before marking ready. Rejects and
deletes the file on mismatch or on empty manifest hash. Test C
in the previous session proved this catches truncated payloads
Electron reports as "completed" (a real class of failure the
Ariadne addon updater has always guarded against here).
- new bookmark-move IPC: splices the list, no-ops on self-drop
or missing entry.
preload.js
- moveBookmark(fromUrl, targetUrl, place) exposed for chrome.
Deliberately NOT changed: install-update-now still spawns setup with
["/S"] alone. The 0.3.32 --updated /S --force-run change was proven
in the previous session's real-install E2E to not address the actual
"browser vanished on D:\Program Files install" symptom — every flag
combination (/S alone, --updated /S --force-run, /S /currentuser,
/S /D=<install>) exits 0 without upgrading anything on that specific
install path. That's a separate open bug; not touched here.
Version stays 0.3.31 — this is a binary rewrite of 0.3.31, not a new
release. Existing 0.3.31 installs won't see an update chip (version
compare returns false), which is intentional given the auto-update
path is still broken for non-default install locations.
Bundled revert of six commits:
9bacdcb Ship 0.3.34
aef7864 0.3.34 UI (grey chrome, DnD bookmarks, chip colors)
c2da8bb Ship 0.3.33
9b92224 fix(updater) verify SHA-256
4e932e0 Ship 0.3.32
4e695c4 fix(updater) --updated flag
Reason: the real-install E2E test (turn N-1) showed silent update
against the user's custom D:\Program Files\Theseus Navigator location
returns exit 0 without upgrading anything — every flag combination
tried (/S alone, --updated /S --force-run, /S /currentuser,
/S /D=<install>) exits successfully and no files change. The 0.3.32
flag fix and 0.3.33 hash-verify fix were defensive improvements but
did not address the actual "browser vanished" symptom, and 0.3.34
inherits the same broken silent-update path so its UI improvements
never reach anyone still on 0.3.33+ with a non-default install path.
Rolling manifest back to 0.3.31 so the update chip stops appearing on
installs that can't complete the update. Users on the default
%LOCALAPPDATA%\Programs\theseus-navigator location are unaffected —
0.3.31 is still their canonical shipped version.
package.json reset to 0.3.31 explicitly (git revert stopped one bump
short on the merged diff).
Screenshot 0.3.0 (8ca5ac3) preserved — unrelated parallel work.
Chrome strip: light-mode --bg/--surface/--surface2/--active retuned to a
soft neutral grey. Flat #ffffff tabs disappeared into light Windows
title bars and the top strip read as one bright rectangle; now the
tab row sits on #e6e8ec, inactive tabs are #f2f4f7, active tab is
white so the current one still pops.
Bookmarks: chips down to 130px max-width, 11px text, 12px favicon,
tighter padding, 22px row (was 26). Fits ~40% more saved pages in
the same width. Chips are now draggable — dragstart/dragover/drop
mirror the tab strip's reorder pattern, dropping before/after by
which half of the target chip the pointer is on. IPC bookmark-move
splices the array; a missing entry or self-drop is a no-op.
Tor + Theseus (⛓ logo) chips in light mode: white-on-#253A49 was
below legible at 12-13px on real light Windows themes. Both now use
#eef1f5 with #253A49 ink and a subtle border — same footprint, dark
letters read cleanly. .tor.connecting/.on keep amber/purple hue but
in light-appropriate fills with dark text. .upchip picks up the
same treatment for consistency.
Two follow-ups from the on-device test.
Two Aegis addons showing up (bchwallet + aegis):
- migrateAegisRename previously only ran when addons/aegis/ didn't exist,
which meant any bchwallet copy the signed OTA update endpoint reinstalls
after the first migration stays there forever, and AddonHost loads both
as separate wallets. Rewritten to always retire addons/bchwallet/ when
it's present, regardless of whether aegis/ is already installed. The
storage-copy (bchwallet.json → aegis.json) still only runs the first
time so a downgrade doesn't clobber fresh 0.4+ state.
- Also flushes any stray addons/siawallet/ that comes back the same way.
Sidebar dock and Extensions list icons showed the raw 🛡 emoji:
- chrome.html's dock-button renderer and settings.html's extensions-list
renderer now accept `data:image/svg+xml…` values for manifest.icon and
render them as <img> instead of text. Emoji strings still render as
before.
- aegis addon.json's icon is now the exact hex-aspis mark from
aegis.x/brand/favicon.svg (URL-encoded inline). Version bumped to
0.4.1 so seedBundledAddons reseeds the new addon.json on next launch.
Standard browser Ctrl+F support:
- main.js: Ctrl+F caught in the shared before-input-event handler (same
place as F12 / reload shortcuts) fires 'find-open' to chrome.
Two new IPC handlers proxy to activeTab().view.webContents:
find-in-page(query, {forward, findNext, matchCase})
find-stop
Each tab's webContents listens for 'found-in-page' and forwards
{activeMatchOrdinal, matches, finalUpdate} back to chrome via
'find-result' — only when it's the active tab so the bar doesn't
update from a background tab's stale match count.
- preload.js: exposes onFindOpen / findInPage / findStop / onFindResult.
- chrome.html: adds a .findbar strip below the bookmarks bar (part of
chrome-view height like .tordisc / .bcnrbar, so syncHeight picks it
up). Input + "N of M" counter + prev/next/close buttons. Enter jumps
next, Shift+Enter previous, Esc closes. Typing runs findNext:false
(fresh search); pressing Enter runs findNext:true (walk matches).
No-match state paints the input border red.
Two spots where the light-mode swap didn't take effect because the
color was hardcoded to a dark-mode pastel yellow:
- .btn (used by 'Check for updates' and the engine 'Add' button) had
color:#eaffb0 — pale yellow tinted on top of the acid tint, invisible
on a white ground. Route through var(--acid) so light mode picks up
#0AC18E (BCH teal) with proper contrast.
- .brand ('⛓ Theseus' in Settings sidebar) was var(--acid). In light
mode that's #0AC18E on #f6f8fb — ~2.7:1 contrast. Swap to #253A49
(BCH dark navy) in the light-media block so it matches the Theseus
toolbar chip treatment and reads as the primary UI accent.
Same #eaffb0 sweep applied to the two chrome.html spots that used it
(upchip download button, bcnrbar open button). Dark mode unchanged.
Two follow-ups on the light-mode acid work:
1) Every hardcoded #d6ff3d and rgba(214,255,61,X) in the browser
chrome and every addon panel now goes through var(--acid), so the
light-mode BCH-teal (#0AC18E) takes effect everywhere — not just
where var(--acid) was already used. Hex-with-alpha (#d6ff3d55 etc.)
converts to color-mix(); rgba() converts to rgb(from var(--acid)…)
for the same alpha with the current --acid hue. Chromium 128+
supports both. Files touched: chrome / settings / error / home /
approval / bchwallet / siawallet / screenshot (html + css).
Screenshot editor.js's #d6ff3d stays — that's the drawing colour
swatch, not UI chrome.
2) The Theseus button (.logo) and update chip (.upchip) become dark
BCH-navy chips (#253A49 background, #F8FDFF text) in light mode.
Previously the .logo hardcoded #d6ff3d text on a bright-acid tint —
invisible on a light toolbar. The dark chip stands out and gives
the light theme a distinct accent using the BCH secondary from
whybitcoincash.com's palette.
Two related visibility fixes:
1) Light-mode --acid → #0AC18E (Bitcoin Cash brand primary, from
whybitcoincash.com's palette per user). Direct swap from #088A66
(darkened variant) to the on-brand primary. Applied across chrome /
settings / error / home / approval / messages / bchwallet /
siawallet / screenshot editor. Dark mode's #d6ff3d is unchanged.
2) User-Agent no longer includes 'theseus-navigator/<ver>' or
'Electron/<ver>' tokens. Cloudflare's WAF was returning HTTP 503
'Service Unavailable' to any request carrying those (verified
directly against whybitcoincash.com — same URL, same headers, only
the UA differed; plain Chrome UA got 200, Theseus UA got 503).
Strip both tokens via a stockChromeUA() helper called from
applyAcceptLanguage(), which whenReady already invokes at boot.
Standard practice: Brave, Vivaldi, Slack all do the same.
Verified via CDP: navigator.userAgent now reports
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/130.0.6723.191 Safari/537.36
— indistinguishable from stock Chrome.
Prior light-mode --acid was #3a5c00 (dark olive-green) — legible but
off-brand. The Bitcoin Cash brand primary is #0AC18E (a teal-leaning
green already used in bchwallet's --bch variable). Darken it a step to
#088A66 for AA text contrast on white (~5:1) while staying in the BCH
family — the light-mode accent now reads as "Bitcoin Cash green,
darkened for legibility" instead of an arbitrary olive.
Applied across every chrome page + addon panel that carries the light
override (chrome / settings / error / home / approval / messages /
bchwallet / siawallet / screenshot editor). Dark mode's #d6ff3d
untouched.
The extension row was stacking into a column when the user dragged the
URL bar wide enough to squeeze the dock's slot. Two things broken:
1) #extbuttons could wrap: added flex-wrap:nowrap + overflow:hidden so
the buttons never break to a new line. Same for .extdock's own
flex container (nowrap + min-width:0 so it can flex-shrink to zero).
2) Collapse was width-based on .bar only — a window wide enough for
level 0 wouldn't collapse the dock even when the URL-bar override
left extdock with 0 px. Added a second signal:
data-extcollapse="1" fires when .bar's contents overflow their slot
(bar.scrollWidth > bar.clientWidth). Applies alongside the level-3
collapse; either path shows .extmore instead of the row.
Hysteresis to avoid RO loop: while EXPANDED, cache the row's natural
scrollWidth. While COLLAPSED, re-expand only when summing every other
.bar child leaves at least (naturalWidth + 8 px slack) of room. Both
directions verified across URL widths 400/800/1200/1500/reset — the
row expands / collapses at the right thresholds with no oscillation.
Two fixes bundled:
1) Right-side items (download, extension dock, Theseus button) no longer
slide leftward when the URL bar is capped. Added margin-right: auto
to .urlwrap under every capped state (data-urlsize=medium/compact
and the new data-urlwidth override) so the leftover flex space sits
AFTER the URL bar, keeping the right group pinned to the right edge.
Verified: logo's gap from bar's right edge stays at 10px across
default / urlBarSize=compact / urlBarWidthPx=500 / reset.
2) Drag handles for live resize. A 6-px col-resize strip sits on the
trailing edge of .urlwrap and the leading edge of .searchbox; a
pointerdown/move/up dance updates the width live via a CSS custom
property and persists to settings on release. Two new keys:
- urlBarWidthPx (0-1800, 0 = follow size preset)
- searchBoxWidthPx (0-800, 0 = follow size preset)
When either is non-zero, the corresponding CSS override wins over
the discrete size preset. Preload gets setSetting so the drag can
push the persistent value from chrome.
Visual affordance: handles are transparent by default, gain a faint
acid tint on hover and while dragging.
Previous #4d7300 (0.3.10) was still too light against actual white
backgrounds — several tint fills (rgba(214,255,61,X)) and unpatched
addon panels were making the effective color feel bright green. Two
fixes bundled:
1) Bump --acid in every top-level page's light-media block from
#4d7300 to #3a5c00 — same hue, ~7:1 contrast on #ffffff (was ~5.5:1).
2) Add the missing light-media --acid override to the addon panels
that were still resolving to #d6ff3d: bchwallet/panel.html,
siawallet/panel.html, and screenshot/editor.css (was #b4e024, now
#3a5c00 to match).
Dark mode unchanged. Tint fills (rgba backgrounds at low alpha) still
stay as-is — at 8–15% opacity the specific hue barely matters and the
darker foreground now dominates.
At responsive level 3 the dock folds into a single button; that button
was a generic 🧩 puzzle piece. Swap it for the first registered
extension's own icon (📝, ₿, Ⓢ, whatever ships first in the panels
list), with the puzzle piece staying as the empty-state fallback if
no addons are registered yet. Clicking still opens the same dropdown
of all installed extensions, so no functional change — just the
button reads as "the extension you're most likely to want" instead of
a generic marker.
A ResizeObserver on .bar sets data-responsive to one of four levels
based on width, and CSS reacts:
0 (>=1050px) wide — everything visible (default)
1 ( >=820px) tight — auto-hide the search box, URL min-width drops
2 ( >=620px) narrow — Theseus button collapses to just the gear
3 ( <) xnarrow — extension dock collapses to a single 🧩 button
that opens a dropdown listing every registered
panel (icon + name), click to open the sidebar
Puzzle dropdown reuses the same open/close pattern as the existing
bookmarks menu — click outside to dismiss, click a row to open (or
close if it's already the active panel). User's explicit
Settings > Address bar size / Search box size still win when the
toolbar is wide enough for them; responsive collapse only forces
extra hides at the tighter widths.
Verified on a running instance across 5 widths (1200/900/700/500/400):
each breakpoint flips exactly the elements it should.
#d6ff3d on a #ffffff / #f6f8fb background sits at ~1.3:1 contrast — the
acid green went almost invisible any time the user flipped Settings >
Theme to light. Override --acid to #4d7300 in every chrome page's
prefers-color-scheme: light block. Same hue family, ~5.5:1 contrast
on white, still reads as the same brand color.
Also: chrome.html was missing --acid: #d6ff3d in :root entirely (every
site used var(--acid, #d6ff3d) fallbacks). Adding the real declaration
means the light override can actually take effect.
Files touched: chrome.html, home.html, settings.html, error.html,
approval.html, messages.html. downloads.html is a dark-only overlay
(hardcoded), collision.html already had a proper light-mode --bcdn.
Tint fills (rgba(214,255,61,X) at low alpha) stay as-is — the specific
hue barely matters through 8% opacity.
Verified live via CDP: getComputedStyle(--acid) returned #d6ff3d in
dark, #4d7300 after cfg.set('theme','light').
Setup cc676e62057b42c1e1221f0e9b69a55806bab90259bbbb4e6eb64701ab3d420a
Portable 1ab811f7edba74220b452763ee0f3329ba91d84c834d3e38be6426de0b04f1c7
Two changes since 0.3.8:
f1d117e - .extbtn was display:grid, which is block-level, so multiple
extension buttons inside #extbuttons stacked vertically. Switch to
inline-grid — same icon-centering, no forced line break between
siblings. Verified on a running instance: all 4 dock buttons at the
same y-coordinate.
(this commit) - Remove the static Aegis ₿ placeholder button and its
handler. Redundant now that the real bchwallet addon ships in the box
and registers its own dock entry. Also flip the dock-hidden logic to
disappear entirely on a fresh install with no add-ons registered, so
the toolbar doesn't carry an empty slot for users who never install
one.
Deployed: scp + sia-upload of both trees. Verified LIVE 0.3.9.
.extbtn had display:grid, which is block-level by default. Multiple
buttons inside #extbuttons stacked vertically because each was a block.
Switch to inline-grid — same centering behaviour for the icon inside
the button, no forced line break between siblings. Verified on a
running instance: all 4 buttons render at the same y-coordinate with
strictly increasing x.
Two new settings under Settings > Appearance > Toolbar:
urlBarSize: wide (default) | medium | compact
searchBoxSize: hidden | compact | normal (default) | wide
Applied as data-attrs on the .bar element in chrome.html; CSS switches
the flex-basis of .urlwrap and the width of .searchbox. min-width on
.urlwrap guards against squeezing the URL invisible.
Settings-set now broadcasts settings-update to the chrome renderer, so
resizing takes effect live without a relaunch. Same channel is exposed
for future chrome-side settings.
Motivation: the extension dock grew a per-addon button per install, and
the URL bar (flex:1) had been eating all the remaining space. Users who
want more room for extensions can now shrink or hide the search box and
cap the URL bar width.
Replaces the single sidebarbtn with a dock that renders one button per
registered addon sidebar-panel. Each button shows the panel's icon
(the emoji from its manifest) and opens the sidebar on that panel.
Clicking the currently-active button collapses the sidebar; clicking a
different one swaps the visible panel. Notepad (📝) and Silent Mode
Relay (🌐) appear automatically because they already register panels.
Also lands a static Aegis Wallet placeholder button (₿) next to the
live ones, styled as .soon so it reads as coming-soon. It'll come out
once the addon registers a real panel.
Preload adds openSidebar(panelId) and closeSidebar() wrappers around
the existing sidebar-open / sidebar-close IPC handlers.