Commit Graph

4447 Commits

Author SHA1 Message Date
filemon 03dcc37083 Add pure segment-display helper for stage-based Blobbi status UI
Introduce getBlobbiStatDisplayState() — a read-only helper that derives
UI segment counts, care states, and badge/pulse flags from internal
1–100 stats without changing any gameplay behaviour.

Egg is always 'protected' with full segments. Baby maps to 4 segments
(urgent/attention/okay/good). Adult maps to 10 segments with wider
threshold bands. Values are clamped to STAT_MIN–STAT_MAX.

Includes 38 unit tests covering all boundary values, clamping, and
flag correctness.
2026-04-24 12:54:34 -03:00
Chad Curtis 6b9aeddb06 Clean up review findings from video/vine/pagination commits
- Remove orphaned JSDoc and dead isShort code path in VideoContent
- Unify vine mute state into shared vineGlobalMute module so mute
  preference carries between NoteCard feeds and VinesFeedPage
- Fix fetchNextPage race cascade by using a ref guard instead of
  putting isFetchingNextPage in the useCallback dep array
- Batch event ingestion in useStreamPosts so the event map is sorted
  and flushed to state once per page instead of per-event
- Don't permanently kill pagination on transient network errors
- Move sort modifiers into searchParts before the join for consistency
- Wrap full VideoGridCard/ShortThumb content in ContentWarningGuard
  so metadata doesn't leak when policy is blur
2026-04-24 10:11:32 -05:00
Chad Curtis 23e845ebc1 Render kind 22 shorts as vines in feeds with volume control
Kind 22 (Short-form Portrait Video) was rendering through VideoContent
with a shrunken max-width and a redundant "Short" badge, duplicating
the vine experience poorly. Now kind 22 shares the VineMedia component
with kind 34236, rendering full-width with play/pause and mute toggle.

- Unified isVine to match both kind 22 and 34236
- Removed isShortVideo flag and the small/badged VideoContent path
- Added mute/unmute button to VineMedia with shared module-level state
- Mute preference persists across shorts as you scroll through a feed
- Removed dead parseImeta function (replaced by parseVideoImeta)
2026-04-24 10:02:49 -05:00
Chad Curtis 5a80df05f5 Add infinite scroll pagination to search page
useStreamPosts previously fetched a single batch of 40 events with no
way to load more. Refactored to track the oldest event timestamp and
expose fetchNextPage/hasNextPage/isFetchingNextPage for cursor-based
pagination using the same NIP-50 search filter with an `until` param.

SearchPage now renders an IntersectionObserver sentinel below the post
list that triggers loading the next page when scrolled into view.
2026-04-24 09:51:50 -05:00
Chad Curtis cc3a5b3415 Fix vines always autoplay regardless of autoplayVideos setting
The autoplayVideos config controls inline video players in normal
feeds. Vines use snap-scroll where the active slide should always
autoplay — gating on this setting made vines not play at all when
the user had autoplay disabled.
2026-04-24 09:44:33 -05:00
filemon 9a48d039db Fix guide not resuming room arrow when leaving target room
The guide step effect only handled the forward transition (room →
item/action on entering the target room). Add the reverse: when the
step is item or action but the user has navigated away, revert to
the room step so the directional arrow resumes blinking.
2026-04-24 11:28:23 -03:00
Chad Curtis fdacb2029a Fix content warnings not applying to video and vine feed pages
VideosFeedPage and VinesFeedPage rendered their own card components
(VideoGridCard, ShortThumb, VineCard) without any content warning
checks, bypassing the ContentWarningGuard used elsewhere. Videos
with NIP-36 content-warning tags displayed without blur or filtering.

- Wrap VideoGridCard and ShortThumb thumbnails in ContentWarningGuard
- Add full-screen dark CW overlay to VineCard matching vine aesthetic
- Filter out CW events when contentWarningPolicy is "hide" in both pages
- Prevent video autoplay while CW overlay is shown in VineCard
2026-04-24 09:25:25 -05:00
filemon 1eb126bdf8 Make all stat icons trigger the guided-care flow
Previously only low-status (warning/critical) stat icons were clickable
and the guide auto-cleared when the stat recovered to normal. Now every
stat icon fires onGuide on tap regardless of status, and the guide is
only dismissed by completion events (item used, sleep started, or a
different stat clicked).

