Widen ComposeBox.replyTo and ReplyComposeModal.event to accept URL,
enabling the standard post composer for NIP-22 comments on external
content. Replace the inline CommentForm on /i/ pages with a FAB that
opens ReplyComposeModal, giving users the full compose experience
with file uploads, emoji picker, and content warnings.
Refactor the external content header components (URL, Book, Country)
and helpers out of ExternalContentPage into a shared module at
ExternalContentHeader.tsx. Both the /i/ page and PostDetailPage now
import from this shared module.
Add a compact ExternalContentPreview component that shows a rich
inline preview (thumbnail, title, favicon) of the external content
being commented on. This is displayed above the focused comment on
the nevent detail page for kind 1111 comments with I tags, similar
to how AncestorThread shows the parent for kind 1 replies.
Use the same NoteCard-based threaded comment display on /i/ pages
that the rest of the app uses for post detail views. This replaces
the custom recursive Comment tree with the standard threaded layout
(direct replies + first sub-reply preview).
Also fix PostDetailPage and ComposeBox to handle I (external content)
tags when reconstructing the root for kind 1111 comments, enabling
proper threading when viewing or replying to comments on URLs.
Replaces the SVG filter approach with dynamic pixel art generation:
rasterize the logo SVG to a 16x16 canvas, run ridge detection to
extract thin 1px strokes, and use the result as a CSS mask. Works
with any custom logo. Triggers ~1 in 20 page loads using crypto random.
When viewing a kind 1111 comment, reconstruct the original root event
from its uppercase tags (K, P, A, E) and pass that to useComments so
the full comment tree is fetched. Then use getDirectReplies(event.id)
to show only the replies to the specific comment being viewed.
- 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
Replace the previous ReplyContext approach for kind 1111 with a new
CommentContext component that fetches the root event and displays its
title/name. Handles all root types:
- Addressable events (A tag): fetches event, shows title/d-tag
- Regular events (E tag): fetches event, shows title with hover preview
- External content (I tag): shows URL hostname, ISBN, hashtag, etc.
with links to the /i/ discussion page
Used in both NoteCard (feed/thread views) and PostDetailContent
(expanded post detail view).
Kind 1111 comments now show reply context using the root author from
the uppercase P tag, with a hover preview of the root event from the
uppercase E tag. Previously only kind 1 replies showed this context.
Add kind 1111 to EXTRA_KINDS tied to feedIncludePosts so comments
appear in all feeds (follows, global, communities) and profile feeds.
Explicitly exclude kind 1111 from profile 'Posts' tab since they are
always replies. Treat kind 1111 like kind 1 in streaming filter so
reply/search/media filtering applies consistently.
Use useComments hook to fetch NIP-22 comments for non-kind-1 events
instead of useReplies (which only fetches kind 1), and render them
with the same NoteCard threaded layout used for kind 1 replies.
Also invalidate comments cache when posting NIP-22 replies.
NIP-85 stats provide counts but not emoji data. Fetch actual kind 7
reaction events via useEventInteractions for the focused post only,
derive the top 3 emojis by frequency, and display them in the stats
row. Ancestors and descendants are unaffected.
Port changes from mkstack: add `#${string}` type support for hashtag
roots, refactor usePostComment into shared makeCommentTags helper with
proper K tag values for hashtags, URLs, and protocol schemes, and update
nostr-comments skill docs with full NIP-73 identifier examples.
Removes duplicated NIP-22 tag logic from ComposeBox and delegates to
usePostComment, which correctly handles addressable/replaceable events
without adding spurious e tags. When replying to a kind 1111 comment,
the root event info is reconstructed from the comment's uppercase tags.
Added optional tags parameter to usePostComment so ComposeBox can pass
through hashtags, mentions, imeta, CW, and quote tags.
Previously, clicking Reply on non-kind-1 events (treasures, themes, vines,
etc.) would create a kind 1 event with NIP-10 tags. NIP-22 specifies that
comments on non-kind-1 events must use kind 1111 with proper uppercase
(root) and lowercase (parent) tag structure.
- Detect non-kind-1 reply targets and switch to kind 1111
- Build NIP-22 tags: A/E/I + K + P for root scope, a/e/i + k + p for parent
- Handle addressable, replaceable, and regular event kinds
- Copy uppercase root tags from kind 1111 targets when replying to comments
- Invalidate comments cache instead of replies cache for NIP-22 events
Add ThemeContent to PostDetailContent's kind-based dispatch so theme
events (kind 36767/16767) display their preview when viewed directly
via nevent/naddr URLs.
Use the same ThemePreviewCard design from /settings/theme (simulated
header bar, text lines, button, sidebar strip) scaled ~4x to fill
the feed card. Includes background image support and title/description
bar below the mockup.
Delay the system-fallback theme override until the profile theme
query has settled (isFetched). Previously, while the query was
loading, the system fallback would kick in immediately — replacing
the user's custom theme with the basic builtin theme, only to
switch back once the profile theme loaded. This was especially
noticeable when visiting your own profile with a matching theme.
Migrate themes from a standalone card to the standard NoteCard rendering
pattern (like treasures), so theme events appear as interactive posts
with reply, repost, reaction, and zap buttons.
- Create ThemeContent component for inline theme color preview
- Add ThemeHeader for 'shared a theme' / 'updated their theme' labels
- Support both kind 36767 (definitions) and 16767 (active themes) in feeds
- Convert extraKinds themes entry to use subKinds pattern with toggles
- Create ThemesPage wrapper (like TreasuresPage) for sub-kind filtering
- Update ContentSettings to sync both sub-kind feed toggles
Add Skater, Kawaii, Grunge, MS Paint, Retro Pop, Bubblegum, and Gamer
theme presets derived from the ditto-1 aesthetic designs. Each preset
includes tailored CoreThemeColors, a display font, and a Blossom-hosted
background image.
Install and register 7 new fontsource display fonts (Permanent Marker,
Cherry Bomb One, Creepster, Silkscreen, Bungee Shade, Luckiest Guy,
Press Start 2P) in the bundled font registry so they appear in the
FontPicker for custom theme building as well.
KindFeedPage's own useLayoutOptions was overwriting the onFabClick
set by WebxdcFeedPage. Now onFabClick is a KindFeedPage prop that
gets forwarded to useLayoutOptions, so only one call wins.
Replace the custom WebxdcFeedPage with KindFeedPage, powered by
a new tagFilters option on useFeed/Feed/KindFeedPage. The webxdc
page now uses the same follows/global tabs, pull-to-refresh, and
NoteCard rendering as every other feed.
- useFeed: accept optional tagFilters (e.g. { '#m': [...] })
- Feed/KindFeedPage: pass tagFilters through
- FloatingComposeButton: accept onFabClick callback override
- LayoutOptions: add onFabClick for custom FAB behavior
- Remove useWebxdcFeed (no longer needed)
Description now appears in its own rounded bg-secondary/50 card
below the media element, similar to YouTube's description box.
Media renders unwrapped at full width.
Each MIME type now renders inside a rounded border card. The media
is the hero at the top and the description sits in a separated
footer section below. When there's no description, images and
videos skip the card wrapper to stay clean.
Media (webxdc embed, image, video, audio) is now the hero element
rendered first. Descriptions appear below as captions. Audio gets
a styled card with track name, file size, and download button.
Generic files get a polished card with MIME label and size info.
Kind 1063 file metadata events now dispatch to FileMetadataContent
which renders based on MIME type: webxdc apps get WebxdcEmbed,
images get ImageGallery, videos get VideoPlayer, audio gets a
native player, and unknown types show a download link.