### GIF Buddy Project Overview Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/nostr.html Provides a high-level overview of the GIF Buddy project, its functionalities, and its powering technology. Includes links to search features and mentions its Nostr integration. ```Markdown GIF Buddy🫂 ========== GIF Collection: Search ![](https://image.nostr.build/9247e8f8a307a4bdd653d43d84810b8a5bf9073fd2cb6d6dc4c583230cce0043.gif) Start Over Powered by Nostr ![GitHub logo](/static/ostrich.svg)⚡ Searching... ``` -------------------------------- ### Confetti Celebration Effect Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/development/confetti.html This snippet includes CSS for styling the confetti and the animation, and JavaScript to dynamically create and animate confetti elements. It sets up an interval to create bursts of confetti and stops the effect after a set duration. ```css body { margin: 0; overflow: hidden; height: 100vh; background-color: #f0f0f0; } .confetti { position: absolute; width: 10px; height: 10px; background-color: #000; animation: fall linear infinite; } @keyframes fall { to { transform: translateY(100vh) rotate(360deg); } } ``` ```javascript let confettiRunning = true; // Flag to stop new confetti function createConfetti() { if (!confettiRunning) return; // Stop creating new confetti if flag is false const colors = [ '#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3', '#03a9f4', '#00bcd4', '#009688', '#4CAF50', '#8BC34A', '#FFEB3B' ]; const confetti = document.createElement('div'); confetti.classList.add('confetti'); // Randomize starting position at the top of the screen confetti.style.left = `${Math.random() * window.innerWidth}px`; // Randomize color confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; // Randomize size const width = Math.random() * 12 + 5; const height = width * 2; confetti.style.width = `${width}px`; confetti.style.height = `${height}px`; // Randomize fall duration and delay const duration = Math.random() * 3 + 2; const delay = Math.random() * 1; confetti.style.animationDuration = `${duration}s`; confetti.style.animationDelay = `${delay}s`; // Randomize rotation confetti.style.transform = `rotate(${Math.random() * 360}deg)`; document.body.appendChild(confetti); // Remove confetti after animation confetti.addEventListener('animationend', () => { confetti.remove(); }); } function startConfetti() { if (!confettiRunning) return; // Ensure no bursts after flag changes // Create multiple confetti pieces for (let i = 0; i < 100; i++) { createConfetti(); } } window.addEventListener('load', () => { const interval = setInterval(startConfetti, 1000); // Trigger confetti bursts every second // Stop confetti generation after 5 seconds setTimeout(() => { clearInterval(interval); confettiRunning = false; // Block all further individual confetti }, 5000); }); ``` -------------------------------- ### Project Information Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/development/original.html Displays project title, author, public key, and lightning address. ```text Project: /happylemonprogramming/gifbuddy GIF Buddy ========= Search ⚡ × Developed by Lemon🍋 -------------------- Public key: npub1hee433872q2gen90cqh2ypwcq9z7y5ugn23etrd2l2rrwpruss8qwmrsv6 📋 Lightning address: palekangaroo1@primal.net 📋 ``` -------------------------------- ### Login with Public Key Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/collection.html This section describes the process of logging into the application using a public key. It includes a form submission for the public key. ```HTML
Login with Public Key
``` -------------------------------- ### Project Links and Assets Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/development/creation.html Contains links to external resources and references to project assets like logos and images. ```HTML GitHub logo ostrich ``` -------------------------------- ### Gif Buddy Development Environment Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/README.md Gif Buddy is hosted on Heroku with auto-deployments from GitHub. A 'dev.html' file in the 'templates' folder allows for safe experimentation without affecting the live application. The development build is accessible via a specific URL. ```APIDOC Hosting: - Platform: Heroku - Deployment: Auto-deploys on GitHub push. Development Setup: - File for experimentation: 'templates/dev.html' - Purpose: Allows testing without breaking the live app. - Access URL: https://gifbuddy.lol/dev ``` -------------------------------- ### GIF Creation Payment Options Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/development/create.html Details the costs associated with creating GIFs, offering payment options in both Bitcoin and US Dollars. Bitcoin payments are discounted. ```text Create GIFs 🪄 ============== Create $0.50 in Bitcoin ================ Tap to Copy (Bitcoin Payments 25% Off) $0.69 in Dollars ================ Creating GIF... ``` -------------------------------- ### Project Structure and Assets Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/development/meme.html This snippet outlines the project's file structure and highlights the use of an image asset for visual representation. It indicates the presence of static files like images. ```Project Structure Project: /happylemonprogramming/gifbuddy Content: GIF Buddy🫂 Upload Search Meme Meme GIFs 🐸 ============ Meme Generating... ![GitHub logo](/static/ostrich.svg)⚡ ``` -------------------------------- ### UI Event Listeners and Initialization Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/development/cashu.html Sets up event listeners for UI elements like send, receive, and refresh buttons. It also includes an initialization function that checks for the Nostr extension, loads wallet state, and publishes the initial wallet event. ```javascript // Event Listeners document.getElementById('sendButton').addEventListener('click', async () => { if (!wallet.pubkey) { showNotification('Please connect your Nostr extension first'); return; } // Implement send logic alert('Send functionality not implemented'); }); document.getElementById('receiveButton').addEventListener('click', async () => { if (!wallet.pubkey) { showNotification('Please connect your Nostr extension first'); return; } // Implement receive logic alert('Receive functionality not implemented'); }); document.getElementById('refreshButton').addEventListener('click', async () => { if (!wallet.pubkey) { showNotification('Please connect your Nostr extension first'); return; } await loadWalletState(); }); // Initialize the wallet async function initialize() { const hasNostr = await checkNostrExtension(); if (hasNostr) { await loadWalletState(); await publishWalletEvent(); } } // Start the initialization when the page loads initialize(); ``` -------------------------------- ### HTML Structure for GIF Buddy Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/favorites.html This snippet shows the basic HTML structure for the GIF Buddy application, including navigation, search functionalities, and user interaction elements like login and favorites. ```html GIF Buddy

