initial version (alpha)

This commit is contained in:
Danny Morabito 2025-10-12 13:03:07 -05:00
commit 0c965b54ed
Signed by: dannym
GPG key ID: 7CC8056A5A04557E
56 changed files with 10437 additions and 0 deletions

View file

@ -0,0 +1,678 @@
// @jsx h
// @jsxImportSource preact
import eveApiExample from "../highlight/eve-api-example.ts" with { type: "text" };
import subscriptionExamples from "../highlight/subscription-examples.ts" with { type: "text" };
import websocketExample from "../highlight/websocket-example.ts" with { type: "text" };
import { useSyntaxHighlighting } from "../hooks/useSyntaxHighlighting.js";
import { CodeBlock } from "./CodeBlock.jsx";
/**
* API Section - Comprehensive guide to available APIs
* Covers window.eve API and WebSocket alternatives
*/
export const APISection = () => {
useSyntaxHighlighting();
return (
<div class="space-y-6">
<h2 class="text-3xl font-bold">API Reference</h2>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h3 class="card-title text-info mb-4">Understanding Arxlet APIs</h3>
<div class="space-y-4">
<p>
Your Arxlet has access to powerful APIs that let you interact with Nostr data, manage user profiles, and
create real-time applications. Think of these APIs as your toolkit for building social, decentralized
applications within the CCN ecosystem.
</p>
<p>
<strong>Two approaches available:</strong> You can use the convenient <code>window.eve</code> API
(recommended for most cases) or connect directly via WebSocket for advanced scenarios. Both give you full
access to Nostr events and CCN features.
</p>
</div>
</div>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h3 class="card-title text-primary mb-4">🎯 Which API Should You Use?</h3>
<div class="grid md:grid-cols-2 gap-6">
<div class="border-2 border-primary rounded-lg p-4">
<h4 class="font-bold text-primary mb-3"> window.eve API (Recommended)</h4>
<p class="text-sm mb-3">
<strong>Best for most Arxlets.</strong> This high-level API handles all the complex Nostr protocol
details for you.
</p>
<div class="space-y-2 text-sm">
<div class="flex items-start gap-2">
<span class="text-success"></span>
<span>Simple promise-based functions</span>
</div>
<div class="flex items-start gap-2">
<span class="text-success"></span>
<span>Automatic error handling</span>
</div>
<div class="flex items-start gap-2">
<span class="text-success"></span>
<span>Built-in RxJS observables for real-time data</span>
</div>
<div class="flex items-start gap-2">
<span class="text-success"></span>
<span>Profile and avatar helpers</span>
</div>
<div class="flex items-start gap-2">
<span class="text-success"></span>
<span>Perfect for beginners</span>
</div>
</div>
</div>
<div class="border-2 border-accent rounded-lg p-4">
<h4 class="font-bold text-accent mb-3"> Direct WebSocket</h4>
<p class="text-sm mb-3">
<strong>For advanced use cases.</strong> Direct connection to the Nostr relay with full protocol
control.
</p>
<div class="space-y-2 text-sm">
<div class="flex items-start gap-2">
<span class="text-success"></span>
<span>Maximum performance and control</span>
</div>
<div class="flex items-start gap-2">
<span class="text-success"></span>
<span>Custom subscription management</span>
</div>
<div class="flex items-start gap-2">
<span class="text-success"></span>
<span>Raw Nostr protocol access</span>
</div>
<div class="flex items-start gap-2">
<span class="text-warning">!</span>
<span>Requires Nostr protocol knowledge</span>
</div>
<div class="flex items-start gap-2">
<span class="text-warning">!</span>
<span>More complex error handling</span>
</div>
</div>
</div>
</div>
<div class="alert alert-info mt-6">
<span>
💡 <strong>Our Recommendation:</strong> Start with <code>window.eve</code> for your first Arxlet. You can
always switch to WebSocket later if you need more control or performance.
</span>
</div>
</div>
</div>
{/* window.eve API */}
<div class="card bg-base-200 shadow-xl">
<div class="card-body">
<h3 id="window-eve-api" class="card-title text-primary mb-4">
🚀 window.eve API - Your Main Toolkit
</h3>
<div class="space-y-4 mb-6">
<p>
The <code>window.eve</code> API is your primary interface for working with Nostr data in Arxlets. It
provides simple, promise-based functions that handle all the complex protocol details behind the scenes.
</p>
<p>
<strong>How it works:</strong> Each function communicates with the local Nostr relay, processes the
results, and returns clean JavaScript objects. No need to understand Nostr protocol internals - just call
the functions and get your data.
</p>
</div>
<div class="space-y-6">
<div>
<h4 class="font-bold text-lg mb-4 text-success">📤 Publishing & Writing Data</h4>
<div class="space-y-4">
<div class="border-l-4 border-success pl-4">
<h5 class="font-bold">publish(event)</h5>
<p class="text-sm opacity-80 mb-2">
Publishes a Nostr event to the relay. This is how you save data, post messages, or create any
content.
</p>
<p class="text-sm mb-2">
<strong>Use cases:</strong> Posting messages, saving user preferences, creating notes, updating
profiles
</p>
<div class="badge badge-outline">Promise&lt;void&gt;</div>
</div>
<div class="border-l-4 border-success pl-4">
<h5 class="font-bold">signEvent(event)</h5>
<p class="text-sm opacity-80 mb-2">
Signs an unsigned Nostr event with the user's private key. Required before publishing most events.
</p>
<p class="text-sm mb-2">
<strong>Use cases:</strong> Preparing events for publication, authenticating user actions
</p>
<div class="badge badge-outline">Promise&lt;NostrEvent&gt;</div>
</div>
</div>
</div>
<div>
<h4 class="font-bold text-lg mb-4 text-info">🔍 Reading & Querying Data</h4>
<div class="space-y-4">
<div class="border-l-4 border-info pl-4">
<h5 class="font-bold">getSingleEventById(id)</h5>
<p class="text-sm opacity-80 mb-2">
Retrieves a specific event when you know its exact ID. Perfect for loading specific posts or data.
</p>
<p class="text-sm mb-2">
<strong>Use cases:</strong> Loading a specific message, fetching referenced content, getting event
details
</p>
<div class="badge badge-outline">Promise&lt;NostrEvent | null&gt;</div>
</div>
<div class="border-l-4 border-info pl-4">
<h5 class="font-bold">getSingleEventWithFilter(filter)</h5>
<p class="text-sm opacity-80 mb-2">
Gets the first event matching your criteria. Useful when you expect only one result or want the most
recent.
</p>
<p class="text-sm mb-2">
<strong>Use cases:</strong> Getting a user's latest profile, finding the most recent post, checking
if something exists
</p>
<div class="badge badge-outline">Promise&lt;NostrEvent | null&gt;</div>
</div>
<div class="border-l-4 border-info pl-4">
<h5 class="font-bold">getAllEventsWithFilter(filter)</h5>
<p class="text-sm opacity-80 mb-2">
Gets all events matching your criteria. Use this for lists, feeds, or when you need multiple
results.
</p>
<p class="text-sm mb-2">
<strong>Use cases:</strong> Building feeds, loading message history, getting all posts by a user
</p>
<div class="badge badge-outline">Promise&lt;NostrEvent[]&gt;</div>
</div>
</div>
</div>
<div>
<h4 class="font-bold text-lg mb-4 text-accent">🔄 Real-time Subscriptions</h4>
<div class="space-y-4">
<div class="border-l-4 border-accent pl-4">
<h5 class="font-bold">subscribeToEvents(filter)</h5>
<p class="text-sm opacity-80 mb-2">
Creates a live stream of events matching your filter. Your app updates automatically when new events
arrive.
</p>
<p class="text-sm mb-2">
<strong>Use cases:</strong> Live chat, real-time feeds, notifications, collaborative features
</p>
<div class="badge badge-outline">Observable&lt;NostrEvent&gt;</div>
</div>
<div class="border-l-4 border-accent pl-4">
<h5 class="font-bold">subscribeToProfile(pubkey)</h5>
<p class="text-sm opacity-80 mb-2">
Watches for profile changes for a specific user. Updates automatically when they change their name,
bio, avatar, etc.
</p>
<p class="text-sm mb-2">
<strong>Use cases:</strong> User profile displays, contact lists, member directories
</p>
<div class="badge badge-outline">Observable&lt;Profile&gt;</div>
</div>
</div>
</div>
<div>
<h4 class="font-bold text-lg mb-4 text-warning">👤 User & Profile Helpers</h4>
<div class="space-y-4">
<div class="border-l-4 border-warning pl-4">
<h5 class="font-bold">getProfile(pubkey)</h5>
<p class="text-sm opacity-80 mb-2">
Retrieves user profile information (name, bio, avatar, etc.) for any user by their public key.
</p>
<p class="text-sm mb-2">
<strong>Use cases:</strong> Displaying user info, building contact lists, showing message authors
</p>
<div class="badge badge-outline">Promise&lt;Profile | null&gt;</div>
</div>
<div class="border-l-4 border-warning pl-4">
<h5 class="font-bold">getAvatar(pubkey)</h5>
<p class="text-sm opacity-80 mb-2">
Quick helper to get just the avatar URL from a user's profile. Saves you from parsing the full
profile.
</p>
<p class="text-sm mb-2">
<strong>Use cases:</strong> Profile pictures, user avatars in lists, message author images
</p>
<div class="badge badge-outline">Promise&lt;string | null&gt;</div>
</div>
<div class="border-l-4 border-warning pl-4">
<h5 class="font-bold">publicKey</h5>
<p class="text-sm opacity-80 mb-2">
Gets the current user's public key. This identifies the user and is needed for many operations.
</p>
<p class="text-sm mb-2">
<strong>Use cases:</strong> Identifying the current user, filtering their content, permission checks
</p>
<div class="badge badge-outline">Promise&lt;string&gt;</div>
</div>
</div>
</div>
</div>
<div class="mt-8">
<h4 class="font-semibold mb-3 text-lg">Practical Example:</h4>
<p class="mb-3 text-sm">
Here's how these functions work together in a real Arxlet. This example shows fetching events, displaying
user profiles, and handling real-time updates:
</p>
<CodeBlock language="typescript" code={eveApiExample} />
</div>
</div>
</div>
{/* Real-time Subscriptions */}
<div class="card bg-base-200 shadow-xl">
<div class="card-body">
<h3 id="real-time-subscriptions" class="card-title text-accent mb-4">
🔄 Understanding Real-time Subscriptions
</h3>
<div class="space-y-4 mb-6">
<p>
<strong>What are subscriptions?</strong> Think of subscriptions as "live feeds" that automatically notify
your Arxlet when new data arrives. Instead of repeatedly asking "is there new data?", subscriptions push
updates to you instantly.
</p>
<p>
<strong>How they work:</strong> When you subscribe to events or profiles, you get an RxJS Observable - a
stream of data that flows over time. Your Arxlet can "listen" to this stream and update the UI whenever
new data arrives.
</p>
<p>
<strong>Why use them?</strong> Subscriptions make your Arxlet feel alive and responsive. Users see new
messages instantly, profile changes update immediately, and collaborative features work in real-time.
</p>
</div>
<div class="grid md:grid-cols-2 gap-6 mb-6">
<div class="border-2 border-accent rounded-lg p-4">
<h4 class="font-bold text-accent mb-3">🎯 Event Subscriptions</h4>
<p class="text-sm mb-3">
<code>subscribeToEvents(filter)</code> gives you a live stream of events matching your criteria.
</p>
<div class="space-y-2 text-sm">
<div>
<strong>Perfect for:</strong>
</div>
<ul class="list-disc list-inside space-y-1 ml-2">
<li>Live chat applications</li>
<li>Real-time feeds and timelines</li>
<li>Notification systems</li>
<li>Collaborative tools</li>
<li>Activity monitoring</li>
</ul>
</div>
</div>
<div class="border-2 border-warning rounded-lg p-4">
<h4 class="font-bold text-warning mb-3">👤 Profile Subscriptions</h4>
<p class="text-sm mb-3">
<code>subscribeToProfile(pubkey)</code> watches for changes to a specific user's profile.
</p>
<div class="space-y-2 text-sm">
<div>
<strong>Perfect for:</strong>
</div>
<ul class="list-disc list-inside space-y-1 ml-2">
<li>User profile displays</li>
<li>Contact lists that stay current</li>
<li>Member directories</li>
<li>Avatar/name displays</li>
<li>User status indicators</li>
</ul>
</div>
</div>
</div>
<div class="mb-6">
<h4 class="font-semibold mb-3 text-lg">How to Use Subscriptions:</h4>
<p class="mb-3 text-sm">
Here's a complete example showing how to set up subscriptions, handle incoming data, and clean up
properly:
</p>
<CodeBlock language="typescript" code={subscriptionExamples} />
</div>
<div class="grid md:grid-cols-2 gap-4">
<div class="alert alert-warning">
<div>
<h4 class="font-bold mb-2">! Memory Management</h4>
<div class="text-sm space-y-1">
<p>
Always call <code>unsubscribe()</code> when:
</p>
<ul class="list-disc list-inside ml-2">
<li>Your component unmounts</li>
<li>User navigates away</li>
<li>You no longer need the data</li>
<li>Your Arxlet is closing</li>
</ul>
<p class="mt-2">
<strong>Why?</strong> Prevents memory leaks and unnecessary disk i/o.
</p>
</div>
</div>
</div>
<div class="alert alert-success">
<div>
<h4 class="font-bold mb-2"> Pro Tips</h4>
<div class="text-sm space-y-1">
<ul class="list-disc list-inside">
<li>Use specific filters to reduce data volume</li>
<li>Debounce rapid updates for better UX</li>
<li>Cache data to avoid duplicate processing</li>
<li>
Handle errors gracefully with <code>catchError</code>
</li>
<li>
Consider using <code>takeUntil</code> for automatic cleanup
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
{/* WebSocket Alternative */}
<div class="card bg-base-200 shadow-xl">
<div class="card-body">
<h3 id="websocket-alternative" class="card-title text-accent mb-4">
🔌 Direct WebSocket Connection - Advanced Usage
</h3>
<div class="space-y-4 mb-6">
<p>
<strong>What is the WebSocket approach?</strong> Instead of using the convenient <code>window.eve</code>{" "}
API, you can connect directly to the Nostr relay at <code>ws://localhost:6942</code> and speak the raw
Nostr protocol.
</p>
<p>
<strong>Why would you use this?</strong> Direct WebSocket gives you maximum control and performance. You
can implement custom subscription logic, handle multiple concurrent subscriptions efficiently, or
integrate with existing Nostr libraries.
</p>
<p>
<strong>The trade-off:</strong> You'll need to understand the Nostr protocol, handle JSON message parsing,
manage connection states, and implement your own error handling. It's more work but gives you complete
flexibility.
</p>
</div>
<div class="mb-6">
<h4 class="font-semibold mb-3 text-lg">WebSocket Implementation Example:</h4>
<p class="mb-3 text-sm">
Here's how to establish a WebSocket connection and communicate using standard Nostr protocol messages:
</p>
<CodeBlock language="typescript" code={websocketExample} />
</div>
<div class="grid md:grid-cols-2 gap-6 mb-6">
<div class="border-2 border-success rounded-lg p-4">
<h4 class="font-bold text-success mb-3"> Use window.eve When:</h4>
<div class="space-y-2 text-sm">
<div class="flex items-start gap-2">
<span class="text-success"></span>
<span>Building your first Arxlet</span>
</div>
<div class="flex items-start gap-2">
<span class="text-success"></span>
<span>You want simple, clean code</span>
</div>
<div class="flex items-start gap-2">
<span class="text-success"></span>
<span>Standard CRUD operations are enough</span>
</div>
<div class="flex items-start gap-2">
<span class="text-success"></span>
<span>You prefer promise-based APIs</span>
</div>
<div class="flex items-start gap-2">
<span class="text-success"></span>
<span>Built-in RxJS observables work for you</span>
</div>
<div class="flex items-start gap-2">
<span class="text-success"></span>
<span>You don't need custom protocol handling</span>
</div>
</div>
</div>
<div class="border-2 border-accent rounded-lg p-4">
<h4 class="font-bold text-accent mb-3"> Use WebSocket When:</h4>
<div class="space-y-2 text-sm">
<div class="flex items-start gap-2">
<span class="text-accent"></span>
<span>You need maximum performance</span>
</div>
<div class="flex items-start gap-2">
<span class="text-accent"></span>
<span>Custom subscription management required</span>
</div>
<div class="flex items-start gap-2">
<span class="text-accent"></span>
<span>Integrating existing Nostr libraries</span>
</div>
<div class="flex items-start gap-2">
<span class="text-accent"></span>
<span>You understand the Nostr protocol</span>
</div>
<div class="flex items-start gap-2">
<span class="text-accent"></span>
<span>Need fine-grained connection control</span>
</div>
<div class="flex items-start gap-2">
<span class="text-accent"></span>
<span>Building high-frequency applications</span>
</div>
</div>
</div>
</div>
<div class="alert alert-info">
<div>
<h4 class="font-bold mb-2">🎯 Choosing the Right Approach</h4>
<div class="text-sm space-y-2">
<p>
<strong>Start with window.eve:</strong> Even if you think you might need WebSocket later, begin with
the high-level API. You can always refactor specific parts to use WebSocket once you understand your
performance requirements.
</p>
<p>
<strong>Hybrid approach:</strong> Many successful Arxlets use <code>window.eve</code> for most
operations and WebSocket only for specific high-performance features like real-time chat or live
collaboration.
</p>
<p>
<strong>Migration path:</strong> The data structures are the same, so you can gradually migrate from{" "}
<code>window.eve</code>
to WebSocket for specific features without rewriting your entire application.
</p>
</div>
</div>
</div>
</div>
</div>
{/* Best Practices */}
<div class="card bg-base-200 shadow-xl">
<div class="card-body">
<h3 id="best-practices" class="card-title text-warning mb-4">
💡 Best Practices for Robust Arxlets
</h3>
<div class="space-y-6">
<div class="border-l-4 border-error pl-4">
<h4 class="font-bold text-lg text-error mb-3">🛡 Error Handling & Reliability</h4>
<div class="space-y-3 text-sm">
<div>
<strong>Always use try-catch blocks:</strong>
<p>
Network requests can fail, relays can be down, or data might be malformed. Wrap all API calls to
prevent crashes.
</p>
</div>
<div>
<strong>Check for null/undefined returns:</strong>
<p>
Query methods return <code>null</code> when no data is found. Always check before using the result.
</p>
</div>
<div>
<strong>Provide meaningful user feedback:</strong>
<p>
Show loading states, error messages, and success confirmations. Users should know what's happening.
</p>
</div>
<div>
<strong>Implement retry logic for critical operations:</strong>
<p>Publishing events or loading essential data should retry on failure with exponential backoff.</p>
</div>
</div>
</div>
<div class="border-l-4 border-success pl-4">
<h4 class="font-bold text-lg text-success mb-3"> Performance & Efficiency</h4>
<div class="space-y-3 text-sm">
<div>
<strong>Use specific, narrow filters:</strong>
<p>
Instead of fetching all events and filtering in JavaScript, use precise Nostr filters to reduce data
transfer.
</p>
</div>
<div>
<strong>Cache frequently accessed data:</strong>
<p>Profile information, avatars, and static content should be cached to avoid repeated API calls.</p>
</div>
<div>
<strong>Implement pagination for large datasets:</strong>
<p>
Don't load thousands of events at once. Use <code>limit</code> and <code>until</code> parameters for
pagination.
</p>
</div>
<div>
<strong>Debounce rapid user actions:</strong>
<p>
If users can trigger API calls quickly (like typing in search), debounce to avoid overwhelming the
relay.
</p>
</div>
<div>
<strong>Unsubscribe from observables:</strong>
<p>Always clean up subscriptions to prevent memory leaks and unnecessary network traffic.</p>
</div>
</div>
</div>
<div class="border-l-4 border-info pl-4">
<h4 class="font-bold text-lg text-info mb-3">🎯 User Experience</h4>
<div class="space-y-3 text-sm">
<div>
<strong>Show loading states:</strong>
<p>Use spinners, skeletons, or progress indicators while data loads. Empty screens feel broken.</p>
</div>
<div>
<strong>Handle empty states gracefully:</strong>
<p>When no data is found, show helpful messages or suggestions rather than blank areas.</p>
</div>
<div>
<strong>Implement optimistic updates:</strong>
<p>
Update the UI immediately when users take actions, then sync with the server. Makes apps feel
faster.
</p>
</div>
<div>
<strong>Provide offline indicators:</strong>
<p>Let users know when they're disconnected or when operations might not work.</p>
</div>
</div>
</div>
<div class="border-l-4 border-warning pl-4">
<h4 class="font-bold text-lg text-warning mb-3">🔒 Security & Privacy</h4>
<div class="space-y-3 text-sm">
<div>
<strong>Validate all user inputs:</strong>
<p>Never trust user input. Validate, sanitize, and escape data before using it in events or UI.</p>
</div>
<div>
<strong>Be mindful of public data:</strong>
<p>
Remember that events are visible to everyone in your CCN by default. Don't accidentally expose
private information.
</p>
</div>
<div>
<strong>Handle signing errors gracefully:</strong>
<p>Users might reject signing requests. Always have fallbacks and clear error messages.</p>
</div>
<div>
<strong>Respect user privacy preferences:</strong>
<p>Some users prefer pseudonymous usage. Don't force real names or personal information.</p>
</div>
</div>
</div>
</div>
<div class="alert alert-success mt-6">
<div>
<h4 class="font-bold mb-2">🚀 Quick Checklist for Production Arxlets</h4>
<div class="grid md:grid-cols-2 gap-4 text-sm">
<div>
<strong>Code Quality:</strong>
<ul class="list-disc list-inside mt-1 space-y-1">
<li>All API calls wrapped in try-catch</li>
<li>Null checks before using data</li>
<li>Subscriptions properly cleaned up</li>
<li>Input validation implemented</li>
</ul>
</div>
<div>
<strong>User Experience:</strong>
<ul class="list-disc list-inside mt-1 space-y-1">
<li>Loading states for all async operations</li>
<li>Error messages are user-friendly</li>
<li>Empty states handled gracefully</li>
<li>Performance tested with large datasets</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};

View file

@ -0,0 +1,628 @@
// @jsx h
// @jsxImportSource preact
import { useSyntaxHighlighting } from "../hooks/useSyntaxHighlighting.js";
import { CodeBlock } from "./CodeBlock.jsx";
/**
* Best Practices Section - Comprehensive development guidelines for Arxlets
*/
export const BestPracticesSection = () => {
useSyntaxHighlighting();
const errorHandlingExample = `// Always wrap API calls in try-catch blocks
async function loadUserData() {
try {
const events = await window.eve.getAllEventsWithFilter({
kinds: [0], // Profile events
limit: 10
});
if (events.length === 0) {
showEmptyState("No profiles found");
return;
}
displayProfiles(events);
} catch (error) {
console.error("Failed to load profiles:", error);
showErrorMessage("Unable to load profiles. Please try again.");
}
}
// Provide user feedback for all states
function showErrorMessage(message) {
const alert = document.createElement('div');
alert.className = 'alert alert-error';
alert.innerHTML = \`<span>\${message}</span>\`;
container.appendChild(alert);
}`;
const performanceExample = `// Use specific filters to reduce data transfer
const efficientFilter = {
kinds: [1], // Only text notes
authors: [userPubkey], // Only from specific user
since: Math.floor(Date.now() / 1000) - 86400, // Last 24 hours
limit: 20 // Reasonable limit
};
// Cache frequently accessed data
const profileCache = new Map();
async function getCachedProfile(pubkey) {
if (profileCache.has(pubkey)) {
return profileCache.get(pubkey);
}
const profile = await window.eve.getProfile(pubkey);
if (profile) {
profileCache.set(pubkey, profile);
}
return profile;
}
// Debounce rapid user actions
let searchTimeout;
function handleSearchInput(query) {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => {
performSearch(query);
}, 300); // Wait 300ms after user stops typing
}`;
const subscriptionExample = `// Proper subscription management
let eventSubscription;
function startListening() {
eventSubscription = window.eve.subscribeToEvents({
kinds: [1],
limit: 50
}).subscribe({
next: (event) => {
addEventToUI(event);
},
error: (error) => {
console.error("Subscription error:", error);
showErrorMessage("Lost connection. Reconnecting...");
// Implement retry logic
setTimeout(startListening, 5000);
}
});
}
// CRITICAL: Always clean up subscriptions
function cleanup() {
if (eventSubscription) {
eventSubscription.unsubscribe();
eventSubscription = null;
}
}
// Clean up when Arxlet is closed or user navigates away
window.addEventListener('beforeunload', cleanup);`;
const uiExample = `// Use DaisyUI components for consistency
function createLoadingState() {
return \`
<div class="flex justify-center items-center p-8">
<span class="loading loading-spinner loading-lg"></span>
<span class="ml-4">Loading profiles...</span>
</div>
\`;
}
function createEmptyState() {
return \`
<div class="text-center p-8">
<div class="text-6xl mb-4">📭</div>
<h3 class="text-lg font-semibold mb-2">No messages yet</h3>
<p class="text-base-content/70">Be the first to start a conversation!</p>
<button class="btn btn-primary mt-4" onclick="openComposer()">
Write a message
</button>
</div>
\`;
}
// Implement optimistic updates for better UX
async function publishMessage(content) {
// Show message immediately (optimistic)
const tempId = 'temp-' + Date.now();
addMessageToUI({ id: tempId, content, pending: true });
try {
const event = await window.eve.publish({
kind: 1,
content: content,
tags: []
});
// Replace temp message with real one
replaceMessageInUI(tempId, event);
} catch (error) {
// Remove temp message and show error
removeMessageFromUI(tempId);
showErrorMessage("Failed to send message");
}
}`;
return (
<div class="space-y-8">
<div class="text-center mb-12">
<h2 class="text-4xl font-bold mb-4"> Best Practices</h2>
<p class="text-xl text-base-content/70 max-w-3xl mx-auto">
Master the art of building production-ready Arxlets with these comprehensive development guidelines
</p>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h3 class="card-title text-info mb-4">Building Production-Ready Arxlets</h3>
<div class="space-y-4">
<p>
Creating a great Arxlet goes beyond just making it work - it needs to be reliable, performant, and provide
an excellent user experience. These best practices will help you build Arxlets that users love and that
work consistently in the CCN environment.
</p>
<p>
<strong>Why these practices matter:</strong> Arxlets run in a shared environment where performance issues
can affect other applications, and users expect the same level of polish they get from native apps.
Following these guidelines ensures your Arxlet integrates seamlessly with the CCN ecosystem.
</p>
</div>
</div>
</div>
<div class="card bg-base-200 shadow-xl">
<div class="card-body">
<h3 id="error-handling" class="card-title text-error mb-4">
🛡 Error Handling & Reliability
</h3>
<div class="space-y-4 mb-6">
<p>
<strong>User experience first:</strong> When something goes wrong, users should know what happened and
what they can do about it. Silent failures are frustrating and make your app feel broken.
</p>
</div>
<div class="space-y-6">
<div class="border-l-4 border-error pl-4">
<h4 class="font-bold text-lg mb-3">Essential Error Handling Patterns</h4>
<div class="space-y-3 text-sm">
<div>
<strong>Wrap all API calls in try-catch blocks:</strong>
<p>
Every call to <code>window.eve</code> functions can potentially fail. Always handle exceptions.
</p>
</div>
<div>
<strong>Check for null/undefined returns:</strong>
<p>
Query methods return <code>null</code> when no data is found. Verify results before using them.
</p>
</div>
<div>
<strong>Provide meaningful user feedback:</strong>
<p>Show specific error messages that help users understand what went wrong and how to fix it.</p>
</div>
<div>
<strong>Implement retry logic for critical operations:</strong>
<p>
Publishing events or loading essential data should retry automatically with exponential backoff.
</p>
</div>
</div>
</div>
<div class="mt-6">
<h4 class="font-semibold mb-3 text-lg">Practical Error Handling Example:</h4>
<CodeBlock language="typescript" code={errorHandlingExample} />
</div>
</div>
<div class="grid md:grid-cols-2 gap-4 mt-6">
<div class="alert alert-error">
<div>
<h4 class="font-bold mb-2"> Common Mistakes</h4>
<ul class="text-sm list-disc list-inside space-y-1">
<li>Not handling API failures</li>
<li>Assuming data will always exist</li>
<li>Silent failures with no user feedback</li>
<li>Generic "Something went wrong" messages</li>
<li>No retry mechanisms for critical operations</li>
</ul>
</div>
</div>
<div class="alert alert-success">
<div>
<h4 class="font-bold mb-2"> Best Practices</h4>
<ul class="text-sm list-disc list-inside space-y-1">
<li>Specific, actionable error messages</li>
<li>Graceful degradation when features fail</li>
<li>Loading states for all async operations</li>
<li>Retry buttons for failed operations</li>
<li>Offline indicators when appropriate</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="card bg-base-200 shadow-xl">
<div class="card-body">
<h3 id="performance" class="card-title text-success mb-4">
Performance & Efficiency
</h3>
<div class="space-y-6">
<div class="border-l-4 border-success pl-4">
<h4 class="font-bold text-lg mb-3">Performance Optimization Strategies</h4>
<div class="space-y-3 text-sm">
<div>
<strong>Use specific, narrow filters:</strong>
<p>
Instead of fetching all events and filtering in JavaScript, use precise Nostr filters to reduce data
transfer.
</p>
</div>
<div>
<strong>Implement intelligent caching:</strong>
<p>Cache profile information, avatars, and other static content to avoid repeated API calls.</p>
</div>
<div>
<strong>Paginate large datasets:</strong>
<p>
Don't load thousands of events at once. Use <code>limit</code> and <code>until</code> parameters for
pagination.
</p>
</div>
<div>
<strong>Debounce rapid user actions:</strong>
<p>
If users can trigger API calls quickly (like typing in search), debounce to avoid overwhelming the
relay.
</p>
</div>
</div>
</div>
<div class="mt-6">
<h4 class="font-semibold mb-3 text-lg">Performance Optimization Example:</h4>
<CodeBlock language="typescript" code={performanceExample} />
</div>
</div>
<div class="alert alert-warning mt-6">
<div>
<h4 class="font-bold mb-2">! Performance Pitfalls to Avoid</h4>
<div class="text-sm space-y-2">
<p>
<strong>Overly broad filters:</strong> Fetching all events and filtering client-side wastes bandwidth.
</p>
<p>
<strong>No pagination:</strong> Loading thousands of items at once can freeze the interface.
</p>
<p>
<strong>Repeated API calls:</strong> Fetching the same profile data multiple times is inefficient.
</p>
<p>
<strong>Unthrottled user input:</strong> Search-as-you-type without debouncing can overwhelm the
relay.
</p>
</div>
</div>
</div>
</div>
</div>
<div class="card bg-base-200 shadow-xl">
<div class="card-body">
<h3 id="subscriptions" class="card-title text-accent mb-4">
🔄 Subscription Management
</h3>
<div class="space-y-4 mb-6">
<p>
<strong>Subscriptions power real-time features.</strong> They make your Arxlet feel alive by automatically
updating when new data arrives. However, they need careful management to prevent memory leaks.
</p>
<p>
<strong>Clean up is critical.</strong> Forgetting to unsubscribe from observables can cause memory leaks,
unnecessary disk i/o, and performance degradation over time.
</p>
</div>
<div class="space-y-6">
<div class="border-l-4 border-accent pl-4">
<h4 class="font-bold text-lg mb-3">Subscription Best Practices</h4>
<div class="space-y-3 text-sm">
<div>
<strong>Always store subscription references:</strong>
<p>Keep references to all subscriptions so you can unsubscribe when needed.</p>
</div>
<div>
<strong>Implement proper cleanup:</strong>
<p>Unsubscribe when components unmount, users navigate away, or the Arxlet closes.</p>
</div>
<div>
<strong>Use specific filters:</strong>
<p>Narrow subscription filters reduce unnecessary data and improve performance.</p>
</div>
</div>
</div>
<div class="mt-6">
<h4 class="font-semibold mb-3 text-lg">Proper Subscription Management:</h4>
<CodeBlock language="typescript" code={subscriptionExample} />
</div>
</div>
<div class="alert alert-error mt-6">
<div>
<h4 class="font-bold mb-2">🚨 Memory Leak Prevention</h4>
<div class="text-sm space-y-2">
<p>
<strong>Always unsubscribe:</strong> Every <code>subscribe()</code> call must have a corresponding{" "}
<code>unsubscribe()</code>.
</p>
<p>
<strong>Clean up on navigation:</strong> Users might navigate away without properly closing your
Arxlet.
</p>
<p>
<strong>Handle page refresh:</strong> Use <code>beforeunload</code> event to clean up subscriptions.
</p>
<p>
<strong>Monitor subscription count:</strong> Too many active subscriptions can impact performance.
</p>
</div>
</div>
</div>
</div>
</div>
<div class="card bg-base-200 shadow-xl">
<div class="card-body">
<h3 id="user-experience" class="card-title text-info mb-4">
🎯 User Experience Excellence
</h3>
<div class="space-y-4 mb-6">
<p>
<strong>Great UX makes the difference.</strong> Users expect responsive, intuitive interfaces that provide
clear feedback. Small details like loading states and empty state messages significantly impact user
satisfaction.
</p>
<p>
<strong>Consistency with CCN design.</strong> Using DaisyUI components ensures your Arxlet feels
integrated with the rest of the platform while saving you development time.
</p>
</div>
<div class="space-y-6">
<div class="border-l-4 border-info pl-4">
<h4 class="font-bold text-lg mb-3">UX Best Practices</h4>
<div class="space-y-3 text-sm">
<div>
<strong>Show loading states for all async operations:</strong>
<p>Users should never see blank screens or wonder if something is happening.</p>
</div>
<div>
<strong>Handle empty states gracefully:</strong>
<p>When no data is available, provide helpful messages or suggestions for next steps.</p>
</div>
<div>
<strong>Implement optimistic updates:</strong>
<p>Update the UI immediately when users take actions, then sync with the server.</p>
</div>
<div>
<strong>Use consistent DaisyUI components:</strong>
<p>Leverage the pre-built component library for consistent styling and behavior.</p>
</div>
</div>
</div>
<div class="mt-6">
<h4 class="font-semibold mb-3 text-lg">UI/UX Implementation Examples:</h4>
<CodeBlock language="typescript" code={uiExample} />
</div>
</div>
<div class="grid md:grid-cols-2 gap-4 mt-6">
<div class="border-2 border-success rounded-lg p-4">
<h4 class="font-bold text-success mb-3"> Excellent UX Includes</h4>
<ul class="text-sm space-y-1 list-disc list-inside">
<li>Loading spinners for async operations</li>
<li>Helpful empty state messages</li>
<li>Immediate feedback for user actions</li>
<li>Clear error messages with solutions</li>
<li>Consistent visual design</li>
<li>Accessible keyboard navigation</li>
<li>Responsive layout for different screen sizes</li>
</ul>
</div>
<div class="border-2 border-warning rounded-lg p-4">
<h4 class="font-bold text-warning mb-3">! UX Anti-patterns</h4>
<ul class="text-sm space-y-1 list-disc list-inside">
<li>Blank screens during loading</li>
<li>No feedback for user actions</li>
<li>Generic or confusing error messages</li>
<li>Inconsistent styling with CCN</li>
<li>Broken layouts on mobile devices</li>
<li>Inaccessible interface elements</li>
<li>Slow or unresponsive interactions</li>
</ul>
</div>
</div>
</div>
</div>
<div class="card bg-base-200 shadow-xl">
<div class="card-body">
<h3 id="security" class="card-title text-warning mb-4">
🔒 Security & Privacy Considerations
</h3>
<div class="space-y-4 mb-6">
<p>
<strong>Security is everyone's responsibility.</strong> Even though Arxlets run in a sandboxed
environment, you still need to validate inputs, handle user data responsibly, and follow security best
practices.
</p>
<p>
<strong>Privacy by design.</strong> Remember that Nostr events are public by default. Be mindful of what
data you're storing and how you're handling user information.
</p>
</div>
<div class="space-y-6">
<div class="border-l-4 border-warning pl-4">
<h4 class="font-bold text-lg mb-3">Security Best Practices</h4>
<div class="space-y-3 text-sm">
<div>
<strong>Validate all user inputs:</strong>
<p>Never trust user input. Validate, sanitize, and escape data before using it in events or UI.</p>
</div>
<div>
<strong>Be mindful of public data:</strong>
<p>Nostr events are public by default. Don't accidentally expose private information.</p>
</div>
<div>
<strong>Handle signing errors gracefully:</strong>
<p>Users might reject signing requests. Always have fallbacks and clear error messages.</p>
</div>
<div>
<strong>Respect user privacy preferences:</strong>
<p>Some users prefer pseudonymous usage. Don't force real names or personal information.</p>
</div>
<div>
<strong>Sanitize HTML content:</strong>
<p>If displaying user-generated content, sanitize it to prevent XSS attacks.</p>
</div>
</div>
</div>
</div>
<div class="alert alert-error mt-6">
<div>
<h4 class="font-bold mb-2">🚨 Security Checklist</h4>
<div class="grid md:grid-cols-2 gap-4 text-sm">
<div>
<strong>Input Validation:</strong>
<ul class="list-disc list-inside mt-1 space-y-1">
<li>Validate all form inputs</li>
<li>Sanitize user-generated content</li>
<li>Check data types and ranges</li>
<li>Escape HTML when displaying content</li>
</ul>
</div>
<div>
<strong>Privacy Protection:</strong>
<ul class="list-disc list-inside mt-1 space-y-1">
<li>Don't store sensitive data in events</li>
<li>Respect user anonymity preferences</li>
<li>Handle signing rejections gracefully</li>
<li>Be transparent about data usage</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h3 id="production-checklist" class="card-title text-success mb-4">
🚀 Production Readiness Checklist
</h3>
<div class="space-y-4 mb-6">
<p>
Before publishing your Arxlet, run through this comprehensive checklist to ensure it meets production
quality standards. A well-tested Arxlet provides a better user experience and reflects positively on the
entire CCN ecosystem.
</p>
</div>
<div class="grid md:grid-cols-2 gap-6">
<div class="space-y-4">
<div class="border-l-4 border-success pl-4">
<h4 class="font-bold text-success mb-3"> Code Quality</h4>
<ul class="text-sm space-y-1 list-disc list-inside">
<li>All API calls wrapped in try-catch blocks</li>
<li>Null/undefined checks before using data</li>
<li>Subscriptions properly cleaned up</li>
<li>Input validation implemented</li>
<li>Error handling with user feedback</li>
<li>Performance optimizations applied</li>
<li>Code is well-commented and organized</li>
</ul>
</div>
<div class="border-l-4 border-info pl-4">
<h4 class="font-bold text-info mb-3">🎯 User Experience</h4>
<ul class="text-sm space-y-1 list-disc list-inside">
<li>Loading states for all async operations</li>
<li>Error messages are user-friendly</li>
<li>Empty states handled gracefully</li>
<li>Consistent DaisyUI styling</li>
<li>Responsive design for mobile</li>
<li>Keyboard navigation works</li>
<li>Accessibility features implemented</li>
</ul>
</div>
</div>
<div class="space-y-4">
<div class="border-l-4 border-warning pl-4">
<h4 class="font-bold text-warning mb-3">🔒 Security & Privacy</h4>
<ul class="text-sm space-y-1 list-disc list-inside">
<li>User inputs are validated and sanitized</li>
<li>No sensitive data in public events</li>
<li>Signing errors handled gracefully</li>
<li>Privacy preferences respected</li>
<li>HTML content properly escaped</li>
<li>No hardcoded secrets or keys</li>
<li>Data usage is transparent</li>
</ul>
</div>
<div class="border-l-4 border-accent pl-4">
<h4 class="font-bold text-accent mb-3"> Performance</h4>
<ul class="text-sm space-y-1 list-disc list-inside">
<li>Efficient Nostr filters used</li>
<li>Data caching implemented</li>
<li>Pagination for large datasets</li>
<li>User actions are debounced</li>
<li>Memory leaks prevented</li>
<li>Bundle size optimized</li>
<li>Performance tested with large datasets</li>
</ul>
</div>
</div>
</div>
<div class="alert alert-success mt-6">
<div>
<h4 class="font-bold mb-2">🎉 Ready for Production!</h4>
<p class="text-sm">
Once you've checked off all these items, your Arxlet is ready to provide an excellent experience for CCN
users. Remember that you can always iterate and improve based on user feedback and changing
requirements.
</p>
</div>
</div>
</div>
</div>
</div>
);
};

View file

@ -0,0 +1,53 @@
// @jsx h
// @jsxImportSource preact
import { useState } from "preact/hooks";
/**
* Reusable code block component with syntax highlighting and a copy button.
*/
export const CodeBlock = ({ language = "javascript", code }) => {
const [isCopied, setIsCopied] = useState(false);
const handleCopy = () => {
if (code) {
navigator.clipboard.writeText(code.trim());
setIsCopied(true);
setTimeout(() => setIsCopied(false), 2000);
}
};
return (
<div class="mockup-code relative">
<button
class="absolute top-2 right-2 btn btn-ghost btn-sm"
type="button"
onClick={handleCopy}
aria-label="Copy code"
>
{isCopied ? (
<span class="text-success">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>
</span>
) : (
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
/>
</svg>
)}
</button>
<pre>
<code class={`language-${language}`}>{code?.trim()}</code>
</pre>
</div>
);
};

View file

@ -0,0 +1,471 @@
// @jsx h
// @jsxImportSource preact
import buildCommand from "../highlight/build-command.sh" with { type: "text" };
import renderFunction from "../highlight/render-function.ts" with { type: "text" };
import { useSyntaxHighlighting } from "../hooks/useSyntaxHighlighting.js";
import { CodeBlock } from "./CodeBlock.jsx";
/**
* Development Section - Guide for building Arxlets
* Covers APIs, restrictions, and the required render function
*/
export const DevelopmentSection = () => {
useSyntaxHighlighting();
return (
<div class="space-y-6">
<h2 class="text-3xl font-bold">Development Guide</h2>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h3 id="understanding-arxlets" class="card-title text-info mb-4">
Understanding the Arxlet Environment
</h3>
<p class="mb-6">
When you build an Arxlet, you're creating a web application that runs inside the CCN. Think of it like
building a mini-website that has access to special CCN features and Nostr data. Here's what you have
available and what the limitations are:
</p>
</div>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h3 id="nostr-vs-arxlets" class="card-title text-purple-600 mb-4">
🔄 Nostr Apps vs Arxlets: What's the Difference?
</h3>
<div class="space-y-4 mb-6">
<p>
If you're coming from the broader Nostr ecosystem, you might be wondering how Arxlets relate to regular
Nostr applications. Here's the key relationship to understand:
</p>
<div class="grid md:grid-cols-2 gap-6">
<div class="border-2 border-success rounded-lg p-4">
<h4 class="font-bold text-success mb-3"> Nostr App Arxlet</h4>
<p class="text-sm mb-3">
<strong>Most Nostr apps CAN become Arxlets</strong> with some modifications:
</p>
<ul class="text-sm space-y-1 list-disc list-inside">
<li>
Replace external API calls with <code>window.eve</code> or local relay
</li>
<li>Adapt the UI to work within a container element</li>
<li>Remove routing if it conflicts with CCN navigation</li>
<li>
Use the provided <code>window.nostr</code> for signing
</li>
<li>Bundle everything into a single JavaScript file</li>
</ul>
</div>
<div class="border-2 border-warning rounded-lg p-4">
<h4 class="font-bold text-warning mb-3">! Arxlet Nostr App</h4>
<p class="text-sm mb-3">
<strong>Not every Arxlet works as a standalone Nostr app</strong> because:
</p>
<ul class="text-sm space-y-1 list-disc list-inside">
<li>
May depend on CCN-specific APIs (<code>window.eve</code>)
</li>
<li>Designed for the sandboxed environment</li>
<li>Might rely on CCN member data or community features</li>
<li>UI optimized for container-based rendering</li>
<li>No independent relay connections</li>
</ul>
</div>
</div>
</div>
<div class="alert alert-info">
<div>
<h4 class="font-bold mb-2">💡 Practical Examples:</h4>
<div class="text-sm space-y-2">
<p>
<strong>Easy to Port:</strong> A simple note-taking app, image gallery, or profile viewer can usually
be adapted to work as both.
</p>
<p>
<strong>CCN-Specific:</strong> A CCN member directory, community chat, or collaborative workspace
might only make sense as an Arxlet.
</p>
<p>
<strong>Hybrid Approach:</strong> Many developers create a core library that works in both
environments, then build different interfaces for standalone vs Arxlet deployment.
</p>
</div>
</div>
</div>
</div>
</div>
<div class="card bg-base-200 shadow-xl">
<div class="card-body">
<h3 id="available-apis" class="card-title text-success mb-4">
What You Can Use
</h3>
<div class="space-y-6">
<div class="border-l-4 border-success pl-4">
<h4 class="font-bold text-lg">window.eve - Your CCN Toolkit</h4>
<p class="text-sm opacity-80 mb-2">
This is your main interface to the CCN. It provides functions to read and write Nostr events, manage
data, and interact with other CCN members.
</p>
<p class="text-sm">
<strong>What it does:</strong> Lets you fetch events, publish new ones, manage user data, and access
CCN-specific features like member directories.
</p>
</div>
<div class="border-l-4 border-success pl-4">
<h4 class="font-bold text-lg">window.nostr - Cryptographic Signing (NIP-07)</h4>
<p class="text-sm opacity-80 mb-2">
This is the standard Nostr extension API that lets your Arxlet create and sign events using the user's
private key.
</p>
<p class="text-sm">
<strong>What it does:</strong> Allows your app to publish events on behalf of the user, like posting
messages, creating profiles, or any other Nostr activity that requires authentication.
</p>
</div>
<div class="border-l-4 border-success pl-4">
<h4 class="font-bold text-lg">DaisyUI 5 - Pre-built UI Components</h4>
<p class="text-sm opacity-80 mb-2">
A complete CSS framework with beautiful, accessible components already loaded and ready to use.
</p>
<p class="text-sm">
<strong>What it does:</strong> Provides buttons, cards, modals, forms, and dozens of other UI components
with consistent styling. No need to write CSS from scratch.
</p>
</div>
<div class="border-l-4 border-success pl-4">
<h4 class="font-bold text-lg">Local Relay Connection</h4>
<p class="text-sm opacity-80 mb-2">
Direct WebSocket connection to <code>ws://localhost:6942</code> for real-time Nostr event streaming.
</p>
<p class="text-sm">
<strong>What it does:</strong> Lets you subscribe to live event feeds, get real-time updates, and
implement features like live chat or notifications.
</p>
</div>
<div class="border-l-4 border-success pl-4">
<h4 class="font-bold text-lg">CCN Member Events</h4>
<p class="text-sm opacity-80 mb-2">Access to events from other members of your current CCN community.</p>
<p class="text-sm">
<strong>What it does:</strong> Enables community features like member directories, shared content, group
discussions, and collaborative tools.
</p>
</div>
<div class="border-l-4 border-success pl-4">
<h4 class="font-bold text-lg">Standard Web APIs</h4>
<p class="text-sm opacity-80 mb-2">
Full access to modern browser APIs like localStorage, fetch (for local requests), DOM manipulation, and
more.
</p>
<p class="text-sm">
<strong>What it does:</strong> Everything you'd expect in a web app - store data locally, manipulate the
page, handle user interactions, etc.
</p>
</div>
</div>
</div>
</div>
<div class="card bg-base-200 shadow-xl">
<div class="card-body">
<h3 id="security-restrictions" class="card-title text-warning mb-4">
🔒 Security & Limitations
</h3>
<div class="space-y-4">
<div class="border-l-4 border-warning pl-4">
<h4 class="font-bold">No External Network Access</h4>
<p class="text-sm">
You can't make HTTP requests to external websites or APIs. All data must come through the CCN. This
prevents data leaks and ensures all communication goes through Nostr protocols.
</p>
</div>
<div class="border-l-4 border-warning pl-4">
<h4 class="font-bold">CCN-Scoped Data</h4>
<p class="text-sm">
You only have access to events and data from your current CCN community. You can't see events from other
CCNs or the broader Nostr network unless they're specifically shared with your community.
</p>
</div>
</div>
<div class="alert alert-info mt-6">
<span>
💡 <strong>Why These Restrictions?</strong> These limitations ensure your Arxlet is secure, respects user
privacy, and works reliably within the CCN ecosystem. They also make your app more predictable and easier
to debug.
</span>
</div>
</div>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<div class="alert alert-info">
<span>
📚 <strong>Need More Details?</strong> Check the{" "}
<button
type="button"
class="link link-primary font-semibold underline"
onClick={() => {
// Find and click the API Reference tab
const tabs = document.querySelectorAll(".menu > li > a");
const apiTab = Array.from(tabs).find((tab) => tab.textContent.trim() === "API Reference");
if (apiTab) {
apiTab.click();
// Scroll to top after tab switch
setTimeout(() => {
window.scrollTo({ top: 0, behavior: "smooth" });
}, 100);
}
}}
>
API Reference
</button>{" "}
tab for comprehensive documentation of the <code>window.eve</code> API, code examples, and WebSocket usage
patterns.
</span>
</div>
</div>
</div>
<div class="card bg-base-200 shadow-xl">
<div class="card-body">
<h3 id="typescript-development" class="card-title text-info mb-4">
🚀 Building Your Arxlet with TypeScript
</h3>
<div class="space-y-4 mb-6">
<p>
<strong>What is TypeScript?</strong> TypeScript is JavaScript with type checking. It helps catch errors
before your code runs and provides better autocomplete in your editor. While you can write Arxlets in
plain JavaScript, TypeScript makes development much smoother.
</p>
<p>
<strong>Why use it for Arxlets?</strong> Eve provides TypeScript definitions for all APIs, so you'll get
autocomplete for <code>window.eve</code> functions, proper error checking, and better documentation right
in your editor.
</p>
</div>
<div class="mb-6">
<h4 class="font-semibold mb-3 text-lg">Building Your Code</h4>
<p class="mb-3">
Since Arxlets need to be a single JavaScript file, you'll use <strong>Bun</strong> (a fast JavaScript
runtime and bundler) to compile your TypeScript code. Here's the command that does everything:
</p>
<CodeBlock language="bash" code={buildCommand} />
<div class="bg-amber-50 border-l-4 border-amber-400 p-4 mt-4">
<div class="flex">
<div class="ml-3">
<p class="text-sm text-amber-700">
<strong> Svelte Exception:</strong> The above build command will NOT work for Svelte projects.
Svelte requires specific Vite configuration to compile properly. Instead, use our{" "}
<a
href="https://git.arx-ccn.com/Arx/arxlets-template"
class="link link-primary"
target="_blank"
rel="noopener noreferrer"
>
arxlets-template
</a>{" "}
and simply run <code class="bg-amber-100 px-1 rounded">bun run build</code>. Your compiled file will
be available at <code class="bg-amber-100 px-1 rounded">dist/bundle.js</code> once built.
</p>
</div>
</div>
</div>
</div>
<div class="grid md:grid-cols-2 gap-6 mt-6">
<div class="space-y-3">
<h4 class="font-semibold text-lg">What Each Build Option Does:</h4>
<div class="space-y-3">
<div class="border-l-4 border-info pl-3">
<code class="font-bold">--minify</code>
<p class="text-sm">
Removes whitespace and shortens variable names to make your file smaller. Smaller files load faster.
</p>
</div>
<div class="border-l-4 border-info pl-3">
<code class="font-bold">--target=browser</code>
<p class="text-sm">Tells Bun to optimize the code for web browsers instead of server environments.</p>
</div>
<div class="border-l-4 border-info pl-3">
<code class="font-bold">--production</code>
<p class="text-sm">
Enables all optimizations and removes development-only code for better performance.
</p>
</div>
</div>
</div>
<div class="space-y-3">
<h4 class="font-semibold text-lg">Why TypeScript Helps:</h4>
<div class="space-y-3">
<div class="border-l-4 border-success pl-3">
<strong>Catch Errors Early</strong>
<p class="text-sm">
TypeScript finds mistakes like typos in function names or wrong parameter types before you run your
code.
</p>
</div>
<div class="border-l-4 border-success pl-3">
<strong>Better Autocomplete</strong>
<p class="text-sm">
Your editor will suggest available functions and show you what parameters they expect.
</p>
</div>
<div class="border-l-4 border-success pl-3">
<strong>Easier Refactoring</strong>
<p class="text-sm">
When you rename functions or change interfaces, TypeScript helps update all the places that use
them.
</p>
</div>
<div class="border-l-4 border-success pl-3">
<strong>Self-Documenting Code</strong>
<p class="text-sm">
Type annotations serve as inline documentation, making your code easier to understand later.
</p>
</div>
</div>
</div>
</div>
<div class="alert alert-success mt-6">
<div>
<h4 class="font-bold mb-2">Recommended Development Workflow:</h4>
<ol class="list-decimal list-inside space-y-1 text-sm">
<li>
Create your main file as <kbd class="kbd">index.ts</kbd> (TypeScript)
</li>
<li>Write your Arxlet code with full TypeScript features</li>
<li>
Run the build command to create <kbd class="kbd">build.js</kbd>
</li>
<li>
Copy the contents of <kbd class="kbd">build.js</kbd> into your Nostr registration event
</li>
<li>Repeat steps 2-4 as you develop and test</li>
</ol>
</div>
</div>
</div>
</div>
<div class="card bg-base-200 shadow-xl">
<div class="card-body">
<h3 id="required-export-function" class="card-title mb-4">
The Heart of Your Arxlet: The Render Function
</h3>
<div class="space-y-4 mb-6">
<p>
<strong>What is the render function?</strong> This is the main entry point of your Arxlet - think of it as
the <code>main()</code>
function in other programming languages. When someone opens your Arxlet, Eve calls this function and gives
it a container element where your app should display itself.
</p>
<p>
<strong>How it works:</strong> The platform creates an empty <code>&lt;div&gt;</code> element and passes
it to your render function. Your job is to fill that container with your app's interface - buttons, forms,
content, whatever your Arxlet does.
</p>
<p>
<strong>Why this pattern?</strong> This approach gives you complete control over your app's interface
while keeping it isolated from other Arxlets and the main CCN interface.
</p>
</div>
<h4 class="font-semibold mb-3 text-lg">Basic Structure:</h4>
<CodeBlock language="typescript" code={renderFunction} />
<div class="mt-6 space-y-4">
<h4 class="font-semibold text-lg">Breaking Down the Example:</h4>
<div class="grid gap-4">
<div class="border-l-4 border-primary pl-4">
<code class="font-bold">export function render(container: HTMLElement)</code>
<p class="text-sm mt-1">
This declares your main function. The <code>container</code> parameter is the DOM element where your
app will live. The <code>export</code> keyword makes it available to the CCN.
</p>
</div>
<div class="border-l-4 border-primary pl-4">
<code class="font-bold">container.innerHTML = '...'</code>
<p class="text-sm mt-1">
This is the simplest way to add content - just set the HTML directly. For simple Arxlets, this might
be all you need.
</p>
</div>
<div class="border-l-4 border-primary pl-4">
<code class="font-bold">const button = container.querySelector('button')</code>
<p class="text-sm mt-1">
After adding HTML, you can find elements and attach event listeners to make your app interactive.
</p>
</div>
<div class="border-l-4 border-primary pl-4">
<code class="font-bold">window.eve.getEvents(...)</code>
<p class="text-sm mt-1">
This shows how to use the CCN API to fetch Nostr events. Most Arxlets will interact with Nostr data in
some way.
</p>
</div>
</div>
</div>
<div class="alert alert-success mt-6">
<div>
<h4 class="font-bold mb-2">Development Tips:</h4>
<ul class="list-disc list-inside space-y-1 text-sm">
<li>
<strong>Start Simple:</strong> Begin with basic HTML and gradually add interactivity
</li>
<li>
<strong>Use Modern JavaScript:</strong> async/await, destructuring, arrow functions - it all works
</li>
<li>
<strong>Leverage DaisyUI:</strong> Use pre-built components instead of writing CSS from scratch
</li>
<li>
<strong>Handle Errors:</strong> Wrap API calls in try/catch blocks for better user experience
</li>
<li>
<strong>Think Reactive:</strong> Update the UI when data changes, don't just set it once
</li>
</ul>
</div>
</div>
<div class="alert alert-info mt-4">
<span>
💡 <strong>Advanced Patterns:</strong> You can use any frontend framework (React, Vue, Svelte) by
rendering it into the container, or build complex apps with routing, state management, and real-time
updates. The render function is just your starting point!
</span>
</div>
</div>
</div>
</div>
);
};

