From 3d97870b5905ee8071ff08a1d8f41686146edc82 Mon Sep 17 00:00:00 2001 From: Chad Curtis Date: Mon, 14 Jul 2025 21:40:30 +0000 Subject: [PATCH] init --- NIP.md | 116 ++++ README.md | 80 ++- index.html | 7 +- package-lock.json | 10 + package.json | 1 + public/manifest.webmanifest | 21 + src/components/GnomeComposer.tsx | 193 ++++++ src/components/GnomeFeed.tsx | 221 +++++++ src/components/GnomeHeader.tsx | 88 +++ src/components/NoteContent.test.tsx | 6 +- src/components/NoteContent.tsx | 7 +- src/index.css | 178 ++++-- src/lib/gnomeSpeak.test.ts | 61 ++ src/lib/gnomeSpeak.ts | 923 ++++++++++++++++++++++++++++ src/main.tsx | 4 +- src/pages/Index.tsx | 38 +- tailwind.config.ts | 3 + 17 files changed, 1875 insertions(+), 82 deletions(-) create mode 100644 NIP.md create mode 100644 public/manifest.webmanifest create mode 100644 src/components/GnomeComposer.tsx create mode 100644 src/components/GnomeFeed.tsx create mode 100644 src/components/GnomeHeader.tsx create mode 100644 src/lib/gnomeSpeak.test.ts create mode 100644 src/lib/gnomeSpeak.ts diff --git a/NIP.md b/NIP.md new file mode 100644 index 0000000..b6e7009 --- /dev/null +++ b/NIP.md @@ -0,0 +1,116 @@ +# NIP-GNOME: Gnome Speak Translation Protocol + +`draft` `optional` + +## Abstract + +This NIP defines a client-side translation protocol for converting kind 1 (Short Text Note) events into "Gnome Speak" - a whimsical fantasy language that transforms regular text into gnome-themed vocabulary and expressions. + +## Motivation + +To create an immersive and entertaining experience for users of gnome-themed Nostr clients, where all text content is automatically translated into a consistent fantasy language that maintains readability while adding magical woodland charm. + +## Specification + +### Translation Scope + +- **MUST** translate all kind 1 events (Short Text Notes) into Gnome Speak +- **SHOULD NOT** translate other event kinds to maintain protocol compatibility +- **MUST** preserve original URLs, Nostr references (npub, note, etc.), and hashtags without translation +- **SHOULD** maintain the semantic meaning of the original text + +### Translation Rules + +#### Vocabulary Substitution + +Common words are replaced with gnome-themed equivalents: + +- Greetings: `hello` → `greetings, fellow earth-dweller` +- Technology: `computer` → `thinking-box of metal and magic` +- Time: `today` → `this blessed sun-cycle` +- Actions: `go` → `venture forth` +- People: `friend` → `companion of the woodland path` + +#### Gnome Expressions + +- Exclamations are enhanced with gnome phrases: `!` → `! By my pointy hat!` +- Sentence starters may be added: `In the sacred grove, ...` +- Sentence endings may be appended: `...as the ancients foretold.` + +#### Special Formatting + +- Bitcoin/crypto terms: `bitcoin` → `✨golden acorns of the digital realm✨` +- Nostr terms: `nostr` → `🍄message-mushroom🍄` +- Greetings: `gm` → `🌅when dew kisses the earth🌅` + +### Implementation + +#### Client Behavior + +Clients implementing this NIP: + +1. **MUST** detect kind 1 events +2. **MUST** apply Gnome Speak translation before displaying content +3. **SHOULD** preserve original content for editing/replying +4. **MAY** provide toggle to view original content +5. **SHOULD** indicate when content has been translated + +#### Content Preservation + +- Original event content **MUST** remain unchanged in storage/transmission +- Translation occurs only at display time +- Replies and quotes **SHOULD** reference original content, not translated + +#### Tag Usage + +Clients **MAY** add a `t` tag with value `gnome` to indicate gnome-themed content: + +```json +{ + "kind": 1, + "content": "Hello world!", + "tags": [["t", "gnome"]], + ... +} +``` + +### Example + +Original event: +```json +{ + "kind": 1, + "content": "Good morning! Having coffee and checking bitcoin prices.", + "tags": [], + ... +} +``` + +Displayed as: +``` +"When dew kisses the earth! Partaking of bitter bean brew and witnessing ✨golden acorns of the digital realm✨ values, blessed by the forest spirits." +``` + +## Rationale + +This approach provides: + +1. **Immersive Experience**: Creates a consistent fantasy atmosphere +2. **Protocol Compatibility**: No changes to underlying Nostr protocol +3. **Reversibility**: Original content always preserved +4. **Interoperability**: Non-gnome clients display original content normally + +## Security Considerations + +- Translation is purely cosmetic and doesn't affect event integrity +- Original content validation remains unchanged +- No additional attack vectors introduced + +## Implementation Status + +This NIP is implemented in Gnome Nostr client as a client-side translation layer. + +## References + +- [NIP-01: Basic protocol flow description](https://github.com/nostr-protocol/nips/blob/master/01.md) +- [NIP-10: Text Notes and Threads](https://github.com/nostr-protocol/nips/blob/master/10.md) \ No newline at end of file diff --git a/README.md b/README.md index 24402b9..9ff19a2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,79 @@ -# MKStack +# 🍄 Gnome Nostr - The Sacred Network of Woodland Folk -Template for building Nostr client application with React 18.x, TailwindCSS 3.x, Vite, shadcn/ui, and Nostrify. \ No newline at end of file +A magical Nostr client where all text notes (kind 1 events) are automatically translated into the ancient gnome tongue! Connect with fellow earth-dwellers across the message-mushroom network. + +## ✨ Features + +- **🍄 Gnome Speak Translation**: All kind 1 events are automatically translated into whimsical gnome language +- **🌲 Forest-Themed UI**: Beautiful gnome-inspired design with earthy colors and woodland aesthetics +- **🔗 Full Nostr Compatibility**: Works with any Nostr relay and preserves all protocol functionality +- **📱 Responsive Design**: Works perfectly on desktop and mobile devices +- **🌙 Dark/Light Mode**: Toggle between day forest and night forest themes +- **⚡ Lightning Fast**: Built with modern React, Vite, and TailwindCSS + +## 🎭 Gnome Speak Examples + +Regular text is magically transformed: + +- `"Hello world!"` → `"Greetings, fellow earth-dweller world! By my pointy hat!"` +- `"Good morning everyone"` → `"🌅When dew kisses the earth🌅 all forest folk"` +- `"I love bitcoin"` → `"I cherish like precious gems ✨golden acorns of the digital realm✨"` +- `"This is about #nostr"` → `"This is a woodland proclamation about #nostr"` (hashtags preserved) + +## 🛡️ Protocol Preservation + +While the display is transformed into gnome speak, the underlying Nostr protocol remains unchanged: + +- ✅ URLs are preserved and linkified +- ✅ Nostr references (npub, note, etc.) work normally +- ✅ Hashtags remain functional for filtering +- ✅ Original content is preserved for replies and quotes +- ✅ Compatible with all other Nostr clients + +## 🏗️ Technical Details + +- **Framework**: React 18 with TypeScript +- **Styling**: TailwindCSS with custom gnome theme +- **Nostr**: Nostrify for protocol integration +- **Font**: Comfortaa Variable for whimsical feel +- **Build**: Vite for fast development and production builds + +## 🌟 Custom NIP + +This client implements a custom translation protocol documented in `NIP.md`. The translation: + +- Only affects kind 1 events (Short Text Notes) +- Preserves URLs, Nostr references, and hashtags +- Maintains semantic meaning while adding magical charm +- Is purely client-side - no protocol changes required + +## 🚀 Getting Started + +1. **Clone the repository** +2. **Install dependencies**: `npm install` +3. **Start development**: `npm run dev` +4. **Build for production**: `npm run build` + +## 🎨 Customization + +The gnome vocabulary can be extended in `src/lib/gnomeSpeak.ts`. Add new word mappings to enhance the magical transformation! + +## 🤝 Contributing + +Contributions are welcome! Whether you want to: +- Add more gnome vocabulary +- Improve the forest-themed UI +- Fix bugs or add features +- Enhance the translation algorithm + +Feel free to open issues and pull requests. + +## 📜 License + +This project is open source and available under the MIT License. + +--- + +*Vibed with [MKStack](https://soapbox.pub/mkstack) • Blessed by the forest spirits 🍄✨* + +**May your mushrooms grow tall and your acorns be golden!** 🌰✨ \ No newline at end of file diff --git a/index.html b/index.html index cd5ada1..c805ab0 100644 --- a/index.html +++ b/index.html @@ -4,10 +4,15 @@ + Gnome Nostr - The Sacred Network of Woodland Folk + + + +
- + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index fec8202..bc4783e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "mkstack", "version": "0.0.0", "dependencies": { + "@fontsource-variable/comfortaa": "^5.2.6", "@hookform/resolvers": "^3.9.0", "@nostrify/nostrify": "npm:@jsr/nostrify__nostrify@^0.46.1", "@nostrify/react": "npm:@jsr/nostrify__react@^0.2.5", @@ -990,6 +991,15 @@ "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", "license": "MIT" }, + "node_modules/@fontsource-variable/comfortaa": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/@fontsource-variable/comfortaa/-/comfortaa-5.2.6.tgz", + "integrity": "sha512-uWjgXWmfUhpsax7Y6lUcWNOveIgtYYw4ZPbCkvMWjGPU2aYtTUa1Q8etT3zaDmmWXJOgv677Cgchj8Fh1304EA==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@hookform/resolvers": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz", diff --git a/package.json b/package.json index 94d0460..31f767b 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "deploy": "npm run build && npx -y nostr-deploy-cli deploy --skip-setup" }, "dependencies": { + "@fontsource-variable/comfortaa": "^5.2.6", "@hookform/resolvers": "^3.9.0", "@nostrify/nostrify": "npm:@jsr/nostrify__nostrify@^0.46.1", "@nostrify/react": "npm:@jsr/nostrify__react@^0.2.5", diff --git a/public/manifest.webmanifest b/public/manifest.webmanifest new file mode 100644 index 0000000..a6f362b --- /dev/null +++ b/public/manifest.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "Gnome Nostr - The Sacred Network of Woodland Folk", + "short_name": "Gnome Nostr", + "description": "A magical Nostr client where all messages are translated into the ancient gnome tongue. Connect with fellow earth-dwellers across the message-mushroom network!", + "start_url": "/", + "display": "standalone", + "background_color": "#f0f5f0", + "theme_color": "#d97706", + "icons": [ + { + "src": "/icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} \ No newline at end of file diff --git a/src/components/GnomeComposer.tsx b/src/components/GnomeComposer.tsx new file mode 100644 index 0000000..726752a --- /dev/null +++ b/src/components/GnomeComposer.tsx @@ -0,0 +1,193 @@ +import { useState } from 'react'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Textarea } from '@/components/ui/textarea'; +import { Button } from '@/components/ui/button'; +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; +import { Badge } from '@/components/ui/badge'; +import { Send, Sparkles, Leaf } from 'lucide-react'; +import { useCurrentUser } from '@/hooks/useCurrentUser'; +import { useNostrPublish } from '@/hooks/useNostrPublish'; +import { useAuthor } from '@/hooks/useAuthor'; +import { genUserName } from '@/lib/genUserName'; +import { translateToGnomeSpeak } from '@/lib/gnomeSpeak'; +import { useToast } from '@/hooks/useToast'; + +export function GnomeComposer() { + const [content, setContent] = useState(''); + const [isPreviewMode, setIsPreviewMode] = useState(false); + const { user } = useCurrentUser(); + const { mutate: createEvent, isPending } = useNostrPublish(); + const { toast } = useToast(); + + const author = useAuthor(user?.pubkey || ''); + const metadata = author.data?.metadata; + const displayName = metadata?.name ?? (user?.pubkey ? genUserName(user.pubkey) : 'Anonymous Gnome'); + const profileImage = metadata?.picture; + + const handleSubmit = () => { + if (!content.trim()) { + toast({ + title: "Empty woodland proclamation!", + description: "Even gnomes need something to say to the forest folk.", + variant: "destructive", + }); + return; + } + + if (!user) { + toast({ + title: "Authentication required", + description: "You must be logged in to share woodland wisdom.", + variant: "destructive", + }); + return; + } + + createEvent( + { + kind: 1, + content: content.trim(), + tags: [['t', 'gnome']] // Tag all posts as gnome content + }, + { + onSuccess: () => { + setContent(''); + setIsPreviewMode(false); + toast({ + title: "Woodland proclamation sent! 🍄", + description: "Your message has been carried by the forest winds to all the message-mushrooms.", + }); + }, + onError: (error) => { + toast({ + title: "Forest spirits are troubled", + description: `Failed to send your proclamation: ${error.message}`, + variant: "destructive", + }); + } + } + ); + }; + + const gnomeTranslatedContent = content ? translateToGnomeSpeak(content) : ''; + + if (!user) { + return ( + + +
+
🍄
+
+

Join the Forest Council

+

+ Log in to share your woodland wisdom with fellow gnomes across the message-mushroom network! +

+
+
+
+
+ ); + } + + return ( + + +
+ + + + {displayName.slice(0, 2).toUpperCase()} + + +
+
+ Share Woodland Wisdom + + 🍄 Gnome Speak + +
+

+ Your words will be translated into the ancient gnome tongue +

+
+
+
+ + +
+
+ + +
+ + {!isPreviewMode ? ( +