UNPKG

nostr-web-components

Version:

collection of web components that provide quick access to basic nostr things

997 lines (913 loc) 59.1 kB
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Nostr Web Components - Documentation</title> <script src="https://cdn.tailwindcss.com"></script> <!-- Load all component scripts once --> <script src="dist/nostr-name.js"></script> <script src="dist/nostr-picture.js"></script> <script src="dist/nostr-follow.js"></script> <script src="dist/nostr-text.js"></script> <script src="dist/nostr-user-search.js"></script> <script src="dist/nostr-rsvp.js"></script> <script src="dist/nostr-livestream.js"></script> <script src="dist/nostr-note.js"></script> <script src="dist/nostr-event-json.js"></script> <style> html { scroll-behavior: smooth; } .prose pre { background: #1e293b; color: #e2e8f0; padding: 1rem; border-radius: 0.5rem; overflow-x: auto; } .prose code { background: #f1f5f9; padding: 0.125rem 0.25rem; border-radius: 0.25rem; font-size: 0.875em; } .prose pre code { background: transparent; padding: 0; } </style> <script> function toggleCode(exampleId) { const codeBlock = document.getElementById(exampleId + '-code') const toggleBtn = document.getElementById(exampleId + '-toggle') if (codeBlock.classList.contains('hidden')) { codeBlock.classList.remove('hidden') toggleBtn.textContent = 'Hide Code' } else { codeBlock.classList.add('hidden') toggleBtn.textContent = 'Show Code' } } document.addEventListener('DOMContentLoaded', function () { document.querySelectorAll('[data-example-container]').forEach(container => { const exampleId = container.getAttribute('data-example-container') const codeBlock = document.getElementById(exampleId + '-code-content') const exampleContent = container.querySelector('[data-example-content]') if (exampleContent && codeBlock) { // get the HTML and format it let html = exampleContent.innerHTML // remove style attributes that are dynamically added html = html.replace(/ style="[^"]*"/g, '') // remove indentation const indentation = html.split('\n')[1].match(/ */)[0].length html = html .split('\n') .map(line => line.slice(indentation)) .slice(1, -1) .join('\n') // display codeBlock.textContent = html } }) }) </script> </head> <body class="bg-gray-50"> <!-- Header --> <header class="bg-fuchsia-900 text-white py-12 shadow-lg"> <div class="container mx-auto px-4"> <h1 class="text-5xl font-bold mb-4">Nostr Web Components</h1> <p class="text-xl opacity-90">Simple, reusable web components for Nostr integration with static HTML</p> </div> </header> <div class="container mx-auto px-4 py-8"> <!-- Overview/Summary Section --> <section id="overview" class="mb-16"> <h2 class="text-4xl font-bold mb-8 text-gray-800">Overview</h2> <div class="bg-white rounded-lg shadow-md p-8"> <p class="text-gray-700 mb-6"> Nostr Web Components provides a collection of simple, standalone, reusable and styleable web components for integrating Nostr functionality into static or server-rendered HTML websites. </p> <div class="grid grid-cols-1 md:grid-cols-2 gap-8"> <!-- Guides Column --> <div> <h3 class="text-2xl font-bold mb-4 text-gray-800">Guides</h3> <ul class="space-y-2"> <li> <a href="#introduction" class="text-blue-600 hover:text-blue-800 hover:underline">Introduction</a> - what Nostr Web Components are for </li> <li> <a href="#installation" class="text-blue-600 hover:text-blue-800 hover:underline">Installation</a> - four ways to use the components </li> <li> <a href="#custom-templates" class="text-blue-600 hover:text-blue-800 hover:underline" >Custom Templates</a > - some components can take optional flexible HTML templates. </li> <li> <a href="#nostr-gadgets" class="text-blue-600 hover:text-blue-800 hover:underline" >Usage with @nostr/gadgets</a > - using nostr-web-components in JavaScript-led applications. </li> <li> <a href="#tailwind-styling" class="text-blue-600 hover:text-blue-800 hover:underline" >Styling with Tailwind</a > - two ways to use Tailwind to style the internals of components. </li> </ul> </div> <!-- Components Column --> <div> <h3 class="text-2xl font-bold mb-4 text-gray-800">Components</h3> <ul class="space-y-2"> <li> <a href="#nostr-name" class="text-purple-600 hover:text-purple-800 hover:underline font-mono" >&lt;nostr-name&gt;</a > - display user names </li> <li> <a href="#nostr-picture" class="text-purple-600 hover:text-purple-800 hover:underline font-mono" >&lt;nostr-picture&gt;</a > - display profile pictures </li> <li> <a href="#nostr-follow" class="text-purple-600 hover:text-purple-800 hover:underline font-mono" >&lt;nostr-follow&gt;</a > - an embeddable interactive follow button </li> <li> <a href="#nostr-text" class="text-purple-600 hover:text-purple-800 hover:underline font-mono" >&lt;nostr-text&gt;</a > - render Nostr text with links, media and so on </li> <li> <a href="#nostr-user-search" class="text-purple-600 hover:text-purple-800 hover:underline font-mono" >&lt;nostr-user-search&gt;</a > - search users </li> <li> <a href="#nostr-rsvp" class="text-purple-600 hover:text-purple-800 hover:underline font-mono" >&lt;nostr-rsvp&gt;</a > - interactive calendar event RSVPs </li> <li> <a href="#nostr-livestream" class="text-purple-600 hover:text-purple-800 hover:underline font-mono" >&lt;nostr-livestream&gt;</a > - display video livestreams </li> <li> <a href="#nostr-note" class="text-purple-600 hover:text-purple-800 hover:underline font-mono" >&lt;nostr-note&gt;</a > - display notes with author and content </li> <li> <a href="#nostr-event-json" class="text-purple-600 hover:text-purple-800 hover:underline font-mono" >&lt;nostr-event-json&gt;</a > - display raw event JSON but with each JSON node formatted differently </li> </ul> </div> </div> </div> </section> <!-- Guides Section --> <section id="guides" class="mb-16"> <h2 class="text-4xl font-bold mb-8 text-gray-800">Guides</h2> <!-- Introduction --> <div id="introduction" class="bg-white rounded-lg shadow-md p-8 mb-8"> <h3 class="text-2xl font-bold mb-4 text-gray-800">Introduction</h3> <div class="prose max-w-none text-gray-700"> <p class="mb-4"> Nostr is very simple, but not simple enough that you would write a full-blown client from scratch in pure JavaScript every time what you just need is to dynamically embed a Nostr note in your otherwise static HTML site; or display a button asking people to RSVP in the static site you made for a local meetup; or even for when you just have a list of npubs and you want to display their names. </p> <p class="mb-4"> <strong>Nostr Web Components</strong> is not trying to be a framework for writing Nostr clients. For that you probably still want something like Svelte, or React, or Solid, or whatever component together with <a href="https://hzrd149.github.io/applesauce/">applesauce</a> or <a href="https://github.com/nostr-dev-kit/ndk" class="text-blue-600 hover:underline">ndk</a> or <a href="https://github.com/nbd-wtf/nostr-gadgets" class="text-blue-600 hover:underline">nostr-gadgets</a> or some other library. </p> <p> For all other situations when you just want little bits of Nostr you can use these web components. Although they're isolated in a shadow DOM and completely unstyled, they come with <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/::part" class="text-blue-600 hover:underline" ><code>part</code></a > attributes that you can use to style them from the outside. That also works with TailwindCSS. </p> </div> </div> <!-- Installation --> <div id="installation" class="bg-white rounded-lg shadow-md p-8 mb-8"> <h3 class="text-2xl font-bold mb-4 text-gray-800">Installation</h3> <div class="prose max-w-none text-gray-700"> <p class="mb-4">There are four ways to use Nostr Web Components in your project:</p> <h4 class="text-xl font-semibold mb-3 mt-6">1. Standalone Script Import</h4> <p class="mb-3">The simplest way is to import individual components directly from jsdelivr:</p> <pre><code>&lt;script src="https://cdn.jsdelivr.net/npm/nostr-web-components/dist/nostr-name.js"&gt;&lt;/script&gt; &lt;nostr-name pubkey="npub..."&gt;&lt;/nostr-name&gt;</code></pre> <h4 class="text-xl font-semibold mb-3 mt-6">2. Complete Bundle Import</h4> <p class="mb-3">If you want to use multiple components, you can import the complete bundle:</p> <pre><code>&lt;script src="https://cdn.jsdelivr.net/npm/nostr-web-components/dist/index.js"&gt;&lt;/script&gt; &lt;nostr-name pubkey="npub..."&gt;&lt;/nostr-name&gt; &lt;nostr-picture pubkey="npub..."&gt;&lt;/nostr-picture&gt;</code></pre> <h4 class="text-xl font-semibold mb-3 mt-6">3. Using native ES module imports and importmap</h4> <p class="mb-3"> If you are using more than one component and not making your own bundle, then this approach is what you're looking for as it has many advantages: </p> <ul class="list-disc ml-6 mb-3"> <li><strong>Efficient Loading:</strong> Only load the components you need</li> <li> <strong>Shared Dependencies:</strong> Dependencies like <code>@nostr/tools</code> are loaded once and shared between components </li> <li> <strong>Better Caching:</strong> Browser can cache individual components and dependencies separately </li> <li><strong>Modern Standards:</strong> Uses native ES modules with proper dependency resolution</li> </ul> <pre><code>&lt;script type="importmap"&gt; { "imports": { "@nostr/tools/": "https://esm.sh/*jsr/@nostr/tools@2.17.4/", "@nostr/gadgets/": "https://esm.sh/*jsr/@nostr/gadgets@0.0.43/", "@noble/curves/": "https://esm.sh/*@noble/curves@1.2.0/", "@noble/hashes/": "https://esm.sh/*@noble/hashes@1.3.1/", "@scure/base/": "https://esm.sh/*@scure/base@1.1.1/", "@scure/base": "https://esm.sh/*@scure/base@1.1.1", "idb-keyval": "https://esm.sh/idb-keyval@6.2.1" }, "scopes": { "https://esm.sh/": { "@jsr/nostr__tools/": "https://esm.sh/*@jsr/nostr__tools@2.17.4/", "@jsr/nostr__gadgets/": "https://esm.sh/*@jsr/nostr__gadgets@0.0.43/", "@jsr/fiatjaf__lru-cache/": "https://esm.sh/jsr/@fiatjaf/lru-cache@0.0.1/" } } } &lt;/script&gt; &lt;script type="module"&gt; import 'https://esm.sh/nostr-web-components/lib/nostr-name.js' import 'https://esm.sh/nostr-web-components/lib/nostr-picture.js' &lt;/script&gt;</code></pre> <h4 class="text-xl font-semibold mb-3 mt-6">4. NPM Installation</h4> <p class="mb-3">For projects using npm:</p> <pre><code>npm install nostr-web-components</code></pre> <p class="mb-3">Then import components in your JavaScript:</p> <pre><code>import 'nostr-web-components/nostr-name' import 'nostr-web-components/nostr-picture'</code></pre> </div> </div> <!-- Custom Templates --> <div id="custom-templates" class="bg-white rounded-lg shadow-md p-8 mb-8"> <h3 class="text-2xl font-bold mb-4 text-gray-800">Custom Templates</h3> <div class="prose max-w-none text-gray-700"> <p class="mb-4"> Some components that render complex data (like <code>&lt;nostr-note&gt;</code>, for example) provide a default template that can be styled using <code>part</code> selectors, but also allow you to pass your own HTML template if you need a different hierarchy. </p> <p class="mb-3"> The template must be defined outside the component and then referenced by an <code>id</code>: </p> <pre><code>&lt;template id="my-note"&gt; &lt;div&gt; &lt;a part="author-link"&gt; &lt;span part="author-name"&gt;&lt;/span&gt; &lt;/a&gt; &lt;/div&gt; &lt;div part="content"&gt;&lt;/div&gt; &lt;div&gt; &lt;a part="link"&gt; &lt;time part="date"&gt;&lt;/time&gt; &lt;/a&gt; &lt;/div&gt; &lt;/template&gt; &lt;nostr-note template="my-note" ref="nevent..."&gt;&lt;/nostr-note&gt;</code></pre> </div> </div> <!-- Nostr Gadgets --> <div id="nostr-gadgets" class="bg-white rounded-lg shadow-md p-8 mb-8"> <h3 class="text-2xl font-bold mb-4 text-gray-800">Usage with @nostr/gadgets</h3> <div class="prose max-w-none text-gray-700"> <p> These components are all using the <a href="https://jsr.io/@nostr/gadgets" class="text-blue-600 hover:underline">@nostr/gadgets</a> library's global pool and shared metadata loaders and relay list loaders, which means that if you're using those same bits anywhere else in your code those resources will be shared efficiently and relay connections won't be duplicated nor resources have to be wasted on duplicated downloads, everything will be reused, including the local <code>IndexedDB</code> cache of profile metadata and relay lists. </p> <p class="mt-4"> That means you can reuse components like <code>&lt;nostr-text&gt;</code> or <code>&lt;nostr-name&gt;</code> inside heavy JavaScript Nostr clients with no fear of bloat or efficiency loss (other components too, but I imagine these simpler ones are the most useful in such contexts). </p> <p class="mt-4"> Even if you are not using <a href="https://jsr.io/@nostr/gadgets" class="text-blue-600 hover:underline">@nostr/gadgets</a> but you are using a global <a href="https://jsr.io/@nostr/tools" class="text-blue-600 hover:underline">@nostr/tools</a> <code>Pool</code> you could still import gadgets' global <code>Pool</code> or call <code>global.setPool()</code> so the same connections are reused by these components. </p> </div> </div> <!-- Tailwind Styling --> <div id="tailwind-styling" class="bg-white rounded-lg shadow-md p-8 mb-8"> <h3 class="text-2xl font-bold mb-4 text-gray-800">Styling with Tailwind</h3> <div class="prose max-w-none text-gray-700"> <p class="mb-4"> Nostr Web Components ship with a bunch of <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/::part" class="text-blue-600 hover:underline" ><code>part</code></a > attributes that can be used by CSS to break the shadow DOM barrier and style the component from the outside. </p> <p class="mb-4"> To do that with <a href="https://tailwindcss.com/" class="text-blue-600 hover:underline">Tailwind</a> we just need a simple plugin that tells Tailwind to transform <code>part-[&lt;part-attribute&gt;]:actual-tw-class</code> into the proper CSS declaration. In short, add this to your Tailwind v4 CSS file: </p> <pre><code>@import "tailwindcss"; @plugin "tailwind-part";</code></pre> <p class="mb-4">And install it with <code>npm</code>: <code>npm install --save-dev tailwind-part</code>.</p> <p class="mb-4">After that you can style components by placing classes on the component tag like this:</p> <pre><code>&lt;nostr-picture pubkey="npub1..." class="part-[img]:rounded"&gt;&lt;/nostr-picture&gt;</code></pre> <p class="mb-4"> Check each component's documentation to see what <code>part</code> selectors are available for each. </p> <h4 class="text-xl font-semibold mb-3 mt-6">Do it manually</h4> <p class="mb-3"> Or you can declare the plugin manually in your Tailwind config or on a separate file that can be imported by the CSS: </p> <pre><code>import plugin from 'tailwindcss/plugin' export default plugin(({ matchVariant }) => { matchVariant('part', value => { return `&::part(${value})` }) })</code></pre> <p>Yes, this is the full source code for the plugin.</p> </div> </div> </section> <!-- Components Section --> <section id="components" class="mb-16"> <h2 class="text-4xl font-bold mb-8 text-gray-800">Components</h2> <!-- nostr-name --> <div id="nostr-name" class="bg-white rounded-lg shadow-md p-8 mb-8"> <h3 class="text-3xl font-bold mb-6 text-purple-600 font-mono">&lt;nostr-name&gt;</h3> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> <!-- Left Column: Documentation --> <div> <p class="text-gray-700 mb-6">Displays a Nostr user's name from their pubkey.</p> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Attributes</h4> <ul class="list-disc ml-6 text-gray-700"> <li> <code class="bg-gray-100 px-2 py-1 rounded">pubkey</code>: The Nostr public key (npub or hex format) of the user </li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Styling Parts</h4> <p class="text-gray-700">This component exposes no styling parts.</p> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Template Support</h4> <p class="text-gray-700">This component does not support external templates.</p> </div> </div> <!-- Right Column: Live Example --> <div> <div class="bg-blue-50 p-6 rounded-lg"> <div class="flex justify-end items-center mb-4"> <button id="nostr-name-example-toggle" onclick="toggleCode('nostr-name-example')" class="text-sm bg-purple-600 text-white px-3 py-1 rounded hover:bg-purple-700" > Show Code </button> </div> <div id="nostr-name-example-code" class="hidden mb-4"> <pre class="bg-gray-900 text-gray-100 p-4 rounded text-sm whitespace-pre-wrap break-words" ><code id="nostr-name-example-code-content"></code></pre> </div> <div class="bg-white p-4 rounded" data-example-container="nostr-name-example"> <div data-example-content> Hello, <nostr-name class="text-2xl text-orange-500 font-bold" pubkey="nprofile1qqsgfhhxuemwtwm8kjk5uppv7uxtmp5pz4wm2dv59lxx5pfnsk98ysqpz3mhxue69uhkummnw3ezummcw3ezuer9wcq3camnwvaz7tmwdaehgu3wvf5hgcm0d9hx2u3wwdhkx6tpds37vfju" ></nostr-name> </div> </div> </div> </div> </div> </div> <!-- nostr-picture --> <div id="nostr-picture" class="bg-white rounded-lg shadow-md p-8 mb-8"> <h3 class="text-3xl font-bold mb-6 text-purple-600 font-mono">&lt;nostr-picture&gt;</h3> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> <!-- Left Column: Documentation --> <div> <p class="text-gray-700 mb-6">Displays a Nostr user's profile picture.</p> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Attributes</h4> <ul class="list-disc ml-6 text-gray-700"> <li> <code class="bg-gray-100 px-2 py-1 rounded">pubkey</code>: The Nostr public key (npub or hex format) of the user </li> <li><code class="bg-gray-100 px-2 py-1 rounded">width</code>: (optional) Width of the image</li> <li><code class="bg-gray-100 px-2 py-1 rounded">height</code>: (optional) Height of the image</li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Styling Parts</h4> <ul class="list-disc ml-6 text-gray-700"> <li> <code class="bg-gray-100 px-2 py-1 rounded">img</code>: The image element showing the profile picture </li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Template Support</h4> <p class="text-gray-700">This component does not support external templates.</p> </div> </div> <!-- Right Column: Live Example --> <div> <div class="bg-emerald-50 p-6 rounded-lg"> <div class="flex justify-end items-center mb-4"> <button id="nostr-picture-example-toggle" onclick="toggleCode('nostr-picture-example')" class="text-sm bg-blue-600 text-white px-3 py-1 rounded hover:bg-blue-700" > Show Code </button> </div> <div id="nostr-picture-example-code" class="hidden mb-4"> <pre class="bg-gray-900 text-gray-100 p-4 rounded text-sm whitespace-pre-wrap break-words" ><code id="nostr-picture-example-code-content"></code></pre> </div> <div class="bg-white p-4 rounded" data-example-container="nostr-picture-example"> <div data-example-content class="flex gap-2 items-center"> <nostr-picture pubkey="nprofile1qqsgfhhxuemwtwm8kjk5uppv7uxtmp5pz4wm2dv59lxx5pfnsk98ysqpz3mhxue69uhkummnw3ezummcw3ezuer9wcq3camnwvaz7tmwdaehgu3wvf5hgcm0d9hx2u3wwdhkx6tpds37vfju" class="part-[img]:h-16 part-[img]:w-16 part-[img]:rounded-full part-[img]:border-4 part-[img]:border-rose-600" ></nostr-picture> <span class="text-gray-700">Profile picture with rounded border</span> </div> </div> </div> </div> </div> </div> <!-- nostr-follow --> <div id="nostr-follow" class="bg-white rounded-lg shadow-md p-8 mb-8"> <h3 class="text-3xl font-bold mb-6 text-purple-600 font-mono">&lt;nostr-follow&gt;</h3> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> <!-- Left Column: Documentation --> <div> <p class="text-gray-700 mb-6"> A button that allows website visitors to follow a Nostr profile with one click. If NIP-07 window.nostr is not present, it will be automatically included when the button is clicked. </p> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Attributes</h4> <ul class="list-disc ml-6 text-gray-700"> <li> <code class="bg-gray-100 px-2 py-1 rounded">pubkey</code>: The Nostr public key (npub or hex format) of the user to follow </li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Styling Parts</h4> <ul class="list-disc ml-6 text-gray-700"> <li><code class="bg-gray-100 px-2 py-1 rounded">button</code>: The follow button container</li> <li> <code class="bg-gray-100 px-2 py-1 rounded">error-message</code>: Error message text when follow fails </li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Slots</h4> <ul class="list-disc ml-6 text-gray-700"> <li><strong>Default:</strong> Main button text</li> <li> <code class="bg-gray-100 px-2 py-1 rounded">loading</code>: Shown while follow request is processing </li> <li><code class="bg-gray-100 px-2 py-1 rounded">success</code>: Shown after successful follow</li> <li><code class="bg-gray-100 px-2 py-1 rounded">failure</code>: Shown if follow request fails</li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Events</h4> <ul class="list-disc ml-6 text-gray-700"> <li> <code class="bg-gray-100 px-2 py-1 rounded">success</code>: Fired when follow is successful. Event detail includes <code>already: boolean</code> </li> <li> <code class="bg-gray-100 px-2 py-1 rounded">failure</code>: Fired when follow fails. Event detail includes error message </li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Template Support</h4> <p class="text-gray-700">This component does not support external templates.</p> </div> </div> <!-- Right Column: Live Example --> <div> <div class="bg-pink-50 p-6 rounded-lg"> <div class="flex justify-end items-center mb-4"> <button id="nostr-follow-example-toggle" onclick="toggleCode('nostr-follow-example')" class="text-sm bg-blue-600 text-white px-3 py-1 rounded hover:bg-blue-700" > Show Code </button> </div> <div id="nostr-follow-example-code" class="hidden mb-4"> <pre class="bg-gray-900 text-gray-100 p-4 rounded text-sm whitespace-pre-wrap break-words" ><code id="nostr-follow-example-code-content"></code></pre> </div> <div class="bg-white p-4 rounded" data-example-container="nostr-follow-example"> <div data-example-content> <nostr-follow pubkey="npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6" class="part-[button]:bg-blue-600 part-[button]:text-white part-[button]:rounded part-[button]:px-4 part-[button]:py-2 part-[button]:border-0 part-[button]:cursor-pointer part-[button]:hover:bg-blue-700 part-[button]:transition-colors" > Follow me on Nostr <span class="text-yellow-600" slot="loading">Following...</span> <span class="text-green-600" slot="success">✓ Followed!</span> <span class="text-red-600" slot="failure">Error: <span part="error-message"></span></span> </nostr-follow> </div> </div> </div> </div> </div> </div> <!-- nostr-text --> <div id="nostr-text" class="bg-white rounded-lg shadow-md p-8 mb-8"> <h3 class="text-3xl font-bold mb-6 text-purple-600 font-mono">&lt;nostr-text&gt;</h3> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> <!-- Left Column: Documentation --> <div> <p class="text-gray-700 mb-6"> Renders "plaintext" (NUML - Nostr Unassuming Markup Language, NIP-27) content with special rendering for NIP-21 nostr: references, relay URLs, and media. </p> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Attributes</h4> <ul class="list-disc ml-6 text-gray-700"> <li> <code class="bg-gray-100 px-2 py-1 rounded">content</code>: The text content to render with Nostr-specific formatting </li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Styling Parts</h4> <ul class="list-disc ml-6 text-gray-700"> <li><code class="bg-gray-100 px-2 py-1 rounded">video</code>: Video elements</li> <li><code class="bg-gray-100 px-2 py-1 rounded">audio</code>: Audio elements</li> <li><code class="bg-gray-100 px-2 py-1 rounded">image</code>: Image elements</li> <li><code class="bg-gray-100 px-2 py-1 rounded">reference</code>: References to other notes/users</li> <li><code class="bg-gray-100 px-2 py-1 rounded">url</code>: URL links</li> <li><code class="bg-gray-100 px-2 py-1 rounded">relay</code>: Relay URLs</li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Template Support</h4> <p class="text-gray-700">This component does not support external templates.</p> </div> </div> <!-- Right Column: Live Example --> <div> <div class="bg-lime-50 p-6 rounded-lg"> <div class="flex justify-end items-center mb-4"> <button id="nostr-text-example-toggle" onclick="toggleCode('nostr-text-example')" class="text-sm bg-blue-600 text-white px-3 py-1 rounded hover:bg-blue-700" > Show Code </button> </div> <div id="nostr-text-example-code" class="hidden mb-4"> <pre class="bg-gray-900 text-gray-100 p-4 rounded text-sm whitespace-pre-wrap break-words" ><code id="nostr-text-example-code-content"></code></pre> </div> <div class="bg-white p-4 rounded" data-example-container="nostr-text-example"> <div data-example-content> <nostr-text class="block part-[video]:block part-[video]:rounded part-[audio]:block part-[image]:block part-[image]:rounded part-[image]:max-w-full part-[reference]:font-semibold part-[reference]:text-black part-[url]:text-blue-600 part-[url]:underline part-[relay]:text-orange-600" content="Check out this video: https://cdn.nostrcheck.me/1fc19ef347a19e3675231bad2d25d43b878886838386adf128175b778b45ac82.mp4" ></nostr-text> </div> </div> </div> </div> </div> </div> <!-- nostr-user-search --> <div id="nostr-user-search" class="bg-white rounded-lg shadow-md p-8 mb-8"> <h3 class="text-3xl font-bold mb-6 text-purple-600 font-mono">&lt;nostr-user-search&gt;</h3> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> <!-- Left Column: Documentation --> <div> <p class="text-gray-700 mb-6"> Displays a search box and renders search results. Relies on NIP-50 queries to specific relays. </p> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Attributes</h4> <ul class="list-disc ml-6 text-gray-700"> <li><code class="bg-gray-100 px-2 py-1 rounded">relays</code>: Comma-separated list of relay URLs</li> <li> <code class="bg-gray-100 px-2 py-1 rounded">limit</code>: Maximum number of results (default: 10) </li> <li> <code class="bg-gray-100 px-2 py-1 rounded">placeholder</code>: Placeholder text for search input </li> <li><code class="bg-gray-100 px-2 py-1 rounded">value</code>: Initial search value</li> <li> <code class="bg-gray-100 px-2 py-1 rounded">template</code>: ID of template element for custom rendering </li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Styling Parts</h4> <ul class="list-disc ml-6 text-gray-700"> <li><code class="bg-gray-100 px-2 py-1 rounded">wrapper</code>: Main container</li> <li><code class="bg-gray-100 px-2 py-1 rounded">input</code>: Search input field</li> <li><code class="bg-gray-100 px-2 py-1 rounded">results</code>: Results container</li> <li><code class="bg-gray-100 px-2 py-1 rounded">item</code>: Individual result item</li> <li><code class="bg-gray-100 px-2 py-1 rounded">picture</code>: User profile picture</li> <li><code class="bg-gray-100 px-2 py-1 rounded">name</code>: User display name</li> <li><code class="bg-gray-100 px-2 py-1 rounded">nip05</code>: NIP-05 identifier</li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Events</h4> <ul class="list-disc ml-6 text-gray-700"> <li> <code class="bg-gray-100 px-2 py-1 rounded">selected</code>: Fired when an item is picked. Event detail includes <code>pubkey</code>, <code>npub</code> and <code>metadata</code> object </li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Template Support</h4> <p class="text-gray-700">Supports external templates through the <code>template</code> attribute.</p> </div> </div> <!-- Right Column: Live Example --> <div> <div class="bg-yellow-50 p-6 rounded-lg"> <div class="flex justify-end items-center mb-4"> <button id="nostr-user-search-example-toggle" onclick="toggleCode('nostr-user-search-example')" class="text-sm bg-blue-600 text-white px-3 py-1 rounded hover:bg-blue-700" > Show Code </button> </div> <div id="nostr-user-search-example-code" class="hidden mb-4"> <pre class="bg-gray-900 text-gray-100 p-4 rounded text-sm whitespace-pre-wrap break-words" ><code id="nostr-user-search-example-code-content"></code></pre> </div> <div class="bg-white p-4 rounded" data-example-container="nostr-user-search-example"> <div data-example-content> <nostr-user-search class="part-[input]:w-full part-[input]:px-4 part-[input]:py-2 part-[input]:border part-[input]:rounded part-[item]:px-4 part-[item]:py-2 part-[item]:cursor-pointer part-[item]:hover:bg-gray-100 part-[picture]:w-12 part-[picture]:h-12 part-[picture]:rounded-full part-[name]:font-semibold" ></nostr-user-search> <p id="search-result" class="mt-4 text-gray-700"></p> <script> document.querySelector('nostr-user-search').addEventListener('selected', function (ev) { document.querySelector('#search-result').textContent = 'Selected: ' + (ev.detail.metadata.name || ev.detail.npub) }) </script> </div> </div> </div> </div> </div> </div> <!-- nostr-rsvp --> <div id="nostr-rsvp" class="bg-white rounded-lg shadow-md p-8 mb-8"> <h3 class="text-3xl font-bold mb-6 text-purple-600 font-mono">&lt;nostr-rsvp&gt;</h3> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> <!-- Left Column: Documentation --> <div> <p class="text-gray-700 mb-6"> Displays information about a NIP-52 calendar event and allows users to RSVP. If window.nostr isn't present, it will be included automatically after user clicks. </p> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Attributes</h4> <ul class="list-disc ml-6 text-gray-700"> <li><code class="bg-gray-100 px-2 py-1 rounded">ref</code>: Event reference (naddr format)</li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Styling Parts</h4> <ul class="list-disc ml-6 text-gray-700"> <li><code class="bg-gray-100 px-2 py-1 rounded">container</code>: Main container</li> <li><code class="bg-gray-100 px-2 py-1 rounded">title</code>: Event title</li> <li><code class="bg-gray-100 px-2 py-1 rounded">time</code>: Event time</li> <li><code class="bg-gray-100 px-2 py-1 rounded">description</code>: Event description</li> <li><code class="bg-gray-100 px-2 py-1 rounded">buttons</code>: RSVP buttons container</li> <li><code class="bg-gray-100 px-2 py-1 rounded">button</code>: Individual button</li> <li><code class="bg-gray-100 px-2 py-1 rounded">accepted</code>: Accepted button state</li> <li><code class="bg-gray-100 px-2 py-1 rounded">tentative</code>: Tentative button state</li> <li><code class="bg-gray-100 px-2 py-1 rounded">declined</code>: Declined button state</li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Slots</h4> <ul class="list-disc ml-6 text-gray-700"> <li><code class="bg-gray-100 px-2 py-1 rounded">declined</code>: Custom text for decline button</li> <li> <code class="bg-gray-100 px-2 py-1 rounded">rsvp-sent</code>: Custom message shown after RSVP is sent </li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Template Support</h4> <p class="text-gray-700">This component does not support external templates.</p> </div> </div> <!-- Right Column: Live Example --> <div> <div class="bg-purple-50 p-6 rounded-lg"> <div class="flex justify-end items-center mb-4"> <button id="nostr-rsvp-example-toggle" onclick="toggleCode('nostr-rsvp-example')" class="text-sm bg-blue-600 text-white px-3 py-1 rounded hover:bg-blue-700" > Show Code </button> </div> <div id="nostr-rsvp-example-code" class="hidden mb-4"> <pre class="bg-gray-900 text-gray-100 p-4 rounded text-sm whitespace-pre-wrap break-words" ><code id="nostr-rsvp-example-code-content"></code></pre> </div> <div class="bg-white p-4 rounded" data-example-container="nostr-rsvp-example"> <div data-example-content> <nostr-rsvp ref="naddr1qqjrys6px56yydpk94prxdes956yxw2r95u5g33s94pnxwpjgg6ygv69xqcrvqgdwaehxw309ahx7uewd3hkcq3q0hj9rg5gds5x2gk0z0s2jlqnq04jg7g30aj2t5pqzdaaztfactgsxpqqqp7tx45kyl4" class="part-[container]:bg-white part-[container]:rounded-lg part-[container]:shadow part-[container]:p-4 part-[title]:text-xl part-[title]:font-bold part-[title]:mb-2 part-[time]:text-gray-600 part-[time]:mb-4 part-[description]:mb-4 part-[buttons]:flex part-[buttons]:gap-2 part-[button]:px-4 part-[button]:py-2 part-[button]:rounded part-[button]:cursor-pointer part-[button]:text-white part-[accepted]:bg-green-500 part-[accepted]:hover:bg-green-600 part-[tentative]:bg-yellow-500 part-[tentative]:hover:bg-yellow-600 part-[declined]:bg-red-500 part-[declined]:hover:bg-red-600" > <span slot="declined">Decline</span> <span slot="rsvp-sent">RSVP Sent!</span> </nostr-rsvp> </div> </div> </div> </div> </div> </div> <!-- nostr-livestream --> <div id="nostr-livestream" class="bg-white rounded-lg shadow-md p-8 mb-8"> <h3 class="text-3xl font-bold mb-6 text-purple-600 font-mono">&lt;nostr-livestream&gt;</h3> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> <!-- Left Column: Documentation --> <div> <p class="text-gray-700 mb-6">Displays metadata and renders the actual video of a NIP-53 livestream.</p> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Attributes</h4> <ul class="list-disc ml-6 text-gray-700"> <li><code class="bg-gray-100 px-2 py-1 rounded">ref</code>: Livestream reference (naddr format)</li> <li> <code class="bg-gray-100 px-2 py-1 rounded">template</code>: ID of template element for custom rendering </li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Styling Parts</h4> <ul class="list-disc ml-6 text-gray-700"> <li><code class="bg-gray-100 px-2 py-1 rounded">error</code>: Error message container</li> <li><code class="bg-gray-100 px-2 py-1 rounded">header</code>: Stream header section</li> <li><code class="bg-gray-100 px-2 py-1 rounded">image</code>: Stream thumbnail</li> <li><code class="bg-gray-100 px-2 py-1 rounded">title</code>: Stream title</li> <li><code class="bg-gray-100 px-2 py-1 rounded">status</code>: Stream status</li> <li><code class="bg-gray-100 px-2 py-1 rounded">time</code>: Stream time information</li> <li><code class="bg-gray-100 px-2 py-1 rounded">summary</code>: Stream description</li> <li><code class="bg-gray-100 px-2 py-1 rounded">stats</code>: Statistics section</li> <li><code class="bg-gray-100 px-2 py-1 rounded">current</code>: Current viewers section</li> <li><code class="bg-gray-100 px-2 py-1 rounded">current-value</code>: Current viewer count</li> <li><code class="bg-gray-100 px-2 py-1 rounded">video</code>: Video player</li> <li><code class="bg-gray-100 px-2 py-1 rounded">participants</code>: Participants section</li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Slots</h4> <ul class="list-disc ml-6 text-gray-700"> <li> <code class="bg-gray-100 px-2 py-1 rounded">error-pointer</code>: Custom message when event pointer is invalid </li> <li> <code class="bg-gray-100 px-2 py-1 rounded">error-fetch</code>: Custom message when event cannot be fetched </li> <li> <code class="bg-gray-100 px-2 py-1 rounded">error-invalid</code>: Custom message when event is not a livestream </li> <li><code class="bg-gray-100 px-2 py-1 rounded">stats</code>: Custom statistics display</li> </ul> </div> <div class="mb-6"> <h4 class="text-lg font-semibold mb-3">Template Support</h4> <p class="text-gray-700">Supports external templates through the <code>template</code> attribute.</p> </div> </div> <!-- Right Column: Live Example --> <div> <div class="bg-orange-50 p-6 rounded-lg"> <div class="flex justify-end items-center mb-4"> <button id="nostr-livestream-example-toggle" onclick="toggleCode('nostr-livestream-example')" class="text-sm bg-blue-600 text-white px-3 py-1 rounded hover:bg-blue-700" > Show Code </button> </div> <div id="nostr-livestream-example-code" class="hidden mb-4"> <pre class="bg-gray-900 text-gray-100 p-4 rounded text-sm whitespace-pre-wrap break-words" ><code id="nostr-livestream-example-code-content"></code></pre> </div> <div class="bg-white p-4 rounded" data-example-container="nostr-livestream-example"> <div data-example-content> <nostr-livestream ref="naddr1qqjr2vehvyenvdtr94nrzetr956rgctr94skvvfs95eryep3x3snwve389nxyqgwwaehxw309ahx7uewd3hkctcpz4mhxue69uhhyetvv9ujuerpd46hxtnfduhszxthwden5te0wfjkccte9eekummjwsh8xmmrd9skctcpzamhxue69uhhyetvv9ujumn0wd68ytnzv9hxgtcpz9mhxue69uhkummnw3ezumrpdejz7qg7waehxw309ahx7um5wgkhqatz9emk2mrvdaexgetj9ehx2ap0qyghwumn8ghj7mn0wd68ytnhd9hx2tcpz4mhxue69uhhyetvv9ujumn0wd68ytnzvuhsz9thwden5te0dehhxarj9ehhsarj9ejx2a30qgsv73dxhgfk8tt76gf6q788zrfyz9dwwgwfk3aar6l5gk82a76v9fgrqsqqqan8tp7le0" class="part-[error]:text-red-500 part-[header]:mb-4 part-[title]:text-xl part-[title]:font-bold part-[status]:inline-block part-[status]:px-2 part-[status]:py-1 part-[status]:bg-yellow-400 part-[status]:rounded part-[stats]:text-gray-600" autoplay muted > <span slot="error-pointer">invalid code!</span> <span slot="error-fetch">couldn't find the event!</span> <span slot="error-invalid">not a livestream!</span> </nostr-livestream> </div> </div> </div> </div> </div> </div> <!-- nostr-note --> <div id="nostr-note" class="bg-white rounded-lg shadow-md p-8 mb-8"> <h3 class="text-3xl font-bold mb-6 text-purple-600 font-mono">&lt;nostr-note&gt;</h3> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">