Changes:
- StatsCrown: remove the status-gate from onClick and cursor-pointer
- StatsCrown: add z-10 so stat icons layer above the Blobbi visual
- Blobbi animation wrapper: pointer-events-none so it cannot intercept
  taps on overlapping stat icons
- BlobbiDashboard: remove the effect that cleared the guide when the
  stat was normal; drop unused getStatStatus import
- Update comments and prop docs to reflect the new behaviour
2026-04-24 11:16:10 -03:00
filemon ca260497cc Merge branch 'main' into feat/status-guide 2026-04-24 10:24:54 -03:00
Chad Curtis 846c4f794a Merge branch 'audit/blobbi-adult-color-application-and-generation' into 'main'
Make Blobbi visual identity seed-driven and sync mirror tags

See merge request soapbox-pub/ditto!200
2026-04-24 04:04:48 +00:00
Chad Curtis 3a9f41892f Harden seed derivation: extract readSeedUint32, guard edge cases
- Extract readSeedUint32() for raw 32-bit reads instead of misusing
  deriveIndexFromSeed with max=0x100000000 (which was a no-op modulus).
  deriveIndexFromSeed is now only used for bounded array indexing.
- Guard deriveAdultFormFromSeed against NaN from short/invalid seeds.
- Guard adjustSeedForAdultType against indexOf returning -1 for
  unknown form values.
2026-04-23 22:54:12 -05:00
Chad Curtis 8b0eb97abb Fix redundant layout reflows and sanitize SVG color interpolation
Cache getBoundingClientRect() result in OverstimulationBlockOverlay
to avoid four forced reflows on the same element per activation.

Add sanitizeSvgColor() guard in generateAngerRiseEffect() so that
config.color is validated before interpolation into SVG stop-color
attributes, preventing injection if a future caller passes untrusted
color strings.
2026-04-23 22:39:27 -05:00
Chad Curtis 5463206d84 Fix mobile nav bar rendering during overstimulation zoom
Scroll to top before applying the zoom transform and restore the
saved scroll position after zoom-out completes. The transform on
#root creates a new containing block that breaks sticky positioning
of the mobile top/bottom nav bars when the user is scrolled down.
2026-04-23 22:31:32 -05:00
Chad Curtis ed637bc9df Compact reaction system: inline drain logic, trim verbosity
Delete useReactionDrain (120 lines) — the abstraction added more
complexity than it saved. Each hook now inlines a ~15-line rAF drain.

Rewrite all major files for density:
- useOverstimulationReaction: 375 → 175 lines
- useShakeReaction: 407 → 188 lines (was 205 with shared hook)
- shakeDetection: 222 → 93 lines
- OverstimulationBlockOverlay: 216 → 112 lines

Total branch diff: 1297 → 900 insertions.
2026-04-23 22:27:49 -05:00
Chad Curtis f465cb7347 Extract shared hooks to deduplicate reaction and SVG renderer code
- useFillLevelUpdate: shared recipe fingerprint + imperative gradient
  stop updates, extracted from BlobbiAdultSvgRenderer and
  BlobbiBabySvgRenderer (removed ~60 duplicated lines from each)

- useReactionDrain: shared rAF-based level drain loop with throttled
  React state push, extracted from useOverstimulationReaction and
  useShakeReaction (removed ~170 duplicated lines from each)

