A decentralized publishing platform for NIP-23 long-form content with version control, collaborative editing, peer review workflow, and Lightning payment integration.
- Complete Version History: Track all changes to your articles with full version history
- Visual Diff Viewer: Compare any two versions side-by-side with highlighted additions and deletions
- One-Click Restore: Restore any previous version of your article instantly
- Draft & Published States: Work on drafts (kind 30024) before publishing (kind 30023)
- Multi-Author Support: Add collaborators by their Nostr public keys
- Custom Revenue Splits: Set custom weight for each collaborator
- Visual Split Preview: See exact percentage breakdown before publishing
- Contribution Tracking: Track each contributor's involvement
- Peer Review Workflow: Comment system using NIP-22 (kind 1111) for feedback
- Zap Support: Receive Lightning payments (zaps) on your articles
- Automatic Multi-Recipient Splits: Payments automatically divided among all collaborators
- NWC Integration: Full Nostr Wallet Connect (NIP-47) support for seamless payments
- WebLN Support: Alternative wallet connection method
- Custom Weight System: Define exact revenue split percentages per contributor
- Real-time Split Preview: See how zaps will be distributed before sending
- Smart Payment Routing: Each contributor receives their share directly to their lightning address
- Engagement Metrics: Track zaps, reactions, reposts, and comments
- Quality Scoring: Algorithmic quality score based on engagement
- Contributor Dashboard: View all contributors and their impact
- Real-time Statistics: Live updates of article performance
- Beautiful Article Preview: Professional rendering with custom styling
- Rich Markdown Support: GFM tables, task lists, strikethrough, and more
- Image Support: JPG, PNG, GIF, WebP with CDN optimization
- Video Playback: Native MP4 video player with controls
- Audio Player: Custom audio player with seek, volume, and time display
- Obsidian Dark Theme: Beautiful dark UI with
#0d1117background - Orange Accents: Eye-catching
#f0883eorange for action items - Responsive Layout: Perfect on desktop, tablet, and mobile
- Browse Latest Articles: View latest long-form content from Nostr network
- Major Relay Support: Pulls from Damus, Primal, Nos.lol, and more
- Live Updates: Auto-refreshes every 30 seconds
- Rich Preview Cards: See title, author, summary, and images
- One-Click Read: Click any article to read in full
- Copy naddr: Easy sharing with naddr copy button
- React 18.x - Modern UI library
- TypeScript - Type-safe development
- TailwindCSS 3.x - Utility-first styling
- Nostrify - Nostr protocol integration
- NIP-23 - Long-form content standard
- NIP-47 - Nostr Wallet Connect
- NIP-57 - Lightning Zaps
- Click "New Article" to generate a unique identifier
- Enter your article title and content in Markdown format
- Add optional metadata (summary, image, topics)
- Click "Save Draft" to save privately
You can load articles in two ways:
Using d-tag (simple identifier):
article-unique-id
Using naddr (full article address):
naddr1qqnk7ur9demk2cnj0qkhqmr4wvkk7m3dwfshxurzv4e8y7fdwp5j6dpdvam826t60ypzp56wsvk59tvtj0q6rs7ggqyngszlxz7tlptl884z7qyvycur77xsqvzqqqr4guft8h2f
The naddr format includes the article identifier, author pubkey, and kind, making it perfect for sharing articles with others.
- Add collaborators with their public keys (npub or hex format)
- Set custom revenue split weights for each contributor
- Preview the percentage breakdown in real-time
- Share the article's naddr with your team
- Each edit creates a new version automatically
- Review your draft in the Preview tab
- Click "Publish" to make it public (kind 30023)
- Your article is now discoverable on the Nostr network
- Copy the naddr to share with others
- Track engagement in the Analytics tab
- Go to the "Versions" tab
- Click "Compare Versions"
- Select two versions to compare
- View the visual diff with highlighted changes
Published articles use NIP-23 kind 30023:
{
"kind": 30023,
"content": "# Article Title\n\nYour markdown content...",
"tags": [
["d", "unique-article-id"],
["title", "Article Title"],
["summary", "Brief description"],
["image", "https://example.com/image.jpg"],
["published_at", "1675642635"],
["t", "topic1"],
["t", "topic2"],
["p", "contributor1-pubkey"],
["contribution_weight", "contributor1-pubkey", "0.6"],
["p", "contributor2-pubkey"],
["contribution_weight", "contributor2-pubkey", "0.4"],
["alt", "Article: Article Title"]
]
}All versions share the same d tag identifier:
// Query all versions of an article
const versions = await nostr.query([{
kinds: [30023, 30024], // Published and drafts
'#d': ['article-unique-id'],
limit: 100
}]);
// Sort by created_at (newest first)
versions.sort((a, b) => b.created_at - a.created_at);Articles can be loaded using either a plain d tag identifier or an naddr (NIP-19 addressable event identifier):
import { nip19 } from 'nostr-tools';
// Decode naddr to get article coordinates
const decoded = nip19.decode('naddr1qqnk7ur9...');
if (decoded.type === 'naddr') {
const { kind, pubkey, identifier } = decoded.data;
// Query for all versions by this author
const versions = await nostr.query([{
kinds: [30023, 30024],
authors: [pubkey],
'#d': [identifier],
}]);
}The naddr format is recommended for sharing as it includes:
- kind: Event kind (30023 for articles)
- pubkey: Author's public key
- identifier: The
dtag value - relays (optional): Relay hints for finding the event
// Automatically calculated based on text diffs
const weight = calculateContributionWeight(
contributorPubkey,
allVersions.map(v => ({
pubkey: v.pubkey,
content: v.content,
created_at: v.created_at
}))
);When a user zaps an article:
- Parse
contribution_weighttags from the published article - Calculate each contributor's share:
zapAmount * weight - Fetch lightning addresses (lud16) for each contributor
- Use NWC or WebLN to send payments to each contributor
- Display success/failure status for each payment
// Example: 10,000 sat zap with 60/40 split
// Contributor 1: 10,000 * 0.6 = 6,000 sats (sent to their lud16)
// Contributor 2: 10,000 * 0.4 = 4,000 sats (sent to their lud16)- In the Article Editor, click "Add Collaborator"
- Enter the collaborator's public key (npub or hex)
- Set their weight (any positive number)
- Weights are automatically normalized to percentages
- Preview shows exact split:
weight / totalWeight * 100%
Example weights:
- Contributor A: weight 3 → 60% (3/5)
- Contributor B: weight 2 → 40% (2/5)
- Total weight: 5
This project extends NIP-23 with custom tags documented in NIP.md:
contribution_weight- Track contributor percentages for revenue splits- Workflow for collaborative editing
- Guidelines for automatic payment distribution
src/
├── components/
│ ├── ArticleEditor.tsx # Main editor with preview
│ ├── VersionHistory.tsx # Version list and selection
│ ├── DiffViewer.tsx # Visual diff comparison
│ └── ArticleAnalytics.tsx # Engagement dashboard
├── hooks/
│ ├── useArticleVersions.ts # Fetch version history
│ ├── useArticleStats.ts # Fetch engagement stats
│ └── ...
├── lib/
│ └── diffUtils.ts # Diff computation algorithms
└── pages/
└── Index.tsx # Main application page
The diff viewer uses a line-based diff algorithm:
- Split old and new content into lines
- Compare lines to find matches
- Identify additions, deletions, and unchanged sections
- Render with color-coded highlighting
const diff = computeDiff(oldVersion.content, newVersion.content);
// Returns: DiffSegment[] with type: 'added' | 'removed' | 'unchanged'Quality score is calculated from engagement:
const qualityScore = Math.min(100,
(reactions.length * 2) +
(comments.length * 3) +
(reposts.length * 5) +
(zaps.length * 10)
);Users can compare any two versions:
- Click "Compare Versions" button
- Select first version
- Select second version
- View side-by-side diff with stats
- Node.js 18+
- npm or yarn
# Clone the repository
git clone <repository-url>
cd nostr-article-versions
# Install dependencies
npm install
# Start development server
npm run dev# Build for production
npm run build
# Preview production build
npm run preview# Run tests
npm test
# Type checking
npm run typecheck
# Linting
npm run lintContributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
MIT License - see LICENSE file for details
Built with Shakespeare - AI-powered website builder
Powered by:
- NIP-23 - Long-form Content
- NIP-47 - Nostr Wallet Connect
- NIP-57 - Lightning Zaps
- Nostrify - Nostr protocol library
For issues or questions:
- Open an issue on GitHub
- Contact via Nostr:
npub16d8gxt2z4k9e8sdpc0yyqzf5gp0np09ls4lnn630qzxzvwpl0rgq5h4rzv
- Publication scheduling (delayed broadcast)
- Encrypted draft sharing
- Advanced markdown editor with plugins
- Export to PDF/HTML
- Integration with IPFS for content permanence
- Multi-language support
- Rich media embedding (videos, audio)
- Automated backups to multiple relays
Vibed with Shakespeare - https://shakespeare.diy