Kept in settings.bin, because the silent refresh needs it and so does the
first download on a fresh device - there is no vault to hide it in yet. But it
was the only secret on that screen shown in clear, right beside a masked
password field, and it is also the least protected of the three: settings.bin
is sealed by the no-auth Keystore key, readable without authentication, unlike
the vault.
Masked now. It is typed once, and reading it back has no use - a wrong token
shows up as a 401 in the log, not by squinting at the field.
What it opens stays worth knowing: read access to the account's repositories,
whose only content is ciphertext.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The translucent circle exists to back the initials of an entry without an
icon. A favicon brings its own transparency, so the circle showed through and
around it - a saucer under the drawing, plainly visible on the Gitea cup.
The circle is dropped as soon as the row carries an image, through a class on
the avatar rather than :has() so it does not depend on how new the engine is.
object-fit also goes from cover to contain: an icon is not a photograph and
has no business being cropped.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Reported symptom, with the local server stopped: Sync leaves the app inert -
neither Open vault nor Sync responds, and nothing is displayed. Log and
Server settings still work, because they are local to the page.
The log proves where it stops: the GET line is there, the "HTTP ... in ... ms"
line that immediately follows the call is not. So the request never returned,
SetBusy(False) never ran, and the two actions stayed disabled - a disabled
button fires no click, hence the silence. THTTPClient is given 15 s timeouts
and the Android request does inherit them, so this should not happen; rather
than keep chasing why it did, the situation is now impossible.
A deadline timer, armed while a request is in flight and longer than the HTTP
timeouts so the precise network error still wins when it works. On expiry the
interface comes back and says so. The thread is abandoned, not cancelled: it
dies when its socket fails, and if it somehow succeeds its result still shows.
The page's bridge queue had the same shape of bug: a throw while removing the
iframe left "sending" true forever, and every later command was dropped in
silence. The removal is now in a try/finally.
Also fixed while in there: SetBusy hardcoded "Sync" as the button label, so a
vault-less device lost "Download vault" on every busy transition.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The caret in an Android TEdit does not land where you touch, which makes a long bearer token impossible to correct - the reason the last FMX screen had to go. A field in the engine inherits the system's text handling (caret, selection, handles, paste) for free.
With no FMX control left, two things became dead weight and are gone: the vendored Dark.style, which had nothing left to style, and Skia - 25 MB carried solely so FMX labels would shape Arabic. The engine does its own shaping; checked on the device, an Arabic filename still reads correctly. The APK drops from 22.0 to 12.7 MB.
Delphi stays the owner of the server settings; the page only edits them and hands them back when a field loses focus, one navigation per changed field rather than one per keystroke.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A swipe with nothing to scroll dragged the whole document, showing the body gradient sliding under the screen gradient - two gradients stacked and no content at all. overflow:hidden does not prevent that in an Android WebView; position:fixed does.
The lock button went missing when the list moved to HTML: it lived in the old FMX bottom bar and had no counterpart in the page. It returns as a padlock next to the search field, no label - it is the one icon universal enough to stand alone.
Icons on the unlock screen too, as code points since this source is read as ANSI. All four are basic-plane and common, so a font without them would show an empty box rather than a wrong glyph; checked on the device, they render.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The FMX controls lost this one on merit. Every screen re-taught the same lessons: a template missing from the loaded style, an alignment order left to chance, a HitTest swallowing a tap, a native view painting over everything. The engine draws the same interface in forty lines of CSS and shapes Arabic on its own.
The bridge needs no local server, so no port is open on the device: evaluateJavascript carries the data down, an intercepted cmd:// navigation carries the actions up. Through a throwaway iframe - FMX always tells the engine it did not handle the navigation, which on an unknown scheme replaces the page with an error screen.
Secrets do not travel over that bridge. The list gets names, sites and usernames, which are already on screen; copying from a row sends an index and Delphi reads the vault itself. Only the open entry has its values in the JS heap, and PM.wipe clears them on lock.
Folders become a row of chips with counts instead of a full-screen panel: one tap, and the count says what to expect.
Also gone: about eight hundred lines of the screen this replaces.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>