View file

@ -0,0 +1,60 @@
// @jsx h
// @jsxImportSource preact
import { CounterExample } from "../examples/CounterExample.jsx";
import { NostrPublisherExample } from "../examples/NostrPublisherExample.jsx";
import { PreactCounterExample } from "../examples/PreactCounterExample.jsx";
import { SvelteCounterExample } from "../examples/SvelteCounterExample.jsx";
const examples = {
vanilla: <CounterExample />,
preact: <PreactCounterExample />,
svelte: <SvelteCounterExample />,
nostr: <NostrPublisherExample />,
};
/**
* Examples Section - Practical Arxlet implementations
* Shows real-world examples with detailed explanations
*/
export const ExamplesSection = ({ activeExample }) => {
const ActiveComponent = examples[activeExample] || <div>Example not found</div>;
return (
<div class="space-y-6">
<h2 class="text-3xl font-bold">Example Arxlets</h2>
<div class="bg-info border-l-4 border-info/50 p-4 mb-6">
<div class="flex">
<div class="ml-3 text-info-content">
<p class="text-sm text-info-content">
<strong>Framework Freedom:</strong> These examples show basic implementations, but you're not limited to
vanilla JavaScript! You can use any framework you prefer - React with JSX, Preact, Vue, Svelte, or any
other modern framework. Bun's powerful plugin system supports transpilation and bundling for virtually any
JavaScript ecosystem.
</p>
<p class="text-sm t-2">
Check out{" "}
<a
href="https://bun.com/docs/runtime/plugins"
target="_blank"
rel="noopener noreferrer"
class="underline hover:text-blue-900"
>
Bun's plugin documentation
</a>{" "}
to see how you can integrate your preferred tools and frameworks.
</p>
<p class="text-sm mt-2">
If you have any questions or run into problems, feel free to reach out to the team @ Arx (builders of Eve)
on Nostr: <kbd class="kbd">npub1ven4zk8xxw873876gx8y9g9l9fazkye9qnwnglcptgvfwxmygscqsxddfhif</kbd>
</p>
</div>
</div>
</div>
{/* Tab Content */}
<div class="mt-6">{ActiveComponent}</div>
</div>
);
};