Net reduction: ~285 lines across the 4 consumer files.
2026-04-23 22:21:54 -05:00
Chad Curtis 49a5461fbe Fix zoom origin: query companion DOM element directly via data attribute
The previous approach put a ref on the pointer-events-auto wrapper div,
which is a full-width block element -- getBoundingClientRect returned
the page width, not Blobbi's position. Now we query the companion's
actual fixed-position container via [data-blobbi-companion] to get
the true visual bounding rect.
2026-04-23 22:14:06 -05:00
Chad Curtis 75f6283d9b Lower zoom origin to bottom edge of Blobbi's bounding box 2026-04-23 22:09:38 -05:00
Chad Curtis a144193cb4 Lower zoom origin to 80% of Blobbi's height 2026-04-23 22:09:18 -05:00
Chad Curtis 2ec57ad027 Lower zoom origin to 65% of Blobbi's height 2026-04-23 22:08:56 -05:00
Chad Curtis ff412bbb29 Crank overstimulation zoom to 5x for screen-hogging close-up 2026-04-23 22:07:43 -05:00
Chad Curtis 12d299a7ec Replace crumble effect with UI zoom + radial shockwave on overstimulation
When Blobbi hits max overstimulation, the entire UI now zooms toward
Blobbi's face (transform on #root) while a radial shockwave expands
from the companion and a red vignette dims the screen edges. On
recovery the zoom eases back out and the vignette fades.

The overlay is portaled onto document.body so it stays at viewport
scale while #root is scaled. Body overflow is hidden during the zoom
to prevent scrollbar flash.

Removes the canvas-based crumble/debris system (crumbleEngine.ts) in
favor of this simpler CSS-driven approach.
2026-04-23 22:05:30 -05:00
Chad Curtis 8fe0751a67 Add overstimulation visual feedback: shockwave, UI crumble, and debris
Replace the invisible click shield with a dramatic visual sequence when
Blobbi reaches max overstimulation: a radial shockwave expands from
Blobbi, a dark backdrop covers the UI (Blobbi stands alone in the void),
and canvas debris particles rain down like rubble. On recovery the
backdrop fades and debris converges back.

Also fix the debug bypass in useShakeReaction (true || threshold) that
shipped nausea on every shake regardless of hunger stat, and remove the
toast notification replaced by the visual overlay.
2026-04-23 21:55:28 -05:00
Chad Curtis 1b940b262c Merge branch 'main' of gitlab.com:soapbox-pub/ditto into feat/blobbi-click-overstimulation-reaction 2026-04-23 21:21:44 -05:00
Chad Curtis 6ea1d0da2b Merge branch 'fix/blobbi-deterministic-legacy-migration' into 'main'
Make legacy Blobbi migration deterministic

Closes #248

See merge request soapbox-pub/ditto!199
2026-04-24 02:18:58 +00:00
Chad Curtis 4dd487e0b2 Merge branch 'feat/improve-blobbi-companion-transitions' into 'main'
Add generic route-transition reaction for Blobbi companion

Closes #225

See merge request soapbox-pub/ditto!182
2026-04-24 02:09:45 +00:00
Chad Curtis 82f97aa1e2 Merge branch 'feat/blobbi-eyes-feed' into 'main'
Enable Blobbi eye tracking on feed cards (feed-only, with touch support)

Closes #249

See merge request soapbox-pub/ditto!201
2026-04-24 01:56:35 +00:00
Chad Curtis 1be0b3f101 Enable Blobbi eye tracking on detail page
Pass lookMode="follow-pointer" to BlobbiStateCard in PostDetailPage,
matching the feed card behavior so Blobbi eyes follow the cursor on the
detail view as well.
2026-04-23 20:53:35 -05:00
filemon 1afafb7abd Simplify guided-care UX: click low-status icon to start guide directly
Remove the intermediate popover + "Guide me" button step. Tapping a
low-status stat icon now calls onGuide immediately, starting the room
navigation or item/action highlight with zero friction.

