| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
| |
beside it (40px).
Adam, 2026-10-17: "Can we give the close button mute button and tall only chip the same height?" The toggle was 32px between two 40px buttons; it's 40px now, with its text a size up to match.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
|
| |
|
|
|
|
|
|
|
|
|
| |
pager, not over the Videos grid.
Over the grid it sat on its own line below the view chips, looking misplaced (Adam, 2026-10-17: "The tall only chip looks a bit misplaced here. Let's only show it in media mode"). The grid still follows the same setting, so a change in the pager shows up in the grid too.
tools/pager-ready-gate-test.js: the grid shows four clips with no toggle over it; the pager's toggle checks are unchanged and pass.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
|
| |
|
|
|
|
|
|
|
|
| |
a moment after it settles.
It comes back the moment the pager is in motion again — a finger on the slides, a glide, a flight — keyed off the same `moving` flag that hides the seek bar, and fades out 1.2 s after landing (the fade restarts on every stop). Opening the pager shows it briefly, then it fades. Picture pagers are unchanged. (Adam, 2026-10-17: "the video position counter should fade out once you've stopped scrolling and re-appear when you scroll again".)
tools/pager-seek-hide-test.js checks it: faded at rest, back while a finger holds the slides, still there just after the snap back, then gone.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
| |
its clip is ready to play; and the carousel shows the clips the screen's way round, with a toggle for all shapes.
Ready to play: the clip's first frame on its slide and its opening in the cache (the head start done). Until then the post waits out of the carousel — parked off screen, no height, no snap point — and a swipe or a flight passes over it to the next post that is ready; it joins when ready, and a post joining above the clip on screen moves the scroll by its height in the same frame (snapping off for it, and never while the pager is in motion: then once it has settled), so the reader doesn't feel it. The post tapped to open the pager is in whatever its state. Seen on Adam's phone before this: 17 of 18 landings had their frame, the 18th was black for a second while the server was still making its frame. (Adam, 2026-10-16: "i never want to land on a black screen i'd rather just not have a video in carousel yet if it's not ready to play".) A local http clip, which can have no server frame, waits only for its opening. Data Saver is unchanged (nothing waits: heavy clips sit under their tap-to-play cover).
The screen's way round: the Videos view used to show tall clips only, everywhere. Now it and the pager show tall clips on a screen taller than wide and wide clips on one wider than tall — a square either way — unless the reader asks for all shapes: a toggle over the Videos grid and in the pager's top bar ("Tall only" / "Wide only" / "All shapes"), remembered on the device. A post the wrong way round is kept out of the carousel the same way (not the one opened on, and never the clip on screen: a turn of the phone doesn't pull it away; shapes not yet known are let in, and a shape is learned from the server frame as it loads). Turning the phone re-sorts them. (Adam: "only show vertical videos by default on a phone that's in vertical orientation and only horizontal videos if the phone is in landscape but have some kind of control to toggle the ability to see all videos".) The posts prepared ahead (frames for five ahead and three behind, head starts for the next two and the one before) skip the ones kept out for shape.
tools/pager-ready-gate-test.js: the grid shows four of five clips (not the wide one) with the toggle, and all five after a tap; in the carousel the next clip, its frame delayed, waits while the one after (ready) has joined; a step lands past it on a clip with its frame; the waiting post joins above the clip on screen without moving it, the counter grows, a step back lands on it; the pager's toggle lets the wide post in and out. pager-tap-slop-test keeps all shapes on (its path has a wide clip before a tall one in a portrait window). Every other pager test passes; feed-pager-ahead and feed-video-pager fail the same checks on a clean checkout.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
| |
while the pager is in motion.
The bar sits in the clip's slide, so a glide to the next clip carried it up the screen, a white line sliding out while the next slid in (Adam, 2026-10-16: "i don't want to see the seek bar while i'm transitioning to the next video"). The pager's frame loop now marks the pager `moving` whenever the clip on screen's slide is off its mark by more than 2px — under a finger, in a glide or a flight, snapping back — and under that mark no seek bar shows at all; it fades back in (180 ms) once the clip has landed. Bars on slides other than the one on screen stay hidden too (a slide seen before kept its bar ready).
pager-seek-hide-test: at rest one bar, on the clip on screen; through a glide to the next clip every frame between the two has the pager marked moving and no bar to be seen; the landed clip's bar fades in and the one before shows none; a finger holding the slides 100px off their mark hides it, letting go brings it back. pager-controls, pager-no-poster and pager-swipe-fly still pass (feed-video-pager-test's two failures are on clean HEAD too).
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
|
|
| |
Five chips (Notes, Conversations, Images, Videos, Articles — Conversations joined them today) no longer fit a phone, and the strip was a flex row that neither wrapped nor scrolled: the page was 42px wider than a 390px screen, which on an iPhone is a page you can pinch out of and pan, a blank strip down the right (Adam, 2026-10-16: "I can kinda zoom out and pan around a bit on my phone"). The strip now scrolls sideways like the feed chips above it, and the root element clips sideways overflow outright so no stray wide row can do this again (on the root, not body: a body that clips becomes the scroll container a sticky rail measures against).
feed-media-views-test: the strip scrolls sideways and the page is no wider than the window.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
is live — names open profiles, the words open the post.
No poster: every poster there was (the kept frame, a post's imeta picture, the server's) is 480px wide and taken at some moment of the clip, so it showed for a beat, soft and not quite the first frame, and the clip's own first frame replaced it — a flash on every flick (Adam, 2026-10-16: "if we're auto playing let's not show the thumbnail let's just start on the first frame of the video"). The slide is the pager's black until the player, whose opening is in the cache by then, has its first frame. Giving the next clip's player its file ahead of time was tried and dropped: it pulled the whole file down (pager-eager-test). On a connection to be careful with the poster stays: a heavy clip waits for a tap, and the still is what there is.
The caption was a plain excerpt, which left "@NostrValley" as dead text. It is the post's words rendered as a post's are now — names, tags, note references and links live. A tap on a name, a tag or a note reference leaves the pager (and the full screen) before going where it goes; a link to the web opens in its own tab and the pager stays; a tap anywhere else on the caption leaves the pager and opens the post, where the whole of a long caption reads ("clicking anywhere on the caption that's not a link should take me out of fullscreen and take me to the post"). The expand-in-place toggle is gone.
Tests: tools/pager-no-poster-test.js (no poster on the clip on screen nor the ones ahead, playing; with Data Saver the posters stay), tools/pager-caption-test.js (the name is a link and opens the profile out of the pager; a tap on the words opens the post; a web link opens its own tab and the pager stays). Pre-existing on a clean checkout: feed-video-pager-test "from where it was in the feed", feed-pager-ahead-test "past fifteen posts with none" (fails on 33c7218 too).
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
|
| |
|
|
|
|
|
|
|
| |
chips.
A soft-grey active tab beside a white one, sitting right on white post cards, read as the WHITE tab being the selected one — its colour matched the posts under it (Adam, 2026-10-16: "the bg coloring on posts/replies is backwards"). The tabs take the feed chips' look now: the chosen tab solid accent with light text, the other one on the quiet surface in muted text. Other segmented controls are unchanged.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
the post it answers.
Amethyst's Conversations beside its New Threads (Adam, 2026-10-16: "a replies
mode or Conversations mode like Amethyst has"). The Following feed showed
their posts and dropped every reply, so what people were saying TO each other
only turned up by opening threads. Now a built-in Conversations chip sits
beside Following (id 'replies'; its own notes, clock, disk cache and relay
choice like the other built-ins): the same people, asked for notes and NIP-22
comments, keeping the replies — a reply as NIP-10 marks it, a comment, never a
quote or a repost (feedKinds/feedKeeps; the merge, seeding, borrowing, the
live watch and the media index all go by them). Each row shows what it
answers above it — "↩ Replying to" and the quote card of the parent (the e
tag marked reply, else root, else the last; a comment's e or E), tapping the
card opens that post, tapping the reply its thread at the reply — and the
header drops its "reply" tag there. The parent is usually in hand already
(the Following feed, a thread, someone's page: noteInHand, which quotedNote
checks before any relay — nostr:nevent quotes gain the same), and a thread
opened from such a row paints its root at once instead of fetching it. In a
conversations feed the parent counts as part of the row, so it arrives whole
with the first screen (noteReady's parent option, per feed); a profile's
Replies tab shows the same context and fills its cards in. Not a shareable
URL (it is nobody's timeline), so no Share in its menu. Any custom feed can
be one: "Replies instead of posts" in the editor (def.replies, synced with
the definition), named in its summary. The disk cache now keeps comments.
tools/feed-conversations-test.js: Following keeps posts only; the chip; the
replies newest first with the parent card from hand (no relay asked) and a
missing parent said so; the menu without Share; the reply's own tap opens
its thread at the reply, with the root painted from hand; the card's tap
opens the parent; Back lands on Conversations; the profile's Replies tab
shows the context, Posts none; a custom conversations feed via the editor
borrows the replies from the feeds in hand, not the posts. feed-custom-test
lists the new chip. Pre-existing failures not touched: feed-custom-test's
"a tagged post lands in it"/"lands in Everything" and feed-streaming-test's
pill position fail on clean HEAD the same way; feed-follow, feed-cache,
own-post-feeds, feed-list, feed-media-views, feed-switch-seed, profile-pager,
thread-pager, feed-repost, feed-live, feed-new-posts, feed-window,
feed-choice-per-wallet, feed-nav-destination, feed-wallet-back, moderation,
feed-mention-tap and feed-reboost pass.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
|
| |
|
|
|
|
|
| |
paid to you; the media pager says when a picture can't be loaded. (1) LUD-03 (Adam, 2026-10-16: "add lnurlw support"): an lnurl whose terms are a withdrawRequest — found when the pay fetch meets one (fetchPayParams throws with the terms attached, resolve() takes them up) — shows the site, their range (or the fixed amount) and description, the amount field at their max; Withdraw makes an invoice over Ark for the amount (arkLnInvoice), hands it to their callback with k1 (submitWithdraw), and watches it (arkLnWatch): "has your invoice for…", then "Received …" on settlement; their refusal in their words, with Try again; Back before it is paid stops watching. An amount outside their range is told the range, nothing sent; a watch-only device says it can't receive. LUD-17 schemes lnurlw:// and lnurlp:// (and lightning: before them) are the URL over https, http on an onion (parseZapTarget). (2) A pager slide whose picture can't load — the server copy and then the original both failing (isolabellart's relay answering 502 that afternoon: the broken-picture glyph sat in the corner of a black slide) — hides the <img> and says "This picture couldn't be loaded" with the host, caption and controls kept (mp-gone / .mp-missing). tools/lnurl-auth-unit-test.js (lnurlw:// and lnurlp://, an onion, the terms, a pay fetch that finds a withdraw, the callback's k1 and pr, a refusal); tools/lnurl-withdraw-test.js (the zaps feature against a faked Ark and a stand-in site: the terms, max prefilled, out of range refused unsent, the invoice at the callback and watched, Received on settlement, a fixed amount with no field, Back unwatches, a refusal's reason); tools/pager-gone-picture-test.js; lnurl-auth and feed-picture-pager tests pass.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
|
| |
|
|
|
|
|
| |
one zoom layer, not one per slide. A flight through the pager stuttered on the phone (Adam, 2026-10-16: "a bit of choppiness as they scroll"): every slide within a screen and a half loaded its picture's ORIGINAL as it passed — camera files of several megapixels, decoded on the main thread and uploaded as textures mid-flight — and every slide's <img> carried will-change: transform (for the pinch), a full-size layer each. Now a slide's <img> starts on the copy the feed painted (1100px wide, decoded already: mediaReady's thumb), or the server's copy (pic.webp s=col, cached at the CDN), or failing both the original (onError falls back to it too); decoding async. The slide at rest is sharpened a moment after landing (pagerSharpen: the original fetched and decoded aside, then swapped in — same box, same aspect), so a pinch has the detail, and a slide paged past costs nothing. Only the current slide (mp-cur, set in pager.arrive) has the layer. tools/pager-sharpen-test.js (three 4-megapixel pictures: every slide opens on a blob copy, the one on screen alone has will-change, it is sharpened to the original within a moment while the others keep their copies, ArrowDown moves the layer and sharpens the next); feed-picture-pager, pager-resize, pager-wheel, thread-pager and pager-swipe-fly tests pass.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
|
| |
|
|
|
|
|
| |
mutes on one tap; a flick flies long, to be caught. Three from Adam, 2026-10-16. (1) "Make the reaction button in media mode give a menu of different emojis": a tap on ♥ opens the feed's own emoji sheet (ui.reactPick → reactPicker) over the pager — the backdrop gets over-pager (z-index 300, above the pager's 200) — the emoji chosen shows on the button (postReactNode, a picture for a :code:), a tap again takes it back, and Escape closes the sheet before it would close the pager. (2) "The mute button, when I click it, immediately unmuted again": under a finger the pager arms a pending tap at touchend and stands it down when the browser's click arrives — but that was judged in a bubbling listener on the pager, and the sound button's own handler stops the bubble, so its click never stood the tap down and the pending tap clicked it again 120 ms later (every button in the pager with stopPropagation; the arrows too). Judged in the capture phase now, before any button's handler; tapDoneAt is set after the pending tap's own click so that click isn't swallowed. (3) "Make it fly longer before it settles… I can slow it down by touching again when I see something I like": a flick coasts 1600 ms' worth (was 800), up to 14 posts, at 260 ms a screen (up to 4 s) — a finger down holds it where it is, and lifting lands it on the nearest post. tools/pager-controls-test.js (one tap mutes and it stays muted; a tap again unmutes; the sheet opens over the pager, on top, with several emoji and a way to more; 🔥 picked shows lit on the button; a tap takes it back; Escape closes the sheet, not the pager); pager-swipe-fly-test adjusted for the longer coast; pager-tap-slop and feed-picture-pager tests pass.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
|
| | |
|
| |
|
|
|
|
|
| |
column under the middle. The caption ran the whole width of a 1920px screen and the counts sat in its far corner, hard to see from the picture (Adam's screenshot). From 900px across, the meta's padding and the seek bar's edges hold a 760px column centred on the screen (the gradient behind them still spans it), and the caption is a point larger. A phone keeps the full width. tools/pager-resize-test.js measures both.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
|
| |
|
|
|
|
|
| |
pointer. Adam reached for the scroll wheel in the pager on his laptop and nothing useful happened: a notch scrolled the snap container a hundred pixels and the glide's snap put it straight back (a headless probe of the live pager: scrollTop stays 0), so the arrow keys were the only desktop way through. Now a wheel notch is a step — down the next post, up the one before, a sideways notch (a trackpad's, or shift+wheel) along the post's pictures — with a 220 ms breath between steps so a spin of the wheel is a step or two, not five; a trackpad's run of small deltas is one step per run (its first 40 px, the rest until a pause is spent). Ctrl+wheel zooms the picture at the cursor, which is also what a trackpad's pinch arrives as in Chrome (attachZoom's new wheel: 'ctrl' mode, 0.004 per unit); while zoomed the plain wheel pans it, and at 1× it is the pager's again. And where there is a pointer to hover with (hover: hover and pointer: fine), arrows: up and down at the right edge, left and right when the post has a picture that way (has-left/has-right, set as the slide on screen changes), faint until the pointer is over the pager. The keys, the wheel and the arrows share one step (pagerStep). No mouse drag-flick: discrete steps suit a pager. tools/pager-wheel-test.js (CDP mouseWheel is always a 120-unit notch to Chrome, so a trackpad run is dispatched page-side; headless has no hovering pointer, so one is given with --blink-settings); feed-picture-pager and image-zoom tests pass.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
|
| |
|
|
|
|
|
| |
pictures. Adam: swipes and arrows up/down should go to the next post's media, left/right to the next within a post. The pager is now posts stacked top to bottom, each a row of its pictures or clips side by side — a quoted post's join the post that quotes it — with dots along the top when a post has more than one, saying how many and which. Up and down (swipe, arrows, j/k, Page) move a post at a time and land on a post where its row was left; left and right (swipe, arrows, h/l) move within the post and stop at its ends. A post's pictures all load as it comes near (those along its row too); a clip's head start goes to the next post's (where its row rests), the next along this one, and the post after next, at half; the look-ahead (the feed paging on, the media crawl) counts posts. Pictures pan sideways as well as up and down at 1×. Tests find the slide on screen by where it sits (the pager's children are posts now); feed-picture-pager walks right within a post, down past one with none, back up to where a post was left, left to its first, and a sideways swipe next to a swipe up; quote-pager walks the quoting post's row (its own picture, then the quote's).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KPw21NEpWeTGu6swVASmBt
|
| |
|
|
|
|
|
| |
one tap. Adam: Chrome's "v3.coinos.io – To exit full screen, drag from the top and touch the back button" shows every time, and a YouTube clip needs its own small full-screen icon. The notice is Chrome's, put up whenever a page goes full screen; a page can't turn it off, so the app goes full screen less. In the installed app (display-mode standalone, or opened from an android-app:// referrer) there is no address bar and the pager already fills the window, so it no longer goes full screen: a wide clip asks the screen to turn without it, and only if that's refused (the browser may allow turning only in full screen) goes full screen — the notice then, but the clip on its side; the screen is let go past that clip, or on closing. A browser tab goes full screen as before. A feed clip's own control bar loses its full-screen button (controlslist=nofullscreen): a tap on the clip is the way, into the pager. YouTube: the player's taps never reach the page, so a layer over a playing clip's picture takes them, leaving YouTube's control bar along the bottom to YouTube; one tap on it — or on the still — plays with sound and takes the clip's box full screen, turned on its side (unMute + playVideo through the iframe API for a player autoplaying muted); full screen the layer steps aside, so all of YouTube's controls are there. YouTube can only fill the screen through Chrome's full screen, so its notice still shows there. tools/fullscreen-notice-test.js (installed app: no full screen, the screen turned and let go, the fallback when turning is refused; no full-screen button on feed clips); tools/youtube-tap-test.js (YouTube stood in for: autoplaying muted, a tap on the control bar stays YouTube's, one tap full screen with unMute and playVideo, the layer aside full screen and back after; with Data Saver, the still to full screen with sound); video-unmute-test stubs ui for its slice of videoNode.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KPw21NEpWeTGu6swVASmBt
|
| |
|
|
|
|
|
| |
the next one, when it comes, is moved onto. Adam reached the last clip before the next was in, and a swipe only nudged the clip and sprang back. Now the pager ends in a screen of its own: a ring sweeping round, three sparks orbiting the other way, the play mark (a picture mark in a pictures pager) breathing in the middle, and "Fetching the next video…" (or "Fetching more pictures…") with a sheen running through it, all still under prefers-reduced-motion. Landing there stops the clip left behind and asks for more at once (the feed pages on; the media grid's pager crawls); slides that arrive go in above it, and one arriving while the reader waits is moved onto and plays. With nothing older to be had (the feed at its end, or the media index), it says "That's everything for now" and goes still. The scroller no longer anchors (overflow-anchor: none), so a slide put in above the screen doesn't hold the screen in view. Strings in all 20 languages. tools/pager-tail-test.js (relays that take 3 s for anything older: the screen at the end, the clip behind stopped, the next clip moved onto and playing, the end saying that's everything, Escape onto the last clip's post); feed-pager-ahead and feed-media-views count slides without it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KPw21NEpWeTGu6swVASmBt
|
| |
|
|
|
|
|
| |
screenshot: the bar ran along the very bottom edge of the screen, under Android's own gesture handle — a drag there is the system's (home, recents), and it was placed by env(safe-area-inset-bottom), which is 0 here (the page doesn't ask for viewport-fit=cover, and the gesture strip is never reported as a safe area anyway). It now sits 32px up from the bottom (plus any safe-area inset) and 16px in from each side, where an edge drag is the back gesture; the post's details stand above it, and its line has rounded ends. feed-video-pager-test checks the bar's place (32px up, 16px in) and that the author, caption and counts clear it; SHOT=path saves a picture of the pager with the details up.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KPw21NEpWeTGu6swVASmBt
|
| |
|
|
|
|
|
| |
clips a swipe away and a seek bar to drag. Adam full-screened a video in the feed and swiped up for the next one, and nothing moved: a tap on a feed clip handed it to the player's own full screen (video.requestFullscreen), where every touch is the player's and there is no next. Now, as pictures do since 6eda018, the tap opens the media pager on that clip over the feed's clips in the feed's order (every shape, not only the tall ones the Videos view flips through), carrying on from where it was. What the player's full screen gave is kept: the pager puts the page itself full screen (the tap's own gesture), and a wide clip locks the screen to landscape — for that clip; past it the screen follows the phone. Each clip in the pager (the media grid's too) gets its own seek bar, a thin line along the bottom that thickens with the details up; dragging it scrubs with the time shown, and the drag is never the pager's swipe or tap. A tap pauses the clip and shows the post's details, the × and the bar; another plays on. Leaving full screen another way (Back on Android, Escape) leaves the pager in the same step; × or Escape after moving on lands the feed on the post last watched. Threads, profiles and chats keep the player's full screen. tools/feed-video-pager-test.js (open, resume, landscape lock and release, seek drag, tap pause/play, swipe and arrows, Escape onto the post, exit full screen); video-tap-test now checks the feed opens the pager and runs its native full-screen checks in a thread. tools/puppeteer.js takes CHROME=/path for a machine without /usr/bin/google-chrome. feed-media-views-test's range-less probe check fails on HEAD before this too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KPw21NEpWeTGu6swVASmBt
|
| |
|
|
|
|
| |
swipe away. Tapping a picture in a feed row used to open it alone (the lightbox); it now opens the media grid's pager on that picture, over the feed's pictures in the feed's own order — every picture of a post before the next post's (a repost's inner post, the feed's moderation) — so a swipe up or down, or any arrow key (up/down as before, now left/right too), steps to the next or previous one. A tap on the picture that opened it still closes it, as a full-screen picture always has; once the reader has moved on, the first tap shows the post's details and actions, and the next (or Escape, or the ×) closes the pager with the feed landed on that post (glideToPost, once the pop has brought the feed back). The pager's pictures pinch to zoom (attachZoom, now exported, without its wheel — the wheel is the pager's; at 1× one finger is the pager's swipe). Pictures in threads, profiles and chats still open alone. tools/feed-picture-pager-test.js; image-tap-test and lightbox-scroll-test accept the pager as the full-screen picture.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
| |
and room composer still had them as buttons in its row — Adam's long DM to yorgos sat squeezed between a clip, GIF and Send. They now sit at the right of the field's last line (the field keeps room on that side, less with GIF alone), leaving the row as field + Send.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
|
| |
|
|
|
|
|
| |
had grown to Post · 📎 · GIF · Preview · Cancel; the reply box's likewise. The paperclip and GIF button are now small borderless tools inside the text field, bottom right (the field keeps room under its text so nothing runs beneath them), and the row under it is only what you do with the post: Post, Preview, Cancel (Preview, Cancel, Reply in a thread). Tests find the tools in .compose-box.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
|
| |
|
|
|
|
|
| |
Adam's follows; we showed nothing — a feed asked only for posts (kinds 1, 1068). A feed of people now asks for their reposts too (NIP-18 kind 6; topic and firehose asks stay posts-only), and a repost shows as the note itself, under "🔁 <name> reposted" — its reactions, zaps, ⋯ and thread are the note's. Only reposts that carry the note and whose copy checks out (kind 1/1068, the id the e tag names, a valid signature); a muted author's note stays out. A note shows once: as itself, or under the first repost of it to arrive, counting rows still warming at the door. Media grids skip reposts. tools/feed-repost-test.js
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
|
| |
|
|
|
|
|
| |
the post composer and in every chat composer (DMs, groups, communities) opens a picker: "GIFs from nostr.build" (linked, as their terms require), starter chips (gm, lol, yes, wow, …; the first runs on open), a search field (debounced 250 ms, no one-letter searches, one shared request per normalised query, never aborted, 429/403/503 answered in words), and a masonry grid whose tiles play the small H.264 copy only while near the screen, over the lqip blur, falling back to the animated preview (stills under reduced motion). A pick puts the original URL in a post — published with a NIP-92 imeta (m, dim, size, alt) — or, in a chat, sends it at once after whatever was typed. Media URLs not on gifs.nostr.build are dropped. Browser calls carry no headers: production needs v3.coinos.io registered in the nostr.build dashboard (localhost works as their shared dev client). tools/gif-picker-test.js runs against the live API.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
|
| |
|
|
|
|
|
| |
A feed row waits up to 8 s for its pictures, then freezes as it is; a picture still downloading at that moment (Juraj's 3.5 MB PNG from blossom.primal.net, on a phone) was frozen as its bare URL, and only showed when the post was opened. Now a picture that was merely slow is drawn in a fixed 4:3 box (fitted inside, so nothing moves when it lands) and keeps loading; only a picture that refused to load (an error, not a timeout — mediaFailed) is still a link. tools/slow-image-test.js (a picture served after 11 s; on the old code it was a link).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
|
| |
|
|
|
|
|
| |
***both***, ~~struck~~ and `code` render in a post's plain text — what newsletter bots and Amethyst users type and Amethyst shows; we showed the asterisks. Strict about edges, so snake_case, x__init__y, 2*3*4, "$5 * 2" and a lone star stay as typed, and markers never reach across a link, mention or hashtag. No headings or lists in posts: "#" at a line's start is a hashtag here, and articles render their own headings. tools/post-markdown-test.js
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
|
| |
|
|
|
|
|
| |
of people under the box — the ones you follow whose name matches first (at once, from what's here), then the header's people search. Picking one (tap, Tab or Enter) puts nostr:npub… in the text, shown everywhere as @name (NIP-27), and the post or reply goes out with their p tag so they're told; Escape closes. coinos-text's setSelectionRange walks into the <div>s Chrome wraps lines in after an Enter — it used to put any position past the first line at the end of the field. tools/mention-ac-test.js
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
|
| |
|
|
|
|
|
| |
note — someone reacted to a zap, and the reaction was followed — showed as "a kind 9735 event this app doesn't show" under the LNURL server's name. It now shows the zapper's face and name, "⚡ zapped 21 sats" and what they said with it. tools/zap-receipt-row-test.js
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
|
| | |
|
| |
|
|
|
|
|
| |
from Spanish · Show original" under the words, automatic by default for posts the language guess calls foreign (Settings → Nostr → Translation switches it off and leaves a Translate link instead), and ⋯ → Translate for any post. The browser's on-device Translator does it when the language pair is already installed (desktop Chrome); otherwise the registrar's new POST /translate (names/translate.js): a cached, 4-at-a-time front for a LibreTranslate container on cs (names/compose-libretranslate.yml, v1.9.6, 32 languages; none for sw/mr/ta/te, so those readers get no offer). Links, nostr: mentions, :emoji:, #tags and long codes are never sent to the model — only the prose between them, batched in one call (src/translate-split.js, shared). Translations are remembered (coinos-translations, 300). tools/translate-test.js
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
|
| |
|
|
|
|
|
| |
the white accent, which glared among the dark bubbles. Android 1.12 (code 13): the TWA no longer names a navigation-bar colour — the helper library always passed one (white when the manifest named none), and Chrome 154's Custom Tab window paints that bar white anyway while the buttons follow the colour asked for, so the colour extras are stripped from the launch intent (both schemes) and the bar is left to the browser and the system. Untested on a device yet; not published.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
|
| |
|
|
|
|
|
| |
they load — the number's place is held by an invisible stand-in and the real count fades in over it
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MgAFLnkF7KrdhuyCLMNgtL
|
| |
|
|
|
|
| |
and DMs, vertically centred on the bubble — tap zaps the default amount (the strike starts at the bolt), hold sets the amount, without opening the message sheet first. Own messages, visitors and wallets that can't zap get none; Marmot groups have no zap yet and are left alone
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
| |
button, in the same type
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
| |
(quick turns stack, a reverse turn brakes, trackpads left alone), and a mouse can grab the feed and flick it (a sideways drag still selects text, a drag doesn't click what it started on)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
| |
key, and a slightly larger font (13px)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
| |
started on) and show a thin scrollbar on mouse devices, so the feed chips that run past the card are reachable on desktop
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
| |
lists and packs, and posts from anyone on a hashtag you follow: your topic feeds' tags and your NIP-51 interests, kind 10015) and a Follows + lists feed beside Following, built from your feeds and offered once there's something to add. Long names in search results end in an ellipsis instead of running off the card
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
| |
with a bigger face and name, Settings last) beside a 600px page column, and from 1220px the wallet (balance, Receive/Send, latest payments) rides in a right column; search is a full field in the header there. Floaters re-centred on the page column, open chats still fill the window, phones unchanged
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
| |
feature that pays it instead of sitting as raw text in the recipient field; Paste and Scan stay one line tall as a long address grows the field
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
| |
button and size (no seek bar or sound button until playing); hovering the cover no longer paints it over with the app's button grey; upright phone clips (stored sideways with a quarter-turn matrix) paint upright, v1 size caches dropped; a clip the probe found HEVC switches to the copy before fetching the original
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
| |
clip this browser can't picture (an iPhone's HEVC on Chrome for Linux played sound over black) — vmedia container on cs (bun + ffmpeg, /vmedia/), switch on zero-width metadata or an HEVC header from the probe, a line saying why if the copy fails too
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
| |
(self-hosted, Unsplash) cycled with arrows or a swipe, or upload your own; untouched, each pubkey gets its own default, published as the kind-0 banner beside the punk
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
| |
text) instead of a bare 'view note' link
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01YRC6aKhwAud231ot9FPmhi
|
| |
|
|
|
| |
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01YRC6aKhwAud231ot9FPmhi
|
| |
|
|
|
| |
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01YRC6aKhwAud231ot9FPmhi
|
| |
|
|
|
|
|
| |
people/topics/relays as cards, own cursor + index; Popular keeps articles two others engaged with
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01YRC6aKhwAud231ot9FPmhi
|
| |
|
|
|
|
|
| |
with headings, list/pack cards that open as feeds
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01YRC6aKhwAud231ot9FPmhi
|
| |
|
|
|
| |
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011bvzKaZosXGHkigcGcSzAR
|