| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
| |
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Posts a refresh found slid in a beat after the cached page painted,
moving what you had begun reading, and reaching the top of the page let
waiting posts in with a re-render under a finger on its way to the pill —
the tap landed on the post beneath. Now everything that arrives after the
first paint, the boot catch-up included, waits behind the pill wherever
you are; the pill is a keyed node whose count bumps on each change; and a
tap scrolls to the top before painting so the new posts open under your
eyes. A rebuilt follow list still rebuilds the feed in place.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
|
| |
|
|
|
|
|
|
|
|
|
| |
Every row repeated "expiry", "exit cost", "renewal cost" as pills. The
labels now sit once in a header row over right-aligned tabular figures;
the table's wrapper scrolls sideways on a screen too narrow for it (the
page never does), and it fits a 390px phone without scrolling. A tap
anywhere on a row toggles it. A sub-satoshi renewal share reads "< 1 sat".
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Tapping "Emoji pack" on a phone opened Vector: the chip was an outbound
link to vectorapp.io, a domain the Vector app owns. The chip is now a
button — one tap adds the pack right here, nothing leaves coinos.
Packs also get a coinos share link, v3.coinos.io/emojis/pack/<naddr>
(copy it from the picker's pack row). The Android app already claims
every v3.coinos.io path, so on a phone that link opens coinos, which adds
the pack once the wallet is open. Pack links from any host render as the
chip; the naddr's kind is what's checked.
Pack changes now save at once (learned shortcodes stay coalesced) — a
remove followed straight away by a navigation was lost to the debounce.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
|
| |
|
|
|
|
|
|
|
|
| |
The quote above a reply bubble, the composer's reply bar, and the same
on the public /chat page rendered the raw :shortcode:. They now run the
one-line snippet through the same NIP-30 tokenizer, cutting on text so a
shortcode is never sliced in half.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A message from Vector says :pika_wave: and carries a NIP-30
["emoji", code, url] tag for it; coinos showed the bare shortcode. Now the
bubble shows the picture (in rooms, DMs, the feed and the public /chat
page), a bubble that is only one to three of them shows them big, and a
:code: reaction chip is a picture too.
Sending: every :code: you type that resolves travels with its tag, so the
other side renders it whether or not they hold the pack. Shortcodes come
from packs you hold (NIP-51 kind 30030 sets) and from what you've seen in
chat, so anyone can answer a :pika_wave: with one. The composer
autocompletes on ":pi" (Tab/Enter takes the first), the reaction picker
lists custom emoji first, and Vector's pack share link renders with an
Add button; a pasted naddr works too.
Packs live on your kind 10030 list — the one Vector reads and writes — so
a pack added in either app appears in both. tools/emoji-test.js drives the
real coinos room headlessly and checks all of the above.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
|
| |
|
|
|
|
|
|
|
|
|
| |
The Join panel used to be one empty box asking for a link nobody had.
It now lists the Vector Hub's public communities under it — icon, name,
member count, category, description, sorted by size, marked when you're
already in one — fetched on demand and kept for the session. The same
box takes a link or filters the list; a tapped suggestion previews
exactly like a pasted invite, since every listing is a CORD-05 link.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
any emoji can be a reaction.
Vector's bundle parser has no default for a channel's key and epoch, so
a coinos invite — which sent only id and name — failed its whole JSON
parse and the link did nothing there. Every channel entry now carries
key and epoch (a public channel's key is the community_root at the base
epoch, CORD-05 §1), and bundles minted before this are re-signed at the
same coordinate once per device, so links already shared start working.
Concord clients now keep their Community List as kind 33302 fragments
(CORD-02 §8): addressable per index, NIP-44 to self, 32-byte values as
unpadded base64url, embedded join material without its community_id,
seed omitted when it equals current. coinos read and wrote only the
retired single-event 13302 on its own two relays, so a community joined
in Vector under the same key never appeared here. The fragmented list
is now read from our relays, the stock CORD set and every held
community's relays, merged, and written back in the same shape; the
home view re-syncs after a minute and a live subscription brings a
fresh write straight in. 13302 stays for older coinos devices.
The reaction row keeps its quick emojis, recents first, and a + opens
a searchable picker of a few hundred more — eyes included.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The composer grows a paper clip. A picked file is encrypted under a
fresh AES-GCM key and 16-byte nonce, the ciphertext uploaded to a public
Blossom host that takes anonymous blobs (blossom.ditto.pub, then
nostr.download as a fallback copy), and the pointer posted the way
Vector posts it: a kind-9 with empty content and a NIP-92 imeta tag
carrying url, mime, key, nonce, both hashes, size and dimensions. In a
DM it is a NIP-17 kind-15 file message with the same fields as flat
tags and the url as content, wrapped to the peer and to ourselves.
Kind-15 file messages are now accepted on receive too, rendered like
any attachment, and previewed as a photo in the thread list.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Vector attaches files as NIP-92 imeta tags: the blob is encrypted with
a one-off AES-GCM key before it reaches Blossom, and the tag carries the
url, mime, key, nonce, plaintext sha256 and dimensions. Such a message
has no text, so the bubble rendered blank. The blob is now fetched
(fallback hosts too), decrypted in the browser, checked against the
hash, and shown from an object URL — a grey box its shape while it
loads, tap to view full size, other file types as a download by name.
The same tags render in DMs.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
| |
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QYJfZ4A1TDCEY4Foq3CApR
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A chevron on a post's action bar, shown once anyone has reacted, boosted
or zapped it, opens a panel that says who and with what: each zap on its
own line with the amount and any message, then each emoji with the faces
and names behind it, then the boosts. Every face opens the profile. The
zap tally now keeps who sent each receipt, not just the sum.
And a Notifications list on the chat home, under the feed: every like,
boost, zap, reply and mention that names you, one filter on the relays we
read, painted from last time while the relays are asked and live while
open. A row opens the post it is about — or the reply itself — and the
entry counts what arrived since you last looked.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QYJfZ4A1TDCEY4Foq3CApR
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A phone freezes a backgrounded tab: timers stop and the relay sockets are
cut, so the feed came back holding whatever it had when you left and only
caught up if you pulled it down by hand. Features now hear about the
return — app.js calls f.resumed(awayMs) on focus — and the feed answers
it by forcing a pass past the thirty-second throttle and rebuilding its
subscriptions, which were talking to sockets that no longer exist. A
blink between apps (under five seconds) is left alone.
That catch-up raises the second problem rather than solving it: dropping
eight posts in at the top moves the words under the thumb of whoever was
reading. So posts that arrive on their own — live, or as a catch-up —
wait when you're scrolled away from the top. A floating pill says how
many, and the tap that lets them in also takes you up to them. Scrolling
back to the top by yourself lets them in too, since up there they cost
nothing. Anything you actually asked for still goes straight in.
Also, on profiles that still show an npub: the batch now falls back to
the relays whose whole job is indexing kind 0 (purplepag.es and friends),
which we never read notes from and so never asked. profile-batch-test's
"handful of asks" bound was a fraction of the row count, which that
fallback broke; the property it means to assert is that the number does
NOT grow with the rows, so it is a constant now.
tools/feed-live-test.js drives a real signed event down a real open
subscription — an unsigned fake is dropped by the signature gate and
would prove nothing — and checks the page does not move, the pill says
"1 new post", tapping it scrolls to the top, and coming back from hidden
re-asks the relays on its own.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A draft is written as plain text but doesn't arrive as plain text: a URL
ending in .jpg becomes a picture, a youtu.be link becomes a player, a
nostr: reference becomes the note it points at, an npub becomes a name.
The only way to find out how yours would land was to send it.
The eye beside the paperclip — on the composer and on a reply — opens a
preview. It runs the draft through noteBody, the very renderer the feed
uses, so this isn't a lookalike of the real thing: it IS the real thing,
in a dashed frame with a PREVIEW label so it can never be mistaken for
the post itself. It follows the draft live while it's open (and only
while it's open — a render per keystroke is not free).
Attaching a picture opens it by itself. Showing you what you attached is
the answer to the question attaching it raises, and it costs no tap.
tools/compose-preview-test.js covers both the composer and a reply: the
picture renders rather than sitting there as a URL, the YouTube link
becomes a player, the preview tracks typing, and it puts itself away.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Four buttons of equal weight, and only the sign-in ones said what they
did. "Get started" said nothing, so the concrete offer on that screen was
Google — which is a convenience wrapped around a seed phrase, not an
alternative to one.
Now the page says what the wallet is before it offers a button, the way
in reads "Create a new wallet" with the cost under it (a minute, twelve
words), and the three sign-ins sit below a line labelled "or, if you
already have an account".
And the second half of the same problem: a wallet that arrives by sign-in
has a recovery phrase its owner has never seen and nobody can reissue.
So the wizard shows it once, right after the first login, while it still
reads as setting up rather than as a warning out of nowhere. "I've
written it down" is the way on; "Later" is a real answer, and the wallet
then keeps one quiet line — not a modal, not dismissible, gone the moment
the phrase is actually seen, here or in settings.
seedSeen records that per account: creating shows the words, importing
means they typed them, revealing in settings counts. Only a derived seed
starts false, which is exactly the case worth asking about.
tools/onboard-backup-test.js covers both halves, including that "Later"
leaves a reminder that survives a reload and leads somewhere.
The sixteen tests that clicked the old button by its text are updated,
and it has an id now so its copy can change without breaking them again.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Measured on a Pixel 9: Amethyst gives a feed avatar 55dp, we gave it 30 —
the same circle the inbox and chat rows use, where a face is a label on a
line. In a feed the face is the point. Feed rows now get 44px; everywhere
else is untouched.
The catch, and the reason this isn't a one-line change: the cached
thumbnail was 96px square, sized for a 30px circle at 3x. A 44px circle
no longer fits inside it, and the fallback is to layer the ORIGINAL over
the thumbnail — which for a feed of twenty rows means twenty full-size
downloads, one of which in this wallet's own cache was a 5MB JPEG. That
is exactly the slow-avatar problem we already fixed once.
So the thumbnail grows with the circle: 144px, which covers 44 at 3x with
room to spare. Faces cached at the old size are remade once (thumbPx says
which size a thumbnail was made for), and the old one keeps painting
meanwhile, so nobody watches a grey circle while it happens. The per-face
cap goes 9000 → 16000 chars and the shared budget 180k → 280k, both
scaled to the new size: the busiest picture on hand measures 12827 chars
at 144px, and at the old cap it would have been rejected and left
fetching its original forever.
avatar-thumb-test gains the guard that would have caught all of this:
the thumbnail must cover the biggest circle in the app at 3x. It failed
at 128px (132 needed) — which is why THUMB_PX is 144 and not 128. Its
redirect assertion was also order-dependent — the seed writes the profile
into every wallet's cache but only the painted one makes a thumbnail, so
it asked the first row and got a coin toss. It takes the best copy now.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A feed of posts was set in .small — 13px, the size used for captions and
row subtitles. On a phone it read like footnotes. Measured against
Amethyst on the same Pixel 9, side by side: its note body renders a line
pitch of 62 device pixels, ours 50.
Post bodies get their own class at 16px, which is Amethyst's own default
for a note body, and chat and DM bubbles move 15 → 16 to match; the names
and inbox previews above them keep pace. New line pitch measured on the
phone: 60 against Amethyst's 62. A quoted note inside a post stays a step
smaller, since it's context rather than the post you're reading.
Worth recording for whoever reads this next: this phone has the Android
font scale at 1.15. Amethyst uses sp and honours that automatically; CSS
px in a TWA does not, and there's no way for a page to read that setting.
So Amethyst will still render about 15% larger than us on this device no
matter what we pick. Matching its DEFAULT is the target that makes sense
for everyone else.
chat-layout-test had rotted (onboarding copy, .header-msgs now matching
the settings button, a screenshot path from another machine's scratch
dir) — repaired, and it's what proves the composer still fits at the
larger size, on all three viewports.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A nostr:note1/nevent1 in someone's post IS a post, and making the reader tap
a link to see it — losing their place in the feed — was the wrong shape. It
renders inside the post now: whose it is, when, and what it said, in a card
of its own so the two voices don't run together. Tapping it opens the note
properly.
Deliberately not a noteRow: a quote is context, not a second post to act on,
so it carries no reply, boost or zap of its own. The action row stops at the
post that did the quoting.
Fetched once per id and remembered, so a feed quoting the same note ten times
asks for it once. One level deep only — a quote of a quote of a quote is a
rabbit hole, and the inner one stays a link you can follow.
tools/quote-card-test.js quotes a real note off the relays and checks it
arrives with its author, its text, and none of the outer post's buttons.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
All three shapes it comes in — youtube.com/watch?v=, youtu.be/, and a Shorts
link — become the video's still with a play button on it and a corner link
out to YouTube. A ?t= keeps its start time. Anything that merely looks like
one (example.com/watch?v=…) stays a link.
The player itself arrives on the first tap, not on render. A feed of ten
videos would otherwise load ten YouTube players, each one telling Google what
you're scrolling past before you've decided to watch anything — and on a
phone, ten players is the scroll. One tap and it plays in place, from
youtube-nocookie.com.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Reply and zap have come down from the header to join boost, quote and the
heart, spread across the post's full width so each is a comfortable target
instead of five buttons huddled in a corner. Only the ellipsis stays up top.
Proper icons at a readable size, all the same weight: a speech bubble for
reply (a return arrow is what a keyboard does, not what a person does), the
repeat arrows for boost, quote marks, a heart, a bolt. Counts sit beside the
icon they count.
And the heart asks which feeling: tapping it opens the same six emoji the
chat sheet offers, and the one you pick is the one the post then shows —
NIP-25 takes any content, so there was never a reason to assume a heart.
Tapping your own reaction still takes it back.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Under every post now: a heart with its count, a boost with its count, and a
quote. Behind an ellipsis beside reply and zap: quote, boost, copy the note's
reference, mute the author, or block them. Likes and boosts come back in the
SAME relay request the zap tallies already make — one round trip answers all
three — and each action paints before any relay does, the way the zap chip
learned to.
A like is kind 7 and a boost kind 6, published to the author's own write
relays as well as ours, so the person being answered actually sees it. A
quote opens the composer with a nostr:nevent reference already in the text
and tags the post on publish, so other clients render the embed. Taking a
like back is the only withdrawal nostr has — a deletion request naming the
reaction.
Mute is the NIP-51 list (kind 10000): their posts leave the feed at once and
the list is published, so it follows you to other clients. Block is the same
list plus an unfollow, which is why it says so.
And a bug the mute found, which the follow list had too: the optimistic paint
was being read back as fact. Both toggles paint the answer immediately and
then publish onto the newest list the relays will give them — but when the
relays had nothing newer, "the newest list" WAS the optimistic paint, so the
code saw the follow already present and published a list without it. Every
follow was a no-op that looked like it worked. The base is now the relays'
copy only when it's genuinely newer, and otherwise what we held before the
paint.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The attach control was a .btn-sm holding a 16px icon, wedged between two
full-height buttons — a sliver to aim at on a phone. It's a 48px square the
height of the row now, with a 20px clip in it.
And tapping an image opens it full screen: the image centred on a dark
ground, tap anywhere (or Escape) to close. Posts crop images to 320px so a
feed stays a feed, which makes a photograph a thumbnail — this is where you
actually look at one.
Two details in the viewer. The page underneath stops scrolling while it's
open, and the scrollbar gutter the page keeps reserved stands down with it:
fixed positioning is sized to the viewport WITHOUT that gutter, so an
"inset: 0" backdrop leaves a pale stripe down the side of an otherwise
full-screen picture.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
It was rendering everything it held — a hundred-odd notes to show the few you
read. It now paints twenty, and reaching the bottom shows twenty more; only
when the window catches up with what's cached does it go back to the relays
for older posts. Posts arriving live at the top widen the window rather than
pushing off the one you'd scrolled to.
And the posts are the page now, not a card sitting inside one: they step out
through the chat page's padding to the container's own edge, leaving one
continuous list with hairlines between the posts instead of two sets of card
walls.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The Spending balance and its history are read from the ark store, which is
IndexedDB: synchronous to read, asynchronous to OPEN. Boot didn't wait for
it, so every refresh painted a Spending wallet with nothing in it — no
balance, no history — and filled it in a beat later. On a phone that second
paint arrived as a change rather than an arrival: the balance counted itself
up from 0 in green, wearing the animation a received payment gets.
Boot now waits for the store (capped at 800ms — a store that won't open must
not hold the app hostage), and the shell stays up a moment longer instead of
a wrong balance going up. If the store does land past the cap, initArk
retries on it — so a Spending wallet whose state arrived late also connects,
which it previously didn't until something else happened to kick it — and
that catch-up paint forgets the last amounts first, so it reads as state
arriving rather than money.
Also: the zap tally chip loses its underline. In chat the chip is a reaction
chip too, and .chat-react.on still rings it; on a post row the underline just
read as an artifact.
tools/boot-balance-test.js pins the ordering, with IndexedDB slowed so it
can't pass by luck.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A zap took a few seconds to pay and its public receipt trailed that by
seconds more, so the tally chip appeared long after the tap — the tap looked
ignored, and the only way to tell it had worked was the toast.
Now the chip appears on the tap carrying your amount, breathing while the
payment is in flight; it goes solid the moment the payment lands, and steps
aside when the real receipt arrives (which is also how it avoids counting
your zap twice). The ark and Lightning one-tap flows report their outcome
back — a failure, or a fallback into the form, takes the chip straight off
again rather than leaving it pulsing. An unreported zap gives up after 45s.
A zap sent from the form reports too, so the tally shows it right away
instead of waiting on a relay. Reduced-motion gets a dimmed chip, no pulse.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Chat and DM message sheets grow a ⚡ Zap item (not on your own messages),
riding the same one-tap flow as a post zap — Ark first, Lightning fallback,
the amount remembered from the first time. A chat message is a rumor with an
id like any event, so the zap e-tags that id; the public receipt reveals
nothing but the id.
Every post row, chat bubble and DM shows how much it's been zapped as a
little amber bolt + sats chip (1.2k / 21k / 1.5M), fed by the public receipts
that e-tag it: NIP-57 kind 9735 (amount from the bolt11, the zap request's
amount as fallback) and coinos' own Ark receipt, kind 9737 (network-checked).
Summed per id, deduped by receipt; underlined when one of ours is in it.
Ids are asked once each (batched per paint, 150 per REQ), and ONE live
subscription follows the ~200 ids most recently on screen so a zap landing
while you watch — yours included, rechecked at 4/12/30s — shows up without
a reload. The tally chip is itself a zap button on others' messages.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01URmfo8VimCNkw6Y7JndYrv
|
| |
|
|
|
|
|
|
|
| |
into Paid.
The armed auto-pay flow flashed a "Paying" title/spinner card that then swapped title, badge and footer for the "Paid" card — too much text change and layout shift for something that completes in a moment. Now the in-flight and success states are ONE structurally identical card (autopayCard): recipient name and amount sit in their final positions the whole time, and the step to Paid is just a class flip. The badge starts as a spinner ring (the ⚡ hidden) and, on success, settles into the green success check with a pop while the ⚡ fades in; the "tap to proceed" footer occupies reserved height so nothing jumps. No "Paying" wording at all — the badge carries the state. Respects prefers-reduced-motion. Manual (non-auto) pays keep their normal Paid screen.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
Reactions were a sibling of the bubble (a separate row under it), so they hung outside the message. Moved the reaction pills inside .chat-bubble, tucked under the text, across all three renderers: community channel messages, DMs, and the read-only public /chat page. The pill background and the "your reaction" highlight border now derive from the bubble's own text colour (color-mix on currentColor), so they read correctly on both the plain bubble and the accent "me" bubble. Tapping a pill stops propagation so it toggles the reaction without also opening the message action sheet.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
wears ⚡ + the ark mark in violet beside its label with a violet hairline across the card's crown; Savings wears the bitcoin disc in its orange with an orange crown. The tint is an inset shadow stacked with the card's own — clips to the radius, zero layout shift, so the carousel's equal-height cards stay equal. Kind-locked and single-balance wallets keep their plain label. (Same feedback proposed vertical-only navigation and one global send/receive button — parked, not obviously better than the carousel + per-account model.)
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
names 15px, last-message previews and member counts 14px — the generic .small (12px) made the inbox feel like fine print next to the freshly enlarged bubbles
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
community rooms and DM threads (now sharing a chat-page class) drop their card walls and run edge to edge under 520px — and the type steps up a notch: bubbles 14→15px, names 13→14px, timestamps 11→12px. Plus: a fixed-price LNURL (minSendable == maxSendable — bolt-card top-ups, paywalls, static charges) no longer prompts for an amount — resolve() fills it and locks the field via the same fixedSat machinery fixed-amount BOLT 12 offers already use (disabled input, adjusted hints, confirm bypass)
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
with an underline, instead of the page link color — that color is calibrated against the page background, so in dark mode a near-white link sat on the accent-white own-bubble, unreadable. Verified in dark mode: link color equals the bubble text color (rgb 15,18,22), underlined
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
pulls the next page of older kind-1s — until = oldest seen, one fetch in flight, a short answer marks the end; the listener registers at feature construction so the public no-wallet /username pages scroll too), VIDEO rendering in noteBody (mp4/webm/mov/m4v as <video controls preload=metadata playsinline> beside the existing inline images), and FULL-BLEED on phones — the notes card drops its walls and runs edge to edge under 520px; the inner 440px scrollbox is gone everywhere, the page itself scrolls. Verified headless at phone width on /adam: feed spans 0px-to-edge both sides, images render inline, and scrolling grew the list 51 → 82 posts
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
bubble opens a centered sheet: a quick row of the six reaction emojis (your current one highlighted; picking fires immediately) above Reply / Copy / Delete (delete on own messages only) — replacing the hover-only 🙂 picker, which was invisible on the phones that need it most. Links, images and the hover × keep their own clicks, and a desktop text-selection drag never pops the sheet over the copy. REPLIES are new: Reply arms a composer bar showing who/what you're answering (× to cancel, cleared on channel/room switch), the sent kind-9 e-tags the quoted message, and rows carrying an e-tag render the quoted context above their text — author + one-line snippet, silent when the original is deleted or not held. The public /chat page renders the same quotes. Verified against the built stylesheet: 300px sheet, six even emoji buttons, quote ellipsizes, circular cancel button
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
overlaps the react opener: the global button padding (11px 16px) was flooring the ×'s border-box past its fixed 18px width — an oval — and the react button's step-aside was calibrated against that phantom width. padding:0 on .chat-del restores the circle; the opener steps to right:16px, leaving a measured 5px of air (verified headless with getBoundingClientRect: both 18×18, gap 5.0)
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
DM bubbles now run through noteBody (URLs → anchors, image URLs inline, npub mentions clickable, all content kept as text nodes — nothing from a relay reaches innerHTML); the public /chat page gets its own linkedBody (same splitter, nostr refs stay dim stubs since there are no profile screens there). Reactions: a 🙂 opener on every bubble (rides the edge like the delete ×, steps left on own rows) unfolds a six-emoji picker; tapping an existing chip joins that vote, your own reaction is highlighted, and picking a different emoji replaces it (kind-7 rumor on the channel stream, one per author, fold keeps the latest — inbound rendering already existed, this adds the sending half). Optimistic like sendMessage: the chip lands instantly and is rolled back to whatever stood before if signing/wrapping fails
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
vertical scrollbar: a short page has none, the history list filling in makes the page tall enough to get one, the viewport narrows ~15px, and the snapped card re-centers into the narrower width — a horizontal nudge right as history lands. scrollbar-gutter:stable reserves the gutter so growing content never changes the width. And the 'false start' — a boot render's alignment firing a scrollTo mid-drag — is guarded: alignment now bails while a finger/mouse is down
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
no longer reads layout (offsetLeft/scrollLeft) on every one of the many boot renders — an _alignedTo guard short-circuits before any layout touch once the card is in place, and the scroll's own settle marks itself aligned so it never re-aligns behind the user. The pass moved to rAF, and each slide gets layout containment so the history list building in below can't force it to reflow mid-scroll
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
button or finger is still down (release is the choice, shared settle logic for mouse, touch and glide) — and the coins table's dotted meta line becomes little labeled pills: an hourglass countdown, depth, and exit cost each in their own chip, the expired one wearing the warning color
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
one puts it away), every landing — login, account creation, an account snapped into the carousel — is the account's own timeline, and that timeline is now SCOPED: Savings shows the on-chain ledger and gift reservations, Spending shows the ark side. The carousel meanwhile learns mouse and mind-reading: desktop gets a real grab-and-pull drag (snap lifted mid-drag, restored on the glide), the peeking neighbor switches on a single click, and the strip wears grab cursors. And a thread's avatars and author names finally go somewhere: profiles stack on top of the conversation and back returns to it
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
riding a full-bleed strip — the neighbor card peeks in from the screen edge and snaps into place, instead of two faces crowding one card's chrome. The active card carries the move/top-up buttons and unfold panel; the peeking one stays a clean preview
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
faces with the neighbor peeking at the card's edge, dragged and snapped by native scroll-snap — settling on a face IS the account switch, programmatic switches glide the carousel to match, and the old swipe-to-flip gesture yields to the carousel's own drag (it still serves single-face wallets)
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
padding (11px/16px, asymmetric in a fixed 42px round) was shoving the ✕ off-center — flex-centered with the padding dropped
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
click event lands 100-250ms after the touch, and a tab has no scroll ambiguity to wait out — keyboard activation keeps the click path), every button takes touch-action:manipulation so double-tap-zoom arbitration stops taxing quick taps, and the sliding pane pre-promotes to its own compositor layer — a slow mid-slide re-render was letting Chrome expose unpainted gray tiles at the viewport edges
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
(pulling the avatar up inside its hat-wrap left the crown planted on the face), and the whole catalog rises 20 points — hats covered a quarter of every avatar and now kiss the top few pixels
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
the legacy avatar and banner and fills the kind 0's empty slots (fill, never overwrite — an existing nostr identity keeps its own look), the wizard's punk picker steps aside for a profile that already has a picture, and v3 finally wears cover photos — nostr's standard kind-0 banner renders full-bleed across the top of every profile card with the avatar overlapping it, settable by URL or upload in the profile editor
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
text (capped at five lines), Enter still sends, and a newline is one Shift+Enter or Ctrl+J away — bubbles already knew how to show them
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019WboeoetMF3MTmgT7Ew8yi
|
| |
|
|
|
|
|
| |
goes purple
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
|
| |
|
|
|
|
|
| |
one, present from the static boot shell's very first paint so it never pops in
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
|
| |
|
|
|
|
|
| |
resolves on the first acceptance, DMs stop idling on the peer's inbox lookup, and the dimming departs — a sent message simply appears
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_012u4oe1KPx9faCsDBLfZYqi
|