GIF Buddy🫂

Shortcode:

GIF Favorites ❤️

Favorites Image

Create a Collection to See Favorites

Go to Collections
Meme GIF
GitHub logo ⚡ Login

Login with Public Key

Loading...
``` -------------------------------- ### GIF Buddy Features Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/gifsearch.html Overview of the core functionalities offered by GIF Buddy, including searching and creation capabilities. ```markdown GIF Buddy🫂 Search GIFs Search Nostr Search Memes Create Meme Collections Decode Upload GIF Buddy 🤙 ============ GIFs Set Free: Search ![](https://image.nostr.build/96d60ede21622499894bb5a20a3d8a6ebd2e3438f71395332e0fb3190a7ce78d.gif) Load More Start Over Powered by Tenor Meme GIF ![GitHub logo](/static/ostrich.svg)⚡ ``` -------------------------------- ### User Authentication and Navigation Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/collection.html Includes elements for user login, logout, and accessing personal collections. It also displays a loading indicator. ```HTML ![GitHub logo](/static/ostrich.svg)⚡ Login [My Collections](/favorites) Log Out Loading... ``` -------------------------------- ### User Interface Elements Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/development/dev.html This snippet represents the HTML structure for the GIF Buddy application's user interface, including elements for uploading, searching, and displaying content. ```HTML GIF Buddy

GIF Buddy🫂

Upload Search Meme

Decode Image 🤫

GitHub logo

Generating...

``` -------------------------------- ### GIF Search App Styling Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/development/original.html Provides the CSS styling for the GIF Search App, including layout, colors, and responsive design elements for search boxes, results, and popups. ```css body { font-family: Arial, sans-serif; background-color: #2C2F33; color: #FFFFFF; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; padding: 20px; box-sizing: border-box; overflow-y: auto; } .container { width: 100%; max-width: 600px; margin: 0 auto; } h1 { text-align: center; color: #FFFFFF; } .search-box { display: flex; margin-bottom: 20px; } #search-input { flex-grow: 1; padding: 10px; font-size: 16px; border: none; background-color: #23272A; color: #FFFFFF; border-radius: 4px 0 0 4px; } #search-button { padding: 10px 20px; font-size: 16px; background-color: #5E35B1; color: #FFFFFF; border: none; cursor: pointer; border-radius: 0 4px 4px 0; } #search-button:hover { background-color: #4527A0; } #results { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; } .gif { width: 100%; height: auto; border-radius: 4px; } #counter { text-align: center; font-size: 18px; margin-bottom: 20px; color: #FFFFFF; } .zap-button { position: fixed; top: 20px; right: 20px; font-size: 24px; background: none; border: none; cursor: pointer; } .popup { display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: #2C2F33; padding: 20px; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.5); z-index: 1000; width: 90%; max-width: 400px; max-height: 90vh; overflow-y: auto; } .popup-content { color: white; text-align: center; position: relative; padding-top: 20px; } .popup-close { position: absolute; top: 10px; right: 10px; font-size: 24px; color: white; cursor: pointer; z-index: 1001; } .popup-text-copy { display: flex; justify-content: space-between; align-items: center; background-color: #23272A; padding: 5px 10px; border-radius: 5px; margin: 5px 0; word-break: break-all; } .copy-icon { cursor: pointer; flex-shrink: 0; margin-left: 10px; } ``` -------------------------------- ### Python Backend Snippet (Conceptual) Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/favorites.html This conceptual Python snippet illustrates potential backend logic for handling user authentication and data retrieval, possibly using a framework like Flask or Django. It's a placeholder for server-side operations. ```python from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/login', methods=['POST']) def login(): public_key = request.json.get('publicKey') if public_key: # Logic to authenticate user using public key # For example, check against a database or Nostr identity return jsonify({'status': 'success', 'message': 'Login successful'}), 200 else: return jsonify({'status': 'error', 'message': 'Public key is required'}), 400 @app.route('/favorites', methods=['GET']) def get_favorites(): # Logic to retrieve user's favorite GIFs # Requires user authentication context favorites = [] # Placeholder for favorite GIFs return jsonify({'favorites': favorites}), 200 if __name__ == '__main__': app.run(debug=True) ``` -------------------------------- ### Button and Preview Styling Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/imageToPDF.html Defines the styles for the upload and convert buttons, as well as the image preview area. Buttons have distinct colors and hover effects. The preview area is styled to display images with maximum width and height constraints, and includes styling for grayscale previews and file information. ```CSS #upload-btn { background-color: var(--primary-color); color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; font-size: 16px; transition: background-color 0.3s ease; } #upload-btn:hover { background-color: var(--primary-hover); } #convert-btn { background-color: var(--success-color); color: white; border: none; padding: 12px 24px; border-radius: 5px; cursor: pointer; font-size: 16px; margin-top: 20px; width: 100%; transition: background-color 0.3s ease; display: none; } #convert-btn:hover { background-color: var(--success-hover); } #convert-btn:disabled { background-color: #505050; cursor: not-allowed; } #preview-container { margin-top: 20px; text-align: center; display: none; } #image-preview { max-width: 100%; max-height: 400px; border-radius: 5px; border: 1px solid #444; } #grayscale-preview { max-width: 100%; max-height: 400px; border-radius: 5px; border: 1px solid #444; display: none; } .file-info { margin-top: 10px; font-size: 14px; color: var(--text-secondary); } ``` -------------------------------- ### JavaScript for Frontend Interactions Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/favorites.html This JavaScript snippet demonstrates frontend logic for handling user interactions, such as submitting login forms or managing UI states like loading indicators. It would typically be used with a frontend framework or vanilla JS. ```javascript document.addEventListener('DOMContentLoaded', () => { const loginForm = document.querySelector('.login form'); const loadingIndicator = document.querySelector('.loading'); if (loginForm) { loginForm.addEventListener('submit', async (event) => { event.preventDefault(); loadingIndicator.style.display = 'block'; // Show loading const publicKeyInput = document.querySelector('.login input[type="text"]'); const publicKey = publicKeyInput.value; try { const response = await fetch('/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ publicKey: publicKey }) }); const data = await response.json(); if (data.status === 'success') { alert('Login successful!'); // Redirect or update UI } else { alert('Login failed: ' + data.message); } } catch (error) { console.error('Error during login:', error); alert('An error occurred during login.'); } finally { loadingIndicator.style.display = 'none'; // Hide loading } }); } }); ``` -------------------------------- ### Core Functionality Navigation Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/collection.html Provides navigation links for the main features of the GIF Buddy application, such as searching GIFs, Nostr, memes, and collections. ```HTML Search GIFs Search Nostr Search Memes Create Meme Collections Decode Upload ``` -------------------------------- ### Gif Buddy Backend Functionality Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/README.md The backend of Gif Buddy handles GIF uploads to nostr.build and broadcasts NIP94 events upon each copy/click action. This process includes uploading the GIF and its metadata (SHA256 hash, URL, fallback URL) to nostr.build, enabling future client integration of GIFs. ```APIDOC Tenor API Integration: - Functionality: Search engine for GIFs. - Input: Search query. - Output: List of GIFs. nostr.build Upload: - Functionality: Uploads GIFs and metadata to nostr.build. - Trigger: User copies or clicks a GIF. - Input: GIF file, metadata (SHA256 hash, URL, fallback URL). - Output: Upload confirmation, broadcasted NIP94 event. NIP94 Event Broadcasting: - Functionality: Broadcasts a NIP94 event containing GIF metadata. - Purpose: Allows clients to natively integrate GIFs. - Content: SHA256 hash, URL, fallback URL. Progressive Web App (PWA): - Functionality: Web application installable on home screens. - Access: https://gifbuddy.lol - Benefit: Direct download and home screen access for users. ``` -------------------------------- ### Badge, Toggle Switch, and Preview Tabs Styling Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/imageToPDF.html Styles various UI components including format badges, the main toggle switch, and preview tab elements. Badges indicate file formats, the toggle switch provides a visual way to switch themes or modes, and preview tabs allow users to switch between different views. ```CSS .format-badge { display: inline-block; padding: 2px 6px; background-color: rgba(79, 110, 242, 0.2); color: var(--primary-color); border-radius: 4px; font-size: 12px; margin-right: 4px; margin-bottom: 4px; } .toggle-container { display: flex; align-items: center; margin-top: 20px; padding: 10px; background-color: var(--upload-bg); border-radius: 5px; } .toggle-label { margin-right: 10px; flex-grow: 1; } .toggle-switch { position: relative; display: inline-block; width: 50px; height: 24px; } .toggle-switch input { opacity: 0; width: 0; height: 0; } .toggle-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: var(--toggle-bg); transition: .4s; border-radius: 24px; } .toggle-slider:before { position: absolute; content: ""; height: 16px; width: 16px; left: 4px; bottom: 4px; background-color: var(--toggle-knob); transition: .4s; border-radius: 50%; } input:checked + .toggle-slider { background-color: var(--toggle-active-bg); } input:checked + .toggle-slider:before { transform: translateX(26px); background-color: var(--toggle-active-knob); } .help-text { margin-top: 5px; font-size: 12px; color: var(--text-secondary); } .preview-tabs { display: flex; margin-bottom: 10px; } .preview-tab { padding: 8px 16px; background-color: var(--upload-bg); border: none; border-radius: 5px 5px 0 0; color: var(--text-secondary); cursor: pointer; margin-right: 2px; } .preview-tab.active { background-color: var(--primary-color); color: white; } ``` -------------------------------- ### GIF Search App Structure Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/development/original.html Defines the HTML structure for the GIF Search App, including elements for search input, search button, GIF results display, and a counter. ```html GIF Search App body { font-family: Arial, sans-serif; background-color: #2C2F33; color: #FFFFFF; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; padding: 20px; box-sizing: border-box; overflow-y: auto; } .container { width: 100%; max-width: 600px; margin: 0 auto; } h1 { text-align: center; color: #FFFFFF; } .search-box { display: flex; margin-bottom: 20px; } #search-input { flex-grow: 1; padding: 10px; font-size: 16px; border: none; background-color: #23272A; color: #FFFFFF; border-radius: 4px 0 0 4px; } #search-button { padding: 10px 20px; font-size: 16px; background-color: #5E35B1; color: #FFFFFF; border: none; cursor: pointer; border-radius: 0 4px 4px 0; } #search-button:hover { background-color: #4527A0; } #results { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; } .gif { width: 100%; height: auto; border-radius: 4px; } #counter { text-align: center; font-size: 18px; margin-bottom: 20px; color: #FFFFFF; } .zap-button { position: fixed; top: 20px; right: 20px; font-size: 24px; background: none; border: none; cursor: pointer; } .popup { display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: #2C2F33; padding: 20px; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.5); z-index: 1000; width: 90%; max-width: 400px; max-height: 90vh; overflow-y: auto; } .popup-content { color: white; text-align: center; position: relative; padding-top: 20px; } .popup-close { position: absolute; top: 10px; right: 10px; font-size: 24px; color: white; cursor: pointer; z-index: 1001; } .popup-text-copy { display: flex; justify-content: space-between; align-items: center; background-color: #23272A; padding: 5px 10px; border-radius: 5px; margin: 5px 0; word-break: break-all; } .copy-icon { cursor: pointer; flex-shrink: 0; margin-left: 10px; } ``` -------------------------------- ### Nostr NIP-94 Information Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/upload.html Provides a link to information about NIP-94, a protocol related to Nostr, which is likely relevant to the project's Nostr search functionality. ```markdown [What is NIP-94?](https://github.com/nostr-protocol/nips/blob/master/94.md) ``` -------------------------------- ### Create Grayscale Image with Contrast Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/imageToPDF.html This function creates a grayscale version of an image using a canvas element. It applies a luminosity-based grayscale conversion and then adjusts contrast by centering the values around zero, scaling, and re-centering. A threshold is applied to make the image appear more document-like, setting values above 180 to white and below 50 to black. ```javascript function createGrayscaleImage(img) { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.width; canvas.height = img.height; // Draw original image ctx.drawImage(img, 0, 0); // Get image data const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; // Apply grayscale conversion with increased contrast for (let i = 0; i < data.length; i += 4) { // Convert to grayscale using luminosity method const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; let gray = 0.299 * r + 0.587 * g + 0.114 * b; // Increase contrast gray = gray - 128; gray = gray * 1.5; gray = gray + 128; // Threshold for more document-like appearance if (gray > 180) { gray = 255; // White } else if (gray < 50) { gray = 0; // Black } // Set RGB values to grayscale data[i] = gray; data[i + 1] = gray; data[i + 2] = gray; } ctx.putImageData(imageData, 0, 0); // Store grayscale image data grayscaleImageData = { canvas: canvas, imageData: imageData, width: canvas.width, height: canvas.height }; } ``` -------------------------------- ### Upload Media Functionality Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/upload.html This section details the media upload feature, specifying supported file types (GIF, MP4, PNG, JPG) and the maximum file size limit (21MB). It includes a drag-and-drop interface or a click-to-browse option for uploading. ```markdown Upload Media 📤 ============== Drop your file here, or click to browse Supported file types: GIF, MP4, PNG, JPG Maximum file size: 21MB × Upload ``` -------------------------------- ### CSS Variables and Body Styling Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/imageToPDF.html Defines the color scheme and basic layout for the application using CSS variables. It sets up the font, maximum width, margins, padding, and background color for the body, ensuring a consistent and responsive design. ```CSS :root { --bg-color: #121212; --container-bg: #1e1e1e; --text-color: #e0e0e0; --text-secondary: #aaaaaa; --primary-color: #4f6ef2; --primary-hover: #3b5bd4; --success-color: #2ecc71; --success-hover: #27ae60; --success-bg: #1e3d2f; --error-bg: #3d1e1e; --error-color: #e74c3c; --upload-bg: #252525; --upload-border: #4f6ef2; --upload-hover-bg: #2a2a2a; --card-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); --toggle-bg: #444; --toggle-knob: #888; --toggle-active-bg: #3b5bd4; --toggle-active-knob: #fff; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; background-color: var(--bg-color); color: var(--text-color); transition: all 0.3s ease; } ``` -------------------------------- ### GIF Search Functionality Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/development/original.html Handles GIF searching, displaying results, and copying GIF URLs. It uses Axios for API requests and includes functionality to send GIF metadata and update a counter. ```javascript const searchInput = document.getElementById('search-input'); const searchButton = document.getElementById('search-button'); const resultsDiv = document.getElementById('results'); const counterValue = document.getElementById('counter-value'); async function fetchCounter() { try { const response = await axios.get('/counter'); counterValue.textContent = response.data.count; } catch (error) { console.error('Error fetching counter:', error); counterValue.textContent = 'Error'; } } async function searchGifs() { const searchTerm = searchInput.value.trim(); if (!searchTerm) return; try { const response = await axios.post('/search', { q: searchTerm }); resultsDiv.innerHTML = ''; Object.entries(response.data).forEach(([alt, gifData]) => { const { gifUrl, gifSize, gifDims, thumb, preview } = gifData; const img = document.createElement('img'); img.src = gifUrl; img.alt = alt; img.className = 'gif'; img.addEventListener('click', () => { copyToClipboard(gifUrl); sendGifMetadata({ gifUrl, gifSize, gifDims, thumb, preview, alt, searchTerm }); }); resultsDiv.appendChild(img); }); } catch (error) { console.error('Error fetching GIFs:', error); resultsDiv.innerHTML = 'An error occurred while fetching GIFs.'; } } async function sendGifMetadata(gifData) { try { await axios.post('/gifmetadata', gifData); console.log('GIF metadata sent successfully.'); } catch (error) { console.error('Error sending GIF metadata:', error); } } function copyToClipboard(text) { const tempInput = document.createElement('input'); tempInput.value = text; document.body.appendChild(tempInput); tempInput.select(); document.execCommand('copy'); document.body.removeChild(tempInput); alert('Copied to clipboard!'); } searchButton.addEventListener('click', searchGifs); searchInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { searchGifs(); } }); const zapButton = document.getElementById('zapButton'); const popup = document.getElementById('popup'); const popupClose = document.getElementById('popupClose'); zapButton.addEventListener('click', () => { popup.styl ``` -------------------------------- ### GIF Collection Management Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/collection.html This section details the functionality for managing GIF collections, including publishing, adding GIFs, and loading more content. ```HTML GIF Collection 💾 ================= Publish + GIFs ![](https://image.nostr.build/584d8827a5280b32d1f2168b69069a8c863f1cc91c04bc5f1058e0294a14210e.jpg) Load More Start Over ``` -------------------------------- ### Copy to Clipboard Functionality Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/development/original.html Implements a 'copy to clipboard' feature for elements within the popup. When a copy icon is clicked, it retrieves the text content from the preceding sibling element, copies it to the clipboard using a `copyToClipboard` function, and provides visual feedback to the user by changing the icon temporarily. ```javascript const copyIcons = document.querySelectorAll('.copy-icon'); copyIcons.forEach(icon => { icon.addEventListener('click', () => { const textElement = icon.previousElementSibling; const textToCopy = textElement.textContent.trim(); copyToClipboard(textToCopy); icon.textContent = '✅'; setTimeout(() => { icon.textContent = '📋'; }, 2000); }); }); ``` -------------------------------- ### GIF Search Response Structure Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/api.html Describes the successful (200 OK) response from the GIF search API, including the JSON structure for GIF data and pagination. ```APIDOC Status Code: 200 OK Response Body (JSON): { "funny_cat": { "gifUrl": "https://example.com/gif.gif", "gifSize": "204800", "gifDims": [300, 200], "thumb": "https://example.com/thumbnail.gif", "preview": "https://example.com/preview.gif", "alt": "A funny cat", "image": "https://example.com/image.jpg", "summary": "funny cat GIF" }, "next": "abcd1234" } ``` -------------------------------- ### Theme Toggle and Loader Animations Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/imageToPDF.html Styles the theme toggle switch and a loading indicator. The toggle switch uses a slider and knob design with active states. The loader is a spinning circle animation, indicating processing. ```CSS .theme-toggle { position: absolute; top: 20px; right: 20px; background-color: transparent; border: 1px solid var(--text-secondary); color: var(--text-color); padding: 5px 10px; border-radius: 5px; cursor: pointer; font-size: 14px; } .loader { display: none; border: 3px solid #f3f3f3; border-top: 3px solid var(--primary-color); border-radius: 50%; width: 20px; height: 20px; animation: spin 2s linear infinite; margin: 0 auto; margin-top: 10px; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } ``` -------------------------------- ### HTML Structure for Image Processing Tool Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/imageToPDF.html Defines the HTML elements used for the client-side image processing tool, including areas for file dropping, input buttons, image previews, and user feedback. ```html
``` -------------------------------- ### ProofManager Class Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/development/cashu.html Manages unspent and spent proofs for a Cashu wallet. It provides methods to add new proofs, spend existing proofs, calculate the total balance from unspent proofs, and retrieve lists of unspent and spent proofs. ```javascript class ProofManager { constructor() { this.unspentProofs = new Map(); this.spentProofs = new Set(); } addProof(proof) { if (!this.spentProofs.has(proof.id)) { this.unspentProofs.set(proof.id, proof); } } spendProof(proofId) { if (this.unspentProofs.has(proofId)) { const proof = this.unspentProofs.get(proofId); this.unspentProofs.delete(proofId); this.spentProofs.add(proofId); return proof; } return null; } calculateBalance() { let total = 0; for (const proof of this.unspentProofs.values()) { total += proof.amount; } return total; } getUnspentProofs() { return Array.from(this.unspentProofs.values()); } getSpentProofIds() { return Array.from(this.spentProofs); } } ``` -------------------------------- ### Load Wallet State from Nostr Events Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/development/cashu.html Loads the wallet state by fetching token events (kind 7375) and spending history events (kind 7376). It processes decrypted event content to add proofs to the ProofManager and mark proofs as spent based on spending history. ```javascript async function loadWalletState() { try { // 1. Fetch all kind:7375 token events const tokenEvents = await fetchTokenEvents(); if (tokenEvents.length === 0) { console.log('No token events found'); wallet.balance = 0; document.getElementById('balance').textContent = '0'; showNotification('No tokens found'); return; } // 2. Process each token event for (const event of tokenEvents) { try { // Decrypt the token event content const decryptedContent = await decryptEventContent(event.content); const proofs = JSON.parse(decryptedContent).proofs; // Add each proof to the proof manager for (const proof of proofs) { wallet.proofManager.addProof(proof); } } catch (error) { console.error('Error processing token event:', error); } } // 3. Fetch all kind:7376 spending history events const spendingEvents = await fetchSpendingHistoryEvents(); // 4. Process spending history to mark proofs as spent for (const event of spendingEvents) { try { const decryptedContent = await decryptEventContent(event.content); const content = JSON.parse(decryptedContent); if (content.direction === 'out') { // Mark proof as spent // ... implementation details for marking proof as spent ... } } catch (error) { console.error('Error processing spending event:', error); } } // Update UI with balance // ... update wallet.balance and UI elements ... } catch (error) { console.error('Error loading wallet state:', error); throw error; } } ``` -------------------------------- ### GIF Search API Endpoint Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/api.html Details the POST request for searching GIFs, including the endpoint, required headers, and the JSON request body. ```APIDOC Endpoint: /api/search_gifs Method: POST Headers: Content-Type: application/json API-Key: YOUR_API_KEY_HERE Body (JSON): { "q": "", // Required. The search term to find GIFs. "pos": "" // Optional. A token to fetch the next set of results. } ``` -------------------------------- ### GIF Creation Status Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/development/creation.html Displays the current status of GIF creation. This snippet indicates that a GIF is currently being created. ```HTML Creating GIF... ``` -------------------------------- ### Image Generation and Value Display Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/memeedit.html Features for generating images and displaying a specific value, such as '200 sats'. Includes a 'Generate' button and a 'Click to Copy' functionality. ```HTML #### Generate Image Create⚡ #### 200 sats Click to Copy! Generating... ``` -------------------------------- ### Cashu Wallet UI Structure Source: https://github.com/happylemonprogramming/gifbuddy/blob/main/templates/development/cashu.html Defines the basic HTML and CSS structure for the Cashu Wallet interface, including styling for the main container, wallet card, balance display, buttons, and transaction list. ```html Cashu Wallet body { font-family: system-ui, -apple-system, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; background: #f5f5f5; } .wallet-card { background: white; border-radius: 12px; padding: 20px; margin: 20px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .balance { font-size: 24px; font-weight: bold; color: #333; } .button { background: #0066cc; color: white; border: none; padding: 10px 20px; border-radius: 6px; cursor: pointer; margin: 5px; } .button:hover { background: #0052a3; } .transaction-list { margin-top: 20px; } .transaction { padding: 10px; border-bottom: 1px solid #eee; } .notification { position: fixed; top: 20px; right: 20px; padding: 10px 20px; background: #4CAF50; color: white; border-radius: 4px; display: none; } ```