<feed xmlns='http://www.w3.org/2005/Atom'>
<title>coinosv3.git/src/i18n.js, branch master</title>
<subtitle>coinos v3 — bitcoin, Ark, Lightning and nostr wallet (web + Android)</subtitle>
<id>https://git.coinos.io/coinosv3.git/atom?h=master</id>
<link rel='self' href='https://git.coinos.io/coinosv3.git/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/'/>
<updated>2026-10-15T05:04:35Z</updated>
<entry>
<title>Media pager with a mouse: the wheel pages, ctrl+wheel zooms, arrows for the pointer. Adam reached for the scroll wheel in the pager on his laptop and nothing useful happened: a notch scrolled the snap container a hundred pixels and the glide's snap put it straight back (a headless probe of the live pager: scrollTop stays 0), so the arrow keys were the only desktop way through. Now a wheel notch is a step — down the next post, up the one before, a sideways notch (a trackpad's, or shift+wheel) along the post's pictures — with a 220 ms breath between steps so a spin of the wheel is a step or two, not five; a trackpad's run of small deltas is one step per run (its first 40 px, the rest until a pause is spent). Ctrl+wheel zooms the picture at the cursor, which is also what a trackpad's pinch arrives as in Chrome (attachZoom's new wheel: 'ctrl' mode, 0.004 per unit); while zoomed the plain wheel pans it, and at 1× it is the pager's again. And where there is a pointer to hover with (hover: hover and pointer: fine), arrows: up and down at the right edge, left and right when the post has a picture that way (has-left/has-right, set as the slide on screen changes), faint until the pointer is over the pager. The keys, the wheel and the arrows share one step (pagerStep). No mouse drag-flick: discrete steps suit a pager. tools/pager-wheel-test.js (CDP mouseWheel is always a 120-unit notch to Chrome, so a trackpad run is dispatched page-side; headless has no hovering pointer, so one is given with --blink-settings); feed-picture-pager and image-zoom tests pass.</title>
<updated>2026-10-15T05:04:35Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-15T05:04:35Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=0355c21254d6d452fd4afbec28db3e124bc1d61a'/>
<id>urn:sha1:0355c21254d6d452fd4afbec28db3e124bc1d61a</id>
<content type='text'>
Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
</content>
</entry>
<entry>
<title>Media pager: past the last slide, a "fetching the next video" screen — and the next one, when it comes, is moved onto. Adam reached the last clip before the next was in, and a swipe only nudged the clip and sprang back. Now the pager ends in a screen of its own: a ring sweeping round, three sparks orbiting the other way, the play mark (a picture mark in a pictures pager) breathing in the middle, and "Fetching the next video…" (or "Fetching more pictures…") with a sheen running through it, all still under prefers-reduced-motion. Landing there stops the clip left behind and asks for more at once (the feed pages on; the media grid's pager crawls); slides that arrive go in above it, and one arriving while the reader waits is moved onto and plays. With nothing older to be had (the feed at its end, or the media index), it says "That's everything for now" and goes still. The scroller no longer anchors (overflow-anchor: none), so a slide put in above the screen doesn't hold the screen in view. Strings in all 20 languages. tools/pager-tail-test.js (relays that take 3 s for anything older: the screen at the end, the clip behind stopped, the next clip moved onto and playing, the end saying that's everything, Escape onto the last clip's post); feed-pager-ahead and feed-media-views count slides without it.</title>
<updated>2026-10-13T05:19:47Z</updated>
<author>
<name>Adam Soltys</name>
<email>asoltys@gmail.com</email>
</author>
<published>2026-10-13T05:19:47Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=6488a0a687144e217f71ab1c7a768613b0f17e58'/>
<id>urn:sha1:6488a0a687144e217f71ab1c7a768613b0f17e58</id>
<content type='text'>
Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01KPw21NEpWeTGu6swVASmBt
</content>
</entry>
<entry>
<title>Feed: ⚡ on a locked wallet says so and opens the Accounts page. A locked wallet — the watch-only view of a sealed vault, or a nostr identity signed in over one (Adam's debug Chrome: three watch-only accounts, the vault gone, his extension identity on the first) — has no keys to pay with, and the zap button knew it: it got no handler at all, so the tap pressed the icon and nothing more, under an arrow cursor. Now the tap toasts "Your wallet is locked, so it can't zap. Unlock it to send sats." and opens the Accounts page, where the padlock and Add account are; the open feed is remembered for the way back (by URL, or by id for Following, which has none — returnAfterSignin learns the id form). tools/zap-locked-test.js.</title>
<updated>2026-10-12T05:30:10Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-12T05:30:10Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=8b65caee4348073f44c36d379bc654c27f5e9ee5'/>
<id>urn:sha1:8b65caee4348073f44c36d379bc654c27f5e9ee5</id>
<content type='text'>
Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Lock with fingerprint or face. The padlock's "Protect this device?" now offers a passkey instead of a password, and Settings → Security can turn it on or off. The saved-wallets vault is sealed under a secret only that passkey's PRF produces (behind the phone's fingerprint/face check, the passkey kept by the platform's secure hardware or password manager), so nothing in this browser's storage opens it. Opening the app on a locked device asks for the passkey by itself; a cancel leaves an Unlock button. The prompts that need the vault opened (save, forget, lock a stray wallet) take the passkey too; there's no Change password for a vault that has none; the recovery words remain the way back. The lock passkey has its own PRF salt and a marked user handle, not discoverable where the platform allows — passkey sign-in refuses it rather than derive a stranger's key from it. English + Italian strings. tools/passkey-lock-test.js (Chrome's virtual authenticator with PRF).</title>
<updated>2026-10-11T05:01:32Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-11T05:01:32Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=8b6d2837716aeb04bedbfdd9c00ab9071be63f90'/>
<id>urn:sha1:8b6d2837716aeb04bedbfdd9c00ab9071be63f90</id>
<content type='text'>
Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
</content>
</entry>
<entry>
<title>Italiano: the locale itself and the language list (the previous commit carried only the test's rename — see 3032399 for the description).</title>
<updated>2026-10-10T15:56:15Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-10T15:56:15Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=b530a7015ef6a1126350a9a30cb49dba7cb3f114'/>
<id>urn:sha1:b530a7015ef6a1126350a9a30cb49dba7cb3f114</id>
<content type='text'>
Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
</content>
</entry>
<entry>
<title>Translation: into the phone's language, and DeepL with your own key. isolabellart saw the Translate button but nothing became Italian: the target was the app's language, coinos has no Italian UI (so it ran in English), and the language guess didn't know Italian. Posts now translate into the reader's choice (Settings → Nostr → Translation, any of 31 languages), else the phone's language when the app has no UI for it, else the app's; the guess knows Italian, Dutch and Polish. And, as Jumble offers: a DeepL API key of your own, kept on the device and sent per request as x-deepl-key — the registrar passes it to DeepL (api-free for ":fx" keys; DeepL refuses browsers) and never stores or logs it; a refused key or used-up quota says so once. names: translateDeepl (prose only, links/tags kept, detected source back, 401/402/422 statuses), CORS allows the header. Deployed to cs names. tools/translate-italian-test.js, tools/deepl-translate.test.js.</title>
<updated>2026-10-10T15:39:24Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-10T15:39:24Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=336fd6da53ace71af2c949e35edd9f0aac664df7'/>
<id>urn:sha1:336fd6da53ace71af2c949e35edd9f0aac664df7</id>
<content type='text'>
Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
</content>
</entry>
<entry>
<title>Feed: reposts. Amethyst showed Zapstore's repost of LWB's "Tankful" post in Adam's follows; we showed nothing — a feed asked only for posts (kinds 1, 1068). A feed of people now asks for their reposts too (NIP-18 kind 6; topic and firehose asks stay posts-only), and a repost shows as the note itself, under "🔁 &lt;name&gt; reposted" — its reactions, zaps, ⋯ and thread are the note's. Only reposts that carry the note and whose copy checks out (kind 1/1068, the id the e tag names, a valid signature); a muted author's note stays out. A note shows once: as itself, or under the first repost of it to arrive, counting rows still warming at the door. Media grids skip reposts. tools/feed-repost-test.js</title>
<updated>2026-10-10T14:45:07Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-10T14:45:07Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=9abd654e8ef57de59a4f4bd5cbfbba560ed7e523'/>
<id>urn:sha1:9abd654e8ef57de59a4f4bd5cbfbba560ed7e523</id>
<content type='text'>
Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
</content>
</entry>
<entry>
<title>GIF search, from nostr.build's GIF index (gifs.nostr.build). A GIF button in the post composer and in every chat composer (DMs, groups, communities) opens a picker: "GIFs from nostr.build" (linked, as their terms require), starter chips (gm, lol, yes, wow, …; the first runs on open), a search field (debounced 250 ms, no one-letter searches, one shared request per normalised query, never aborted, 429/403/503 answered in words), and a masonry grid whose tiles play the small H.264 copy only while near the screen, over the lqip blur, falling back to the animated preview (stills under reduced motion). A pick puts the original URL in a post — published with a NIP-92 imeta (m, dim, size, alt) — or, in a chat, sends it at once after whatever was typed. Media URLs not on gifs.nostr.build are dropped. Browser calls carry no headers: production needs v3.coinos.io registered in the nostr.build dashboard (localhost works as their shared dev client). tools/gif-picker-test.js runs against the live API.</title>
<updated>2026-10-10T14:35:16Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-10T14:35:16Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=79b09acc3c4c471fb29bb8d6994c26e849b90bb9'/>
<id>urn:sha1:79b09acc3c4c471fb29bb8d6994c26e849b90bb9</id>
<content type='text'>
Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
</content>
</entry>
<entry>
<title>Messages: Amber users can read their DMs. Signed in with Amber by intent (NIP-55), nothing sealed to the login npub was ever decrypted — each decrypt would be a trip through the app — so isolabellart's thread with Adam showed only what that phone had sent. Worse, decryptorsComplete() counted the Amber signer as complete, so those wraps were tried on the wallet key alone and logged as opened, hidden for good. Now an interactive signer counts as incomplete (wraps wait, uncounted); Messages and the DM thread say "messages sent to you can't be opened here" with Connect Amber, which opens a nostrconnect:// link for the SAME account over NIP-46 (Amber's bunker mode; a different npub is refused, a callbackUrl reload resumes the handshake). Once a signer that can decrypt takes over, a wallet that ran with Amber re-runs the heal pass over every wrap the relays hold, so messages the old code wrote off come back. tools/amber-dm-upgrade-test.js (plays the remote signer over a shimmed relay).</title>
<updated>2026-10-10T06:54:43Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-10T06:54:43Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=a70f34f2a5d35352f99bae8f10cbab12637e08ee'/>
<id>urn:sha1:a70f34f2a5d35352f99bae8f10cbab12637e08ee</id>
<content type='text'>
Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
</content>
</entry>
<entry>
<title>Names: letters and numbers only, 2–24 — coinos v2's rule. The registrar took a-z, 0-9 and . _ - (1–30), so bitcoin-education and a dozen others got through. A NEW claim must now match /^[a-z0-9]{2,24}$/ (v2's /^[\p{L}\p{N}]{2,24}$/u, held to ASCII since a name is a DNS label); a name already held (19 with . _ -, plus "f" and "g") is grandfathered — its owner can still update it — as are v2 migrations carrying their own name and npub-shaped defaults. /name/&lt;n&gt; answers invalid: true for a free name that breaks the rule; the claim form says "Letters and numbers only" as it's typed, and the suggested name from a nostr profile is stripped to letters and digits.</title>
<updated>2026-10-10T04:55:11Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-10T04:55:11Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=3d6bf81a19ca080d3710893b1e65f5aa3f01315f'/>
<id>urn:sha1:3d6bf81a19ca080d3710893b1e65f5aa3f01315f</id>
<content type='text'>
Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
</content>
</entry>
</feed>