View file

@ -0,0 +1,15 @@
// @jsx h
// @jsxImportSource preact
import contextMd from "../highlight/context.md" with { type: "text" };
import { useSyntaxHighlighting } from "../hooks/useSyntaxHighlighting.js";
import { CodeBlock } from "./CodeBlock.jsx";
export const LLMsSection = () => {
useSyntaxHighlighting();
return (
<section id="llms" className="arxlet-docs-section">
<CodeBlock code={contextMd} />
</section>
);
};

View file

@ -0,0 +1,50 @@
// @jsx h
// @jsxImportSource preact
import typeDefinitions from "../highlight/type-definitions.ts" with { type: "text" };
import { useSyntaxHighlighting } from "../hooks/useSyntaxHighlighting.js";
import { CodeBlock } from "./CodeBlock.jsx";
/**
* Overview Section - Introduction to Arxlets
* Explains what Arxlets are and their key features
*/
export const OverviewSection = () => {
useSyntaxHighlighting();
return (
<div class="space-y-8">
<div class="card bg-base-200 shadow-xl">
<div class="card-body">
<h2 class="card-title text-2xl mb-4">What are Arxlets?</h2>
<p class="text-lg mb-4">
Arxlets are secure, sandboxed JavaScript applications that extend Eve's functionality. They run inside Eve
and are registered on your CCN for member-only access.
</p>
</div>
</div>
<div class="alert alert-info">
<span>
<strong>Coming Soon:</strong> WASM support will be added in future releases for even more powerful
applications.
</span>
</div>
<div class="card bg-base-200 shadow-xl">
<div class="card-body">
<h3 class="card-title text-secondary">📝 TypeScript Definitions</h3>
<p class="mb-4">Use these type definitions for full TypeScript support in your Arxlets:</p>
<CodeBlock language="typescript" code={typeDefinitions} />
<div class="alert alert-info mt-4">
<span>
<strong>Pro Tip:</strong> Save these types in a <code>types.ts</code> file and import them throughout your
Arxlet for better development experience and type safety.
</span>
</div>
</div>
</div>
</div>
);
};

