Site approvals and panel approvals share one allowedOrigins list on
disk because the policy is one SitePolicy either way, but on screen
they are two different things: a URL you signed in on versus an
Aegis sidebar panel you unlocked. The panel showed them in one mono
list under "Sites Claude may control", so an Aegis approval read as
"theseus-panel://aegis" with no label and no clue it was a panel at
all.
publicState() now splits allowedOrigins into allowedSites (web
origins) and allowedPanels (theseus-panel://<addon>, enriched with
the add-on's own title via api.tabControl.list()). The panel.html
renders them in their own sections: "Sites Claude may control" and
"Add-on panels Claude may control", the latter hidden when nothing
is approved. Each panel row shows the add-on title in bold with the
mono origin as a subtitle. The ✕ button goes through the existing
remove-origin IPC — removing either kind takes one call.
The renderer falls back to deriving the split from allowedOrigins so
a UI loaded against an older host still renders both sections.
The connector only drove web pages. Add-on sidebar panels — Aegis's
wallet, Hermes's messenger, Pithos's storage panel, anything with
a sidebar-panel capability — were walled off: list_tabs skipped them
and webContents() refused anything that was not kind:"web".
Panels are now first-class tabs with a negative id, kind:"panel", and
an addon/panelId pair. Each open panel is listed next to the real
tabs; a closed one is still listed so a client can see what exists.
screenshot / snapshot / read_page_text / click / type / press_key /
paste_html / paste_text / wait_for / upload_file / evaluate all work
on them with the same TabDriver a web page uses. navigate /
activate_tab / close_tab refuse panels — the user owns panel
visibility, not the connector.
Approval is per add-on under a synthetic theseus-panel://<addon>
origin, so Aegis is approved once and every Aegis panel is reachable
from then on. The approval modal titles itself "Let Claude control
the Aegis panel?" when the origin is a panel origin, and the body
explains the panel is read, not the whole browser profile. Origin
pinning is skipped (the panel is served from the add-on scheme and
never navigates under us); password-field refusal and the action log
still apply, so a PIN input in a wallet panel is never typed into
and every call is in the connector's log.
The file tracks working-tree state that is not yet committed or shipped;
0.1.7 is both, so leaving the block would misdescribe the tree to the next
session reading it.
Typing never triggered a translation. The input handler was wired only to the
language detector, so nothing happened until Enter, the Translate button or a
language change -- the panel looked broken to anyone who simply typed and
waited. Translation now runs 500 ms after the text settles.
Each request carries a sequence number. Typing outruns the network, and
without that guard a slower earlier response paints its stale text over a
newer one.
Detection already worked, but wrote its answer only into the text of the
collapsed "Auto-detect" option, which a closed select hides -- so it read as
"detection is broken" when it had in fact succeeded. Name the language in a
row under the selects, and lower the 12-character floor to 5, since short
phrases never cleared it. With translate-as-you-type on, skip the standalone
detect call: the translation response already carries the detected language,
so asking twice only doubled the load on a public mirror.
Two of the three fallback mirrors were dead, checked by posting to both
endpoints: translate.plausibility.cloud answers 405, and lingva.ml serves an
HTML page because it is not a LibreTranslate API at all, so it could only ever
produce the "HTML instead of JSON" failure. Drop both and add
silentmode.st/libre, which stays a fallback rather than the default because it
covers six languages against disroot's fifty.
Auto-translate sends text to a translation server as it is typed, so it is a
visible toggle rather than silent behaviour. Default on.
The file opened with the 0.0.2 hashes and rollback points from August, which
reads as the current state to a session skimming it. Releases live in the
manifest and in git history; the collision-policy group below is still
accurate and stays.
Rebuild + redeploy of the prior commit (9b9c93c). Version bumped from
0.0.1 to 0.0.2 (parallel-session bump in package.json). New hashes live
on dl.silentmode.st + Sia bns/silentmode/:
TheseusNavigator-Setup-0.0.2.exe
sha256 b62ecf528dc672a07a144af5a8bbb5772b2853b4ad5eec5926bdc6247792ad53
TheseusNavigator-0.0.2-portable.exe
sha256 ba80a808446bd75a6b649d93094af9e21be38476dd23980bad7ac0e228a6ce76
Also snapshotted the parallel-session 0.0.1 (81831c08 / b4807aa1) that
was live just before this ship into /opt/silent-mode/dl/_prev/ so it's
available as a rollback alongside the earlier 418e7bba / 208b059a /
036e94c7 builds.
Manifest hashes were previously updated by the parallel session to a
STALE 0.0.2 build (b3a81047 / 37679d29) that never made it live; those
have been overwritten with the actual live hashes now.
On-chain pointer at releases.silentmode.bch unchanged — publishes the
manifest URL, so BCNR clients see the new hashes automatically without
a wallet spend.
PENDING.md updated with the new rollback list.
Rebuild + redeploy of the prior commit (5ef4bba). New hashes live on
dl.silentmode.st + Sia bns/silentmode/:
TheseusNavigator-Setup-0.0.1.exe
sha256 418e7bbaa08c248cebdf4b9137eaf0351139c2f328fc788bcf5d08888bba2ec3
TheseusNavigator-0.0.1-portable.exe
sha256 93d579639affa6108d9ad80fd0a20eb183fc156b7d954a7acd525ed74d9e1ccf
Two rollback points on the VPS at /opt/silent-mode/dl/_prev/:
208b059a (pre-shield, from earlier today)
036e94c7 (two revisions back)
On-chain pointer at releases.silentmode.bch is unchanged — publishes
the manifest URL so this build is reachable through BCNR without a
wallet spend.
PENDING.md updated with the new rollback list.
Rebuild + redeploy of the prior commit (4654940). New hashes:
TheseusNavigator-Setup-0.0.1.exe
sha256 208b059a14c8ddf10cf553e530e77f34129cf8ba999b697da08720ffc3685d22
TheseusNavigator-0.0.1-portable.exe
sha256 bf08976500978dcd0d7305b04cc1480e63da54a24a71253cf4cfd9c910431a89
Verified all runtime-loaded files present in app.asar (downloads.html,
downloads-preload.js, engine-picker.html, popover.html — the trap from
the earlier 6bbccf9c build).
Live on dl.silentmode.st + Sia bns/silentmode/. Manifest date bumped to
2026-08-02. Previous build backed up on the VPS at
/opt/silent-mode/dl/_prev/ so rollback is `cp` away if the new build
misbehaves. On-chain pointer at releases.silentmode.bch is unchanged —
it publishes {"u":"https://dl.silentmode.st/releases-manifest.json"} so
new hashes are reachable through BCNR too, no wallet spend needed.
PENDING.md drained — this session's group is empty now.
Bundle of UX + feature work. Split from packaging by intent so the diff
is reviewable; the next Theseus rebuild ships it.
Features
- Download tracker (new): session.on("will-download") → per-item state
{id, filename, url, mime, total, received, state, savePath, startedAt}
with updated/done event handlers. New downloadsPop WebContentsView
loads downloads.html (new file) + downloads-preload.js (new file);
panel positioned under a new #downloads toolbar button between search
and Tor. Full IPC: downloads-get, toggle/close/resize-downloads,
download-open/show/cancel/clear, downloads-clear-all. In-memory only —
cross-session persistence is a future addition. Button badge shows
active count + spin/done/err color.
- Search engines split by kind + tier:
* kind: "search" | "llm" — separate headers in picker + settings
("Search with" / "Ask an AI"). Empty sections hidden.
* tier: "catalog" | "extra" — Settings now has THREE panes behind the
"+ Add search engine" button: curated catalog, wider discoverable
bank filtered by a live search input, custom URL form.
* DEFAULT_ENABLED unchanged (5 major engines).
* Custom user-added engines carry tier="custom" (never in catalog/extra
panes).
- 9 tier="extra" engines added (all non-login ?q=): Marginalia, Stract,
Yep, Presearch, MetaGer, Qwant, Swisscows, Naver, Baidu. Same rot rule
as LLMs: if one starts bouncing to a login gate, drop it.
Bug fixes
- Loadbar collapses to 0px when idle (was reserving a permanent 2px
strip below the address bar). .loadbar {height:0} + .loadbar.on
{height:2px} + 120ms transition.
- Native <select> popup theme sync via :root { color-scheme: dark } +
@media(prefers-color-scheme: light). nativeTheme.themeSource already
drives prefers-color-scheme, so the OS popup color follows the app
theme automatically (fixed light popup on dark app / vice versa).
- .ctl layout flipped to flex-direction: row with flex-wrap so
anti-fingerprint mode + value fields fit side-by-side.
Settings restructure
- General section: Startup group at the top ("Open previous windows and
tabs" toggle), then Appearance below with three visual THEME CARDS
(System / Light / Dark) — small mock-browser previews per theme,
Firefox-style, active card gets a blue ring. System pipes through to
nativeTheme.themeSource = "system".
- Search promoted to a top-level sidebar item between General and
Naming. Search-engine controls moved out of General into Search.
- Search section: enabled list shows only enabled engines, grouped by
kind, drag-reorder within a kind. "+ Add search engine" opens the
catalog/extras/custom-URL panel.
Search engine catalog trims (already flagged in prior work)
- Removed ChatGPT / Claude / You.com (login-gated ?q=).
- Removed SearXNG (federated; every single-instance default rots).
Docs
- TheseusNavigator/PENDING.md and GOTCHAS.md born with this work
(see the HANDOFF.md commit for the convention).
- PENDING.md's own "session: 2026-08-02:theseus-ux-polish" group will be
emptied after this ship lands.
Preview harness
- _preview.html + _settings-preview.html stubs updated with kind + tier
+ downloads seed + tier="extra" samples so the preview reflects reality.
Both files are gitignored — local only.
Coordination
- Parallel session's collision-policy work (chrome.html registry chips,
popover switcher, Naming section, in-tab collision prompt) already
landed in commits 256079d/42b340f/b0d6375/78dddda. This commit adds
cleanly on top.