feat: event anatomy slide

NIP-01 event rendered as syntax-highlighted JSON hugged by large
nostr-purple braces, with the derived id/sig fields badged and a
fill -> hash -> sign strip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
padreug 2026-07-22 02:53:45 +02:00
commit 07f98a04dc

View file

@ -0,0 +1,85 @@
<script setup lang="ts">
import { PenLine, Hash, KeyRound, ArrowRight } from '@lucide/vue'
import SlideShell from './SlideShell.vue'
// Each field of a NIP-01 event: the key, a sample value, and what it means.
// `derived` fields aren't typed by you they're computed when the event is signed.
interface Field {
key: string
value: string
kind: 'string' | 'number' | 'array'
comment: string
derived?: boolean
}
const fields: Field[] = [
{ key: 'pubkey', value: '"3bf0c6…2a"', kind: 'string', comment: 'who wrote it — your public key' },
{ key: 'created_at', value: '1721600000', kind: 'number', comment: 'when — unix seconds' },
{ key: 'kind', value: '1', kind: 'number', comment: '1 = short text note' },
{ key: 'tags', value: '[["e","…"],["p","…"]]', kind: 'array', comment: 'references: reply-to, mentions' },
{ key: 'content', value: '"gm nostr ⚡"', kind: 'string', comment: 'the message itself' },
{ key: 'id', value: '"0a1b2c…9f"', kind: 'string', comment: 'sha256 of the fields above', derived: true },
{ key: 'sig', value: '"d4e5…c7"', kind: 'string', comment: 'Schnorr sig over id — proof its you', derived: true },
]
const valueClass = { string: 'text-emerald-300', number: 'text-amber-300', array: 'text-zinc-300' }
const steps = [
{ icon: PenLine, label: 'Fill the fields', sub: 'pubkey · kind · tags · content · time' },
{ icon: Hash, label: 'Hash them → id', sub: 'sha256 of the serialized event' },
{ icon: KeyRound, label: 'Sign id → sig', sub: 'Schnorr signature with your nsec' },
]
</script>
<template>
<SlideShell kicker="NIP-01 · the atom of Nostr" title="Anatomy of an event">
<!-- The event, as JSON, hugged by big nostr-purple braces -->
<div class="flex items-center justify-center gap-3 sm:gap-6">
<span class="select-none text-[9rem] font-thin leading-none text-violet-500/80 sm:text-[11rem]">{</span>
<div class="grid grid-cols-[auto_auto_1fr] items-center gap-x-5 gap-y-1.5 font-mono text-base sm:text-lg">
<template v-for="(f, i) in fields" :key="f.key">
<span
class="justify-self-end"
:class="f.derived ? 'font-semibold text-violet-300' : 'text-violet-300'"
>
"{{ f.key }}"<span class="text-zinc-500">:</span>
</span>
<span class="whitespace-nowrap" :class="valueClass[f.kind]"
>{{ f.value }}<span v-if="i < fields.length - 1" class="text-zinc-500">,</span></span
>
<span class="whitespace-nowrap text-sm text-zinc-600">
<span class="text-zinc-500">// </span>{{ f.comment
}}<span v-if="f.derived" class="ml-1 rounded bg-violet-500/15 px-1 text-xs text-violet-300"
>computed</span
>
</span>
</template>
</div>
<span class="select-none text-[9rem] font-thin leading-none text-violet-500/80 sm:text-[11rem]">}</span>
</div>
<!-- How it's built & signed -->
<div class="mt-8 flex flex-col items-stretch gap-3 sm:flex-row sm:items-center sm:justify-center">
<template v-for="(s, i) in steps" :key="s.label">
<div class="flex items-center gap-3 rounded-xl border border-zinc-700 bg-zinc-900/60 px-4 py-3">
<component :is="s.icon" class="h-6 w-6 flex-none text-violet-400" />
<div>
<p class="font-semibold text-white">{{ s.label }}</p>
<p class="text-xs text-zinc-400">{{ s.sub }}</p>
</div>
</div>
<ArrowRight
v-if="i < steps.length - 1"
class="mx-auto h-5 w-5 flex-none rotate-90 text-zinc-500 sm:rotate-0"
/>
</template>
</div>
<p class="mt-6 text-center text-sm text-zinc-500">
Anyone recomputes the hash and checks <code class="text-zinc-300">sig</code> against your
<code class="text-zinc-300">pubkey</code> tamper with one character and it fails. No server vouches for you; the math does.
</p>
</SlideShell>
</template>