aboutsummaryrefslogtreecommitdiffstats
Commit message (Collapse)AuthorAgeFilesLines
...
* Paginate History: cap inline gifts at 3, transactions at 10Adam Soltys2026-06-242-23/+73
| | | | | | | | | Outstanding gifts no longer dominate the History page — show at most 3 inline with a 'View all gifts (N)' button that opens a paginated gift-only list. Transactions paginate 10 per page with prev/next controls. Both reuse a shared pager helper; the all-gifts sub-view resets when leaving the History tab. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Show outstanding sent gifts in History, cancellable thereAdam Soltys2026-06-242-2/+39
| | | | | | | | | Unclaimed gift links now appear at the top of the History list (they aren't on-chain transactions until claimed or revoked). Each is tappable to cancel via the same reclaim/revoke flow as the Send → gift card, reusing ui.revokeId and the doReclaim/doRevoke handlers. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Instant gift-via-split and spend-unconfirmed supportAdam Soltys2026-06-243-36/+98
| | | | | | | | | | | | | | | | Gift split no longer makes the sender wait for confirmation: createGiftFromSplit broadcasts the carve-out, waits only until it's visible in the mempool, then builds the gift spending that still-unconfirmed coin. Funding + claim form a chain that confirms together; the recipient sees a pending balance until then. Polls for mempool visibility before reserving so a background refresh can't drop a not-yet-indexed coin and prune the reservation (double-spend risk). Regular sends can already draw on unconfirmed coins; surface that honestly: a 'pending' tag on unconfirmed coins in coin control, and a plain-language warning at review when a payment spends unconfirmed bitcoin (won't confirm until the ancestor does). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Minimize gift lock-up: best-fit coins + optional pre-splitAdam Soltys2026-06-243-19/+112
| | | | | | | | | | | | | | Gift coin selection now picks the smallest single coin that covers the gift (was largest-first), locking the least value until claim. When no near-exact coin exists, offer to pre-split: broadcast a self-send carving out a gift+dust coin so the gift later locks only ~the gift amount. The split is only offered when the liquidity it frees (lock - gift - dust) exceeds the split's own fee, so it never costs more than it regains. Decision is surfaced as an interstitial on Create, since a full re-render drops input focus and a live preview isn't viable. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Deduct gift-locked coins from the spendable balanceAdam Soltys2026-06-243-5/+26
| | | | | | | | | Coins reserved behind unclaimed gift links were already excluded from spending, but the headline balance still counted them. Show the spendable balance (total minus gift locks) as the headline and surface the locked amount as its own line, so a gift debits immediately rather than at claim. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Gift links: stop pre-reserving fee, show it at claim timeAdam Soltys2026-06-243-13/+21
| | | | | | | | | | | | | | Previously createGift reserved a claim-fee headroom on top of the entered amount based on the fee rate at creation. Because the fee was only actually charged at claim time, fee-rate drift between the two moments silently ate into (or padded) the recipient's amount. Now the entered amount is exactly the recipient's full received total (sum of inputs less our change); no fee is reserved. The claim screen shows that full total as the headline and surfaces the live network-fee estimate on the Claim button, where it's deducted from the recipient's balance at claim time. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Simplify import placeholder to 'Your seed words'Adam Soltys2026-06-241-1/+1
| | | | Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Add a 🎁 to the Gift some Bitcoin linkAdam Soltys2026-06-241-1/+1
| | | | Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Fix dark-mode contrast and stop install-link jitterAdam Soltys2026-06-242-12/+43
| | | | | | | | | | | - warn-box, addr-box, summary, toast and green badge/notice borders used hardcoded light colors that washed out or vanished in dark mode; route them through theme variables (new --toast-bg/-fg and --green-line pairs). - Drive the footer "Install app" link from a persisted localStorage flag instead of the live beforeinstallprompt event so it's present on first paint after a refresh, eliminating the layout shift. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Add dark mode; gift amount unit toggle; multi-input giftsAdam Soltys2026-06-2424-50/+154
| | | | | | | | | | | | | | - Dark mode: theme variables + a footer "Dark mode/Light mode" toggle, persisted and applied pre-paint via a small head script (defaults to the OS preference). Hardcoded surfaces moved to CSS variables. - Gift amount: the unit label is now the clickable sats/BTC toggle, and the amount is parsed in the selected unit. - Gifts can now span multiple coins: createGift selects several confirmed coins (first input SIGHASH_SINGLE to lock the change, the rest SIGHASH_NONE), so a gift larger than any single coin works. Claim/preview sum all inputs and size the fee by input count. Verified end-to-end. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Move the language picker from Settings to the footerAdam Soltys2026-06-241-8/+3
| | | | | | It now lives in the global footer, available on every screen. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Relabel tx explorer link to "View on block explorer"Adam Soltys2026-06-2420-20/+20
| | | | | | | The link already targets the selected explorer (explorerTx); only the label said mempool.space. Now generic, across all 19 locales. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Guard the receive celebration to recent payments onlyAdam Soltys2026-06-241-3/+11
| | | | | | | | | Defense-in-depth so the "payment received" screen can never fire for an old payment on opening a wallet: in addition to the ack baseline, require an incoming tx that is pending or confirmed within the last ~2 hours. Verified it still celebrates genuine new (pending/recent) payments. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Remove the caret from the account selector chipAdam Soltys2026-06-241-1/+1
| | | | Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Drop the "saved" tag from the account titleAdam Soltys2026-06-241-1/+1
| | | | | | It was redundant with the "Saved · forget" link shown beneath the name. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Rename "Lock all" to "Clear all" with confirmation; editable wallet namesAdam Soltys2026-06-2421-21/+131
| | | | | | | | | | | | - The Accounts action is now "Clear all", which prompts a confirmation warning that any wallet whose recovery phrase/key isn't backed up will be lost. Confirming wipes everything from the device: session accounts, the encrypted vault, and saved watch-only accounts. - Each account has a rename (✎) control: edit the label inline and save. Persists to the session, the watch-only store, and the vault as needed. New strings translated across all 19 locales. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Fold watch-only into Import; support xprv spending importAdam Soltys2026-06-2422-116/+199
| | | | | | | | | | | | | | | | | | Home screen is back to two tabs (Create / Import). The Import box now takes a recovery phrase, an xpub/zpub, or an xprv/zprv: - mnemonic → full wallet (as before) - xpub/zpub → watch-only (view/receive, no Send) - xprv/zprv → full spending wallet with no mnemonic. parseExtendedKey classifies + normalizes version bytes; account() derives the BIP84 path from a master xprv (depth 0) or uses an account-level xprv as-is; signing works (only watch-only blocks it). Vault persists xprv accounts too; Settings shows an "Imported key" note instead of a recovery card and hides Nostr sync (no seed). Public-key export still works. Note: a seed phrase cannot be derived from an xprv (one-way KDF), so an xprv wallet has no recovery words by design. New strings in all 19 locales. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Don't celebrate pre-existing payments on opening a walletAdam Soltys2026-06-241-12/+20
| | | | | | | | | | | | | | | | The "payment received" screen is for payments that arrive while you're using the wallet, but the seen-index is persisted per-seed, so re-importing/unlocking/switching to a wallet used before would resurface the celebration for a payment that had already arrived (and confirmed) in between. Opening a wallet (import, unlock, account switch, watch-only add, fresh-session restore) now baselines the ack to the current receive frontier; only a same-session refresh keeps the persisted ack so an unacknowledged live celebration survives the refresh. Verified: with a stale persisted ack, a fresh import shows no celebration and re-baselines to the current frontier. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Fix confirmed txs stuck showing "pending"Adam Soltys2026-06-241-2/+11
| | | | | | | | | | | | | | | | Two paths left a tx's confirmed flag stale once the balance had already reconciled: - The frontier poll re-fetched an address's txs but only appended new ones (`if (!txs.some(...))`), so a tx that moved from mempool to a block kept its old pending status. Now it refreshes the record in place. - On reload, scan() skipped the history re-pull when the cached balance already matched on-chain, so a stale-pending cached tx never updated. scan() now also re-pulls history whenever any tx is still pending. Verified: a cached pending tx that is confirmed on-chain flips to confirmed (and pending balance clears) after a scan. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Add optional password-encrypted persistence for full accountsAdam Soltys2026-06-2424-38/+538
| | | | | | | | | | | | | Per-account "Save on this device" encrypts a seed account into a localStorage vault (scrypt KDF + XChaCha20-Poly1305 via @noble), so it survives a browser close. On reopen, a vault prompt unlocks all saved wallets with the password (or "Use another wallet" to skip). Default stays session-only; the password is held only in memory for the session and re-encrypts on save/forget. Wrong password fails closed (AEAD). Adds @noble/ciphers dependency. New strings translated across all 19 locales. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Add account switching and watch-only walletsAdam Soltys2026-06-2422-94/+664
| | | | | | | | | | | | | | | | | | | | | | Accounts: a switcher (header chip → Accounts screen) lets you keep several wallets loaded and flip between them without re-importing. Per the chosen model, full (seed) accounts live only in sessionStorage — ephemeral, no seed on disk; watch-only accounts (no seed) are persisted in localStorage and reload across restarts. enterWallet is refactored around an accounts list (add/switch/remove/lock); Add wallet reaches the unlock screen with a back button. Watch-only: load any wallet from its account xpub/zpub (parseAccountKey normalizes zpub→xpub; the existing public-derivation path yields the same addresses). Signing is blocked, the Send tab and seed/sync/offline cards are hidden, and a watch-only note is shown. Settings gains a "Public key" card (export this wallet's zpub) so it can be watched read-only elsewhere. Password-encrypted persistence for full accounts is the planned next step. New strings translated across all 19 locales. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Fee-aware gift minimum; move gift entrypoint to Send pageAdam Soltys2026-06-2422-47/+96
| | | | | | | | | | | | | | | | | | Minimum: the flat 546-sat floor is replaced by a fee-aware minimum (dust + one claim fee of headroom, floored at 546) via giftMinimum(), so the recipient clears dust comfortably even if fees rise before they claim — e.g. ~1000 sats at 5 sat/vB, ~7300 at 50. The create form shows the live minimum and rejects below it; buildClaimTx keeps the hard 294-sat dust guard as a final backstop (claim fails rather than handing over dust). UI: the gift entrypoint moves from Settings to a "Gift some Bitcoin" link on the Send page, opening the gift view as a send sub-view with a back button. New strings translated across all 19 locales. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Keep reclaimed gifts revocable until the coin is spentAdam Soltys2026-06-2422-68/+134
| | | | | | | | | | | | Passive reclaim previously dropped the coin from tracking, so there was no way to revoke afterward. Now reclaim moves the coin from "reserved" to a "reclaimed" set: it becomes spendable, but its still-live link stays listed (tagged "still claimable") with a Revoke option, until the coin is actually spent (by a payment or by revoking), at which point it drops off. revokeGift and outstandingGifts handle both sets; existing reserved gifts are unaffected. Strings updated across all 19 locales. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Offer passive reclaim vs immediate revoke for giftsAdam Soltys2026-06-2421-67/+118
| | | | | | | | | | | | The reserved-coin button is now "Reclaim". Tapping it prompts a choice: - Reclaim (free): just un-reserves the coin so a future payment can spend it — no fee, but the link stays claimable until then. - Revoke now (pays a fee): sweeps the coin back immediately, killing the link once it confirms. New/updated strings translated across all 19 locales. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Make gift Reclaim a real on-chain revokeAdam Soltys2026-06-2422-54/+136
| | | | | | | | | | | | The old "Reclaim" only un-reserved the coin locally, so the gift link stayed claimable. Replace it with "Revoke": a confirmed action that sweeps the reserved coin back into the sender's own wallet at a high fee (wallet.revokeGift), double-spending the gift's presigned input so the link can't be claimed once it confirms. Races a simultaneous claimer; if the gift was already claimed the broadcast just fails. New strings translated across all 19 locales. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Replace seed gift links with presigned PSBT claim linksAdam Soltys2026-06-2422-117/+460
| | | | | | | | | | | | | | | | | | | | | | Gifting no longer shares a seed. "Gift some Bitcoin" (Settings) presigns a chosen amount as a #gift= link: one SIGHASH_SINGLE input (the sender's change output is cryptographically fixed, so a front-runner can only take the gift portion, never the change) plus the rest left open. The coin is reserved per-wallet so normal sends skip it; reclaim it anytime. Opening the link generates a brand-new wallet the sender never sees, shows the amount, and on Claim adds the claimer's address as the output, finalizes, and broadcasts — then prompts backup of the fresh seed. This gives forward secrecy (sender locked out once confirmed), one-shot semantics (claiming spends the input), and keeps funds in the sender's wallet until claimed. Crypto verified end-to-end (sign NONE/SINGLE -> add output -> finalize -> valid tx; deterministic-signature equality proves the presigned sig is valid for any output the claimer picks). Strings retranslated across all 19 locales. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Add shareable gift links for fast onboardingAdam Soltys2026-06-2422-38/+344
| | | | | | | | | | | | | | | | Create a #claim=<code> link from Settings → "Gift this wallet" (gated behind a reveal + warning, with a QR): the seed (and passphrase, if any) are encoded as BIP39 entropy in base64url in the URL fragment, which never reaches a server. Opening such a link loads that wallet and shows a welcome → balance → firm "back up your recovery phrase" flow, then drops into the wallet. The claim code is scrubbed from the address bar on load. Bearer by design — whoever opens the link controls the funds — so the creator UI warns to keep amounts small. New strings translated into all 19 locales. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Add fee-rate picker to RBF bumpAdam Soltys2026-06-2422-64/+176
| | | | | | | | | | | Bump now opens its own screen with the Economy/Normal/Priority/Custom fee tiers (defaults to Priority) and a live "fee: old → new" preview as you change the rate, then Replace builds + signs + broadcasts. Refactor bumpTx into prepareBump (async fetch/reconstruct) + planBump (pure preview) + buildBump (sign), so the picker can preview each rate without rebuilding. New bumpInsufficient string translated into all 19 locales. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Add RBF fee bumping for unconfirmed sendsAdam Soltys2026-06-2423-21/+194
| | | | | | | | | | | | | | | | | | - Signal opt-in RBF (BIP125, sequence 0xfffffffd) on all new sends so they're replaceable. - "Bump fee" on an unconfirmed sent tx (its detail view): rebuilds the same inputs + recipient outputs at the Priority rate, taking the extra fee from the change output (pulling in a confirmed coin if change can't cover it), re-signs, and routes to a "Replace with a higher fee?" confirmation showing old → new fee before broadcasting. - New keys translated into all 19 locales. Honest scope: P2A/TRUC/package-relay are L2/pre-signed-tx + node-relay features that don't apply to a single-sig wallet broadcasting single txs via public explorers, so they're intentionally out. CPFP (for receives / unbumpable sends) is a possible follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Split translations into per-language files, lazy-loadedAdam Soltys2026-06-2423-1459/+2816
| | | | | | | | | | | | | | | | Only English is bundled in index.html now; the other 19 languages are emitted as dist/locales/<code>.json and fetched on demand when a visitor actually uses one (via auto-detect, the picker, or ?lang=xx). This drops index.html from ~511KB to ~318KB; a non-English visitor fetches just their one ~8-10KB locale, which the service worker then caches for offline use. t() falls back to English until a locale resolves. - i18n.js keeps only en + a loadLocale() fetcher; locale data moved to src/locales/*.json. - build.js emits dist/locales/; dev.js serves /locales/<code>.json. - Deploy hook publishes the locales/ subdir. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Update README for Hal Wallet; fill in all translationsAdam Soltys2026-06-242-34/+641
| | | | | | | | | | | | | - Rewrite README: Hal Wallet name, halwallet.app, configurable explorer, QR scanner, optional/configurable Nostr sync, PWA, updated layout and reconcile/security notes. - Translate the ~31 UI keys added this cycle (explorer, device sync, QR scanner, How it works, install, etc.) into all 19 languages. Brand and technical tokens (Hal Wallet, Hal Finney, mempool.space, BIP39/BIP84, Sparrow/Electrum, Nostr, URLs) kept literal so linkify still matches; appTitle stays "Hal Wallet" via English fallback. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Point GitHub footer link at renamed repo (asoltys/halwallet)Adam Soltys2026-06-241-1/+1
| | | | Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Trim blockstream mention from explorer setting descriptionAdam Soltys2026-06-241-1/+1
| | | | Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Add Hal Finney tribute line to How it worksAdam Soltys2026-06-243-2/+8
| | | | | | | | | A footnote at the bottom: "The name Hal Wallet is a tribute to legendary cypherpunk and Bitcoin pioneer, Hal Finney (1956–2014)", with his name linking to his Wikipedia article. The contextual nod, rather than leaning on his name up front. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Choose your own block explorer; flatten How it worksAdam Soltys2026-06-245-65/+113
| | | | | | | | | | | | | | | - Settings → Block explorer: pick mempool.space (default, with blockstream.info as a silent failover), blockstream.info only, or a custom Esplora/electrs REST URL (e.g. your own node). The realtime WebSocket only applies to mempool.space; other explorers fall back to polling. Switching rebuilds the API and rescans. Replaces the old undocumented explorer-proxy hack. - How it works: merge the "For the technically curious" disclosure into the main body so it's all visible under "The basics", and dedupe the overlapping no-server/offline sentences. Mentions the new explorer setting. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Rebrand to Hal Wallet; roomier language-select caretAdam Soltys2026-06-244-26/+32
| | | | | | | | | | | - Name the app "Hal Wallet" (header, title, PWA manifest, package.json). A nod to Hal Finney — just the name, no taglines or tributes. - appTitle is a brand name now, so it's constant across locales (dropped the translated overrides; they fall back to "Hal Wallet"). - Replace the native <select> caret with a custom chevron spaced off the right border (the native one is crammed against the edge in Chrome). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Configurable Nostr sync; fully reconcile wallet state on loadAdam Soltys2026-06-244-53/+179
| | | | | | | | | | | | | | | | | | | | | Device sync: - Add a "Device sync" settings card: toggle (on by default) + editable relay list (defaults to wss://relay.coinos.io). When off, the wallet never contacts a relay. nostr.js is now multi-relay and reads an enabled/relays preference from localStorage. Reconciliation (fixes a stray "payment received" + stale history on import, especially via the Nostr/cache load path): - Always full-scan on load instead of a frontier-only refreshLive, so balance, history confirmations and spends are reconciled and the celebration baselines against everything that already exists (cache/ Nostr still shows instantly first). - Actually start the periodic full-scan safety net (_deepTimer, 2 min); it was declared but never wired up. - WS pushes now refresh an existing tx's pending→confirmed status, not only add new transactions. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Reconcile spends of held coins on refreshAdam Soltys2026-06-231-0/+58
| | | | | | | | | | | | refreshLive only walked the frontier forward, so a coin spent from an already-scanned address never reconciled — the balance stayed stale until a full rescan, even after the spend confirmed (e.g. a tx broadcast from another device). Add _reconcileHeld: re-verify every address that currently holds a UTXO and rebuild its balance/UTXOs from the explorer, catching spends whether pending or confirmed. Runs in the refreshLive path (load, poll, WS), so a refresh now self-corrects. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Debit balance optimistically on pending send; pointer cursor on summariesAdam Soltys2026-06-232-1/+4
| | | | | | | | | | | | | - Headline balance now shows wallet.total (confirmed + mempool) instead of confirmed, so a pending outgoing spend is debited immediately rather than waiting for confirmation. The chain_stats-based confirmed balance counts a coin as yours until the spend is mined, which is why it didn't drop after broadcasting (incl. scanned-tx broadcasts). Incoming pending is still surfaced by the "+X pending" line. - summary { cursor: pointer } so the "Show QR" disclosure (and any other <summary>) shows the hand cursor. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Show full recipient address below the input for verificationAdam Soltys2026-06-232-2/+31
| | | | | | | | | The address input scrolls/truncates on mobile, hiding the ends. Render the full address as wrapping text beneath it with the first/last 6 chars emphasized, so paste/type/scan can be verified without scrolling. Updated imperatively on input so the field keeps focus. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Add QR scanner to the send page (lazy-loaded decoder)Adam Soltys2026-06-2310-4/+332
| | | | | | | | | | | | | | | | | | | Scan a Bitcoin address, a BIP21 URI (fills address + amount), or a signed-transaction QR (opens a broadcast confirmation, then relays it) from a viewfinder button on the Send form. Decoder strategy keeps the main bundle small: - Prefer the native BarcodeDetector (Chrome desktop/Android) — no lib. - Otherwise lazy-load jsQR from a separate dist/jsqr.js, only when the scanner is opened. jsQR stays out of index.html (~424 KB vs ~551 KB) and off the initial load. - The service worker precaches jsqr.js so offline scanning works for installed PWAs on browsers without BarcodeDetector. The scanner needs a camera + secure context, so it never shows on a standalone file:// page; offline air-gapped signing is unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Allow signed-tx export when online; clarify it isn't sentAdam Soltys2026-06-232-3/+9
| | | | | | | | | | | - Add a "Sign & export (broadcast elsewhere)" action on the online review screen so you can sign here and broadcast from another device, not just in offline mode. - The signed-result screen now reads as an amber caution ("Signed, but not sent yet… it will not send on its own") instead of a green success banner, so it can't be mistaken for a completed send. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Match Hide button font to Copy phrase in settingsAdam Soltys2026-06-231-1/+1
| | | | | | | The reveal-phrase Hide button used the default size (16px) next to the btn-sm Copy phrase button (13px). Add btn-sm so they match. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Add PWA install affordance; serve manifest as manifest+jsonAdam Soltys2026-06-232-1/+35
| | | | | | | | | | | Chrome no longer shows an automatic install prompt, so catch beforeinstallprompt and surface an "Install app" link in the footer that replays the prompt on a user tap (hides once installed). (nginx on the host now serves manifest.webmanifest as application/manifest+json instead of octet-stream.) Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Fix false "payment received" on import; PWA; send-page fontAdam Soltys2026-06-227-8/+108
| | | | | | | | | | | | | | | - Don't celebrate historical payments on import. receiveSeenIndex was baselined to 0 before the scan ran, so when the scan advanced the index every past payment looked new and flashed "payment received". Leave it null until the post-scan ack logic baselines it. - PWA: add a web manifest, ₿ icons (192/512 + maskable), theme/apple meta tags, and a service worker that precaches the shell so the installed app works offline. index.html stays self-contained; the sidecar files just 404 harmlessly from file://. build.js emits them. - Send page: drop btn-sm from the amount unit toggle so "sats" matches the "Max" button size (both 16px). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Trim How it works technical sectionAdam Soltys2026-06-212-8/+23
| | | | | | | | | | - Drop the @scure/@noble crypto paragraph and the passphrase paragraph. - Link mempool.space in the no-backend paragraph; trim its trailing "only your addresses leave the device" clause. - "This page is one static HTML file" (was "The whole thing"). - Remove the cold-storage sentence from the offline paragraph. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Make How it works copy more human; tidy passphrase fieldAdam Soltys2026-06-213-19/+12
| | | | | | | | | | | | | - Rewrite the How it works page in a plainer, less generated voice; drop the intro lead, the receive/send paragraph, and trim the rest. - Fix passphrase wording: "13th word" (12-word phrase), not 25th. - genIntro: "It's" instead of "It is". - Unlock screen: label the field "Passphrase" (was "Optional passphrase") and drop the "leave blank if unused" placeholder. - Remove now-unused hiwLead / optionalPassphrase / leaveBlank keys and the .lead style. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Add How it works page, plainer copy, faster import scanAdam Soltys2026-06-194-156/+1373
| | | | | | | | | | | | | | | | | | | | | | | | | | | | UI / copy: - Footer: "made by Adam Soltys" (drop the heart) + a "How it works" link on its own line, matching the footer's size. - New "How it works" page: plain-language basics plus an expandable "for the technically curious" section. Routed via ui.screen, with Back/logo returning to wherever you opened it from. - Scrub jargon from the main UI for newcomers: drop BIP39, soften "raw hex" -> "code", "txid" -> "transaction ID", "air-gapped" -> "offline"; replace em dashes in the intro/keys notes. The technical terms now live only in the How it works technical section. Import / scan performance (fewer requests -> fewer 429s, faster): - Skip the /utxo fetch for fully-settled, zero-balance addresses (they can't hold UTXOs) -- the bulk of a history-heavy wallet. - Show balance as soon as the address scan finishes, then load UTXOs and history in the background. - Probe the receive and change chains concurrently so their per-hit delays overlap. - History fills in progressively as each address resolves, with a loading indicator on the History tab. i18n.js is now tracked; new English keys fall back to English for the other languages until translated. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Add Skip verification on the verify screen tooAdam Soltys2026-06-161-1/+2
| | | | Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* Tidy tx details layout on mobileAdam Soltys2026-06-162-8/+7
| | | | | | | | | Fix the key/value rows wrapping awkwardly on narrow screens (right-align + wrap-safe values, fixed-width key). Drop the redundant "When: Unconfirmed…" line; pending now shows a single "Status: Pending (in mempool)". Confirmed txs show Status / Block / Date. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>