nostr-components 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +25 -16
  2. package/dist/assets/{base-styles-DC0ilu4S.js → base-styles-Dmuzg8I4.js} +3 -3
  3. package/dist/assets/{base-styles-DC0ilu4S.js.map → base-styles-Dmuzg8I4.js.map} +1 -1
  4. package/dist/assets/{copy-delegation-CcagQMIW.js → copy-delegation-xzt-t_do.js} +5 -5
  5. package/dist/assets/{copy-delegation-CcagQMIW.js.map → copy-delegation-xzt-t_do.js.map} +1 -1
  6. package/dist/assets/dark-nostrich-running.gif +0 -0
  7. package/dist/assets/default_dp-NQ3TGrtT.png +0 -0
  8. package/dist/assets/default_dp.png +0 -0
  9. package/dist/assets/default_dp_32.png +0 -0
  10. package/dist/assets/{dialog-component-Dqg0QU9I.js → dialog-component-Da1ZIYh9.js} +7 -2
  11. package/dist/assets/dialog-component-Da1ZIYh9.js.map +1 -0
  12. package/dist/assets/{dialog-likers-D3c7WIMp.js → dialog-likers-B15m_NxI.js} +4 -4
  13. package/dist/assets/{dialog-likers-D3c7WIMp.js.map → dialog-likers-B15m_NxI.js.map} +1 -1
  14. package/dist/assets/light-nostrich-running.gif +0 -0
  15. package/dist/assets/{nostr-service-m3Hgc5Xx.js → nostr-service-CA0Qx4nJ.js} +3 -3
  16. package/dist/assets/{nostr-service-m3Hgc5Xx.js.map → nostr-service-CA0Qx4nJ.js.map} +1 -1
  17. package/dist/assets/nostr-user-component-r-MUbTL6.js +2 -0
  18. package/dist/assets/nostr-user-component-r-MUbTL6.js.map +1 -0
  19. package/dist/assets/{pure-laCRX9eG.js → pure-DOoUcNQv.js} +2 -2
  20. package/dist/assets/{pure-laCRX9eG.js.map → pure-DOoUcNQv.js.map} +1 -1
  21. package/dist/assets/{theme-C1r1Zw8r.js → theme-BN1Bvweb.js} +2 -2
  22. package/dist/assets/{theme-C1r1Zw8r.js.map → theme-BN1Bvweb.js.map} +1 -1
  23. package/dist/assets/{user-resolver-DqI5KGh6.js → user-resolver-ArI0680e.js} +2 -2
  24. package/dist/assets/{user-resolver-DqI5KGh6.js.map → user-resolver-ArI0680e.js.map} +1 -1
  25. package/dist/assets/{zap-utils-BZcaCsT_.js → zap-utils-QRxLBOst.js} +2 -2
  26. package/dist/assets/{zap-utils-BZcaCsT_.js.map → zap-utils-QRxLBOst.js.map} +1 -1
  27. package/dist/components/nostr-comment.es.js +6 -6
  28. package/dist/components/nostr-comment.es.js.map +1 -1
  29. package/dist/components/nostr-dm.es.js +2 -2
  30. package/dist/components/nostr-dm.es.js.map +1 -1
  31. package/dist/components/nostr-follow-button.es.js +3 -3
  32. package/dist/components/nostr-follow-button.es.js.map +1 -1
  33. package/dist/components/nostr-like.es.js +6 -6
  34. package/dist/components/nostr-like.es.js.map +1 -1
  35. package/dist/components/nostr-live-chat.es.js +3 -3
  36. package/dist/components/nostr-live-chat.es.js.map +1 -1
  37. package/dist/components/nostr-post.es.js +2 -2
  38. package/dist/components/nostr-post.es.js.map +1 -1
  39. package/dist/components/nostr-profile-badge.es.js +3 -3
  40. package/dist/components/nostr-profile-badge.es.js.map +1 -1
  41. package/dist/components/nostr-profile.es.js +5 -5
  42. package/dist/components/nostr-profile.es.js.map +1 -1
  43. package/dist/components/nostr-zap.es.js +5 -5
  44. package/dist/components/nostr-zap.es.js.map +1 -1
  45. package/dist/index.d.ts +6 -0
  46. package/dist/nostr-comment.d.ts +4 -0
  47. package/dist/nostr-components.es.js +1 -1
  48. package/dist/nostr-components.es.js.map +1 -1
  49. package/dist/nostr-components.umd.js +69 -40
  50. package/dist/nostr-components.umd.js.map +1 -1
  51. package/dist/nostr-dm.d.ts +4 -0
  52. package/dist/nostr-follow-button.d.ts +4 -0
  53. package/dist/nostr-like.d.ts +4 -0
  54. package/dist/nostr-live-chat.d.ts +4 -0
  55. package/dist/nostr-post.d.ts +4 -0
  56. package/dist/nostr-profile-badge.d.ts +4 -0
  57. package/dist/nostr-profile.d.ts +4 -0
  58. package/dist/nostr-zap.d.ts +4 -0
  59. package/dist/src/base/base-component/nostr-base-component.d.ts +116 -0
  60. package/dist/src/base/copy-delegation.d.ts +5 -0
  61. package/dist/src/base/dialog-component/dialog-component.d.ts +67 -0
  62. package/dist/src/base/dialog-component/style.d.ts +5 -0
  63. package/dist/src/base/event-component/nostr-event-component.d.ts +53 -0
  64. package/dist/src/base/render-options.d.ts +5 -0
  65. package/dist/src/base/resolvers/event-resolver.d.ts +20 -0
  66. package/dist/src/base/resolvers/user-resolver.d.ts +19 -0
  67. package/dist/src/base/text-row/render-name.d.ts +7 -0
  68. package/dist/src/base/text-row/render-nip05.d.ts +1 -0
  69. package/dist/src/base/text-row/render-npub.d.ts +1 -0
  70. package/dist/src/base/text-row/render-text-row.d.ts +9 -0
  71. package/dist/src/base/user-component/nostr-user-component.d.ts +43 -0
  72. package/dist/src/common/base-styles.d.ts +44 -0
  73. package/dist/src/common/constants.d.ts +4 -0
  74. package/dist/src/common/date-utils.d.ts +9 -0
  75. package/dist/src/common/icons.d.ts +7 -0
  76. package/dist/src/common/nip05-utils.d.ts +13 -0
  77. package/dist/src/common/nostr-login-service.d.ts +26 -0
  78. package/dist/src/common/nostr-service.d.ts +40 -0
  79. package/dist/src/common/theme.d.ts +4 -0
  80. package/dist/src/common/types.d.ts +1 -0
  81. package/dist/src/common/utils.d.ts +34 -0
  82. package/dist/src/index.d.ts +32 -0
  83. package/dist/src/nostr-comment/nostr-comment.d.ts +60 -0
  84. package/dist/src/nostr-comment/render.d.ts +15 -0
  85. package/dist/src/nostr-comment/utils.d.ts +81 -0
  86. package/dist/src/nostr-dm/nostr-dm.d.ts +34 -0
  87. package/dist/src/nostr-dm/render.d.ts +15 -0
  88. package/dist/src/nostr-follow-button/nostr-follow-button.d.ts +24 -0
  89. package/dist/src/nostr-follow-button/render.d.ts +11 -0
  90. package/dist/src/nostr-follow-button/style.d.ts +1 -0
  91. package/dist/src/nostr-like/dialog-help-style.d.ts +1 -0
  92. package/dist/src/nostr-like/dialog-help.d.ts +2 -0
  93. package/dist/src/nostr-like/dialog-likers-style.d.ts +1 -0
  94. package/dist/src/nostr-like/dialog-likers.d.ts +24 -0
  95. package/dist/src/nostr-like/like-utils.d.ts +48 -0
  96. package/dist/src/nostr-like/nostr-like.d.ts +49 -0
  97. package/dist/src/nostr-like/render.d.ts +10 -0
  98. package/dist/src/nostr-like/style.d.ts +1 -0
  99. package/dist/src/nostr-live-chat/nostr-live-chat.d.ts +65 -0
  100. package/dist/src/nostr-live-chat/render.d.ts +31 -0
  101. package/dist/src/nostr-post/nostr-post.d.ts +25 -0
  102. package/dist/src/nostr-post/parse-text.d.ts +8 -0
  103. package/dist/src/nostr-post/render-content.d.ts +5 -0
  104. package/dist/src/nostr-post/render.d.ts +19 -0
  105. package/dist/src/nostr-post/style.d.ts +1 -0
  106. package/dist/src/nostr-profile/nostr-profile.d.ts +24 -0
  107. package/dist/src/nostr-profile/render-stats.d.ts +1 -0
  108. package/dist/src/nostr-profile/render.d.ts +22 -0
  109. package/dist/src/nostr-profile/style.d.ts +1 -0
  110. package/dist/src/nostr-profile-badge/nostr-profile-badge.d.ts +34 -0
  111. package/dist/src/nostr-profile-badge/render.d.ts +11 -0
  112. package/dist/src/nostr-profile-badge/style.d.ts +1 -0
  113. package/dist/src/nostr-zap/dialog-help-style.d.ts +5 -0
  114. package/dist/src/nostr-zap/dialog-help.d.ts +2 -0
  115. package/dist/src/nostr-zap/dialog-zap-style.d.ts +6 -0
  116. package/dist/src/nostr-zap/dialog-zap.d.ts +31 -0
  117. package/dist/src/nostr-zap/dialog-zappers-style.d.ts +1 -0
  118. package/dist/src/nostr-zap/dialog-zappers.d.ts +25 -0
  119. package/dist/src/nostr-zap/nostr-zap.d.ts +45 -0
  120. package/dist/src/nostr-zap/render.d.ts +9 -0
  121. package/dist/src/nostr-zap/style.d.ts +1 -0
  122. package/dist/src/nostr-zap/zap-utils.d.ts +57 -0
  123. package/dist/vite.config.d.ts +2 -0
  124. package/dist/vite.config.esm.d.ts +2 -0
  125. package/dist/vite.config.umd.d.ts +2 -0
  126. package/package.json +1 -1
  127. package/dist/assets/dialog-component-Dqg0QU9I.js.map +0 -1
  128. package/dist/assets/nostr-user-component-XEnanH-d.js +0 -2
  129. package/dist/assets/nostr-user-component-XEnanH-d.js.map +0 -1
package/README.md CHANGED
@@ -6,13 +6,13 @@
6
6
 
7
7
  Nostr Components makes it easy to embed Zap button, Nostr profiles, posts, and follow buttons in any website. Inspired by <a href="https://unpkg.com/nostr-web-components@0.0.15/demo.html">fiatjaf's Nostr Web Components</a>, this project adds a beautiful UI, a Storybook component generator (for webmasters), and allows embedding Nostr content anywhere on the Internet.
8
8
 
