Kind 16769 (profile tabs): - src/lib/profileTabsEvent.ts: encode/decode kind 16769 — tab tags are ['tab', label, filtersJSON], no UUIDs, label is the stable key - useProfileTabs: queries kind 16769 with authors filter (secure) - usePublishProfileTabs: publishes kind 16769, invalidates cache - useProfileSupplementary: now fetches kind 16769 alongside kind 3 and 10001 in a single relay round-trip, seeds profile-tabs query cache - NIP.md: documents kind 16769 schema ProfileTabEditModal: - Save button is now full-width with a Check icon (Cancel below) - hideFrom + hideSort props on SavedFeedFiltersEditor used here — author is the profile owner implicitly, sort is irrelevant for profile tabs ProfileTabsManagerModal: - dnd-kit drag-to-reorder sortable list of custom tabs - Per-row edit (pencil) and remove (trash) buttons - Add custom tab button at the bottom - Single Save button publishes the full updated list ProfilePage tab bar (own profile): - Custom tabs read from kind 16769 via useProfileTabs - + button opens a DropdownMenu: core tabs listed as already-added (dimmed with checkmarks), Add custom tab at the bottom - SlidersHorizontal pencil at end of row opens the manager modal (only shown when custom tabs exist) - Per-tab onEdit pencil removed — manager handles all editing - ProfileTab type aliased to CoreProfileTab to avoid collision with CustomProfileTab from profileTabsEvent
9.0 KiB
NIP: Custom Event Kinds
Event Kinds Overview
| Kind | Name | Description |
|---|---|---|
| 36767 | Theme Definition | Shareable, named custom UI theme |
| 16767 | Active Profile Theme | The user's currently active theme (one per user) |
| 16769 | Profile Tabs | The user's custom profile page tabs (one per user) |
Kind 36767: Theme Definition
Summary
Addressable event kind for publishing shareable custom UI themes. A single user may publish multiple themes, each identified by a unique d tag.
A theme consists of colors, optional fonts, and an optional background. Colors are stored in c tags, fonts in f tags, and background in a bg tag.
Event Structure
{
"kind": 36767,
"content": "",
"tags": [
["d", "mk-dark-theme"],
["c", "#1a1a2e", "background"],
["c", "#e0e0e0", "text"],
["c", "#6c3ce0", "primary"],
["f", "Inter", "https://example.com/inter.woff2"],
["bg", "url https://example.com/bg.jpg", "mode cover", "m image/jpeg", "dim 1920x1080"],
["title", "MK Dark Theme"],
["alt", "Custom theme: MK Dark Theme"]
]
}
Content
The content field is unused and MUST be an empty string ("").
Tags
| Tag | Required | Description |
|---|---|---|
d |
Yes | Unique identifier (slug) for this theme, e.g. "mk-dark-theme" |
c |
Yes (×3) | Hex color with marker. See Color Tags. |
f |
No | Font declaration. See Font Tag. |
bg |
No | Background media. See Background Tag. |
title |
Yes | Human-readable theme name |
alt |
Yes | NIP-31 human-readable fallback |
Multiple Themes Per User
Since kind 36767 is addressable, a user can publish multiple themes by using different d tag values. Publishing a new event with the same d tag replaces the previous version (this is how editing works).
Kind 16767: Active Profile Theme
Summary
Replaceable event that represents the user's currently active profile theme. Only one per user. When other users visit a profile, they query this kind to determine what theme to display.
Event Structure
{
"kind": 16767,
"content": "",
"tags": [
["c", "#1a1a2e", "background"],
["c", "#e0e0e0", "text"],
["c", "#6c3ce0", "primary"],
["f", "Inter", "https://example.com/inter.woff2"],
["bg", "url https://example.com/bg.jpg", "mode cover", "m image/jpeg"],
["title", "MK Dark Theme"],
["alt", "Active profile theme"]
]
}
Content
The content field is unused and MUST be an empty string ("").
Tags
| Tag | Required | Description |
|---|---|---|
c |
Yes (×3) | Hex color with marker. See Color Tags. |
f |
No | Font declaration. See Font Tag. |
bg |
No | Background media. See Background Tag. |
title |
No | Human-readable name for the theme |
alt |
Yes | NIP-31 human-readable fallback |
Client Behavior
- When visiting a profile, clients query
{ kinds: [16767], authors: [pubkey], limit: 1 }to get the active theme. - Clients read the
ctags to extract colors,ftags for fonts, andbgtag for the background. - Setting a new active theme publishes a new kind 16767 event (replacing the old one).
- To remove the active theme, publish a kind 5 deletion event targeting kind 16767.
Shared Tag Definitions
The following tag definitions apply to both kind 36767 and kind 16767.
Color Tags
Format: ["c", "#rrggbb", "<marker>"]
| Index | Required | Description |
|---|---|---|
| 0 | Yes | Tag name: "c" |
| 1 | Yes | Lowercase 6-digit hex color code including the # sign (e.g. "#ff0000") |
| 2 | Yes | Color role marker: one of "primary", "text", or "background" |
- All three markers (
"primary","text","background") MUST be present. - Only one
ctag per marker is allowed.
Font Tag
Format: ["f", "<family>", "<url>"]
| Index | Required | Description |
|---|---|---|
| 0 | Yes | Tag name: "f" |
| 1 | Yes | CSS font-family name (e.g. "Inter") |
| 2 | Yes | Direct URL to a font file (.woff2, .ttf, .otf) |
- The
ftag is optional on the event. - At most one
ftag per event is allowed. - The font applies globally to all text (body, headings, UI elements).
- If the URL fails to load, the client SHOULD fall back to a default font gracefully.
- Variable font files (covering multiple weights in a single file) are preferred.
Background Tag
The bg tag uses an imeta-style variadic format where each entry (after the tag name) is a space-delimited key/value pair.
Format: ["bg", "url <url>", "mode <mode>", "m <mime-type>", ...]
| Key | Required | Description |
|---|---|---|
url |
Yes | URL to an image or video file |
mode |
Yes | Display mode: "cover" or "tile" |
m |
Yes | MIME type (e.g. "image/jpeg", "image/png", "video/mp4") |
dim |
No | Dimensions in pixels: "<width>x<height>" (e.g. "1920x1080") |
blurhash |
No | Blurhash placeholder string for progressive loading |
- At most one
bgtag is allowed per event. - Clients MAY choose not to render video backgrounds for performance or bandwidth reasons.
- Unknown keys SHOULD be ignored for forward compatibility.
Kind 16769: Profile Tabs
Summary
Replaceable event kind for publishing a user's custom profile page tabs. Exactly one event per user (no d tag). Each tab is a saved search feed scoped to the author's pubkey.
Visitors who load a profile fetch this event to display the custom tabs alongside the standard Posts / Media / Likes / Wall tabs.
Event Structure
{
"kind": 16769,
"content": "",
"tags": [
["tab", "<label>", "<filtersJSON>"],
["tab", "<label>", "<filtersJSON>"],
["alt", "Custom profile tabs"]
]
}
Tags
| Tag | Values | Description |
|---|---|---|
tab |
label, filtersJSON |
One tag per custom tab. Order defines display order. |
alt |
"Custom profile tabs" |
NIP-31 human-readable fallback. Required. |
Tab Filters JSON
The third value of each tab tag is a JSON-encoded object matching the SavedFeedFilters schema:
{
"query": "bitcoin",
"mediaType": "all",
"language": "global",
"platform": "nostr",
"kindFilter": "all",
"customKindText": "",
"authorScope": "people",
"authorPubkeys": ["<hex-pubkey>"],
"sort": "recent"
}
Behavior
- To add or update tabs: publish a new kind 16769 event with all current
tabtags. - To clear all tabs: publish a kind 16769 event with no
tabtags (onlyalt). - Clients MUST filter by
authors: [pubkey]when querying to prevent spoofing. - The
authorPubkeysfield inside filters SHOULD always include the profile owner's pubkey so tabs show the owner's own posts.