Add a 3-dots menu to the Blobbi dashboard with a 'View Blobbi' link that
navigates to the naddr detail page. Register kind 31124 (Blobbi Pet State)
across all UI registration points so Blobbi events render properly in
feeds, detail pages, comment contexts, and embedded previews.
Previously comments shared feedKey 'feedIncludePosts' with kind 1, and
generic reposts shared 'feedIncludeReposts' with kind 6. This made it
impossible to toggle them independently in settings.
Add feedIncludeComments and feedIncludeGenericReposts to FeedSettings
and wire them to their respective EXTRA_KINDS entries.
Remove the inline toggle from the post composer toolbar and add
imageQuality to AppConfig instead. The setting now lives in
Settings > Network under 'Image Uploads' with a Compressed/Original
pill toggle, persisted to localStorage like other app settings.
Register kind 62 in the EXTRA_KINDS feed system so Request to Vanish
events from followed users appear in the home feed. Enabled by default
via the feedIncludeVanish toggle, and can be disabled in feed settings.
Blossom servers now work exactly like relays:
- App default servers (APP_BLOSSOM_SERVERS) can be toggled on/off
- User's kind 10063 server list is fetched and synced from Nostr
- NostrSync fetches kind 10063 on login and applies it to config
- useInitialSync fetches kind 10063 in parallel on first login
- BlossomSettings UI mirrors RelayListManager with two sections:
App Blossom Servers (with enable toggle) + Your Blossom Servers
- User changes publish a kind 10063 replaceable event to Nostr
- getEffectiveBlossomServers() merges app + user servers like getEffectiveRelays()
- Migration from old blossomServers[] to blossomServerMetadata handled in AppProvider
Resolve merge conflicts from badges, books, help, and emojis rename.
Fix biome lint: add type="button" to all buttons, use template literals,
format new components with project conventions.
Implement badge definitions (kind 30009) and profile badges (kind 30008)
as a new Whimsy content type with feed page, NoteCard rendering, sidebar
entry, dedicated detail page with awardee list, and a Badges core profile
tab showing a user's accepted badges with links to badge definitions.
- Add NIP-51 Follow Sets management (useUserLists, UserListsPage at /lists)
- Add Follow Packs support (useFollowPacks) for curated people lists
- Add FeedEditModal for creating/editing home feed tabs with author scope,
kind picker, and list/pack author population
- Add AddToListDialog accessible from the note more-menu
- Unify home feed tab resolution with profile tabs via useResolveTabFilter,
supporting $follows variable expansion
- Wire People filter in Search and FeedEditModal with list/pack picker
- Extract shared ScopeToggle, ListPackPicker, parseSelectedKinds primitives
into SavedFeedFiltersEditor to eliminate duplication across three call sites
- Cache savedFeeds in AppConfig for instant tab render on load
- Strip orphaned profile-destination entries from encrypted settings on read
- Add homePage config field (default: 'feed') that determines which
sidebar item renders at the '/' route
- Any sidebar item can be set as the homepage via Feed settings
- The feed is always accessible at /feed when not configured as home
- Normalize sidebar item IDs: every item follows path === '/' + id
(feed path changed from '/' to '/feed', emoji-packs renamed to emojis)
- Remove BOTTOM_NAV_ITEMS filter from MobileDrawer so all sidebar
items are visible on mobile (feed, notifications, search included)
- Mobile bottom nav dynamically reflects the configured homepage icon
- Homepage setting syncs across devices via encrypted settings
- Add emoji-packs -> emojis migration for existing sidebar orders
Sentry SDK is loaded via dynamic import() only when a DSN is configured
and error reporting is enabled, keeping it out of the initial bundle.
Users can control error reporting via a toggle and DSN field in
Advanced Settings, with the DSN synced across devices via encrypted
NIP-78 settings. The ErrorBoundary now reports fatal crashes to Sentry
with component stack context, and a beforeSend hook censors nsec
private keys before any data leaves the browser.
New 'Development' section under Other Stuff in Content Settings that
aggregates developer-focused Nostr content: git repository announcements
(kind 30617), patches (kind 1617), pull requests (kind 1618), custom
NIP proposals (kind 30817), and app submissions (kind 31733).
Adds /dev route using KindFeedPage with external links to Gitworkshop
and NostrHub. Five new card components render each kind in the feed.
Resolve merge conflicts in 5 files, keeping both NIP-38 user statuses
and music/podcasts/events features from main:
- src/App.tsx
- src/contexts/AppContext.ts
- src/lib/extraKinds.ts
- src/lib/schemas.ts
- src/test/TestApp.tsx
Resolve conflicts in AppRouter, NoteCard, sidebarItems, and PostDetailPage
by keeping both music/podcast features and main's additions (books,
calendar events).
Register kind 30315 in extraKinds as a feedOnly entry with showKey
(not feedKey) so statuses appear as a toggle in Other Stuff > Social
but are NOT included in the main feed query. The toggle controls
whether statuses are displayed on NoteCard author rows and profile
pages.
- extraKinds: registered with showKey='showUserStatuses', feedOnly=true
- ContentSettings: handle feedOnly entries with showKey as display toggles
- NoteCard: gate useUserStatus behind feedSettings.showUserStatuses
- ProfilePage: gate status display behind feedSettings.showUserStatuses
- Defaults: showUserStatuses=true (enabled by default)
Integrate custom emojis from the user's kind 10030 emoji list into
the emoji picker, compose flow, reactions, and DMs:
Hook:
- useCustomEmojis: query kind 10030, extract emoji shortcode/url pairs
EmojiPicker:
- Accept customEmojis prop, render as dedicated 'Custom' tab via
emoji-mart's custom categories feature
- Changed onSelect from string to EmojiSelection union type
(native unicode | custom shortcode+url)
ComposeBox:
- Pass custom emojis to picker, insert :shortcode: on selection
- Scan content on publish for :shortcode: patterns, cross-reference
against user's emoji list, add ['emoji', name, url] tags (NIP-30)
QuickReactMenu:
- Custom emoji reactions: content=':shortcode:', with emoji tag
- Optimistic cache update includes custom emoji URL/name
DMChatArea:
- Pass custom emojis to picker, insert :shortcode: text
Settings:
- Registered as 'Custom Emojis' in Other Stuff > Whimsy section
- showCustomEmojis toggle (default true) gates all custom emoji
features — when disabled, no kind 10030 queries are made
- Fix pre-existing Bot icon missing import in sidebarItems
Implement full calendar event rendering, dedicated events page, and
RSVP functionality for NIP-52 calendar events:
Registration:
- FeedSettings: showEvents + feedIncludeEvents toggles
- extraKinds: kind 31923 registered as 'events' in social section
with link to plektos.app for event creation/management
- Sidebar: Events entry with CalendarDays icon at /events
- Onboarding: added to content picker, pre-checked by default
Components:
- CalendarEventContent: rich card with cover image, title, date/time,
location, description, participant count, hashtags. Handles both
kind 31922 (date-based) and 31923 (time-based) with Intl formatting
- CalendarEventDetailPage: full detail view with RSVP UI (Going/Maybe/
Can't Go buttons + optional note), attendee sections with avatar
stacks, participant roles, share button, zap support
- RSVPAvatars: reusable overlapping avatar stack with tooltips
Hooks:
- useEventRSVPs: query kind 31925 RSVPs for an event, deduplicate by
author, group by status (accepted/declined/tentative)
- useMyRSVP: check current user's RSVP status for an event
- usePublishRSVP: publish kind 31925 RSVP with cache invalidation
Pages:
- EventsFeedPage: two-tab layout — Upcoming (calendar events from
follows/global with future-first sorting) and Activity (social feed
showing 'Alice is going to [Event]' from followed users' RSVPs)
Wiring:
- NoteCard: isCalendarEvent detection + CalendarEventContent dispatch
- PostDetailPage: routes kind 31922/31923 to CalendarEventDetailPage
- AppRouter: /events route
Implement recording, publishing, and rendering of NIP-A0 voice messages:
- VoiceMessagePlayer: compact waveform bar player when imeta waveform
data is available, falls back to existing AudioVisualizer sinewave
- useVoiceRecorder: MediaRecorder hook with real-time amplitude capture,
60s max duration, and waveform sampling for NIP-A0 imeta tags
- ComposeBox: mic button in toolbar with recording UI (live waveform,
timer, cancel/send controls), publishes kind 1222 (root) or 1244
(NIP-22 reply) with imeta waveform and duration fields
- NoteCard + PostDetailPage: render kind 1222/1244 via VoiceMessagePlayer
- Feed settings: voice messages registered in Other Stuff > Media with
feed toggle (feedIncludeVoiceMessages), defaults to off
- Remove defaultZapComment config; derive from appName instead
- Add appId (string) for NIP-78 d-tag prefixes (e.g. ${appId}/metadata)
- Add client (optional NIP-89 addr) as third element of client tags
- Update useNostrPublish and useEncryptedSettings to use config.appName
and config.client for client tags instead of location.hostname
- Replace hardcoded 'ditto-metadata' d-tag with ${config.appId}/metadata
- Remove Default Zap Comment setting from AdvancedSettings UI
All runtime references to 'Ditto' now use config.appName, which defaults
to 'Ditto' and can be overridden at build-time via ditto.json. This
covers useSeoMeta titles across all pages, the welcome screen, feed CTA,
Nostr Connect app name, encrypted settings event title, and the
AdvancedSettings zap comment placeholder.
Files in the external directory take precedence over the built-in
public/ directory. Works in both dev (middleware) and build (post-copy)
modes. Also fix missing magicMouse property in TestApp.
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
Query NIP-94 kind 1063 events filtered by #m tag for
application/x-webxdc MIME type. Includes infinite scroll,
pull-to-refresh, embedded webxdc launcher, and onboarding
content selection.
- Desktop: Remove redundant Home/Search nav items (logo serves as Home, inline
search covers both quick lookup and full search page navigation)
- Add visual section grouping with labeled separators (Explore, You, Settings)
- Replace ugly dashed 'Add' button with subtle 'More...' inline link
- Add edit mode with drag-and-drop reordering (@dnd-kit) and remove buttons
for content-type items in the Explore section
- Hover-to-remove on desktop content-type items outside edit mode
- Mobile: Replace avatar menu trigger with standard hamburger icon
- Mobile: Add search icon to top bar right side
- Mobile: Redesign bottom nav as 4-tab layout (Home, Explore, Notifications, You)
- Mobile: Explore tab opens bottom sheet with content type links (vaul Drawer)
- Mobile drawer: Replace profile header with logo, restructure to match desktop
sections, add 'Manage...' link for settings, remove redundant Profile link
- Add sidebarOrder to AppConfig for persisting custom item order
- Extend useFeedSettings hook with orderedRoutes, addToSidebar, removeFromSidebar
The pubkey was incorrectly hardcoded in the previous commit. Restore it
to AppConfig, the Zod schema, defaultConfig objects, all three
useNip85Stats hooks, and the Stats Source section in AdvancedSettings.
The NIP-85 Only Mode toggle (and its nip85OnlyMode config field) remains
removed — NIP-85 is always used.
Remove nip85StatsPubkey and nip85OnlyMode from AppConfig, AppProvider
schema, and all defaultConfig objects. The stats pubkey is now a
hardcoded constant in useNip85Stats. Remove the Stats Source section
from AdvancedSettings (UI toggle and pubkey input). Simplify
useEventStats to use NIP-85 exclusively — delete the direct-query
fallback, computeStats, extractZapAmount, and parseBolt11Amount.
Simplify useComments to remove the NIP-85-driven limit heuristic and
the nip85Stats return value.
- Add blossom.ditto.pub and blossom.dreamith.to as primary servers
- Mirror uploads to all configured servers via BUD-04 (fire-and-forget)
- Prefer first configured server for upload URLs
- Fall back to alternative Blossom servers when images/videos fail to load
Link previews now fetch from a dedicated link preview service (default:
https://fetch.ditto.pub/link/{url}) that returns OEmbed JSON, instead of
fetching raw HTML through a CORS proxy and parsing OG tags client-side.
- Add linkPreviewUrl config with RFC 6570 URI template support
- Rewrite useLinkPreview to consume OEmbed responses
- Update LinkPreview to render OEmbed fields (thumbnail_url, provider_name, etc.)
- Use ExternalFavicon component for link preview favicons (consistent with rest of app)
- Add linkPreviewUrl setting to Advanced Settings UI
- Generalize faviconUrl.ts into templateUrl() with {url} variable support
The old DomainFavicon component had a multi-step fallback chain (HTML scraping,
direct URL guessing, then the configured provider) which meant the configurable
setting was almost never reached. The new ExternalFavicon component uses the
configured favicon URL template directly via RFC 6570 URI templates.
- Rename config field faviconProvider -> faviconUrl
- Add faviconUrl() utility with uri-templates for RFC 6570 support
- Remove local Favicon component from ProfileRightSidebar
- Update all consumers to use ExternalFavicon
Support kind 37381 addressable events for MTG deck sharing per NIP-MG spec.
Includes MagicDeckContent component with card list rendering, format/archetype
badges, commander/companion display, sideboard, foil indicators, and
expand/collapse for large decks. Adds /decks route and sidebar navigation.
Articles (kind 30023) now have both a showArticles sidebar toggle and
feedIncludeArticles feed toggle, matching the pattern of other content
types like Vines and Streams. Added FileText icon to all icon maps.
- Add 'feed' section to EXTRA_KINDS for Posts (kind 1), Reposts (kind 6),
and Articles (kind 30023) as feed-only toggles
- Add feedIncludePosts, feedIncludeReposts, feedIncludeArticles to FeedSettings
(posts and reposts default to enabled, articles disabled)
- Add 'Notes' collapsible section in ContentSettings above 'Other Stuff'
- Feed kinds are now fully dynamic from settings instead of hardcoded
- Repost unwrapping accepts any event kind, not just kind 1
- Add article (kind 30023) card rendering with title, image, and summary
- Update all feed hooks for consistency (useFeed, useProfileFeed,
useStreamPosts, DomainFeedPage, HashtagPage)
Respect content-warning tags on events. Users can choose how to handle
them in Settings > Content > Sensitive Content:
- Blur (default): Shows a grey placeholder overlay with the warning
reason in quotes. Media is not loaded until the user clicks reveal.
- Hide: Removes content-warned posts from the feed entirely.
- Show: Ignores content warnings and displays everything normally.
The setting syncs across devices via encrypted NIP-78 settings.