| Commit message (Collapse) | Author | Age | Files | Lines |
| ... | |
| |
|
|
|
|
|
|
|
| |
A group that misses a commit is stuck until it hears it, and a group
founded by White Noise can live on a single relay. Its subscriptions now
report a close to the chat watchdog, and one made while a relay was being
skipped as dead is rebuilt when that relay comes back.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
|
| |
MDK refuses relay.damus.io and relay.nostr.band outright (its retired
hosts), so a group founded here must not name them: relay.primal.net
takes damus's place. The UI test can also run against a deployed site.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The Chat tab now speaks the protocol the White Noise app speaks, beside
Concord communities and NIP-17 DMs, without anyone having to pick one:
- A two-person group with no name is a direct message. It lands in the
same thread as that person's NIP-17 messages, and a reply goes back the
way their last message came. A first message goes by White Noise only
to someone whose KeyPackage another app published (a coinos wallet
reads NIP-17 on all its devices; an MLS group reaches one).
- Groups get rows of their own in the Messages list, a New group button,
an invitation card, members, add/remove/rename for admins, leave.
- Every wallet publishes a KeyPackage (kind 30443), so it can be invited.
src/mls.js is RFC 9420 for ciphersuite 0x0001 on the noble libs (no
WebAssembly), with the extensions-draft pieces Marmot needs
(app_data_dictionary, app_data_update, app_ephemeral, self_remove).
src/marmot.js is the Marmot layer: components, the account identity
proof, the 30443/444/445 event shapes and the group engine
(publish-before-apply, retained epochs, same-epoch tie-break).
State is per device, in IndexedDB (src/marmot-store.js). The whole stack
is one lazy chunk (72 KB).
Tested against the RFC 9420 vectors (tools/mls-vectors-test.js) and
against the Rust reference, MDK's wn CLI, at the protocol level
(tools/marmot-interop-test.js) and through the real UI
(tools/marmot-chat-test.js).
Not yet: attachments in these chats, push for group messages, the
app-message witness boost in fork selection.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
|
| |
It opened the history tab. Any open payment detail still closes with the
switch.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
|
| |
showSend flipped the wallet's tab to Send but left the screen where it
was; from the Spending settings page (where the co-operative exit lives
now) the form opened out of sight and the button seemed to do nothing.
Opening Send now always means the wallet screen.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
| |
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
| |
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
|
|
| |
It was one device-wide key, so a new account opened on whatever the last
one was reading (#gardenstr: the starter feed has the same id in every
account). Now keyed by the wallet, re-read when a wallet opens (the
feature outlives a sign-out), Popular when nothing is remembered, and the
old device-wide key is dropped. A feed opened from a link stays put.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
- Pasted with the keyboard (not only the Paste button) it is complete
text: it advances at once, as a paste always did through the button.
- Typed and then left alone for 0.9s, it is checked quietly (names: our
registrar or BIP-353 DNS; zaps: the LNURL-pay endpoint answers) and the
form moves on only if something answers. A half-typed domain answers
nothing, so it just waits, with no error; finishing it moves on.
- tools/send-lnaddr-advance-test.js (live registrar/DNS, [email protected]).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
sending
- The recipient is a one-line textarea that wraps and grows (rows from
the text's length over the field's measured width), so a whole address
is in view in the field itself — no second copy in small type below.
Enter doesn't break it and pasted line breaks are dropped. Empty, it is
as tall as its hint (measured in the hint's own face), so that's whole
too.
- Past the recipient — the amount, a review, a Lightning/Ark
confirmation, a raw tx to broadcast — the history list below the send
pane is left out.
- tools/send-dest-test.js (390px and 1280px).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
|
| |
The grid's 1fr columns wouldn't shrink below their words, so an
eight-letter word pushed the third column out past the card's padding.
minmax(0, 1fr) columns, slightly tighter cells, and two columns up to
480px wide (was 420).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
new-post tint
- The new/edit feed form gets a Relays field (read this feed from these
relays only) and "Every post on these relays", which saves a firehose
feed (optionally curated) named after its relay and hides the people,
packs and topics it doesn't need. It used to take a hand-typed
/feed/all?r= link.
- The form's Back did nothing visible: opening it made no history entry,
so Back went to the page behind the feed and left the form up. The
editor (and the gear page's Spending/Savings view) are now part of the
navigation state.
- New-post tint 9% -> 16% green in light mode; dark mode unchanged.
- tools/feed-editor-relays-test.js.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
|
|
| |
It waited for half the row to sit in view for two seconds; scrolling
reset the clock and a replaced node lost it, so a tall post read in
passing or one the window remounted could stay green for good. Now the
first visible pixel starts a 3s fade (the row's background transition),
marked seen by id, whether the reader stays or scrolls on.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Measured in the live tab (screencast frames): back from a profile, the
feed's text was on screen ~200ms before its photo. The <img> was loaded
and complete on the first frame — a 3024px, 12-megapixel original that
Chrome re-decodes whenever the page is rebuilt.
- Post photos wider than the column at 2x are painted from an 1100px
WebP copy made once (fetch, createImageBitmap, OffscreenCanvas) and
kept in Cache Storage; it decodes in a few ms, decoding=sync, inside
the same frame as the text. GIFs and small pictures keep the original;
the lightbox opens the original. Profile banners use the same copies,
warmed on prefetch.
- corsFetch: primal's CDN caches its no-Origin answer without
Vary: Origin, so a CORS fetch after the <img> got that header-less copy
and failed — which had written blossom.primal.net, m.primal.net and
coinos.io off as 'no CORS', so their avatars never got local
thumbnails and painted late from the originals. A refusal is now
retried once past the cache before a host is blamed; the old list is
dropped (nocors-v2).
- DM list: the unread dot sits centred at the row's end, like the
communities' below, instead of beside the time.
- Balance card gear: the icon alone, no circle.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
| |
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
|
| |
It was one device-wide localStorage flag, so whoever signed in next on a
till landed in the last owner's point of sale. Now it is keyed by the
wallet (still per device); the old flag is dropped since it can't say
whose it was.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
| |
The truncated key button took a whole row on every profile. A "Public
key" link now ends the Following/Followers line; it reveals the npub in
full, monospace, tap to copy, and hides it again.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
| |
A Settings button beside the Notifications title opens Settings >
Notifications (which kinds buzz this device); browser Back returns.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- The "Accounts" pill on each balance face is now a gear. It opens that
face's settings: Spending leads with its coins (renewal, the
co-operative move to Savings, unilateral exit), then Security (lock,
change password), the recovery phrase, auto-logout; Savings adds its
public key. A wallet with no nostr identity also gets sign-in and
log-out there, since it has no profile page to hold them.
- The exit cost is no longer said under the balance; the Spending
settings state it, always, not only when coin chains run deep.
- The lone Manage link under the balance is gone (the gear is the way
in); warning notices that are about the coins link to the same page.
- The Accounts list, Add wallet and Delete all are no longer reached
from the wallet (Delete all stays behind profile > Log out > forget).
- Tests follow the gear: padlock-ask, lock-wallet and account-access
pass again; cooperative-exit-ui clicks the Spending gear.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A kind 3 is replaceable: publishing one built on a list we didn't find
wipes the real one wherever it lands. 2026-10-02, an Amethyst user with
398 follows (kept on her own relays and an archive) signed in to coinos
for the first time; the starter-follows seed asked only our relays, saw
nothing, and published Adam + coinos over her list.
- deepContactList: our relays, the profile indexes, the archives, the
person's own NIP-65 write relays, and Primal's signed copy (verified).
- Starter follows only for a key with no follow list AND no profile
anywhere ('new' meant new to coinos, not new to nostr).
- A follow, follow-many or mute made while we have never seen the list
does the deep lookup first and builds on what it finds.
- Settings > Nostr > Follow list: find older copies on history-keeping
relays and restore one as a union with the current list (nobody is
removed), for anyone already hit.
- tools/follows-overwrite-test.js: fake relay network, only an archive
holds the list; fails on the old code with the exact two-person event.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Under the bio: "116 Following · 572 Followers". Tapping either opens the
people below it (50 at a time); tapping a person opens their profile.
Follows are the person's kind 3, now read from Primal's cache alongside
the relays (a list kept only on their outbox relays used to read as
nobody). Followers, which no relay counts, come from Primal: the count
from user_profile, the list from user_followers, a first screenful fast
and the rest after, without reordering what is on screen. Every event
taken from Primal is signature-checked in the verify worker first, and
the profiles among them fill names and faces the relays lacked.
Also translates the post-zap "no address" message.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
| |
growComposer sized the border-box field to scrollHeight, two pixels short
of its own content (the border), so even an empty box overflowed and
showed a scrollbar. Count the border, and scroll only at the height cap.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
| |
The placeholder is ::before text in the flow, and the caret blinked after it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
BTCPay's NWC connector requires get_info, make_invoice, lookup_invoice
and list_transactions; we never offered lookup_invoice, so it refused the
connection string.
- ArkManager.lnLookup answers lookup_invoice from wallet state (by hash or
bolt11, either direction; pending/settled/expired/failed, preimage once
claimed). A retry's several actions on one hash defer to the furthest.
- The open app pushes a pending receive along before answering, so a
merchant polling sees it settle.
- The service worker now answers make_invoice and lookup_invoice too, and
claims a paid invoice's HTLCs during the lookup: a BTCPay sale settles
with the wallet closed. Its list_transactions reports receives as well.
- get_info and the 13194 info event list the new method.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
| |
A post's author with no Lightning or Ark address now gets a toast saying
so (or a notice on the hold-to-zap card) instead of a locked gift sent
by DM. Paying a person from Pay / the send form still falls back to one.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
|
| |
The pill is thumb-sized now (16px, 12/24 padding) and rides above the
Post button on a phone. Drag it sideways or down and let go: it flies
off and stays away until more posts arrive than it was showing. A short
drag springs back and is not a tap.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
|
| |
searchable() only let [a-z0-9._-] through, so "jordan miller" never
searched. Words and non-Latin letters pass now, and a multi-word query
also asks Primal with the spaces removed, since it matches "jordanmiller"
but not "jordan miller".
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
| |
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
| |
A tall clip was 360px high at most (profile, thread) or sized to its own
width (feed), leaving most of the post blank. Now every inline video is
the column's width, up to three quarters of the screen tall, letterboxed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
|
| |
|
|
|
|
|
|
|
|
|
|
| |
The server renders /nevent1 and /note1 pages with the thread inlined
(post, root, replies, kind 0s) and Open Graph tags; the client seeds its
thread cache from it and opens the thread on the first paint. Without
it, a pending frame holds the screen instead of the front door. Profile
lookups ask our relays and the profile indexes at once, streamed,
instead of one after the other behind five-second waits. A thread
opened straight from a link has no Back button.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
| |
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
Arrivals used to wait behind the pill whenever the feed header was on
screen. They now go in above the first post with that post held where
it is, so the page grows upward and can be scrolled to before tapping
the pill. While the composer or another header field is in use they
still wait. The pill clears only when the reader scrolls up to the
newest post, not on the hold's own downward correction.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
| |
The planner picked relays to cover each followed author once, so an
author was read on a single relay even when several of their write
relays were open in the same plan. Fishcake's posts on damus and
primal never showed because he was assigned to nos.lol alone.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| | |
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
| |
The form asks the ASP for a co-operative offboard quote (mining share +
service fee, minimum = fee + dust) for the address on screen, disables
Send for a balance below it and points to Lightning. A too-small amount
reads in words instead of the manager's internal error, and the
unilateral-exit note only appears when co-operation actually failed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VP5Ljqk7EYcm5QQAbzDwog
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
|
|
| |
With no files beside it, relative punks/, locales/, nip46.js and jsqr.js
looked on the disk. From file:// they now come from v3.coinos.io (images
and scripts need no CORS; /locales/ now sends Access-Control-Allow-Origin
on the server), and standalone.html carries Public Sans inline.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VP5Ljqk7EYcm5QQAbzDwog
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
| |
The notifier now watches kind-7 reactions p-tagged at the user (opt-in
by an explicit flag, so older apps that can't word them stay quiet) and
sends zap receipts as 'zap' to apps that report the category, 'payment'
to older ones. Coinos zap receipts carry their amount into the push. The
service worker names the reactor when it knows them and stays quiet for
a muted one.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VP5Ljqk7EYcm5QQAbzDwog
|
| |
|
|
|
|
|
|
|
|
| |
Replies (kind 1 and NIP-22 comments) are asked in their own batched #e
request beside the likes/zaps one and counted under every post of the
conversation they name; quotes ('mention') aren't replies, muted authors
aren't counted, and a sent reply counts at once.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VP5Ljqk7EYcm5QQAbzDwog
|
| |
|
|
|
|
|
|
|
|
| |
not its top.
It now walks history like the system Back (whose popstate restores the
scroll place); the profile page's Back does the same.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VP5Ljqk7EYcm5QQAbzDwog
|
| |
|
|
| |
Build the default Popular feed once on the server and let browsers verify and paint its signed events before slow media finishes. Keep custom relays, pagination, filtering, and relay fallback on their existing paths.\n\nAdd stale-while-refresh persistence, service-worker bypass, development integration, and unit/browser coverage.
|
| |
|
|
|
|
|
|
|
|
|
| |
The phone had written off relay.coinos.io, nos.lol and damus after a
network blip, so only primal answered. Home relays are now skipped for a
minute at most and forgiven on resume/online; failures while hidden or
offline don't count. Profiles also ask history-keeping relays, merge
answers instead of replacing the page, and re-ask on reopen.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VP5Ljqk7EYcm5QQAbzDwog
|
| |
|
|
|
| |
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VP5Ljqk7EYcm5QQAbzDwog
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
bands beside a portrait clip.
The shape comes from the post's imeta dim when it gives one (Amethyst,
Primal and others publish it), else from the clip's metadata fetched while
the row warms (3 s cap), and is remembered on the device. The box is as
wide as the column, never taller than 75vh; an unknown shape keeps 16:9.
tools/feed-video-aspect-test.js: 3:4 and 16:9 clips measured ahead, a 9:16
one shaped by imeta alone (its file served too late to probe), a broken one.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WMMwWYJkPQxLgfMsY7hWcU
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
answer.
The disk cache held four feeds, so with more chips than that (or one never
opened here) a switch opened on nothing. Now:
- up to twelve feeds on disk: the four read last keep a page of 30, the
rest 5 seed posts (same 256 KB budget);
- once the feed on screen settles, every other feed with nothing on disk
has its first posts fetched ahead, one small request each, every 15 min;
- a feed with nothing on disk borrows matching posts (its people, its
topics) that open feeds already hold.
tools/feed-switch-seed-test.js: with 8 s relays, a fetched-ahead feed and a
never-opened topic feed both paint rows at once (fails on the old code).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WMMwWYJkPQxLgfMsY7hWcU
|
| |
|
|
|
| |
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WMMwWYJkPQxLgfMsY7hWcU
|