86 Commits

Author SHA1 Message Date
Alex Gleason c957041cf3 Render Birdstar bird detections and custom constellations
Adds feed support for kind 2473 (bird-by-ear detections) and kind 30621
(user-drawn star figures) from Birdstar. Detections render as species
cards using the existing Wikidata + Wikipedia summary hooks; constellations
render as gnomonically-projected SVG star-maps backed by the Hipparcos
catalog from d3-celestial. The 1.1 MB catalog is code-split via lazy() so
it only loads when a constellation event is actually viewed.
2026-04-28 03:12:24 -05:00
filemon 6684efd146 Add Blobbi social interactions (kind 1124)
Introduce external social interactions for Blobbi companions. Other users
can feed, play, clean, and medicate a Blobbi they don't own via kind 1124
events. The owner's canonical 31124 state absorbs these interactions
through a checkpoint-based consolidation system with auto-sync on page load.

Key additions:
- Kind 1124 event schema with validation, parsing, and deterministic sort
- Social projection pipeline (read-only stat overlay from pending interactions)
- Owner-side consolidation into canonical state with checkpoint advancement
- Auto-sync hook (useCanonicalSync) triggered when owner opens /blobbi
- Social permission toggle (open/closed tag on 31124)
- Interaction UI: popover with item carousel on feed cards, detail pages,
  and the owner dashboard action bar
- Interaction reactions: facial expressions, sparkles, bubbles, floating
  hearts with phase-based animation system
- Activity tab showing interaction history with caretaker attribution
- BlobbiSocialActions component with egg gating and cooldown logic
- NIP.md documentation for the new kind
2026-04-27 23:02:19 -03:00
lemon c91bdc1d89 Harden community moderation foundation 2026-04-27 09:22:08 -07:00
lemon fec7021a7f Implement community moderation: reporting, content bans, and member bans
Add two-tier moderation system for hierarchical communities using kind 1984
events scoped via A tags. Authoritative bans use NIP-32 labels
([l, ban, moderation]) and require rank authority. Soft reports use standard
NIP-56 types and trigger content warnings for any valid member.

- Update NIP.md with ban/report classification, NIP-32 label schema, and
  reinstatement via kind 5
- Add parseCommunityReport(), resolveCommunityModeration() to communityUtils
- Update resolveMembership() to apply moderation overlay (remove banned members)
- Update useCommunityMembers to fetch kind 1984/5 and resolve moderation
- Add CommunityModerationContext for propagating moderation state
- Add CommunityReportDialog for soft reports (NIP-56 types)
- Add BanConfirmDialog for content removal and member bans with optional reason
- Add CommunityContentWarning component for click-to-reveal reported content
- Wire moderation into NoteMoreMenu (auto-detects community context)
- Wire moderation into CommunityDetailPage (member ban buttons, feed filtering)
- Add Remove content / Ban @user menu items to NoteMoreMenu
- Remove Copy Link to Post and Mention @user from NoteMoreMenu
- Move Mute Conversation into the mute/report section
2026-04-27 09:22:07 -07:00
sam 89a3562a1e update nips used doc 2026-04-27 18:33:33 +07:00
Alex Gleason b5b7424472 Harden Bitcoin zap implementation from code review
- Add 13 regression tests for Taproot address derivation, pubkey
  validation, npub→address, and mainnet address validation
- Validate pubkey hex format (/^[0-9a-fA-F]{64}$/) in
  nostrPubkeyToBitcoinAddress to fail fast on malformed input
