Custom emoji images with non-1:1 aspect ratios were being stretched
into a square. Added object-contain to preserve natural aspect ratio
within the bounding box. Moved text sizing classes to parent containers
for reaction emoji bubbles so unicode emojis still size correctly.
Replace /#\w+/g with /#[\p{L}\p{N}_]+/gu across all hashtag regexes
so that hashtags like #Bíblia and #verdade parse correctly. Affects
NoteContent, BioContent, ComposeBox, and PhotoComposeModal.
- Add min-height placeholder to InlineImage to prevent layout shifts when
images load from 0px to natural height
- Remove 60s refetchInterval from useFeed that caused all pages to re-sort
in the background, triggering full feed re-renders
- Pre-filter deprecated/hidden events at Feed level (shouldHideFeedEvent)
instead of returning null inside NoteCard after hooks have already run
- Stabilize author info skeleton/loaded height with consistent min-h-[42px]
to prevent cascading layout recalculations
All shortcode regexes used [a-zA-Z0-9_] which excluded hyphens,
causing custom emojis like :GM-Chachi: to fail at every stage:
publishing (no emoji tag added to event), rendering (shortcode not
resolved to image), and detection (not recognized as custom emoji).
Updated the character class to [a-zA-Z0-9_-] across all 8 locations
in NoteContent, CustomEmoji, ComposeBox, and customEmoji utility.
Also added viewer's custom emoji collection as a fallback in
NoteContent so shortcodes render even when events from other clients
omit the emoji tag.
Closes#62
- Add AudioVisualizer component: mirrors VideoPlayer chrome exactly (same overlay controls, progress bar, hide-on-idle, scroll-pause) with a canvas sinewave and author avatar in the centre
- Wire audio into NoteCard (kind 1 notes), FileMetadataContent (kind 1063), and ComposeBox preview
- Extract shared utilities: formatTime, usePlayerControls hook, and mediaUrls (regexes + mimeFromExt) to eliminate duplication across VideoPlayer, AudioVisualizer, NoteCard, NoteContent, ComposeBox, and EmbeddedNote
- Fix ComposeBox: audio URLs now get correct imeta MIME tags and are excluded from link-embed detection
- Create LinkEmbed that dispatches to YouTubeEmbed, TweetEmbed, or LinkPreview based on URL
- Centralize extractYouTubeId and extractTweetId in LinkEmbed module
- Simplify NoteContent: merge link-preview and youtube-embed token types into link-embed
- Tweet URLs in posts now render as embedded tweets automatically
- Remove duplicate extractYouTubeId from NoteContent and ExternalContentHeader
When a nostr:nevent1 URI is decoded in NoteContent, the relay hints and
author pubkey embedded in the identifier were being discarded. This meant
EmbeddedNote would only query the user's configured relays and skip the
relay hints, causing quoted events hosted on other relays to silently fail
to load. Now the hints are stored in the nevent-embed token and forwarded
to EmbeddedNote → useEvent.
- Images now render at full width with natural aspect ratio (no cropping)
- Single-image posts skip the 'Read more' truncation
- Re-measure scrollHeight after images load to correctly detect overflow on multi-image posts
When disableEmbeds is true, image-embed tokens now return null instead of
rendering the raw URL as a link. This fixes the triple-dot (more menu)
preview showing bare blossom/image URLs instead of clean post text.
- Images now render inline via NoteContent at the position of their URL
rather than collected into a gallery block below the text
- Clicking an inline image opens a full lightbox with navigation across
all images in the post; lightbox index is tracked by position so
duplicate URLs open at the correct index
- Removed separate ImageGallery/extractImages from NoteCard,
PostDetailPage, and ComposeBox to prevent double-rendering
- Added disableEmbeds to NoteMoreMenu clamped preview to avoid block
embeds inside a line-clamp-3 container
- Fixed hasPreviewableContent in ComposeBox to detect image-only posts
so the Preview tab appears correctly
- Removed mp3/ogg/wav/pdf from MEDIA_URL_REGEX suppression list; those
URLs now render as clickable links instead of being silently dropped
- Added avif to IMAGE_URL_REGEX; added cursor-pointer to InlineImage
The cache-peek approach always returned npub because the cache was empty
at render time. Replace it with a proper hook that calls useNip05Verify,
relying on TanStack Query's deduplication to avoid redundant requests.
Also fix the dot separator in NoteCard to hide when NIP-05 is unverified,
and refactor ProfileSearchDropdown to pass the verified URL through
onClick rather than recomputing it in an event handler.
- Add overflow-hidden to NoteCard article elements in both normal and
threaded layouts to clip content that exceeds card width
- Add overflow-hidden to NoteContent root div to prevent link preview
cards and embedded content from pushing beyond container bounds
- Strip .xdc URLs in EmbeddedNote content preview (was missing from
the media URL regex, causing raw long URLs to render in quote embeds)
- Add overflow-hidden to EmbedContentPreview paragraph for defense
against long unbreakable strings
- Use disableEmbeds on NoteContent inside ReplyComposeModal's embedded
post preview so URLs render as plain links instead of full preview
cards that overflow the clamped container
- Add attachment indicator chips to EmbeddedNote (images, videos, apps,
links) so stripped media URLs still have a visible presence
URLs like https://following.party/naddr1... were having all surrounding
whitespace removed because naddr-embed tokens from URLs were treated as
block-level elements. Only bare naddr1 identifiers (without a URL) should
strip surrounding whitespace; URL-sourced ones are inline and need their
spacing preserved.
Mentions now trigger the Edit/Preview toggle. Compose preview uses
NoteContent directly for link previews and embeds instead of rendering
them separately, matching how real notes display. LinkPreview falls back
to a domain+favicon card when OEmbed data is unavailable. Added
disableEmbeds prop to NoteContent for future use.
- Create useWebxdc hook bridging the Webxdc API with Nostr kind 4079 state updates
- Create WebxdcEmbed component with launch-on-click UX for sandboxed iframe rendering
- Update ComposeBox to accept .xdc files and generate imeta tags with webxdc UUID
- Update NoteCard and PostDetailPage to detect and render webxdc attachments
- Skip .xdc URLs in NoteContent to avoid rendering them as raw links
Add emojify/EmojifiedText utilities that replace :shortcode: patterns with
inline images using emoji tags from kind 0 and kind 1 events. Applied to
display names across NoteCard, PostDetailPage, ProfilePage, ProfileHoverCard,
InteractionsModal, NotificationsPage, and NoteContent @mentions. Also
emojifies about/bio text in ProfilePage and ProfileHoverCard.
Remove string-level truncation from getDisplayName to avoid breaking
shortcodes mid-pattern (CSS truncate handles visual overflow). Use
em-relative sizing and align-text-bottom for proper inline alignment.
- Show link preview cards for URLs that end a line, not just standalone
URLs; only suppress previews for URLs followed by more text on the
same line
- Show skeleton placeholders on notifications page until the query has
completed at least once instead of flashing 'No notifications yet'
- Add skeleton loading for author rows in notification cards while
profile data loads
- Replace inline image <a> tags with ImageGallery component so clicking
images opens the lightbox with navigation
- Match notification tab height/weight to feed tabs on mobile while
preserving original desktop styling
- Add true optimistic updates for reactions and reposts via setQueryData
instead of only invalidating queries after publish
- Track user's own reaction per event and display their emoji in place of
the Heart icon across feed, post details, and notifications
- Strip whitespace around block embeds and skipped media URLs to eliminate
blank lines caused by whitespace-pre-wrap
- Only render link preview cards for standalone URLs; inline URLs in
sentences render as plain clickable links
- Remove border-b separators and use fixed-height object-cover on preview
images to eliminate gaps
- Tighten padding on EmbeddedNote and EmbeddedNaddr cards
- Use fixed-height object-cover for ImageGallery grid images
- Unify RepostMenu to use Popover on all screen sizes instead of Drawer
on mobile
- Domain feed page at /timeline/:domain fetches .well-known/nostr.json and queries events from domain users
- NIP-05 domains are clickable in usernames throughout the app (Nip05Badge, NoteCard, PostDetailPage, etc.)
- @_@domain.com renders as @domain.com (underscore prefix hidden)
- Profile hover cards on avatars, usernames, and mentions across all components
- Clicking avatar/banner on profile page opens image in gallery lightbox with safe-area support
- NIP-05 verified users get clean profile URLs (e.g. /user@domain.com) instead of /npub1...
- NIP19Page unified dispatcher handles NIP-19, NIP-05, and profile routing at /:param
- Removed /u/:npub and /d/:domain routes in favor of root-level routing
- CORS proxy fallback for NIP-05 resolution on non-CORS-compliant servers
- Mobile top bar logo links to home
- Safe area fixes for gallery lightbox, mobile drawer, and sheet close button
- Show multiple reply authors like "Replying to @chad and @alex"
- Display up to 2 authors, with "and X others" for more
- Enlarge emojis when they're the only content (text-5xl)
- Detect emoji-only content (max 12 emoji chars)
- Update ReplyContext to accept array of pubkeys
- Extract all non-mention p tags for reply context
- Matches Ditto's reply author display behavior
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Strip trailing punctuation from URLs (.,;:!?)]) that are likely not part of the URL
- Fixes issue where (https://example.com) was parsed incorrectly
- Preserve newlines and formatting after embedded content
- Only collapse excessive whitespace (3+ newlines) instead of removing all whitespace
- Maintains proper text flow after link previews and embedded events
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Preserve one newline after naddr-embed tokens when present
- Prevents text from flowing directly after URL without spacing
- Embedded event now properly displays with preserved formatting
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Removed lookahead that was preventing matches in certain contexts
- NIP-19 identifiers now render as mentions regardless of surrounding characters
- Maintains single @ display by consuming optional @ prefix
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Updated regex to optionally match @ before NIP-19 identifiers
- Removed word boundary requirement to allow mentions in quotes
- Mentions now render correctly with single @ symbol in all contexts
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
Add e.stopPropagation() to hashtag, nostr-link, and mention Links
inside NoteContent so clicks don't bubble up to the parent card's
onClick handler that navigates to the post detail page.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
The content parser regex only matched nostr:-prefixed identifiers like
`nostr:naddr1...`. Bare identifiers like `naddr1...` were rendered as
plain text. Added a \b word-boundary alternation to also match bare
npub1, note1, nprofile1, nevent1, and naddr1 identifiers, so they
render as proper embeds/mentions/links.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Fix NoteContent fallback that incorrectly rendered raw media URLs as
plain text when the entire content was a media URL. The parser would
skip media URLs (handled by parent as embeds), but when all tokens
were skipped, the empty-result fallback re-added the full content
as text. Now tracks whether any regex matches occurred to distinguish
"no matches at all" from "all matches were skipped media URLs".
- Restructure PostDetailSkeleton to match the actual PostDetailContent
layout: proper article/replies separation (fixes double padding on
reply skeletons), image placeholder, rounded action button circles,
and -mx-4 action bar matching the real component.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
The whitespace collapsing logic was stripping newlines before naddr-embed
tokens that have a URL, causing text and the URL to smash together.
Now only pure block embeds (no URL) trim preceding whitespace.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- NoteContent now shows the original URL as a clickable link above the
embedded naddr card when the naddr was extracted from a regular URL
- Replaced "Addressable event view coming soon" placeholder in NIP19Page
with a full AddrDetailPage that shows hero image, author, title,
description, image gallery, metadata fields, and tags
- AddrDetailPage extracts metadata from both event tags and JSON content
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Added useAddrEvent hook for fetching addressable events by kind+pubkey+d-tag
- Created EmbeddedNaddr component that displays title, description, image, and author
from addressable events (extracting metadata from tags and JSON content)
- Updated NoteContent to detect naddr1 identifiers in regular URLs (e.g.
bitpopart.com/art/naddr1...) and render them as Nostr embeds
- Added naddr1 to the nostr: URI regex so nostr:naddr1... is also handled
- Added naddr-embed token type with proper whitespace collapsing
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Create EmbeddedNote component with avatar, author name, timestamp,
truncated content preview, and optional image thumbnail
- Update NoteContent tokenizer to detect note1 and nevent1 references
and render them as embedded cards instead of plain text links
- Collapse whitespace around embedded note cards like link previews
- Clicking an embedded card navigates to the full post detail page
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
Spacing:
- Reduce link preview margins from my-2 to mt-1.5 mb-0.5
- Collapse whitespace on text tokens adjacent to block-level tokens
(link-preview, youtube-embed) so newlines from the original content
don't stack with the card's own spacing
- Filter out empty text tokens after trimming
Bot headers:
- Send proper User-Agent header ("Mew/1.0 Link Preview Bot") when
fetching OG data so dynamic sites (SPAs, Twitter, etc.) return
server-rendered HTML with OG meta tags instead of a JS shell
- Accept application/xhtml+xml in addition to text/html
YouTube embeds:
- Detect YouTube URLs (youtube.com/watch, youtu.be, /embed/, /shorts/)
and extract video ID
- New YouTubeEmbed component renders a privacy-enhanced iframe player
(youtube-nocookie.com) with proper 16:9 aspect ratio
- YouTube links render as playable inline embeds instead of OG cards
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
Move LinkPreview rendering from a fixed position below the post into
NoteContent itself, so previews appear exactly where the URL was in
the original text.
- Rewrite NoteContent to use a token-based architecture: useMemo
produces typed tokens (text, link-preview, mention, nostr-link,
hashtag), then the render phase maps tokens to components
- Non-media URLs produce a `link-preview` token that renders an
inline <LinkPreview> card at that position in the content flow
- Remove separate LinkPreview blocks from NoteCard and PostDetailPage
since previews are now handled by NoteContent
- Remove unused extractPreviewUrl helper from useLinkPreview hook
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>