// Content Script - runs on X.com // Adds "Save to Hookd" option to tweet/post menu console.log('Hookd content script loaded'); // Listen for messages from background chrome.runtime.onMessage.addListener((msg, sender, response) => { if (msg.type === 'GET_SAVED') { response([]); } }); // Add "Save to Hookd" to X's menu function addHookdOption() { // Find all "more" buttons (the ... button on tweets/posts) document.querySelectorAll('[data-testid="tweet"] [role="button"], [data-testid="tweet"] [aria-label*="more"], [data-testid="tweet"] [aria-label*="More"]').forEach(menuBtn => { // Check if we already added our option if (menuBtn.closest('[data-testid="tweet"]')?.querySelector('.hookd-menu-option')) continue; const tweet = menuBtn.closest('[data-testid="tweet"]'); if (!tweet) return; // Create save button const saveBtn = document.createElement('div'); saveBtn.className = 'hookd-menu-option'; saveBtn.style.cssText = ` padding: 12px 16px; cursor: pointer; display: flex; align-items: center; gap: 12px; font-size: 15px; color: #fff; `; saveBtn.innerHTML = '📌Save to Hookd'; saveBtn.addEventListener('click', (e) => { e.stopPropagation(); saveTweet(tweet); }); // Find the dropdown menu and append const dropdown = menuBtn.closest('[role="menu"]') || menuBtn.closest('div[aria-labelledby]'); if (dropdown && !dropdown.querySelector('.hookd-menu-option')) { dropdown.appendChild(saveBtn); } }); // Also try to find DM more options document.querySelectorAll('[data-testid="DMMessage"] [role="button"]').forEach(btn => { if (btn.textContent.includes('more') || btn.getAttribute('aria-label')?.includes('more')) { const dm = btn.closest('[data-testid="DMMessage"]'); if (dm && !dm.querySelector('.hookd-menu-option')) { const saveBtn = document.createElement('div'); saveBtn.className = 'hookd-menu-option'; saveBtn.style.cssText = ` padding: 8px 12px; cursor: pointer; display: flex; align-items: center; gap: 8px; font-size: 13px; `; saveBtn.innerHTML = '📌 Save to Hookd'; saveBtn.addEventListener('click', (e) => { e.stopPropagation(); saveDM(dm); }); // Insert after the clickable area btn.parentElement?.appendChild(saveBtn); } } }); } // Save tweet function saveTweet(tweetElement) { try { // Extract author const authorEl = tweetElement.querySelector('[data-testid="User-Name"] span a[role="link"]') || tweetElement.querySelector('a[tabindex="-1"]'); const author = authorEl?.textContent?.trim()?.replace('@', '') || 'unknown'; // Extract content const contentEl = tweetElement.querySelector('[data-testid="tweetText"]'); const content = contentEl?.textContent?.trim() || ''; // Extract link const linkEl = tweetElement.querySelector('a[href*="/status/"]'); const link = linkEl?.href || ''; const item = { id: Date.now(), author: author, content: content.substring(0, 500), link: link, source: 'tweet', time: new Date().toISOString() }; // Save to storage via background chrome.runtime.sendMessage({ type: 'SAVE_ITEM', data: item }, (response) => { if (response?.success) { showNotification('Saved to Hookd!'); } else { showNotification('Failed to save'); } }); } catch (err) { console.error('Hookd save error:', err); showNotification('Error saving'); } } // Save DM function saveDM(dmElement) { try { const contentEl = dmElement.querySelector('[data-testid="dmMessageContent"]'); const content = contentEl?.textContent?.trim() || ''; const item = { id: Date.now(), author: 'DM', content: content.substring(0, 500), source: 'dm', time: new Date().toISOString() }; chrome.runtime.sendMessage({ type: 'SAVE_ITEM', data: item }, (response) => { if (response?.success) { showNotification('Saved to Hookd!'); } else { showNotification('Failed to save'); } }); } catch (err) { console.error('Hookd save error:', err); } } // Show notification function showNotification(message) { const notif = document.createElement('div'); notif.style.cssText = ` position: fixed; bottom: 20px; right: 20px; background: #4F46E5; color: white; padding: 12px 20px; border-radius: 8px; font-family: Arial, sans-serif; font-size: 14px; z-index: 99999; box-shadow: 0 4px 12px rgba(0,0,0,0.3); `; notif.textContent = message; document.body.appendChild(notif); setTimeout(() => { notif.style.opacity = '0'; notif.style.transition = 'opacity 0.3s'; setTimeout(() => notif.remove(), 300); }, 2000); } // Watch for new elements const observer = new MutationObserver(() => { addHookdOption(); }); // Start observing observer.observe(document.body, { childList: true, subtree: true }); // Initial scan setTimeout(addHookdOption, 1000); setTimeout(addHookdOption, 3000); console.log('Hookd content script initialized');