aboutsummaryrefslogtreecommitdiffstats
path: root/src
Commit message (Collapse)AuthorAgeFilesLines
...
* Browser Back leaves the Manage and exit pages: they are history entries now, ↵Adam Soltys2026-09-272-3/+4
| | | | | | | and their own Back pops one. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* The Manage and exit pages keep the top nav; they are pages of the wallet, ↵Adam Soltys2026-09-271-1/+5
| | | | | | | not takeovers. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* An event of a kind this app has no renderer for shows as what its author's ↵Adam Soltys2026-09-272-12/+58
| | | | | | | | | | client said it is (NIP-31 alt), not as raw content dressed as a post. A chess game (kind 64) reached through a reaction to it painted its PGN as a reply. Now it reads "♟ Daily chess game #11: …". Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Expired dust gets a quiet line and a Forget button, not a red warning.Adam Soltys2026-09-273-3/+31
| | | | | | | | | | | A coin past its expiry and under the 330-sat floor can be renewed by nobody and exited by nobody; the home page said "the server can still renew it" in red over 21 sats. Now a muted line points at Manage, where the coin's row offers Forget: held as spent, quietly, so it stops counting and stops warning. Larger expired coins keep the warning. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* The visitor's sheet offers a new account, not a wallet: what they came for ↵Adam Soltys2026-09-271-2/+2
| | | | | | | is a nostr account. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* A renewal the round would refuse is retired, not resubmitted every sync; ↵Adam Soltys2026-09-272-0/+24
| | | | | | | | | | | | | | | /feed opened before the wallet no longer doubles Popular. A refresh at the created step with an input past its expiry (only the server's backstop can renew it now), or whose fee leaves less than the 330-sat floor (a 21-sat coin with a 150-sat fee sat "in progress" for days), is failed quietly and its inputs go back to spendable. /feed read before the wallet restored made a visitor's session copy of Popular beside the built-in one; the wallet's arrival now folds it away. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Faces on hosts that refuse CORS are left alone after two quick refusals.Adam Soltys2026-09-271-1/+25
| | | | | | | | | | | The thumbnail maker fetches each face once to shrink it; a host that serves pictures without CORS headers (primal's blossom, cdn.nostrcheck.me, most personal sites) refuses the bytes and the browser prints a red line per face while the <img> paints fine. The host is now remembered for a week after a second quick refusal and not asked about every new face. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* A visitor who taps zap, reply, react or boost is shown the way in; the ↵Adam Soltys2026-09-273-8/+80
| | | | | | | | | | | | | | public header has Search and Sign in. Reading is free. A tap on an action without a wallet opens a sheet with the sign-in doors (Google, passkey, Nostr), Create a new wallet, and Not now; the missing-identity path lands there too instead of a toast, and zapping is never offered to a visitor as if it could go through. The header on a public page (a feed, a profile, a thread) carries Search and Sign in. The page is remembered, so a sign-in lands back on it. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* The default public feed is Popular, not the firehose: what people reacted ↵Adam Soltys2026-09-274-13/+196
| | | | | | | | | | | | | | | | | | | | | | to, in your language, with the machine posts gone. A brand-new post has no reactions yet (of 73 fresh English posts on the four relays, one had two), so Popular starts from the other end: the latest reactions, reposts and zaps, counted per post by distinct people, then the posts two or more people touched. Older pages walk the reactions back in time; new ones arrive by a two-minute poll. At the door, JSON and other machine output are dropped, and so is anything not in the reader's language (script first, then a vote of function words among the Latin-script languages the app speaks; unknown passes). The raw firehose stays at /feed/all. A language selector sits under every feed, and changing it rebuilds the curated ones. A tall video scrolled past used to let go of its media at once and collapse, shifting the page under a thumb; it now waits a moment and holds its height until the clip is back. A tap on a full-screen picture closes it. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Relays are picked from a list, the firehose reads four of them, and a reader ↵Adam Soltys2026-09-273-32/+133
| | | | | | | | | | | | | | | | | | | | | | | with no web of trust borrows coinos's. Each relay in a feed's list is a select over popular relays that answer a bare "everything" query, with Other… for one typed in; the firehose reads coinos, damus, nos.lol and primal by default. A relay chosen by hand or named in a link is tried even if it had been marked sick, which had made a hand-picked nos.lol fetch nothing. A visitor, or a wallet that follows fewer than five people, has no follows whose flags could fold anything, so the public feeds showed the relays raw. They now get the moderation the coinos account gets: its follows' reports (and, for visitors, its mute list), read every few hours and kept on the device. A wallet's own follows and flags always come first. The new-posts pill: the generic button press moved it half its width to the right (its centring transform was replaced), so the tap never landed, and the generic hover painted it light over light text. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Everything on the coinos relay is a feed of its own, and every feed can read ↵Adam Soltys2026-09-273-38/+137
| | | | | | | | | | | | | | | | | | | | from relays of your choosing. Plain /feed is the firehose: every post relay.coinos.io carries, no filter. It sits beside Following as a built-in chip for wallets and opens as a session feed for a visitor. A feed's relay list is independent of what it asks for. The Relays button in the header opens a panel to add to or take from the list; empty means the usual choice (each author's outbox, the wide relays for a topic, the coinos relay for the firehose). The list rides in the URL as ?r=…, on every kind of feed, so a shared link reads where you read. A feed state dropped while its fetch was still out used to swallow the answers meant for its replacement; a relay change showed nothing. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Every feed has a public URL, and anyone can read it without a wallet.Adam Soltys2026-09-273-13/+173
| | | | | | | | | | | | | | | | /feed/<npub> is the timeline that person sees: their follow list, read from the relays, as a feed. /feed/<naddr> is a follow pack or a follow set, /feed/t/<tag> a topic, and /feed?p=…&t=…&pack=…&name=… any mix. A visitor gets the feed over the front door, can open a post's thread and come back; the composer, chips and editing stay with a wallet. Every feed has a Share button (the built-in Following shares as your own timeline), a profile page has "Their timeline", and Back/Forward walk through feeds by id. Public pages used to miss their repaint: the scheduler only painted the wallet screen, so a visitor's feed filled up invisibly. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Links in posts show as the cards their pages describe.Adam Soltys2026-09-272-0/+95
| | | | | | | | | | | | A plain link becomes an Open Graph card: picture, title, a line, the site. The registrar fetches the page on the reader's behalf (names/preview.js, GET /preview?url=): only public hosts, at most half a megabyte of HTML, cached a day. The client warms the card with the rest of the post so the row arrives whole, keeps a few hundred cards across sessions, and leaves a page with nothing to say as a bare link. Pictures and YouTube keep their boxes. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Sending to a coinos.io name shows the person, not the ark address it became.Adam Soltys2026-09-275-13/+34
| | | | | | | | | The recipient box keeps the name, with their face beside it and "Sent instantly, no fee."; the review names them too. The ark1… the name resolved to is plumbing, and read as gibberish to anyone new to this. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Three fixes: orphaned pending coins settle with the server; a saved start ↵Adam Soltys2026-09-273-3/+27
| | | | | | | | | | | | | | | | | screen no longer shadows a restored wallet; stop() no longer throws. Reconcile now asks the server about a 'pending' coin no live action or standing renewal order holds: spendable again, or gone. Thirty zap-sized coins another device had already spent counted in a balance for weeks. A nav saved by a page with no wallet (the start screen) was restored over a wallet that had since been saved to the device, painting the start page. messages.stop() referenced feedAt, which no longer exists, so the rest of the teardown never ran on an account switch. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Public Sans, the coinos.io face, across the app.Adam Soltys2026-09-271-1/+18
| | | | | | | | | One variable-weight woff2 each for upright and italic (41 KB + 45 KB, OFL), shipped with the build and precached by the service worker; the system stack paints first and stays when the file never arrives. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Copy: no more em dashes, anywhere people read.Adam Soltys2026-09-2730-608/+608
| | | | | | | | | Every English string, the translations, the README and the messages the code shows people now break with a full stop, a colon or a comma instead. Only console logs and the "—" placeholder for an empty cell keep the dash. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Accounts: tapping an identity signs in as it and opens its profile; the ↵Adam Soltys2026-09-272-6/+26
| | | | | | | | | | | | padlock moves from the header to this page. The identity chip used to put the npub in the URL and leave the list on screen (the profile only paints over the wallet screen). Lock/Unlock now sits with the other device-wide actions (log out, clear); the padlock tests reach it through the Accounts page and lose their stale "Wallets" button. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Amber (NIP-55) signs you in on Android: one intent per request, the answer ↵Adam Soltys2026-09-274-5/+171
| | | | | | | | | | | | | | | read off the clipboard or pasted. Closes #7. A "Sign in with Amber" door on Android opens nostrsigner: intents for get_public_key, sign_event and nip44 encrypt/decrypt, with a waiting card that reads the clipboard when the tab returns (or takes a paste). Requests queue one at a time — a sign-in fires the backup, relay list and follows together — and each answer is checked for its type so a stale clipboard is never mistaken for one. An interactive signer is not asked to decrypt DMs or private lists that arrive on their own; each trip through Amber is a deliberate one. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Reports: a person-level flag needs two follows to fold their posts; 'other' ↵Adam Soltys2026-09-271-6/+13
| | | | | | | | | | | never counts. One follow's mis-tapped "impersonation" on fiatjaf's key was folding his entire timeline. A flag on a specific post from one follow still folds that post. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Feed: the foot says when older posts are loading, and when there are none left.Adam Soltys2026-09-272-3/+13
| | | | | Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Video previews release their media off screen; a paused player kept ↵Adam Soltys2026-09-261-2/+12
| | | | | | | buffering the whole clip. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Renewal repair: a coin already held as spent is not asked about (its bytes ↵Adam Soltys2026-09-261-2/+8
| | | | | | | may be pruned). Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Renewal: a submitted round the server has forgotten is repaired, not asked ↵Adam Soltys2026-09-263-2/+38
| | | | | | | | | | | | | | | | | | | | | | about forever. Adam's wallet carried a refresh action from August 31 in step 'submitted' whose unlock hash the server never ran: the round went through under a later submission (another hash), the inputs were spent by it and the 5,233-sat output reached the wallet through the mailbox — but the original action kept asking RoundParticipationStatus on every sync for four weeks, and since this week's coins page shows renewals in flight, it showed up as "in progress" with the raw gRPC error underneath. On "not found" the manager now checks the inputs: all spent means the round ran under another submission and the record is retired (failed, superseded — no history row, no card); none spent means the server dropped it before a round ran, so it is submitted again; a mix goes through the existing input repair. The coins page never prints a server error string; a failed check reads as "the wallet keeps trying". Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Media sources wait until the node is in the page; thumbnails use the cache.Adam Soltys2026-09-262-6/+66
| | | | | | | | | | | | | | | | | | | | | Every render builds a fresh tree to diff against the live one, and a media element starts downloading the moment its src is set, even off the document: a sixteen-second feed scroll built 204 <video> and 1,416 <img> elements whose loads the morph then threw away — 375 cancelled video range requests, and full re-downloads from hosts that send no cache headers. h() now puts the source on data-lazy-src and it becomes src only once the node is in the page (render promotes it; a MutationObserver covers nodes appended by hand); the morph treats the two as one attribute. The thumbnail maker fetched every original with cache: 'reload' (38 MB in one scroll); the face was just painted from that URL, so the cache serves. holdScroll now recovers an anchor row that a large insert above pushed out of the window, from the height cache. Tests: the YouTube test accepts the in-view players autoplay loads; the stability test watches the notice for the catch-up, since the new rows land inside the window's spacer. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Keyed morph: drop stale nodes before placing survivors, so a repaint moves ↵Adam Soltys2026-09-261-0/+12
| | | | | | | | | | | | | | | | nothing. With stale nodes still in front, every kept row's position was off by the stale count and each one was pulled out and re-inserted — a remove and an add per row per repaint. The feed's window evicting rows at the top did this to every mounted row on every scroll step; a moved <video> leaves the document and comes back, restarting its loading (one clip was fetched 61 times in 40 seconds), and images were refetched. Measured with a MutationObserver on the live tab: 160 of 179 row additions during a 20-second scroll were the same key going out and back in. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Feed scroll: stop re-parsing the whole messages state per row per repaint.Adam Soltys2026-09-262-18/+49
| | | | | | | | | | | | | | | | A CPU profile of a 44 s scroll on Adam's tab: 21 of 27 busy seconds in loadFeatureState — every read parsed the multi-megabyte messages blob from localStorage and re-hashed the seed for its key, and the mute/spam checks read it for every row on every repaint. Now the cache key is hashed once per identity, the messages state is parsed once per tick (a write refreshes the copy; the copy is dropped on account switch), and the profile blob stays parsed in memory and is written back once per burst instead of being parsed and re-serialized for each of thirty profiles landing mid-scroll. Videos preload metadata only: a window of autoplaying players was streaming 20 MB during the same scroll. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Console: NWC chatter behind a debug switch; dead relays remembered longer ↵Adam Soltys2026-09-263-25/+78
| | | | | | | | | | | | | | | | | | and dropped from the pool. `coinosDebug(true)` in the console (or ?debug=1) turns the nwc: listener logs back on; they are off by default. Warnings and errors stay. The red WebSocket lines were the pool's own reconnect loop redialling relays from other people's relay lists that have been dead for weeks — the sick list only stopped NEW dials, for ten minutes, and forgot on reload. A dead relay is now dropped from the pool (no reconnect loop), skipped twice as long each time it fails (up to a day), keyed by normalized URL, and the verdict survives a reload. A relay the pool has failed to reconnect to three times counts as dead. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Profile: the action row's labels never break inside a button; a button wraps ↵Adam Soltys2026-09-262-1/+4
| | | | | | | | | whole. The list button made four across, and ⚡ Pay folded onto two lines. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Thread: Cancel and Reply share one size, the toolbar's button height.Adam Soltys2026-09-262-2/+5
| | | | | Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Thread: the reply box continues the note's highlight, one panel at the ↵Adam Soltys2026-09-262-6/+22
| | | | | | | | | | | | note's width. The box sat inset beneath the focused note's grey highlight as a separate field. It now shares the highlight: same width and background, the note's bottom corners squared off and the box carrying them, the field inside a quiet hairline. Under the root (no highlight) it is a soft panel of its own. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Thread: the reply box appears on Reply, full width, with its tools on a row ↵Adam Soltys2026-09-262-8/+20
| | | | | | | | | | | | | | | | | beneath. A thread opened to read came with an empty field under its first post. The box now appears under the note whose Reply was tapped (or while a draft is pending), folds away once the reply is sent or Cancel is tapped, and takes Amethyst's shape: the field across the full width, attach and preview on a row beneath it with Cancel and Reply at the right. Tests: thread-reply's helper follows the new shape and expects the box to fold after sending; compose-preview and feed-list tap Reply before expecting a box (the thread card carries .thread-card as the marker). Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
* Feed: the new-posts pill glides up to the top instead of cutting there.Adam Soltys2026-09-261-1/+14
| | | | | | | | | An instant scrollTo read as a page refresh. A long way down, most of the distance is closed in one step (so the glide is short and passes rows the window has mounted), then the last two screens roll by; reduced-motion keeps the cut. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Notifications: the faces the page will paint are warmed before it opens.Adam Soltys2026-09-261-1/+16
| | | | | | | | | | | | | | | | The list was already cached locally and refreshed from the chat home, but each row only asked for its author's profile when painted, so the page opened as quiet circles filling in one by one. Now the actors' names are fetched in one batch and each picture decoded into a thumbnail the moment the list is read from disk or grows — which the chat home does before the page is ever opened. Test: notif-faces (stub relay answers the kind-0 batch; the pictures are fetched and cached from the chat home; every row has its face on the first frame). Note for tests: serve /verify-worker.js from dist, or every relay answer waits four seconds for the inline fallback. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Feed: only the rows near the viewport are in the DOM, however far you scroll.Adam Soltys2026-09-262-9/+98
| | | | | | | | | | | | | | | | | | A long scroll left hundreds of rows mounted, and every repaint (a zap count, a profile landing, the thirty-second sync) rebuilt and diffed all of them — the slowdown deep in the feed. Rows beyond two and a half screens either side of the viewport are now replaced by a spacer at their measured height, so the page keeps its length (scrollbar, Back, arrival anchoring behave as before) while a repaint touches a few dozen rows. After svelte-virtual-list: measure after paint, estimate unmeasured rows at the running average, hold the reader's row still when a measurement corrects an estimate. Applies to the profile notes list too. Tests: feed-window-dom (120 posts, 27,800px page, 12–24 rows mounted, anchor held, spacers both sides); feed-live looks at the top after scrolling up; the moderation test's stub relay no longer lets real posts in. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Feed: cached posts paint at once, never a spinner over them; the chat home ↵Adam Soltys2026-09-251-5/+14
| | | | | | | | | | | | | | prefetches the feed. The boot gate hid everything, cached posts included, until the opening page's faces and pictures had decoded — seconds of spinner on a feed we already held. Cached rows now paint immediately as they are and take their prepared presentation once the warm-up lands (the container carries data-booting meanwhile). The relay fetch starts at once instead of after the warm-up; arrivals still wait at the door. Opening the chat home warms and refreshes the current feed, so the Feed tap opens on the latest posts. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Prefetch a smaller feed and bound its local cacheAdam Soltys2026-09-242-24/+123
|
* Sign-in lands on Receive: a freshly opened wallet shows its deposit address ↵Adam Soltys2026-09-241-8/+14
| | | | | | | | | | | and QR. Creating, importing, unlocking or switching to an account (and finishing the onboarding wizard) now opens the Receive tab instead of History. A refresh still resumes wherever you were; loading a seed into a watch-only wallet mid-flow keeps its place too. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
* Coins page: a renewal in flight keeps the whole page; a renewal you asked ↵Adam Soltys2026-09-243-21/+70
| | | | | | | | | | | | | | | | for is in history. Tapping Renew marked the coins pending and the page, finding nothing spendable, collapsed to a single "Pending: N sats" line — no table, no exit explainer. Coins in a round now stay in the table (greyed, tagged renewing), with a card saying where the round is (queued, in a round waiting for a confirmation, finishing). The page no longer closes on Renew. History: a manual renewal shows as "Renewing · in progress" while the round runs, and as "Renewed" after, even when it was free (automatic free renewals stay out of history, as before). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
* Send: the form is cleared the moment a payment goes through, not only when ↵Adam Soltys2026-09-241-0/+5
| | | | | | | | | its card is tapped. Leaving the sent card by a tab or Back kept the recipient and amount in the form, so the next send opened already filled in. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Seed phrase: three columns read across (1 2 3), two columns still run down.Adam Soltys2026-09-241-5/+6
| | | | Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Preserve restored thread state across rapid refreshesAdam Soltys2026-09-242-3/+19
|
* Paint cached thread counts and header hat on refreshAdam Soltys2026-09-244-17/+62
|
* Keep thread avatars and viewport stable on refreshAdam Soltys2026-09-243-7/+80
|
* Videos play by themselves, muted, while on screen — with an Unmute button; ↵Adam Soltys2026-09-233-28/+111
| | | | | | | | | | | | | | | | | | | YouTube too. An inline video starts as it scrolls into view (muted, the only way a browser lets a page start one), pauses as it leaves, and a corner button turns the sound on; the native controls' own mute keeps the button honest. A YouTube box loads its player muted once in view (enablejsapi, so the same button can unmute it over the iframe API) and pauses out of view; a tap on the poster still means sound. One IntersectionObserver watches every player. Data Saver keeps tap-to-play. Tests adapted to the feed's new admission rule (a row waits for its author's face and pictures): the markdown and post-actions tests seed the poster's profile and serve their pictures locally; a picture that cannot be decoded is now a link, by design. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Feed: threads cached locally, posts never inserted under the reader, rows ↵Adam Soltys2026-09-233-183/+335
| | | | | | | | | | | | | | | | | | | | | | | | | | | | keep their first look. Work by Codex, wrapped up and verified here. Threads: a viewed conversation is persisted (thread-cache.js, up to 12 threads / 160 events), so a refresh or a cached note link shows the root and ancestors at once instead of the selected reply and a spinner; relays are re-asked in the background and an empty answer keeps the cache. Feed window (feed-window.js): arrivals are merged without inserting between visible rows — a gap fill under the reader's eyes waits until that part of the feed is offscreen, prepends anchor the reading position, the notice counts what went in above, and only its tap goes to the top. Older pages are prepared a few screens ahead; a page's pictures and faces are decoded before it is shown. Rows keep the presentation they were admitted with (feedPaint): background profile and quote refreshes no longer rewrite text or swap faces mid-read; media dimensions are known before paint. Thumbnails queue instead of being dropped past the first three. Tests: thread-cache, feed-window, feed-stability; feed-live updated for the new arrival rules (the notice counts up while you read). Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Always offer coin management for a Spending balanceAdam Soltys2026-09-231-7/+14
|
* Keep chat headers and composers pinned without page scrollingAdam Soltys2026-09-231-10/+17
|
* Moderation: the whole mute list everywhere, follows' reports fold posts, ↵Adam Soltys2026-09-233-34/+363
| | | | | | | | | | | | | | | | | | | | | | strangers' spam hidden. Mutes were only applied when the feed merged and painted. One predicate (hiddenWhy / hiddenPk) now gates the feed, thread replies, notifications, the who-reacted panel and counts, quote cards, rooms, the DM list and the DM push. The list itself is read in full — people, words, hashtags, whole threads, and the nip44 private half — and edited through one publish path (mute thread and hidden words from the ⋯ menu and a Settings → Nostr card). Reports (NIP-56) from the people you follow are read from their outbox relays every half hour; a post or author they flagged is folded behind who said so, with Show. Report… in the ⋯ menu publishes a kind 1984 and hides the post for you. Amethyst's stranger rules: a post of 60+ characters repeated under another id is a duplicate and five duplicates hide the author for the session; more than ten hashtags hides the post. Both switchable in Settings. Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Social: NIP-51 lists as feeds, posts and people in the address bar, the ⋯ ↵Adam Soltys2026-09-233-24/+344
| | | | | | | | | | | | | | | | | | | | | | menu on every screen. Lists: a feed of hand-picked people is published under your key as a kind-30000 follow set (title, p tags, t tags), so Coracle, Nostria and friends show it; your existing follow sets from other clients turn up as feeds (relay copy wins when newer; private nip44 members read and followed, never rewritten). "Keep as a nostr list" in the editor; unticking sends a NIP-09 deletion and the feed stays. "Add to a list" from a profile or a post's ⋯; someone's lists show on their profile and open as feeds (a kind-30000 naddr works as a pack too). Links: a thread puts its nevent in the address bar, a profile its npub — copy, share, reload; Copy link / Share in the ⋯ sheet. Boot-time renders after a reload share one history slot, so Back goes to the screen before. Fix: the ⋯ sheet and the reaction picker were only drawn by the feed and notification views, so the button did nothing on a thread or a profile. Co-Authored-By: Claude Fable 5.1 <[email protected]>