nostr-components 0.2.7 → 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 (60) hide show
  1. package/README.md +115 -186
  2. package/dist/assets/{base-styles-BSEzBDsk.js → base-styles-Dmuzg8I4.js} +2 -2
  3. package/dist/assets/{base-styles-BSEzBDsk.js.map → base-styles-Dmuzg8I4.js.map} +1 -1
  4. package/dist/assets/{copy-delegation-B7y2q5Kn.js → copy-delegation-xzt-t_do.js} +2 -2
  5. package/dist/assets/{copy-delegation-B7y2q5Kn.js.map → copy-delegation-xzt-t_do.js.map} +1 -1
  6. package/dist/assets/dialog-component-Da1ZIYh9.js.map +1 -1
  7. package/dist/assets/{dialog-likers-BqDn2P_3.js → dialog-likers-B15m_NxI.js} +5 -5
  8. package/dist/assets/dialog-likers-B15m_NxI.js.map +1 -0
  9. package/dist/assets/index.esm-ByUtE_cm.js +2159 -0
  10. package/dist/assets/index.esm-ByUtE_cm.js.map +1 -0
  11. package/dist/assets/nip05-utils-BNBHUmkr.js.map +1 -1
  12. package/dist/assets/nostr-login-service-D2FmscPI.js +2 -0
  13. package/dist/assets/nostr-login-service-D2FmscPI.js.map +1 -0
  14. package/dist/assets/nostr-service-CA0Qx4nJ.js +266 -0
  15. package/dist/assets/nostr-service-CA0Qx4nJ.js.map +1 -0
  16. package/dist/assets/{nostr-user-component-Cbs97dlK.js → nostr-user-component-r-MUbTL6.js} +2 -2
  17. package/dist/assets/{nostr-user-component-Cbs97dlK.js.map → nostr-user-component-r-MUbTL6.js.map} +1 -1
  18. package/dist/assets/{pure-DPo-pzxM.js → pure-DOoUcNQv.js} +2 -2
  19. package/dist/assets/pure-DOoUcNQv.js.map +1 -0
  20. package/dist/assets/theme-BN1Bvweb.js.map +1 -1
  21. package/dist/assets/{user-resolver-CMmbtY9Y.js → user-resolver-ArI0680e.js} +2 -2
  22. package/dist/assets/{user-resolver-CMmbtY9Y.js.map → user-resolver-ArI0680e.js.map} +1 -1
  23. package/dist/assets/utils--bxLbhGF.js.map +1 -1
  24. package/dist/assets/zap-utils-QRxLBOst.js +2 -0
  25. package/dist/assets/zap-utils-QRxLBOst.js.map +1 -0
  26. package/dist/components/nostr-comment.es.js +26 -26
  27. package/dist/components/nostr-comment.es.js.map +1 -1
  28. package/dist/components/nostr-dm.es.js +2 -2
  29. package/dist/components/nostr-dm.es.js.map +1 -1
  30. package/dist/components/nostr-follow-button.es.js +6 -7
  31. package/dist/components/nostr-follow-button.es.js.map +1 -1
  32. package/dist/components/nostr-like.es.js +16 -16
  33. package/dist/components/nostr-like.es.js.map +1 -1
  34. package/dist/components/nostr-live-chat.es.js +3 -3
  35. package/dist/components/nostr-live-chat.es.js.map +1 -1
  36. package/dist/components/nostr-post.es.js +19 -19
  37. package/dist/components/nostr-post.es.js.map +1 -1
  38. package/dist/components/nostr-profile-badge.es.js +2 -2
  39. package/dist/components/nostr-profile-badge.es.js.map +1 -1
  40. package/dist/components/nostr-profile.es.js +5 -5
  41. package/dist/components/nostr-profile.es.js.map +1 -1
  42. package/dist/components/nostr-zap.es.js +24 -24
  43. package/dist/components/nostr-zap.es.js.map +1 -1
  44. package/dist/nostr-components.es.js +1 -1
  45. package/dist/nostr-components.es.js.map +1 -1
  46. package/dist/nostr-components.umd.js +2669 -301
  47. package/dist/nostr-components.umd.js.map +1 -1
  48. package/dist/src/common/constants.d.ts +1 -1
  49. package/dist/src/common/nostr-login-service.d.ts +26 -0
  50. package/dist/src/nostr-dm/nostr-dm.d.ts +1 -1
  51. package/dist/src/nostr-like/like-utils.d.ts +2 -6
  52. package/dist/src/nostr-live-chat/nostr-live-chat.d.ts +1 -1
  53. package/dist/src/nostr-zap/zap-utils.d.ts +4 -0
  54. package/package.json +5 -2
  55. package/dist/assets/dialog-likers-BqDn2P_3.js.map +0 -1
  56. package/dist/assets/nostr-service-CnaPxjc6.js +0 -78
  57. package/dist/assets/nostr-service-CnaPxjc6.js.map +0 -1
  58. package/dist/assets/pure-DPo-pzxM.js.map +0 -1
  59. package/dist/assets/zap-utils-KFUD_vTU.js +0 -2
  60. package/dist/assets/zap-utils-KFUD_vTU.js.map +0 -1