View file

@ -0,0 +1,147 @@
// @jsx h
// @jsxImportSource preact
import registrationEvent from "../highlight/registration-event.json" with { type: "text" };
import { useSyntaxHighlighting } from "../hooks/useSyntaxHighlighting.js";
import { CodeBlock } from "./CodeBlock.jsx";
/**
* Registration Section - How to register Arxlets on CCN
* Covers the Nostr event structure and required fields
*/
export const RegistrationSection = () => {
useSyntaxHighlighting();
return (
<div class="space-y-6">
<h2 class="text-3xl font-bold">Arxlet Registration</h2>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h3 class="card-title text-info">What are Nostr Events?</h3>
<div class="space-y-3">
<p>
<strong>Nostr</strong> (Notes and Other Stuff Transmitted by Relays) is a decentralized protocol where all
data is stored as <strong>events</strong>. Think of events as structured messages that contain information
and are cryptographically signed by their authors.
</p>
<p>Each event has:</p>
<ul class="list-disc list-inside space-y-1 ml-4">
<li>
<strong>Kind:</strong> A number that defines what type of data the event contains (like a category)
</li>
<li>
<strong>Content:</strong> The main data or message
</li>
<li>
<strong>Tags:</strong> Additional metadata organized as key-value pairs
</li>
<li>
<strong>Signature:</strong> Cryptographic proof that the author created this event
</li>
</ul>
<p>
To register your Arxlet, you create a special event (kind 30420) that tells the CCN about your
application. This event acts like a "business card" for your Arxlet, containing its code, name, and other
details. If you publish this event publicly outside your CCN, this will be available in the arxlet store,
and any CCN will be able to install it.
</p>
</div>
</div>
</div>
<div class="card bg-base-200 shadow-xl">
<div class="card-body">
<h3 id="nostr-event-structure" class="card-title">
Nostr Event Structure
</h3>
<p class="mb-4">
Register your Arxlet using a replaceable Nostr event with kind{" "}
<code class="badge badge-primary">30420</code>:
</p>
<CodeBlock language="json" code={registrationEvent} />
</div>
</div>
<div id="tag-reference" class="card bg-base-200 shadow-xl">
<div class="card-body">
<h3 class="card-title">Tag Reference</h3>
<div class="overflow-x-auto">
<table class="table table-zebra w-full">
<thead>
<tr>
<th>Tag</th>
<th>Required</th>
<th>Description</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>d</code>
</td>
<td>
<span class="badge badge-error">Required</span>
</td>
<td>Unique identifier (alphanumeric, hyphens, underscores)</td>
<td>
<code>my-todo-app</code>
</td>
</tr>
<tr>
<td>
<code>name</code>
</td>
<td>
<span class="badge badge-error">Required</span>
</td>
<td>Human-readable display name</td>
<td>
<code>Todo Manager</code>
</td>
</tr>
<tr>
<td>
<code>description</code>
</td>
<td>
<span class="badge badge-warning">Optional</span>
</td>
<td>Brief description of functionality</td>
<td>
<code>Manage your tasks</code>
</td>
</tr>
<tr>
<td>
<code>script</code>
</td>
<td>
<span class="badge badge-error">Required</span>
</td>
<td>Complete JavaScript code with render export</td>
<td>
<code>export function render...</code>
</td>
</tr>
<tr>
<td>
<code>icon</code>
</td>
<td>
<span class="badge badge-warning">Optional</span>
</td>
<td>Iconify icon name and hex color</td>
<td>
<code>mdi:check-circle, #10b981</code>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
);
};