Back to Nostr Bubbles
SOURCE / PINNED RELEASE

Made of little things.

Nostr Bubbles

Release
80d99bc81c75…
Author-recorded commit
Not provided
License
Check the original source

The hash-verified playable HTML, which may contain bundled or minified code. This view does not execute it.

index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Nostr Bubbles</title>
    <script type="module" crossorigin>function e(){let e=window;if(!e.napplet?.theme)throw Error(`window.napplet.theme is unavailable -- runtime did not inject this domain`);return e.napplet.theme}function t(){return e().get()}function n(t){return e().onChanged(t)}function r(){let e=window;if(!e.napplet)throw Error(`window.napplet is unavailable -- runtime did not inject the napplet namespace`);return e.napplet}function i(e){let t=r()[e];if(!t)throw Error(`window.napplet.${String(e)} is unavailable -- runtime did not inject this domain`);return t}var a={subscribe(e,t,n,r){return i(`relay`).subscribe(e,t,n,r)},publish(e,t){return i(`relay`).publish(e,t)},publishEncrypted(e,t,n=`nip44`){return i(`relay`).publishEncrypted(e,t,n)},query(e){return i(`relay`).query(e)}},o={getPublicKey(){return i(`identity`).getPublicKey()},onChanged(e){return i(`identity`).onChanged(e)},getRelays(){return i(`identity`).getRelays()},getProfile(){return i(`identity`).getProfile()},getFollows(){return i(`identity`).getFollows()},getList(e){return i(`identity`).getList(e)},getZaps(){return i(`identity`).getZaps()},getMutes(){return i(`identity`).getMutes()},getBlocked(){return i(`identity`).getBlocked()},getBadges(){return i(`identity`).getBadges()}},s={get(){return i(`config`).get()},subscribe(e){return i(`config`).subscribe(e)},openSettings(e){i(`config`).openSettings(e)},registerSchema(e,t){return i(`config`).registerSchema(e,t)},onSchemaError(e){return i(`config`).onSchemaError(e)},get schema(){return i(`config`).schema}},c={info(){return i(`resource`).info()},bytes(e,t){return i(`resource`).bytes(e,t)},bytesMany(e,t){return i(`resource`).bytesMany(e,t)},bytesAsObjectURL(e){return i(`resource`).bytesAsObjectURL(e)}},l={getEvent(e,t){return i(`outbox`).getEvent(e,t)},query(e,t){return i(`outbox`).query(e,t)},subscribe(e,t){return i(`outbox`).subscribe(e,t)},publish(e,t){return i(`outbox`).publish(e,t)},resolveRelays(e){return i(`outbox`).resolveRelays(e)}},u={encodeNip19(e){return i(`common`).encodeNip19(e)},decodeNip19(e){return i(`common`).decodeNip19(e)},getProfile(e){return i(`common`).getProfile(e)},follows(){return i(`common`).follows()},follow(...e){return i(`common`).follow(...e)},unfollow(...e){return i(`common`).unfollow(...e)},react(e,t,n){return i(`common`).react(e,t,n)},report(e,t,n){return i(`common`).report(e,t,n)}},d={"--hg-color-scheme":`dark`,"--hg-bg-base":`#14110c`,"--hg-bg-surface":`#1a1a1a`,"--hg-bg-elevated":`#242016`,"--hg-bg-overlay":`#080706`,"--hg-border-default":`#4a453c`,"--hg-border-dim":`#3a3a3a`,"--hg-text-primary":`#f5f1e8`,"--hg-text-secondary":`#d8d0c0`,"--hg-text-muted":`#b8b1a4`,"--hg-text-dim":`#776f62`,"--hg-accent-green":`#9be564`,"--hg-accent-amber":`#e5c464`,"--hg-accent-red":`#ff7369`,"--hg-font-body":`ui-monospace, 'JetBrains Mono', SFMono-Regular, Menlo, Consolas, monospace`,"--hg-font-mono":`ui-monospace, 'JetBrains Mono', SFMono-Regular, Menlo, Consolas, monospace`},f={background:`--hg-bg-base`,text:`--hg-text-primary`,primary:`--hg-accent-green`};function p(e){return Math.max(0,Math.min(255,Math.round(e)))}function m(e){if(!e)return null;let t=/^#([0-9a-f]{6})$/i.exec(e.trim());if(!t)return null;let n=t[1];return[parseInt(n.slice(0,2),16),parseInt(n.slice(2,4),16),parseInt(n.slice(4,6),16)]}function h([e,t,n]){return`#${[e,t,n].map(e=>p(e).toString(16).padStart(2,`0`)).join(``)}`}function g(e,t,n){return e.map((e,r)=>e+(t[r]-e)*n)}function _([e,t,n]){let[r,i,a]=[e,t,n].map(e=>{let t=e/255;return t<=.03928?t/12.92:((t+.055)/1.055)**2.4});return .2126*r+.7152*i+.0722*a}function v(e){let t=document.documentElement.style;for(let[n,r]of Object.entries(e))t.setProperty(n,r);let n=e[`--hg-color-scheme`];(n===`light`||n===`dark`)&&(document.documentElement.dataset.hgTheme=n);for(let[n,r]of Object.entries(e)){let e=/^#([0-9a-f]{6})$/i.exec(r.trim());if(!e)continue;let i=e[1];t.setProperty(`${n}-rgb`,`${parseInt(i.slice(0,2),16)} ${parseInt(i.slice(2,4),16)} ${parseInt(i.slice(4,6),16)}`)}}function y(e){let t={},n=e.fonts?.body?.name;typeof n==`string`&&n.trim()&&(t[`--hg-font-body`]=n);let r=m(e.colors.background),i=m(e.colors.text),a=m(e.colors.primary);if(r&&i&&a){for(let[n,r]of Object.entries(f)){let i=e.colors[n];typeof i==`string`&&i.trim()&&(t[r]=i)}let n=_(r)>.56,o=n?[255,255,255]:[0,0,0],s=n?[48,42,30]:[255,255,255];t[`--hg-color-scheme`]=n?`light`:`dark`,t[`--hg-bg-surface`]=h(g(r,o,n?.44:.12)),t[`--hg-bg-elevated`]=h(g(r,o,n?.68:.2)),t[`--hg-bg-overlay`]=h(g(r,o,n?.82:.48)),t[`--hg-border-default`]=h(g(r,s,n?.2:.28)),t[`--hg-border-dim`]=h(g(r,s,n?.12:.18)),t[`--hg-text-secondary`]=h(g(i,r,.32)),t[`--hg-text-muted`]=h(g(i,r,.48)),t[`--hg-text-dim`]=h(g(i,r,.64)),t[`--hg-accent-amber`]=h(n?g(a,[245,158,11],.72):g(a,[229,196,100],.72)),t[`--hg-accent-red`]=h(n?g(a,[220,38,38],.76):g(a,[255,115,105],.76))}return t}function b(){if(v(d),!globalThis.napplet?.theme)return{close:()=>void 0};try{t().then(e=>v(y(e))).catch(()=>void 0)}catch{}let e=null;try{e=n(e=>v(y(e)))}catch{}return{close:()=>e?.close()}}var ee=b;function te(e){let t=ne.get(e);if(t)return t;let n=document.createElement(`canvas`);n.width=768,n.height=768;let r=n.getContext(`2d`);if(!r)return``;r.textAlign=`center`,r.textBaseline=`middle`,r.font=`512px serif`,r.fillText(e,384,384);let{data:i,width:a,height:o}=r.getImageData(0,0,768,768),s=o,c=0,l=a,u=0;for(let e=0;e<o;e+=1)for(let t=0;t<a;t+=1)i[(e*a+t)*4+3]>25&&(s=Math.min(s,e),c=Math.max(c,e),l=Math.min(l,t),u=Math.max(u,t));if(u<l||c<s)return``;let d=u-l+1,f=c-s+1;d>f?(s=Math.max(0,s-Math.floor((d-f)/2)),f=d):f>d&&(l=Math.max(0,l-Math.floor((f-d)/2)),d=f);let p=document.createElement(`canvas`);p.width=256,p.height=256;let m=p.getContext(`2d`);if(!m)return``;m.drawImage(n,l,s,d,f,0,0,256,256);let h=m.getImageData(0,0,256,256);for(let e=0;e<h.data.length;e+=4)h.data[e]=255,h.data[e+1]=255,h.data[e+2]=255;m.putImageData(h,0,0);let g=p.toDataURL(`image/png`);return ne.set(e,g),g}var ne=new Map,x={sourceMode:`contacts`,bubbleDensityMode:`auto`,bubbleTargetCount:44,enableReactions:!0,includeZaps:!0,includeOnchainZaps:!0,zapBreaksBubbles:!0},re=3e4,ie=.18,ae=[28,74],oe=[34,58],se=[15,28],ce=[18,30],le=1850,ue=Array.from({length:9},(e,t)=>t*Math.PI*2/9);function S(e,t,n){return Math.min(n,Math.max(t,e))}function C(e,t){return e+Math.random()*(t-e)}function de(e,t){return Math.round(S(e*t/45e3,18,72))}function fe(e){return e===`root`?oe:e===`zap`?se:e===`reaction`?ce:ae}function w(e){return e===`note`||e===`reply`||e===`root`}function pe(e){return Math.PI*e*e}function me(e,t,n){let r=t/Math.max(n,1/(re/1e3))*(e===`root`?1.24:e===`zap`?.95:e===`reaction`?.26:1)*1e3;return e===`root`?S(r,14e3,7e4):e===`zap`?S(r,12e3,5e4):e===`reaction`?S(r,3500,12e3):S(r,8e3,6e4)}function he(e,t,n){let r=n*ie,i=t;for(let t of e)w(t.role)&&(i+=pe(t.radius));if(i<=r)return e;let a=e.filter(e=>w(e.role)).sort((e,t)=>e.createdAt-t.createdAt),o=new Set;for(let e of a){if(i<=r)break;o.add(e.id),i-=pe(e.radius)}return o.size>0?e.filter(e=>!o.has(e.id)):e}function ge(e,t){let n=Math.max(1,Math.floor(t)),r=[];for(let t=0;t<e.length;t+=n)r.push(e.slice(t,t+n));return r}var _e=9735,ve=8333,ye=/^[0-9a-f]{64}$/i;function T(e){return typeof e==`string`&&ye.test(e)}function be(e){if(typeof e==`string`&&e.trim())try{let t=new URL(e.includes(`://`)?e:`wss://${e}`);return t.protocol!==`wss:`&&t.protocol!==`ws:`?void 0:(t.hash=``,t.search=``,t.toString().replace(/\/$/,``))}catch{return}}function xe(e){let t=e[1];if(!T(t))return;let n=e[4];return{eventId:t.toLowerCase(),relayHint:be(e[2]),authorHint:T(n)?n.toLowerCase():void 0}}function Se(e){let t=e.tags.filter(e=>e[0]===`e`&&T(e[1])),n=t.find(e=>e[3]===`root`)??t[0],r=t.find(e=>e[3]===`reply`)??t.at(-1),i=n?xe(n):void 0,a=r?xe(r):void 0;if(t.length!==0)return i?{root:i,reply:a?.eventId===e.id?void 0:a}:void 0}function Ce(e){if(e)try{let t=new URL(e);return t.protocol===`https:`?t.toString():void 0}catch{return}}function we(e){let t=e.tags.filter(e=>e[0]===`e`&&T(e[1]));return(t.find(e=>e[3]===`root`)??t.find(e=>e[3]===`reply`)??t.find(e=>e[3]===`mention`)??t.at(-1))?.[1]?.toLowerCase()}function Te(e){let t=e.trim();return!t||t===`+`?`❤️`:t===`-`?`👎`:Array.from(t).slice(0,4).join(``)||`❤️`}function Ee(e){let t=E(e,`P`);if(T(t))return t.toLowerCase();let n=E(e,`description`);if(typeof n==`string`)try{let e=JSON.parse(n);return T(e.pubkey)?e.pubkey.toLowerCase():void 0}catch{return}}function De(e){let t=[E(e,`amount`)],n=E(e,`description`);if(typeof n==`string`)try{let e=JSON.parse(n).tags;if(Array.isArray(e)){let n=e.find(e=>Array.isArray(e)&&e[0]===`amount`&&typeof e[1]==`string`);t.push(n?.[1])}}catch{}for(let e of t){if(typeof e!=`string`)continue;let t=Number.parseInt(e,10);if(Number.isFinite(t)&&t>0)return Math.max(1,Math.round(t/1e3))}return je(E(e,`bolt11`))}function Oe(e){let t=E(e,`amount`);if(typeof t!=`string`)return;let n=Number.parseInt(t,10);return Number.isFinite(n)&&n>0?n:void 0}function ke(e){return e.tags.some(([t,n])=>t===`p`&&n===e.pubkey)}function Ae(e){return e.replace(/\s+/g,` `).trim()||`Empty note`}function je(e){if(typeof e!=`string`)return;let t=e.toLowerCase().match(/^ln(?:bc|tb|bcrt)(\d+)([munp]?)/);if(!t)return;let n=Number.parseInt(t[1],10);if(!Number.isFinite(n)||n<=0)return;let r=t[2],i=r===`m`?n*1e5:r===`u`?n*100:r===`n`?n/10:r===`p`?n/1e4:n*1e8;return Math.max(1,Math.round(i))}function E(e,t){return e.tags.find(e=>e[0]===t)?.[1]}var D=`data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 160 160%22%3E%3Cdefs%3E%3CradialGradient id=%22g%22 cx=%2235%25%22 cy=%2225%25%22 r=%2275%25%22%3E%3Cstop offset=%220%25%22 stop-color=%22%23f0f9ff%22/%3E%3Cstop offset=%2245%25%22 stop-color=%22%239be564%22/%3E%3Cstop offset=%22100%25%22 stop-color=%22%23e5c464%22/%3E%3C/radialGradient%3E%3C/defs%3E%3Crect width=%22160%22 height=%22160%22 rx=%2280%22 fill=%22url(%23g)%22/%3E%3Ccircle cx=%2280%22 cy=%2265%22 r=%2228%22 fill=%22white%22 fill-opacity=%22.9%22/%3E%3Cpath d=%22M36 134c8-28 26-43 44-43s36 15 44 43%22 fill=%22white%22 fill-opacity=%22.78%22/%3E%3C/svg%3E`,Me=500,Ne=400,Pe=800,Fe=8,Ie=200,O=[`wss://relay.ditto.pub`,`wss://relay.primal.net`,`wss://relay.damus.io`],Le=ee(),k={app:G(`#app`),effects:G(`#effects`),bubbleLayer:G(`#bubbleLayer`),footerBar:G(`#footerBar`),status:G(`#status`),message:G(`#message`),contactCount:G(`#contactCount`),noteCount:G(`#noteCount`),satsCount:G(`#satsCount`),orbCount:G(`#orbCount`),settingsOverlay:G(`#settingsOverlay`),closeSettingsButton:G(`#closeSettingsButton`),openSettingsButton:G(`#openSettingsButton`),restartButton:G(`#restartButton`),emptyState:G(`#emptyState`)},Re=null,ze=null,A=[],j=0,M=0,Be=0,Ve,N=performance.now(),P=`connecting`,F=0,He=0,Ue=0,I=0,L={...x},R=[],z=new Map,B=new Set,V=new Map,H=new Set,U=new Set,W=[];function G(e){let t=document.querySelector(e);if(!t)throw Error(`Missing required element: ${e}`);return t}function K(e){return!!globalThis.napplet?.[e]}function We(e){let t=e.bubbleDensityMode===`manual`?`manual`:`auto`;return{sourceMode:e.sourceMode===`contacts`?`contacts`:`popular`,bubbleDensityMode:t,bubbleTargetCount:S(Number(e.bubbleTargetCount??x.bubbleTargetCount),8,96),enableReactions:e.enableReactions!==!1,includeZaps:e.includeZaps!==!1,includeOnchainZaps:e.includeOnchainZaps!==!1,zapBreaksBubbles:e.zapBreaksBubbles!==!1}}function q(e,t){P=e,k.status.className=`footer-status status-${e}`,k.status.textContent=e.replace(`-`,` `),k.message.textContent=t}function J(){k.contactCount.textContent=F.toLocaleString(),k.noteCount.textContent=He.toLocaleString(),k.satsCount.textContent=I.toLocaleString(),k.orbCount.textContent=R.length.toLocaleString(),k.emptyState.classList.toggle(`hidden`,R.length>0||P===`live`)}function Ge(){let e=k.app.getBoundingClientRect();return{width:Math.max(160,e.width),height:Math.max(160,e.height)}}function Ke(e,t){return L.bubbleDensityMode===`auto`?de(e,t):L.bubbleTargetCount}function qe(){for(let e of A)e.close();A=[],j=0}function Je(){qe(),U.clear(),B.clear(),H.clear(),V.clear(),W=[],R=[],He=0,Ue=0,I=0,J()}async function Y(){let e=++M;if(Je(),F=0,q(`connecting`,L.sourceMode===`contacts`?`Reading shell identity and follows...`:`Opening popular relay streams...`),J(),L.sourceMode!==`contacts`){Xe(e);return}if(!K(`identity`)||!K(`outbox`)){q(`error`,`This shell did not grant NAP-IDENTITY and NAP-OUTBOX.`);return}let t=``;try{t=await o.getPublicKey()}catch(e){q(`error`,e instanceof Error?e.message:`Identity lookup failed.`);return}if(e!==M)return;if(!t){q(`no-identity`,`Connect an identity in the shell to watch your one-hop contact orbit.`);return}let n=[];try{n=[...new Set(await o.getFollows())]}catch(e){q(`error`,e instanceof Error?e.message:`Could not load follows from NAP-IDENTITY.`);return}if(e!==M)return;if(F=n.length,J(),n.length===0){q(`no-contacts`,`Your identity has no contacts to watch.`);return}let r=[1,7];L.includeZaps&&r.push(_e),L.includeOnchainZaps&&r.push(ve);let i=Math.floor(Date.now()/1e3)-Fe,a=ge(n,Ie);q(`connecting`,`Opening ${a.length.toLocaleString()} contact batch stream${a.length===1?``:`s`}...`);for(let t of a){if(e!==M)return;Ye(t,r,i,e)}}function Ye(e,t,n,r){let i=[{kinds:t,authors:e,since:n,limit:60}],a={authors:e,timeoutMs:5e3},o=l.subscribe(i,a);j+=1,o.on(`event`,e=>{r===M&&(q(`live`,`Watching ${F.toLocaleString()} one-hop contacts through NAP-OUTBOX.`),Qe(e.event))}),o.on(`closed`,()=>{r===M&&(j=Math.max(0,j-1),j===0&&q(`offline`,`The live stream closed. Try restarting.`))}),A.push(o),(async()=>{try{let{events:e}=await l.query(i,a);if(r!==M)return;for(let t of e)Qe(t.event);q(`live`,`Watching ${F.toLocaleString()} one-hop contacts through NAP-OUTBOX.`)}catch{r===M&&P===`connecting`&&q(`offline`,`Initial OUTBOX scan failed; waiting for live events.`)}})()}function Xe(e){if(!K(`relay`)){q(`error`,`This shell did not grant NAP-RELAY for popular relay mode.`);return}let t=[1,7];L.includeZaps&&t.push(_e),L.includeOnchainZaps&&t.push(ve);let n=[{kinds:t,since:Math.floor(Date.now()/1e3)-Fe,limit:120}];F=O.length,J();for(let t of O){let r=a.subscribe(n,t=>{e===M&&(q(`live`,`Watching ${O.length.toLocaleString()} popular relays through NAP-RELAY.`),Qe(t.event))},()=>{e===M&&P===`connecting`&&q(`live`,`Watching ${O.length.toLocaleString()} popular relays through NAP-RELAY.`)},{relay:t});A.push(r)}}function Ze(e){if(U.has(e))return!1;for(U.add(e);U.size>Pe;){let e=U.values().next().value;if(!e)break;U.delete(e)}return!0}function Qe(e){Ze(e.id)&&(e.kind===1?$e(e):e.kind===7&&L.enableReactions?et(e):e.kind===9735&&L.includeZaps?tt(e):e.kind===8333&&L.includeOnchainZaps&&nt(e))}function $e(e){He+=1,J();let t=Se(e),n=t?`reply`:`note`,r=fe(n),i=C(r[0],r[1]);X(e,{role:n,radius:i,rootEventId:t?.root.eventId,replyEventId:t?.reply?.eventId}),t?.root&&t.root.eventId!==e.id&&at(t.root,{role:`root`,maxRadius:i}),t?.reply&&t.reply.eventId!==e.id&&t.reply.eventId!==t.root.eventId&&at(t.reply,{role:`reply`,rootEventId:t.root.eventId,maxRadius:i})}function et(e){let t=we(e);t&&R.some(e=>e.eventId===t&&e.role!==`reaction`&&e.role!==`zap`)&&X({...e,content:Te(e.content)},{role:`reaction`,rootEventId:t})}function tt(e){let t=Ee(e),n=De(e);t&&n&&(Ue+=1,I+=n,J(),X({...e,pubkey:t,content:``},{role:`zap`,zapAmountSats:n}))}function nt(e){let t=Oe(e);t&&!ke(e)&&(Ue+=1,I+=t,J(),X({...e,content:``},{role:`zap`,zapAmountSats:t}))}async function X(e,t={}){let n=z.get(e.pubkey),r=t.role===`zap`?`${wt(t.zapAmountSats??0)} sats`:Ae(e.content);if(n){Z(e,n.imageUrl,n.shape,r,t);return}if(B.has(e.pubkey)&&!t.reuseExisting){Z(e,D,void 0,r,t);return}B.add(e.pubkey);try{let n=await u.getProfile(e.pubkey),i=typeof n.profile?.picture==`string`?n.profile.picture:void 0,a=typeof n.profile?.shape==`string`?n.profile.shape:void 0,o=await rt(i);o.shape=a,it(e.pubkey,o),Z(e,o.imageUrl,a,r,t)}catch{let n={imageUrl:D};it(e.pubkey,n),Z(e,D,void 0,r,t)}finally{B.delete(e.pubkey)}}async function rt(e){let t=Ce(e);if(!t||!K(`resource`))return{imageUrl:D};try{let e=await c.bytes(t),n=URL.createObjectURL(e);return{imageUrl:n,objectUrl:n}}catch{return{imageUrl:D}}}function it(e,t){for(z.set(e,t);z.size>Me;){let e=z.keys().next().value;if(!e)break;let t=z.get(e);t?.objectUrl&&URL.revokeObjectURL(t.objectUrl),z.delete(e)}}async function at(e,t){let n=V.get(e.eventId);if(n){X(n,{...t,reuseExisting:!0});return}if(!(n===null||H.has(e.eventId))){H.add(e.eventId);try{let n=(await l.getEvent(e.eventId,{author:e.authorHint,timeoutMs:3e3})).result?.event;for(V.set(e.eventId,n??null);V.size>Ne;){let e=V.keys().next().value;if(!e)break;V.delete(e)}n&&X(n,{...t,reuseExisting:!0})}finally{H.delete(e.eventId)}}}function Z(e,t,n,r,i={}){let{width:a,height:o}=Ge(),s=i.role??`note`,c=fe(s),l=Math.min(i.radius??C(c[0],c[1]),i.maxRadius??1/0),u=performance.now(),d=Ke(a,o);W=[...W,u].filter(e=>u-e<=re);let f=me(s,d,W.length/(re/1e3))*C(.88,1.12),p=Math.min(112,d+12);if(i.reuseExisting){let i=R.find(t=>t.eventId===e.id&&t.role===s);if(i){i.expiresAt=Math.max(i.expiresAt,u+f),i.messagePreview=r,i.imageUrl=t,i.shape=n,J();return}}let m=R;if(s===`root`){let e=m.filter(e=>e.role===`root`);e.length>=18&&(m=m.filter(t=>t.id!==e[0]?.id))}let h=w(s)?pe(l):0;m=h>0?he(m,h,a*o):m;let g=Math.random()>.5,_=s===`root`?C(36,96):s===`zap`?C(620,1040):s===`reaction`?C(360,720):C(70,210),v=s===`zap`?C(-Math.PI*.32,Math.PI*.32):s===`reaction`?C(-Math.PI*.11,Math.PI*.11):C(0,Math.PI*2),y=g?v:Math.PI-v,b=s===`zap`?g?-l*C(2.6,6):a+l*C(2.6,6):s===`reaction`?g?-l*C(3.2,8):a+l*C(3.2,8):C(l,Math.max(l,a-l)),ee=s===`zap`||s===`reaction`?C(l+80,Math.max(l+80,o-l)):C(l+84,Math.max(l+84,o-l)),te={id:`${e.id}-${s}-${u}-${Math.random().toString(36).slice(2)}`,eventId:e.id,pubkey:e.pubkey,imageUrl:t,shape:n,messagePreview:r,x:b,y:ee,vx:Math.cos(s===`zap`||s===`reaction`?y:v)*_,vy:Math.sin(s===`zap`||s===`reaction`?y:v)*_,radius:l,createdAt:u,expiresAt:u+f,hue:s===`root`?C(34,62):s===`zap`?C(42,55):s===`reaction`?C(280,340):C(88,210),role:s,rootEventId:i.rootEventId,replyEventId:i.replyEventId,zapAmountSats:i.zapAmountSats,trail:s===`zap`?[{x:b,y:ee,time:u}]:void 0,enteredViewport:s!==`zap`&&void 0,targetSpeed:s===`zap`?_:void 0};R=[...m.slice(Math.max(0,m.length-p+1)),te],J()}function ot(e){let t=Math.min(.032,(e-(Ve??e))/1e3);Ve=e,N=e;let{width:n,height:r}=Ge(),i=R.filter(t=>t.expiresAt>e).map(e=>({...e,trail:e.trail?[...e.trail]:void 0}));for(let a of i){if(a.x+=a.vx*t,a.y+=a.vy*t,a.role===`reaction`){let t=Math.max(180,a.radius*8);(a.x<-t||a.x>n+t||a.y<-t||a.y>r+t)&&(a.expiresAt=e);continue}if(a.role===`zap`){let i=a.x-a.radius>=0&&a.x+a.radius<=n&&a.y-a.radius>=0&&a.y+a.radius<=r;if(a.enteredViewport||=i,a.enteredViewport&&st(a,n,r),a.targetSpeed&&a.enteredViewport){let e=Math.hypot(a.vx,a.vy);if(e>.1&&e<a.targetSpeed){let n=e+(a.targetSpeed-e)*(1-Math.exp(-.5*t));a.vx*=n/e,a.vy*=n/e}}a.trail=[...a.trail??[],{x:a.x,y:a.y,time:e}].filter(t=>e-t.time<=le).slice(-34);continue}st(a,n,r)}ct(i,e),R=i,ut(),Be=window.requestAnimationFrame(ot)}function st(e,t,n){e.x-e.radius<0?(e.x=e.radius,e.vx=Math.abs(e.vx)):e.x+e.radius>t&&(e.x=t-e.radius,e.vx=-Math.abs(e.vx)),e.y-e.radius<0?(e.y=e.radius,e.vy=Math.abs(e.vy)):e.y+e.radius>n&&(e.y=n-e.radius,e.vy=-Math.abs(e.vy))}function ct(e,t){for(let n=0;n<e.length;n+=1)for(let r=n+1;r<e.length;r+=1){let i=e[n],a=e[r];if(i.role===`reaction`||a.role===`reaction`||i.explodingAt!==void 0||a.explodingAt!==void 0)continue;let o=a.x-i.x,s=a.y-i.y,c=Math.hypot(o,s)||.001,l=i.radius+a.radius;if(c>=l)continue;let u=o/c,d=s/c,f=l-c,p=i.radius*i.radius+a.radius*a.radius,m=i.role===`zap`?0:a.radius*a.radius/p,h=a.role===`zap`?0:i.radius*i.radius/p;i.x-=u*f*m,i.y-=d*f*m,a.x+=u*f*h,a.y+=d*f*h;let g=(a.vx-i.vx)*u+(a.vy-i.vy)*d;if(g>=0)continue;let _=i.role===`zap`||a.role===`zap`?1.08:.96,v=i.radius*i.radius,y=a.radius*a.radius,b=-(1+_)*g/(1/v+1/y);lt(i,a,t),i.vx-=b*u/v,i.vy-=b*d/v,a.vx+=b*u/y,a.vy+=b*d/y}}function lt(e,t,n){if(!L.zapBreaksBubbles)return;let r=e.role===`zap`?e:t.role===`zap`?t:null,i=r===e?t:r===t?e:null;if(!r||!i||!w(i.role))return;let a=Math.hypot(r.vx,r.vy);r.targetSpeed==null||a<r.targetSpeed*.9||(i.cracks=(i.cracks??0)+1,i.cracks>=2&&(i.explodingAt=n,i.expiresAt=n+560,i.vx*=.18,i.vy*=.18))}function ut(){dt(),ht(),J()}function dt(){let e=mt(),t=R.filter(e=>e.role===`zap`&&e.trail&&e.trail.length>1);k.effects.replaceChildren(Q(`defs`,{},[Q(`linearGradient`,{id:`thread-string-gradient`,x1:`0`,x2:`1`,y1:`0`,y2:`0`},[Q(`stop`,{offset:`0%`,"stop-color":`rgba(229,196,100,.85)`}),Q(`stop`,{offset:`100%`,"stop-color":`rgba(155,229,100,.72)`})])]),...t.flatMap(ft),...e.map(pt))}function ft(e){let t=e.trail??[],n=Q(`g`,{opacity:String(Math.min(.86,Math.max(0,(e.expiresAt-N)/1200)))});for(let r=1;r<t.length;r+=1){let i=t[r-1],a=t[r],o=r/(t.length-1);n.append(Q(`line`,{x1:i.x,y1:i.y,x2:a.x,y2:a.y,stroke:`hsla(${e.hue}, 96%, 63%, ${.03+o*.22})`,"stroke-linecap":`round`,"stroke-width":e.radius*(.28+o*1.08)}),Q(`line`,{x1:i.x,y1:i.y,x2:a.x,y2:a.y,stroke:`hsla(${e.hue}, 100%, 72%, ${.08+o*.72})`,"stroke-linecap":`round`,"stroke-width":Math.max(2.5,e.radius*(.08+o*.4))}))}return[n]}function pt({from:e,to:t,relation:n,id:r}){let i=Math.min(Math.min(1,Math.max(0,(e.expiresAt-N)/2200)),Math.min(1,Math.max(0,(t.expiresAt-N)/2200)),n===`root`?.82:.68),a=(e.x+t.x)/2,o=(e.y+t.y)/2-Math.min(88,Math.hypot(t.x-e.x,t.y-e.y)*.12);return Q(`g`,{opacity:i,"data-id":r},[Q(`path`,{d:`M ${e.x} ${e.y} Q ${a} ${o} ${t.x} ${t.y}`,fill:`none`,stroke:n===`root`?`url(#thread-string-gradient)`:`rgba(168,85,247,.72)`,"stroke-linecap":`round`,"stroke-width":n===`root`?2.5:2,"stroke-dasharray":n===`root`?`7 9`:`3 8`}),Q(`circle`,{cx:e.x,cy:e.y,r:n===`root`?4:3.5,fill:n===`root`?`rgba(229,196,100,.9)`:`rgba(168,85,247,.8)`}),Q(`circle`,{cx:t.x,cy:t.y,r:3,fill:`rgba(155,229,100,.8)`})])}function mt(){let e=new Map,t=new Map;for(let n of R)n.role===`root`&&e.set(n.eventId,n),n.role!==`zap`&&n.role!==`reaction`&&t.set(n.eventId,n);return R.flatMap(n=>{if(n.role!==`reply`)return[];let r=[],i=n.rootEventId?e.get(n.rootEventId):void 0,a=n.replyEventId&&n.replyEventId!==n.rootEventId?t.get(n.replyEventId):void 0;return i&&i.id!==n.id&&r.push({id:`${i.id}-${n.id}-root`,from:i,to:n,relation:`root`}),a&&a.id!==n.id&&r.push({id:`${a.id}-${n.id}-reply`,from:a,to:n,relation:`reply`}),r})}function ht(){k.bubbleLayer.replaceChildren(...R.map(gt))}function gt(e){let t=N-e.createdAt,n=Math.min(1,Math.max(0,(e.expiresAt-N)/2200)),r=Math.min(1,t/450),i=Math.min(r,n),a=document.createElement(`div`);return a.className=`bubble bubble-${e.role}`,a.style.width=`${e.role===`reaction`?e.radius*5.4:e.radius*2}px`,a.style.height=`${e.role===`reaction`?e.radius*2.45:e.radius*2}px`,a.style.transform=`translate3d(${e.x-e.radius}px, ${e.y-e.radius}px, 0)`,a.style.opacity=`${e.role===`reaction`?i*.82:i}`,a.style.filter=`drop-shadow(0 0 ${Math.round(e.radius*(e.role===`zap`?1.25:e.role===`reaction`?1.9:.62))}px hsla(${e.hue}, 95%, 62%, ${e.role===`zap`||e.role===`reaction`?`.9`:`.55`}))`,e.role===`reaction`?_t(a,e):vt(a,e),a}function _t(e,t){let n=yt(t,`Nostr reaction avatar`),r=document.createElement(`div`);r.className=`reaction-emoji`,r.textContent=t.messagePreview.slice(0,4),e.append(n,r)}function vt(e,t){let n=t.explodingAt!==void 0,r=n?Math.min(1,Math.max(0,(N-(t.explodingAt??N))/560)):0,i=yt(t,`Nostr ${t.role} orb`);i.classList.add(`orb-core`),i.style.setProperty(`--hue`,String(t.hue)),(t.cracks??0)>0&&i.append(St(t.cracks??0)),n?(i.style.opacity=`${Math.max(0,(1-r)*.85)}`,i.style.transform=`scale(${1+r*.18})`,e.append(i,Ct(t,r))):e.append(i,xt(t))}function yt(e,t){let n=document.createElement(`div`);if(n.className=e.shape?`avatar avatar-shaped`:`avatar`,e.shape){let t=te(e.shape);t&&(n.style.maskImage=`url(${t})`,n.style.webkitMaskImage=`url(${t})`)}let r=document.createElement(`img`);return r.src=e.imageUrl,r.alt=t,r.draggable=!1,r.addEventListener(`error`,()=>{r.src=D}),n.append(r,bt()),n}function bt(){let e=document.createElement(`div`);return e.className=`avatar-gloss`,e}function xt(e){let t=document.createElement(`div`);return t.className=`bubble-label label-${e.role}`,e.role===`zap`?t.innerHTML=`<span class="label-kicker">zap</span><strong>${wt(e.zapAmountSats??0)}</strong><span>sats</span>`:t.textContent=e.messagePreview,t}function St(e){let t=Q(`svg`,{class:`cracks`,viewBox:`0 0 100 100`,preserveAspectRatio:`none`});return t.append(Q(`path`,{d:`M 8 52 L 30 44 L 42 56 L 56 38 L 70 52 L 92 46`}),Q(`path`,{d:`M 30 44 L 24 30`}),Q(`path`,{d:`M 56 38 L 60 22`})),e>=2&&t.append(Q(`path`,{d:`M 48 8 L 56 30 L 42 44 L 58 60 L 44 78 L 52 92`}),Q(`path`,{d:`M 56 30 L 70 24`}),Q(`path`,{d:`M 58 60 L 76 64`})),t}function Ct(e,t){let n=document.createElement(`div`);n.className=`explosion`,n.style.setProperty(`--hue`,String(e.hue));let r=document.createElement(`div`);r.className=`explosion-ring`,r.style.width=`${e.radius*2*(1+t*1.7)}px`,r.style.height=r.style.width,r.style.opacity=`${1-t}`,n.append(r);for(let r=0;r<ue.length;r+=1){let i=ue[r],a=document.createElement(`div`);a.className=`explosion-particle`;let o=e.radius*(1.05+t*2.3),s=Math.max(2,e.radius*.22*(1-t*.5));a.style.width=`${s}px`,a.style.height=`${s}px`,a.style.transform=`translate(${Math.cos(i)*o-s/2}px, ${Math.sin(i)*o-s/2}px)`,a.style.opacity=`${1-t}`,n.append(a)}return n}function Q(e,t={},n=[]){let r=document.createElementNS(`http://www.w3.org/2000/svg`,e);for(let[e,n]of Object.entries(t))r.setAttribute(e,String(n));return r.append(...n),r}function wt(e){return new Intl.NumberFormat(void 0,{maximumFractionDigits:0}).format(e)}function Tt(){k.footerBar.addEventListener(`click`,Ot),k.closeSettingsButton.addEventListener(`click`,$),k.openSettingsButton.addEventListener(`click`,()=>{try{s.openSettings()}catch{q(`error`,`This shell did not grant NAP-CONFIG settings.`)}}),k.settingsOverlay.addEventListener(`click`,e=>{e.target===k.settingsOverlay&&$()}),k.settingsOverlay.addEventListener(`keydown`,Et),k.restartButton.addEventListener(`click`,()=>{Y()})}function Et(e){if(k.settingsOverlay.classList.contains(`hidden`))return;if(e.key===`Escape`){e.preventDefault(),$();return}if(e.key!==`Tab`)return;let t=Dt();if(t.length===0)return;let n=t[0],r=t[t.length-1];e.shiftKey&&document.activeElement===n?(e.preventDefault(),r.focus()):!e.shiftKey&&document.activeElement===r&&(e.preventDefault(),n.focus())}function Dt(){return Array.from(k.settingsOverlay.querySelectorAll(`button:not([disabled]), input:not([disabled]), select:not([disabled])`)).filter(e=>e.offsetParent!==null)}function Ot(){k.settingsOverlay.classList.remove(`hidden`),k.footerBar.setAttribute(`aria-expanded`,`true`),Dt()[0]?.focus()}function $(){k.settingsOverlay.classList.add(`hidden`),k.footerBar.setAttribute(`aria-expanded`,`false`),k.footerBar.focus()}async function kt(){if(Tt(),K(`config`))try{L=We(await s.get()),ze=s.subscribe(e=>{let t=We(e);JSON.stringify(t)!==JSON.stringify(L)&&(L=t,J(),Y())})}catch{L={...x}}if(K(`identity`))try{Re=o.onChanged(()=>{Y()})}catch{}Be=window.requestAnimationFrame(ot),await Y()}window.addEventListener(`pagehide`,()=>{M+=1,qe(),Re?.close(),ze?.close(),Le.close(),window.cancelAnimationFrame(Be);for(let e of z.values())e.objectUrl&&URL.revokeObjectURL(e.objectUrl)}),kt().catch(e=>{q(`error`,e instanceof Error?e.message:`Nostr Bubbles failed to start.`)});</script>
    <style rel="stylesheet" crossorigin>*,:before,:after{box-sizing:border-box;border-style:solid;border-width:0;border-color:var(--un-default-border-color,#e5e7eb)}:before,:after{--un-content:""}html,:host{-webkit-text-size-adjust:100%;tab-size:4;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{line-height:inherit;margin:0}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-feature-settings:normal;font-variation-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-feature-settings:inherit;font-variation-settings:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:#0000;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{margin:0;padding:0;list-style:none}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}html,body,#app{width:100%;height:100%;margin:0}:root{color-scheme:var(--hg-color-scheme,dark);--napplet-text-selection:none}*{box-sizing:border-box}:where(html,body,body *){-webkit-user-select:var(--napplet-text-selection,none);user-select:var(--napplet-text-selection,none)}button{font:inherit}.bubble-stage{isolation:isolate;background:rgb(var(--hg-bg-base-rgb,20 17 12));width:100%;height:100%;color:rgb(var(--hg-text-primary-rgb,245 241 232));font-family:var(--hg-font-body,ui-monospace, monospace);position:relative;overflow:hidden}.space-backdrop,.space-backdrop:before,.space-backdrop:after{pointer-events:none;position:absolute;inset:0}.space-backdrop{z-index:-3;background:radial-gradient(circle at 18% 22%, rgba(var(--hg-accent-green-rgb,155 229 100) / .2), transparent 30%), radial-gradient(circle at 78% 18%, #a855f738, transparent 28%), radial-gradient(circle at 52% 80%, rgba(var(--hg-accent-amber-rgb,229 196 100) / .17), transparent 34%), linear-gradient(135deg, rgb(var(--hg-bg-overlay-rgb,8 7 6)) 0%, rgb(var(--hg-bg-base-rgb,20 17 12)) 52%, #030303 100%)}:root[data-hg-theme=light] .space-backdrop{background:radial-gradient(circle at 14% 18%, rgba(var(--hg-accent-green-rgb,34 197 94) / .28), transparent 30%), radial-gradient(circle at 82% 16%, #818cf838, transparent 30%), radial-gradient(circle at 50% 86%, rgba(var(--hg-accent-amber-rgb,245 158 11) / .24), transparent 34%), linear-gradient(135deg, rgb(var(--hg-bg-overlay-rgb,255 255 255)) 0%, rgb(var(--hg-bg-base-rgb,248 250 252)) 58%, rgb(var(--hg-bg-surface-rgb,241 245 249)) 100%)}.space-backdrop:before{content:"";opacity:.24;background-image:radial-gradient(circle,#ffffff73 0 1px,#0000 1px);background-size:34px 34px}.space-backdrop:after{content:"";background:linear-gradient(#000000ad,#0000 26% 72%,#000000b8)}:root[data-hg-theme=light] .space-backdrop:before{opacity:.38;background-image:radial-gradient(circle at center, rgba(var(--hg-text-primary-rgb,15 23 42) / .14) 0 1px, transparent 1px)}:root[data-hg-theme=light] .space-backdrop:after{background:linear-gradient(#ffffffbd,#0000 26% 72%,#ffffffc7)}.effects-layer,.bubble-layer{pointer-events:none;width:100%;height:100%;position:absolute;inset:0;overflow:visible}.effects-layer{z-index:0}.bubble-layer{z-index:1}.visually-hidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}.footer-bar:focus-visible,.controls button:focus-visible,.setting-row input:focus-visible,.setting-row select:focus-visible,.setting-row:focus-visible,.icon-button:focus-visible{outline:2px solid rgb(var(--hg-accent-green-rgb,155 229 100));outline-offset:2px}.eyebrow,.title{display:block}.eyebrow{color:rgba(var(--hg-accent-green-rgb,155 229 100) / .82);letter-spacing:.18em;text-transform:uppercase;font-size:.72rem;font-weight:800}.footer-bar{z-index:4;border:1px solid rgba(var(--hg-border-default-rgb,74 69 60) / .62);background:rgba(var(--hg-bg-overlay-rgb,8 7 6) / .68);min-height:2.45rem;color:rgba(var(--hg-text-secondary-rgb,216 208 192) / .9);-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);cursor:pointer;border-radius:999px;justify-content:center;align-items:center;gap:clamp(.45rem,2vmin,1rem);padding:.42rem .7rem;font-size:clamp(.68rem,1.7vmin,.86rem);display:flex;position:absolute;bottom:clamp(.45rem,2vmin,1rem);left:clamp(.45rem,2vmin,1rem);right:clamp(.45rem,2vmin,1rem);box-shadow:0 1rem 3rem #00000061}.footer-bar strong{color:rgb(var(--hg-text-primary-rgb,245 241 232));font-size:1.08em;font-weight:950}.footer-brand,.footer-settings{color:rgb(var(--hg-accent-green-rgb,155 229 100));text-transform:uppercase;letter-spacing:.08em;font-weight:950}.footer-settings{color:rgb(var(--hg-accent-amber-rgb,229 196 100))}.footer-status{white-space:nowrap;text-transform:uppercase;border:1px solid #0000;border-radius:999px;padding:.28rem .55rem;font-size:.72em;font-weight:900}.status-connecting,.status-idle{border-color:rgba(var(--hg-accent-amber-rgb,229 196 100) / .4);background:rgba(var(--hg-accent-amber-rgb,229 196 100) / .14);color:rgb(var(--hg-accent-amber-rgb,229 196 100))}.status-live{border-color:rgba(var(--hg-accent-green-rgb,155 229 100) / .44);background:rgba(var(--hg-accent-green-rgb,155 229 100) / .14);color:rgb(var(--hg-accent-green-rgb,155 229 100))}.status-offline,.status-error,.status-no-identity,.status-no-contacts{border-color:rgba(var(--hg-accent-red-rgb,255 115 105) / .42);background:rgba(var(--hg-accent-red-rgb,255 115 105) / .13);color:rgb(var(--hg-accent-red-rgb,255 115 105))}.message{color:rgba(var(--hg-text-secondary-rgb,216 208 192) / .82);margin:0;font-size:.86rem;line-height:1.45}.controls{flex-wrap:wrap;gap:.5rem;display:flex}.controls button{border:1px solid rgba(var(--hg-border-default-rgb,74 69 60) / .75);background:rgba(var(--hg-bg-elevated-rgb,36 32 22) / .74);color:rgb(var(--hg-text-primary-rgb,245 241 232));cursor:pointer;border-radius:999px;padding:.55rem .8rem}.controls button:hover{background:rgba(var(--hg-accent-green-rgb,155 229 100) / .18)}.settings-overlay{z-index:5;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);background:#0000006b;place-items:end center;padding:clamp(.75rem,3vmin,1.5rem);display:grid;position:absolute;inset:0}:root[data-hg-theme=light] .settings-overlay{background:rgba(var(--hg-bg-base-rgb,248 250 252) / .48)}.settings-overlay.hidden{display:none}.settings-card{border:1px solid rgba(var(--hg-border-default-rgb,74 69 60) / .75);background:rgba(var(--hg-bg-overlay-rgb,8 7 6) / .88);width:min(34rem,100%);max-height:min(34rem,100% - 1rem);color:rgb(var(--hg-text-primary-rgb,245 241 232));border-radius:1.35rem;padding:clamp(.7rem,2.2vmin,1rem);overflow:auto;box-shadow:0 1.5rem 5rem #0000008c}.settings-head{justify-content:space-between;align-items:center;gap:.75rem;margin-bottom:.5rem;display:flex}.settings-head h2{text-transform:uppercase;letter-spacing:.08em;margin:0;font-size:1rem;line-height:1.2}.icon-button{border:1px solid rgba(var(--hg-border-default-rgb,74 69 60) / .8);width:2rem;height:2rem;color:rgb(var(--hg-text-primary-rgb,245 241 232));cursor:pointer;background:#ffffff14;border-radius:999px;place-items:center;display:grid}.settings-summary{grid-template-columns:repeat(auto-fit,minmax(13rem,1fr));gap:.42rem;margin:.65rem 0;display:grid}.setting-row{border:1px solid rgba(var(--hg-border-dim-rgb,58 58 58) / .72);width:100%;color:inherit;text-align:left;background:#fff1;border-radius:.95rem;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:.45rem;padding:.5rem .62rem;display:grid}.setting-row:focus-within,.setting-row:hover{background:rgba(var(--hg-accent-green-rgb,155 229 100) / .12)}.setting-row span{color:rgba(var(--hg-text-secondary-rgb,216 208 192) / .88);font-weight:800}.setting-row input,.setting-row select{border:1px solid rgba(var(--hg-border-default-rgb,74 69 60) / .78);background:rgba(var(--hg-bg-elevated-rgb,36 32 22) / .84);min-width:0;color:rgb(var(--hg-text-primary-rgb,245 241 232));border-radius:999px;padding:.32rem .5rem}.setting-row input[type=number]{width:5.1rem}.setting-row input[type=checkbox]{width:1.35rem;height:1.35rem;accent-color:rgb(var(--hg-accent-green-rgb,155 229 100))}.empty-state{z-index:2;text-align:center;pointer-events:none;place-content:center;padding:1.5rem;display:grid;position:absolute;inset:0}.empty-state.hidden{display:none}.empty-state>*{max-width:34rem}.spark{border:1px solid rgba(var(--hg-accent-green-rgb,155 229 100) / .32);background:rgba(var(--hg-bg-overlay-rgb,8 7 6) / .34);width:3.5rem;height:3.5rem;color:rgb(var(--hg-accent-green-rgb,155 229 100));box-shadow:0 0 2rem rgba(var(--hg-accent-green-rgb,155 229 100) / .22);border-radius:999px;place-items:center;margin-inline:auto;font-size:2rem;display:grid}.empty-state h2{margin:1rem 0 .35rem;font-size:clamp(1.4rem,5vmin,2.5rem)}.empty-state p{color:rgba(var(--hg-text-secondary-rgb,216 208 192) / .76);margin:0}.bubble{pointer-events:none;will-change:transform;position:absolute}.bubble-note,.bubble-reply,.bubble-root{z-index:2}.bubble-zap{z-index:3}.bubble-reaction{z-index:1;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);background:#00000038;border:1px solid #ffffff29;border-radius:999px;align-items:center;gap:.35rem;padding:.2rem;display:flex}:root[data-hg-theme=light] .bubble-reaction{border-color:rgba(var(--hg-border-default-rgb,203 213 225) / .38);background:#ffffff85}.avatar{background:#00000059;border:2px solid #ffffffb8;border-radius:999px;width:100%;height:100%;position:relative;overflow:hidden;box-shadow:inset 0 0 1.1rem #ffffff75}:root[data-hg-theme=light] .avatar{box-shadow:inset 0 0 1.2rem #ffffffb8, 0 .45rem 1.4rem rgba(var(--hg-text-primary-rgb,15 23 42) / .16);background:#ffffff94;border-color:#ffffffeb}.avatar-shaped{border:0;border-radius:0;-webkit-mask-position:50%;mask-position:50%;-webkit-mask-size:100% 100%;mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.avatar img{object-fit:cover;width:100%;height:100%}.avatar-gloss{border-radius:inherit;background:radial-gradient(circle at 32% 24%,#ffffffb8,#0000 18%),linear-gradient(135deg,#ffffff3d,#0000 45%,#00000061);position:absolute;inset:0}.orb-core:before{content:"";z-index:-1;border-radius:inherit;background:hsl(var(--hue) 95% 58% / .38);filter:blur(.5rem);position:absolute;inset:-.5rem}.bubble-root .avatar{border-color:rgb(var(--hg-accent-amber-rgb,229 196 100));box-shadow:0 0 2rem rgba(var(--hg-accent-amber-rgb,229 196 100) / .24), inset 0 0 1.1rem #ffffff75}.bubble-reply .avatar{border-color:rgba(var(--hg-accent-green-rgb,155 229 100) / .88)}.bubble-zap .avatar{border-color:rgba(var(--hg-accent-amber-rgb,229 196 100) / .92);box-shadow:0 0 1.6rem rgba(var(--hg-accent-amber-rgb,229 196 100) / .48), inset 0 0 1.1rem #ffffff75}.bubble-reaction .avatar{aspect-ratio:1;border-width:1px;width:auto}.reaction-emoji{background:#ffffff1f;border:1px solid #ffffff24;border-radius:999px;place-items:center;min-width:2rem;padding:.25rem .45rem;font-size:1.1rem;font-weight:950;display:grid}.bubble-label{width:min(14rem,max(7.25rem,310%));color:rgba(var(--hg-text-primary-rgb,245 241 232) / .86);text-align:center;overflow-wrap:anywhere;-webkit-line-clamp:2;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);background:#0000007a;border:1px solid #ffffff24;border-radius:.8rem;-webkit-box-orient:vertical;padding:.45rem .6rem;font-size:.68rem;font-weight:700;line-height:1.25;display:-webkit-box;position:absolute;top:calc(100% + .45rem);left:50%;overflow:hidden;transform:translate(-50%);box-shadow:0 .75rem 1.5rem #00000047}:root[data-hg-theme=light] .bubble-label{border-color:rgba(var(--hg-border-default-rgb,203 213 225) / .38);color:rgba(var(--hg-text-primary-rgb,15 23 42) / .9);box-shadow:0 .75rem 1.8rem rgba(var(--hg-text-primary-rgb,15 23 42) / .16);background:#ffffffb8}.label-zap{border-color:rgba(var(--hg-accent-amber-rgb,229 196 100) / .34);width:min(7rem,max(4.5rem,410%));color:rgb(var(--hg-text-primary-rgb,245 241 232));-webkit-line-clamp:unset;background:#281c00b8;display:grid}:root[data-hg-theme=light] .label-zap{background:rgba(var(--hg-accent-amber-rgb,245 158 11) / .18);color:rgb(var(--hg-text-primary-rgb,15 23 42))}.label-zap strong{font-size:.95rem}.label-kicker,.label-zap span:last-child{color:rgba(var(--hg-accent-amber-rgb,229 196 100) / .86);letter-spacing:.16em;text-transform:uppercase;font-size:.55rem;font-weight:950}.cracks{pointer-events:none;width:100%;height:100%;position:absolute;inset:0}.cracks path{stroke:#fffffff5;stroke-width:2.3px;stroke-linecap:round;stroke-linejoin:round;fill:none;filter:drop-shadow(0 0 1.2px #000000d9)}.explosion{pointer-events:none;position:absolute;top:50%;left:50%}.explosion-ring,.explosion-particle{border-radius:999px;position:absolute;top:0;left:0}.explosion-ring{border:.18rem solid hsl(var(--hue) 100% 72%);box-shadow:0 0 2rem hsl(var(--hue) 100% 70% / .55);transform:translate(-50%,-50%)}.explosion-particle{background:hsl(var(--hue) 100% 70%);box-shadow:0 0 .8rem hsl(var(--hue) 100% 72% / .85)}@media (width<=360px),(height<=300px){.footer-bar{scrollbar-width:none;justify-content:start;overflow-x:auto}.footer-bar::-webkit-scrollbar{display:none}.footer-status,.footer-brand,.bubble-label{display:none}}@media (height<=220px){.settings-overlay,.empty-state p{display:none}}
/*$vite$:1*/</style>
  </head>
  <body>
    <main id="app" class="bubble-stage" aria-label="Nostr Bubbles">
      <div class="space-backdrop" aria-hidden="true"></div>
      <svg id="effects" class="effects-layer" aria-hidden="true"></svg>
      <div id="bubbleLayer" class="bubble-layer" aria-hidden="true"></div>

      <header class="visually-hidden" aria-labelledby="app-title">
        <h1 id="app-title">Nostr Bubbles</h1>
      </header>

      <section id="emptyState" class="empty-state">
        <div class="spark">*</div>
        <h2>Waiting for Nostr activity</h2>
        <p>Notes become profile bubbles. Zaps become fast comets.</p>
      </section>

      <button
        id="footerBar"
        class="footer-bar"
        type="button"
        aria-haspopup="dialog"
        aria-controls="settingsOverlay"
        aria-expanded="false"
      >
        <span class="footer-brand">Nostr Bubbles</span>
        <output id="status" class="footer-status status-idle">Booting</output>
        <span><strong id="contactCount">0</strong> sources</span>
        <span><strong id="noteCount">0</strong> notes</span>
        <span><strong id="satsCount">0</strong> sats</span>
        <span><strong id="orbCount">0</strong> orbs</span>
        <span class="footer-settings">settings</span>
      </button>

      <section
        id="settingsOverlay"
        class="settings-overlay hidden"
        role="dialog"
        aria-modal="true"
        aria-labelledby="settingsTitle"
      >
        <div class="settings-card">
          <div class="settings-head">
            <h2 id="settingsTitle">Settings</h2>
            <button
              id="closeSettingsButton"
              class="icon-button"
              type="button"
              aria-label="Close settings"
            >
              x
            </button>
          </div>
          <p id="message" class="message">Loading shell identity...</p>
          <div class="controls">
            <button id="openSettingsButton" type="button">Open shell settings</button>
            <button id="restartButton" type="button">Restart stream</button>
          </div>
        </div>
      </section>
    </main>
  </body>
</html>