From e33d15f41f473182f4cd404a7386f43984d1fdfc Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Mon, 18 May 2026 13:44:46 -0400 Subject: [PATCH] =?UTF-8?q?Add=20Lightning=20tips=20(LNURL-pay=20=E2=86=92?= =?UTF-8?q?=20bolt11=20=E2=86=92=20QR)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A self-contained tip flow that works for any author whose kind-0 profile publishes a lud16 Lightning address or lud06 LNURL. Picks an amount, fetches a bolt11 invoice from the LNURL-pay endpoint, renders it as a scannable QR + copyable string. No NIP-57 zap request, no NWC, no WebLN — just the smallest path to actually moving sats. Phase A — LNURL helpers and tip modal: - Vendored qrcode-generator (~20 KB, MIT) as src/vendor/qrcode.min.js so the QR encoder runs offline with no CDN dependency. - lud16ToLnurlpUrl / lud06ToHttpUrl / resolveLnurlPayUrl pick the right endpoint from a profile. - fetchLnurlPayMetadata validates the payRequest tag and surfaces the min/max range; fetchLnurlInvoice requests an amount-locked bolt11. - renderQrInto auto-scales the typeNumber so long invoices still fit. - tipModal: amount-chip picker (21 / 100 / 500 / 1k / 5k / 21k / Custom), inline min/max validation, fetching state, then an invoice view with QR, lightning: deeplink, copyable bolt11, and a New amount / Done row. - Brought to front via the modal z-stack; ESC, backdrop, and × close it. Phase B — Feed card surface: - Every feed card whose author has a resolvable LN address gets a ⚡ Tip button in the actions row. No button when there's no address, so feeds stay clean. Phase C — Design preview surface: - Same conditional ⚡ Tip button in the design preview action row, next to Save JSON and Copy share link. Profile pages now treat the Lightning chip as the entry point — clicking the chip opens the tip modal instead of copying the address. Profiles with an address that fails to resolve fall back to the original copy chip. Co-Authored-By: Claude Opus 4.7 --- src/index.html | 327 ++++++++++++++++++++++++++++++++++++++- src/vendor/qrcode.min.js | 8 + 2 files changed, 331 insertions(+), 4 deletions(-) create mode 100644 src/vendor/qrcode.min.js diff --git a/src/index.html b/src/index.html index ed3ce2d..b12ba44 100644 --- a/src/index.html +++ b/src/index.html @@ -1582,6 +1582,16 @@ .profileAvatar { margin-top:-44px; } } + .tipAmounts { display:grid; grid-template-columns:repeat(auto-fit,minmax(72px,1fr)); gap:8px; margin:0 0 14px; } + .tipAmounts button { padding:10px 6px; font-size:.9rem; font-weight:600; cursor:pointer; border:1px solid var(--line); background:var(--bg); color:var(--ink); border-radius:8px; } + .tipAmounts button.active { background:var(--accent); color:#fff; border-color:var(--accent); } + .tipAmounts button:hover { filter:brightness(.97); } + .tipQr { display:flex; justify-content:center; align-items:center; background:#fff; padding:14px; border-radius:8px; margin:0 auto 14px; max-width:280px; } + .tipQr svg { display:block; width:240px; height:240px; } + .tipBolt11 { display:flex; gap:8px; align-items:center; margin-bottom:10px; padding:8px 10px; border:1px solid var(--line); border-radius:6px; background:var(--bg); } + .tipBolt11 code { flex:1; min-width:0; font-family:ui-monospace,SFMono-Regular,monospace; font-size:.72rem; word-break:break-all; max-height:3em; overflow:hidden; line-height:1.3; } + .tipBolt11 button { flex-shrink:0; } + .addIdentifiersDetails { } .addIdentifiersBtn { display: inline-flex; @@ -2737,6 +2747,48 @@ + + +