Commit Graph

4447 Commits

Author SHA1 Message Date
shakespeare.diy dd02f2ff27 Render link previews inline where URLs appear in post content
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>
2026-02-16 23:00:58 -06:00
shakespeare.diy 00d5024218 Replace non-media link URLs with rich preview cards
- 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>
2026-02-16 22:58:19 -06:00
shakespeare.diy ce90ded614 Strip embedded image URLs from NoteContent text display
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>
2026-02-16 22:55:11 -06:00
shakespeare.diy 8b46ce9692 Fix NIP-05 and display name truncation in NoteCard header
- 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>
2026-02-16 22:53:36 -06:00
shakespeare.diy 627b19a810 Remove post count from profile header
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>
2026-02-16 22:52:30 -06:00
shakespeare.diy 168cc28b39 Remove "Joined Nostr" calendar item from profile page, fix long strings breaking container
- 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>
2026-02-16 22:51:51 -06:00
shakespeare.diy 5270a5b7e8 Add subtle scrollbars that only appear on hover
- 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>
2026-02-16 22:49:19 -06:00
shakespeare.diy 38bfb91624 Fix search icon vertical alignment and remove focus ring
- 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>
2026-02-16 22:48:44 -06:00
shakespeare.diy b88b75a90b Fix search spinner animation and remove debounce delay
- Replace Loader2 icon with inline SVG spinner using explicit @keyframes
  spin to avoid tailwindcss-animate conflicts
- Remove 250ms debounce so search fires immediately on input
- Add @keyframes spin to index.css for reliable rotation animation

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 22:47:30 -06:00
shakespeare.diy 5264a7004c Add profile search autocomplete using relay.ditto.pub NIP-50 search
- Create useSearchProfiles hook that queries wss://relay.ditto.pub
  with NIP-50 search filter for kind 0 (profile) events
- Create ProfileSearchDropdown component with debounced input,
  keyboard navigation, and animated dropdown of matching profiles
- Update SearchPage to use the new profile autocomplete
- Update LeftSidebar search bar to use the profile autocomplete
- Selecting a profile navigates to their npub profile page

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 22:46:02 -06:00
shakespeare.diy 5863ef17f1 Increase right sidebar horizontal padding to px-6 for visual alignment with left sidebar
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 22:42:28 -06:00
shakespeare.diy fdb65ff430 Increase right sidebar top padding to visually align with left sidebar content
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 22:41:37 -06:00
shakespeare.diy 66fb2ad9d1 Add py-2.5 to left sidebar search bar wrapper div
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 22:39:22 -06:00
shakespeare.diy 7dafc69845 Update left sidebar search bar to use py-2.5 padding instead of fixed h-10 height
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 22:38:27 -06:00
shakespeare.diy e57c7c6a14 Pin "Vibed with Shakespeare" to bottom-right of sidebar
- Removed the border-top from the footer
- Changed aside from lg:block to lg:flex flex-col for flex layout
- Added mt-auto to push footer to the bottom, mirroring the left
  sidebar's profile position
- Right-aligned the text with text-right

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 22:34:40 -06:00
shakespeare.diy 86c9627cd0 Limit latest accounts to 5 with client-side slice
The query already had limit: 5 at the relay level, but relays may
return more results than requested. Added .slice(0, 5) after sorting
to guarantee only the 5 most recent accounts are shown.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 22:33:25 -06:00
shakespeare.diy eeb7c89a56 Fix profile pictures clipping into Follows/Global sticky tabs
Added z-10 to the Feed tab bar so note content scrolls behind
it instead of overlapping on top.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 22:30:24 -06:00
shakespeare.diy 6e24acc266 Set custom logo as favicon and in-app logo
- Added logo.png to public folder
- Added favicon and apple-touch-icon link tags to index.html
- Updated manifest.webmanifest with icon references
- Updated MewLogo component to render the PNG image instead of SVG

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 22:27:13 -06:00
shakespeare.diy 9a375f3d3d Unify new post and reply into a single ComposeModal
ReplyComposeModal now handles both cases: when an event is passed it's a
reply (shows embedded post preview), when no event is passed it's a new
post. FloatingComposeButton reuses ReplyComposeModal instead of having
its own duplicate dialog.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 22:24:30 -06:00
shakespeare.diy 42446fe0c7 Move X close button to the right side on all compose/reply/interaction modals
Updated ReplyComposeModal, FloatingComposeButton, and InteractionsModal
to place the title on the left and the dismiss X button on the right,
matching the standard dialog layout pattern.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 22:22:50 -06:00
shakespeare.diy c6317c3893 Add interactions modal for reposts, reactions, and zaps on post details
- Created useEventInteractions hook that fetches kind 6 (reposts),
  kind 7 (reactions), and kind 9735 (zap receipts) for a given event ID
