aboutsummaryrefslogtreecommitdiffstats
path: root/src/app.js
Commit message (Collapse)AuthorAgeFilesLines
* The passphrase gets a page of its own in the create flow, between the words ↵Adam Soltys4 hours1-21/+60
| | | | | | | and the check. "The add passphrase button is too small … maybe it should get its own separate prompt screen with explainer of what it is and letting people know it's optional but recommended. we don't need a 'show previous screen' link on verify page because there's already a back button. 'Have an existing seed or entropy to import?' can go away once the seed is displayed" (Adam, 2026-11-07). New wallets go heads-up → words (Copy, Next) → "Add a passphrase?" (an extra secret on top of the 12 words; optional but recommended; write it down with the seed, nobody can reset it), with the field, its confirmation once typed, and one button that reads "Continue without one" until something is typed, then Next; a mismatch is caught there and clears once the fields agree → "Which one is word #5?". The page's own Back now steps through those (check → passphrase → words → heads-up, keeping what was typed) on the first-run and "Sign into another account" pages, so the check drops "Show the words again"; the classic card, which has no Back of its own, keeps one. "Have an existing seed or entropy to import?" shows only on the heads-up (and its "create instead" twin on the import page). The import pane's Add passphrase is a full-width button. passFields() split out of optionsPanel Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
* "Sign into another account" opens the way the start screen does: "Add an ↵Adam Soltys4 hours1-3/+73
| | | | | | | account" with Create a new wallet / Sign in to an existing account, then Sign in with Seed phrase, Passkey, Google, Nostr. "Can you update the 'sign in to another account' screen to match the initial start screen?" (Adam, 2026-11-07). It used to open on the old Create new / Import existing tabs with the sign-in buttons stacked underneath. addAccountScreen (unlockScreen while ui.fromWallet && ui.addFlow, set by signInAnother) draws the front door's two steps with the same buttons (frontDoorSignin, short labels, the same loginWith, so a sign-in still adds beside the open accounts); Nostr opens its signer card in place; a choice leads to the front door's seed pages (no tabs, titled Set up your wallet / Enter seed phrase, Back to where the choice was made), and Back on the first step returns to the wallet. The coinos.io migrate door stays on the first-run screen only. The Accounts "new wallet" kind picker keeps the classic card (addFlow false). account-access and padlock-flow tests walk the new screens Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
* A new seed phrase comes in three steps: a heads-up, the words, then one word ↵Adam Soltys5 hours1-62/+82
| | | | | | | to pick. "Reintroduce the seed phrase preamble telling users to write it down but don't give it a warning icon … an intermediary step before we display the seed phrase … instead of verify and skip buttons just have a next and another intermediate step that asks for just the 5th seed word and gives a few options to click and also a skip button" (Adam, 2026-11-07). Creating a wallet (front door, Accounts, signInAnother) now opens on a plain note, "Next you'll see your seed phrase: 12 words … Write them down on paper, in order … make sure no one is looking", with Show seed phrase; the words page has one Next; the check asks "Which one is word #5?" with four of the phrase's own words as buttons (a wrong one turns red and says "Not that one"), Skip, and Show the words again. The gift claim's new-wallet backup gets the same three steps in place of its warning box and typed three-word check, and a sign-in's "Write down your recovery phrase" shows its explanation first and the words behind Show seed phrase (Later still on both). createStep starts at 'intro'; pickConfirm returns { index, options, wrong } Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
* Under the "Sign in" heading the ways in just name themselves: Seed phrase, ↵Adam Soltys13 hours1-2/+2
| | | | | | | Passkey, Google, Nostr. "I think we can remove 'Sign in' from all the sign in buttons since it's at the top in the heading" (Adam, 2026-11-06). frontDoorSignin takes { short } and the sign-in step asks for it; the coinos.io move page (under "or") and the sign-in sheet in Messages keep the full "Sign in with …" labels, where nothing above says it. "Migrate a coinos account" is unchanged. Tests wait for the list by its last door Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
* Pull down to refresh works in the Android app, the Beta and an installed ↵Adam Soltys13 hours1-0/+2
| | | | | | | PWA. "Pull down to refresh isn't working for me in the apk" (Adam, 2026-11-06): measured on his Pixel, the same pull reloads a Chrome tab but not the app — Chrome turns its pull-to-refresh off whenever a page runs full screen, which the TWA (sticky-immersive since 1.14) always does, and a WebView or a standalone PWA has none at all. src/pull-refresh.js takes over where the page reports display-mode standalone or fullscreen, or runs in our WebView (window.CoinosHost), and switches the browser's off there (overscroll-behavior-y: contain) so the two never both fire; a browser tab keeps the browser's. A pull counts only at the top of the page, vertical (sideways swipes stay the tab navigation's), and not from inside something scrolled down (a chat, a picker) or over the page (dialogs, the media viewer, fields); damped like the browser's, a round arrow follows the finger and spins once the pull is far enough, and release reloads (through the network, as a refresh does). On the phone, the local build served into the app: a pull at the top reloads, one while scrolled down only scrolls, a short one does nothing Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
* The seed-phrase pages say less: no "write these 12 words down on paper" box, ↵Adam Soltys13 hours1-7/+15
| | | | | | | the passphrase behind "Add passphrase", and the import titled "Enter seed phrase". Adam, 2026-11-06: the warning box goes from the create pane and the backup step (the words and the page's own title carry it); the BIP39 passphrase field, which read as a required password under the seed, waits behind an "Add passphrase" button and, opened, is labelled "Passphrase (optional)" with "Leave it empty unless you want one. Most wallets have none." (What's this? still explains it; a typed passphrase keeps it open; clearing the drafts closes it). The import pane is titled "Enter seed phrase" (was "Restore your wallet"), its field "Seed phrase" (was "Recovery phrase or key") with the placeholder "Your seed words" — an xpub or xprv pasted there still opens as before. tools/onboard-backup-test.js follows the new words Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
* The sign-in list is seed phrase, passkey, Google, Nostr, coinos.io, evenly ↵Adam Soltys13 hours1-33/+46
| | | | | | | spaced, and setup pages keep only the language picker under them. Adam, 2026-11-06: passkey second, below the seed phrase; "check the spacing on all the buttons, some of them seem a bit different" — the sign-in feature returned its doors in a wrapper of their own (8px apart) inside the page's 20px column, so the gaps ran 20/8/8/20. frontDoorSignin now returns them loose (externalButtons takes passkeyFirst), and the front door's lists sit in one 10px column: measured at 390px, every door 53px tall and 10–11px apart; the welcome's two choices 55px and 10px apart; the sign-in sheet in messages spaces them at its own 12px. And "on all the wizard/setup pages let's hide the footer except for the language selection": the unlock screen and every page the wizard draws (.onb) get a footer with just the language picker, as wide as the language's name; everywhere else keeps the full footer. tools/onboard-backup-test.js checks the front page's footer holds only the picker Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
* The front door is two choices: "Create a new wallet" or "Sign in to an ↵Adam Soltys14 hours1-17/+31
| | | | | | | existing account". "Login page is getting busy" (Adam, 2026-11-06): it had grown a door per method — Google, passkey, Nostr, twelve words, coinos.io — under the way in, and a footer of credits, pickers and the build stamp under that. Now the welcome step holds the two choices and nothing else (no footer on the front-door steps; the language follows the browser's), and the second opens a sign-in step with the ways in: "Sign in with seed phrase" first (was "Restore from twelve words"), then the feature's doors (Google, passkey, Nostr), then "Migrate a coinos account". Back from the seed-phrase import, the Nostr card and the coinos.io move returns to the sign-in step. Tests follow the extra step: onboard-backup (two choices, no footer; the seed phrase first among the ways in), onboard-migrate-door (Back to the sign-in choices), amber-login, account-access (logout lands on the two choices) Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
* Opening the wallet no longer stretches the same keys in JavaScript two and ↵Adam Soltys14 hours1-1/+22
| | | | | | | three times over. Profiled on Adam's Pixel 9 at 4x CPU (a mid-range phone), a cold launch — a fresh tab, so the wallet comes out of the device vault — spent ~850 ms of its 1.9 s to the balance stretching keys on the main thread: scrypt (N=2^15) to open the vault under its blank password (~520 ms), then PBKDF2-2048 of the same twelve words once per saved wallet to match it to its watch-only entry and twice more for the open one (the BIP84 account and, inside nostr-tools' nip06, the nostr identity). Now src/seed.js keeps each phrase's BIP-39 seed: the boot stretches the phrases it's about to open with WebCrypto's native PBKDF2 while the stores open (warmBootSeeds, alongside the IndexedDB wait it already had), and the wallet, zaps and the nostr key (NIP-06 m/44'/1237'/n'/0/0, derived from that seed — nostrKeyFromSeed) read it; a reader that comes first stretches it the old way and leaves it for the rest. A lock forgets them. The vault under the BLANK password — the default, which scrypt protects from no one, since anyone with the blob can compute scrypt('') — keeps its key beside the blob and new blank seals reuse that salt (fresh nonce each time); sealing under a real password drops it, and real passwords are stretched every time as before. Same cold launch at 4x: balance 2.2–2.9 s → ~1.5 s, scrypt and the JS PBKDF2 gone from the profile. tools/seed-cache-test.js: WebCrypto's seed = @scure/bip39's for plain, passphrased and NFKD input, warmed or cold; the NIP-06 key = nostr-tools'; a malformed phrase still refused; a blank vault opens without scrypt and reseals with the kept salt, a wrong guess fails and drops nothing, a real password keeps nothing Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
* The feed's post button is on screen the moment the feed is, while the page ↵Adam Soltys5 days1-1/+6
| | | | | | | is still sliding in. "I'm still not seeing the Post button right away when I open the feed. It should be there from the start" (Adam, 2026-11-02): measured on his phone over DevTools, the button was in the page 87 ms after the Feed tap — 4109 px down, below an 809 px screen — and in place at 562 ms. It is position: fixed, and it was rendered inside the page, whose entrance animation puts a transform on it; a transform makes fixed descendants position within the page, so the button rode at the foot of the feed card until the slide ended. The feed now hands the button and the new-posts pill to the shell (a pageFloaters hook, taken once per render), which paints them beside the page, outside anything that animates; the pill's CSS no longer depends on sitting inside the page, and its phone-width rule stays after the base one so it still rides above the button. tools/feed-fab-anim-test.js taps Feed from the wallet and samples the button every frame of the slide: on screen from the first. The direct-mount harnesses (feed-new-posts, feed-streaming, feed-bottom-nav) paint the floaters as the shell does Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VMan6TFgisGKUDhdStLUvs
* A zoomed-in picture is drawn again at its real scale once the pinch settles, ↵Adam Soltys5 days1-0/+10
| | | | | | | so no faint grid shows across it. "When I zoom in on my image, I see very faint grid lines. Is that a compression artifact?" (Adam, 2026-11-02): no — Chrome keeps a will-change: transform layer rastered at 1× and scales that bitmap on the GPU, and the bitmap's tiles meet in faint seams, a regular grid over the sky. The hint stays on while a gesture moves the picture (no re-raster per frame) and is dropped once it comes to rest zoomed, so Chrome rasters the picture at the scale it is shown at: sharp, and seamless. At 1× the stylesheet's hint is back for the pager's swipe Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VMan6TFgisGKUDhdStLUvs
* The front page has a door for a wallet made elsewhere: "Restore from twelve ↵Adam Soltys5 days1-1/+14
| | | | | | | words", straight to the phrase field. "Signing in on another device … it was hard to find how until I realised you click to make a new wallet, where then you get the option to use the seed phrase for an existing wallet" (user feedback, 2026-11-02): the import used to hide as a line under the freshly minted words behind "Create a new wallet". It sits below the sign-in buttons with the migrate door, and the step it opens is titled "Restore your wallet" rather than "Set up your wallet". tools/onboard-backup-test.js checks the door, that it opens on the phrase field with no minted words, and that a typed phrase opens the wallet Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VMan6TFgisGKUDhdStLUvs
* The feed opened by URL paints as the restoring wallet's from its first ↵Adam Soltys7 days1-6/+38
| | | | | | | frame: the Post button, the feed chips and the nav come with the page, not a beat after it. /feed is a public page, so it painted as a visitor's (no button, chips or nav) the moment the app ran, and the wallet's restore — synchronous work: the vault's scrypt on a cold start, the wallet's state, every feature's init, ~0.5 s on a laptop and more on a phone — re-painted it with the button only once it had run ("the post button takes a while to appear", Adam, 2026-10-31). Boot now tells before that work whether a wallet is about to open (a session list in the tab, a vault the last seal or unlock opened without a password — noted, since the vault alone can't say short of the scrypt being avoided — or watch-only accounts) and the feed, the nav and the chips paint for it; taps still go by the wallet actually being open, which it is by the time one lands. The wide-screen wallet column waits for the numbers. A visitor with no wallet on the device gets the visitor's page as before. tools/feed-boot-chrome-test.js boots the built app at /feed behind a session, a noted vault, an unnoted vault (the old order, and the unlock leaves the note) and as a visitor Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VMan6TFgisGKUDhdStLUvs
* A toast no longer sticks on screen for good. One raised while the app was in ↵Adam Soltys8 days1-2/+9
| | | | | | | the background (a zap failing after you switched away) was hidden by its timer before it ever showed, because a hidden tab pauses requestAnimationFrame but not setTimeout; on return it showed and nothing took it down (Adam's phone, 2026-10-30). The hide timer now starts when the toast actually shows Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01VMan6VLgQdujavxPsfU7A9
* An upload that fails says why: the host and its answer (or the signer's ↵Adam Soltys12 days1-1/+5
| | | | | | | error) in the toast, not just "check your connection", which hid hosts refusing the file and a signer that never answered Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
* A gift opened into a new wallet goes on, once its seed is written down, to ↵Adam Soltys13 days1-5/+17
| | | | | | | the first-run steps: pick a username, then a picture and a cover ("Not now" lands in the account the gift arrived in). Also fixes Verify backup on the gift screen, which crashed (pickConfirm wasn't passed to the gifts feature) Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
* The single-file build boots again: app.js exported its safe-mode helpers, ↵Adam Soltys2026-10-231-2/+2
| | | | | | | and a classic inline script cannot carry an export (standalone.html threw "Unexpected token 'export'" since b3cee21; nothing imported them) Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
* Switching the unit with an amount typed into the send form converts it: the ↵Adam Soltys2026-10-231-5/+18
| | | | | | | same money in the new unit, round-tripping exactly Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
* iPhone: a push shows its line first and the outcome replaces it; a crash ↵Adam Soltys2026-10-231-0/+32
| | | | | | | | | | | | | | | | | | | | | | | | | | | loop on open lands in safe mode; a closed-app payment is one history row PullCap's night of 2026-10-22 on an iPhone: four hourly payments made while closed, no notification for the fourth, then no more wake-ups until the app was opened — WebKit's silent-push quota. APNs accepted every push (no 410); the device had stopped listening. The stand-in line went up at the END of the push handler, after a payment that can take most of the budget, and a worker stopped before its last line showed nothing. On an Apple endpoint the line now goes up first and the outcome ("Payment sent") replaces it. The same phone then crashed on every open ("A problem repeatedly occurred"). An inline guard counts page starts before the app runs; the app clears the count after eight seconds alive or a clean leave, and notes the boot phase it reaches. A third start in a row opens in safe mode: no automatic Ark connection, a notice naming the last phase, "Open normally" and "Copy report". Every payment the worker made showed twice once the app opened: the worker's own ln-send row (merged from the mirror) and the "while closed" note the app adds from its spend log. The note now stamps the existing row, and lnFoldedIds hides a twin that still forms. The balance was never counted twice — it comes from the coins. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Sign media uploads with the user's Nostr login identityAdam Soltys2026-10-181-3/+6
|
* Settings → Wallet → Transaction history: Export CSV — every payment in ↵Adam Soltys2026-10-181-1/+41
| | | | | | | Spending and Savings as a line of a CSV file, oldest first: date (UTC), account, type, rail (Lightning, Ark, on-chain), what was paid, its fee, the change to the balance and its worth in your currency at the time, status, the transaction or payment hash, who it went to, the invoice. A failed payment is left out; a renewal is its fee. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Allow custom Blossom servers for attachments and post mediaAdam Soltys2026-10-181-0/+23
|
* Voice and video calls in DMs (NIP-AC, as Amethyst, nospeak and Noscall speak ↵Adam Soltys2026-10-181-1/+3
| | | | | | | it): 📞 and 🎥 in a conversation's header; a call from someone you follow or have written to rings over any page (tone, buzz, a notification when the app is out of sight) with Answer and Decline; mute, camera off, switch camera, minimize to a bar, hang up. WebRTC device to device (DTLS-SRTP), set up by signed messages in ephemeral gift wraps to the DM inboxes; TURN from coinos's live server (Cloudflare) for the connections that can't go direct. A remote signer puts the candidates inside the offer and answer instead of signing one each. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Live rings and going live. A face whose person is streaming wears a red ring ↵Adam Soltys2026-10-181-3/+3
| | | | | | | (LIVE under the bigger ones) and opens the stream. Go live (the composer's red dot, or your profile) sends your camera or screen to coinos's live server (coinosv3-server live/: the recording over a WebSocket, HLS out), announces it as your kind 30311 once three segments exist (Chrome's own player won't start a live playlist with fewer), refreshes it each minute with how many watch, reconnects to the same stream if the link drops, and ends it with its recording. Needs a coinos name. Also: the service worker leaves /live/ and /vmedia/hls alone (it froze a live playlist at its first copy). Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* Live streams (NIP-53), to watch: a row of what's on above a feed (Following: ↵Adam Soltys2026-10-181-4/+5
| | | | | | | your follows' streams; Everything and Popular: all), a stream's own page at /naddr1… (the HLS stream playing — hls.js where the browser has no HLS — its chat and zaps as they come, a chat line sent as kind 1311, ⚡ to the host), a card for a post linking a stream, and a LIVE line on a streamer's profile. A stream quiet for an hour is over; one that drops is retried; zaps on a stream carry its a tag. Also: the single-file build's video stub gains streamVideo (its uploads threw since the HLS uploads). Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* HLS for long videos: vmedia serves a long clip as a 360p/720p stream made as ↵Adam Soltys2026-10-181-3/+11
| | | | | | | it's watched (/vmedia/hls.m3u8), the feed and pager play long clips and posted .m3u8 streams through the browser's own HLS, and a long video posted from the app goes up as an HLS stream made on the device (master playlist on Blossom). Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* A mouse drag on the feed selects text again instead of grabbing and flicking ↵Adam Soltys2026-10-181-46/+3
| | | | | | | the page. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
* The wheel coasts wherever the pointer is while a feed or a thread is up.Adam Soltys2026-10-161-2/+10
| | | | | | | | | The coast only took a notch whose target sat inside the feed or the thread, so a pointer resting on the rail, the header or the wallet column beside the feed on a wide screen got the browser's plain scroll (Adam, 2026-10-16: "when my cursor is hovered over the right side bar or any part of the page that's not the feed, wheeling scrolls without coasting. can we make it coast no matter where my pointer is?"). Now the question is whether a feed or a thread is the page on screen; the pointer's spot only matters when it is over something that takes the wheel itself — a text box, the chips strip, the lightbox, the pager, a box that scrolls on its own — and on a page with no feed on it (the wallet) the wheel stays the browser's. feed-wheel-reverse-test: the fixture's page carries a feed unless told otherwise; a notch with the pointer over the wallet column, or over nothing in particular, coasts; one on a page without a feed does not. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
* A turn back against a flying feed brakes first: that notch eases the coast ↵Adam Soltys2026-10-161-17/+39
| | | | | | | | | | | to a stop, the next is the gentle start back, the ones after wind up. The gentle half push straight away from a flight still overshot (Adam, 2026-10-16: "the first click of the wheel should just stop the coasting/flying maybe with a quick easing rather than a sudden stop and then the next notch should start going slowly in reverse and the next ones should start speeding up"). Now, when the page is flying (over 1.2 px/ms — a notch from rest peaks near 0.9), the first notch back only brakes: the coast's decay is shortened to 40 ms from that moment (the frames a busy thread missed don't travel first), so a flight at 8 px/ms stops within ~300 px, eased. The notch after it, up to 2.5 s later, is the gentle start — half a notch's travel at half the speed — and the ones after wind up as usual. Against a stroll or a coast that only just died there is nothing worth braking: the turn back is the gentle start itself, as before. feed-wheel-reverse-test: the brake moves nothing by itself and eases out within a short way, never the other way; the next notch creeps back half a notch; the ones after wind up; a notch back a second and a half later is still gentle, three seconds later a full push; a stroll turned back starts gently at once. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
* The wheel coasts over a video and on a thread, and a turn back against a ↵Adam Soltys2026-10-161-17/+24
| | | | | | | | | | | | | coast is a gentler half push that winds up again from the next notch. A notch with the pointer over a clip fell to the browser's single-notch scroll: video and audio were on the wheel handler's hands-off list from the first commit, though the wheel does nothing to a player itself. The thread page was outside the handler's scope altogether, so a thread scrolled plain while the feed coasted. Both coast now, like the words around them (Adam, 2026-10-16). A reversal was a full notch the other way followed by two notches without wind-up, and felt like an overshoot. It is now half a notch's travel at half the speed, so the reader who flew past a post settles back onto it; the next notch the same way is a plain one, winding up from whatever speed the half push left, so a spin on after a reversal gets going at once. (The five-notch quarter-size hunt stays gone.) feed-wheel-reverse-test: the half push, the wind-up from the second notch, a notch over a video / audio / a thread coasts, one in a text box or the pager does not; the fixture's target can sit inside a given element and on a thread page. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
* Feed wheel: after a reversal the next two notches are plain, then a spin ↵Adam Soltys2026-10-151-9/+11
| | | | | | | winds up again. The 700 ms spell without wind-up after a turn back (63fc25d) also covered a spin begun inside it, so correcting the direction and spinning on felt slow for most of a second (Adam, 2026-10-16: "it slows down for a while when I don't want it to"; "overall the scrolling still isn't as fast"). The calm is now a count: the reversal's next two notches don't wind the speed up — a couple of correcting notches still travel a couple of notches — and from the third the wind-up is back, so a spin after a reversal goes as far as one from rest. tools/feed-wheel-reverse-test.js (a reversal plus four quick notches goes well past five plain ones, and about as far as five from rest). Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
* Feed wheel: a turn back is a plain notch again, with no wind-up for a ↵Adam Soltys2026-10-141-30/+21
| | | | | | | moment. The "hunt" on a reversal (3ff588d: a quarter-size notch settling fast, then four growing ones) read as a stutter on every change of direction and a slow patch after it (Adam, 2026-10-16: "it stutters and sometimes when I'm scrolling it slows down for a while"). Now a reversal still catches the coast at once, and the notch travels its full distance at the usual pace; for 700 ms after it notches don't wind the speed up, so a couple of correcting notches travel a couple of notches rather than launching a flight the other way — which was the complaint the hunt had answered. tools/feed-wheel-reverse-test.js (full travel on a reversal, a reversal plus a quick notch vs two wound-up ones from rest, a reversal against a coast that only just died); feed-momentum-test passes. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
* Media pager with a mouse: the wheel pages, ctrl+wheel zooms, arrows for the ↵Adam Soltys2026-10-141-1/+8
| | | | | | | 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. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
* Closing the pager (or a picture) lands on the page under it, not wherever ↵Adam Soltys2026-10-141-3/+37
| | | | | | | | | | | | | | Back goes The × was a blind history.back(), and the entry under the pager isn't always the feed: the browser's older entries outlive a background reload of the app, and boot merges entries. It took the reader to the wallet, then the chat list. closeLayer still steps Back (so the phone's Back doesn't reopen it), but if Back lands anywhere but the page under the layer, the reader is put on that page and that entry rewritten to it. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01KPw21NEpWeTGu6swVASmBt
* Feed wheel: a turn back against the coast hunts, in short steps that grow. ↵Adam Soltys2026-10-121-17/+43
| | | | | | Flying down the feed and turning the wheel back for the post just passed used to send the page flying the other way (a full notch's coast, 300px) the moment it caught — the reader overshoots again, and hunts by overshooting. Now a reversal against a coast (or one that died within 400 ms) catches the page and takes that notch as a quarter of its travel, over a quick coast (110 ms, not 325); each notch on in the same direction grows the step — 0.35, 0.5, 0.7, 0.85 of a notch — and the sixth is a full push with the usual wind-up, so a few flicks have the wheel flying again. Another turn back while hunting starts the small steps over, and two seconds without a notch ends the hunt: a notch from rest is the push it has always been. Keys, a touch or a grab end it too. tools/feed-wheel-reverse-test.js (short step on reversal, the ramp, the restart, the pause, the just-died coast, the full push from rest); feed-momentum-test still passes. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Feed: a picture opens in the media pager, with the feed's other pictures a ↵Adam Soltys2026-10-111-2/+5
| | | | | | swipe away. Tapping a picture in a feed row used to open it alone (the lightbox); it now opens the media grid's pager on that picture, over the feed's pictures in the feed's own order — every picture of a post before the next post's (a repost's inner post, the feed's moderation) — so a swipe up or down, or any arrow key (up/down as before, now left/right too), steps to the next or previous one. A tap on the picture that opened it still closes it, as a full-screen picture always has; once the reader has moved on, the first tap shows the post's details and actions, and the next (or Escape, or the ×) closes the pager with the feed landed on that post (glideToPost, once the pop has brought the feed back). The pager's pictures pinch to zoom (attachZoom, now exported, without its wheel — the wheel is the pager's; at 1× one finger is the pager's swipe). Pictures in threads, profiles and chats still open alone. tools/feed-picture-pager-test.js; image-tap-test and lightbox-scroll-test accept the pager as the full-screen picture. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Feed wheel: turning the wheel into a page already moving winds the speed up. ↵Adam Soltys2026-10-111-1/+6
| | | | | | Each notch added the same push whatever the speed, so a sustained turn settled at a stroll (the coast's decay ate what each notch added). Now the push scales with the speed on the page, 1x from rest up to 2.5x at a fast coast, so keeping the wheel turning keeps accelerating; a single notch from rest, the reversal and the trackpad path are as before (feed-momentum + feed-wheel-reverse tests green). Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Lock with fingerprint or face. The padlock's "Protect this device?" now ↵Adam Soltys2026-10-101-11/+149
| | | | | | | 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). Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
* Move desktop logo and search into sticky sidebarsAdam Soltys2026-10-091-8/+14
|
* Footer: the source link and the running commit point at git.coinos.io (cgit) ↵Adam Soltys2026-10-091-2/+2
| | | | | | | instead of GitHub, where the coinos organisation is suspended. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
* Amounts: no plus sign on money coming in either — the word (Received) and ↵Adam Soltys2026-10-091-3/+3
| | | | | | | the green say it. History rows, transaction details, the "payment received" screens (wallet, Spending, gifts, boards/onboards) and the till's paid bills. The till's tip choices keep theirs: "+$2" there is what a tip adds to the bill. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
* Amounts: no minus sign on money going out. The word (Sent, Renewal, ↵Adam Soltys2026-10-091-2/+2
| | | | | | | Exiting…) and the red already say it's a debit — history rows, the transaction details, the sent screens, and on-chain rows (whose negative net printed its own sign). Received keeps its +. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
* Front door: a "Migrate a coinos account" button under the sign-in doors, ↵Adam Soltys2026-10-091-24/+67
| | | | | | opening a page that says a wallet and its twelve words come first (Create, or the sign-in doors), and /migrate on a device without a wallet opens on that page instead of an unexplained welcome; while a move is queued the recovery-phrase step offers no Later. Spending step: an account whose custom name the registrar handed back (a wallet a sign-in restored) leaves the wizard instead of being shown a claim form it could only retype its own name into — claiming it again "succeeded" onto the same page; the claim form also waits behind "Getting things ready…" until the registrar lookups settle Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Restart wheel coast timing and respond immediately on reversalAdam Soltys2026-10-091-3/+12
|
* Feed scrolls faster on a desktop: a wheel notch pushes a quarter further, ↵Adam Soltys2026-10-091-3/+3
| | | | | | and a grab-and-flick throws twice as hard with a higher top speed Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Grab-and-flick no longer opens the post: a flick that starts a little ↵Adam Soltys2026-10-091-8/+13
| | | | | | sideways still scrolls (only a clearly sideways drag selects text), and any press that moved eats the click Chrome still fires. A wheel push at the very top of the page no longer dies on its first frame (a frame stamp older than the push read as a step up) Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Desktop feed scrolls like a phone: a mouse-wheel notch coasts and slows ↵Adam Soltys2026-10-091-0/+97
| | | | | | (quick turns stack, a reverse turn brakes, trackpads left alone), and a mouse can grab the feed and flick it (a sideways drag still selects text, a drag doesn't click what it started on) Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Footer: "Show your support" beside How it works opens the hat shop from any ↵Adam Soltys2026-10-091-0/+5
| | | | | | page (shown once a wallet is open, since the shop pays from it) Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Keep carousel flick destination through stale scroll-end eventsAdam Soltys2026-10-091-12/+25
|
* Chip strips drag sideways with a mouse (a drag doesn't click the chip it ↵Adam Soltys2026-10-081-0/+32
| | | | | | started on) and show a thin scrollbar on mouse devices, so the feed chips that run past the card are reachable on desktop Co-Authored-By: Claude Opus 5.5 <[email protected]>