feat: implement NIP-B0 web bookmarks renderer (kind 39701)

- Update icon from Globe to Bookmark in kind definitions
- Create Kind39701Renderer with title, URL (from d/u tag), and description
- Register renderer in kind registry
- Extract URL from d tag (identifier) or optional u tag
- Display with RichText formatting support
This commit is contained in:
Alejandro Gómez
2025-12-11 13:11:09 +01:00
parent 38f5461b54
commit ee54695fee
3 changed files with 53 additions and 2 deletions

View File

@@ -0,0 +1,50 @@
import { BaseEventContainer, BaseEventProps } from "./BaseEventRenderer";
import { RichText } from "../RichText";
import { ExternalLink } from "lucide-react";
/**
* Renderer for Kind 39701 - Web Bookmarks (NIP-B0)
* Displays bookmark title, URL, and description
*/
export function Kind39701Renderer({ event }: BaseEventProps) {
// Extract bookmark data from tags
const title = event.tags.find((t) => t[0] === "title")?.[1];
// URL comes from d tag (identifier) or optional u tag
const dTag = event.tags.find((t) => t[0] === "d")?.[1];
const uTag = event.tags.find((t) => t[0] === "u")?.[1];
const url = uTag || dTag;
return (
<BaseEventContainer event={event}>
<div className="flex flex-col gap-2">
{/* Title */}
{title && <h3 className="text-lg font-bold">{title}</h3>}
{/* URL with external link icon */}
{url && (
<a
href={url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 text-accent hover:underline"
>
<ExternalLink className="size-4" />
<span className="text-sm break-all">{url}</span>
</a>
)}
{/* Description/Content as RichText */}
{event.content && (
<RichText event={event} className="text-sm text-muted-foreground" />
)}
{/* Fallback if no data */}
{!title && !url && (
<p className="text-sm text-muted-foreground italic">
(Empty bookmark)
</p>
)}
</div>
</BaseEventContainer>
);
}

View File

@@ -11,6 +11,7 @@ import { Kind9735Renderer } from "./Kind9735Renderer";
import { Kind9802Renderer } from "./Kind9802Renderer";
import { Kind10002Renderer } from "./Kind10002Renderer";
import { Kind30023Renderer } from "./Kind30023Renderer";
import { Kind39701Renderer } from "./Kind39701Renderer";
import { NostrEvent } from "@/types/nostr";
import { BaseEventContainer, type BaseEventProps } from "./BaseEventRenderer";
@@ -33,6 +34,7 @@ const kindRenderers: Record<number, React.ComponentType<BaseEventProps>> = {
9802: Kind9802Renderer, // Highlight
10002: Kind10002Renderer, // Relay List Metadata (NIP-65)
30023: Kind30023Renderer, // Long-form Article
39701: Kind39701Renderer, // Web Bookmarks (NIP-B0)
};
/**

View File

@@ -31,7 +31,6 @@ import {
Wallet,
Package,
Map,
Globe,
Highlighter,
BarChart3,
Timer,
@@ -902,7 +901,7 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
name: "Web Bookmarks",
description: "Web bookmarks",
nip: "B0",
icon: Globe,
icon: Bookmark,
},
};