- Created InteractionsModal component with three tabs:
  - Reposts: shows who reposted with avatar, name, and timestamp
  - Reactions: groups reactions by emoji, showing each user under the
    emoji header with counts
  - Zaps: shows who zapped with amount badges (sorted by largest first),
    zap messages, and a total sats summary at top
- Stats row text (e.g. "2 Reposts", "1 👍", "500 sats") is now clickable
  and opens the modal to the relevant tab
- Action buttons (Repost, Reactions, Zaps) in the post detail view also
  open the modal to their respective tabs
- All user rows link to the user's profile page

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 22:14:40 -06:00
shakespeare.diy 7cdbf4a6e6 Add reply compose modal with embedded original post preview
- Created ReplyComposeModal component that shows the original post embedded
  in a compact card above the compose area, matching the reference design
- Updated ComposeBox to accept replyTo, forceExpanded, and hideAvatar props
- ComposeBox now generates proper NIP-10 reply tags (root/reply markers,
  p tags for thread participants)
- Wired reply buttons in NoteCard and PostDetailPage to open the modal
- Reply success invalidates both feed and replies query caches

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 21:35:26 -06:00
shakespeare.diy 23502ccb5a Remove border between Post Details header and the post
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 21:31:07 -06:00
shakespeare.diy cd194aa639 Fix emoji reactions display and remove stats border
- Map "+" and empty reactions to 👍 emoji so they appear in the stats row
- Remove border-top from stats row and date-only row to match Ditto

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 21:30:31 -06:00
shakespeare.diy 29a143337b Match PostDetailPage to Ditto's exact layout
- Remove reply composer (Ditto shows replies directly below action bar)
- Change action buttons from justify-around to justify-between (matching Ditto)
- Show reaction emojis in stats row (e.g. "32 👍😂👆") instead of "32 Likes"
  when emoji reactions are available, matching Ditto's display
- Extract unique reaction emojis from kind:7 events in useEventStats hook
- Clean up unused imports (Button, Loader2, useRef, useCurrentUser, etc.)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 21:29:14 -06:00
shakespeare.diy 6876f8e626 Refine PostDetailPage to match Ditto's layout precisely
- Use justify-around for action buttons (full-width distribution like Ditto)
- Match icon sizes to 18px (same as feed NoteCards)
- Add border-top separator above stats row
- Show date-only row when no stats exist
- Conditionally hide stats row when no engagement data
- Make reply composer expandable (collapsed = single line, no button)
- Smaller avatar (size-9) in reply composer matching Ditto
- Adjust spacing and padding to match Ditto reference screenshots
- Match header font size to xl for "Post Details" title

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 21:26:42 -06:00
shakespeare.diy 3fa066bd46 Add post detail page with replies and reply composer
- Create PostDetailPage with expanded post view showing author info,
  full content, images, stats (reposts, likes, zap amount), full date,
  action buttons, and inline reply composer
- Create useEvent hook for fetching single events by hex ID
- Create useReplies hook for fetching kind:1 replies to an event
- Update NIP19Page to render PostDetailPage for note1/nevent1 identifiers
- Make NoteCard clickable to navigate to post detail via nevent1 URL
- Fix NoteMoreMenu to use React Router navigate instead of window.location.href

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 21:24:01 -06:00
shakespeare.diy 83f20fe56a Remove z-index from sticky tabs so emoji picker popover isn't clipped
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 21:18:03 -06:00
shakespeare.diy 4eeba97a4e Show inline compose box on mobile at top of feed
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 21:15:23 -06:00
shakespeare.diy faf382cdcc Fix zap amount not showing on notes
- Fixed early return using old `zaps` key instead of `zapAmount`
- Added robust amount extraction with 3 fallback strategies:
  1. Top-level `amount` tag on the zap receipt
  2. `amount` tag inside the embedded zap request (description tag)
  3. Parsing the bolt11 invoice string