9
- 🔹 **[Zap button](#1-nostr-zap-)**
10
- 🔹 **[Follow button](#2-nostr-follow-)**
11
- 🔹 **[Like button](#3-nostr-like-)**
12
- 🔹 **[Profile Badge](#4-nostr-profile-badge-)**
13
- 🔹 **[Profile](#5-nostr-profile-)**
14
- 🔹 **[Post](#6-nostr-post-)**
15
- 🔹 **[WordPress Integration](#7-wordpress-integration)** - WordPress Integration
9
+ * **[Zap button](#1-nostr-zap)**
10
+ * **[Follow button](#2-nostr-follow)**
11
+ * **[Like button](#3-nostr-like)**
12
+ * **[Profile Badge](#4-nostr-profile-badge)**
13
+ * **[Profile](#5-nostr-profile)**
14
+ * **[Post](#6-nostr-post)**
15
+ * **[WordPress Integration](#7-wordpress-integration)**
16
16
 
17
17
  ## 🛠️ Usage
18
18
 
@@ -29,8 +29,17 @@ Add the component script to your HTML's `<head>`. Each component can be loaded i
29
29
 
30
30
  <!-- Or load the full bundle -->
31
31
  <script src="https://cdn.jsdelivr.net/npm/nostr-components@latest/dist/nostr-components.umd.js"></script>
32
+ <!-- Components auto-register when the script loads, no init() needed -->
33
+ <!-- Optional: Call init() for explicit initialization (for backward compatibility) -->
32
34
  <script>
33
- NostrComponents.default.init();
35
+ // Components are already registered automatically
36
+ // You can optionally call init() for explicit initialization
37
+ if (window.NostrComponents && window.NostrComponents.default) {
38
+ window.NostrComponents.default.init();
39
+ } else if (window.NostrComponents && window.NostrComponents.init) {
40
+ // Fallback: use named export
41
+ window.NostrComponents.init();
42
+ }
34
43
  </script>
35
44
  </head>
36
45
  ```
@@ -70,7 +79,7 @@ import { NostrLike } from 'nostr-components';
70
79
 
71
80
  ### Authentication
72
81
 
73
- All interactive components (Follow, Like, Zap, DM, Live Chat, Comment) require user authentication. Components use [NostrLogin](https://github.com/nostrband/nostr-login) which supports:
82
+ All interactive components (Follow, Like, Zap) require user authentication. Components use [NostrLogin](https://github.com/nostrband/nostr-login) which supports:
74
83
  - **NIP-07 Browser Extensions** (Alby, nos2x, etc.)
75
84
  - **NIP-46 Remote Signers** (Bunkers)
76
85
 
@@ -78,7 +87,7 @@ The authentication flow is handled automatically when users interact with compon
78
87
 
79
88
  ---
80
89
 
81
- ## 1. Nostr Zap
90
+ ## 1. Nostr Zap
82
91
 
83
92
  A Lightning Network zap button that allows users to send sats to any Nostr user with a lightning address or LNURL.
84
93
 
@@ -103,7 +112,7 @@ A Lightning Network zap button that allows users to send sats to any Nostr user
103
112
 
104
113
  ---
105
114
 
106
- ## 2. Nostr Follow
115
+ ## 2. Nostr Follow
107
116
 
108
117
  A simple button that allows users to follow a Nostr profile.
109
118
 
@@ -129,7 +138,7 @@ A simple button that allows users to follow a Nostr profile.
129
138
 
130
139
  ---
131
140
 
132
- ## 3. Nostr Like ❤️
141
+ ## 3. Nostr Like
133
142
 
134
143
  A like button that uses NIP-25 (External Content Reactions) to like any URL on the web.
135
144
  When URL is not specified, current URL is taken.
@@ -155,7 +164,7 @@ When URL is not specified, current URL is taken.
155
164
 
156
165
  ---
157
166
 
158
- ## 4. Nostr Profile Badge 🔖
167
+ ## 4. Nostr Profile Badge
159
168
 
160
169
  A small badge displaying a Nostr profile with a username and avatar.
161
170
 
@@ -181,7 +190,7 @@ A small badge displaying a Nostr profile with a username and avatar.
181
190
 
182
191
  ---
183
192
 
184
- ## 5. Nostr Profile 👤
193
+ ## 5. Nostr Profile
185
194
 
186
195
  A detailed profile card showing avatar, name, bio, notes count, followers, etc.
187
196
 
@@ -204,7 +213,7 @@ A detailed profile card showing avatar, name, bio, notes count, followers, etc.
204
213
 
205
214
  ---
206
215
 
207
- ## 6. Nostr Post 📝
216
+ ## 6. Nostr Post
208
217
 
209
218
  Embed any Nostr post by providing the event ID.
210
219
 
@@ -227,7 +236,7 @@ Embed any Nostr post by providing the event ID.
227
236
 
228
237
  ---
229
238
 
230
- ## 7. WordPress Integration 🔌
239
+ ## 7. WordPress Integration
231
240
 
232
241
  The Nostr Components WordPress plugin provides Gutenberg blocks and shortcodes for all components, making it easy to embed Nostr functionality in your WordPress site.
233
242
 
@@ -1,4 +1,4 @@
1
- var w=Object.defineProperty;var k=(t,r,e)=>r in t?w(t,r,{enumerable:!0,configurable:!0,writable:!0,value:e}):t[r]=e;var c=(t,r,e)=>k(t,typeof r!="symbol"?r+"":r,e);import{D as v,c as p,g as m,M as E,N as T}from"./nostr-service-m3Hgc5Xx.js";const I=t=>{if(typeof t!="string"||!t.startsWith("npub1"))return"";try{const r=p.decode(t);if(r&&typeof r.data=="string")return r.data}catch(r){console.error("Failed to decode npub:",r)}return""};function M(t){if(!t||!N(t))return"";try{return p.npubEncode(t.toLowerCase())}catch(r){return console.error("Failed to encode hex to npub:",r),""}}function O(t="",r=3){const e=t.length;if(!t.startsWith("npub1"))return"Invalid nPub: expected npub1...";if(!S(t))return"Invalid nPub";let s="npub1";for(let n=5;n<r+5;n++)s+=t[n];s+="...";let o="";for(let n=e-1;n>=e-r;n--)o=t[n]+o;return s+=o,s}async function z(t,r){const e=await t.fetchEvents({kinds:[m.Repost],"#e":[r||""]}),s=d=>d.tags.filter(g=>g[0]==="p").length===1,o=d=>d.tags.filter(g=>g[0]==="e").length===1,n=Array.from(e).filter(s).length,a=await t.fetchEvents({kinds:[m.Reaction],"#e":[r||""]}),i=0,u=await t.fetchEvents({kinds:[m.Text],"#e":[r||""]}),l=Array.from(u).filter(o).length;return{likes:a.size,reposts:n,zaps:i/E,replies:l}}function C(t){if(t){const r=t.split(",").map(e=>e.trim()).filter(Boolean).filter(y);return r.length?Array.from(new Set(r)):[...v]}return[...v]}function L(t){const r=t==null?void 0:t.trim().toLowerCase();return r==="light"||r==="dark"?r:"light"}function P(t){return t===null?!1:t===""||t.toLowerCase()==="true"}function B(t){const r=document.createElement("div");return r.textContent=t,r.innerHTML}function F(t){try{const r=new URL(t);return["http:","https:"].includes(r.protocol)}catch{return!1}}function y(t){try{const r=new URL(t);return r.protocol==="wss:"||r.protocol==="ws:"}catch{return!1}}function N(t){return/^[0-9a-fA-F]+$/.test(t)&&t.length===64}function S(t){try{const{type:r}=p.decode(t);return r==="npub"}catch{return!1}}function j(t){return/^[a-zA-Z0-9_\-\.]+@[a-zA-Z0-9_\-\.]+\.[a-zA-Z]{2,}$/.test(t)}function b(t,r){try{const{type:e}=p.decode(t);return e===r}catch{return!1}}function U(t){return b(t,"note")}function D(t){return b(t,"nevent")}function H(t){return navigator.clipboard.writeText(t)}function q(t){try{const r=Date.now(),e=t*1e3,s=r-e,o=Math.floor(s/1e3);if(o<60)return"just now";if(o<3600){const a=Math.floor(o/60);return`${a} ${a===1?"min":"mins"} ago`}if(o<86400){const a=Math.floor(o/3600);return`${a} ${a===1?"hour":"hours"} ago`}if(o<2592e3){const a=Math.floor(o/86400);return`${a} ${a===1?"day":"days"} ago`}if(o<31536e3){const a=Math.floor(o/2592e3);return`${a} ${a===1?"month":"months"} ago`}const n=Math.floor(o/31536e3);return`${n} ${n===1?"year":"years"} ago`}catch(r){return console.error("Error formatting relative time:",r),"unknown"}}var f=(t=>(t[t.Idle=0]="Idle",t[t.Loading=1]="Loading",t[t.Ready=2]="Ready",t[t.Error=3]="Error",t))(f||{});const $="nc:status";class K extends HTMLElement{constructor(e=!0){super();c(this,"nostrService",T.getInstance());c(this,"theme","light");c(this,"errorMessage","");c(this,"nostrReady");c(this,"nostrReadyResolve");c(this,"nostrReadyReject");c(this,"conn",this.channel("connection"));c(this,"_statuses",new Map);c(this,"_overall",0);c(this,"connectSeq",0);e&&this.attachShadow({mode:"open"}),this.resetNostrReadyBarrier()}static get observedAttributes(){return["data-theme","relays"]}connectedCallback(){this.validateInputs()&&(this.getTheme(),this.conn.get()===0&&this.connectToNostr())}disconnectedCallback(){this.shadowRoot&&this._delegated&&this._delegated.clear()}attributeChangedCallback(e,s,o){s!==o&&(e==="data-theme"||e==="relays")&&this.validateInputs()&&(e==="relays"&&(this.resetNostrReadyBarrier(),this.connectToNostr()),e==="data-theme"&&(this.getTheme(),this.render()))}setStatusFor(e,s,o){const n=this._statuses.get(e);if(!(n!==s||s===3&&!!o))return;this._statuses.set(e,s),s===3&&o?this.errorMessage=o:n===3&&s!==3&&(this.errorMessage="");const i=`${e}-status`,u=f[s].toLowerCase();this.getAttribute(i)!==u&&this.setAttribute(i,u);const l=this.computeOverall(),d=f[l].toLowerCase();this._overall!==l?(this._overall=l,this.setAttribute("status",d),this.onStatusChange(l)):l===3&&o&&this.onStatusChange(l),this.dispatchEvent(new CustomEvent($,{detail:{key:e,status:s,all:this.snapshotStatuses(),overall:this._overall,errorMessage:this.errorMessage||void 0},bubbles:!0,composed:!0}))}getStatusFor(e){return this._statuses.get(e)??0}snapshotStatuses(){return Object.fromEntries(this._statuses.entries())}onStatusChange(e){}onNostrRelaysConnected(){}computeOverall(){const e=[...this._statuses.values()];return e.includes(3)?3:e.includes(1)?1:e.includes(2)?2:0}initChannelStatus(e,s,o={reflectOverall:!1}){if(this._statuses.set(e,s),this.setAttribute(`${e}-status`,f[s].toLowerCase()),o.reflectOverall){const n=this.computeOverall();this._overall=n,this.setAttribute("status",f[n].toLowerCase())}}channel(e){return{set:(s,o)=>this.setStatusFor(e,s,o),get:()=>this.getStatusFor(e)}}validateInputs(){const e=this.getAttribute("data-theme")||"light",s=this.getAttribute("relays"),o=this.tagName.toLowerCase();if(e==="light"||e==="dark"){if(s&&typeof s!="string")return this.conn.set(3,"Invalid relays list"),console.error(`Nostr-Components: ${o}: ${this.errorMessage}`),!1;if(s){const a=s.split(",").map(i=>i.trim()).filter(Boolean).filter(i=>!y(i));if(a.length>0){const i=a.join(", ");return this.conn.set(3,`Invalid relay URLs: ${i}. Relay URLs must start with 'wss://' or 'ws://'`),console.error(`Nostr-Components: ${o}: ${this.errorMessage}`),!1}}}else return this.conn.set(3,`Invalid theme '${e}'. Accepted values are 'light', 'dark'`),console.error(`Nostr-Components: ${o}: ${this.errorMessage}`),!1;return this.errorMessage="",!0}async connectToNostr(){var s,o;const e=++this.connectSeq;this.conn.set(1);try{if(await this.nostrService.connectToNostr(this.getRelays()),e!==this.connectSeq)return;this.conn.set(2),(s=this.nostrReadyResolve)==null||s.call(this);try{this.onNostrRelaysConnected()}catch(n){console.error("Error in onNostrRelaysConnected hook:",n)}}catch(n){if(e!==this.connectSeq)return;console.error("Failed to connect to Nostr relays:",n),this.conn.set(3,"Failed to connect to relays"),(o=this.nostrReadyReject)==null||o.call(this,n)}}ensureNostrConnected(){return this.nostrReady}getRelays(){return C(this.getAttribute("relays"))}getTheme(){this.theme=L(this.getAttribute("data-theme"))}delegateEvent(e,s,o){var i;const n=this.shadowRoot;if(!n)return;const a=`${e}:${s}`;(i=this._delegated)!=null&&i.has(a)||(this._delegated||(this._delegated=new Set),this._delegated.add(a),n.addEventListener(e,u=>{u.target.closest(s)&&o(u)}))}addDelegatedListener(e,s,o){this.delegateEvent(e,s,o)}renderError(e){return`Error: ${e}`}updateHostClasses(){const e=this.computeOverall()===1,s=this.computeOverall()===3,o=this.computeOverall()===2;this.classList.remove("is-clickable","is-disabled","is-error"),e?this.classList.add("is-disabled"):s?this.classList.add("is-error"):o&&this.classList.add("is-clickable")}render(){this.updateHostClasses(),this.renderContent()}handleNjumpClick(e,s,o){if(this.computeOverall()!==2)return;const n=new CustomEvent(e,{detail:s,bubbles:!0,composed:!0,cancelable:!0});this.dispatchEvent(n)&&window.open(`https://njump.me/${o}`,"_blank","noopener,noreferrer")}resetNostrReadyBarrier(){this.connectSeq++,this.nostrReady=new Promise((e,s)=>{this.nostrReadyResolve=e,this.nostrReadyReject=s})}}function x(){return`
1
+ var w=Object.defineProperty;var k=(t,r,e)=>r in t?w(t,r,{enumerable:!0,configurable:!0,writable:!0,value:e}):t[r]=e;var c=(t,r,e)=>k(t,typeof r!="symbol"?r+"":r,e);import{c as v,a as p,e as m,M as E,b as T}from"./nostr-service-CA0Qx4nJ.js";const I=t=>{if(typeof t!="string"||!t.startsWith("npub1"))return"";try{const r=p.decode(t);if(r&&typeof r.data=="string")return r.data}catch(r){console.error("Failed to decode npub:",r)}return""};function M(t){if(!t||!S(t))return"";try{return p.npubEncode(t.toLowerCase())}catch(r){return console.error("Failed to encode hex to npub:",r),""}}function O(t="",r=3){const e=t.length;if(!t.startsWith("npub1"))return"Invalid nPub: expected npub1...";if(!N(t))return"Invalid nPub";let s="npub1";for(let n=5;n<r+5;n++)s+=t[n];s+="...";let o="";for(let n=e-1;n>=e-r;n--)o=t[n]+o;return s+=o,s}async function z(t,r){const e=await t.fetchEvents({kinds:[m.Repost],"#e":[r||""]}),s=d=>d.tags.filter(g=>g[0]==="p").length===1,o=d=>d.tags.filter(g=>g[0]==="e").length===1,n=Array.from(e).filter(s).length,a=await t.fetchEvents({kinds:[m.Reaction],"#e":[r||""]}),i=0,u=await t.fetchEvents({kinds:[m.Text],"#e":[r||""]}),l=Array.from(u).filter(o).length;return{likes:a.size,reposts:n,zaps:i/E,replies:l}}function C(t){if(t){const r=t.split(",").map(e=>e.trim()).filter(Boolean).filter(y);return r.length?Array.from(new Set(r)):[...v]}return[...v]}function L(t){const r=t==null?void 0:t.trim().toLowerCase();return r==="light"||r==="dark"?r:"light"}function P(t){return t===null?!1:t===""||t.toLowerCase()==="true"}function B(t){const r=document.createElement("div");return r.textContent=t,r.innerHTML}function F(t){try{const r=new URL(t);return["http:","https:"].includes(r.protocol)}catch{return!1}}function y(t){try{const r=new URL(t);return r.protocol==="wss:"||r.protocol==="ws:"}catch{return!1}}function S(t){return/^[0-9a-fA-F]+$/.test(t)&&t.length===64}function N(t){try{const{type:r}=p.decode(t);return r==="npub"}catch{return!1}}function j(t){return/^[a-zA-Z0-9_\-\.]+@[a-zA-Z0-9_\-\.]+\.[a-zA-Z]{2,}$/.test(t)}function b(t,r){try{const{type:e}=p.decode(t);return e===r}catch{return!1}}function U(t){return b(t,"note")}function D(t){return b(t,"nevent")}function H(t){return navigator.clipboard.writeText(t)}function q(t){try{const r=Date.now(),e=t*1e3,s=r-e,o=Math.floor(s/1e3);if(o<60)return"just now";if(o<3600){const a=Math.floor(o/60);return`${a} ${a===1?"min":"mins"} ago`}if(o<86400){const a=Math.floor(o/3600);return`${a} ${a===1?"hour":"hours"} ago`}if(o<2592e3){const a=Math.floor(o/86400);return`${a} ${a===1?"day":"days"} ago`}if(o<31536e3){const a=Math.floor(o/2592e3);return`${a} ${a===1?"month":"months"} ago`}const n=Math.floor(o/31536e3);return`${n} ${n===1?"year":"years"} ago`}catch(r){return console.error("Error formatting relative time:",r),"unknown"}}var f=(t=>(t[t.Idle=0]="Idle",t[t.Loading=1]="Loading",t[t.Ready=2]="Ready",t[t.Error=3]="Error",t))(f||{});const $="nc:status";class K extends HTMLElement{constructor(e=!0){super();c(this,"nostrService",T.getInstance());c(this,"theme","light");c(this,"errorMessage","");c(this,"nostrReady");c(this,"nostrReadyResolve");c(this,"nostrReadyReject");c(this,"conn",this.channel("connection"));c(this,"_statuses",new Map);c(this,"_overall",0);c(this,"connectSeq",0);e&&this.attachShadow({mode:"open"}),this.resetNostrReadyBarrier()}static get observedAttributes(){return["data-theme","relays"]}connectedCallback(){this.validateInputs()&&(this.getTheme(),this.conn.get()===0&&this.connectToNostr())}disconnectedCallback(){this.shadowRoot&&this._delegated&&this._delegated.clear()}attributeChangedCallback(e,s,o){s!==o&&(e==="data-theme"||e==="relays")&&this.validateInputs()&&(e==="relays"&&(this.resetNostrReadyBarrier(),this.connectToNostr()),e==="data-theme"&&(this.getTheme(),this.render()))}setStatusFor(e,s,o){const n=this._statuses.get(e);if(!(n!==s||s===3&&!!o))return;this._statuses.set(e,s),s===3&&o?this.errorMessage=o:n===3&&s!==3&&(this.errorMessage="");const i=`${e}-status`,u=f[s].toLowerCase();this.getAttribute(i)!==u&&this.setAttribute(i,u);const l=this.computeOverall(),d=f[l].toLowerCase();this._overall!==l?(this._overall=l,this.setAttribute("status",d),this.onStatusChange(l)):l===3&&o&&this.onStatusChange(l),this.dispatchEvent(new CustomEvent($,{detail:{key:e,status:s,all:this.snapshotStatuses(),overall:this._overall,errorMessage:this.errorMessage||void 0},bubbles:!0,composed:!0}))}getStatusFor(e){return this._statuses.get(e)??0}snapshotStatuses(){return Object.fromEntries(this._statuses.entries())}onStatusChange(e){}onNostrRelaysConnected(){}computeOverall(){const e=[...this._statuses.values()];return e.includes(3)?3:e.includes(1)?1:e.includes(2)?2:0}initChannelStatus(e,s,o={reflectOverall:!1}){if(this._statuses.set(e,s),this.setAttribute(`${e}-status`,f[s].toLowerCase()),o.reflectOverall){const n=this.computeOverall();this._overall=n,this.setAttribute("status",f[n].toLowerCase())}}channel(e){return{set:(s,o)=>this.setStatusFor(e,s,o),get:()=>this.getStatusFor(e)}}validateInputs(){const e=this.getAttribute("data-theme")||"light",s=this.getAttribute("relays"),o=this.tagName.toLowerCase();if(e==="light"||e==="dark"){if(s&&typeof s!="string")return this.conn.set(3,"Invalid relays list"),console.error(`Nostr-Components: ${o}: ${this.errorMessage}`),!1;if(s){const a=s.split(",").map(i=>i.trim()).filter(Boolean).filter(i=>!y(i));if(a.length>0){const i=a.join(", ");return this.conn.set(3,`Invalid relay URLs: ${i}. Relay URLs must start with 'wss://' or 'ws://'`),console.error(`Nostr-Components: ${o}: ${this.errorMessage}`),!1}}}else return this.conn.set(3,`Invalid theme '${e}'. Accepted values are 'light', 'dark'`),console.error(`Nostr-Components: ${o}: ${this.errorMessage}`),!1;return this.errorMessage="",!0}async connectToNostr(){var s,o;const e=++this.connectSeq;this.conn.set(1);try{if(await this.nostrService.connectToNostr(this.getRelays()),e!==this.connectSeq)return;this.conn.set(2),(s=this.nostrReadyResolve)==null||s.call(this);try{this.onNostrRelaysConnected()}catch(n){console.error("Error in onNostrRelaysConnected hook:",n)}}catch(n){if(e!==this.connectSeq)return;console.error("Failed to connect to Nostr relays:",n),this.conn.set(3,"Failed to connect to relays"),(o=this.nostrReadyReject)==null||o.call(this,n)}}ensureNostrConnected(){return this.nostrReady}getRelays(){return C(this.getAttribute("relays"))}getTheme(){this.theme=L(this.getAttribute("data-theme"))}delegateEvent(e,s,o){var i;const n=this.shadowRoot;if(!n)return;const a=`${e}:${s}`;(i=this._delegated)!=null&&i.has(a)||(this._delegated||(this._delegated=new Set),this._delegated.add(a),n.addEventListener(e,u=>{u.target.closest(s)&&o(u)}))}addDelegatedListener(e,s,o){this.delegateEvent(e,s,o)}renderError(e){return`Error: ${e}`}updateHostClasses(){const e=this.computeOverall()===1,s=this.computeOverall()===3,o=this.computeOverall()===2;this.classList.remove("is-clickable","is-disabled","is-error"),e?this.classList.add("is-disabled"):s?this.classList.add("is-error"):o&&this.classList.add("is-clickable")}render(){this.updateHostClasses(),this.renderContent()}handleNjumpClick(e,s,o){if(this.computeOverall()!==2)return;const n=new CustomEvent(e,{detail:s,bubbles:!0,composed:!0,cancelable:!0});this.dispatchEvent(n)&&window.open(`https://njump.me/${o}`,"_blank","noopener,noreferrer")}resetNostrReadyBarrier(){this.connectSeq++,this.nostrReady=new Promise((e,s)=>{this.nostrReadyResolve=e,this.nostrReadyReject=s})}}function x(){return`
2
2
  :host {
3
3
  /* === GENERIC DESIGN TOKENS === */
4
4
  --nostrc-color-background: #ffffff;
@@ -141,5 +141,5 @@ var w=Object.defineProperty;var k=(t,r,e)=>r in t?w(t,r,{enumerable:!0,configura
141
141
  font-size: 2em;
142
142
  margin: auto;
143
143
  }
144
- `};export{K as N,f as a,S as b,F as c,H as d,B as e,q as f,V as g,M as h,N as i,U as j,D as k,z as l,O as m,I as n,P as p,j as v};
145
- //# sourceMappingURL=base-styles-DC0ilu4S.js.map
144
+ `};export{f as N,S as a,D as b,K as c,z as d,B as e,H as f,V as g,I as h,F as i,M as j,q as k,j as l,O as m,N as n,P as p,U as v};
145
+ //# sourceMappingURL=base-styles-Dmuzg8I4.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"base-styles-DC0ilu4S.js","sources":["../../src/common/utils.ts","../../src/base/base-component/nostr-base-component.ts","../../src/common/base-styles.ts"],"sourcesContent":["// SPDX-License-Identifier: MIT\n\nimport NDK, { NDKKind, NDKEvent } from '@nostr-dev-kit/ndk';\nimport { nip19 } from \"nostr-tools\";\n\n\nimport { Theme } from './types';\nimport { DEFAULT_RELAYS, MILLISATS_PER_SAT } from './constants';\n\nexport const decodeNpub = (npub: string): string => {\n if (typeof npub !== 'string' || !npub.startsWith('npub1')) {\n return '';\n }\n\n try {\n const decoded = nip19.decode(npub);\n if (decoded && typeof decoded.data === 'string') {\n return decoded.data;\n }\n } catch (error) {\n console.error('Failed to decode npub:', error);\n }\n \n return '';\n};\n\n/**\n * Convert hex pubkey to npub\n */\nexport function hexToNpub(hex: string): string {\n if (!hex || !isValidHex(hex)) return '';\n try {\n return nip19.npubEncode(hex.toLowerCase());\n } catch (error) {\n console.error('Failed to encode hex to npub:', error);\n return '';\n }\n}\n\n// Could be npub, note1, naddr, nsec, etc.,\nexport const decodeNip19Entity = (entity: string): any => {\n if (typeof entity !== 'string' || !/^[a-z0-9]+1[ac-hj-np-z02-9]+/.test(entity)) {\n return null;\n }\n\n try {\n const decoded = nip19.decode(entity);\n return decoded?.data ?? null;\n } catch (error) {\n console.error('Failed to decode NIP-19 entity:', error);\n return null;\n }\n};\n\nexport function maskNPub(npubString: string = '', length = 3) {\n const npubLength = npubString.length;\n\n if (!npubString.startsWith('npub1')) {\n return 'Invalid nPub: expected npub1...';\n }\n\n if (!validateNpub(npubString)) {\n return 'Invalid nPub';\n }\n\n let result = 'npub1';\n\n for (let i = 5; i < length + 5; i++) {\n result += npubString[i];\n }\n\n result += '...';\n\n let suffix = '';\n for (let i = npubLength - 1; i >= npubLength - length; i--) {\n suffix = npubString[i] + suffix;\n }\n\n result += suffix;\n\n return result;\n}\n\nexport type Stats = {\n likes: number;\n reposts: number;\n zaps: number;\n replies: number;\n};\n\nexport async function getPostStats(ndk: NDK, postId: string): Promise<Stats> {\n const reposts = await ndk.fetchEvents({\n kinds: [NDKKind.Repost],\n '#e': [postId || ''],\n });\n\n const isDirectRepost = (repost: NDKEvent): boolean => {\n const pTagCounts = repost.tags.filter(tag => tag[0] === 'p').length;\n return pTagCounts === 1;\n };\n\n const isDirectReply = (reply: NDKEvent): boolean => {\n const eTagsCount = reply.tags.filter(tag => tag[0] === 'e').length;\n return eTagsCount === 1;\n };\n\n // Only take the count of direct reposts\n const repostsCount = Array.from(reposts).filter(isDirectRepost).length;\n\n const likes = await ndk.fetchEvents({\n kinds: [NDKKind.Reaction],\n '#e': [postId || ''],\n });\n\n // TODO: Add zap receipt validation - https://github.com/nostr-protocol/nips/blob/master/57.md#appendix-f-validating-zap-receipts\n // const zaps = await ndk.fetchEvents({\n // kinds: [NDKKind.Zap],\n // '#e': [postId || '']\n // });\n\n // const zapAmount = Array.from(zaps).reduce((prev, curr) => {\n // const bolt11Tag = curr.getMatchingTags('bolt11');\n\n // if(\n // !bolt11Tag ||\n // !Array.isArray(bolt11Tag) ||\n // bolt11Tag.length === 0 ||\n // !bolt11Tag[0] ||\n // !Array.isArray(bolt11Tag[0]) ||\n // (bolt11Tag[0] as string[]).length === 0\n // ) {\n // return prev;\n // }\n\n // const bolt11 = bolt11Tag[0][1];\n\n // const decodedbol11 = decode(bolt11);\n\n // const amountSection = decodedbol11.sections.find(section => section.name === 'amount');\n\n // if(amountSection) {\n // const millisats = Number(amountSection.value);\n\n // return prev + millisats;\n // }\n\n // return prev;\n // }, 0);\n\n const zapAmount = 0;\n\n const replies = await ndk.fetchEvents({\n kinds: [NDKKind.Text],\n '#e': [postId || ''],\n });\n\n // Only take the direct replies\n // https://github.com/nostr-protocol/nips/blob/master/10.md#positional-e-tags-deprecated\n const replyCount = Array.from(replies).filter(isDirectReply).length;\n\n return {\n likes: likes.size,\n reposts: repostsCount,\n zaps: zapAmount / MILLISATS_PER_SAT,\n replies: replyCount,\n };\n}\n\nexport function parseRelays(relaysAttr: string | null): string[] {\n if (relaysAttr) {\n const list = relaysAttr\n .split(',')\n .map(r => r.trim())\n .filter(Boolean)\n .filter(isValidRelayUrl);\n // fall back to defaults if user provided no valid entries\n return list.length ? Array.from(new Set(list)) : [...DEFAULT_RELAYS];\n }\n return [...DEFAULT_RELAYS];\n}\n\nexport function parseTheme(themeAttr: string | null): Theme {\n\n const theme = themeAttr?.trim().toLowerCase();\n\n if (theme === 'light' || theme === 'dark') {\n return theme;\n }\n\n return 'light';\n}\n\nexport function parseBooleanAttribute(attr: string | null): boolean {\n // Handles: \"true\", \"\", null, \"false\"\n if (attr === null) return false;\n if (attr === '' || attr.toLowerCase() === 'true') return true;\n return false;\n}\n\nexport function escapeHtml(text: string): string {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n}\n\nexport function isValidUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return ['http:', 'https:'].includes(parsed.protocol);\n } catch {\n return false;\n }\n}\n\nexport function isValidRelayUrl(url: string): boolean {\n try {\n const u = new URL(url);\n return u.protocol === 'wss:' || u.protocol === 'ws:';\n } catch {\n return false;\n }\n}\n\nexport function isValidHex(hex: string): boolean {\n return /^[0-9a-fA-F]+$/.test(hex) && hex.length === 64;\n}\n\nexport function validateNpub(npub: string): boolean {\n try {\n const { type } = nip19.decode(npub);\n return type === 'npub';\n } catch (e) {\n return false;\n }\n}\n\nexport function validateNip05(nip05: string): boolean {\n const nip05Regex = /^[a-zA-Z0-9_\\-\\.]+@[a-zA-Z0-9_\\-\\.]+\\.[a-zA-Z]{2,}$/;\n return nip05Regex.test(nip05);\n}\n\nfunction validateBech32OfType(input: string, expected: 'note' | 'nevent'): boolean {\n try {\n const { type } = nip19.decode(input);\n return type === expected;\n } catch {\n return false;\n }\n}\n\nexport function validateNoteId(noteId: string): boolean {\n return validateBech32OfType(noteId, 'note');\n}\n\nexport function validateEventId(eventId: string): boolean {\n return validateBech32OfType(eventId, 'nevent');\n}\n\nexport function copyToClipboard(text: string): Promise<void> {\n return navigator.clipboard.writeText(text)\n}\n\n/**\n * Format timestamp as relative time (e.g., \"2 mins ago\", \"1 month ago\")\n * @param ts Timestamp in seconds\n * @returns Formatted relative time string\n */\nexport function formatRelativeTime(ts: number): string {\n try {\n const now = Date.now();\n const messageTime = ts * 1000;\n const diffMs = now - messageTime;\n\n // Convert to seconds\n const diffSec = Math.floor(diffMs / 1000);\n\n if (diffSec < 60) {\n return 'just now';\n }\n\n // Minutes\n if (diffSec < 3600) {\n const mins = Math.floor(diffSec / 60);\n return `${mins} ${mins === 1 ? 'min' : 'mins'} ago`;\n }\n\n // Hours\n if (diffSec < 86400) {\n const hours = Math.floor(diffSec / 3600);\n return `${hours} ${hours === 1 ? 'hour' : 'hours'} ago`;\n }\n\n // Days\n if (diffSec < 2592000) { // ~30 days\n const days = Math.floor(diffSec / 86400);\n return `${days} ${days === 1 ? 'day' : 'days'} ago`;\n }\n\n // Months\n if (diffSec < 31536000) { // ~365 days\n const months = Math.floor(diffSec / 2592000);\n return `${months} ${months === 1 ? 'month' : 'months'} ago`;\n }\n\n // Years\n const years = Math.floor(diffSec / 31536000);\n return `${years} ${years === 1 ? 'year' : 'years'} ago`;\n } catch (error) {\n console.error('Error formatting relative time:', error);\n return 'unknown';\n }\n}\n","// SPDX-License-Identifier: MIT\n\nimport { NostrService } from '../../common/nostr-service';\nimport { Theme } from '../../common/types';\nimport { parseRelays, parseTheme, isValidRelayUrl } from '../../common/utils';\n\nexport enum NCStatus {\n Idle, // 0\n Loading, // 1\n Ready, // 2\n Error, // 3\n}\n\nconst EVT_STATUS = 'nc:status';\n\n/**\n * NostrBaseComponent\n * ==================\n * Foundation for all Nostr web components in this library.\n *\n * Overview\n * - Manages relay connectivity via a shared `NostrService`.\n * - Parses common attributes (`theme`, `relays`) and applies theme.\n * - Provides a generic status management logic, that is extensible by derived classes.\n * - Offers small utilities useful to subclasses (event delegation, error snippet).\n *\n * Observed attributes\n * - `data-theme` — \"light\" | \"dark\"\n * - `relays` — CSV of relay URLs\n * \n * Events\n * - `nc:status` — from base, reflects connection and user/profile loading status\n * \n * TODO: Is this class doing too much work? Time to split into smaller components?\n */\nexport abstract class NostrBaseComponent extends HTMLElement {\n\n protected nostrService: NostrService = NostrService.getInstance();\n\n protected theme: Theme = 'light';\n protected errorMessage: string = '';\n\n protected nostrReady!: Promise<void>;\n protected nostrReadyResolve?: () => void;\n protected nostrReadyReject?: (e: unknown) => void;\n\n protected conn = this.channel('connection');\n\n private _statuses = new Map<string, NCStatus>();\n private _overall: NCStatus = NCStatus.Idle;\n\n // guard to ignore stale connects\n private connectSeq = 0;\n\n constructor(shadow: boolean = true) {\n super();\n if (shadow) this.attachShadow({ mode: 'open' });\n this.resetNostrReadyBarrier();\n }\n\n /** Lifecycle methods */\n static get observedAttributes() {\n return ['data-theme', 'relays'];\n }\n\n connectedCallback() {\n if (this.validateInputs()) {\n this.getTheme();\n // Avoid duplicate connects if a subclass handles it\n if (this.conn.get() === NCStatus.Idle) {\n void this.connectToNostr();\n }\n }\n }\n\n disconnectedCallback() {\n // Clean up delegated event listeners if shadow root exists\n if (this.shadowRoot && (this as any)._delegated) {\n (this as any)._delegated.clear();\n }\n }\n\n attributeChangedCallback(\n name: string,\n oldValue: string | null,\n newValue: string | null\n ) {\n if (oldValue === newValue) return;\n\n if (name === 'data-theme' || name === 'relays') {\n if (this.validateInputs()) {\n if (name === 'relays') {\n this.resetNostrReadyBarrier();\n void this.connectToNostr();\n }\n\n if (name === 'data-theme') {\n this.getTheme();\n this.render();\n }\n }\n }\n }\n\n /** Status map API */\n\n protected setStatusFor(key: string, next: NCStatus, error?: string) {\n const prev = this._statuses.get(key);\n const changed = prev !== next || (next === NCStatus.Error && !!error);\n\n if (!changed) return;\n\n this._statuses.set(key, next);\n\n if (next === NCStatus.Error && error) {\n this.errorMessage = error;\n } else if (prev === NCStatus.Error && next !== NCStatus.Error) {\n this.errorMessage = '';\n }\n\n // Reflect per-key attribute, e.g. user-status=\"loading\"\n const perKeyAttr = `${key}-status`;\n const perKeyVal = NCStatus[next].toLowerCase();\n if (this.getAttribute(perKeyAttr) !== perKeyVal) {\n this.setAttribute(perKeyAttr, perKeyVal);\n }\n\n // Compute & reflect overall for backward-compat CSS\n const overall = this.computeOverall();\n const overallVal = NCStatus[overall].toLowerCase();\n if (this._overall !== overall) {\n this._overall = overall;\n this.setAttribute('status', overallVal);\n this.onStatusChange(overall);\n } else if (overall === NCStatus.Error && error) {\n // propagate error updates even if overall state didn't flip\n this.onStatusChange(overall);\n }\n\n // Emit a single event with structured detail\n this.dispatchEvent(new CustomEvent(EVT_STATUS, {\n detail: {\n key,\n status: next,\n all: this.snapshotStatuses(),\n overall: this._overall,\n errorMessage: this.errorMessage || undefined,\n },\n bubbles: true,\n composed: true,\n }));\n }\n\n protected getStatusFor(key: string): NCStatus {\n return this._statuses.get(key) ?? NCStatus.Idle;\n }\n\n protected snapshotStatuses(): Record<string, NCStatus> {\n return Object.fromEntries(this._statuses.entries());\n }\n\n /** Overall status change hook */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n protected onStatusChange(_overall: NCStatus) { }\n\n /** Hook for subclasses to react when relays are connected */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n protected onNostrRelaysConnected() { }\n\n protected computeOverall(): NCStatus {\n const vals = [...this._statuses.values()];\n if (vals.includes(NCStatus.Error)) return NCStatus.Error;\n if (vals.includes(NCStatus.Loading)) return NCStatus.Loading;\n if (vals.includes(NCStatus.Ready)) return NCStatus.Ready;\n return NCStatus.Idle;\n }\n\n /**\n * Before first channel.set(), the map won't be set and hence get() will return `idle`.\n * In some cases (or maybe in all cases), this is not ideal.\n * In UserComponent, 'user' not set and hence returning `idle`, while\n * 'connect' switched between idle -> loading -> ready\n * and then\n * 'user' switches to loading -> ready,\n * results in loading -> ready -> loading -> ready in profile-badge onStatusChange.\n * \n * To avoid this, this function is called from UserComponent constructor,\n * to set default value as 'loading' without emitting onStatusChange event.\n * \n * Makes sense?\n * Try commenting this function call in UserComponent()\n * and add a log in ProfileBadge :: onStatusChange. You will get it.\n */\n protected initChannelStatus(key: string, status: NCStatus, opts = { reflectOverall: false }) {\n this._statuses.set(key, status);\n this.setAttribute(`${key}-status`, NCStatus[status].toLowerCase());\n if (opts.reflectOverall) {\n const overall = this.computeOverall();\n this._overall = overall;\n this.setAttribute('status', NCStatus[overall].toLowerCase());\n }\n }\n\n protected channel(key: string) {\n return {\n set: (s: NCStatus, e?: string) => this.setStatusFor(key, s, e),\n get: () => this.getStatusFor(key)\n };\n }\n\n /** Protected methods */\n protected validateInputs(): boolean {\n const theme = this.getAttribute('data-theme') || 'light';\n const relays = this.getAttribute('relays');\n const tagName = this.tagName.toLowerCase();\n\n if (!(theme === 'light' || theme === 'dark')) {\n this.conn.set(NCStatus.Error, `Invalid theme '${theme}'. Accepted values are 'light', 'dark'`);\n console.error(`Nostr-Components: ${tagName}: ${this.errorMessage}`);\n return false;\n } else if (relays && typeof relays != 'string') {\n this.conn.set(NCStatus.Error, `Invalid relays list`);\n console.error(`Nostr-Components: ${tagName}: ${this.errorMessage}`);\n return false;\n } else if (relays) {\n const relayList = relays.split(',').map(r => r.trim()).filter(Boolean);\n const invalidRelays = relayList.filter(relay => !isValidRelayUrl(relay));\n \n if (invalidRelays.length > 0) {\n const invalidRelaysList = invalidRelays.join(', ');\n this.conn.set(NCStatus.Error, `Invalid relay URLs: ${invalidRelaysList}. Relay URLs must start with 'wss://' or 'ws://'`);\n console.error(`Nostr-Components: ${tagName}: ${this.errorMessage}`);\n return false;\n }\n }\n\n this.errorMessage = \"\";\n return true;\n }\n\n protected async connectToNostr() {\n const seq = ++this.connectSeq;\n this.conn.set(NCStatus.Loading);\n try {\n await this.nostrService.connectToNostr(this.getRelays());\n if (seq !== this.connectSeq) return; // stale attempt\n this.conn.set(NCStatus.Ready);\n this.nostrReadyResolve?.();\n try {\n this.onNostrRelaysConnected();\n } catch (hookError) {\n console.error('Error in onNostrRelaysConnected hook:', hookError);\n }\n } catch (error) {\n if (seq !== this.connectSeq) return; // stale attempt\n console.error('Failed to connect to Nostr relays:', error);\n this.conn.set(NCStatus.Error, 'Failed to connect to relays');\n this.nostrReadyReject?.(error);\n }\n }\n\n protected ensureNostrConnected(): Promise<void> {\n return this.nostrReady;\n }\n\n protected getRelays() {\n return parseRelays(this.getAttribute('relays'));\n }\n\n protected getTheme() {\n this.theme = parseTheme(this.getAttribute('data-theme'));\n }\n\n /**\n * Delegate events within shadow DOM.\n * Example: this.delegateEvent('click', '#npub-copy', (e) => this.copyNpub(e));\n */\n protected delegateEvent<K extends keyof HTMLElementEventMap>(\n type: K,\n selector: string,\n handler: (event: HTMLElementEventMap[K]) => void\n ) {\n const root = this.shadowRoot;\n if (!root) return;\n\n // Attach once per (event type, selector)\n const key = `${type}:${selector}`;\n if ((this as any)._delegated?.has(key)) return;\n\n if (!(this as any)._delegated) (this as any)._delegated = new Set<string>();\n (this as any)._delegated.add(key);\n\n root.addEventListener(type, (e) => {\n const t = e.target as HTMLElement;\n if (t.closest(selector)) {\n handler(e as any);\n }\n });\n }\n\n protected addDelegatedListener<K extends keyof HTMLElementEventMap>(\n type: K,\n selector: string,\n handler: (event: HTMLElementEventMap[K]) => void\n ) {\n this.delegateEvent(type, selector, handler);\n }\n\n protected renderError(errorMessage: string): string {\n return `Error: ${errorMessage}`;\n }\n\n /**\n * Updates host element classes based on component status\n * This is a common pattern used by all components\n */\n protected updateHostClasses() {\n const isLoading = this.computeOverall() === NCStatus.Loading;\n const isError = this.computeOverall() === NCStatus.Error;\n const isReady = this.computeOverall() === NCStatus.Ready;\n \n // Remove all state classes\n this.classList.remove('is-clickable', 'is-disabled', 'is-error');\n \n // Add appropriate state class\n if (isLoading) {\n this.classList.add('is-disabled');\n } else if (isError) {\n this.classList.add('is-error');\n } else if (isReady) {\n this.classList.add('is-clickable');\n }\n }\n\n /**\n * Base render method that handles common render logic\n * Subclasses should override renderContent() instead of render()\n */\n protected render() {\n this.updateHostClasses();\n this.renderContent();\n }\n\n /**\n * Handles click events with njump.me default action\n * Creates custom event, dispatches it, and opens njump.me if not prevented\n */\n protected handleNjumpClick(\n eventType: string,\n detail: any,\n njumpPath: string\n ): void {\n if (this.computeOverall() !== NCStatus.Ready) return;\n \n const event = new CustomEvent(eventType, {\n detail,\n bubbles: true,\n composed: true,\n cancelable: true,\n });\n \n const notPrevented = this.dispatchEvent(event);\n if (notPrevented) {\n window.open(`https://njump.me/${njumpPath}`, '_blank', 'noopener,noreferrer');\n }\n }\n\n /**\n * Abstract method for component-specific rendering\n * Must be implemented by subclasses\n */\n protected abstract renderContent(): void;\n\n /** Private methods */\n private resetNostrReadyBarrier() {\n this.connectSeq++;\n this.nostrReady = new Promise<void>((resolve, reject) => {\n this.nostrReadyResolve = resolve;\n this.nostrReadyReject = reject;\n });\n }\n}\n","// SPDX-License-Identifier: MIT\n\n/**\n * Base Styles Utility for Nostr Components\n * =========================================\n * \n * This utility provides common base styles that can be shared across components.\n * It includes design tokens, common patterns, and utility functions.\n */\n\n/**\n * Generates minimal base styles for any Nostr component\n * Includes only essential design tokens and base component styles\n * Uses generic CSS variables that can be overridden by data-theme\n */\nexport function getBaseStyles(): string {\n return `\n :host {\n /* === GENERIC DESIGN TOKENS === */\n --nostrc-color-background: #ffffff;\n --nostrc-color-hover-background: rgba(0, 0, 0, 0.05);\n --nostrc-color-border: #e0e0e0;\n --nostrc-color-error-background: #ffebee;\n --nostrc-color-error-text: #d32f2f;\n --nostrc-color-error-icon: #d32f2f;\n \n /* === TYPOGRAPHY === */\n --nostrc-font-family-primary: ui-sans-serif, system-ui, sans-serif;\n --nostrc-font-family-mono: monospace;\n --nostrc-font-size-base: 1em;\n --nostrc-font-size-small: 0.8em;\n --nostrc-font-size-large: 1.2em;\n --nostrc-font-weight-normal: 400;\n --nostrc-font-weight-medium: 500;\n --nostrc-font-weight-bold: 700;\n \n /* === SPACING === */\n --nostrc-spacing-xs: 4px;\n --nostrc-spacing-sm: 8px;\n --nostrc-spacing-md: 12px;\n --nostrc-spacing-lg: 16px;\n --nostrc-spacing-xl: 20px;\n \n /* === BORDERS === */\n --nostrc-border-radius-sm: 4px;\n --nostrc-border-radius-md: 8px;\n --nostrc-border-radius-lg: 12px;\n --nostrc-border-radius-full: 50%;\n --nostrc-border-width: 1px;\n \n /* === SKELETON === */\n --nostrc-skeleton-color-min: #f0f0f0;\n --nostrc-skeleton-color-max: #e0e0e0;\n --nostrc-skeleton-duration: 1.5s;\n --nostrc-skeleton-timing-function: linear;\n --nostrc-skeleton-iteration-count: infinite;\n \n /* === TRANSITIONS === */\n --nostrc-transition-duration: 0.2s;\n --nostrc-transition-timing: ease;\n }\n \n :host(.is-disabled) {\n opacity: 0.7;\n cursor: not-allowed;\n }\n \n /* === ESSENTIAL UTILITY STYLES === */\n ${styleUtils.skeleton()}\n ${styleUtils.copyButton()}\n ${styleUtils.textRow()}\n ${styleUtils.profileName()}\n ${styleUtils.errorIcon()}\n `;\n}\n\n/**\n * Generates component-specific styles by combining base styles with custom styles\n * Uses CSS theme variables instead of theme prop\n */\nexport function getComponentStyles(customStyles: string): string {\n return `\n <style>\n ${getBaseStyles()}\n /* === COMPONENT-SPECIFIC STYLES === */\n ${customStyles}\n </style>\n `;\n}\n\nexport const styleUtils = {\n /**\n * Generates error state styles\n */\n error: () => `\n :host(.is-error) {\n color: var(--nostrc-color-error-text);\n }\n `,\n \n /**\n * Generates skeleton loading styles\n */\n skeleton: () => `\n .skeleton {\n background: linear-gradient(\n 90deg,\n var(--nostrc-skeleton-color-min) 0%,\n var(--nostrc-skeleton-color-max) 50%,\n var(--nostrc-skeleton-color-min) 100%\n );\n background-size: 200% 100%;\n animation: skeleton-loading var(--nostrc-skeleton-duration) var(--nostrc-skeleton-timing-function) var(--nostrc-skeleton-iteration-count);\n border-radius: var(--nostrc-border-radius-sm);\n height: 16px;\n margin-bottom: var(--nostrc-spacing-xs);\n }\n \n .skeleton:last-child {\n margin-bottom: 0;\n }\n \n @keyframes skeleton-loading {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .skeleton { animation: none; }\n }\n `,\n \n /**\n * Generates copy button styles\n */\n copyButton: () => `\n .nc-copy-btn {\n cursor: pointer;\n opacity: 0.7;\n transition: opacity var(--nostrc-transition-duration) var(--nostrc-transition-timing);\n font-size: 1.5em;\n border: none;\n background: transparent;\n color: var(--nostrc-color-text-muted);\n }\n \n .nc-copy-btn:hover {\n opacity: 1;\n }\n \n .nc-copy-btn.copied {\n color: var(--nostrc-color-accent);\n }\n `,\n \n /**\n * Generates profile name styles\n */\n profileName: () => `\n .nostr-profile-name {\n color: var(--nostrc-theme-text-primary, #333333);\n font-weight: var(--nostrc-font-weight-bold);\n padding-bottom: var(--nostrc-spacing-xs);\n }\n `,\n \n /**\n * Generates text row styles\n */\n textRow: () => `\n .text-row {\n display: flex;\n align-items: center;\n gap: var(--nostrc-spacing-sm);\n font-size: var(--nostrc-font-size-base);\n }\n \n .text-row.mono {\n font-family: var(--nostrc-font-family-mono);\n font-size: var(--nostrc-font-size-small);\n }\n `,\n \n /**\n * Generates error icon styles\n */\n errorIcon: () => `\n .error-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: var(--nostrc-border-radius-full);\n background-color: var(--nostrc-color-error-background);\n color: var(--nostrc-color-error-icon);\n font-size: 2em;\n margin: auto;\n }\n `,\n \n};\n"],"names":["decodeNpub","npub","decoded","nip19","error","hexToNpub","hex","isValidHex","maskNPub","npubString","length","npubLength","validateNpub","result","i","suffix","getPostStats","ndk","postId","reposts","NDKKind","isDirectRepost","repost","tag","isDirectReply","reply","repostsCount","likes","zapAmount","replies","replyCount","MILLISATS_PER_SAT","parseRelays","relaysAttr","list","r","isValidRelayUrl","DEFAULT_RELAYS","parseTheme","themeAttr","theme","parseBooleanAttribute","attr","escapeHtml","text","div","isValidUrl","url","parsed","u","type","validateNip05","nip05","validateBech32OfType","input","expected","validateNoteId","noteId","validateEventId","eventId","copyToClipboard","formatRelativeTime","ts","now","messageTime","diffMs","diffSec","mins","hours","days","months","years","NCStatus","EVT_STATUS","NostrBaseComponent","shadow","__publicField","NostrService","name","oldValue","newValue","key","next","prev","perKeyAttr","perKeyVal","overall","overallVal","_overall","vals","status","opts","e","relays","tagName","invalidRelays","relay","invalidRelaysList","seq","_a","hookError","_b","selector","handler","root","errorMessage","isLoading","isError","isReady","eventType","detail","njumpPath","event","resolve","reject","getBaseStyles","styleUtils","getComponentStyles","customStyles"],"mappings":"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"}
1
+ {"version":3,"file":"base-styles-Dmuzg8I4.js","sources":["../../src/common/utils.ts","../../src/base/base-component/nostr-base-component.ts","../../src/common/base-styles.ts"],"sourcesContent":["// SPDX-License-Identifier: MIT\n\nimport NDK, { NDKKind, NDKEvent } from '@nostr-dev-kit/ndk';\nimport { nip19 } from \"nostr-tools\";\n\n\nimport { Theme } from './types';\nimport { DEFAULT_RELAYS, MILLISATS_PER_SAT } from './constants';\n\nexport const decodeNpub = (npub: string): string => {\n if (typeof npub !== 'string' || !npub.startsWith('npub1')) {\n return '';\n }\n\n try {\n const decoded = nip19.decode(npub);\n if (decoded && typeof decoded.data === 'string') {\n return decoded.data;\n }\n } catch (error) {\n console.error('Failed to decode npub:', error);\n }\n \n return '';\n};\n\n/**\n * Convert hex pubkey to npub\n */\nexport function hexToNpub(hex: string): string {\n if (!hex || !isValidHex(hex)) return '';\n try {\n return nip19.npubEncode(hex.toLowerCase());\n } catch (error) {\n console.error('Failed to encode hex to npub:', error);\n return '';\n }\n}\n\n// Could be npub, note1, naddr, nsec, etc.,\nexport const decodeNip19Entity = (entity: string): any => {\n if (typeof entity !== 'string' || !/^[a-z0-9]+1[ac-hj-np-z02-9]+/.test(entity)) {\n return null;\n }\n\n try {\n const decoded = nip19.decode(entity);\n return decoded?.data ?? null;\n } catch (error) {\n console.error('Failed to decode NIP-19 entity:', error);\n return null;\n }\n};\n\nexport function maskNPub(npubString: string = '', length = 3) {\n const npubLength = npubString.length;\n\n if (!npubString.startsWith('npub1')) {\n return 'Invalid nPub: expected npub1...';\n }\n\n if (!validateNpub(npubString)) {\n return 'Invalid nPub';\n }\n\n let result = 'npub1';\n\n for (let i = 5; i < length + 5; i++) {\n result += npubString[i];\n }\n\n result += '...';\n\n let suffix = '';\n for (let i = npubLength - 1; i >= npubLength - length; i--) {\n suffix = npubString[i] + suffix;\n }\n\n result += suffix;\n\n return result;\n}\n\nexport type Stats = {\n likes: number;\n reposts: number;\n zaps: number;\n replies: number;\n};\n\nexport async function getPostStats(ndk: NDK, postId: string): Promise<Stats> {\n const reposts = await ndk.fetchEvents({\n kinds: [NDKKind.Repost],\n '#e': [postId || ''],\n });\n\n const isDirectRepost = (repost: NDKEvent): boolean => {\n const pTagCounts = repost.tags.filter(tag => tag[0] === 'p').length;\n return pTagCounts === 1;\n };\n\n const isDirectReply = (reply: NDKEvent): boolean => {\n const eTagsCount = reply.tags.filter(tag => tag[0] === 'e').length;\n return eTagsCount === 1;\n };\n\n // Only take the count of direct reposts\n const repostsCount = Array.from(reposts).filter(isDirectRepost).length;\n\n const likes = await ndk.fetchEvents({\n kinds: [NDKKind.Reaction],\n '#e': [postId || ''],\n });\n\n // TODO: Add zap receipt validation - https://github.com/nostr-protocol/nips/blob/master/57.md#appendix-f-validating-zap-receipts\n // const zaps = await ndk.fetchEvents({\n // kinds: [NDKKind.Zap],\n // '#e': [postId || '']\n // });\n\n // const zapAmount = Array.from(zaps).reduce((prev, curr) => {\n // const bolt11Tag = curr.getMatchingTags('bolt11');\n\n // if(\n // !bolt11Tag ||\n // !Array.isArray(bolt11Tag) ||\n // bolt11Tag.length === 0 ||\n // !bolt11Tag[0] ||\n // !Array.isArray(bolt11Tag[0]) ||\n // (bolt11Tag[0] as string[]).length === 0\n // ) {\n // return prev;\n // }\n\n // const bolt11 = bolt11Tag[0][1];\n\n // const decodedbol11 = decode(bolt11);\n\n // const amountSection = decodedbol11.sections.find(section => section.name === 'amount');\n\n // if(amountSection) {\n // const millisats = Number(amountSection.value);\n\n // return prev + millisats;\n // }\n\n // return prev;\n // }, 0);\n\n const zapAmount = 0;\n\n const replies = await ndk.fetchEvents({\n kinds: [NDKKind.Text],\n '#e': [postId || ''],\n });\n\n // Only take the direct replies\n // https://github.com/nostr-protocol/nips/blob/master/10.md#positional-e-tags-deprecated\n const replyCount = Array.from(replies).filter(isDirectReply).length;\n\n return {\n likes: likes.size,\n reposts: repostsCount,\n zaps: zapAmount / MILLISATS_PER_SAT,\n replies: replyCount,\n };\n}\n\nexport function parseRelays(relaysAttr: string | null): string[] {\n if (relaysAttr) {\n const list = relaysAttr\n .split(',')\n .map(r => r.trim())\n .filter(Boolean)\n .filter(isValidRelayUrl);\n // fall back to defaults if user provided no valid entries\n return list.length ? Array.from(new Set(list)) : [...DEFAULT_RELAYS];\n }\n return [...DEFAULT_RELAYS];\n}\n\nexport function parseTheme(themeAttr: string | null): Theme {\n\n const theme = themeAttr?.trim().toLowerCase();\n\n if (theme === 'light' || theme === 'dark') {\n return theme;\n }\n\n return 'light';\n}\n\nexport function parseBooleanAttribute(attr: string | null): boolean {\n // Handles: \"true\", \"\", null, \"false\"\n if (attr === null) return false;\n if (attr === '' || attr.toLowerCase() === 'true') return true;\n return false;\n}\n\nexport function escapeHtml(text: string): string {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n}\n\nexport function isValidUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n return ['http:', 'https:'].includes(parsed.protocol);\n } catch {\n return false;\n }\n}\n\nexport function isValidRelayUrl(url: string): boolean {\n try {\n const u = new URL(url);\n return u.protocol === 'wss:' || u.protocol === 'ws:';\n } catch {\n return false;\n }\n}\n\nexport function isValidHex(hex: string): boolean {\n return /^[0-9a-fA-F]+$/.test(hex) && hex.length === 64;\n}\n\nexport function validateNpub(npub: string): boolean {\n try {\n const { type } = nip19.decode(npub);\n return type === 'npub';\n } catch (e) {\n return false;\n }\n}\n\nexport function validateNip05(nip05: string): boolean {\n const nip05Regex = /^[a-zA-Z0-9_\\-\\.]+@[a-zA-Z0-9_\\-\\.]+\\.[a-zA-Z]{2,}$/;\n return nip05Regex.test(nip05);\n}\n\nfunction validateBech32OfType(input: string, expected: 'note' | 'nevent'): boolean {\n try {\n const { type } = nip19.decode(input);\n return type === expected;\n } catch {\n return false;\n }\n}\n\nexport function validateNoteId(noteId: string): boolean {\n return validateBech32OfType(noteId, 'note');\n}\n\nexport function validateEventId(eventId: string): boolean {\n return validateBech32OfType(eventId, 'nevent');\n}\n\nexport function copyToClipboard(text: string): Promise<void> {\n return navigator.clipboard.writeText(text)\n}\n\n/**\n * Format timestamp as relative time (e.g., \"2 mins ago\", \"1 month ago\")\n * @param ts Timestamp in seconds\n * @returns Formatted relative time string\n */\nexport function formatRelativeTime(ts: number): string {\n try {\n const now = Date.now();\n const messageTime = ts * 1000;\n const diffMs = now - messageTime;\n\n // Convert to seconds\n const diffSec = Math.floor(diffMs / 1000);\n\n if (diffSec < 60) {\n return 'just now';\n }\n\n // Minutes\n if (diffSec < 3600) {\n const mins = Math.floor(diffSec / 60);\n return `${mins} ${mins === 1 ? 'min' : 'mins'} ago`;\n }\n\n // Hours\n if (diffSec < 86400) {\n const hours = Math.floor(diffSec / 3600);\n return `${hours} ${hours === 1 ? 'hour' : 'hours'} ago`;\n }\n\n // Days\n if (diffSec < 2592000) { // ~30 days\n const days = Math.floor(diffSec / 86400);\n return `${days} ${days === 1 ? 'day' : 'days'} ago`;\n }\n\n // Months\n if (diffSec < 31536000) { // ~365 days\n const months = Math.floor(diffSec / 2592000);\n return `${months} ${months === 1 ? 'month' : 'months'} ago`;\n }\n\n // Years\n const years = Math.floor(diffSec / 31536000);\n return `${years} ${years === 1 ? 'year' : 'years'} ago`;\n } catch (error) {\n console.error('Error formatting relative time:', error);\n return 'unknown';\n }\n}\n","// SPDX-License-Identifier: MIT\n\nimport { NostrService } from '../../common/nostr-service';\nimport { Theme } from '../../common/types';\nimport { parseRelays, parseTheme, isValidRelayUrl } from '../../common/utils';\n\nexport enum NCStatus {\n Idle, // 0\n Loading, // 1\n Ready, // 2\n Error, // 3\n}\n\nconst EVT_STATUS = 'nc:status';\n\n/**\n * NostrBaseComponent\n * ==================\n * Foundation for all Nostr web components in this library.\n *\n * Overview\n * - Manages relay connectivity via a shared `NostrService`.\n * - Parses common attributes (`theme`, `relays`) and applies theme.\n * - Provides a generic status management logic, that is extensible by derived classes.\n * - Offers small utilities useful to subclasses (event delegation, error snippet).\n *\n * Observed attributes\n * - `data-theme` — \"light\" | \"dark\"\n * - `relays` — CSV of relay URLs\n * \n * Events\n * - `nc:status` — from base, reflects connection and user/profile loading status\n * \n * TODO: Is this class doing too much work? Time to split into smaller components?\n */\nexport abstract class NostrBaseComponent extends HTMLElement {\n\n protected nostrService: NostrService = NostrService.getInstance();\n\n protected theme: Theme = 'light';\n protected errorMessage: string = '';\n\n protected nostrReady!: Promise<void>;\n protected nostrReadyResolve?: () => void;\n protected nostrReadyReject?: (e: unknown) => void;\n\n protected conn = this.channel('connection');\n\n private _statuses = new Map<string, NCStatus>();\n private _overall: NCStatus = NCStatus.Idle;\n\n // guard to ignore stale connects\n private connectSeq = 0;\n\n constructor(shadow: boolean = true) {\n super();\n if (shadow) this.attachShadow({ mode: 'open' });\n this.resetNostrReadyBarrier();\n }\n\n /** Lifecycle methods */\n static get observedAttributes() {\n return ['data-theme', 'relays'];\n }\n\n connectedCallback() {\n if (this.validateInputs()) {\n this.getTheme();\n // Avoid duplicate connects if a subclass handles it\n if (this.conn.get() === NCStatus.Idle) {\n void this.connectToNostr();\n }\n }\n }\n\n disconnectedCallback() {\n // Clean up delegated event listeners if shadow root exists\n if (this.shadowRoot && (this as any)._delegated) {\n (this as any)._delegated.clear();\n }\n }\n\n attributeChangedCallback(\n name: string,\n oldValue: string | null,\n newValue: string | null\n ) {\n if (oldValue === newValue) return;\n\n if (name === 'data-theme' || name === 'relays') {\n if (this.validateInputs()) {\n if (name === 'relays') {\n this.resetNostrReadyBarrier();\n void this.connectToNostr();\n }\n\n if (name === 'data-theme') {\n this.getTheme();\n this.render();\n }\n }\n }\n }\n\n /** Status map API */\n\n protected setStatusFor(key: string, next: NCStatus, error?: string) {\n const prev = this._statuses.get(key);\n const changed = prev !== next || (next === NCStatus.Error && !!error);\n\n if (!changed) return;\n\n this._statuses.set(key, next);\n\n if (next === NCStatus.Error && error) {\n this.errorMessage = error;\n } else if (prev === NCStatus.Error && next !== NCStatus.Error) {\n this.errorMessage = '';\n }\n\n // Reflect per-key attribute, e.g. user-status=\"loading\"\n const perKeyAttr = `${key}-status`;\n const perKeyVal = NCStatus[next].toLowerCase();\n if (this.getAttribute(perKeyAttr) !== perKeyVal) {\n this.setAttribute(perKeyAttr, perKeyVal);\n }\n\n // Compute & reflect overall for backward-compat CSS\n const overall = this.computeOverall();\n const overallVal = NCStatus[overall].toLowerCase();\n if (this._overall !== overall) {\n this._overall = overall;\n this.setAttribute('status', overallVal);\n this.onStatusChange(overall);\n } else if (overall === NCStatus.Error && error) {\n // propagate error updates even if overall state didn't flip\n this.onStatusChange(overall);\n }\n\n // Emit a single event with structured detail\n this.dispatchEvent(new CustomEvent(EVT_STATUS, {\n detail: {\n key,\n status: next,\n all: this.snapshotStatuses(),\n overall: this._overall,\n errorMessage: this.errorMessage || undefined,\n },\n bubbles: true,\n composed: true,\n }));\n }\n\n protected getStatusFor(key: string): NCStatus {\n return this._statuses.get(key) ?? NCStatus.Idle;\n }\n\n protected snapshotStatuses(): Record<string, NCStatus> {\n return Object.fromEntries(this._statuses.entries());\n }\n\n /** Overall status change hook */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n protected onStatusChange(_overall: NCStatus) { }\n\n /** Hook for subclasses to react when relays are connected */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n protected onNostrRelaysConnected() { }\n\n protected computeOverall(): NCStatus {\n const vals = [...this._statuses.values()];\n if (vals.includes(NCStatus.Error)) return NCStatus.Error;\n if (vals.includes(NCStatus.Loading)) return NCStatus.Loading;\n if (vals.includes(NCStatus.Ready)) return NCStatus.Ready;\n return NCStatus.Idle;\n }\n\n /**\n * Before first channel.set(), the map won't be set and hence get() will return `idle`.\n * In some cases (or maybe in all cases), this is not ideal.\n * In UserComponent, 'user' not set and hence returning `idle`, while\n * 'connect' switched between idle -> loading -> ready\n * and then\n * 'user' switches to loading -> ready,\n * results in loading -> ready -> loading -> ready in profile-badge onStatusChange.\n * \n * To avoid this, this function is called from UserComponent constructor,\n * to set default value as 'loading' without emitting onStatusChange event.\n * \n * Makes sense?\n * Try commenting this function call in UserComponent()\n * and add a log in ProfileBadge :: onStatusChange. You will get it.\n */\n protected initChannelStatus(key: string, status: NCStatus, opts = { reflectOverall: false }) {\n this._statuses.set(key, status);\n this.setAttribute(`${key}-status`, NCStatus[status].toLowerCase());\n if (opts.reflectOverall) {\n const overall = this.computeOverall();\n this._overall = overall;\n this.setAttribute('status', NCStatus[overall].toLowerCase());\n }\n }\n\n protected channel(key: string) {\n return {\n set: (s: NCStatus, e?: string) => this.setStatusFor(key, s, e),\n get: () => this.getStatusFor(key)\n };\n }\n\n /** Protected methods */\n protected validateInputs(): boolean {\n const theme = this.getAttribute('data-theme') || 'light';\n const relays = this.getAttribute('relays');\n const tagName = this.tagName.toLowerCase();\n\n if (!(theme === 'light' || theme === 'dark')) {\n this.conn.set(NCStatus.Error, `Invalid theme '${theme}'. Accepted values are 'light', 'dark'`);\n console.error(`Nostr-Components: ${tagName}: ${this.errorMessage}`);\n return false;\n } else if (relays && typeof relays != 'string') {\n this.conn.set(NCStatus.Error, `Invalid relays list`);\n console.error(`Nostr-Components: ${tagName}: ${this.errorMessage}`);\n return false;\n } else if (relays) {\n const relayList = relays.split(',').map(r => r.trim()).filter(Boolean);\n const invalidRelays = relayList.filter(relay => !isValidRelayUrl(relay));\n \n if (invalidRelays.length > 0) {\n const invalidRelaysList = invalidRelays.join(', ');\n this.conn.set(NCStatus.Error, `Invalid relay URLs: ${invalidRelaysList}. Relay URLs must start with 'wss://' or 'ws://'`);\n console.error(`Nostr-Components: ${tagName}: ${this.errorMessage}`);\n return false;\n }\n }\n\n this.errorMessage = \"\";\n return true;\n }\n\n protected async connectToNostr() {\n const seq = ++this.connectSeq;\n this.conn.set(NCStatus.Loading);\n try {\n await this.nostrService.connectToNostr(this.getRelays());\n if (seq !== this.connectSeq) return; // stale attempt\n this.conn.set(NCStatus.Ready);\n this.nostrReadyResolve?.();\n try {\n this.onNostrRelaysConnected();\n } catch (hookError) {\n console.error('Error in onNostrRelaysConnected hook:', hookError);\n }\n } catch (error) {\n if (seq !== this.connectSeq) return; // stale attempt\n console.error('Failed to connect to Nostr relays:', error);\n this.conn.set(NCStatus.Error, 'Failed to connect to relays');\n this.nostrReadyReject?.(error);\n }\n }\n\n protected ensureNostrConnected(): Promise<void> {\n return this.nostrReady;\n }\n\n protected getRelays() {\n return parseRelays(this.getAttribute('relays'));\n }\n\n protected getTheme() {\n this.theme = parseTheme(this.getAttribute('data-theme'));\n }\n\n /**\n * Delegate events within shadow DOM.\n * Example: this.delegateEvent('click', '#npub-copy', (e) => this.copyNpub(e));\n */\n protected delegateEvent<K extends keyof HTMLElementEventMap>(\n type: K,\n selector: string,\n handler: (event: HTMLElementEventMap[K]) => void\n ) {\n const root = this.shadowRoot;\n if (!root) return;\n\n // Attach once per (event type, selector)\n const key = `${type}:${selector}`;\n if ((this as any)._delegated?.has(key)) return;\n\n if (!(this as any)._delegated) (this as any)._delegated = new Set<string>();\n (this as any)._delegated.add(key);\n\n root.addEventListener(type, (e) => {\n const t = e.target as HTMLElement;\n if (t.closest(selector)) {\n handler(e as any);\n }\n });\n }\n\n protected addDelegatedListener<K extends keyof HTMLElementEventMap>(\n type: K,\n selector: string,\n handler: (event: HTMLElementEventMap[K]) => void\n ) {\n this.delegateEvent(type, selector, handler);\n }\n\n protected renderError(errorMessage: string): string {\n return `Error: ${errorMessage}`;\n }\n\n /**\n * Updates host element classes based on component status\n * This is a common pattern used by all components\n */\n protected updateHostClasses() {\n const isLoading = this.computeOverall() === NCStatus.Loading;\n const isError = this.computeOverall() === NCStatus.Error;\n const isReady = this.computeOverall() === NCStatus.Ready;\n \n // Remove all state classes\n this.classList.remove('is-clickable', 'is-disabled', 'is-error');\n \n // Add appropriate state class\n if (isLoading) {\n this.classList.add('is-disabled');\n } else if (isError) {\n this.classList.add('is-error');\n } else if (isReady) {\n this.classList.add('is-clickable');\n }\n }\n\n /**\n * Base render method that handles common render logic\n * Subclasses should override renderContent() instead of render()\n */\n protected render() {\n this.updateHostClasses();\n this.renderContent();\n }\n\n /**\n * Handles click events with njump.me default action\n * Creates custom event, dispatches it, and opens njump.me if not prevented\n */\n protected handleNjumpClick(\n eventType: string,\n detail: any,\n njumpPath: string\n ): void {\n if (this.computeOverall() !== NCStatus.Ready) return;\n \n const event = new CustomEvent(eventType, {\n detail,\n bubbles: true,\n composed: true,\n cancelable: true,\n });\n \n const notPrevented = this.dispatchEvent(event);\n if (notPrevented) {\n window.open(`https://njump.me/${njumpPath}`, '_blank', 'noopener,noreferrer');\n }\n }\n\n /**\n * Abstract method for component-specific rendering\n * Must be implemented by subclasses\n */\n protected abstract renderContent(): void;\n\n /** Private methods */\n private resetNostrReadyBarrier() {\n this.connectSeq++;\n this.nostrReady = new Promise<void>((resolve, reject) => {\n this.nostrReadyResolve = resolve;\n this.nostrReadyReject = reject;\n });\n }\n}\n","// SPDX-License-Identifier: MIT\n\n/**\n * Base Styles Utility for Nostr Components\n * =========================================\n * \n * This utility provides common base styles that can be shared across components.\n * It includes design tokens, common patterns, and utility functions.\n */\n\n/**\n * Generates minimal base styles for any Nostr component\n * Includes only essential design tokens and base component styles\n * Uses generic CSS variables that can be overridden by data-theme\n */\nexport function getBaseStyles(): string {\n return `\n :host {\n /* === GENERIC DESIGN TOKENS === */\n --nostrc-color-background: #ffffff;\n --nostrc-color-hover-background: rgba(0, 0, 0, 0.05);\n --nostrc-color-border: #e0e0e0;\n --nostrc-color-error-background: #ffebee;\n --nostrc-color-error-text: #d32f2f;\n --nostrc-color-error-icon: #d32f2f;\n \n /* === TYPOGRAPHY === */\n --nostrc-font-family-primary: ui-sans-serif, system-ui, sans-serif;\n --nostrc-font-family-mono: monospace;\n --nostrc-font-size-base: 1em;\n --nostrc-font-size-small: 0.8em;\n --nostrc-font-size-large: 1.2em;\n --nostrc-font-weight-normal: 400;\n --nostrc-font-weight-medium: 500;\n --nostrc-font-weight-bold: 700;\n \n /* === SPACING === */\n --nostrc-spacing-xs: 4px;\n --nostrc-spacing-sm: 8px;\n --nostrc-spacing-md: 12px;\n --nostrc-spacing-lg: 16px;\n --nostrc-spacing-xl: 20px;\n \n /* === BORDERS === */\n --nostrc-border-radius-sm: 4px;\n --nostrc-border-radius-md: 8px;\n --nostrc-border-radius-lg: 12px;\n --nostrc-border-radius-full: 50%;\n --nostrc-border-width: 1px;\n \n /* === SKELETON === */\n --nostrc-skeleton-color-min: #f0f0f0;\n --nostrc-skeleton-color-max: #e0e0e0;\n --nostrc-skeleton-duration: 1.5s;\n --nostrc-skeleton-timing-function: linear;\n --nostrc-skeleton-iteration-count: infinite;\n \n /* === TRANSITIONS === */\n --nostrc-transition-duration: 0.2s;\n --nostrc-transition-timing: ease;\n }\n \n :host(.is-disabled) {\n opacity: 0.7;\n cursor: not-allowed;\n }\n \n /* === ESSENTIAL UTILITY STYLES === */\n ${styleUtils.skeleton()}\n ${styleUtils.copyButton()}\n ${styleUtils.textRow()}\n ${styleUtils.profileName()}\n ${styleUtils.errorIcon()}\n `;\n}\n\n/**\n * Generates component-specific styles by combining base styles with custom styles\n * Uses CSS theme variables instead of theme prop\n */\nexport function getComponentStyles(customStyles: string): string {\n return `\n <style>\n ${getBaseStyles()}\n /* === COMPONENT-SPECIFIC STYLES === */\n ${customStyles}\n </style>\n `;\n}\n\nexport const styleUtils = {\n /**\n * Generates error state styles\n */\n error: () => `\n :host(.is-error) {\n color: var(--nostrc-color-error-text);\n }\n `,\n \n /**\n * Generates skeleton loading styles\n */\n skeleton: () => `\n .skeleton {\n background: linear-gradient(\n 90deg,\n var(--nostrc-skeleton-color-min) 0%,\n var(--nostrc-skeleton-color-max) 50%,\n var(--nostrc-skeleton-color-min) 100%\n );\n background-size: 200% 100%;\n animation: skeleton-loading var(--nostrc-skeleton-duration) var(--nostrc-skeleton-timing-function) var(--nostrc-skeleton-iteration-count);\n border-radius: var(--nostrc-border-radius-sm);\n height: 16px;\n margin-bottom: var(--nostrc-spacing-xs);\n }\n \n .skeleton:last-child {\n margin-bottom: 0;\n }\n \n @keyframes skeleton-loading {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .skeleton { animation: none; }\n }\n `,\n \n /**\n * Generates copy button styles\n */\n copyButton: () => `\n .nc-copy-btn {\n cursor: pointer;\n opacity: 0.7;\n transition: opacity var(--nostrc-transition-duration) var(--nostrc-transition-timing);\n font-size: 1.5em;\n border: none;\n background: transparent;\n color: var(--nostrc-color-text-muted);\n }\n \n .nc-copy-btn:hover {\n opacity: 1;\n }\n \n .nc-copy-btn.copied {\n color: var(--nostrc-color-accent);\n }\n `,\n \n /**\n * Generates profile name styles\n */\n profileName: () => `\n .nostr-profile-name {\n color: var(--nostrc-theme-text-primary, #333333);\n font-weight: var(--nostrc-font-weight-bold);\n padding-bottom: var(--nostrc-spacing-xs);\n }\n `,\n \n /**\n * Generates text row styles\n */\n textRow: () => `\n .text-row {\n display: flex;\n align-items: center;\n gap: var(--nostrc-spacing-sm);\n font-size: var(--nostrc-font-size-base);\n }\n \n .text-row.mono {\n font-family: var(--nostrc-font-family-mono);\n font-size: var(--nostrc-font-size-small);\n }\n `,\n \n /**\n * Generates error icon styles\n */\n errorIcon: () => `\n .error-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n border-radius: var(--nostrc-border-radius-full);\n background-color: var(--nostrc-color-error-background);\n color: var(--nostrc-color-error-icon);\n font-size: 2em;\n margin: auto;\n }\n `,\n \n};\n"],"names":["decodeNpub","npub","decoded","nip19","error","hexToNpub","hex","isValidHex","maskNPub","npubString","length","npubLength","validateNpub","result","i","suffix","getPostStats","ndk","postId","reposts","NDKKind","isDirectRepost","repost","tag","isDirectReply","reply","repostsCount","likes","zapAmount","replies","replyCount","MILLISATS_PER_SAT","parseRelays","relaysAttr","list","r","isValidRelayUrl","DEFAULT_RELAYS","parseTheme","themeAttr","theme","parseBooleanAttribute","attr","escapeHtml","text","div","isValidUrl","url","parsed","u","type","validateNip05","nip05","validateBech32OfType","input","expected","validateNoteId","noteId","validateEventId","eventId","copyToClipboard","formatRelativeTime","ts","now","messageTime","diffMs","diffSec","mins","hours","days","months","years","NCStatus","EVT_STATUS","NostrBaseComponent","shadow","__publicField","NostrService","name","oldValue","newValue","key","next","prev","perKeyAttr","perKeyVal","overall","overallVal","_overall","vals","status","opts","e","relays","tagName","invalidRelays","relay","invalidRelaysList","seq","_a","hookError","_b","selector","handler","root","errorMessage","isLoading","isError","isReady","eventType","detail","njumpPath","event","resolve","reject","getBaseStyles","styleUtils","getComponentStyles","customStyles"],"mappings":"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"}
@@ -1,15 +1,15 @@
1
- import{e as r,m,d as f}from"./base-styles-DC0ilu4S.js";function l(e){const{value:a,display:t=a,className:s="",monospace:o=!1,title:n=t,showCopyButton:c=!1}=e,i=s.replace(/[^\w\- ]/g,""),p=r(t),u=r(n),d="&#x2398;";if(c){const y=r(a);return`
2
- <div class="${`text-row nc-copy ${o?"mono":""} ${i}`.trim()}" data-copy="${y}" title="${u}">
1
+ import{e as r,m,f}from"./base-styles-Dmuzg8I4.js";function l(e){const{value:a,display:t=a,className:s="",monospace:o=!1,title:n=t,showCopyButton:c=!1}=e,i=s.replace(/[^\w\- ]/g,""),p=r(t),u=r(n),y="&#x2398;";if(c){const d=r(a);return`
2
+ <div class="${`text-row nc-copy ${o?"mono":""} ${i}`.trim()}" data-copy="${d}" title="${u}">
3
3
  <span class="nc-copy-text">${p}</span>
4
4
  <button type="button"
5
5
  class="nc-copy-btn"
6
6
  aria-label="Copy"
7
7
  title="Copy"
8
- >${d}</button>
8
+ >${y}</button>
9
9
  </div>
10
10
  `}return`
11
11
  <div class="${`text-row ${o?"mono":""} ${i}`.trim()}" title="${u}">
12
12
  ${p}
13
13
  </div>
14
- `}function w(e){return l({value:e,display:m(e),monospace:!0,showCopyButton:!0,title:e})}function v(e){return l({value:e,display:e,monospace:!0,showCopyButton:!0})}function N(e){const{name:a,className:t="",title:s,showCopyButton:o=!1}=e;return l({value:a,display:a,className:`nostr-profile-name ${t}`.trim(),title:s??a,showCopyButton:o})}function g(e){e.addDelegatedListener("click",".nc-copy-btn",async a=>{var n;a.stopPropagation();const t=(n=a.target)==null?void 0:n.closest(".nc-copy-btn");if(!t)return;const s=t==null?void 0:t.closest(".nc-copy"),o=(s==null?void 0:s.dataset.copy)??"";if(o)try{await f(o),t.classList.add("copied");const c=t.getAttribute("aria-label")||"Copy";t.setAttribute("aria-label","Copied!"),setTimeout(()=>{t.classList.remove("copied"),t.setAttribute("aria-label",c)},1200)}catch{}})}export{v as a,w as b,g as c,l as d,N as r};
15
- //# sourceMappingURL=copy-delegation-CcagQMIW.js.map
14
+ `}function w(e){return l({value:e,display:m(e),monospace:!0,showCopyButton:!0,title:e})}function v(e){return l({value:e,display:e,monospace:!0,showCopyButton:!0})}function N(e){const{name:a,className:t="",title:s,showCopyButton:o=!1}=e;return l({value:a,display:a,className:`nostr-profile-name ${t}`.trim(),title:s??a,showCopyButton:o})}function g(e){e.addDelegatedListener("click",".nc-copy-btn",async a=>{var n;a.stopPropagation();const t=(n=a.target)==null?void 0:n.closest(".nc-copy-btn");if(!t)return;const s=t==null?void 0:t.closest(".nc-copy"),o=(s==null?void 0:s.dataset.copy)??"";if(o)try{await f(o),t.classList.add("copied");const c=t.getAttribute("aria-label")||"Copy";t.setAttribute("aria-label","Copied!"),setTimeout(()=>{t.classList.remove("copied"),t.setAttribute("aria-label",c)},1200)}catch{}})}export{v as a,w as b,l as c,g as d,N as r};
15
+ //# sourceMappingURL=copy-delegation-xzt-t_do.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"copy-delegation-CcagQMIW.js","sources":["../../src/base/text-row/render-text-row.ts","../../src/base/text-row/render-npub.ts","../../src/base/text-row/render-nip05.ts","../../src/base/text-row/render-name.ts","../../src/base/copy-delegation.ts"],"sourcesContent":["// SPDX-License-Identifier: MIT\n\nimport { escapeHtml } from '../../common/utils';\n\nexport interface TextRowOptions {\n value: string; // the raw text to copy\n display?: string; // what to show (defaults to value)\n className?: string; // extra classes to add to the row\n monospace?: boolean; // show in mono font\n title?: string; // title/tooltip for the text\n showCopyButton?: boolean; // show copy button (default false)\n}\n\nexport function renderTextRow(opts: TextRowOptions): string {\n const {\n value,\n display = value,\n className = '',\n monospace = false,\n title = display,\n showCopyButton = false,\n } = opts;\n\n // Allow only class token chars to avoid breaking out of class attr\n const safeClassName = className.replace(/[^\\w\\- ]/g, '');\n const safeDisplay = escapeHtml(display);\n const safeTitle = escapeHtml(title);\n const iconHtml = '&#x2398;'; // ⎘\n\n if (showCopyButton) {\n const safeValue = escapeHtml(value);\n const rowClass = `text-row nc-copy ${monospace ? 'mono' : ''} ${safeClassName}`.trim();\n return `\n <div class=\"${rowClass}\" data-copy=\"${safeValue}\" title=\"${safeTitle}\">\n <span class=\"nc-copy-text\">${safeDisplay}</span>\n <button type=\"button\" \n class=\"nc-copy-btn\"\n aria-label=\"Copy\"\n title=\"Copy\"\n >${iconHtml}</button>\n </div>\n `;\n }\n\n const rowClass = `text-row ${monospace ? 'mono' : ''} ${safeClassName}`.trim();\n return `\n <div class=\"${rowClass}\" title=\"${safeTitle}\">\n ${safeDisplay}\n </div>\n `;\n}\n","// SPDX-License-Identifier: MIT\n\nimport { renderTextRow } from \"./render-text-row\";\nimport { maskNPub } from \"../../common/utils\";\n\nexport function renderNpub(\n npub: string,\n): string {\n\n return renderTextRow({\n value: npub,\n display: maskNPub(npub),\n monospace: true,\n showCopyButton: true,\n title: npub,\n });\n}","// SPDX-License-Identifier: MIT\n\nimport { renderTextRow } from \"./render-text-row\";\n\nexport function renderNip05(\n nip05: string,\n): string {\n return renderTextRow({\n value: nip05,\n display: nip05,\n monospace: true,\n showCopyButton: true,\n });\n}","// SPDX-License-Identifier: MIT\n\nimport { renderTextRow } from './render-text-row';\n\nexport interface RenderNameOptions {\n name: string;\n className?: string;\n title?: string;\n showCopyButton?: boolean;\n}\n\nexport function renderName(options: RenderNameOptions): string {\n const { name, className = '', title, showCopyButton = false } = options;\n \n return renderTextRow({\n value: name,\n display: name,\n className: `nostr-profile-name ${className}`.trim(),\n title: title ?? name,\n showCopyButton: showCopyButton,\n });\n}\n","// SPDX-License-Identifier: MIT\n\nimport { copyToClipboard } from '../common/utils';\n\ninterface DelegatedHost {\n addDelegatedListener(\n type: string,\n selector: string,\n handler: (e: Event) => void\n ): void;\n }\n\nexport function attachCopyDelegation(host: DelegatedHost) {\n host.addDelegatedListener('click', '.nc-copy-btn', async (e: Event) => {\n e.stopPropagation();\n const btn = (e.target as HTMLElement)?.closest('.nc-copy-btn') as HTMLElement;\n if (!btn) return;\n const row = btn?.closest('.nc-copy') as HTMLElement | null;\n const value = row?.dataset.copy ?? '';\n if (!value) return;\n\n try {\n await copyToClipboard(value);\n btn.classList.add('copied');\n const prev = btn.getAttribute('aria-label') || 'Copy';\n btn.setAttribute('aria-label', 'Copied!');\n setTimeout(() => {\n btn.classList.remove('copied');\n btn.setAttribute('aria-label', prev);\n }, 1200);\n } catch {\n // optional: surface error // todo\n }\n });\n}\n"],"names":["renderTextRow","opts","value","display","className","monospace","title","showCopyButton","safeClassName","safeDisplay","escapeHtml","safeTitle","iconHtml","safeValue","renderNpub","npub","maskNPub","renderNip05","nip05","renderName","options","name","attachCopyDelegation","host","e","btn","_a","row","copyToClipboard","prev"],"mappings":"uDAaO,SAASA,EAAcC,EAA8B,CAC1D,KAAM,CACJ,MAAAC,EACA,QAAAC,EAAUD,EACV,UAAAE,EAAY,GACZ,UAAAC,EAAY,GACZ,MAAAC,EAAQH,EACR,eAAAI,EAAiB,EAAA,EACfN,EAGEO,EAAgBJ,EAAU,QAAQ,YAAa,EAAE,EACjDK,EAAcC,EAAWP,CAAO,EAChCQ,EAAcD,EAAWJ,CAAK,EAC9BM,EAAc,WAEpB,GAAIL,EAAgB,CAClB,MAAMM,EAAYH,EAAWR,CAAK,EAElC,MAAO;AAAA,oBADU,oBAAoBG,EAAY,OAAS,EAAE,IAAIG,CAAa,GAAG,KAAA,CAExD,gBAAgBK,CAAS,YAAYF,CAAS;AAAA,qCACrCF,CAAW;AAAA;AAAA;AAAA;AAAA;AAAA,iBAK/BG,CAAQ;AAAA;AAAA,KAGvB,CAGA,MAAO;AAAA,kBADU,YAAYP,EAAY,OAAS,EAAE,IAAIG,CAAa,GAAG,KAAA,CAEhD,YAAYG,CAAS;AAAA,QACvCF,CAAW;AAAA;AAAA,GAGnB,CC7CO,SAASK,EACdC,EACQ,CAER,OAAOf,EAAc,CACnB,MAAOe,EACP,QAASC,EAASD,CAAI,EACtB,UAAW,GACX,eAAgB,GAChB,MAAOA,CAAA,CACR,CACH,CCZO,SAASE,EACdC,EACQ,CACR,OAAOlB,EAAc,CACnB,MAAOkB,EACP,QAASA,EACT,UAAW,GACX,eAAgB,EAAA,CACjB,CACH,CCFO,SAASC,EAAWC,EAAoC,CAC7D,KAAM,CAAE,KAAAC,EAAM,UAAAjB,EAAY,GAAI,MAAAE,EAAO,eAAAC,EAAiB,IAAUa,EAEhE,OAAOpB,EAAc,CACnB,MAAOqB,EACP,QAASA,EACT,UAAW,sBAAsBjB,CAAS,GAAG,KAAA,EAC7C,MAAOE,GAASe,EAChB,eAAAd,CAAA,CACD,CACH,CCTO,SAASe,EAAqBC,EAAqB,CACxDA,EAAK,qBAAqB,QAAS,eAAgB,MAAOC,GAAa,OACrEA,EAAE,gBAAA,EACF,MAAMC,GAAOC,EAAAF,EAAE,SAAF,YAAAE,EAA0B,QAAQ,gBAC/C,GAAI,CAACD,EAAK,OACV,MAAME,EAAMF,GAAA,YAAAA,EAAK,QAAQ,YACnBvB,GAAQyB,GAAA,YAAAA,EAAK,QAAQ,OAAQ,GACnC,GAAKzB,EAEL,GAAI,CACF,MAAM0B,EAAgB1B,CAAK,EAC3BuB,EAAI,UAAU,IAAI,QAAQ,EAC1B,MAAMI,EAAOJ,EAAI,aAAa,YAAY,GAAK,OAC/CA,EAAI,aAAa,aAAc,SAAS,EACxC,WAAW,IAAM,CACfA,EAAI,UAAU,OAAO,QAAQ,EAC7BA,EAAI,aAAa,aAAcI,CAAI,CACrC,EAAG,IAAI,CACT,MAAQ,CAER,CACF,CAAC,CACH"}
1
+ {"version":3,"file":"copy-delegation-xzt-t_do.js","sources":["../../src/base/text-row/render-text-row.ts","../../src/base/text-row/render-npub.ts","../../src/base/text-row/render-nip05.ts","../../src/base/text-row/render-name.ts","../../src/base/copy-delegation.ts"],"sourcesContent":["// SPDX-License-Identifier: MIT\n\nimport { escapeHtml } from '../../common/utils';\n\nexport interface TextRowOptions {\n value: string; // the raw text to copy\n display?: string; // what to show (defaults to value)\n className?: string; // extra classes to add to the row\n monospace?: boolean; // show in mono font\n title?: string; // title/tooltip for the text\n showCopyButton?: boolean; // show copy button (default false)\n}\n\nexport function renderTextRow(opts: TextRowOptions): string {\n const {\n value,\n display = value,\n className = '',\n monospace = false,\n title = display,\n showCopyButton = false,\n } = opts;\n\n // Allow only class token chars to avoid breaking out of class attr\n const safeClassName = className.replace(/[^\\w\\- ]/g, '');\n const safeDisplay = escapeHtml(display);\n const safeTitle = escapeHtml(title);\n const iconHtml = '&#x2398;'; // ⎘\n\n if (showCopyButton) {\n const safeValue = escapeHtml(value);\n const rowClass = `text-row nc-copy ${monospace ? 'mono' : ''} ${safeClassName}`.trim();\n return `\n <div class=\"${rowClass}\" data-copy=\"${safeValue}\" title=\"${safeTitle}\">\n <span class=\"nc-copy-text\">${safeDisplay}</span>\n <button type=\"button\" \n class=\"nc-copy-btn\"\n aria-label=\"Copy\"\n title=\"Copy\"\n >${iconHtml}</button>\n </div>\n `;\n }\n\n const rowClass = `text-row ${monospace ? 'mono' : ''} ${safeClassName}`.trim();\n return `\n <div class=\"${rowClass}\" title=\"${safeTitle}\">\n ${safeDisplay}\n </div>\n `;\n}\n","// SPDX-License-Identifier: MIT\n\nimport { renderTextRow } from \"./render-text-row\";\nimport { maskNPub } from \"../../common/utils\";\n\nexport function renderNpub(\n npub: string,\n): string {\n\n return renderTextRow({\n value: npub,\n display: maskNPub(npub),\n monospace: true,\n showCopyButton: true,\n title: npub,\n });\n}","// SPDX-License-Identifier: MIT\n\nimport { renderTextRow } from \"./render-text-row\";\n\nexport function renderNip05(\n nip05: string,\n): string {\n return renderTextRow({\n value: nip05,\n display: nip05,\n monospace: true,\n showCopyButton: true,\n });\n}","// SPDX-License-Identifier: MIT\n\nimport { renderTextRow } from './render-text-row';\n\nexport interface RenderNameOptions {\n name: string;\n className?: string;\n title?: string;\n showCopyButton?: boolean;\n}\n\nexport function renderName(options: RenderNameOptions): string {\n const { name, className = '', title, showCopyButton = false } = options;\n \n return renderTextRow({\n value: name,\n display: name,\n className: `nostr-profile-name ${className}`.trim(),\n title: title ?? name,\n showCopyButton: showCopyButton,\n });\n}\n","// SPDX-License-Identifier: MIT\n\nimport { copyToClipboard } from '../common/utils';\n\ninterface DelegatedHost {\n addDelegatedListener(\n type: string,\n selector: string,\n handler: (e: Event) => void\n ): void;\n }\n\nexport function attachCopyDelegation(host: DelegatedHost) {\n host.addDelegatedListener('click', '.nc-copy-btn', async (e: Event) => {\n e.stopPropagation();\n const btn = (e.target as HTMLElement)?.closest('.nc-copy-btn') as HTMLElement;\n if (!btn) return;\n const row = btn?.closest('.nc-copy') as HTMLElement | null;\n const value = row?.dataset.copy ?? '';\n if (!value) return;\n\n try {\n await copyToClipboard(value);\n btn.classList.add('copied');\n const prev = btn.getAttribute('aria-label') || 'Copy';\n btn.setAttribute('aria-label', 'Copied!');\n setTimeout(() => {\n btn.classList.remove('copied');\n btn.setAttribute('aria-label', prev);\n }, 1200);\n } catch {\n // optional: surface error // todo\n }\n });\n}\n"],"names":["renderTextRow","opts","value","display","className","monospace","title","showCopyButton","safeClassName","safeDisplay","escapeHtml","safeTitle","iconHtml","safeValue","renderNpub","npub","maskNPub","renderNip05","nip05","renderName","options","name","attachCopyDelegation","host","e","btn","_a","row","copyToClipboard","prev"],"mappings":"kDAaO,SAASA,EAAcC,EAA8B,CAC1D,KAAM,CACJ,MAAAC,EACA,QAAAC,EAAUD,EACV,UAAAE,EAAY,GACZ,UAAAC,EAAY,GACZ,MAAAC,EAAQH,EACR,eAAAI,EAAiB,EAAA,EACfN,EAGEO,EAAgBJ,EAAU,QAAQ,YAAa,EAAE,EACjDK,EAAcC,EAAWP,CAAO,EAChCQ,EAAcD,EAAWJ,CAAK,EAC9BM,EAAc,WAEpB,GAAIL,EAAgB,CAClB,MAAMM,EAAYH,EAAWR,CAAK,EAElC,MAAO;AAAA,oBADU,oBAAoBG,EAAY,OAAS,EAAE,IAAIG,CAAa,GAAG,KAAA,CAExD,gBAAgBK,CAAS,YAAYF,CAAS;AAAA,qCACrCF,CAAW;AAAA;AAAA;AAAA;AAAA;AAAA,iBAK/BG,CAAQ;AAAA;AAAA,KAGvB,CAGA,MAAO;AAAA,kBADU,YAAYP,EAAY,OAAS,EAAE,IAAIG,CAAa,GAAG,KAAA,CAEhD,YAAYG,CAAS;AAAA,QACvCF,CAAW;AAAA;AAAA,GAGnB,CC7CO,SAASK,EACdC,EACQ,CAER,OAAOf,EAAc,CACnB,MAAOe,EACP,QAASC,EAASD,CAAI,EACtB,UAAW,GACX,eAAgB,GAChB,MAAOA,CAAA,CACR,CACH,CCZO,SAASE,EACdC,EACQ,CACR,OAAOlB,EAAc,CACnB,MAAOkB,EACP,QAASA,EACT,UAAW,GACX,eAAgB,EAAA,CACjB,CACH,CCFO,SAASC,EAAWC,EAAoC,CAC7D,KAAM,CAAE,KAAAC,EAAM,UAAAjB,EAAY,GAAI,MAAAE,EAAO,eAAAC,EAAiB,IAAUa,EAEhE,OAAOpB,EAAc,CACnB,MAAOqB,EACP,QAASA,EACT,UAAW,sBAAsBjB,CAAS,GAAG,KAAA,EAC7C,MAAOE,GAASe,EAChB,eAAAd,CAAA,CACD,CACH,CCTO,SAASe,EAAqBC,EAAqB,CACxDA,EAAK,qBAAqB,QAAS,eAAgB,MAAOC,GAAa,OACrEA,EAAE,gBAAA,EACF,MAAMC,GAAOC,EAAAF,EAAE,SAAF,YAAAE,EAA0B,QAAQ,gBAC/C,GAAI,CAACD,EAAK,OACV,MAAME,EAAMF,GAAA,YAAAA,EAAK,QAAQ,YACnBvB,GAAQyB,GAAA,YAAAA,EAAK,QAAQ,OAAQ,GACnC,GAAKzB,EAEL,GAAI,CACF,MAAM0B,EAAgB1B,CAAK,EAC3BuB,EAAI,UAAU,IAAI,QAAQ,EAC1B,MAAMI,EAAOJ,EAAI,aAAa,YAAY,GAAK,OAC/CA,EAAI,aAAa,aAAc,SAAS,EACxC,WAAW,IAAM,CACfA,EAAI,UAAU,OAAO,QAAQ,EAC7BA,EAAI,aAAa,aAAcI,CAAI,CACrC,EAAG,IAAI,CACT,MAAQ,CAER,CACF,CAAC,CACH"}
Binary file
Binary file
@@ -8,8 +8,13 @@ var d=Object.defineProperty;var c=(a,t,e)=>t in a?d(a,t,{enumerable:!0,configura
8
8
  padding: var(--nostrc-spacing-xl, 20px);
9
9
  background: var(--nostrc-theme-bg, #ffffff);
10
10
  color: var(--nostrc-theme-text-primary, #000000);
11
- position: relative;
11
+ margin: auto;
12
12
  font-family: var(--nostrc-font-family-primary, ui-sans-serif, system-ui, sans-serif);
13
+ /* Ensure dialog is centered when opened */
14
+ position: fixed;
15
+ top: 50%;
16
+ left: 50%;
17
+ transform: translate(-50%, -50%);
13
18
  }
14
19
 
15
20
  .nostr-base-dialog[open] {
@@ -63,4 +68,4 @@ var d=Object.defineProperty;var c=(a,t,e)=>t in a?d(a,t,{enumerable:!0,configura
63
68
  color: var(--nostrc-theme-text-primary, #000000);
64
69
  }
65
70
  `;class g extends HTMLElement{constructor(){super();l(this,"dialog",null)}static get observedAttributes(){return["header","data-theme"]}injectStyles(){if(document.querySelector("style[data-dialog-component-styles]"))return;const e=document.createElement("style");e.setAttribute("data-dialog-component-styles","true"),e.textContent=h(),document.head.appendChild(e)}render(){this.injectStyles();const e=this.getAttribute("header")||"Dialog",o=this.getAttribute("data-theme");this.dialog=document.createElement("dialog"),this.dialog.className="nostr-base-dialog",o&&this.dialog.setAttribute("data-theme",o);const i=document.createElement("div");i.className="dialog-header";const s=document.createElement("h2");s.textContent=e;const n=document.createElement("button");n.className="dialog-close-btn",n.setAttribute("aria-label","Close dialog"),n.textContent="✕",i.appendChild(s),i.appendChild(n);const r=document.createElement("div");for(r.className="dialog-content";this.firstChild;)r.appendChild(this.firstChild);this.dialog.appendChild(i),this.dialog.appendChild(r),document.body.appendChild(this.dialog),this.setupEventListeners()}setupEventListeners(){if(!this.dialog)return;const e=this.dialog.querySelector(".dialog-close-btn");e==null||e.addEventListener("click",()=>{this.close()}),this.dialog.addEventListener("click",o=>{o.target===this.dialog&&this.close()}),this.dialog.addEventListener("cancel",o=>{o.preventDefault(),this.close()}),this.dialog.addEventListener("close",()=>{this.cleanup()})}show(){this.showModal()}showModal(){var e;this.dialog||this.render(),(e=this.dialog)==null||e.showModal()}close(){var e;(e=this.dialog)==null||e.close()}cleanup(){this.dialog&&this.dialog.isConnected&&this.dialog.remove(),this.isConnected&&this.remove(),this.dialog=null}disconnectedCallback(){this.cleanup()}attributeChangedCallback(e,o,i){if(e==="header"&&this.dialog){const s=this.dialog.querySelector(".dialog-header h2");s&&(s.textContent=i||"Dialog")}else e==="data-theme"&&this.dialog&&(i?this.dialog.setAttribute("data-theme",i):this.dialog.removeAttribute("data-theme"))}}customElements.get("dialog-component")||customElements.define("dialog-component",g);
66
- //# sourceMappingURL=dialog-component-Dqg0QU9I.js.map
71
+ //# sourceMappingURL=dialog-component-Da1ZIYh9.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialog-component-Da1ZIYh9.js","sources":["../../src/base/dialog-component/style.ts","../../src/base/dialog-component/dialog-component.ts"],"sourcesContent":["// SPDX-License-Identifier: MIT\n\n/**\n * Base dialog component styles\n * Provides common styling for all dialog components\n */\nexport const getDialogComponentStyles = (): string => {\n return `\n /* Base Dialog Styles */\n .nostr-base-dialog {\n width: 400px;\n max-width: 90vw;\n border: none;\n border-radius: var(--nostrc-border-radius-lg, 10px);\n padding: var(--nostrc-spacing-xl, 20px);\n background: var(--nostrc-theme-bg, #ffffff);\n color: var(--nostrc-theme-text-primary, #000000);\n margin: auto;\n font-family: var(--nostrc-font-family-primary, ui-sans-serif, system-ui, sans-serif);\n /* Ensure dialog is centered when opened */\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n }\n\n .nostr-base-dialog[open] {\n display: block;\n }\n\n .nostr-base-dialog::backdrop {\n background: rgba(0, 0, 0, 0.5);\n }\n\n .dialog-header {\n position: relative;\n margin-bottom: var(--nostrc-spacing-lg, 16px);\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .dialog-header h2 {\n font-size: var(--nostrc-font-size-large, 1.25rem);\n font-weight: var(--nostrc-font-weight-bold, 700);\n margin: 0;\n flex: 1;\n text-align: left;\n padding-top: 2px;\n color: var(--nostrc-theme-text-primary, #000000);\n }\n\n .dialog-close-btn {\n border: none;\n background: var(--nostrc-theme-hover-bg, #f7fafc);\n border-radius: var(--nostrc-border-radius-full, 50%);\n width: 32px;\n height: 32px;\n min-width: 32px;\n font-size: var(--nostrc-font-size-base, 16px);\n cursor: pointer;\n color: var(--nostrc-theme-text-secondary, #666666);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .dialog-close-btn:hover {\n background: var(--nostrc-theme-border, rgba(0, 0, 0, 0.05));\n color: var(--nostrc-theme-text-primary, #000000);\n }\n\n .dialog-content {\n line-height: 1.6;\n color: var(--nostrc-theme-text-primary, #000000);\n }\n `;\n};\n\n","// SPDX-License-Identifier: MIT\n\nimport { getDialogComponentStyles } from './style';\n\n/**\n * Base dialog component that extends HTMLElement\n * Provides common dialog functionality with header, close button, and content area\n * \n * Usage:\n * ```typescript\n * const dialog = document.createElement('dialog-component');\n * dialog.setAttribute('header', 'Dialog Title');\n * dialog.innerHTML = '<p>Your content goes here</p>';\n * dialog.showModal(); // Don't append to body, just call showModal()\n * ```\n * \n * Features:\n * - Header with customizable text\n * - Close button\n * - Click outside to close\n * - ESC key to close\n * - Automatic cleanup on close\n * \n * Important: Only one instance of this component should be added to the DOM at any time.\n * Multiple instances may cause conflicts with event listeners and cleanup behavior.\n */\nexport class DialogComponent extends HTMLElement {\n private dialog: HTMLDialogElement | null = null;\n\n constructor() {\n super();\n }\n\n /**\n * Observed attributes for the component\n */\n static get observedAttributes() {\n return ['header', 'data-theme'];\n }\n\n /**\n * Inject dialog styles into document head\n * Prevents duplicate injection by checking for existing styles\n */\n private injectStyles(): void {\n if (document.querySelector('style[data-dialog-component-styles]')) return;\n \n const style = document.createElement('style');\n style.setAttribute('data-dialog-component-styles', 'true');\n style.textContent = getDialogComponentStyles();\n document.head.appendChild(style);\n }\n\n /**\n * Render the dialog\n */\n private render(): void {\n this.injectStyles();\n\n const headerText = this.getAttribute('header') || 'Dialog';\n const theme = this.getAttribute('data-theme');\n\n this.dialog = document.createElement('dialog');\n this.dialog.className = 'nostr-base-dialog';\n if (theme) {\n this.dialog.setAttribute('data-theme', theme);\n }\n\n const headerDiv = document.createElement('div');\n headerDiv.className = 'dialog-header';\n \n const headerH2 = document.createElement('h2');\n headerH2.textContent = headerText;\n \n const closeBtn = document.createElement('button');\n closeBtn.className = 'dialog-close-btn';\n closeBtn.setAttribute('aria-label', 'Close dialog');\n closeBtn.textContent = '✕';\n \n headerDiv.appendChild(headerH2);\n headerDiv.appendChild(closeBtn);\n\n const contentDiv = document.createElement('div');\n contentDiv.className = 'dialog-content';\n \n // Safely move child nodes from component to dialog content\n // This preserves any existing DOM nodes without using innerHTML\n while (this.firstChild) {\n contentDiv.appendChild(this.firstChild);\n }\n\n this.dialog.appendChild(headerDiv);\n this.dialog.appendChild(contentDiv);\n\n document.body.appendChild(this.dialog);\n\n this.setupEventListeners();\n }\n\n /**\n * Setup event listeners for closing the dialog\n */\n private setupEventListeners(): void {\n if (!this.dialog) return;\n\n // Close button click\n const closeBtn = this.dialog.querySelector('.dialog-close-btn');\n closeBtn?.addEventListener('click', () => {\n this.close();\n });\n\n // Click outside dialog (on backdrop)\n this.dialog.addEventListener('click', (e) => {\n if (e.target === this.dialog) {\n this.close();\n }\n });\n\n // ESC key handler\n this.dialog.addEventListener('cancel', (e) => {\n e.preventDefault();\n this.close();\n });\n\n // Cleanup on close\n this.dialog.addEventListener('close', () => {\n this.cleanup();\n });\n }\n\n /**\n * Show the dialog (alias for showModal)\n */\n public show(): void {\n this.showModal();\n }\n\n /**\n * Show the dialog as modal\n */\n public showModal(): void {\n if (!this.dialog) {\n this.render();\n }\n this.dialog?.showModal();\n }\n\n /**\n * Close the dialog\n */\n public close(): void {\n this.dialog?.close();\n }\n\n /**\n * Cleanup when dialog is closed\n */\n private cleanup(): void {\n if (this.dialog && this.dialog.isConnected) {\n this.dialog.remove();\n }\n if (this.isConnected) {\n this.remove();\n }\n this.dialog = null;\n }\n\n /**\n * Called when component is removed from DOM\n */\n disconnectedCallback(): void {\n this.cleanup();\n }\n\n /**\n * Called when observed attributes change\n */\n attributeChangedCallback(name: string, _oldValue: string, newValue: string): void {\n if (name === 'header' && this.dialog) {\n const heading = this.dialog.querySelector('.dialog-header h2');\n if (heading) {\n heading.textContent = newValue || 'Dialog';\n }\n } else if (name === 'data-theme' && this.dialog) {\n if (newValue) {\n this.dialog.setAttribute('data-theme', newValue);\n } else {\n this.dialog.removeAttribute('data-theme');\n }\n }\n }\n}\n\n// Define custom element\nif (!customElements.get('dialog-component')) {\n customElements.define('dialog-component', DialogComponent);\n}\n\n"],"names":["getDialogComponentStyles","DialogComponent","__publicField","style","headerText","theme","headerDiv","headerH2","closeBtn","contentDiv","e","_a","name","_oldValue","newValue","heading"],"mappings":"oKAMO,MAAMA,EAA2B,IAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,ICmBF,MAAMC,UAAwB,WAAY,CAG/C,aAAc,CACZ,MAAA,EAHMC,EAAA,cAAmC,KAI3C,CAKA,WAAW,oBAAqB,CAC9B,MAAO,CAAC,SAAU,YAAY,CAChC,CAMQ,cAAqB,CAC3B,GAAI,SAAS,cAAc,qCAAqC,EAAG,OAEnE,MAAMC,EAAQ,SAAS,cAAc,OAAO,EAC5CA,EAAM,aAAa,+BAAgC,MAAM,EACzDA,EAAM,YAAcH,EAAA,EACpB,SAAS,KAAK,YAAYG,CAAK,CACjC,CAKQ,QAAe,CACrB,KAAK,aAAA,EAEL,MAAMC,EAAa,KAAK,aAAa,QAAQ,GAAK,SAC5CC,EAAQ,KAAK,aAAa,YAAY,EAE5C,KAAK,OAAS,SAAS,cAAc,QAAQ,EAC7C,KAAK,OAAO,UAAY,oBACpBA,GACF,KAAK,OAAO,aAAa,aAAcA,CAAK,EAG9C,MAAMC,EAAY,SAAS,cAAc,KAAK,EAC9CA,EAAU,UAAY,gBAEtB,MAAMC,EAAW,SAAS,cAAc,IAAI,EAC5CA,EAAS,YAAcH,EAEvB,MAAMI,EAAW,SAAS,cAAc,QAAQ,EAChDA,EAAS,UAAY,mBACrBA,EAAS,aAAa,aAAc,cAAc,EAClDA,EAAS,YAAc,IAEvBF,EAAU,YAAYC,CAAQ,EAC9BD,EAAU,YAAYE,CAAQ,EAE9B,MAAMC,EAAa,SAAS,cAAc,KAAK,EAK/C,IAJAA,EAAW,UAAY,iBAIhB,KAAK,YACVA,EAAW,YAAY,KAAK,UAAU,EAGxC,KAAK,OAAO,YAAYH,CAAS,EACjC,KAAK,OAAO,YAAYG,CAAU,EAElC,SAAS,KAAK,YAAY,KAAK,MAAM,EAErC,KAAK,oBAAA,CACP,CAKQ,qBAA4B,CAClC,GAAI,CAAC,KAAK,OAAQ,OAGlB,MAAMD,EAAW,KAAK,OAAO,cAAc,mBAAmB,EAC9DA,GAAA,MAAAA,EAAU,iBAAiB,QAAS,IAAM,CACxC,KAAK,MAAA,CACP,GAGA,KAAK,OAAO,iBAAiB,QAAUE,GAAM,CACvCA,EAAE,SAAW,KAAK,QACpB,KAAK,MAAA,CAET,CAAC,EAGD,KAAK,OAAO,iBAAiB,SAAWA,GAAM,CAC5CA,EAAE,eAAA,EACF,KAAK,MAAA,CACP,CAAC,EAGD,KAAK,OAAO,iBAAiB,QAAS,IAAM,CAC1C,KAAK,QAAA,CACP,CAAC,CACH,CAKO,MAAa,CAClB,KAAK,UAAA,CACP,CAKO,WAAkB,CDtIpB,IAAAC,ECuIE,KAAK,QACR,KAAK,OAAA,GAEPA,EAAA,KAAK,SAAL,MAAAA,EAAa,WACf,CAKO,OAAc,CDhJhB,IAAAA,GCiJHA,EAAA,KAAK,SAAL,MAAAA,EAAa,OACf,CAKQ,SAAgB,CAClB,KAAK,QAAU,KAAK,OAAO,aAC7B,KAAK,OAAO,OAAA,EAEV,KAAK,aACP,KAAK,OAAA,EAEP,KAAK,OAAS,IAChB,CAKA,sBAA6B,CAC3B,KAAK,QAAA,CACP,CAKA,yBAAyBC,EAAcC,EAAmBC,EAAwB,CAChF,GAAIF,IAAS,UAAY,KAAK,OAAQ,CACpC,MAAMG,EAAU,KAAK,OAAO,cAAc,mBAAmB,EACzDA,IACFA,EAAQ,YAAcD,GAAY,SAEtC,MAAWF,IAAS,cAAgB,KAAK,SACnCE,EACF,KAAK,OAAO,aAAa,aAAcA,CAAQ,EAE/C,KAAK,OAAO,gBAAgB,YAAY,EAG9C,CACF,CAGK,eAAe,IAAI,kBAAkB,GACxC,eAAe,OAAO,mBAAoBb,CAAe"}
@@ -1,5 +1,5 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/zap-utils-BZcaCsT_.js","assets/nostr-service-m3Hgc5Xx.js","assets/nostr-login-service-D2FmscPI.js","assets/preload-helper-D7HrI6pR.js","assets/utils--bxLbhGF.js"])))=>i.map(i=>d[i]);
2
- import{_ as y}from"./preload-helper-D7HrI6pR.js";import"./dialog-component-Dqg0QU9I.js";import{getBatchedProfileMetadata as x,extractProfileMetadataContent as k}from"./zap-utils-BZcaCsT_.js";import{h as p,e as g,f as b,c as v}from"./base-styles-DC0ilu4S.js";import"./nostr-service-m3Hgc5Xx.js";import"./nostr-login-service-D2FmscPI.js";import"./utils--bxLbhGF.js";function $(t="light"){const e=t==="dark";return`
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/zap-utils-QRxLBOst.js","assets/nostr-service-CA0Qx4nJ.js","assets/nostr-login-service-D2FmscPI.js","assets/preload-helper-D7HrI6pR.js","assets/utils--bxLbhGF.js"])))=>i.map(i=>d[i]);
2
+ import{_ as y}from"./preload-helper-D7HrI6pR.js";import"./dialog-component-Da1ZIYh9.js";import{getBatchedProfileMetadata as x,extractProfileMetadataContent as k}from"./zap-utils-QRxLBOst.js";import{j as p,e as g,k as b,i as v}from"./base-styles-Dmuzg8I4.js";import"./nostr-service-CA0Qx4nJ.js";import"./nostr-login-service-D2FmscPI.js";import"./utils--bxLbhGF.js";function $(t="light"){const e=t==="dark";return`
3
3
  .likers-dialog-content {
4
4
  padding: 0;
5
5
  max-height: 60vh;
@@ -234,5 +234,5 @@ import{_ as y}from"./preload-helper-D7HrI6pR.js";import"./dialog-component-Dqg0Q
234
234
  ${t.map((a,n)=>P(a,e[n],n)).join("")}
235
235
  </div>
236
236
  </div>
237
- `}async function N(t,e){const r=t.querySelector(".likers-list");if(!r)return;const a=[...new Set(e.map(n=>n.authorPubkey))];console.log("Nostr-Components: Likers dialog: Fetching profiles for",a.length,"unique authors");try{const n=await x(a),i=new Map;n.forEach(o=>{i.set(o.id,o.profile)});const s=new Map;a.forEach(o=>{s.set(o,p(o))});for(let o=0;o<e.length;o++){const l=e[o],c=i.get(l.authorPubkey),d=s.get(l.authorPubkey)||l.authorPubkey;let u;if(c){const h=k(c);u={...l,authorName:h.display_name||h.name||d,authorPicture:h.picture,authorNpub:d}}else u={...l,authorName:d,authorNpub:d};const f=r.querySelector(`[data-like-index="${o}"]`);if(f){const h=m(u,o);f.outerHTML=h}}console.log("Nostr-Components: Likers dialog: Progressive enhancement completed for",e.length,"like entries")}catch(n){console.error("Nostr-Components: Likers dialog: Error in batched profile enhancement",n),console.log("Nostr-Components: Likers dialog: Falling back to individual profile fetching"),await E(t,e)}}async function E(t,e){const r=t.querySelector(".likers-list");if(!r)return;const a=new Map,n=e.map(async(i,s)=>{if(a.has(i.authorPubkey)){const o=a.get(i.authorPubkey);return{index:s,enhanced:{...i,authorName:o.authorName,authorPicture:o.authorPicture,authorNpub:o.authorNpub}}}try{const{getProfileMetadata:o}=await y(async()=>{const{getProfileMetadata:f}=await import("./zap-utils-BZcaCsT_.js");return{getProfileMetadata:f}},__vite__mapDeps([0,1,2,3,4])),l=await o(i.authorPubkey),c=k(l),d=p(i.authorPubkey),u={...i,authorName:c.display_name||c.name||d,authorPicture:c.picture,authorNpub:d};return a.set(i.authorPubkey,u),{index:s,enhanced:u}}catch(o){console.error("Nostr-Components: Likers dialog: Error fetching profile for",i.authorPubkey,o);const l=p(i.authorPubkey),c={...i,authorName:l,authorNpub:l};return a.set(i.authorPubkey,c),{index:s,enhanced:c}}});for(const i of n)try{const{index:s,enhanced:o}=await i,l=r.querySelector(`[data-like-index="${s}"]`);if(l){const c=m(o,s);l.outerHTML=c}}catch(s){console.error("Nostr-Components: Likers dialog: Error processing profile enhancement",s)}}export{w as injectLikersDialogStyles,D as openLikersDialog};
238
- //# sourceMappingURL=dialog-likers-D3c7WIMp.js.map
237
+ `}async function N(t,e){const r=t.querySelector(".likers-list");if(!r)return;const a=[...new Set(e.map(n=>n.authorPubkey))];console.log("Nostr-Components: Likers dialog: Fetching profiles for",a.length,"unique authors");try{const n=await x(a),i=new Map;n.forEach(o=>{i.set(o.id,o.profile)});const s=new Map;a.forEach(o=>{s.set(o,p(o))});for(let o=0;o<e.length;o++){const l=e[o],c=i.get(l.authorPubkey),d=s.get(l.authorPubkey)||l.authorPubkey;let u;if(c){const h=k(c);u={...l,authorName:h.display_name||h.name||d,authorPicture:h.picture,authorNpub:d}}else u={...l,authorName:d,authorNpub:d};const f=r.querySelector(`[data-like-index="${o}"]`);if(f){const h=m(u,o);f.outerHTML=h}}console.log("Nostr-Components: Likers dialog: Progressive enhancement completed for",e.length,"like entries")}catch(n){console.error("Nostr-Components: Likers dialog: Error in batched profile enhancement",n),console.log("Nostr-Components: Likers dialog: Falling back to individual profile fetching"),await E(t,e)}}async function E(t,e){const r=t.querySelector(".likers-list");if(!r)return;const a=new Map,n=e.map(async(i,s)=>{if(a.has(i.authorPubkey)){const o=a.get(i.authorPubkey);return{index:s,enhanced:{...i,authorName:o.authorName,authorPicture:o.authorPicture,authorNpub:o.authorNpub}}}try{const{getProfileMetadata:o}=await y(async()=>{const{getProfileMetadata:f}=await import("./zap-utils-QRxLBOst.js");return{getProfileMetadata:f}},__vite__mapDeps([0,1,2,3,4])),l=await o(i.authorPubkey),c=k(l),d=p(i.authorPubkey),u={...i,authorName:c.display_name||c.name||d,authorPicture:c.picture,authorNpub:d};return a.set(i.authorPubkey,u),{index:s,enhanced:u}}catch(o){console.error("Nostr-Components: Likers dialog: Error fetching profile for",i.authorPubkey,o);const l=p(i.authorPubkey),c={...i,authorName:l,authorNpub:l};return a.set(i.authorPubkey,c),{index:s,enhanced:c}}});for(const i of n)try{const{index:s,enhanced:o}=await i,l=r.querySelector(`[data-like-index="${s}"]`);if(l){const c=m(o,s);l.outerHTML=c}}catch(s){console.error("Nostr-Components: Likers dialog: Error processing profile enhancement",s)}}export{w as injectLikersDialogStyles,D as openLikersDialog};
238
+ //# sourceMappingURL=dialog-likers-B15m_NxI.js.map