### 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  Start Over Powered by Nostr ⚡ 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
``` -------------------------------- ### 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
Create a Collection to See Favorites
Go to CollectionsGenerating...
``` -------------------------------- ### 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  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