Deletes the StatIndicatorWithHelp component (~120 lines) and its
associated imports (Popover, STAT_HELP_TEXT, Navigation, React state/
ref/callback/effect hooks). All stat icons now render through the
same StatIndicator; low-status icons get cursor-pointer and onClick
on their positioning wrapper.
2026-04-23 22:27:03 -03:00
filemon afce15d2d4 Add low-status guided UX with glow indicators and Guide me flow
Low-stat indicators now glow/pulse at warning and critical levels across
    BlobbiPage and widgets via a shared StatIndicator. On BlobbiPage, hovering
    (desktop) or tapping (mobile) a low stat shows contextual help with a
    Guide me button that visually leads the user to the correct room and
    item/action through a sequential glow chain: stat popover → room nav
    arrow → carousel arrow → target item/action.

    Implementation details:
    - Synchronized low-status icon animations via a shared CSS @property
      clock on the StatsCrown parent, so all icons pulse in phase regardless
      of mount timing
    - Split warning (stat-glow, 2s) and critical (stat-glow-critical, 2s)
      into separate keyframes with distinct visual weight
    - Guide glow uses a quick-blink-and-pause rhythm (1.1s cycle) distinct
      from the status pulse
    - Stable popover hover zone shared between trigger and content with
      paired open/close timers; onOpenAutoFocus prevented to avoid flicker
    - Guide cleanup on item use, action execution, new guide start, or stat
      recovery
    - ItemCarousel index only clamped when actually out of bounds, preventing
      guide visual instability from unrelated re-renders
    - Energy modeled as a first-class action target (sleep) rather than a
      special case
    - companionNeedsCare() uses calculateProjectedDecay() instead of raw
      persisted stats, consistent with all other low-status UI
2026-04-23 22:21:52 -03:00
lemon 348bbf6522 Invalidate query cache on world feed pull-to-refresh 2026-04-23 17:02:31 -07:00
lemon 9aa7366c74 Remove diversity cap from world feed, sort purely by recency 2026-04-23 17:02:31 -07:00
lemon f68f257234 Replace Ditto feed tab with World feed
- Add useWorldFeed hook combining infinite-scroll pagination with live
  streaming and 'X new posts' buffer/flush pattern
- World feed queries all country-tagged events globally with a diversity
  cap (max 4 posts per country per page)
- Live streaming via persistent relay subscription with scroll-aware
  buffering and highlight animation on flush
- Rename Ditto tab to World across Feed, ContentSettings, and useFeedTab
- Migrate localStorage key from ditto:showDittoFeed to agora:showWorldFeed
2026-04-23 17:02:31 -07:00
Chad Curtis 360a8c88e3 Merge branch 'fix-crysti' into 'main'
Fix crysti blobbi: add missing sparkle animations and fix pink facet path

Closes #252

See merge request soapbox-pub/ditto!202
2026-04-23 22:21:02 +00:00
Chad Curtis be4a741a73 Add RTL support for Arabic, Hebrew, and other RTL languages
Add dir="auto" to NoteContent, BioContent, and DM message bubbles
so the browser's Unicode Bidirectional Algorithm automatically detects
text direction from the first strong directional character.
2026-04-23 15:29:52 -05:00
Patrick PReis 589fb8ebba Fix crysti facet5 degenerate path (bottom-right purple section)
The path started and ended at the same point (100,105), making it a
triangle instead of a quadrilateral. The bottom-right hexagon vertex
(140,130) was missing entirely. Changed to trace center → right-mid →
bottom-right vertex → inner bottom-right point.

Fixed in all four locations: both .svg source files and both inlined
constants in adult-svg-data.ts.
2026-04-23 17:27:48 -03:00
Chad Curtis 0156a82629 Fix overflow menu rendering inside compose modal
The Poll/Spoiler popover opened side="bottom" from the toolbar, which
is near the bottom edge of the dialog.  Because the dialog container
uses overflow-hidden (needed for flex layout containment) and the
PortalContainerProvider portals content inside the dialog DOM, the
popover was clipped.

Switch to side="top" so the menu opens upward into the visible area
of the modal.
2026-04-23 15:25:44 -05:00
Patrick PReis 8497d87238 Sync crysti fixes into inlined SVG data constants
The app renders Blobbi SVGs from inlined string constants in
adult-svg-data.ts, not from the .svg source files. The previous commits
only fixed the source files. This syncs all three fixes into the
inlined CRYSTI_BASE and CRYSTI_SLEEPING constants:

- Add animateTransform groups to CRYSTI_BASE sparkle circles
- Fix self-intersecting pink facet path in both constants
- Match sleeping facet opacities to base values
2026-04-23 17:00:50 -03:00
Patrick PReis 787e0f6902 Match crysti sleeping facet opacities to base variant
The sleeping SVG had all six facet opacities reduced by 0.2 compared to
the base, making it look washed out. Only the eyes/mouth should differ
between states — the body colors should stay vibrant, consistent with
how bloomi handles its sleeping variant.
2026-04-23 15:27:39 -03:00
Patrick PReis 6ac7bdf826 Fix crysti blobbi: add missing sparkle animations and fix pink facet path
The base SVG sparkle circles were missing animateTransform tags, causing
them to render static instead of orbiting like the sleeping variant.
Wrapped them in two animated groups matching crysti-sleeping.

The top-left pink facet (crystiFacet2) had a self-intersecting path
(bowtie shape) that caused inconsistent fill rendering. Reordered the
vertices to trace a proper convex quadrilateral in both base and sleeping
SVGs.
2026-04-23 15:02:40 -03:00
sam 0240e77bf9 Merge branch 'main' into feat/dms 2026-04-23 12:18:16 +05:45
lemon b3b7bdd20c replace theme showcase with simple System/Light/Dark appearance setting
Remove the 'Make it yours' theme strip from the landing hero and the
ThemeStep from the signup/onboarding flow. Add an Appearance settings
page at /settings/appearance with three options (System, Light, Dark)
defaulting to System.
2026-04-22 18:38:13 -07:00
Alex Gleason cbfd4a1f60 Require two-tap confirmation for Bitcoin sends over $100
When the total debit (amount + network fee) crosses $100 USD, flip the
primary action into a confirmation affordance instead of silently
sending. Normal sub-threshold amounts are unchanged — still one tap.

OnchainZapContent: first tap arms a destructive-variant button,
second tap actually sends. Editing the amount or fee speed re-arms.

SendBitcoinDialog ConfirmView: adds a neutral informational note
("Sending $X — double-check the recipient and amount.") and flips
the Confirm & Send button to the destructive variant, so the second
click carries visible weight without extra friction.

Threshold and helper (`isLargeAmount`) live in lib/bitcoin.ts with
6 new tests covering the boundary, price-unavailable, and negative
input cases.
2026-04-22 17:14:03 -05:00
filemon 2a2ebd6a46 Enable Blobbi eye tracking on feed cards (feed-only, with touch support)
BlobbiStateCard gains an optional lookMode prop (default: 'forward'),
threaded through to BlobbiStageVisual. Only the NoteCard feed call site
passes 'follow-pointer'; post detail, embedded notes, and embedded naddr
keep the default forward gaze.

The global pointer listener in useBlobbiEyes now also tracks touchstart
and touchmove so the effect works on mobile.
2026-04-22 18:58:36 -03:00
Alex Gleason ef7af83e5d Remove fee-percentage warning from Bitcoin zap flow
The amber "Network fee is ~N% of your zap" message was alarmist and
added no useful signal — the user already sees the exact fee in the
Fee line above and the final total when they submit.

Regression-of: bddfe4b8
2026-04-22 16:49:24 -05: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 3805bf39a5 Replace technical jargon in Bitcoin-signing error copy
User-facing strings about signer capability referenced 'signPsbt',
'sign_psbt', 'PSBT', 'nsec', 'NIP-07', and 'NIP-46' — implementation
details a normal user shouldn't have to parse. Each site now says
'your browser extension doesn't support sending Bitcoin' (or the
bunker / generic variants), and points users at the 'secret key'
login option by its friendly name.

Changed sites:
  * OnchainZapContent — unsupported-capability panel
  * useOnchainZap — pre-send capability error
  * bitcoin-signers.ts — NBrowserSignerBtc and NConnectSignerBtc
    error strings (these surface as toasts in SendBitcoinDialog)
  * SendBitcoinDialog — 'Signing Not Available' panel and the
    in-mutation guard error

isSignerCapabilityError still matches the new copy (they all contain
"doesn't support"), so the capability-detection flow that flips the
UI from 'unknown' to 'unsupported' continues to work.

