The emoji picker and the @-mention list were absolutely positioned inside
.mention-textarea-wrap, so every ancestor with a clipping box cut them off —
and TextEditor sits inside plenty: .dump-card-inner, .journal-card and
.rich-content-card clip with overflow: hidden, while .modal-body and the chat
scroller clip just as hard with overflow-y: auto. The picker also always
opened upward, so a textarea near the top of the viewport pushed it off-screen.
Both popups are now portaled to <body> and anchored to the textarea with
position: fixed, which no ancestor can clip. The new useAnchoredFloat hook
flips to whichever side has more room, clamps horizontally on narrow screens,
and publishes the space left as --float-max-h so the list shrinks and scrolls
instead of overflowing. It re-places on scroll, on resize, and through a
ResizeObserver, since autoResize grows the textarea as you type.
z-index goes to 1100 to clear the modal backdrop, staying under tooltips.
Drops two point-fixes that only existed to work around the clipping: the
hardcoded upward flip for the chat composer's mention list, whose leftover
margin would now double the gap, and the claim that the chat modal's body
needed overflow: visible for the popovers' sake.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The dump card is proportioned for the 860px feed; in a ~400px profile
column the title wrapped four lines deep, the meta row stacked, every
category chip took a line of its own and the vote button floated in the
middle of the resulting void. Re-proportion it for the column — smaller
thumb, title clamped to two lines, no teaser, meta and chips sharing the
line under the title — scoped to the width where the columns split.
Also stop the playlist card's inner from clipping its own hover: it
inherits overflow:hidden from the rule it shares with the dump card,
which needs it for the collapse animation. A playlist card never
collapses and its height is set by its 80px square thumb, so the clip
shaved the corners off the scaled thumb and sliced its lift shadow into
a hard-edged slab against the card border.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Posting a dump was a single form where the important choices were the easiest
to miss. It is now three panels: link or file, why & where, playlists.
Composition:
- No more URL/File toggle. An empty panel offers both ways in at once and the
kind follows what you actually did; a file dropped anywhere in the modal is
accepted, not just on the zone.
- Categories and visibility get their own panel instead of a disclosure that
read as optional, and the primary button stays "Next" until they've been
seen. Visibility carries a real label now.
- The draft (link, title, why, categories, visibility) is mirrored to
localStorage on every change and restored on reopen, so Escape or a stray
backdrop click costs nothing. Only an attached file can't be restored, so
that is the one case that asks before closing.
- URL dumps can carry a poster-supplied title instead of being stuck with
whatever the page scraped, editable right under the preview.
- Multipart uploads go through XHR so there is a real progress bar and a
percentage on the button, rather than 50 MB of silence.
Duplicates:
- New dumps.url_canonical column (+ index, backfilled by 0013) holding a lossy
key that ignores scheme, www., trailing slashes, tracking parameters and
YouTube share shapes. GET /api/dumps/by-url reads it, and the create form
warns "already dumped by X" while it fetches the preview. Never blocking.
Fixes:
- /api/preview now reports whether the page was actually reached: a failed
fetch still yields a hostname-only stub, so a dead link and a page without
metadata used to render identically.
- The Web Share Target never worked. The manifest posts to "/", but the index
redirect dropped the query string, so every Android share landed on the feed
with nothing pre-filled.
- File dumps no longer take the extension into their title.
- The link field no longer autofocuses on touch, where it raised a keyboard
over the modal.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TiAPtJZeCYYk8rKehtLUQU
The player now holds a queue instead of a single item, so albums play
through and single tracks share the same code path. Adds a "stream" item
kind for expiring, remotely-hosted sources: Bandcamp signs its mp3 URLs
for ~24h, so an item carries the page it came from and re-resolves itself
on error or on a stale restored session, falling back to the iframe embed
if that fails too.
Gated behind GERBEUR_BANDCAMP_PLAYER (default "embed"), injected into a
meta tag the same way as site-name/site-emoji.
Also: player header gains artwork and a subtitle, volume persists across
queue advances and reloads, and cross-origin streams get a plain seek bar
rather than a waveform that can never decode.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SbtjNsT5wvuABnfqhegZEJ
Thumbnails were failing in two different ways that both ended as an empty box.
Cloudflare hotlink protection answers a cross-site Referer with 403, so images
we had extracted correctly (dles.aukspot.com's og:image among them) never
rendered — every onError handler set display:none and swallowed it. The new
Thumbnail component loads with referrerPolicy="no-referrer", retries once
through /api/proxy-image for hosts that reject an empty referrer too, and only
then falls back to a placeholder.
Separately, the extraction cascade ended at the page's icon and then a guessed
/favicon.ico, so thumbnailUrl was almost never empty — just a 16x16 icon
cover-cropped into a 128x72 box. It now stops at real artwork, with faviconUrl
and accentColor (theme-color / msapplication-TileColor / mask-icon) as their own
fields. An absent thumbnailUrl finally means "no artwork", which is what makes
the placeholder possible: the site's own color mixed into the theme surface,
with its favicon centered on it, or its initial. Contrast holds for any
third-party color by construction rather than by luminance math, so nyt only
has to set --thumb-tint-strength to 0% to stay monochrome and geocities only
has to raise it. Missing accents fall back to a stable hostname-derived hue, so
rows saved before this get a tint with no backfill.
Migration 0011 reclassifies favicon-shaped thumbnailUrls on existing dumps.
The journal mosaic keeps its pull-quote and text fallbacks — the placeholder
appears there only to repair a broken image.
Also fixed: refresh silently overwrote good metadata with a failure stub, the
refresh button swallowed every error, refresh never broadcast the update,
extractBestIcon ranked SVG icons below 16x16 PNGs, and shared links with no
artwork carried no og:image at all.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>