SkateSpots/.agents/skills/nostr-comments/SKILL.md
shakespeare.diy 06fae294b8 New project created with Shakespeare
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-06-15 19:49:07 +07:00

207 lines
7.3 KiB
Markdown

---
name: nostr-comments
description: Implement Nostr comment systems, add discussion features to posts/articles, build community interaction features, or attach comments to any external content identifier including URLs, hashtags, and NIP-73 identifiers (ISBN, podcast GUIDs, geohashes, movie ISANs, blockchain transactions, and more).
---
# Adding Nostr Comments Sections
This skill provides a complete commenting system using NIP-22 (kind 1111) comments that can be added to any Nostr event, URL, hashtag, or NIP-73 external content identifier. The `CommentsSection` component renders a lightweight, inline commenting interface with threaded replies, user authentication, and a per-comment actions menu (Open in Ditto, Delete own comment).
**The comment system is not included in the project by default.** When the user wants comment functionality, follow the setup instructions below to install the files.
## Files Provided by This Skill
All files live under `.agents/skills/nostr-comments/files/` and must be copied into `src/` preserving the directory structure:
| Skill file | Copy to |
|---|---|
| `files/hooks/useComments.ts` | `src/hooks/useComments.ts` |
| `files/hooks/usePostComment.ts` | `src/hooks/usePostComment.ts` |
| `files/components/comments/Comment.tsx` | `src/components/comments/Comment.tsx` |
| `files/components/comments/CommentForm.tsx` | `src/components/comments/CommentForm.tsx` |
| `files/components/comments/CommentsSection.tsx` | `src/components/comments/CommentsSection.tsx` |
## Setup Instructions
### 1. Dependencies
No extra npm packages are required — the skill uses only packages already present in the template (`@nostrify/nostrify`, `@nostrify/react`, `@tanstack/react-query`, `nostr-tools`, `date-fns`, `lucide-react`, `react-router-dom`, and shadcn/ui components).
### 2. Copy the Skill Files Into `src/`
Copy every file listed in the table above from `.agents/skills/nostr-comments/files/` into its corresponding location under `src/`. Preserve the exact paths — the files use `@/hooks/...` and `@/components/...` imports that depend on these locations.
### 3. Required Project Components
The comment components depend on pieces that already ship with the template:
- `@/components/NoteContent` — renders comment text (used by `Comment.tsx`)
- `@/components/auth/LoginArea` — shown to logged-out users (used by `CommentForm.tsx`)
- `@/components/ui/alert-dialog` — confirms deletion (used by `Comment.tsx`)
- `@/hooks/useAuthor` — fetches comment author profile (used by `Comment.tsx`)
- `@/hooks/useCurrentUser` — determines whether to show the composer and the Delete action (used by `CommentForm.tsx`, `Comment.tsx`)
- `@/hooks/useNostrPublish` — publishes kind 1111 comments and kind 5 deletion requests (used by `usePostComment.ts`, `Comment.tsx`)
- `@/hooks/useToast` — user feedback on delete success/failure (used by `Comment.tsx`)
All of these are standard in the template; no extra work is needed beyond copying the skill files.
## Basic Usage
`CommentsSection` renders without an outer card, title, or header — it's just the composer followed by the threaded comment list. Wrap it in whatever page layout you want:
```tsx
import { CommentsSection } from "@/components/comments/CommentsSection";
function ArticlePage({ article }: { article: NostrEvent }) {
return (
<div className="space-y-6">
{/* Your article content */}
<div>{/* article content */}</div>
{/* Comments section — add your own heading if needed */}
<section className="space-y-4">
<h2 className="text-lg font-semibold">Comments</h2>
<CommentsSection root={article} />
</section>
</div>
);
}
```
## Per-Comment Actions Menu
Each comment has a 3-dots menu with:
- **Open in Ditto** — opens the comment at `https://ditto.pub/<nevent>` in a new tab so users can view replies, react, or zap via Ditto.
- **Delete** — only shown when the logged-in user is the comment's author. Publishes a NIP-09 (kind 5) deletion request tagging the comment's `id` and `kind`, then optimistically removes the comment from the cached query result via `queryClient.setQueriesData`. Deletion is not guaranteed across the network — the `AlertDialog` confirmation makes this clear to the user.
## Props and Customization
The `CommentsSection` component accepts the following props:
- **`root`** (required): The root to comment on. Accepts three types:
- `NostrEvent` — comment on a Nostr event (kind 1 note, long-form article, etc.)
- `URL` — comment on an external identifier: web URLs (`new URL("https://...")`) or any NIP-73 identifier except hashtags (e.g. `new URL("isbn:9780765382030")`, `new URL("iso3166:US")`)
- `#${string}` — NIP-73 hashtag only (e.g. `"#bitcoin"`); this template string type is exclusively for hashtags and must not be used for other NIP-73 identifiers
- **`emptyStateMessage`**: Message shown when no comments exist (default: "No comments yet")
- **`emptyStateSubtitle`**: Subtitle for empty state (default: "Be the first to share your thoughts!")
- **`className`**: Additional CSS classes applied to the outer `<div>` (it's a plain `space-y-6` container, no card/border)
- **`limit`**: Maximum number of comments to load (default: 500)
```tsx
<CommentsSection
root={event}
emptyStateMessage="Start the conversation"
emptyStateSubtitle="Share your thoughts about this post"
className="mt-8"
limit={100}
/>
```
## Commenting on URLs
The comments system supports commenting on external URLs, making it useful for web pages, articles, or any online content:
```tsx
<CommentsSection
root={new URL("https://example.com/article")}
/>
```
## Commenting on Hashtags
Pass a hashtag string (`#${string}` format) to attach comments to a topic. The hashtag must be lowercase:
```tsx
// Comments for the #bitcoin hashtag
<CommentsSection
root="#bitcoin"
/>
// Comments for a community-specific tag
<CommentsSection
root="#nostr"
/>
```
## Commenting on NIP-73 External Content Identifiers
NIP-73 defines a standard set of external content IDs. All NIP-73 identifiers (except hashtags) are passed as `URL` objects — the identifier string is used directly as the URL:
### Books (ISBN)
```tsx
// ISBN must be without hyphens
<CommentsSection
root={new URL("isbn:9780765382030")}
/>
```
### Podcasts
```tsx
// Podcast feed
<CommentsSection
root={new URL("podcast:guid:c90e609a-df1e-596a-bd5e-57bcc8aad6cc")}
/>
// Podcast episode
<CommentsSection
root={new URL("podcast:item:guid:d98d189b-dc7b-45b1-8720-d4b98690f31f")}
/>
```
### Movies (ISAN)
```tsx
// ISAN without version part
<CommentsSection
root={new URL("isan:0000-0000-401A-0000-7")}
/>
```
### Geohashes
```tsx
// Geohash must be lowercase
<CommentsSection
root={new URL("geo:ezs42e44yx96")}
/>
```
### Countries (ISO 3166)
```tsx
// ISO 3166 codes must be uppercase
<CommentsSection
root={new URL("iso3166:US")}
/>
// Subdivision (state/province)
<CommentsSection
root={new URL("iso3166:US-CA")}
/>
```
### Academic Papers (DOI)
```tsx
// DOI must be lowercase
<CommentsSection
root={new URL("doi:10.1000/xyz123")}
/>
```
### Blockchain Transactions and Addresses
```tsx
// Bitcoin transaction
<CommentsSection
root={new URL("bitcoin:tx:a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d")}
/>
// Ethereum address
<CommentsSection
root={new URL("ethereum:1:address:0xd8da6bf26964af9d7eed9e03e53415d37aa96045")}
/>
```