Files
eranos/NIP.md
T
Chad Curtis 134236636a Profile tabs: kind 16769 Nostr event, manager modal, + dropdown, wider save button
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
2026-03-05 22:53:44 -06:00

9.0 KiB
Raw Blame History

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 c tags to extract colors, f tags for fonts, and bg tag 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 c tag 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 f tag is optional on the event.
  • At most one f tag 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 bg tag 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 tab tags.
  • To clear all tabs: publish a kind 16769 event with no tab tags (only alt).
  • Clients MUST filter by authors: [pubkey] when querying to prevent spoofing.
  • The authorPubkeys field inside filters SHOULD always include the profile owner's pubkey so tabs show the owner's own posts.