- This ensures zap amounts display even when relays omit the amount tag

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 17:54:53 -06:00
shakespeare.diy f181f58425 Show total zap amount (sats) instead of zap count on notes
- useEventStats now sums the `amount` tag (millisatoshis) from kind 9735
  zap receipts and returns `zapAmount` in sats instead of a zap count
- NoteCard displays the total with compact formatting (e.g. 1.2K, 3.5M)

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 17:53:22 -06:00
shakespeare.diy 03e563d3a5 Remove bookmark button from NoteCard action bar
Bookmark functionality is still accessible via the More menu dialog.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 17:52:07 -06:00
shakespeare.diy 858ec3beaa Add post preview to More menu, blur modal backdrops, polish compose modal
- NoteMoreMenu now shows a compact post preview at the top (avatar,
  author name, timestamp, and 3-line-clamped content) before menu items
- Dialog overlay updated globally: bg-black/60 + backdrop-blur-sm for a
  frosted-glass effect behind all modals
- FloatingComposeButton modal reworked to match the same presentation
  pattern: rounded-2xl, hidden default close button replaced with a
  centered header (X / "New post" title), separator, compose area

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 17:50:46 -06:00
shakespeare.diy 6e92ff3468 Add More menu dialog to NoteCard (Ditto-style)
- Created NoteMoreMenu component as a centered dialog with grouped actions:
  - Show Post Details, Copy Link to Post, View on njump.me, Bookmark
  - Mute Conversation, Mention @user
  - Mute @user, Report @user (destructive)
  - Close button
- Added the ⋯ More button back to NoteCard action bar alongside Bookmark
- Menu items show the author's display name dynamically
- Copy Link copies nevent-based URL to clipboard with toast feedback
- Bookmark option in the menu toggles bookmark state via useBookmarks hook

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 17:48:48 -06:00
shakespeare.diy 589a60ec89 Replace "More" with Bookmarks page
- Created useBookmarks hook for NIP-51 kind 10003 bookmark list management
  (query, add, remove bookmarks)
- Created BookmarksPage with login prompt, loading skeletons, and empty state
- Replaced "More" nav item with "Bookmarks" in LeftSidebar
- Added bookmark toggle button to NoteCard action bar (replaces three-dot menu)
- Removed /more route from AppRouter
- Bookmarks are stored per NIP-51 as replaceable kind 10003 events

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 17:46:37 -06:00
shakespeare.diy 9b92141b95 Hide inline compose on mobile, polish FAB compose dialog
- Inline ComposeBox hidden below sidebar breakpoint (mobile uses FAB)
- "New post" dialog: rounded-2xl corners, clean header with title and
  border divider, no padding gap, overflow hidden for crisp edges
- Removed redundant DialogHeader wrapper, title sits in a flex bar

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 17:33:23 -06:00
shakespeare.diy 0f922c79f4 Fix emoji picker z-index (tabs to z-[1]) and center avatar in mobile top bar
- Tabs z-index lowered to z-[1] so the emoji popover portal (z-50)
  always renders above them
- Mobile top bar avatar container uses flex centering instead of
  fixed width, ensuring the profile picture is visually centered
- Logo wrapped in flex-1 centered container for proper horizontal balance

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 17:31:54 -06:00
shakespeare.diy 5a8a9376ad Shrink mobile top bar to h-10 (40px) and fix tab sticky offset
- Header: h-10 fixed height, smaller avatar (size-7), smaller logo (22px),
  tighter padding (px-3) — eliminates the gap/clipping between header
  and sticky tabs
- Tabs sticky offset updated to top-10 (40px) to sit flush under header

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 17:30:01 -06:00
shakespeare.diy f4c8e42343 Fix emoji picker z-index and make Follows/Global tabs sticky under header
- Lowered tabs z-index to z-[5] so the emoji popover (z-50 portal)
  renders above them
- Tabs now stick at top-[48px] on mobile (below the 48px top bar)
  and top-0 on desktop (sidebar: breakpoint), so they remain visible
  when scrolling through the feed

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 17:28:49 -06:00
shakespeare.diy d53ebfe197 Add real emoji picker with categorized grid, search, and cursor insertion
- EmojiPicker component with 9 categories: Smileys, Gestures, People,
  Nature, Food, Activities, Objects, Symbols, Flags