Regression-of: 008f3979
2026-04-22 16:06:00 -05:00
Alex Gleason 008f3979e1 Detect Bitcoin signer capability before submitting a zap
Previously, when a user's signer couldn't sign PSBTs, the Bitcoin zap
flow only discovered this after the user pressed Zap — surfacing a
toast after an otherwise-normal submission. The zap button was offered
as if it would work, and failure felt like a bug rather than a
capability limit.

Now useBitcoinSigner returns a three-state `capability`:
  * supported   — nsec login, or extension with window.nostr.signPsbt
                  present.
  * unsupported — extension without signPsbt, OR a bunker that has
                  already rejected sign_psbt once in this session.
  * unknown     — bunker login with no capability info yet (NIP-46
                  has no capability-discovery RPC). Attempt is allowed
                  and if it fails with a 'does not support' error, the
                  hook calls reportSignerUnsupported(pubkey) to flip
                  the capability to 'unsupported' for the rest of the
                  session. A DOM event broadcasts the change so
                  consumer hooks re-render without a shared store.

OnchainZapContent renders an explicit 'Bitcoin zaps aren't available'
panel whenever capability === 'unsupported', with copy tailored to the
login type (different hints for nsec/extension/bunker). Inside
useOnchainZap, capability errors no longer show the generic failure
toast — the UI replacement is the only feedback the user sees.

ZapDialog defaults to the Lightning tab when Bitcoin is unsupported
and Lightning is available, and auto-switches mid-session if a bunker
rejects sign_psbt while the dialog is open — so the user is never
stranded on an unusable tab.
2026-04-22 15:57:00 -05:00
Alex Gleason 01980918bc Simplify Bitcoin zap flow to match Lightning's simplicity
The Bitcoin zap dialog was heavier than the Lightning one, with a Review
step, a confirm screen, a balance card, a dropdown for transaction
speed, and a success view — all before you could actually send a zap.
The Lightning flow is presets → optional comment → Zap. Now Bitcoin is
the same shape.

Changes:
  * Drop the form→confirm→success wizard. Single screen, single button.
    The 'Zap' button does the whole thing; success closes the dialog
    via the existing onSuccess callback (the hook already shows a toast).
  * Remove the always-visible balance card. Balance only appears when
    the amount exceeds available funds (or funds are zero).
  * Collapse transaction speed into a one-line fee readout like
    'Fee ≈ $0.12 · ~30 min' that opens a popover of the 4 speed options
    when clicked. No dropdown taking up vertical space by default.
  * Drop the 'Paying to <address>' card, the 'transactions are final'
    warning, and the dedicated confirm screen — all redundant for a
    small zap flow.
  * Button label now reads 'Zap $5 · 5,123 sats' so users see both the
    fiat amount they chose and the sats they're committing, without
    needing a separate confirm screen to see either.

Fee-dominated warning becomes a single line of amber text instead of a
destructive alert; errors become a single line of destructive text.
Result is roughly half the vertical space and one click instead of three
to send a zap.
2026-04-22 15:43:00 -05:00
Alex Gleason ca63c21080 Rename 'on-chain' to 'Bitcoin' in UI copy
Every user-facing string that previously said 'on-chain' or 'onchain'
now says 'Bitcoin'. Lightning stays as 'Lightning'. Code identifiers
(hook names, types, query keys, component names, tab state values) are
unchanged — only text the user actually reads.

Changes:
  Tab label 'On-chain' → 'Bitcoin'
  Dialog subtitle 'Send Bitcoin on-chain' → 'Send Bitcoin'
  Balance label 'Your on-chain balance' → 'Your Bitcoin balance'
  Confirm warning 'On-chain transactions…' → 'Bitcoin transactions…'
  Success 'sent on-chain' → 'sent via Bitcoin'
  Toasts 'On-chain zap sent/failed' → 'Bitcoin zap sent/failed'
  Error 'on-chain wallet has no funds' → 'Bitcoin wallet has no funds'
  PSBT error 'send on-chain zaps' → 'send Bitcoin zaps'
  NIP-31 alt 'On-chain zap' → 'Bitcoin zap'
  Secondary Lightning subtitle → 'Send a Lightning payment…'
2026-04-22 15:38:14 -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