- Match tapInternalKey against the signer's x-only pubkey in
  signPsbtLocal, per the BITCOIN-SIGNING.md spec ("inputs whose
  tapInternalKey does not match the signer's key MUST be left
  unchanged"). Throw if no owned inputs are found.
- Use >= DUST_LIMIT (not >) for change-output dust check, so a change
  of exactly 546 sats is preserved rather than donated to fees
- Extract formatBTC() helper into lib/bitcoin.ts; remove duplicated
  replace(/\.?0+$/, '') from WalletPage, SendBitcoinDialog, and
  BitcoinContentHeader
- Register kind 8333 ("Bitcoin zap") in CommentContext KIND_LABELS,
  CommentContext KIND_ICONS, NoteCard KIND_HEADER_MAP,
  signerWithNudge KIND_LABELS, and shellTitleForKind
- Disambiguate sign_psbt errors in NConnectSignerBtc: only re-wrap as
  "doesn't support sending Bitcoin" when the error message looks like
  a capability failure (unknown method, not implemented, etc.);
  propagate transient errors unchanged
- Show the recipient's derived Bitcoin address in OnchainZapContent
  so users can verify the destination before signing
- Clear knownUnsupportedBunkers on logout so a fresh login with an
  upgraded bunker isn't tainted by a previous session's rejection
- Reject self-zaps in verifyOnchainZap (sender == recipient)
- Update NIP.md to specify: change-output handling, amount-cap vs
  discard semantics, self-zap rejection, mempool/confirmation policy,
  and mainnet-only scope
- Delete unused useNsecAccess hook
2026-04-22 16:39:21 -05:00
Alex Gleason 0d637a55b1 Rename onchain zap kind 3043 → 8333
8333 is the Bitcoin mainnet P2P port, creating a clean semantic parallel
with NIP-57: kind 9735 (Lightning's P2P port) for Lightning zaps, kind
8333 for on-chain zaps. 3043 was the first free kind the generator
returned and carried no meaning.
2026-04-22 15:23:17 -05:00
Alex Gleason bddfe4b838 Add on-chain Bitcoin zaps as the default zap method
Introduce kind 3043, a new Nostr event that attests an on-chain Bitcoin
payment against a target event or profile. Because every Nostr pubkey
deterministically maps to a Taproot address, any user can receive an
on-chain zap without configuring lud06/lud16 — the zap button now
appears on every post whose author is not the current user.

Publishing flow: sender builds and broadcasts a Bitcoin transaction
paying the recipient's derived Taproot address, then publishes a
kind 3043 event with an `i` tag (`bitcoin:tx:<txid>`), the recipient's
`p`, the target's `e` / `a`, and a self-reported `amount` in sats.
Before displaying or counting a kind 3043 event clients verify the
referenced transaction on-chain and use the sum of outputs paying the
recipient's address as the authoritative amount, capping the sender's
claim at the verified value to prevent spoofing.

Lightning zaps remain available as an opt-in tab inside the zap dialog
whenever the author has a Lightning address configured; otherwise the
dialog is purely on-chain. Defaults favour on-chain: USD amount presets
($1 / $5 / $10 / $25 / $100), fee-speed selection, and a 3-step
form → confirm → success flow mirroring SendBitcoinDialog.
2026-04-22 15:17:09 -05:00
lemon e1d4939c81 add hierarchical communities protocol spec to NIP.md 2026-04-19 17:55:20 -07:00
sam 6d51f6eeac geo chat wip 2026-04-18 19:12:13 +05:45
sam 55fe82adf9 community stats 2026-04-18 16:28:41 +05:45
sam ced5d00163 ported actions/challenges 2026-04-18 13:38:50 +05:45
Mary Kate Fain 72a25d09aa feat: show tracks on playlist detail page with full playback and album support
- Add usePlaylistTracks hook to resolve a-tag refs into ordered track events
- Render track list in PlaylistDetail with Play All button and per-track
  playlist playback via playPlaylist()
- Support albums as playlists tagged with t:album, showing release date,
  label, and Disc3 icon
- Add All/Playlists/Albums filter toggle to MusicPlaylistsTab
- Show Album badge on playlist cards in grid views
- Add KIND_HEADER_MAP entries for music tracks (36787) and playlists (34139)
- Fix shellTitleForKind to return 'Playlist Details' for kind 34139
- Document music kinds and album convention in NIP.md
2026-04-17 15:25:06 -05:00
Chad Curtis a27ee3af86 Fix self-review findings: remove dead code, fix task progress display, fix hydration race
- Remove dead code: useSyncTaskCompletions, incrementInteractionTaskTags,
  getInteractionCount, getEvolveInteractionCount, unused lookup maps
- Fix task progress showing 0/N on load: compute event-based task counts
  directly from Nostr query results (authoritative) instead of relying
  solely on the evolution mission store which may not be hydrated yet.
  Use max(queryCount, missionCount) so progress displays immediately.
- Fix hydration race: useDailyMissions raw memo now waits for hydration
  before creating fresh missions, preventing overwrite of persisted
  evolution[] with empty array. Also preserve evolution missions across
  daily resets during hydration.
- Fix session store miss: use ensureSessionStore in incubation/evolution
  start so evolution missions are always populated even if the store
  hasn't been hydrated yet.
- Extract duplicate findMission to shared findEvolutionMission in
  evolution-missions.ts
- Document evolution[] field on kind 11125 in NIP.md
2026-04-16 16:17:57 -05:00
Alex Gleason c4354774ad Add Color Moments and Geocaching to community NIP specs, sort table by kind number 2026-04-01 18:05:52 -05:00
Alex Gleason 8a44f77fb1 Add community NIP specs to NIP.md (Letters, Weather, Blobbi)
Add a Community Kinds section to the overview table and a Community
NIP Specifications section with summaries and links to the full specs
maintained by Chad Curtis, Sam Thomson, and Danifra.
2026-04-01 17:56:50 -05:00
Chad Curtis 865fabce98 Add Letters inbox page with full letter composing from lief
- Port letter protocol (kind 8211, NIP-44 encrypted) from lief
- LettersPage at /letters with inbox and sent tabs
- ComposeLetterSheet with full stationery, font, frame, sticker, drawing support
- LetterCard with expand-to-read animation and deletion
- LetterPreferencesSection stored in encrypted settings (NIP-78)
- /settings/letters route for letter preferences
- Letters added to sidebar nav
- All letter lib utilities: letterTypes, letterUtils, colorUtils extensions, sanitizeSvg, svgDrawing
- StationeryBackground, StationeryPicker, FramePicker, StickerPicker, DrawingCanvas all ported
2026-03-26 16:11:29 -05:00
Alex Gleason 2326dac0b4 Add title font role to f tag spec in NIP.md
Extend the font tag format with a required role marker ("body" or
"title") to support a dedicated profile display-name font. Body tags
must be ordered before title tags for backward compatibility. Legacy
3-element f tags without a role are treated as body.
2026-03-25 13:18:07 -05:00
Alex Gleason 6569478fc4 Remove implementation notes from avatar shape NIP section 2026-03-13 20:48:03 -05:00
Alex Gleason 91b96984cb Make NIP.md implementation notes platform-generic (web, iOS, Android) 2026-03-13 20:47:38 -05:00
Alex Gleason 196f3dd64e Update NIP.md: shape field is emoji-only, remove predefined shape references 2026-03-13 20:47:07 -05:00
Alex Gleason 55e8075542 Support emoji as avatar shape: use any emoji as a mask over the avatar
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
2026-03-13 20:27:14 -05:00
Alex Gleason 9e03579d0f Fix heart shape: use polygon approximation instead of path() which requires pixel coordinates
clip-path: path() uses pixel coordinates, making a 0-1 normalized path
invisible. Replaced with a parametric heart curve sampled into a 50-point
polygon using percentage coordinates, consistent with all other shapes.
2026-03-13 17:29:03 -05:00
Alex Gleason 61ea5ff1fe Add heart shape and document kind 0 shape extension in NIP.md
- Add 'heart' as the 8th avatar shape using SVG path() clip-path with cubic beziers
- Document the kind 0 'shape' metadata extension in NIP.md with all 8 defined values,
  client behavior rules, and forward-compatibility guidance
2026-03-13 17:25:50 -05:00
Alex Gleason 9ea8788b92 Replace kind 16769 SavedFeedFilters with standard NIP-01 filters and variable tags
The proprietary SavedFeedFilters JSON schema in kind 16769 tab events was
non-interoperable. Replace it with standard NIP-01 filter objects that any
Nostr client can execute, plus a variable system (var tags) for dynamic
values like follow lists.

- Tab filters are now standard NIP-01 filters with optional NIP-50 search
- Variables ($name) in filters are resolved via var tags that extract tag
  values from referenced events (e.g. follow list pubkeys from kind 3)
- $me is the only runtime variable (profile owner's pubkey)
- Variables in arrays are expanded in-place (spliced)
- Add useResolveTabFilter hook for variable resolution with caching
- Update all consumers: ProfilePage, Feed, SearchPage, ContentSettings
- Remove SavedFeedFilters type entirely in favor of TabFilter
2026-03-06 01:28:09 -06:00
Chad Curtis 134236636a Profile tabs: kind 16769 Nostr event, manager modal, + dropdown, wider save button
Kind 16769 (profile tabs):
- src/lib/profileTabsEvent.ts: encode/decode kind 16769 — tab tags are
  ['tab', label, filtersJSON], no UUIDs, label is the stable key
- useProfileTabs: queries kind 16769 with authors filter (secure)
- usePublishProfileTabs: publishes kind 16769, invalidates cache
- useProfileSupplementary: now fetches kind 16769 alongside kind 3 and
  10001 in a single relay round-trip, seeds profile-tabs query cache
- NIP.md: documents kind 16769 schema

ProfileTabEditModal:
- Save button is now full-width with a Check icon (Cancel below)
- hideFrom + hideSort props on SavedFeedFiltersEditor used here — author
  is the profile owner implicitly, sort is irrelevant for profile tabs

ProfileTabsManagerModal:
- dnd-kit drag-to-reorder sortable list of custom tabs
- Per-row edit (pencil) and remove (trash) buttons
- Add custom tab button at the bottom
- Single Save button publishes the full updated list

ProfilePage tab bar (own profile):
- Custom tabs read from kind 16769 via useProfileTabs
- + button opens a DropdownMenu: core tabs listed as already-added
  (dimmed with checkmarks), Add custom tab at the bottom
- SlidersHorizontal pencil at end of row opens the manager modal
  (only shown when custom tabs exist)
- Per-tab onEdit pencil removed — manager handles all editing
- ProfileTab type aliased to CoreProfileTab to avoid collision with
  CustomProfileTab from profileTabsEvent
2026-03-05 22:53:44 -06:00
Alex Gleason c0c65047fa Update NIP.md kind numbers: 33891 -> 36767, 11667 -> 16767 2026-02-26 17:31:05 -06:00
Alex Gleason b5a59ac9a6 Remove 'contain' background mode from NIP.md and codebase, keep only cover and tile 2026-02-26 15:22:35 -06:00
Alex Gleason bc4cc80581 Simplify theme fonts to a single font (remove heading/body roles)
Reduce font complexity from two-role (title/body) to a single font
that applies globally to all text. Updates NIP.md f tag format from
["f", family, role, url] to ["f", family, url], removes ThemeFonts
wrapper in favor of a single ThemeFont on ThemeConfig.font, and
simplifies FontPicker to a single dropdown.
2026-02-26 14:57:41 -06:00
Alex Gleason 51304a3a7c Add font (f) and background (bg) tags to theme event spec
Replace the u tag with a richer bg tag using imeta-style key-value
pairs (url, mode, m required; dim, blurhash optional). Add f tag for
font declarations (family, role, and font file URL required). Both
tags are documented in a shared definitions section for kind 33891
and kind 11667.
2026-02-26 14:26:08 -06:00
Alex Gleason 14dcb8ce81 Add optional u tag for background image to both theme kinds 2026-02-26 12:45:05 -06:00
Alex Gleason ed8a1d59b5 Remove description and t tags from kind 33891 2026-02-26 12:40:15 -06:00
Alex Gleason 9d0efe76a3 Update kind 33891 to use c tags for hex colors instead of content JSON
Align theme definition event with kind 11667 by replacing the HSL
color token JSON in the content field with c tags using 6-digit hex
color codes and required markers (primary, text, background).
2026-02-26 12:39:19 -06:00
Alex Gleason 2c3260760a Update kind 11667 to use c tags for hex colors instead of content JSON
Replace the HSL color tokens in the content field with c tags using
6-digit hex color codes and required markers (primary, text, background).
Remove the a tag for theme attribution. Add optional title tag.
2026-02-26 12:35:56 -06:00
Mary Kate Fain 096096f685 Replace kind 30203 with new multi-theme NIP: kind 33891 (definitions) + kind 11667 (active profile theme)
- Theme definitions are addressable (multiple per user, each with title/description)
- Active profile theme is replaceable (one per user, copies tokens from source)
- Updated all hooks, components, and feed integration for new kinds
- Theme builder preview now shows accent color on streak flame, banner gradient, and repost action
2026-02-25 15:23:18 -06:00
Mary Kate Fain bac31a5b6d Add profile theme sharing: publish kind 30203, scoped themes on profiles, feed cards, and content settings 2026-02-25 13:49:55 -06:00