- Category tab bar with emoji icons for quick switching
- Search input filters by category name
- Scrollable grid of emoji buttons (8 columns)
- Click an emoji to insert it at the cursor position in the textarea
- Popover opens from the smiley face button in the compose toolbar
- Button highlights when the picker is open
- No external emoji dependencies — all native Unicode emoji

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 17:27:13 -06:00
shakespeare.diy 926874c0eb Expand compose box on focus with toolbar, char counter, and NIP-36 content warning
- Compact single-line compose expands to multi-line on focus/click
- Toolbar shows: paperclip (file upload via Blossom), emoji, content
  warning triangle (NIP-36 sensitive content toggle)
- Content warning toggle adds `content-warning`, `L`, and `l` tags
  per NIP-36 spec, with optional reason input field
- Circular progress ring shows character usage against 5000 char limit
- Ring turns amber at 90%, red when over limit
- Remaining character count displayed next to ring
- "Post!" button disabled when over limit or empty
- File uploads append URL to content via useUploadFile hook

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 17:24:22 -06:00
shakespeare.diy f4cdf89e60 Show right border on feed only when right sidebar is visible (lg+)
Left border appears at the sidebar breakpoint (900px), right border
only at lg (1024px) when the right sidebar is present. In the middle
view (sidebar visible, right panel hidden), the feed extends to the
edge without a floating right border — matching Ditto's behavior.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 17:22:05 -06:00
shakespeare.diy e53210ce4d Raise sidebar breakpoint to 900px — no more icon-only strip
The sidebar now either shows fully (280px with labels, search, user
info) or is completely replaced by the mobile UI. The icon-only 72px
strip is eliminated — below 900px the mobile top bar, bottom nav,
drawer, and FAB take over entirely.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 17:19:16 -06:00
shakespeare.diy 7740c63328 Use custom sidebar breakpoint (688px) for mobile/desktop toggle
The mobile layout now activates the moment the left sidebar would need
to shrink — at 688px (72px sidebar + 600px feed + padding). Added a
custom `sidebar` Tailwind screen breakpoint and replaced all `md:`
references that control the sidebar/mobile toggle with `sidebar:`.

This ensures the bottom nav, top bar, drawer, FAB, and full-width feed
kick in precisely when the sidebar can no longer fit alongside the feed.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 17:17:37 -06:00
shakespeare.diy be5cb2180f Add responsive mobile layout with bottom nav, drawer menu, and FAB compose
Mobile UI (< md breakpoint):
- MobileTopBar: user avatar (opens drawer) + centered Mew logo
- MobileDrawer: left slide-out sheet with user profile, follower stats,
  menu items (Profile, Wallet, Bookmarks, Mutes, Preferences, Domain blocks,
  Logout), and expandable Switch accounts section
- MobileBottomNav: fixed bottom bar with Home, Notifications, Search, Vines
- FloatingComposeButton: purple FAB in bottom-right corner opens compose dialog
- Feed goes full-width on mobile, max-w-[600px] only on md+
- Left sidebar hidden on mobile, right sidebar hidden below lg
- Pages with their own headers use hideMobileTopBar to avoid double headers
- Safe area bottom padding for iOS devices
- Added routes for /bookmarks, /mutes, /domain-blocks placeholders

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 17:13:25 -06:00
shakespeare.diy 4f1b4800d0 Build Mew: a full-featured kind 1 Nostr client
- Dark theme with purple accent colors matching the reference design
- Three-column layout: left sidebar, main feed, right sidebar
- Left sidebar: Mew logo, search, navigation (Home, Notifications, Search, Vines, Profile, Wallet, Settings, More), compose button, user profile/login
- Main feed: compose box, Follows/Global tabs, live note cards with avatars, author info, NIP-05 verification, relative timestamps, image attachments, and action buttons (reply, repost, like, zap, more)
- Right sidebar: trending hashtags with sparklines, latest accounts, footer
- Full routing: home, profile, notifications, search, hashtags, settings (profile/relays), placeholder pages
- NIP-19 routing with automatic profile redirect
- Custom hooks: useFeed, useTrendingTags, useLatestAccounts, useEventStats
- NoteContent renders mentions, links, and hashtags as clickable elements
- Inter Variable font, skeleton loading states, responsive layout

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 17:05:32 -06:00
shakespeare.diy 98ff1e09e0 New project created with Shakespeare
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-02-16 16:54:01 -06:00