The default pool eoseTimeout (300ms) races and resolves shortly after the
fastest relay. Blobbi pet state and profile data are accuracy-sensitive —
stale data from a single fast relay can cause data loss when mutations
overwrite newer versions on other relays.
- Add eoseTimeout option to fetchFreshEvent and new fetchFreshEvents variant
- Update useBlobbisCollection, useBlobbonautProfile, and useBlobbiSleepToggle
to use fetchFreshEvents/fetchFreshEvent with eoseTimeout: 1000
- Widen NostrBatcher.req() type to pass through eoseTimeout to NPool
- Gate unconditional console.log in parseBlobbiEvent behind import.meta.env.DEV
- Remove unconditional console.logs from useBlobbisCollection
Instead of generating a random session ID for the iframe subdomain,
derive it from the nsite event using the NIP-5A canonical format:
- Root sites (kind 15128): npub subdomain
- Named sites (kind 35128): base36(pubkey) + d-tag subdomain
Extract hexToBase36 and getNsiteSubdomain into a shared utility
used by both NsiteCard and NsitePreviewDialog.
AppConfig.client now expects a NIP-19 naddr1 string pointing to the app's
kind 31990 handler event instead of a raw 'a' tag value. useNostrPublish
decodes the naddr at publish time to extract the 31990:<pubkey>:<d-tag>
addr and any embedded relay hint, producing a fully NIP-89-compliant
client tag: ["client", <name>, <addr>, <relay-hint>].
- Rename Zapstore kind labels to include 'Zapstore' prefix across all
label registries (NoteCard, PostDetailPage, CommentContext,
ExternalContentHeader, NotificationsPage, extraKinds)
- Wrap Zapstore (32267, 30063, 3063) compact and detail content in
rounded bordered cards with hover effects; remove redundant mt-2/mt-3
margins from component roots
- Replace useLinkPreview thumbnail with metadata banner/picture in kind
31990 app handler cards (compact and full views)
- Add pt-4 to Zapstore detail card wrappers in PostDetailPage
- Fix sticky tab bar (SubHeaderBar z-10) being painted over by card
content: remove z-10 from AppHandlerContent inner div and add z-0 to
the main content column in MainLayout
- New ZapstoreReleaseContent component: shows app icon/name fetched from the
linked kind 32267, version badge, channel badge, release notes, and a
downloads section that fetches and renders each linked kind 3063 asset
- New ZapstoreAssetContent component: shows MIME-type icon, platform/arch
badges, file size, SHA-256 hash, commit hash, supported NIPs, and APK
certificate hashes
- Register both kinds in NoteCard, PostDetailPage, extraKinds, CommentContext,
ExternalContentHeader, and NotificationsPage label/icon maps
- Route kind 3063 to the Zapstore relay in NostrProvider and useEvent
- Kind 3063 is excluded from feeds (display-only on direct navigation)
No imports remained pointing at the @/lib/blobbi* or @/hooks/use{ProjectedBlobbiState,BlobbisCollection,BlobbiMigration} paths.
Delete the transitional re-exports and the dead hook copies so only
src/blobbi/core/lib/ and src/blobbi/core/hooks/ remain as the single
source of truth.
- Convert src/lib/blobbi*.ts files to thin re-exports from canonical
src/blobbi/core/lib/ sources, eliminating duplicated logic
- Remove unused emoji, title, description props from TasksPanelProps
and their call site in BlobbiMissionsModal
- Remove dead direction state from MissionSurfaceCard (was always 'right')
- Remove unused onContinue prop from FirstHatchTourCard and call site
The onboarding completion flag was stored as a generic 'onboarding_done'
tag on the kind 11125 Blobbonaut profile, while the first-hatch tour
relied solely on device-local localStorage. This caused issues with
multi-account usage on the same browser.
Changes:
- New profiles write 'blobbi_onboarding_done' (not 'onboarding_done')
- Parsing reads 'blobbi_onboarding_done' first, falls back to old tag
- Auto-migration: useBlobbonautProfileNormalization detects old tag
and replaces it with the new one on next profile republish
- MANAGED_BLOBBONAUT_PROFILE_TAG_NAMES includes both tags so the
merge logic can remove the old one during migration
- Tour activation now accepts profileOnboardingDone flag from the
Blobbonaut profile as the authoritative completion source;
localStorage remains a secondary fallback for in-progress UI state
- BlobbiPage passes profile.onboardingDone to the activation hook
Move build-time ditto.json injection from a Vite define global to
import.meta.env.DITTO_CONFIG (a JSON string parsed and validated at
runtime via DittoConfigSchema). Remove the global type declaration
from vite-env.d.ts.
Drop ThemeSchemaCompat and its legacy "black"/"pink" migration code
from AppProvider and NostrSync — invalid theme values now simply fail
Zod validation.
Fix a latent bug where a partial feedSettings from ditto.json would
replace the full hardcoded defaults; defaultConfig now deep-merges
feedSettings.
- Combine Shop and Inventory into a single tabbed dialog (Shop tab
with category sub-tabs, Inventory tab with item list and use flow)
- Remove BlobbiInfoModal entirely
- Move dev tools (Dev Hatch/Evolve, State Editor, Emotion Tester) into
the bottom bar 'More' dropdown with yellow text, remove floating
dev tools panel
- Remove 'visibleToOthers' / 'visible_to_others' concept from the
entire codebase: types, interfaces, tag schemas, event construction,
parsing, UI badges, dev editor, and documentation
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.
Replace external Inkwell link with a built-in article creation experience.
Uses Milkdown editor with tabbed UI (Write/Details/Drafts) matching the
letters compose pattern, FAB publish button, relay+local draft support,
and kind 30023/30024 publishing.
Move the hardcoded < 70 stat visibility checks from BlobbiPage.tsx into
the shared getVisibleStatsWithValues() utility in blobbi-decay.ts. This
ensures egg, baby, and adult stages all use the same STAT_VISIBILITY_THRESHOLD
constant, and any future UI consuming visibleStats gets the filtering for free.
Stop filtering requiresAuth items from navigation. Pages already render
their own LoginArea when the user is not logged in, so hiding the items
from the sidebar, mobile drawer, and search prevented feature discovery
without providing any benefit.
Also update the default sidebar order: remove bookmarks and profile,
add letters.
- Make LetterContent.body optional; a letter requires a non-empty body
or at least one sticker
- Replace colors:[]/flatMode with event-stripping: flat color moment =
event field stripped from Stationery, removing the colors? field
- Remove edgeScale: stickers render at their stored scale value with no
edge-proximity size reduction, matching lief and the NIP
- Fix sticker shrinking near card edges: add max-width:none to override
Tailwind preflight max-width:100% on img/svg elements
- Allow sending letters with only stickers/drawings (no body text required)
- Fix sticker drag positioning by using cardRef instead of page container
ref, so percentage coordinates map correctly to the card boundaries
- Preserve stationery source events (color moments/themes) in preferences
so they embed as gift attachments in sent letters
- Accept sticker-only letters during decryption validation
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.
Profile badges should be a replaceable event (kind 10008), not an
addressable event (kind 30008) with a fixed d-tag. This follows the
same deprecation pattern used by NIP-51 lists.
All writes now publish kind 10008. All reads query both 10008 and
legacy 30008, picking whichever is newest, for backwards compatibility
during the transition period.
Extract shared fetchFreshEvent() utility that fetches the freshest
version of a replaceable/addressable event directly from relays before
every mutation. This prevents data loss when the TanStack Query cache
is stale (cross-device edits, rapid sequential operations).
Previously only useFollowActions and useMuteList had this safety
pattern. Now all list-type hooks use the same shared primitive:
useAcceptBadge, useRemoveBadge, useBookmarks, usePinnedNotes,
useInterests, and useUserLists.
Interests, custom emojis, and Blossom server list queries now batch
with profile/follow/mute queries instead of firing separate REQs,
reducing ~3 REQs on feed load.
Add defense-in-depth sanitization at the output boundary of the Blobbi
SVG rendering pipeline. The upstream pipeline validates user inputs
(normalizeHexColor, instanceId regex), but 3000+ lines of regex-based
SVG string manipulation feed directly into dangerouslySetInnerHTML with
no structural guarantee that the output is safe.
sanitizeBlobbiSvg() uses DOMPurify with an allowlist tuned for the
Blobbi pipeline (gradients, clip paths, animations, @keyframes) while
blocking scripts, event handlers, foreignObject, href, and other
dangerous constructs.
Encrypt and decrypt operations now call the signer directly without
nudge/timeout/retry wrapping. The sign operation already provides
the user-facing nudge when approval is needed, so the encrypt nudge
was redundant noise. Phase-transition toast and related constants
removed as dead code.
- Decrypt operations now bypass signerWithNudge entirely (no nudge toast)
- Pinned SubHeaderBar safe area uses a separate fill div matching
MobileTopBar's bg-background/85, avoiding double-opacity stacking
with the ArcBackground SVG below
- Pinned SubHeaderBar uses safe-area-inset-top (top offset) instead of
safe-area-top (padding) so the arc stays flush with the tab content
- Throttle signer nudge toasts (8s cooldown) to prevent rapid-fire storm
when relay connection is unstable
- New posts pill fades out when nav hides instead of translating, avoiding
it floating in the safe area zone
- Signer toasts use finite duration (120s) so Radix swipe-to-dismiss works
- Lower toast swipe threshold from 50px to 30px for easier dismissal
- SubHeaderBar pinned mode adds safe-area-top padding when nav is hidden
- Increase signer nudge delay to 10s for decrypt ops (reduces false triggers on Amber)
- Add arc overhang spacer to Search, Notifications, and Profile pages
- Add PageHeader to Search page for consistent top-level layout
- Buffer streamed posts when user is scrolled down to prevent scroll jumps
- Show 'N new posts' pill that tracks SubHeaderBar position and nav state
- MediaCollage respects NIP-36 content warnings (blur/hide/show policy)
- Normalize main element classes across Profile and Notifications pages