The Mentions tab now excludes kind 1 reply events (those with NIP-10
reply/root e-tags), showing only pure mentions where someone tagged the
user in a new post. Kind 1111 comments continue to appear in both tabs.
Kind 1 events that are replies (have NIP-10 reply/root e-tags) now show
a reply icon with 'replied to your note' instead of the '@' icon with
'mentioned you'. Only pure mentions (no reply threading) use the mention
label.
- Fix silent notification drops: reactions, reposts, and zaps were being
discarded when the referenced event couldn't be fetched from relays.
Now keeps notifications with missing context instead of hiding them.
Zaps no longer require the author-ownership check since the #p filter
already confirms the user is the recipient. (fixes tabs appearing
identical and missing zap notifications)
- Add real-time WebSocket subscriptions for instant notification updates
instead of relying solely on 60-second polling. Both the full
notification list and the unread dot indicator now react immediately
when new events arrive.
- Wire up zap amounts from NIP-85 stats (zap_amount tag on kind 30383)
through to the NoteCard action bar, replacing the hardcoded 0.
- Seed client-side reply counts into the event-stats cache from the
loaded comment tree in PostDetailPage, ensuring sub-comment counts
are visible even when NIP-85 stats are unavailable for kind 1111.
Closes#136
Enter/submit now always performs a text search instead of redirecting
when the query looks like a NIP-05, npub, nevent, etc.
Identifiers are detected in real-time and shown as autocomplete items:
- NIP-05 (user@domain.com, domain.com): resolves to profile via .well-known
- npub/nprofile: shows profile with avatar and metadata
- note/nevent: shows event content preview with author
- naddr: shows addressable event with title/content preview
- hex: shows navigable link to the identifier
Resolved profiles are deduplicated from regular search results.
Works in both desktop sidebar and mobile bottom sheet search.
Replace the confusing externalLink/hideActions/showDiscuss props with
a clearer model:
- navigateToComments: opt-in to /i/ navigation (default: card opens
the URL externally, which is the natural expectation)
- showActions: single prop controlling the action button across both
LinkPreview and DiscussBar (default: true)
The action button always offers the opposite of what clicking the card
does: 'Discuss' when the card opens externally, 'Open' when the card
navigates to /i/.
Link previews now show the correct action button based on context:
- In posts (externalLink): card opens externally, shows 'Discuss' button
- In hover cards (!externalLink): card navigates to /i/, shows 'Open' button
- On /i/ page (hideActions): no action button since both would be redundant
LinkEmbed now defaults externalLink to true so link previews embedded
in post content open the URL externally as expected. Only standalone
uses of LinkPreview (like the CommentContext hover card) navigate to
the /i/ comment page.
When the card navigates to /i/ (the default), show an 'Open' button
with an external link icon that opens the original URL in a new tab.
Hidden when externalLink is true since the whole card already opens
the URL externally.
Remove the Discuss button from LinkPreview since the whole card now
navigates to /i/:url. Add externalLink prop so the card opens the URL
in a new tab instead when already on the /i/ page or in a hover card.
Fetch OEmbed link preview for URL identifiers in CommentContext so
'Commenting on' displays the page title alongside the site favicon
instead of a raw hostname+pathname.
When a full URL (http/https) is entered in the search bar, a new
'Comment on this link' option appears below the existing 'Search for'
option. Clicking it navigates to /i/<url> for commenting. The option
shows a link preview thumbnail (fetched via OEmbed) on the left where
avatars usually are, with the site's favicon as fallback. Works in
both the desktop sidebar dropdown and mobile bottom nav search sheet.
Keyboard navigation updated to support the new item.
The profile feed queried all enabled kinds (20+) with limit:15, but the
Posts tab filters client-side to only show non-reply posts. Events from
other kinds (badges, themes, emojis, etc.) consumed the limit, and the
pagination cursor jumped over time ranges containing visible posts.
Three-pronged fix:
- Filter kinds per active tab at query time (exclude kind 1111 on Posts)
- Over-fetch 3x the page size to compensate for reply filtering
- Auto-fetch next page when visible items fall below threshold
Closes#146
The same stripes + glitch icon + title + npub + reason layout was
duplicated across EmbeddedVanishCard, EmbeddedVanishPost,
VanishEventContent compact mode, and an inline version in NoteCard's
threaded branch. Extract a single VanishCardCompact component and
replace all four copies with it.
Net reduction of 76 lines.
The vanish layout (kind 62) in NoteCard didn't handle the threaded/
threadedLast props, so when rendered as a parent in AncestorThread,
it used the standard feed card layout where the caution stripes are
siblings of the bordered container — visually appearing to escape
above and below the card.
Add a dedicated threaded branch for vanish cards that:
- Uses the avatar column + connector line pattern like other threaded cards
- Places the stripes INSIDE the bordered container (as top/bottom bands)
so they stay visually contained within the card boundary
Allow users to browse and restore historical versions of their
kind 10000 mute list, similar to the existing Profile Recovery
dialog. Each snapshot is decrypted and displays a summary of
muted items by type (users, hashtags, words, threads).
The replay integration hooks into every DOM mutation via rrwebWrapped
event listeners, adding ~133ms of overhead during heavy re-renders.
We only use Sentry for error reporting, not session replay.
The IndexedDB profile cache (d9e943d0) introduced a regression where
parseAuthorEvent() — which runs the full Zod n.metadata().parse() chain —
was called synchronously on every render via TanStack Query's initialData
option. With 50+ NoteCards in a feed and no React.memo, this caused ~970ms
of Zod validation + ~1.3s of Nostrify core overhead per tab navigation.
Fix by:
1. Storing pre-parsed metadata in ProfileCacheEntry so useAuthor can
provide initialData without re-running Zod on every render
2. Wrapping NoteCard in React.memo to prevent re-renders when props
haven't changed (event objects are referentially stable from the
query cache)
- Add mission pool with 8 missions (interact, feed, clean, sing, play_music, sleep, take_photo, change_shape)
- Implement weighted random selection for 3 daily missions per user
- Add localStorage persistence with automatic daily reset
- Track progress from all Blobbi actions (inventory, direct actions, sleep, photo, profile shape)
- Add DailyMissionsPanel UI component with progress bars and claim buttons
- Integrate daily missions section into BlobbiMissionsModal
- Each mission rewards 80-110 coins depending on difficulty
The date text was wrapping to a second line in the exported PNG but not
in the modal preview. This was caused by html-to-image rendering text
differently when using Tailwind classes.
Fixes:
- Convert caption area to inline styles for consistent html-to-image export
- Add whitespace: nowrap to date, stage badge, and caption elements
- Add explicit width constraint to caption container
- Use inline styles instead of Tailwind classes for all caption text
This ensures the downloaded PNG and Blossom-posted image match the
modal preview exactly, with the date staying on one line.
1. Polaroid layout - now looks like a real polaroid:
- White frame on ALL sides (top: 16px, sides: 16px, bottom: 80px)
- Photo area is inset within the white frame
- Caption area positioned at bottom of frame
- Consistent off-white background (#fafafa) for export
2. Removed noisy debug logs:
- Removed [BlobbiStageVisual][baby] and [adult] console.logs
- These were render-time logs that added noise without value
3. Improved export/post robustness:
- URL extraction now explicitly finds 'url' tag instead of assuming [0][1]
- Added error handling if upload returns no URL
- Safer parsing that doesn't depend on tag order
4. Verified visual export consistency:
- White frame visible on all sides
- Blobbi centered in photo area
- No clipping or gaps
- Caption properly positioned
Add the ability to take polaroid-style photos of Blobbis and share them:
- Add lookMode prop to Blobbi rendering system:
- 'follow-pointer': Eyes track mouse cursor (default, existing behavior)
- 'forward': Eyes look straight ahead (for photos/export)
- Updated useBlobbiEyes, BlobbiBabyVisual, BlobbiAdultVisual, BlobbiStageVisual
- Create BlobbiPolaroidCard component:
- Classic polaroid-style frame with white background and shadow
- Soft gradient background for photo area
- Caption area with Blobbi name, stage, and date
- Fixed dimensions (320x400) for consistent export
- Built with HTML+CSS (not canvas) for easy customization
- Create BlobbiPhotoModal component:
- Opens from 'Take a Photo' button on BlobbiPage
- Shows polaroid preview with Blobbi looking forward
- Download button: exports as PNG using html-to-image
- Post button: uploads to Blossom and creates kind 1 note
- Clean, minimal UI focused on the photo
- Wire up to BlobbiPage:
- Photo modal state and handler
- Connected to floating 'Take a Photo' action button
Dependencies:
- Added html-to-image for DOM-to-PNG conversion
Hide 'Set as Companion' and 'Open PiP' buttons from BlobbiDashboardFloatingControls
until their features are implemented. Code is commented out with TODO markers for
easy re-enablement later.
Remaining visible buttons: Take a Photo, Info, Hatch/Evolve actions
- Detect final stage from tags and validate tags against stage constraints
- Remove tags not valid for the detected stage (e.g., adult_type only on adults)
- Fix state after transitions (incubating/evolving -> active)
- Validate state is valid for the stage (per VALID_STATES_BY_STAGE)
- Skip required tag recovery for tags not valid for current stage
- Skip persistent tag recovery for tags not valid for current stage
- Add dev diagnostics with console.warn when repairs are applied
- Return finalStage in TagRepairResult for caller inspection
Add validateAndRepairBlobbiTags function that ensures tag integrity
whenever a Blobbi is republished. The guard:
1. Validates tags against the canonical schema
2. Removes deprecated tags automatically
3. Removes task-related tags during stage transitions (when requested)
4. Recovers missing required tags from:
- Previous canonical tags (if available)
- System defaults (for b, t, client only)
5. Preserves all persistent tags from previous state
6. NEVER invents personality/trait/adult_type values
Integration points:
- mergeBlobbiStateTagsForRepublish: validates all tag merges
- useBlobbiHatch: validates with task cleanup before publishing
- useBlobbiEvolve: validates with task cleanup before publishing
- buildMigrationTags: validates migration output
Repair strategy:
- System tags (b, t, client): recover from defaults
- Identity tags (name, seed, d): recover from previous only, never invent
- Personality tags: preserve if exist, never invent
- Visual tags: preserve (regenerable from seed if needed)
- Stats: preserve current values
- Task tags: cleanup during transitions
Update buildMigrationTags to preserve all persistent tags when migrating
legacy Blobbis to canonical format:
- personality, trait, favorite_food, voice_type, mood
- adult_type
- theme, crossover_app
Per blobbi-tag-schema.md spec: Do NOT invent values for tags that don't
exist. Only preserve values that are already present in the legacy event.
Also adds docs/blobbi/blobbi-tag-schema.md as the product spec for all
Blobbi tag definitions. The runtime schema in blobbi-tag-schema.ts MUST
align with this spec.
- Add identity/personality tags (personality, trait, favorite_food,
voice_type, mood, adult_type) to MANAGED_BLOBBI_STATE_TAG_NAMES
- Add 'interact_6_progress' to DEPRECATED_BLOBBI_TAG_NAMES to remove
legacy interaction tracking
- Update hatch flow to clean only task/state-specific tags while
preserving all identity attributes from canonical.allTags
- Update evolve flow to clean only task/state-specific tags while
preserving all identity attributes, and set state to 'active'
This ensures Blobbi identity persists across egg → baby → adult
transitions, treating them as persistent entities rather than
reconstructed objects at each stage.
Adds a global capture-phase paste listener that detects nsec private
keys and prevents them from being pasted into any field. Shows a
destructive toast warning the user that private keys should never
be shared.