| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
| |
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011bvzKaZosXGHkigcGcSzAR
|
| |
|
|
|
|
|
|
|
| |
The 3:4 crop for tall pictures cut the top and bottom off comics. Now
nothing is cropped; a picture is as wide as the column allows up to
min(560px, 70vh) tall, and only one taller than that narrows.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011bvzKaZosXGHkigcGcSzAR
|
| |
|
|
|
| |
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011bvzKaZosXGHkigcGcSzAR
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Efrat's clip (blossom.primal.net → r2a.primal.net) showed a blank tile on
a phone: the 64 KB probe failed there (slow connection), the failure was
kept for good, and a tile with a failed probe never tried for a frame.
- A failed probe is dated and asked again after an hour (it was kept
forever; old 'failed: true' entries are retried). The probe waits 10 s.
- A tile whose probe failed still tries for a first frame, giving up and
letting go of the file after 8 s.
- Primal's CDN lets scripts read the bytes but not Content-Range, so the
size was unknown: a HEAD's Content-Length (readable by any page) fills
it in.
- No size in imeta but a bitrate and duration (Primal posts those): the
size is estimated from them. 'dim 1080.0x1920.0' is understood.
- A tile with nothing to show is dark, like a video, not a pale gap.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01ULQZS9MQa6iAsXnrJUD9Du
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Choosing Videos no longer jumps into the full-screen pager: it shows
the vertical clips as a grid, like Images, and a tile opens the pager
on that clip.
Every tile gets a first frame when that costs only the start of the
file (the index leads, or the server serves ranges), whatever the
clip's size; a clip over budget wears its cost as a badge. Only an
index-last file from a server without ranges shows its cost instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01ULQZS9MQa6iAsXnrJUD9Du
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- What a clip costs is learned up front: the post's imeta (size, dim,
image) when it says, else one 64 KB ranged request — the total from
Content-Range, whether ranges are served, whether the MP4's index
(moov) comes before its data, and the picture size from the track
header. Up to three at a time; remembered across sessions. It also
replaces the player-based shape probe the feed ran on every clip.
- Clips over 25 MB (8 MB on 2G/3G, 3 MB with data saver), above 1080p,
index-last files over 8 MB from servers without ranges, or clips that
couldn't be measured wait for a tap: a cover saying what they cost
("50 MB", "4K"). Settings → Nostr → Videos: Autoplay large videos.
- The pager loads only the clip on screen in full; the next gets its
opening if within budget; everything else holds nothing. Sizes are
asked three slides ahead.
- Feed clips fetch nothing until they're about to play (preload none),
with the post's still as a poster.
- Video grid tiles show a first frame only for small clips that start
quickly; others show their cost.
- Videos lists only clips known to stand taller than wide; one of unknown
shape is measured and joins when it qualifies; choosing Videos with none
known yet opens the pager on the first that turns up.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01ULQZS9MQa6iAsXnrJUD9Du
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
All / Images / Videos sits under the feed chips (remembered).
- Images lays the feed's pictures out as a three-column grid.
- Videos opens straight into a full-screen pager: one clip per screen,
snapping, the one on screen playing with sound (muted with an Unmute
button when the browser refuses), the next a flick or ↓ away. Closing
it leaves a grid of the clips.
- The pager shows only the media until tapped; a tap shows the author
(tap for their profile), the post's words as a caption without the
media links (tap to expand), and likes and sats in the bottom right,
which also like and zap.
- It is a place: Back or Escape closes it. Built outside the morph, so a
repaint never restarts a clip; off-screen clips let go of their media.
- Near the end of the grid or the pager the feed fetches older posts and
new slides are appended.
- Toasts now sit above full-screen viewers.
tools/feed-media-views-test.js
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01ULQZS9MQa6iAsXnrJUD9Du
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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
|
| |
|
|
|
|
|
|
|
| |
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
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- 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
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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
|
| |
|
|
|
|
|
| |
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
|
| |
|
|
|
|
|
|
|
|
| |
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
|
| |
|
|
|
| |
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
|
| |
|
|
| |
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| | |
|
| | |
|
| | |
|
| |
|
|
|
| |
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VP5Ljqk7EYcm5QQAbzDwog
|
| |
|
|
|
|
|
|
|
|
| |
A pill with the pencil and "Post", fixed above the bottom nav inside the
page column, wherever the reader is in the feed; the tap scrolls to the
top, opens the composer and focuses it. Hidden while the composer is
open (it has its own Post button) and for visitors.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Du1qT1wbkYwYK6zkGH79Rp
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
then uploading; the pencil reads "New post".
ui.mediaProgress { stage, pct } (a render per whole percent) drawn as a
labelled bar under the reply and post composers, instead of only the
paperclip turning into a spinner. Upload progress comes from
XMLHttpRequest upload events (fetch() has none) in uploadPublicMedia's
new onProgress; injected fetchers (tests) keep the fetch path.
upload-progress-test: 1.2 MB at 400 KB/s climbs 0→100 in steps.
video-compress-test: the bar counts the shrink, then the upload.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Du1qT1wbkYwYK6zkGH79Rp
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
are contenteditable.
Android keyboards (Gboard GIFs, stickers) only offer images to fields that
accept them, and Chrome says so for contenteditable, never <textarea> —
hence the greyed-out GIF key and "This app does not support images here".
<coinos-text> (src/rich-text.js) is a contenteditable that acts like a
textarea to the code around it (value, selectionStart/End, setSelection-
Range, placeholder), keeps text plain, and hands any pasted/dropped/
keyboard picture to onMedia — the paperclip's upload. The morph syncs its
value like a field's and leaves its own editing attributes alone.
composer-media-test: types like a textarea, strips pasted formatting,
a pasted GIF uploads and lands in the reply, still editable after repaint.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Du1qT1wbkYwYK6zkGH79Rp
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
feed and the thread.
A kind 1068 poll showed as "an event this app doesn't show". Now its
options are rows with the vote share behind them (kind 1018 votes, each
person's latest counts, single/multiple choice, endsAt honored); a tap
votes, marked as yours at once. A quoted poll is pictured in its card.
Feeds and profiles list polls alongside notes, and a thread's root is
looked up by id with any kind, so replies to a poll find it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Du1qT1wbkYwYK6zkGH79Rp
|
| |
|
|
|
|
|
|
|
| |
Pinch zooms around the fingers (up to 5x), one finger pans a zoomed picture
within its edges, double-tap zooms in at a spot and again to reset, and
the wheel zooms on a desktop. A tap still closes an unzoomed picture (after
a beat, so it can be the first half of a double-tap); a gesture never does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
| |
you've seen it.
The new-posts pill used to glide to the top of the feed; now it stops at
the oldest post that came in, right above where you were reading, so you
read on upward. Posts that arrive while you read are tinted until they've
been in view about two seconds, then fade to the plain page (like Amethyst).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
| |
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
| |
Feed and Notifications leave the chat list (the nav reaches both; the bell
carries the notifications dot now). On a wide screen the bar runs edge to
edge with its buttons over the page column; quiet muted tabs, a pill for the
current one. Feed gets a newspaper icon instead of a house.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
| |
It now shows across the signed-in app and a visitor's public pages (never
over sign-in, onboarding or the lock prompt), marks the current page, and
carries the unread dot. Home is renamed Feed, and the Messages button leaves
the header.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| | |
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
sound is on; the list action moves under the profile row.
The pill was at the top of the window, a stretch for a thumb. An unmuted
video kept a Mute button the player's own controls already provide, and in
a quoted note the box could be rebuilt muted; unmuting now pins the box and
removes the button. The ☰ on the profile's action row wrapped onto a line
of its own; it is now "Add to a list" beside "Their timeline".
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
with no web of trust borrows coinos's.
Each relay in a feed's list is a select over popular relays that answer a
bare "everything" query, with Other… for one typed in; the firehose reads
coinos, damus, nos.lol and primal by default. A relay chosen by hand or
named in a link is tried even if it had been marked sick, which had made a
hand-picked nos.lol fetch nothing.
A visitor, or a wallet that follows fewer than five people, has no follows
whose flags could fold anything, so the public feeds showed the relays raw.
They now get the moderation the coinos account gets: its follows' reports
(and, for visitors, its mute list), read every few hours and kept on the
device. A wallet's own follows and flags always come first.
The new-posts pill: the generic button press moved it half its width to
the right (its centring transform was replaced), so the tap never landed,
and the generic hover painted it light over light text.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
from relays of your choosing.
Plain /feed is the firehose: every post relay.coinos.io carries, no filter.
It sits beside Following as a built-in chip for wallets and opens as a
session feed for a visitor.
A feed's relay list is independent of what it asks for. The Relays button
in the header opens a panel to add to or take from the list; empty means
the usual choice (each author's outbox, the wide relays for a topic, the
coinos relay for the firehose). The list rides in the URL as ?r=…, on every
kind of feed, so a shared link reads where you read.
A feed state dropped while its fetch was still out used to swallow the
answers meant for its replacement; a relay change showed nothing.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
|
| |
|
|
|
|
|
|
|
|
|
|
| |
A plain link becomes an Open Graph card: picture, title, a line, the site.
The registrar fetches the page on the reader's behalf (names/preview.js,
GET /preview?url=): only public hosts, at most half a megabyte of HTML,
cached a day. The client warms the card with the rest of the post so the row
arrives whole, keeps a few hundred cards across sessions, and leaves a page
with nothing to say as a bare link. Pictures and YouTube keep their boxes.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
|
| |
|
|
|
|
|
|
|
| |
One variable-weight woff2 each for upright and italic (41 KB + 45 KB, OFL),
shipped with the build and precached by the service worker; the system stack
paints first and stays when the file never arrives.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
|
| |
|
|
|
|
|
|
|
| |
whole.
The list button made four across, and ⚡ Pay folded onto two lines.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
|
| |
|
|
|
| |
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
|
| |
|
|
|
|
|
|
|
|
|
|
| |
note's width.
The box sat inset beneath the focused note's grey highlight as a separate
field. It now shares the highlight: same width and background, the note's
bottom corners squared off and the box carrying them, the field inside a
quiet hairline. Under the root (no highlight) it is a soft panel of its own.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
beneath.
A thread opened to read came with an empty field under its first post.
The box now appears under the note whose Reply was tapped (or while a
draft is pending), folds away once the reply is sent or Cancel is tapped,
and takes Amethyst's shape: the field across the full width, attach and
preview on a row beneath it with Cancel and Reply at the right.
Tests: thread-reply's helper follows the new shape and expects the box to
fold after sending; compose-preview and feed-list tap Reply before
expecting a box (the thread card carries .thread-card as the marker).
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A long scroll left hundreds of rows mounted, and every repaint (a zap
count, a profile landing, the thirty-second sync) rebuilt and diffed all
of them — the slowdown deep in the feed. Rows beyond two and a half
screens either side of the viewport are now replaced by a spacer at their
measured height, so the page keeps its length (scrollbar, Back, arrival
anchoring behave as before) while a repaint touches a few dozen rows.
After svelte-virtual-list: measure after paint, estimate unmeasured rows
at the running average, hold the reader's row still when a measurement
corrects an estimate. Applies to the profile notes list too.
Tests: feed-window-dom (120 posts, 27,800px page, 12–24 rows mounted,
anchor held, spacers both sides); feed-live looks at the top after
scrolling up; the moderation test's stub relay no longer lets real posts in.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
| |
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
YouTube too.
An inline video starts as it scrolls into view (muted, the only way a
browser lets a page start one), pauses as it leaves, and a corner button
turns the sound on; the native controls' own mute keeps the button honest.
A YouTube box loads its player muted once in view (enablejsapi, so the
same button can unmute it over the iframe API) and pauses out of view; a
tap on the poster still means sound. One IntersectionObserver watches
every player. Data Saver keeps tap-to-play.
Tests adapted to the feed's new admission rule (a row waits for its
author's face and pictures): the markdown and post-actions tests seed the
poster's profile and serve their pictures locally; a picture that cannot
be decoded is now a link, by design.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|