The debug logs confirmed the stream IS working correctly:
- req() subscription opens successfully
- EOSE is received and subscription stays open
- New events arrive after EOSE
- Cleanup only happens when the preview environment reloads (replaceDocument)
Removed eoseTimeout from NPool in previous commit which was the
main fix for subscription lifecycle issues.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Pass `relays` array directly to nostr.req() and nostr.query()
to bypass pool's reqRouter/eoseTimeout that was killing streams
- This is the pattern clawchat uses successfully for live subscriptions
- Default stream uses multiple relays, NIP-50 search uses relay.ditto.pub only
- Filter out future-dated events (created_at > now)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Always use relay.ditto.pub directly for req() streaming
(NPool's eoseTimeout: 200 was killing subscriptions prematurely)
- Filter out events with created_at in the future
- Matches clawchat pattern: fetchInitial + streamNew in parallel
- Both query() and req() go to same single relay
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Match clawchat's proven streaming pattern
- fetchInitial() and streamNew() run in parallel, not sequentially
- req() uses limit: 100 (not limit: 0 which was wrong)
- Added isSubscribed guard for safe state updates
- Proper AbortError handling on both fetch and stream paths
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Rewrote useStreamPosts to use Nostrify's correct req() API which
returns AsyncIterable, not a subscription object with .close()
- Uses AbortController to cancel streams on cleanup (fixes sub.close error)
- Streams posts by default with no search query (global kind:1 feed)
- When search query is provided, uses NIP-50 search on relay.ditto.pub
- Properly handles EOSE, CLOSED, and EVENT messages from the stream
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Tabs (Posts/Trends/Accounts) at the very top, sticky
- Search input below tabs inside content area (icon on right)
- Search filters open by default with circled chevron toggle
- "Including replies" label + switch inline on same row
- Radio buttons laid out horizontally with flex-wrap
- "In the language:" label inline with dropdown
- Filter labels are bold (matching Ditto's white/bold style)
- Removed back arrow from header (Ditto doesn't have one)
- Accounts tab gets its own search input
- Posts stream live below filters
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Removed broken sidebar-inside-main-column layout that squished content
- Filters now collapse/expand above the post results (matching Ditto's pattern)
- Smooth animated expand/collapse with chevron indicator
- Simplified empty states (plain text, no cards)
- Cleaned up unused useSearchPosts.ts (replaced by useStreamPosts)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Added Posts, Trends, and Accounts tabs
- Implemented sidebar search filters for Posts tab (replies, media type, language)
- Created useStreamPosts hook for real-time streaming subscriptions
- Search uses relay.ditto.pub for NIP-50 search functionality
- Posts stream in real-time with REQ subscription instead of static queries
- Layout matches Ditto design with filters in left sidebar on desktop
- All tabs functional with proper empty states
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Changed min-h from 84px to 75px to match actual ComposeBox height
- Both headers now have identical 75px height
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Changed profile header padding from py-2 to py-3
- Now both headers have consistent 12px vertical padding
- Provides better visual alignment across pages
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Show NIP-05 username if available
- Otherwise show npub with monospace font
- Removed the previous behavior of showing both username and NIP-05 separated by a dot
- Made npub slightly smaller (11px) with monospace font for better readability
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Updated CTA text to "Follow accounts you care about on Mew"
- Reinforces brand identity in the call-to-action
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Reduced padding from py-6 to py-3 for more compact design
- Horizontal layout instead of vertical stacking
- Better CTA copy: "Follow accounts you care about"
- Smaller button size (sm) and simpler "Join" text
- Keeps gradient background but with better proportions
- More balanced and less overwhelming
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Center-aligned design for better visual balance
- Gradient background (primary/5 to primary/10) for subtle depth
- Larger, more prominent "Join Mew" button with shadow effects
- Engaging copy: "Join to unlock your personalized feed ✨"
- Increased padding and spacing for breathing room
- More visually appealing and less boring design
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Change to "Join Mew to follow accounts & personalize your feed"
- Add emphasis with bold "Join Mew" in foreground color
- Use medium font weight for better visual hierarchy
- Make Join button smaller (sm size) for better balance
- More engaging and visually distinct CTA
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Changed "Join Mew to personalize your feed and connect with others" to "Join to personalize your feed"
- More concise and direct call-to-action
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Show Follow/Global tabs only when user is logged in
- Display CTA banner with Join button when logged out
- CTA message: "Join Mew to personalize your feed and connect with others"
- Join button triggers login dialog in Feed component
- Added LoginDialog and SignupDialog to Feed component
- Improves UX with clear call-to-action instead of empty tab bar
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Import useCurrentUser hook to check login status
- Conditionally render tabs only when user is logged in
- Change default tab to 'global' for logged-out users
- Improves UX by showing only relevant UI elements based on auth state
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Added onSignupClick prop to LoginDialog interface
- Added "New here? Create account" link in LoginDialog
- Link appears after extension login button (if available) or with other options
- Matches borkstr's login dialog pattern for better UX
- Users can easily switch between login and signup flows
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Changed Join button onClick handler to open LoginDialog instead of SignupDialog
- Follows the same pattern as borkstr project where Join opens the login modal
- Users can still access signup through the login modal's "More Options" section
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Moved early return to after all hooks to comply with Rules of Hooks
- Changed handleLogout to async/await pattern for better error handling
- Prevents "Rendered more hooks than during the previous render" error
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Simplified code to inline filters in className
- Light theme: dark grey filter
- Pink theme: dark pink filter with medium brightness
- Dark/Black themes: no filter (original logo)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Light theme: neutral dark grey filter (no color tint)
- Pink theme: vibrant dark pink filter with high saturation
- Improved contrast and readability on light backgrounds
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Apply dark blue-purple filter for light theme to make logo readable
- Apply dark pink filter for pink theme to match theme and improve visibility
- Replace opacity with color filters for better contrast
- Logo remains unfiltered in dark and black themes
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Show current theme name and icon in dropdown trigger button
- Fix light theme not working (update class removal from 'mew' to 'dark')
- Remove opacity from logo in pink theme (keep full visibility)
- Logo now only uses opacity in light theme (70%)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Use opacity (60% pink, 70% light) for MewLogo instead of color filters
- Change Mew theme icon to Cat, Black theme icon to Moon
- Convert theme selector to proper dropdown menu with chevron indicator
- Add DropdownMenuLabel and separator for better UX
- Maintain theme icons: Cat (Mew), Sun (Light), Moon (Black), Heart (Pink)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Rename "mew" theme to "dark" (Mew=dark, Light=light remain)
- Make pink theme more vibrant with increased saturation and adjusted hues
- Convert theme selector to dropdown menu with icons (Moon, Sun, Zap, Heart)
- Add CSS filters to MewLogo for pink and light themes with smooth transitions
- Keep Black and Pink themes as requested
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
Add showEmojis prop to ReactionButton (default false). The emoji
indicators next to the count now only render when showEmojis is
true, which is only passed from PostDetailPage.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
The optimistic +1 delta was stacking on top of the refetched stats
(which already included the new reaction), causing the count to
jump by 2. Now we reset the delta to 0 on success so only the
real server count is displayed.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Create ReactionButton component with Popover-based emoji picker
- Clicking the reaction button opens the full emoji picker (EmojiPicker)
- Selecting an emoji publishes a NIP-25 kind 7 reaction event with proper e/p/k tags
- Optimistically increments the reaction count and shows the user's chosen emoji
- Reverts optimistic update on publish failure
- Invalidates event-stats and event-interactions queries on success
- Replace static heart/like buttons in NoteCard and PostDetailPage with ReactionButton
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
Parse nostr:npub1… and nostr:nprofile1… references within embedded note
card text and render them as clickable @username mentions (resolving the
display name via useAuthor), matching the style used in NoteContent.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
Embedded note cards now strip out nostr:nevent1... and nostr:note1...
references from the content preview text, preventing recursive embedding
and keeping the preview clean.
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>
- Add useLinkPreview hook that fetches Open Graph metadata (title,
description, image, favicon, site name) via CORS proxy with 1hr
stale time caching
- Add extractPreviewUrl helper to find the first non-media URL in
note content
- Add LinkPreview component with a polished card UI showing OG image,
title, description, domain favicon, and external link indicator
- Strip all URLs (both media and non-media) from NoteContent inline
text — media URLs are rendered as image embeds, non-media URLs are
rendered as LinkPreview cards
- Integrate LinkPreview into NoteCard and PostDetailPage between
image attachments and action buttons
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
Image URLs (jpg, jpeg, png, gif, webp, svg) are already rendered as
embedded image previews by NoteCard and PostDetailPage. This change
removes them from the inline text content to avoid duplication.
Also trims leading/trailing whitespace left behind after stripping
image URLs from the edges of content.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Cap display name to max 40% width so it doesn't get crushed by long NIP-05 addresses
- Make NIP-05 span properly truncate with min-w-0 instead of pushing the layout
- Timestamp and separator remain fixed (shrink-0) so they're always visible
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
Unreliable metric since it only reflects the limited number of posts fetched, not the user's actual total.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Removed the Calendar icon and "Joined Nostr" text from profile info
- Added truncate to NIP-05 address to prevent overflow
- Added min-w-0 and truncate to website URL to prevent container breakout
- Made link icon shrink-0 to prevent it from being squished by long URLs
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Scrollbars are hidden by default (transparent thumb and track)
- Thin 6px scrollbar appears with 25% opacity on container hover
- Thumb brightens to 40% on direct hover for feedback
- Works across both webkit and Firefox (scrollbar-width/scrollbar-color)
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Use flex items-center on the input wrapper instead of absolute
top-1/2 -translate-y-1/2 hacks for proper icon centering
- Remove focus-visible ring and ring-offset on search input
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>