| Commit message (Collapse) | Author | Age | Files | Lines |
| ... | |
| |
|
|
|
|
|
| |
stops opening the staging PWA. The same seed derives the same nostr pubkey on mainnet and staging, so both PWAs registered push under one pubkey and the notifier — matching only by pubkey — fanned every mainnet payment out to the staging subscription too; tapping it opened staging.coinos.io. Now the client stamps its network on the push registration (pushWatch net: getNetwork()), the registrar declares its own network on the /notify call (CFG.forwarder.network), and nwcpush delivers a payment only to registrations whose net matches — with a rollout-safe fallback: a device that hasn't re-registered yet (net=null) still gets the push, and a caller that omits net still reaches everyone, so nobody goes dark mid-deploy. Deployed names + nwcpush on cs
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
cards hide behind a merchant designation — a one-line "I'm a merchant — show point-of-sale tools" toggle at the foot of the payment-address card, swapping to "Hide point-of-sale tools" once on. The flag lives in the names feature state, so it follows the account to every device via wallet sync; regular users just see one quiet line instead of two cards of terminal plumbing
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
showing it hands the LNURL straight to whatever wallet owns the scheme, no scanning your own screen
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
bech32 with the prefix) — tappable and pasteable anywhere that registers the scheme, including our own Android app — while the QR keeps the bare uppercase form for alphanumeric-mode density
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Nostr
Custom name, point of sale, the terminal's tap-to-pay link, own node and
own domain each get a card on Settings → Payments instead of hiding in
collapsed sections. The one-tap zap amount and the CLINK zap code move to
Settings → Nostr, where the other nostr settings are.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMi2Y1oKPZ3TeeFmLej47y
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Every background repaint stripped the browser-set `open` attribute (the
fresh render never carries it), collapsing expanded sections, and the
positional child match handed one section's open state to its neighbour
whenever the conditional zap-code section came or went. `open` on a
details is now user-owned unless a render sets it, and nodes carrying
data-key only patch into the same key.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMi2Y1oKPZ3TeeFmLej47y
|
| |
|
|
|
| |
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMi2Y1oKPZ3TeeFmLej47y
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Registrar: the owner mints a bearer POS token from Settings (NIP-98
POST /pos/token, DELETE revokes; /admin/pos/token for operators). With it
the terminal gets /pos/me, rings up sales at /pos/invoice (fiat via
coinos.io rates, or sats), reads /pos/payments, and holds a websocket at
/pos/ws that gets a payment message when the sale settles. Sales are
always minted on our node so the settle loop sees them and the ordinary
forward path delivers the sats to the name's ark address. The name's
lnurlp pins the newest unpaid sale under five minutes old — coinos.io's
/p/<name> rule — so an NFC tap or a scan pays exactly that invoice.
Wallet: a "Point of sale" section on the names card creates, copies and
revokes tokens.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMi2Y1oKPZ3TeeFmLej47y
|
| |
|
|
|
|
|
| |
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
|
| |
|
|
|
|
|
| |
text/identifier (the lightning address) replaces the bech32 blob as the payee's name and address on the amount screen, with the text/plain description as fallback. And when the address is a coinos name — whether it arrived as an lnurl's metadata, a typed [email protected], or an intent — the registrar is asked who owns it and the screen wears their AVATAR via the same profileChip the npub zap flow already uses; the pubkey also lets a NIP-57 zap request attribute to the right person. Lookup is fire-and-forget so the amount screen never waits on it
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
|
| |
|
|
|
|
|
| |
arrives BEFORE the paying feature is ready — ark connects lazily, so zaps' matchSendText declined and the raw URI fell through into the recipient field as dead text; the intent dispatcher now keeps re-offering the text to the feature chain for up to 20s and sweeps the leftover raw text out of the recipient when a feature claims it (the zap amount screen takes over). (2) When the fallback button did render, it read "⚡ Zap lightning:lnurl1dp68gurn8ghj7…<overflow>" — zapDisplay now names targets like a human: the address when there is one, a shortened bech32 otherwise, on both the send-form button and the amount screen
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
gone (no sp in the feature registry) but its mentions lingered: the zapstore description now says plain "full BIP84 bitcoin wallet" (fresh kind 32267 republished, verified clean on the relay), the README loses the sp feature section and its mentions in the E2E-suite and dev-proxy lists, the dead receiveSpTab i18n key is removed, and two stale comments (app.js seams, build.js HAL_FEATURES list) stop naming it. Deliberately KEPT: silentPaymentNote — the send review still labels an output paying someone else's sp1 address, which remains supported
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
whole reason to exist is intent registration — lightning:/bitcoin:/nostr:/lnurl:/lnurlw:/lnurlp: VIEW intents and the same schemes via NFC NDEF dispatch (bolt cards, address stickers; NFC declared required=false), plus verified https links to the site. LauncherActivity forwards a scheme URI into the web app as ?u=<encoded>; the web app reads it BEFORE features construct — a nostr npub/nprofile rewrites itself onto the existing /npub profile deep link, payment URIs stash and dispatch through handleScanned (the QR path) once a wallet is open, and the param is stripped from the URL either way. assetlinks.json (static/.well-known/, copied by build.js) vouches for the signing cert so the TWA runs URL-bar-less; the built APK is served at /coinos.apk for sideloading. Signing keystore lives in android/ GITIGNORED (keystore.properties beside it) — the assetlinks fingerprint pins it, so losing it means re-shipping assetlinks too. Verified: headless ?u= routing (lightning → Send with invoice filled + URL cleaned; nostr → profile page), APK installed on a real phone via adb, schemes registered (chooser appears where other wallets also claim lightning:)
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
client tag — ['client', 'coinos'], name-only since no kind-31990 handler coordinate is published yet — so other clients show "via coinos". Deliberately absent from everything encrypted (DMs, community wraps): private traffic needs no billboard
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
sats" (unit included) instead of "exit 3,688", the renewal price moved off the row's right edge into a chip like its siblings — "renewal cost 117 sats" (or "renewal cost free") — and the expiry chip carries a word beside its hourglass ("⏳ expiry 21d"). The depth chip is gone: hop count is implementation detail nobody needed to weigh, and the exit cost already prices its consequence. depthOf and the depth strings retired with it
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
chunk, so every boot painted bare heads until it arrived — but the core bundle can't carry the hat art. Instead, wrapAvatar now leaves a STAMP per head in localStorage (art + percent geometry, size-independent, capped at 40, cleared when a head goes bare so removals can't ghost), and the core avatar factory replays stamps until hatsReady answers — from then on a null from wrapAvatar genuinely means bare. Verified headless: a stamped hat is on the header avatar 15ms after DOM-ready, and once the chunk lands with a bare-head answer the stamp is cleared with no double-wrap
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
sits INLINE, directly under the note it answers, instead of at the bottom of the thread where nobody scrolled to find it — slotted after the focused note (the root by default), appended at the end only until the focused note loads, with a refocus handshake so the cursor survives the box relocating when the thread finishes loading. (2) Posting is OPTIMISTIC: a provisional dimmed copy tops the feed before any signing or relay round-trip (a remote signer alone can take seconds), swapped for the signed event on success and withdrawn on failure — the composer closes immediately and reopens with the text intact if publishing fails; pending rows can't be opened/replied/zapped until real. (3) The full header nav (search, chat, settings, lock, avatar) now shows on the profile, thread, and search screens whenever a wallet is open — losing the search button on a profile made finding the next person a trek back home; the public no-wallet surface keeps the plain brand. All verified headless: box renders mid-card under the focused note with focus landing after load, a post appears 250ms after tapping Post (pre-publish) and survives settle, and the search button works from a profile
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
the post's thread focused on that note and puts the cursor straight in the reply box (inside an already-open thread it just re-targets the reply to that message). Verified headless on /adam: 51 rows all carry the button, tapping one lands with the thread reply input focused
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
|
| |
|
|
|
|
|
| |
Delete — a gift-wrapped DM can't be retracted from the peer's relays). DM reactions ride the kind-7-in-a-gift-wrap shape 0xchat uses (makeDMReaction in dm.js), sent to the peer and echoed to our own inbox so other devices fold them in; inbound kind-7 rumors land in dmReacts keyed by the reacted message's id — no thread lookup, so a reaction arriving before its message still sticks. Replies e-tag the quoted kind-14 (makeDMRumor grew extraTags) and render the same accent-bar quote as community chat; the composer gets the same replying-to bar. Chips are joinable and your own reaction is highlighted/replaceable, all optimistic with rollback. Round-trip verified: reply and reaction rumors wrap/unwrap with tags intact
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
the ends: Next on the last page returns to page 1, Prev on page 1 jumps to the end. Unlike the swipe ring they never leave the list for the tabs — buttons paginate, swipes navigate. Applies to the shared pager, so the address list wraps the same way
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
lastPage ⇄ Receive ⇄ Send ⇄ page0 … Left from the last history page now wraps into Receive then Send (matching the tab bar's visual order and the pre-ring muscle memory of left-from-history landing on Receive); right from page 1 gives Send, then Receive, then the last page. Verified headless: right×3 walks history → Send → Receive → history(last), left×3 walks it back
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
page1 … lastPage ⇄ Send ⇄ Receive ⇄ page0 … Swiping left over history turns pages older and wraps into Send, then Receive, then back to page 0; swiping right from page 1 goes the other way — Receive, then Send, then the LAST page (entering history from a wrap lands on the chosen page; an oversized index clamps to the last). A detail view / spinner / empty list publishes no pager and is treated as a single page, so the swipe still reaches the tabs from anywhere. Verified headless with synthetic touches: right×3 walks history → Receive → Send → history(last), left×3 walks it back
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
profile page immediately, not after Save: while the editor is open the DRAFT is the truth for the banner, the avatar, and the display name shown up top — clearing a field previews the removal too. The editor's text fields render per keystroke so pasted URLs track live (the morph never rewrites a focused field, so it can't fight the typing); uploads already rendered after filling the field. Verified headless: typing picture/cover URLs into the edit form updates the page-top avatar and banner backgrounds before any save
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
— it was hidden on your own page), bio, and latest posts, exactly like anyone else's, with the editor behind an explicit Edit profile button (Save or the new Cancel returns to the view) and a New post button that unfolds a composer — publishPost signs a plain kind-1 with the login/wallet identity, publishes to the identity's own relays, and prepends it to the notes list so it shows immediately. The header avatar's tooltip says "Your profile" instead of "Edit profile" to match. Compose/edit state clears on open/back so drafts never leak across profiles. Verified headless end-to-end: onboard → avatar → view mode (buttons + npub, no form) → Edit opens form → Cancel → New post opens composer
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
|
| |
|
|
|
|
|
| |
that shows the live chat history to anyone, logged in or not, with a "Sign in to chat" button ("Open coinos" when the device already has an account). No server-side decoder needed: the community's join material was already public by construction (baked into the client bundle — every coinos user is meant to be a member), so the page derives the stream keys and decrypts wraps client-side exactly like the app's Chat tab, worker-offloaded with the same sliced fallback. Renders messages + edits + deletes + reactions and honors the folded control state's ban list; presence/typing/guestbook deliberately omitted. Profiles come from ONE batched kind-0 query per burst (a REQ per author tripped relay concurrency limits). Plumbing: the join material moved to src/community.js (shared with messages.js so they can never drift), /chat is excluded from the single-segment profile deep-link (which was eating the path and rewriting the URL before the route ran), and app renders stand down in public-chat mode so a feature-init render can't paint onboarding over the page. Verified headless against the live relays: 59 messages decrypt, ban-fold applies, names resolve
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
state) at sync time, newest-first per txid, instead of adopting the newest core's list wholesale. The wholesale apply lost history: a device whose history fetch keeps failing (mobile 429 storms — the balance scan is cheap per-address calls, history is the heavy /txs pulls) publishes a THIN core with txs:[] and a fine balance, and every fresh device then adopted the newest=thin core and showed a balance with no transactions — adam's phone today, with three ~2KB thin cores on the relay shadowing the fat 7KB core from Aug 18 that still holds the history. Union is safe because history is append-mostly and every SUCCESSFUL local scan still prunes rows its addresses no longer report; a wiped device also self-heals the fleet, since its republished core carries the union'd txs back to the relay
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
the pager's Next; right = newer) instead of bouncing straight into the Receive tab — the same swipe falls through to tab navigation only once the pages run out in that direction. historyTab publishes what's on screen ({page, pages} in _histPager, cleared for detail views/spinner/empty) and the wallet-screen swipe gesture consults it when the touch began inside the list card (.history-card)
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
not after the slide lands. The old model held ALL renders until the glide settled (a mid-glide morph used to strip the drag's inline snap-none and yank the card — so nothing could paint until rest, and the history switch waited out the whole animation). New model: the strip alone is frozen against the morph (_skipMorph short-circuits that subtree) while renders run freely, so the release can commit the account switch immediately — desktop knows the chosen card exactly (it computes the target itself), touch predicts it from the release position + scroll velocity (nearest card, pushed one over on a real flick — the same physics mandatory snap applies). settle stays the authority: it unfreezes the strip, runs a syncing render, and corrects a rare wrong touch guess when the snap actually lands. Renders stay held only while the finger is DOWN (pointermove writes scrollLeft on the main thread — a render there eats frames)
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
key is now kept in the wallet's nostrlogin state and resumeLogin reattaches from it instantly, so opening chat no longer greets passkey users with "Your nostr signer isn't connected". Kept deliberately and only for the passkey door: a pasted nsec keeps its used-once-never-stored promise, but a passkey key exists only through this app's PRF derivation and re-deriving it costs a WebAuthn ceremony (system dialog + biometric) on every open — and the same storage already holds the wallet seed, so this is the same exposure class. Existing passkey sessions heal on their next reconnect: that ceremony saves the key, and none is asked for again
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
Savings button (and the unfold panel) used to render only on the SELECTED card, and the strip's flex row stretches both cards to the tallest — so the whole strip resized right as a swipe settled onto Spending. The extras now belong to the spending card itself, selected or not, so card heights never move during a drag; tapping the button on the peeking card selects Spending and opens the move panel in one go
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
after verification moved off (chunk-60dfb4nt in profiles). The verify worker grows two ops: 'openWraps' opens community (Concord) wraps given the symmetric stream conversation key — nip44 decrypt + the seal's schnorr verify + hash checks per wrap, no secret crossing the thread boundary since stream keys are group material; 'unwrapDMs' runs the full NIP-17 unwrap (two ECDH ops + seal verify) when the wallet holds the raw identity key. Remote signers can't export their key, so their DM path stays on the main thread, exactly as before. The time-sliced main-thread queues survive as fallbacks — a dead or stalled worker only changes WHERE the crypto runs (workerCrypto resolves null and callers re-enqueue), never whether chat catches up. Bonus: subChannel derives its stream key once per channel instead of once per wrap (groupKey does hkdf + ECDH per call — real curve work on a 200-wrap backfill)
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
the Ark mark: a faucet paying the LN address settles on the registrar's CLN and arrives as an anonymous arkoor from the float (the mailbox protocol carries no memo), so the wallet genuinely couldn't tell the rail. The registrar now records a receipt {sat, hash, ts} per LN settlement it forwards via the float (7-day ring per name) and serves them at GET /lnreceipts (NIP-98, owner/manager only — it's a feed of incoming amounts); the wallet fetches them through the names feature (which holds the signer) and fuzzy-matches receive movements by exact amount + delivery within ±3 min, stamping viaLn + the payment hash so a receipt can never claim a second row. History row and detail view show ⚡ for viaLn receives; the tag rides the sync snapshot to other devices via merge enrichment. viaClink deliveries need none of this — the wallet's own LN-receive flow already tags itself. Best-effort by design: an amount+time collision only mislabels an icon
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
buffers, frame-drop + scroll-position tracing) now that the stutter is fixed — the render-gate-during-drag fix stands on its own
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
showed scrollLeft climbing (19→40→64) then JUMPING to 0 and stalling ~80ms before gliding — a position jump at a steady 17ms/frame, not a dropped frame. Cause: a background render (ark update, balance tick) firing mid-drag; the morph rebuilds the strip and strips the drag's inline scroll-snap-type:none, so mandatory snap yanks the card to its origin. Fix: renders are held for the sub-second of a carousel drag and one flushes when motion stops, so the DOM never churns under the finger. Verified: a mid-drag render now leaves scrollLeft untouched
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
survives in the buffer regardless of read timing
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
jump during a flick (a stutter that isn't a dropped frame)
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
rebuilding it costs ~60ms (sort + dedupe + gift resolve + secp256k1), and its 1.5s TTL meant an unhurried flick always hit a cold rebuild, stuttering the slide's tail — 'warms up if I keep going fast, cold if I pause, refresh reproduces' is the classic TTL-expiry signature. Now keyed on a state revision (_save bumps state._rev) so it's reused until the data actually changes and never re-cools between flicks. Diagnostics still on to confirm
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
frame gap into __dl so a real drag's jank appears next to the release/settle it overlaps
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
release decision (vel/travelled/dir/from/best) and settle outcome, to capture what a real failing flick does. To be removed once diagnosed
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
unfolded on the balance card with no way to dismiss it short of completing or switching tabs. A centered Cancel link closes it back to the plain card
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
release. My new travel-fraction variable was also named 'moved', colliding with the drag-started flag of the same name in the enclosing scope — a temporal-dead-zone ReferenceError that threw up() before it could commit or even snap back, so a release did nothing. Renamed to 'travelled'. (This is what made 4f0e444's drags dead.)
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
latest video shows the card sliding a third of the way toward the neighbor then springing back to where it started — a short flick that never crossed the halfway settle point. Release now commits to the neighbor on a deliberate small move (past ~15% of a card) or any quick flick, instead of only past the midpoint; otherwise it snaps back. Direction comes from flick velocity, or from how far the grabbed card actually travelled
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
synchronous verifyEvent (a schnorr check per event, ~560ms on a relay backlog — the profiled carousel-stutter cause) is switched off, and every event is now gated through a Web Worker before any caller sees it. Nothing unverified is ever trusted — the worker runs the same nostr-tools verifyEvent, just on another thread, with a synchronous fallback if the worker is unavailable (standalone file://) or stalls. Bundled to dist/verify-worker.js, precached in the SW
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|