Moves the arc from the generic Sheet component into MobileDrawer
so it can inherit the theme background image. Uses clip-path
ellipse on an HTML div instead of SVG fill, with the same
bg-background/70 overlay applied when a background image is set.
Increases the arc width from 20px to 36px with a deeper curve,
and changes fill from background/80 to solid background to
match the drawer's actual background color.
Removes the flat right border from the left sheet variant and
adds an SVG arc that curves outward along the right edge,
matching the curved styling used on the horizontal nav bars.
Replaces the flat right border with an SVG arc that curves
outward along the full height of the drawer, matching the
curved design language used on the nav bars.
CSS mask-image with SVG data URLs doesn't work reliably for complex SVGs
with transforms in some browsers. The picker preview works because it renders
inline SVG directly, but the avatar mask-image was failing for shapes like
droppi, flammi, leafy, mushie, owli, rocky, and rosey.
Changes:
- Rewrite getBlobbiMaskUrl() to render SVG to canvas and export PNG
- Use Blob URL for SVG loading (more reliable than data URL)
- Add async mask generation with proper caching and deduplication
- Update Avatar component to load masks asynchronously via useEffect
- Add getAvatarMaskUrlAsync() for async mask URL retrieval
The picker preview continues to use inline SVG (which works fine),
while the avatar mask now uses rasterized PNG (which works everywhere).
1. Remove stroke="white" from catti shape - let the styling apply colors
2. Fix Avatar component to properly update mask when shape changes:
- Compute maskUrl outside useMemo so it's always fresh
- Use maskUrl directly in dependencies instead of shape string
- This ensures the mask updates immediately when selecting a new shape
- avatar.tsx: consolidate to single isEmojiShape boolean, fix AvatarFallback using same logic as Avatar
- avatarShape.ts: accept NostrMetadata directly in getAvatarShape (no cast needed)
- Remove 'as Record<string, unknown>' casts from all ~50 call sites
- Replace 'circle' magic string with empty string in form defaults and parseShape
- Use isValidAvatarShape instead of string comparison in save logic
- ProfileCard: extract IIFE overlay style into useMemo, use isEmojiShape throughout
Extend the kind-0 'shape' field to accept any emoji string in addition to
the predefined geometric shapes (circle, triangle, hexagon, star, etc.).
When an emoji is chosen, its native OS glyph is rendered onto a canvas,
the alpha channel extracted as a PNG mask, and applied via CSS mask-image.
Implementation:
- Widen AvatarShape type to accept emoji strings alongside predefined names
- isEmoji() detects any short non-ASCII string (avoids fragile Unicode regex)
- getEmojiMaskUrl() renders emoji at 512px, crops to tight bounding box
(with alpha threshold to ignore shadows/glows), squares the crop, scales
to 256px output, converts to white+alpha mask PNG. Cached per emoji.
- Avatar component applies mask-image for emoji, clip-path for geometric
- ProfileCard uses ring-4 instead of border-4 for emoji shapes to avoid
mask/content misalignment (border is inside the element; ring is outside)
- AvatarShapePicker adds emoji-mart picker in a popover
- Form schemas use z.string() to accommodate both predefined and emoji values
- NIP.md documents emoji shape support
- New 'shape' property on kind 0 profile metadata with 7 predefined shapes:
circle, triangle, inverted-triangle, hexagon, star, inverted-star, hexagram
- Avatar component updated with clip-path support for non-circle shapes
- Visual shape picker added to both EditProfileForm and ProfileSettings
- Shape applied across all 49 avatar render sites in the app
- New UserAvatar wrapper component for future simplified avatar rendering
- Unknown shape values gracefully fall back to circle (forward compatible)
- Add 'How do I report harmful content?' help tip to Muted Content and Sensitive Content sections
- Add 'For You page' help tip to Home Feed settings header
- Move wallet help tip from NWC section to Wallet page header
- Add 'What are zaps?' help tip to Zap dialog title
- Add 'Mastodon/Bluesky comparison' help tip to Platform filter in search
- Fix Select dropdown z-index (250->270) so dropdowns render above filter popover (260)
- Raised dialog z-index from z-[150] to z-[250] so the overlay sits above
CommentsSheet's backdrop (z-[200]) and receives touch events
- Removed stopPropagation from DialogOverlay/DialogContent onClick handlers
which was preventing Radix DismissableLayer's deferred click listener from
reaching document on iOS Safari
On iOS touch, Radix registers a one-time click listener on document after
pointerdown. The stopPropagation was intercepting that click before it could
reach document, so the dismiss callback never fired. Z-index stacking now
handles isolation between the dialog and the CommentsSheet backdrop.
Radix Dialog's RemoveScroll blocks wheel events on elements outside the
Dialog's DOM tree. The GIF picker Popover was portaling to document.body,
placing it outside the RemoveScroll boundary. Trackpad scrolling was
blocked while scrollbar dragging still worked.
Added PortalContainerContext so PopoverContent can portal into the modal
element instead of document.body when rendered inside a dialog/modal.
SelectContent used z-50 while PopoverContent used z-[200]. Both are
portaled to the body, so when a Select inside a Popover is opened,
the Select dropdown rendered behind the Popover panel. Raised
SelectContent to z-[250] so it always appears above popovers.
Closes#55
- ComposeBox: invalidate the correct 'event-comments' cache key after
posting a NIP-22 comment so CommentsSheet refreshes immediately
- toast: raise z-index to 300 so it renders above the CommentsSheet
backdrop blur (which sits at z-200)
- Cache follow list and author profiles in localStorage for instant feed on return visits
- Prefetch profile images via <link rel=preload> for instant avatar rendering
- Replace Radix Avatar with lightweight component that renders <img> immediately
- Lazy-load route components (React.lazy) reducing main bundle from 1.7MB to 920KB
- Dynamic import hls.js (1.3MB) only when visiting stream pages
- Remove unused Buffer polyfill
- Add pre-React loading spinner in index.html outside #root for seamless handoff
- Fix 3-5s feed rerender caused by NostrSync relay/settings sync
- Fix Zod 4 strict mode rejecting feedSettings with extra/missing keys from localStorage
- Fix useLocalStorage skip update when value reference unchanged
- Reduce eoseTimeout from default to 600ms
- 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
- Add safe area top padding to Sheet close button (X icon)
- Add safe area bottom padding to theme toggle section
- Ensures UI elements don't overlap with device notches/navigation bars
- Install @capacitor/app and @capacitor/status-bar as dependencies
- Initialize StatusBar in App.tsx with dark style and overlay mode
- Add safe-area-top utility class to index.css
- Apply safe-area-top to MobileTopBar component
- Apply safe-area-top to toast notifications on mobile
- Ensures content is not hidden behind device notches/status bars
- Add icons to media type filters (Images, Videos, Vines)
- Add Languages icon to language filter
- Remove icons from "All media", "No media", and platform checkboxes
- Increase radio button and checkbox size from 16px to 18px
- Remove text-sm classes for better readability
- Increase icon sizes from 3.5px to 4px
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
Add stopPropagation to dialog and drawer overlay/content click handlers to prevent clicks on modals from triggering click events on underlying elements like NoteCard.
This fixes the issue where clicking outside the zap modal would still register as a note click and navigate to the post detail page.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- Add nostrconnect method to useLoginActions for NIP-46 client-initiated connections
- Add helper functions generateNostrConnectParams and generateNostrConnectURI
- Create QRCodeCanvas component wrapper for displaying QR codes
- Update LoginDialog to support remote signer login with QR code scanning
- Add "Use remote signer" button when extension is available
- Display QR code on desktop, "Open Signer App" button on mobile
- Support manual bunker URI input as fallback option
- Add connection retry on error
- Use existing useIsMobile hook for responsive behavior
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
- 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>