chore: sync NIPs

This commit is contained in:
Alejandro Gómez
2026-03-26 09:58:07 +01:00
parent 5d22403235
commit 4a501634c5
6 changed files with 330 additions and 250 deletions

View File

@@ -21,7 +21,7 @@ Read the current file, then:
### VALID_NIPS array
- Add new NIP identifiers found upstream
- Remove NIPs no longer listed upstream (unless referenced by a kind in `kinds.ts`)
- Maintain sort order: numeric first (`"01"``"99"`), then hexadecimal (`"7D"`, `"A0"`, etc.)
- Maintain sort order: numeric first (`"01"``"99"`), then hexadecimal (`"5A"`, `"7D"`, `"A0"`, etc.)
- Preserve the `// Numeric NIPs` and `// Hexadecimal NIPs` section comments
### NIP_TITLES record
@@ -30,10 +30,27 @@ Read the current file, then:
- Keep existing entries that still match
### DEPRECATED_NIPS array
- Sync with upstream: add NIPs marked `deprecated`, remove ones no longer deprecated
- Sync with upstream: add NIPs marked `unrecommended`/`deprecated`, remove ones no longer deprecated
- Keep `as const` assertion
## Step 3: Update `src/constants/kinds.ts`
## Step 3: Update `src/lib/nip-icons.ts`
Read the current file. The `NIP_METADATA` record provides icons, short names, and descriptions for each NIP — used by NIPBadge and NipsViewer.
### For every new NIP added in Step 2:
- Add a corresponding entry to `NIP_METADATA` with: `id` (string, matches the NIP identifier), `name` (short), `description`, `icon` (lucide-react), and `deprecated` if applicable
- All keys and `id` values are strings (e.g., `"01"`, `"5A"`, `"A0"`)
- Pick an icon from existing imports; add new imports if needed
- Place in correct position (numeric keys first, then hex keys sorted)
### For deprecated status changes:
- Add or remove the `deprecated: true` field to match upstream
### Preserve existing entries:
- Never change icons on existing entries
- Only update name/description if upstream changed
## Step 4: Update `src/constants/kinds.ts`
Read the current file, then apply changes carefully.
@@ -57,13 +74,14 @@ Read the current file, then apply changes carefully.
- If the NIP reference changed, update the `nip` field
- **Never** change the `icon` field on existing entries
## Step 4: Cross-reference validation
## Step 5: Cross-reference validation
- Every `nip` field in `EVENT_KINDS` should exist in `VALID_NIPS` (except empty strings `""` and external specs like `"BUD-03"`, `"AMB"`, `"85"`)
- Every `nip` field in `EVENT_KINDS` should exist in `VALID_NIPS` (except empty strings `""` and external specs like `"BUD-03"`, `"AMB"`, `"Marmot"`)
- Every NIP in `DEPRECATED_NIPS` should also be in `VALID_NIPS`
- Every NIP in `VALID_NIPS` should have a corresponding entry in `NIP_METADATA` (`src/lib/nip-icons.ts`)
- Flag and fix any inconsistencies
## Step 5: Verify
## Step 6: Verify
```bash
npm run lint && npm run test:run && npm run build
@@ -71,10 +89,11 @@ npm run lint && npm run test:run && npm run build
Fix any lint/type/build issues before reporting.
## Step 6: Report
## Step 7: Report
Summarize:
- NIPs added / removed / title-updated
- NIP icons added / updated in `nip-icons.ts`
- Kinds added / updated (with icon choices explained for new ones)
- Inconsistencies found and resolved
- Verification results (lint/test/build)

View File

@@ -1,6 +1,6 @@
import { getNIPInfo } from "../lib/nip-icons";
import { useAddWindow } from "@/core/state";
import { isNipDeprecated } from "@/constants/nips";
import { isNipDeprecated, isValidNip } from "@/constants/nips";
import { getCommunityNipForNipId } from "@/constants/kinds";
export interface NIPBadgeProps {
@@ -26,6 +26,7 @@ export function NIPBadge({
const description =
nipInfo?.description || `Nostr Implementation Possibility ${nipNumber}`;
const isDeprecated = isNipDeprecated(nipNumber);
const isExternal = !isValidNip(nipNumber);
const communityNip = getCommunityNipForNipId(nipNumber);
@@ -48,6 +49,18 @@ export function NIPBadge({
);
};
// External specs (Marmot, BUD-03, etc.) render as non-interactive labels
if (isExternal && !communityNip) {
return (
<span
className={`flex items-center gap-2 border bg-card px-2.5 py-1.5 text-sm ${className}`}
title={`${nipNumber} (External spec)`}
>
<span className="text-muted-foreground">{nipNumber}</span>
</span>
);
}
return (
<button
onClick={openNIP}

View File

@@ -95,7 +95,7 @@ const RELAY_LIST_KINDS: RelayListKindUIConfig[] = [
name: "KeyPackage Relays",
description:
"Relays where you publish MLS KeyPackage events. Anyone who wants to start an encrypted group chat with you looks up this list.",
nip: "EE",
nip: "Marmot",
tagName: "relay",
hasMarkers: false,
},

View File

@@ -26,6 +26,7 @@ import {
Gavel,
GitBranch,
GitMerge,
Globe,
GitPullRequest,
GraduationCap,
BookHeart,
@@ -251,6 +252,13 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
nip: "71",
icon: Video,
},
24: {
kind: 24,
name: "Public Message",
description: "Public Message",
nip: "A4",
icon: MessageSquare,
},
// References (NKBIP-03) - External spec, commented out
// 30: {
@@ -789,6 +797,13 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
nip: "51",
icon: Users,
},
10011: {
kind: 10011,
name: "External Identities",
description: "External Identities",
nip: "39",
icon: UserCheck,
},
10012: {
kind: 10012,
name: "Favorite Relay List",
@@ -842,7 +857,7 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
kind: 10051,
name: "KeyPackage Relays",
description: "KeyPackage Relays List",
nip: "EE",
nip: "Marmot",
icon: Key,
},
10040: {
@@ -901,13 +916,13 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
nip: "",
icon: WandSparkles,
},
// 10312: {
// kind: 10312,
// name: "Room Presence",
// description: "Room Presence",
// nip: "53",
// icon: Users,
// },
10312: {
kind: 10312,
name: "Room Presence",
description: "Room Presence",
nip: "53",
icon: Users,
},
// 10377: {
// kind: 10377,
// name: "Proxy Announce",
@@ -934,17 +949,31 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
13534: {
kind: 13534,
name: "Relay Members",
description: "Relay membership list",
description: "Membership Lists",
nip: "43",
icon: Users,
},
// 17375: {
// kind: 17375,
// name: "Cashu Wallet Event",
// description: "Cashu Wallet Event",
// nip: "60",
// icon: Wallet,
15128: {
kind: 15128,
name: "Nsite Manifest",
description: "Root nsite manifest",
nip: "5A",
icon: Globe,
},
// 14388: {
// kind: 14388,
// name: "Sound Effect List",
// description: "User Sound Effect Lists",
// nip: "Corny Chat",
// icon: Music,
// },
17375: {
kind: 17375,
name: "Cashu Wallet Event",
description: "Cashu Wallet Event",
nip: "60",
icon: Wallet,
},
// 21000: {
// kind: 21000,
// name: "Lightning Pub RPC",
@@ -1124,6 +1153,13 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
nip: "51",
icon: Video,
},
30006: {
kind: 30006,
name: "Picture Set",
description: "Picture sets",
nip: "51",
icon: Image,
},
30007: {
kind: 30007,
name: "Kind Mute Set",
@@ -1141,7 +1177,7 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
30009: {
kind: 30009,
name: "Badge",
description: "Badge",
description: "Badge Definition",
nip: "58",
icon: Award,
},
@@ -1218,7 +1254,7 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
30063: {
kind: 30063,
name: "App Release",
description: "Application release with version and files",
description: "Release artifact sets",
nip: "51",
icon: Package,
},
@@ -1257,7 +1293,7 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
30267: {
kind: 30267,
name: "App Collection",
description: "Curated collection of applications",
description: "App curation sets",
nip: "51",
icon: BookHeart,
},
@@ -1271,21 +1307,21 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
30382: {
kind: 30382,
name: "User Assertion",
description: "Trusted Assertion: User",
description: "User Trusted Assertion",
nip: "85",
icon: ShieldCheck,
},
30383: {
kind: 30383,
name: "Event Assertion",
description: "Trusted Assertion: Event",
description: "Event Trusted Assertion",
nip: "85",
icon: ShieldCheck,
},
30384: {
kind: 30384,
name: "Address Assertion",
description: "Trusted Assertion: Addressable Event",
description: "Addressable Trusted Assertion",
nip: "85",
icon: ShieldCheck,
},
@@ -1425,35 +1461,56 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
31989: {
kind: 31989,
name: "Recommendation",
description: "Application Recommendation",
description: "Handler recommendation",
nip: "89",
icon: Star,
},
31990: {
kind: 31990,
name: "Application",
description: "Application Definition",
name: "Handler",
description: "Handler information",
nip: "89",
icon: Package,
},
32267: {
kind: 32267,
name: "App",
description: "Application metadata with platforms and screenshots",
description: "Software Application",
nip: "",
icon: Package,
},
// 32388: {
// kind: 32388,
// name: "Room Favorites",
// description: "User Room Favorites",
// nip: "Corny Chat",
// icon: Star,
// },
// 33388: {
// kind: 33388,
// name: "High Scores",
// description: "High Scores",
// nip: "Corny Chat",
// icon: Star,
// },
34128: {
kind: 34128,
name: "Legacy Nsite",
description: "Legacy nsite manifest (deprecated)",
nip: "5A",
icon: Globe,
},
34235: {
kind: 34235,
name: "Video",
description: "Horizontal Video (legacy)",
description: "Addressable Video Event",
nip: "71",
icon: Video,
},
34236: {
kind: 34236,
name: "Short Video",
description: "Vertical Video (legacy)",
description: "Addressable Short Video Event",
nip: "71",
icon: Video,
},
@@ -1464,6 +1521,13 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
nip: "",
icon: ListMusic,
},
// 34388: {
// kind: 34388,
// name: "Sound Effects",
// description: "Sound Effects",
// nip: "Corny Chat",
// icon: Music,
// },
34550: {
kind: 34550,
name: "Community",
@@ -1471,6 +1535,13 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
nip: "72",
icon: Users,
},
35128: {
kind: 35128,
name: "Named Nsite",
description: "Named nsite manifest",
nip: "5A",
icon: Globe,
},
36787: {
kind: 36787,
name: "Music Track",

View File

@@ -5,7 +5,7 @@
/**
* Deprecated NIPs that are no longer recommended for use
*/
export const DEPRECATED_NIPS = ["04", "08", "26", "96"] as const;
export const DEPRECATED_NIPS = ["04", "08", "26", "96", "EE"] as const;
export const VALID_NIPS = [
// Numeric NIPs
@@ -79,6 +79,7 @@ export const VALID_NIPS = [
"77",
"78",
"84",
"85",
"86",
"87",
"88",
@@ -90,8 +91,10 @@ export const VALID_NIPS = [
"98",
"99",
// Hexadecimal NIPs
"5A",
"7D",
"A0",
"A4",
"B0",
"B7",
"BE",
@@ -177,6 +180,7 @@ export const NIP_TITLES: Record<string, string> = {
"78": "Application-specific data",
"7D": "Threads",
"84": "Highlights",
"85": "Trusted Assertions",
"86": "Relay Management API",
"87": "Ecash Mint Discoverability",
"88": "Polls",
@@ -187,7 +191,9 @@ export const NIP_TITLES: Record<string, string> = {
"96": "HTTP File Storage Integration",
"98": "HTTP Auth",
"99": "Classified Listings",
"5A": "Pubkey Static Websites",
A0: "Voice Messages",
A4: "Public Messages",
B0: "Web Bookmarks",
B7: "Blossom",
BE: "Nostr BLE Communications Protocol",
@@ -207,6 +213,10 @@ export function getNipTitle(nipId: string): string {
return NIP_TITLES[nipId] || `NIP-${nipId}`;
}
export function isValidNip(nipId: string): boolean {
return VALID_NIPS.includes(nipId as NipId);
}
export function isNipDeprecated(nipId: string): boolean {
return DEPRECATED_NIPS.includes(nipId as any);
}

View File

@@ -1,6 +1,6 @@
/**
* NIP Icon Mapping
* Maps NIP numbers to Lucide icons for visual representation
* Maps NIP identifiers to Lucide icons for visual representation
*/
import {
@@ -46,539 +46,506 @@ import {
} from "lucide-react";
export interface NIPInfo {
number: number;
id: string;
name: string;
description: string;
icon: LucideIcon;
deprecated?: boolean;
}
export const NIP_METADATA: Record<number | string, NIPInfo> = {
export const NIP_METADATA: Record<string, NIPInfo> = {
// Core Protocol
"01": {
number: 1,
id: "01",
name: "Basic Protocol",
description: "Basic protocol flow description",
icon: FileText,
},
"02": {
number: 2,
id: "02",
name: "Follow List",
description: "Contact list and petnames",
icon: Users,
},
"03": {
number: 3,
id: "03",
name: "OpenTimestamps Attestations for Events",
description: "A proof of any event",
icon: Signature,
},
"04": {
number: 4,
id: "04",
name: "Encrypted DMs",
description: "Encrypted direct messages",
icon: Mail,
deprecated: true,
},
"05": {
number: 5,
id: "05",
name: "Mapping Nostr keys to DNS",
description: "Mapping Nostr keys to DNS-based internet identifiers",
icon: Globe,
},
"06": {
number: 6,
id: "06",
name: "Key Derivation",
description: "Basic key derivation from mnemonic seed phrase",
icon: Key,
},
"07": {
number: 7,
id: "07",
name: "window.nostr",
description: "window.nostr capability for web browsers",
icon: Globe,
},
"08": {
number: 8,
id: "08",
name: "Mentions",
description: "Handling mentions",
icon: Tag,
deprecated: true,
},
"09": {
number: 9,
id: "09",
name: "Event Deletion",
description: "Event deletion",
icon: AlertCircle,
},
10: {
number: 10,
"10": {
id: "10",
name: "Conventions",
description: "Conventions for clients' use of e and p tags",
icon: Tag,
},
11: {
number: 11,
"11": {
id: "11",
name: "Relay Info",
description: "Relay information document",
icon: Server,
},
13: {
number: 13,
"13": {
id: "13",
name: "Proof of Work",
description: "Proof of work",
icon: Zap,
},
14: {
number: 14,
"14": {
id: "14",
name: "Subject Tag",
description: "Subject tag in text events",
icon: Tag,
},
15: {
number: 15,
"15": {
id: "15",
name: "Marketplace",
description: "Marketplace (for resilient marketplaces)",
icon: ShoppingCart,
},
17: {
number: 17,
"17": {
id: "17",
name: "Private DMs",
description: "Private Direct Messages",
icon: Lock,
},
18: { number: 18, name: "Reposts", description: "Reposts", icon: Share2 },
19: {
number: 19,
"18": { id: "18", name: "Reposts", description: "Reposts", icon: Share2 },
"19": {
id: "19",
name: "bech32 Entities",
description: "bech32-encoded entities",
icon: Hash,
},
21: {
number: 21,
"21": {
id: "21",
name: "nostr: URI",
description: "nostr: URI scheme",
icon: Link,
},
22: {
number: 22,
"22": {
id: "22",
name: "Comment",
description: "Comment",
icon: MessageSquare,
},
23: {
number: 23,
"23": {
id: "23",
name: "Long-form",
description: "Long-form content",
icon: FileText,
},
24: {
number: 24,
"24": {
id: "24",
name: "Extra Metadata",
description: "Extra metadata fields and tags",
icon: Tag,
},
25: { number: 25, name: "Reactions", description: "Reactions", icon: Heart },
26: {
number: 26,
"25": { id: "25", name: "Reactions", description: "Reactions", icon: Heart },
"26": {
id: "26",
name: "Delegated Signing",
description: "Delegated event signing",
icon: Key,
deprecated: true,
},
27: {
number: 27,
"27": {
id: "27",
name: "Text Note References",
description: "Text note references",
icon: Link,
},
28: {
number: 28,
"28": {
id: "28",
name: "Public Chat",
description: "Public chat",
icon: MessageSquare,
},
29: {
number: 29,
"29": {
id: "29",
name: "Relay Groups",
description: "Relay-based groups",
icon: Users,
},
30: {
number: 30,
"30": {
id: "30",
name: "Custom Emoji",
description: "Custom emoji",
icon: Gift,
},
31: {
number: 31,
"31": {
id: "31",
name: "Unknown Events",
description: "Dealing with unknown event kinds",
icon: AlertCircle,
},
32: { number: 32, name: "Labeling", description: "Labeling", icon: Tag },
34: { number: 34, name: "Git", description: "git stuff", icon: GitBranch },
35: { number: 35, name: "Torrents", description: "Torrents", icon: Share2 },
36: {
number: 36,
"32": { id: "32", name: "Labeling", description: "Labeling", icon: Tag },
"34": { id: "34", name: "Git", description: "git stuff", icon: GitBranch },
"35": { id: "35", name: "Torrents", description: "Torrents", icon: Share2 },
"36": {
id: "36",
name: "Sensitive Content",
description: "Sensitive content warnings",
icon: Eye,
},
37: {
number: 37,
"37": {
id: "37",
name: "Draft Events",
description: "Draft Events",
icon: FileText,
},
38: {
number: 38,
"38": {
id: "38",
name: "User Status",
description: "User statuses",
icon: Flag,
},
39: {
number: 39,
"39": {
id: "39",
name: "External Identity",
description: "External identities in profiles",
icon: Globe,
},
40: {
number: 40,
"40": {
id: "40",
name: "Expiration",
description: "Expiration timestamp",
icon: Calendar,
},
42: {
number: 42,
"42": {
id: "42",
name: "Authentication",
description: "Authentication of clients to relays",
icon: Shield,
},
43: {
number: 43,
"43": {
id: "43",
name: "Relay Access",
description: "Fast Authentication and Relay Access",
icon: Server,
},
44: {
number: 44,
"44": {
id: "44",
name: "Encrypted Payloads",
description: "Encrypted Payloads (Versioned)",
icon: Lock,
},
45: {
number: 45,
"45": {
id: "45",
name: "Event Counts",
description: "Counting results",
icon: Hash,
},
46: {
number: 46,
"46": {
id: "46",
name: "Remote Signing",
description: "Nostr connect protocol",
icon: Key,
},
47: {
number: 47,
"47": {
id: "47",
name: "Wallet Connect",
description: "Wallet connect",
icon: Wallet,
},
48: { number: 48, name: "Proxy Tags", description: "Proxy tags", icon: Tag },
49: {
number: 49,
"48": { id: "48", name: "Proxy Tags", description: "Proxy tags", icon: Tag },
"49": {
id: "49",
name: "Private Key Encryption",
description: "Private key encryption",
icon: Lock,
},
50: {
number: 50,
"50": {
id: "50",
name: "Search",
description: "Search capability",
icon: Search,
},
51: { number: 51, name: "Lists", description: "Lists", icon: Filter },
52: {
number: 52,
"51": { id: "51", name: "Lists", description: "Lists", icon: Filter },
"52": {
id: "52",
name: "Calendar Events",
description: "Calendar Events",
icon: Calendar,
},
53: {
number: 53,
"53": {
id: "53",
name: "Live Activities",
description: "Live Activities",
icon: Radio,
},
54: { number: 54, name: "Wiki", description: "Wiki", icon: FileText },
55: {
number: 55,
"54": { id: "54", name: "Wiki", description: "Wiki", icon: FileText },
"55": {
id: "55",
name: "Android Signer",
description: "Android Signer Application",
icon: Key,
},
56: { number: 56, name: "Reporting", description: "Reporting", icon: Flag },
57: {
number: 57,
"56": { id: "56", name: "Reporting", description: "Reporting", icon: Flag },
"57": {
id: "57",
name: "Lightning Zaps",
description: "Lightning zaps",
icon: Zap,
},
58: { number: 58, name: "Badges", description: "Badges", icon: Star },
59: { number: 59, name: "Gift Wrap", description: "Gift Wrap", icon: Gift },
60: {
number: 60,
"58": { id: "58", name: "Badges", description: "Badges", icon: Star },
"59": { id: "59", name: "Gift Wrap", description: "Gift Wrap", icon: Gift },
"60": {
id: "60",
name: "Cashu Wallet",
description: "Cashu Wallet",
icon: Wallet,
},
61: { number: 61, name: "Nutzaps", description: "Nutzaps", icon: Zap },
62: {
number: 62,
"61": { id: "61", name: "Nutzaps", description: "Nutzaps", icon: Zap },
"62": {
id: "62",
name: "Request to Vanish",
description: "Request to Vanish",
icon: Eye,
},
64: { number: 64, name: "Chess", description: "Chess (PGN)", icon: Gamepad2 },
65: {
number: 65,
"64": { id: "64", name: "Chess", description: "Chess (PGN)", icon: Gamepad2 },
"65": {
id: "65",
name: "Relay List",
description: "Relay list metadata",
icon: Server,
},
66: {
number: 66,
"66": {
id: "66",
name: "Relay Discovery",
description: "Relay Discovery",
icon: Compass,
},
68: {
number: 68,
"68": {
id: "68",
name: "Picture-first",
description: "Picture-first feeds",
icon: Image,
},
69: {
number: 69,
"69": {
id: "69",
name: "P2P Order",
description: "Peer-to-peer Order events",
icon: ShoppingCart,
},
70: {
number: 70,
"70": {
id: "70",
name: "Protected Events",
description: "Protected Events",
icon: Shield,
},
71: {
number: 71,
"71": {
id: "71",
name: "Video Events",
description: "Video Events",
icon: Video,
},
72: {
number: 72,
"72": {
id: "72",
name: "Moderation",
description: "Moderated communities",
icon: Shield,
},
73: {
number: 73,
"73": {
id: "73",
name: "External Content IDs",
description: "External Content IDs",
icon: Link,
},
75: { number: 75, name: "Zap Goals", description: "Zap Goals", icon: Zap },
77: {
number: 77,
"75": { id: "75", name: "Zap Goals", description: "Zap Goals", icon: Zap },
"77": {
id: "77",
name: "Negentropy",
description: "Negentropy Protocol Sync",
icon: Server,
},
78: {
number: 78,
"78": {
id: "78",
name: "App Data",
description: "Application-specific data",
icon: Database,
},
84: {
number: 84,
"84": {
id: "84",
name: "Highlights",
description: "Highlights",
icon: Bookmark,
},
86: {
number: 86,
"85": {
id: "85",
name: "Trusted Assertions",
description: "Trusted Assertions",
icon: Shield,
},
"86": {
id: "86",
name: "Relay Management",
description: "Relay Management API",
icon: Server,
},
87: {
number: 87,
"87": {
id: "87",
name: "Ecash Mints",
description: "Ecash Mint Discoverability",
icon: Coins,
},
88: { number: 88, name: "Polls", description: "Polls", icon: Filter },
89: {
number: 89,
"88": { id: "88", name: "Polls", description: "Polls", icon: Filter },
"89": {
id: "89",
name: "App Handlers",
description: "Recommended application handlers",
icon: Package,
},
90: {
number: 90,
"90": {
id: "90",
name: "Data Vending",
description: "Data Vending Machines",
icon: Database,
},
92: {
number: 92,
"92": {
id: "92",
name: "Media Attachments",
description: "Media Attachments",
icon: Image,
},
94: {
number: 94,
"94": {
id: "94",
name: "File Metadata",
description: "File metadata",
icon: Image,
},
96: {
number: 96,
"96": {
id: "96",
name: "HTTP File Storage",
description: "HTTP File Storage Integration",
icon: Server,
deprecated: true,
},
98: {
number: 98,
"98": {
id: "98",
name: "HTTP Auth",
description: "HTTP authentication",
icon: Lock,
},
99: {
number: 99,
"99": {
id: "99",
name: "Classified Listings",
description: "Classified listings",
icon: Tag,
},
// Hex NIPs (A0-EE)
// Hex NIPs (5A-EE)
"5A": {
id: "5A",
name: "Static Websites",
description: "Pubkey Static Websites",
icon: Globe,
},
"7D": {
id: "7D",
name: "Threads",
description: "Threads",
icon: MessageSquare,
},
A0: {
number: 0xa0,
id: "A0",
name: "Voice Messages",
description: "Voice Messages",
icon: Music,
},
A4: {
id: "A4",
name: "Public Messages",
description: "Public Messages",
icon: MessageSquare,
},
B0: {
number: 0xb0,
id: "B0",
name: "Web Bookmarks",
description: "Web Bookmarks",
icon: Bookmark,
},
B7: { number: 0xb7, name: "Blossom", description: "Blossom", icon: Package },
B7: { id: "B7", name: "Blossom", description: "Blossom", icon: Package },
BE: {
number: 0xbe,
id: "BE",
name: "BLE",
description: "BLE Communications",
icon: Radio,
},
C0: {
number: 0xc0,
id: "C0",
name: "Code Snippets",
description: "Code Snippets",
icon: Code,
},
C7: {
number: 0xc7,
id: "C7",
name: "Chats",
description: "Chats",
icon: MessageSquare,
},
EE: {
number: 0xee,
id: "EE",
name: "E2EE MLS",
description: "E2EE Messaging (MLS)",
icon: Lock,
},
"7D": {
number: 0x7d,
name: "Threads",
description: "Threads",
icon: MessageSquare,
deprecated: true,
},
};
/**
* Get NIP metadata by number (handles both decimal and hex)
* Get NIP metadata by identifier (handles both string IDs and numeric lookups)
*/
export function getNIPInfo(nipNumber: number | string): NIPInfo | undefined {
export function getNIPInfo(nipId: number | string): NIPInfo | undefined {
const key = typeof nipId === "string" ? nipId : String(nipId);
// Try direct lookup
if (NIP_METADATA[nipNumber]) {
return NIP_METADATA[nipNumber];
if (NIP_METADATA[key]) {
return NIP_METADATA[key];
}
// Try hex conversion for numbers > 99
if (typeof nipNumber === "number" && nipNumber > 99) {
const hexKey = nipNumber.toString(16).toUpperCase();
if (typeof nipId === "number" && nipId > 99) {
const hexKey = nipId.toString(16).toUpperCase();
return NIP_METADATA[hexKey];
}
return undefined;
}
/**
* Get all supported NIPs with their metadata, excluding deprecated ones
*/
export function getSupportedNIPsInfo(
nipNumbers: number[],
includeDeprecated: boolean = false,
): (NIPInfo | { number: number; name: string; icon: LucideIcon })[] {
return nipNumbers
.map((num) => {
const info = getNIPInfo(num);
if (info) {
// Skip deprecated NIPs unless explicitly included
if (!includeDeprecated && info.deprecated) {
return null;
}
return info;
}
// Fallback for unknown NIPs
return {
number: num,
name: `NIP-${num}`,
icon: FileText,
};
})
.filter(
(
nip,
): nip is NIPInfo | { number: number; name: string; icon: LucideIcon } =>
nip !== null,
);
}
/**
* Group NIPs by a single consolidated category
* For relay viewer, we show all NIPs in one list
*/
export function groupNIPsByCategory(
nipNumbers: number[],
): Record<string, NIPInfo[]> {
const grouped: Record<string, NIPInfo[]> = {
"Supported NIPs": [],
};
nipNumbers.forEach((num) => {
const info = getNIPInfo(num);
// Skip deprecated NIPs
if (info && !info.deprecated) {
grouped["Supported NIPs"].push(info);
}
});
return grouped;
}