package/README.md CHANGED
@@ -6,163 +6,113 @@
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
- 🔹 **[Nostr Zap](#5-nostr-zap-)** - Lightning Network zap button for Nostr
10
- 🔹 **[Nostr Profile Badge](#1-nostr-profile-badge-)** - Compact badge-style profile display
11
- 🔹 **[Nostr Profile](#2-nostr-profile-)** - Full Nostr profile with more details
12
- 🔹 **[Nostr Post](#3-nostr-post-)** - Embed a specific Nostr post
13
- 🔹 **[Nostr Follow](#4-nostr-follow-)** - Follow button for Nostr
14
- 🔹 **[Nostr DM](#6-nostr-dm-)** - Send a direct message on Nostr (Under construction)
15
- 🔹 **[Nostr Live Chat](#7-nostr-live-chat-)** - Real-time chat with message history (Under construction)
16
- 🔹 **[Nostr Comment](#8-nostr-comment-)** - Decentralized comment system for any website (Under construction)
17
- 🔹 **[Wordpress Integration](#9-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)**
18
16
 
19
17
  ## 🛠️ Usage
20
18
 
21
- ### Option 1: Using with Bundlers (Webpack, Vite, React, etc.)
19
+ ### Quick Start
22
20
 
23
- For modern bundler-based projects (React, Vue, Next.js, etc.):
21
+ #### Option 1: CDN (Recommended for Quick Integration)
24
22
 
25
- ```typescript
26
- // Import the main package - this automatically registers all components
27
- import 'nostr-components';
28
-
29
- // Or import specific components only
30
- import 'nostr-components/components/nostr-like';
31
- import 'nostr-components/components/nostr-zap';
32
-
33
- // Import themes
34
- import 'nostr-components/themes/dark';
35
- // or
36
- import 'nostr-components/themes/light';
37
- ```
38
-
39
- Then use the components in your JSX/HTML:
40
-
41
- ```tsx
42
- function MyComponent() {
43
- return (
44
- <>
45
- <nostr-like url="https://example.com" text="Like" />
46
- <nostr-zap npub="npub1..." theme="dark" />
47
- </>
48
- );
49
- }
50
- ```
51
-
52
- **Note:** When importing from the main package (`import 'nostr-components'`), all components are automatically registered. Individual component imports also work and register only that component.
53
-
54
- ### Option 2: Direct Script Tag Usage
55
-
56
- For direct HTML usage without a bundler:
23
+ Add the component script to your HTML's `<head>`. Each component can be loaded individually or use the full bundle.
57
24
 
58
25
  ```html
59
26
  <head>
60
- <!-- Required: Nostr Components Script (choose UMD or ES) -->
61
- <script src="./dist/nostr-components.umd.js"></script>
62
- <!-- Or nostr-components.es.js -->
27
+ <!-- Load individual component (recommended for smaller bundles) -->
28
+ <script type="module" src="https://cdn.jsdelivr.net/npm/nostr-components@latest/dist/components/nostr-follow-button.es.js"></script>
29
+
30
+ <!-- Or load the full bundle -->
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) -->
63
34
  <script>
64
- // Initialize components (only needed for UMD build)
65
- 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
+ }
66
43
  </script>
67
-
68
- <!-- Optional: Glide.js CSS for Post Carousel -->
69
- <!-- Needed only if displaying posts that might contain multiple images/videos -->
70
- <link
71
- rel="stylesheet"
72
- href="https://cdn.jsdelivr.net/npm/@glidejs/glide/dist/css/glide.core.min.css"
73
- />
74
- <link
75
- rel="stylesheet"
76
- href="https://cdn.jsdelivr.net/npm/@glidejs/glide/dist/css/glide.theme.min.css"
77
- />
78
44
  </head>
79
45
  ```
80
46
 
81
- - **UMD (Universal Module Definition):** Use `<script src="./dist/nostr-components.umd.js"></script>` and the initialization script.
82
- - **ES Module:** Use `<script type="module">import NostrComponents from './dist/nostr-components.es.js'; NostrComponents.init();</script>`.
83
-
84
- _Note: Replace `./dist/nostr-components._.js`with the actual path to the file on your server or use a CDN link if available (e.g.,`https://nostr-components.web.app/dist/nostr-components.umd.js`).\*
85
-
86
- ### Using the Components
47
+ #### Option 2: NPM Package
87
48
 
88
- Place the component tags anywhere in your HTML/JSX:
49
+ Install the package via npm:
89
50
 
90
- ---
91
-
92
- ## 1. Nostr Profile Badge 🔖
51
+ ```bash
52
+ npm install nostr-components
53
+ ```
93
54
 
94
- A small badge displaying a Nostr profile with a username and avatar.
55
+ Then import components in your JavaScript/TypeScript:
95
56
 
96
- **Usage:**
57
+ ```javascript
58
+ // Import individual components
59
+ import 'nostr-components/dist/components/nostr-follow-button.es.js';
60
+ import 'nostr-components/dist/components/nostr-zap.es.js';
61
+ import 'nostr-components/dist/components/nostr-like.es.js';
97
62
 
98
- ```html
99
- <head>
100
- <script
101
- type="module"
102
- src="./dist/components/nostr-profile-badge.es.js"
103
- ></script>
104
- </head>
105
- <body>
106
- <nostr-profile-badge
107
- pubkey="npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6"
108
- ></nostr-profile-badge>
109
- </body>
63
+ // Or import the full bundle
64
+ import 'nostr-components/dist/nostr-components.es.js';
110
65
  ```
111
66
 
112
- **Preview:**
113
-
114
- ![Preview of profile badge](images/profile-badge-preview.png)
115
-
116
- ---
67
+ For bundlers (Vite, Webpack, etc.), you can also import the source:
117
68
 
118
- ## 2. Nostr Profile 👤
69
+ ```javascript
70
+ // Import from source (requires bundler)
71
+ import { NostrFollowButton } from 'nostr-components';
72
+ import { NostrZap } from 'nostr-components';
73
+ import { NostrLike } from 'nostr-components';
74
+ ```
119
75
 
120
- A detailed profile card showing avatar, name, bio, notes count, followers, etc,.
76
+ **Note:** When using npm packages, make sure your bundler is configured to handle Web Components properly.
121
77
 
122
- **Usage:**
78
+ **Use the Components:** Place the component tags anywhere in your `<body>`.
123
79
 
124
- ```html
125
- <head>
126
- <script type="module" src="./dist/components/nostr-profile.es.js"></script>
127
- </head>
128
- <body>
129
- <nostr-profile
130
- pubkey="npub1a2cww4kn9wqte4ry70vyfwqyqvpswksna27rtxd8vty6c74era8sdcw83a"
131
- ></nostr-profile>
132
- </body>
133
- ```
80
+ ### Authentication
134
81
 
135
- **Preview:**
82
+ All interactive components (Follow, Like, Zap) require user authentication. Components use [NostrLogin](https://github.com/nostrband/nostr-login) which supports:
83
+ - **NIP-07 Browser Extensions** (Alby, nos2x, etc.)
84
+ - **NIP-46 Remote Signers** (Bunkers)
136
85
 
137
- ![Preview of profile](images/profile-preview.png)
86
+ The authentication flow is handled automatically when users interact with components.
138
87
 
139
88
  ---
140
89
 
141
- ## 3. Nostr Post 📝
90
+ ## 1. Nostr Zap
142
91
 
143
- Embed any Nostr post by providing the event ID.
92
+ A Lightning Network zap button that allows users to send sats to any Nostr user with a lightning address or LNURL.
144
93
 
145
94
  **Usage:**
146
95
 
147
96
  ```html
148
97
  <head>
149
- <script type="module" src="./dist/components/nostr-post.es.js"></script>
98
+ <script type="module" src="https://cdn.jsdelivr.net/npm/nostr-components@latest/dist/components/nostr-zap.es.js"></script>
150
99
  </head>
151
100
  <body>
152
- <nostr-post
153
- eventId="note1t2jvt5vpusrwrxkfu8x8r7q65zzvm32xuur6y7am4zn475r8ucjqmwwhd2"
154
- ></nostr-post>
155
- <!-- Note: The previous example incorrectly used a pubkey, use eventId for posts -->
101
+ <nostr-zap
102
+ npub="npub1qsvv5ttv6mrlh38q8ydmw3gzwq360mdu8re2vr7rk68sqmhmsh4svhsft3"
103
+ theme="dark"
104
+ text="⚡ Zap Me"
105
+ ></nostr-zap>
156
106
  </body>
157
107
  ```
158
108
 
159
109
  **Preview:**
160
110
 
161
- ![Preview of post](images/post-preview.png)
111
+ ![Preview of zap button](images/zap-preview.png)
162
112
 
163
113
  ---
164
114
 
165
- ## 4. Nostr Follow
115
+ ## 2. Nostr Follow
166
116
 
167
117
  A simple button that allows users to follow a Nostr profile.
168
118
 
@@ -172,12 +122,12 @@ A simple button that allows users to follow a Nostr profile.
172
122
  <head>
173
123
  <script
174
124
  type="module"
175
- src="./dist/components/nostr-follow-button.es.js"
125
+ src="https://cdn.jsdelivr.net/npm/nostr-components@latest/dist/components/nostr-follow-button.es.js"
176
126
  ></script>
177
127
  </head>
178
128
  <body>
179
129
  <nostr-follow-button
180
- pubkey="npub1qsvv5ttv6mrlh38q8ydmw3gzwq360mdu8re2vr7rk68sqmhmsh4svhsft3"
130
+ npub="npub1qsvv5ttv6mrlh38q8ydmw3gzwq360mdu8re2vr7rk68sqmhmsh4svhsft3"
181
131
  ></nostr-follow-button>
182
132
  </body>
183
133
  ```
@@ -186,142 +136,111 @@ A simple button that allows users to follow a Nostr profile.
186
136
 
187
137
  ![Preview of follow button](images/follow-button-preview.png)
188
138
 
189
- ## 5. Nostr Zap ⚡
139
+ ---
190
140
 
191
- A Lightning Network zap button that allows users to send sats to any Nostr user with a lightning address or LNURL.
141
+ ## 3. Nostr Like
142
+
143
+ A like button that uses NIP-25 (External Content Reactions) to like any URL on the web.
144
+ When URL is not specified, current URL is taken.
192
145
 
193
146
  **Usage:**
194
147
 
195
148
  ```html
196
149
  <head>
197
- <script type="module" src="./dist/components/nostr-zap.es.js"></script>
150
+ <script type="module" src="https://cdn.jsdelivr.net/npm/nostr-components@latest/dist/components/nostr-like.es.js"></script>
198
151
  </head>
199
152
  <body>
200
- <nostr-zap
201
- npub="npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6"
202
- theme="dark"
203
- button-text="⚡ Zap Me"
204
- button-color="#8a63d2"
205
- amount="1000"
206
- ></nostr-zap>
153
+ <!-- Like the current page URL -->
154
+ <nostr-like></nostr-like>
155
+
156
+ <!-- Like a specific URL with custom text -->
157
+ <nostr-like url="https://github.com/saiy2k/nostr-components" text="❤️"></nostr-like>
207
158
  </body>
208
159
  ```
209
160
 
210
161
  **Preview:**
211
162
 
212
- ![Preview of zap button](images/zap-preview.png)
163
+ ![Preview of like button](images/like-button-preview.png)
213
164
 
214
165
  ---
215
166
 
216
- ## 6. Nostr DM 💬
167
+ ## 4. Nostr Profile Badge
217
168
 
218
- A simple direct message composer for sending one-time messages to any Nostr user.
169
+ A small badge displaying a Nostr profile with a username and avatar.
219
170
 
220
171
  **Usage:**
221
172
 
222
173
  ```html
223
174
  <head>
224
- <script type="module" src="./dist/components/nostr-dm.es.js"></script>
175
+ <script
176
+ type="module"
177
+ src="https://cdn.jsdelivr.net/npm/nostr-components@latest/dist/components/nostr-profile-badge.es.js"
178
+ ></script>
225
179
  </head>
226
180
  <body>
227
- <!-- Basic DM, user will be prompted to enter a recipient -->
228
- <nostr-dm theme="light"></nostr-dm>
229
-
230
- <!-- Pre-configured recipient with npub -->
231
- <nostr-dm
232
- recipient-npub="npub1g53mukxnjkcmr94fhryzkqutdz2ukq4ks0gvy5af25rgmwsl4ngq43drvk"
233
- theme="light"
234
- relays="wss://relay.damus.io,wss://relay.primal.net"
235
- ></nostr-dm>
236
-
237
- <!-- Using NIP-05 identifier -->
238
- <nostr-dm
239
- nip05="user@domain.com"
240
- theme="dark"
241
- ></nostr-dm>
181
+ <nostr-profile-badge
182
+ pubkey="npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6"
183
+ ></nostr-profile-badge>
242
184
  </body>
243
185
  ```
244
186
 
245
-
246
187
  **Preview:**
247
188
 
248
- ![Preview of DM component](images/dm-preview.png)
189
+ ![Preview of profile badge](images/profile-badge-preview.png)
249
190
 
250
191
  ---
251
192
 
252
- ## 7. Nostr Live Chat 💬
193
+ ## 5. Nostr Profile
253
194
 
254
- Real-time chat component with persistent message history and live updates.
195
+ A detailed profile card showing avatar, name, bio, notes count, followers, etc.
255
196
 
256
197
  **Usage:**
257
198
 
258
199
  ```html
259
200
  <head>
260
- <script type="module" src="./dist/components/nostr-live-chat.es.js"></script>
201
+ <script type="module" src="https://cdn.jsdelivr.net/npm/nostr-components@latest/dist/components/nostr-profile.es.js"></script>
261
202
  </head>
262
203
  <body>
263
- <!-- Basic live chat, user will be prompted to enter a recipient -->
264
- <nostr-live-chat theme="light"></nostr-live-chat>
265
-
266
- <!-- Pre-configured recipient with npub -->
267
- <nostr-live-chat
268
- recipient-npub="npub1g53mukxnjkcmr94fhryzkqutdz2ukq4ks0gvy5af25rgmwsl4ngq43drvk"
269
- theme="light"
270
- relays="wss://relay.damus.io,wss://relay.primal.net,wss://relay.snort.social"
271
- ></nostr-live-chat>
272
-
273
- <!-- Using NIP-05 identifier -->
274
- <nostr-live-chat
275
- nip05="user@domain.com"
276
- theme="dark"
277
- ></nostr-live-chat>
204
+ <nostr-profile
205
+ pubkey="npub1a2cww4kn9wqte4ry70vyfwqyqvpswksna27rtxd8vty6c74era8sdcw83a"
206
+ ></nostr-profile>
278
207
  </body>
279
208
  ```
280
209
 
281
-
282
-
283
210
  **Preview:**
284
211
 
285
- ![Preview of live chat component](images/live-chat-preview.png)
212
+ ![Preview of profile](images/profile-preview.png)
286
213
 
287
214
  ---
288
215
 
289
- ## 8. Nostr Comment 💬
216
+ ## 6. Nostr Post
290
217
 
291
- A complete decentralized comment system that stores comments on the Nostr network instead of a traditional database.
218
+ Embed any Nostr post by providing the event ID.
292
219
 
293
220
  **Usage:**
294
221
 
295
222
  ```html
296
223
  <head>
297
- <script type="importmap">
298
- {
299
- "imports": {
300
- "lit": "https://unpkg.com/lit@3.1.0/index.js?module",
301
- "dayjs": "https://unpkg.com/dayjs@1.11.10/dayjs.min.js?module"
302
- }
303
- }
304
- </script>
305
- <script type="module" src="./dist/components/nostr-comment.es.js"></script>
224
+ <script type="module" src="https://cdn.jsdelivr.net/npm/nostr-components@latest/dist/components/nostr-post.es.js"></script>
306
225
  </head>
307
226
  <body>
308
- <nostr-comment
309
- theme="light"
310
- placeholder="Write a comment..."
311
- relays="wss://relay.damus.io,wss://nostr.wine,wss://relay.nostr.net"
312
- ></nostr-comment>
227
+ <nostr-post
228
+ eventId="note1t2jvt5vpusrwrxkfu8x8r7q65zzvm32xuur6y7am4zn475r8ucjqmwwhd2"
229
+ ></nostr-post>
313
230
  </body>
314
231
  ```
315
232
 
316
233
  **Preview:**
317
234
 
318
- ![Preview of comment system](images/comment-preview.png)
235
+ ![Preview of post](images/post-preview.png)
319
236
 
320
237
  ---
321
238
 
322
- ## 9. WordPress Integration
239
+ ## 7. WordPress Integration
240
+
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.
323
242
 
324
- Install the Nostr Components plugin from the WordPress plugin directory to easily embed Nostr content in your posts and pages.
243
+ For more details, see the [WordPress Plugin](https://wordpress.org/plugins/saiy2k-nostr-components/).
325
244
 
326
245
  ---
327
246
 
@@ -333,6 +252,15 @@ Check out our full documentation [here](https://nostr-components.web.app).
333
252
 
334
253
  ## 🛠️ Development
335
254
 
255
+ ### npm Installation Notes (Windows / newer npm versions)
256
+
257
+ If you encounter dependency resolution errors (`ERESOLVE`) when running `npm install`
258
+ with newer versions of npm, use:
259
+
260
+ ````bash
261
+ npm install --legacy-peer-deps
262
+
263
+
336
264
  ### Storybook Setup
337
265
 
338
266
  This project uses Storybook for component development and testing. The setup includes both public showcase stories and private testing stories.
@@ -345,9 +273,10 @@ npm run storybook
345
273
 
346
274
  # Build Storybook for production (excludes testing stories)
347
275
  STORYBOOK_ENV=production npm run build-storybook
348
- ```
276
+ ````
349
277
 
350
278
  **Story Organization:**
279
+
351
280
  - **Public Stories**: Showcase stories for component demos and documentation
352
281
  - **Testing Stories**: Private stories for development testing (excluded from production builds)
353
282
 
@@ -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{c as v,a as p,e as m,M as E,b as T}from"./nostr-service-CnaPxjc6.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`
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;
@@ -142,4 +142,4 @@ var w=Object.defineProperty;var k=(t,r,e)=>r in t?w(t,r,{enumerable:!0,configura
142
142
  margin: auto;
143
143
  }
144
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-BSEzBDsk.js.map
145
+ //# sourceMappingURL=base-styles-Dmuzg8I4.js.map