Jason Tudisco 7077191c0c NIP-78 encrypted transport, instant UI, re-sync recovery
Nostr transport:
- Switch from kind 1 to NIP-78 kind 30078 (application-specific data)
- Relays store events persistently — no inventory protocol needed
- AES-256-GCM encryption via Web Crypto API (room key = shared secret)
- PBKDF2 key derivation (100k iterations) from room key
- Chunking for large events (images >48KB) with per-chunk encryption
- Auto re-publish missing local events on room join
- Manual "Re-sync" button for recovery of failed publishes

Performance (all variants):
- Emit 'change' immediately after local store write (IDB/NeDB/SQLite)
- Folder and Nostr writes run fire-and-forget in background
- Fast event hashing: fingerprint metadata only, skip full payload SHA-256
- Saving spinner in paste UI while write completes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 01:11:29 -06:00

45 lines
2.0 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>paste — Nostr + Folder</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<header>
<h1>paste <span id="variant-label" class="variant">nostr</span></h1>
<p class="sub">type text + enter, or paste an image &mdash; use #hashtags to add tags</p>
<div class="folder-bar">
<button id="select-folder" class="folder-btn">Select sync folder</button>
<span id="folder-status" class="status"></span>
</div>
<div class="folder-bar" style="margin-top:6px">
<input type="text" id="room-input" placeholder="room key" autocomplete="off"
style="padding:6px 10px;border:1px solid #555;border-radius:6px;background:#1e1e2e;color:#cdd6f4;font-size:0.85rem;width:180px">
<button id="join-room" class="folder-btn">Join room</button>
<button id="resync-btn" class="folder-btn" style="margin-left:4px;font-size:0.7rem;padding:4px 8px" title="Re-publish all local events to relay">Re-sync</button>
<span id="nostr-status" class="status"></span>
</div>
</header>
<main>
<div class="input-area">
<div class="input-row">
<input type="text" id="paste-input"
placeholder="type something and press Enter, or Ctrl+V an image"
autocomplete="off" spellcheck="false">
<button class="clip-btn" id="clip-btn" title="Attach a file">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48"/>
</svg>
</button>
<input type="file" id="file-input" hidden>
</div>
<div id="status" class="status"></div>
</div>
<div id="items" class="items"></div>
</main>
<script type="module" src="./app.ts"></script>
</body>
</html>