aboutsummaryrefslogtreecommitdiffstats
path: root/src/app.js
Commit message (Collapse)AuthorAgeFilesLines
...
* "Unlock for" stays on one lineAdam Soltys2026-08-151-1/+1
| | | | | Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv
* The password prompt asks how long: unlock for 5 minutes, 30, a day, or ↵Adam Soltys2026-08-151-3/+57
| | | | | | | forever — the deadline survives refreshes and relocks on time; the skip button says what it does; the header padlock hangs open in green while you're in Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv
* The balance card's switch becomes Manage accounts, the header chip retires, ↵Adam Soltys2026-08-151-25/+8
| | | | | | | Move money is gone — and pasting an on-chain address in a Spending wallet just exits ark to it Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv
* Wallets go flat: every Spending and Savings is a top-level wallet in the ↵Adam Soltys2026-08-151-28/+138
| | | | | | | switcher, named for its purpose, renamable apart — and a new wallet can carry its own seed or live at the next BIP84 account of one you already back up Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv
* The header speaks in icons: gear for settings, padlock to lock, and the ↵Adam Soltys2026-08-151-20/+33
| | | | | | | avatar goes straight to your profile — the popup menu retires Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv
* Threads, search, and zap setup become real places: hardware Back walks out ↵Adam Soltys2026-08-151-2/+9
| | | | | | | of a post to the profile it came from, and a refresh mid-thread reopens the thread Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv
* A refresh stays where you were: the navigation snapshot in history.state ↵Adam Soltys2026-08-151-0/+15
| | | | | | | survives reload and is re-applied on boot — profiles, tabs, settings pages all hold their ground Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv
* The social layer grows up: a header magnifier finds anyone on nostr, ⚡ ↵Adam Soltys2026-08-151-1/+31
| | | | | | | zaps your saved amount in one tap (asking once if you haven't picked one), and tapping a note opens its thread Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv
* The DOM is patched, not replaced: renders morph the live tree in place so ↵Adam Soltys2026-08-151-8/+88
| | | | | | | hover, focus, selection and scroll simply persist — with a smoke test that walks the app and proves node identity survives Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv
* Background repaints breathe at 200ms instead of strobing at frame rate — a ↵Adam Soltys2026-08-151-7/+12
| | | | | | | resting cursor keeps its hover through a boot burst Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv
* Settings grows a Notifications page — per-category switches the notifier ↵Adam Soltys2026-08-141-0/+2
| | | | | | | honours server-side — and the status bar gets a real coinos badge instead of a black blob Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv
* Holding backspace works in the recipient field: search results and reveals ↵Adam Soltys2026-08-141-7/+31
| | | | | | | stop rebuilding the focused input mid-keypress; the profile npub is itself the copy button Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv
* Spending waits to be asked for: a wallet is born on-chain only, the toggle ↵Adam Soltys2026-08-131-100/+49
| | | | | | and Move money appear once it's set up (or money's already there), and the tour is gone Co-Authored-By: Claude Fable 5 <[email protected]>
* A passphrase is typed twice, while the field to fix it is still on screen; ↵Adam Soltys2026-08-131-28/+56
| | | | | | the tour stops flickering under a busy first scan and finds the balance card it lost Co-Authored-By: Claude Fable 5 <[email protected]>
* The passphrase explains itself: a tap on "What's this?" says it's an ↵Adam Soltys2026-08-131-1/+9
| | | | | | optional 13th word, not a login or encryption Co-Authored-By: Claude Fable 5 <[email protected]>
* The door asks less: Get started shows your seed, and the wallet greets you ↵Adam Soltys2026-08-131-62/+105
| | | | | | in Savings — naming yourself waits until after the tour, as an offer to set up Spending Co-Authored-By: Claude Fable 5 <[email protected]>
* Switching accounts hangs up first: feature runtimes stop on lock and ↵Adam Soltys2026-08-081-0/+9
| | | | | | | activate, so no one inherits another wallet's chats or steals its inbox Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01DbA53PJA8fgSvMcEzfuw22
* Choose your look shows the look: one big avatar previews the pick — punks ↵Adam Soltys2026-08-081-1/+8
| | | | | | | and uploads just change it Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01DbA53PJA8fgSvMcEzfuw22
* The wizard waits out a signer login; a picked recipient shows its work, and ↵Adam Soltys2026-08-081-5/+10
| | | | | | search answers from memory before the network Co-Authored-By: Claude Fable 5 <[email protected]>
* Profiles wear their latest notes, each one zappable; a zap with no ark ↵Adam Soltys2026-08-081-0/+2
| | | | | | address to land on becomes a gift only their key can open Co-Authored-By: Claude Fable 5 <[email protected]>
* Spending trusts its own ledger: gifts read persisted ark state, and Savings ↵Adam Soltys2026-08-051-2/+4
| | | | | | | keeps its pending line to itself Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01M5kFodm9azZJVTQxnkDdKe
* The Nostr card owns its built-in account; the exported key opens this wallet ↵Adam Soltys2026-08-041-0/+1
| | | | | | | anywhere Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01M5kFodm9azZJVTQxnkDdKe
* A wallet too small to renew warns before its coins expire; dust never diesAdam Soltys2026-08-041-0/+1
| | | | | | | | | | | | | | | | | | The warning: when the Spending balance minus round fee is under the 330-sat round minimum AND a vtxo enters the renewal window, auto-refresh can't save the coins — a wallet-screen notice (and a daily local notification, when permitted) says to receive any amount or spend before the deadline. New walletNotices feature hook renders under the balance card. The amnesty: sub-dust vtxos have no unilateral exit to protect, and regtest proves captaind keeps cosigning their spends past expiryHeight — even after the watchman claims the backing on-chain (tools/ark/expired-dust-test.js). Only hal's own receive validation stood in the way; it now exempts sub-330 vtxos from the expiry check. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015QwAtBSpKvECqp1rHLHjJk
* More options also points at Savings for a plain on-chain addressAdam Soltys2026-08-041-0/+1
| | | | | | | | | Not a pane of its own: a regular Bitcoin address means receiving to Savings, so the entry switches the account selector over and lets the savings receive view show its fresh address. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015QwAtBSpKvECqp1rHLHjJk
* The wizard offers a profile, it doesn't overwrite oneAdam Soltys2026-08-041-1/+4
| | | | | | | | | Finishing the avatar step published name = the payment-address name and the default punk under the user's real nostr identity — renaming (and re-facing) profiles that already existed. Wizard-suggested fields are fill-only now: they land only where the profile has nothing. Co-Authored-By: Claude Fable 5 <[email protected]>
* No logo in the wizard; every step has a way back insteadAdam Soltys2026-08-041-5/+8
| | | | Co-Authored-By: Claude Fable 5 <[email protected]>
* The logo rides along on every wizard stepAdam Soltys2026-08-041-1/+5
| | | | Co-Authored-By: Claude Fable 5 <[email protected]>
* The wizard can no longer trap anyoneAdam Soltys2026-08-041-2/+10
| | | | | | | | | Publishing the profile on the avatar step is best-effort now — a signer that can't sign (a sleeping phone bunker) skips it with a toast instead of erroring the Continue button forever. And once a wallet exists, the logo leads home even out of a resumed wizard. Co-Authored-By: Claude Fable 5 <[email protected]>
* The logo leads home from the name page tooAdam Soltys2026-08-041-0/+1
| | | | Co-Authored-By: Claude Fable 5 <[email protected]>
* Copy joins the pencil inside the address boxAdam Soltys2026-08-041-1/+1
| | | | Co-Authored-By: Claude Fable 5 <[email protected]>
* Choosing a name gets a page of its ownAdam Soltys2026-08-041-2/+2
| | | | | | | The pencil opens a dedicated screen — no balances, no tabs — wired into the history stack like the other sub-pages. Co-Authored-By: Claude Fable 5 <[email protected]>
* A restored login ends the wizard instead of stalling in itAdam Soltys2026-08-041-0/+8
| | | | | | | | | | Signing in mid-wizard with a key whose wallet was restored left you on the sign-in step: the back-button hold matched the restored account and blocked the auto-advance. A login now clears the hold explicitly — and a restored wallet skips the rest of onboarding entirely; it was already onboarded wherever it came from. Co-Authored-By: Claude Fable 5 <[email protected]>
* Back and Skip become a button row; the coinos link says what it doesAdam Soltys2026-08-041-2/+3
| | | | Co-Authored-By: Claude Fable 5 <[email protected]>
* A way back from the username stepAdam Soltys2026-08-041-3/+9
| | | | | | | | | Back from username (and sign-in) holds the auto-advance for the wallet you backed away from, so welcome doesn't bounce you straight forward; Get started then continues with the already-minted wallet instead of minting another. Co-Authored-By: Claude Fable 5 <[email protected]>
* Park the send field above the phone keyboard; wizard whenever walletlessAdam Soltys2026-08-041-6/+20
| | | | | | | | | | | | The recipient candidate list was hiding under the on-screen keyboard: scroll the field to the top on focus and when fresh candidates land, give the list scroll room to get there, and let the keyboard resize the viewport (interactive-widget) so the scroll has somewhere to go. The wizard now shows whenever the device has no wallet — the one-time onboarded flag is gone; deleting every wallet brings the wizard back. Co-Authored-By: Claude Fable 5 <[email protected]>
* A refresh mid-wizard resumes the step it was onAdam Soltys2026-08-041-4/+18
| | | | | | | | | | The wizard mints the wallet on its first step, so 'an account exists' stopped meaning 'wizard done' — reloading dropped you into the half-set-up wallet. Persist the current step until the wizard finishes. Also trims the balance coach-mark copy. Co-Authored-By: Claude Fable 5 <[email protected]>
* Keep the punks carousel where you left it, and on the pageAdam Soltys2026-08-041-1/+12
| | | | | | | | | A background repaint rebuilt the strip at scrollLeft 0 mid-swipe; render() now carries scroll positions across the swap the same way it carries focus. The onboarding column also sized itself to the strip's intrinsic width (auto margins defeat flex stretch), overflowing phones below ~450px. Co-Authored-By: Claude Fable 5 <[email protected]>
* Sign avatar uploads — nostr.build stopped taking them bareAdam Soltys2026-08-041-1/+11
| | | | Co-Authored-By: Claude Fable 5 <[email protected]>
* Paste lives in the field; the footer fits; claiming a name comes firstAdam Soltys2026-08-041-18/+31
| | | | | | | | | | | | | | | | | | | | | | | | | | Paste, on the import and load-seed screens, was a bordered box floating beside a small label — heavier than the label, detached from the field it fills. It now sits inside the textarea, and only while that textarea is empty: the only time it's wanted, and the only time it can't cover what you typed. It's toggled on the node rather than through render(), since re-rendering a textarea per keystroke rebuilds it under the caret. Beside a one-line input (Send) the old placement was right and is untouched. The footer measured 100dvh, which on a phone can resolve to the tall viewport at first paint and leave the language picker just below the fold. svh is the one that fits with the address bar showing. Short screens also tighten their padding, which buys back the ~50px the create/import screen was overflowing at 375x553. And the wizard no longer asks every new user whether they have an old coinos.io account before letting them have a name. Claiming the name is the step; the people who do have an old account know it, and say so with a link. That screen is a statement now, not a question they just answered. The npub-shaped default address is no longer offered as a button — a plain skip goes past without showing anyone that. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
* Put the ostrich on the sign-in buttonAdam Soltys2026-08-041-1/+5
| | | | | | | | | Same construction as the sign-in button on the unlock screen — .btn-block forces display:block, so the flex centering lives on an inner wrapper rather than the button. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
* One question fewer on the way in, and an 'or' that means itAdam Soltys2026-08-041-20/+12
| | | | | | | | | | | | | | | | | | | | | The sign-in screen stacked 'Connect a signer app' directly above a bunker/nsec field, which reads as two steps rather than two ways in. A divider says which it is. The wizard's second step asked whether you have a nostr account, then sent you to a screen that asks the same thing. Step one now offers both paths directly — Get started, or sign in with Nostr — and the middle step is gone, along with its strings. The welcome blurb goes too: the screen is one decision, and prose above it is something to read before you can make it. The footer sinks to the bottom of the window rather than trailing the content up the page. #app is a full-height column now; the chat card sizes itself against the viewport, so the layout test covers it at three sizes. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
* Offer a password the first time you lockAdam Soltys2026-08-041-9/+37
| | | | | | | | | | | | | | | | | | | | | Locking with no password locks nothing — the wallets sit on the device under an empty one and reopen with a tap. So the first deliberate lock now asks whether to set one, explaining plainly what happens if you don't. 'Not now' is a real answer, recorded, and never asked again: a wallet that nags is a wallet people stop reading. The offer rides the deliberate lock only. lock() is also where the idle timer, the last-wallet-removed path and boot-after-expiry land, and a password form appearing because you went to lunch is a different app than the one you left. Two subtleties worth naming: the offer is set after lock()'s ui reset block, which clears ui.pw and had been swallowing it; and taking the offer re-enters lock(), which now finds a password and lands on the unlock screen instead of the wallet list. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
* Call it 'Lock wallet', because that is what it doesAdam Soltys2026-08-031-1/+1
| | | | | | | | | | | | | | | | 'Logout' promises a return to a fresh app with nothing of yours left on it. This closes the open wallet and keeps everything saved, which is why it read as interchangeable with 'Clear all' — the one that really does send you back to the beginning. Renamed via a new key rather than rewording the old one: nineteen locale tables carry their own word for 'Logout' and t() prefers the locale's value, so reusing the key would have kept the wrong meaning in every language but English. The dead key is dropped from those files; they fall back to English until translated. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
* Logout shouldn't ask for a password nobody setAdam Soltys2026-08-031-4/+16
| | | | | | | | | | | | | | | | | | | | | | | Wallets save themselves to this device as soon as you open one, encrypted under an empty password unless you choose one. Logging out still sent you to 'Unlock saved wallets — enter your password', which asks for something that doesn't exist and makes logout look like it did something drastic. It didn't: logout only drops the keys from memory, and 'Clear all' is the one that deletes. With no password, logout now returns to the wallet list — honest, since nothing is protecting those wallets and a blank prompt protects nothing. With a password set it prompts exactly as before. Fixed underneath: mergeVaultList derived the comparison xpub with accountXpubFor's mainnet default, so on any other network it never matched the stored one and every unlock re-added the same wallet as a fresh entry — visible as the same wallet listed twice, once watch-only. The seed-mismatch checks in 'load recovery phrase' had the same default and would have rejected the correct seed on testnet. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
* Messages in the header, and a room you can tell is aliveAdam Soltys2026-08-031-0/+18
| | | | | | | | | | | | | | | | | | | | | | | | | | | | The header carries a message button again, left of the wallet selector, with a red dot when a conversation is waiting. Unread is a per-thread watermark on the newest message from someone else, kept in the rumor's own created_at so it means the same thing on every device it syncs to. Rooms subscribe at startup now, or the dot could not speak for a community nobody was listening to. Communities no longer buzz a phone by default: push registration only carries the stream keys of communities you have opted in, per community, from a bell in the room header. DMs and payments are unchanged. Presence and typing ride the channel stream as 21059 wraps, so who is around stays inside the community's encryption rather than becoming a public status. Measured against relay.coinos.io and nos.lol, neither actually drops NIP-01 ephemeral kinds, so beats carry a NIP-40 expiration and relay.coinos.io sweeps kind 21059 hourly — and presence is dated by the beat, never by arrival, or a replayed beat would light up the whole member list. The member list follows Telegram: online, then last seen in minutes and hours, then a vague 'recently' rather than advertising how long someone has been gone. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
* Auto-withdraw runs with the app closed; fix the dead Back on the legacy stepAdam Soltys2026-08-031-1/+2
| | | | | | | | | | | | | | | | | | | | | The service worker can't watch a relay, so it doesn't try: the app resolves the destination to something concrete (ark address, LNURL endpoint, or on-chain script) and mirrors it beside the keys the background NWC payer already uses. On a push wake-up — which is exactly when money has just landed — the worker re-checks the same gate and sends, writing the outcome back into the mirror for the app to absorb on next open. A partial on-chain forward still waits for the app, since carving an exact vtxo needs more round trips than a worker is promised. writeBgMirror now carries forward the NWC connections and offer when another caller refreshes the mirror, instead of blanking them. And onboarding's legacy step loses its Back: the wallet exists by then, so returning to the nostr question just bounced straight forward again and the link looked broken. Its copy no longer promises a rename either. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
* Save wallets by default; reveal the recovery phrase in two stepsAdam Soltys2026-08-031-9/+37
| | | | | | | | | | | | | | | | A seed that only lives in this tab is one refresh from gone, and that surprise costs people money — so a wallet now saves itself to the device when it's opened or committed, no button required. No password is set (Settings still offers one), and a locked existing vault is left to the manual Save button since writing it needs their password. The recovery card gains a middle step: the first click opens the grid with the words masked — the warning and the shape of what's coming are on screen before anything readable is — and a second click shows them. Copy only appears once they're visible. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
* Balance card: drop the descriptor lineAdam Soltys2026-08-031-3/+0
| | | | | | | | | | "Instant · free to send" and "On-chain · mining fees apply" were one line that swapped with the account; the card is just the balance now. Strings, styles and stale translations removed with it, and the face's reserved height comes down to match. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
* Balance card: a real button to change account, not two dotsAdam Soltys2026-08-031-3/+10
| | | | | | | | | | | The dots were an indicator pressed into service as a control — fine under a thumb that can just swipe, too small for a mouse. They're replaced by a pill in the header row naming the account it takes you to (⇄ Savings / ⇄ Spending), which is both a proper click target and a clearer answer to "what's the other one?". Swiping the card still works. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
* Onboarding: no waiting state before the migration questionAdam Soltys2026-08-031-12/+24
| | | | | | | | | | The step needed this wallet's payment address for the migrate link, and the background claim often hasn't landed yet when the step appears — so it flashed a spinner first. The page renders complete now; if someone taps before the address exists, the wait happens on the button instead. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi