SiteMente - AI-Powered Lead Generation Platform
Features: - Mission Control dashboard - HP Submissions tracking - AI Agents integration - Lead management CRM - Marketing email templates - Chrome extension support Tech: Next.js, TypeScript, Tailwind CSS, MySQL
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,78 @@
|
||||
# Hookd - Chrome Extension
|
||||
|
||||
Save, organize and AI-sort your X (Twitter) content.
|
||||
|
||||
## Features
|
||||
|
||||
- 📥 **DM Organizer** - Extract and categorize DMs
|
||||
- 🐦 **Feed Saver** - Checkmark posts to save in bulk
|
||||
- 🏷️ **Custom Categories** - AI, News, Ideas, Memes, Other
|
||||
- 🤖 **AI Processing Ready** - Connect to OpenClaw for AI analysis
|
||||
- 👥 **Contact Lists** - Quick share to friends/family lists
|
||||
- 💾 **Export** - Save your data anytime
|
||||
|
||||
## Installation
|
||||
|
||||
1. Open Chrome and go to `chrome://extensions/`
|
||||
2. Enable "Developer mode" (toggle in top right)
|
||||
3. Click "Load unpacked"
|
||||
4. Select the `hookd` folder
|
||||
5. Click the extension icon and pin it to toolbar
|
||||
|
||||
## OpenClaw Integration
|
||||
|
||||
To enable AI processing:
|
||||
|
||||
1. Install OpenClaw Browser Relay extension
|
||||
2. Configure connection in extension settings (⚙️ tab)
|
||||
3. AI will automatically:
|
||||
- Read saved content
|
||||
- Suggest categories
|
||||
- Score by relevance
|
||||
- Delete spam
|
||||
|
||||
## Categories
|
||||
|
||||
- 💡 **AI** - AI tools, news, developments
|
||||
- 📰 **News** - Breaking news, trends
|
||||
- 💡 **Ideas** - Inspiration, ideas, concepts
|
||||
- 😂 **Memes** - Fun content
|
||||
- 📁 **Other** - Uncategorized
|
||||
|
||||
## Usage
|
||||
|
||||
### Saving DMs
|
||||
1. Open X DMs
|
||||
2. Hover over a message
|
||||
3. Click 📌 to save
|
||||
|
||||
### Saving Feed Posts
|
||||
1. Open X home/timeline
|
||||
2. Hover over any tweet
|
||||
3. Click the ○ checkbox on left
|
||||
4. Select multiple tweets
|
||||
5. Click "Save to Hookd" in floating bar
|
||||
|
||||
### Viewing Saved
|
||||
1. Click Hookd extension icon
|
||||
2. Go to 💾 Saved tab
|
||||
|
||||
### AI Processing
|
||||
1. Go to 🤖 AI tab
|
||||
2. Click "Process All with AI"
|
||||
3. AI will analyze and categorize
|
||||
|
||||
## Files
|
||||
|
||||
- `manifest.json` - Extension config
|
||||
- `popup.html/js` - Popup UI
|
||||
- `background.js` - Service worker
|
||||
- `content.js/css` - X.com page interaction
|
||||
|
||||
## TODO
|
||||
|
||||
- [ ] Connect to OpenClaw Browser Relay
|
||||
- [ ] Add X API integration (alternative to scraping)
|
||||
- [ ] FB/IG support
|
||||
- [ ] Cloud sync
|
||||
- [ ] Mobile companion
|
||||
@@ -0,0 +1,159 @@
|
||||
// Service Worker - Hookd Extension
|
||||
|
||||
const STORAGE_KEY = 'hookd_items';
|
||||
const SETTINGS_KEY = 'hookd_settings';
|
||||
const LISTS_KEY = 'hookd_lists';
|
||||
|
||||
const defaultSettings = {
|
||||
categories: ['AI', 'News', 'Ideas', 'Memes', 'Other'],
|
||||
userHandle: 'HaithamEKhalifa',
|
||||
relayEnabled: true
|
||||
};
|
||||
|
||||
const defaultLists = [
|
||||
{ id: 'dev', name: 'Dev Friends', emoji: '👨💻', contacts: [] },
|
||||
{ id: 'memes', name: 'Meme Buddies', emoji: '😂', contacts: [] },
|
||||
{ id: 'business', name: 'Business', emoji: '💼', contacts: [] },
|
||||
{ id: 'family', name: 'Family', emoji: '👨👩👧', contacts: [] }
|
||||
];
|
||||
|
||||
// Initialize
|
||||
chrome.runtime.onInstalled.addListener(() => {
|
||||
console.log('Hookd installed');
|
||||
chrome.storage.local.set({
|
||||
[STORAGE_KEY]: [],
|
||||
[SETTINGS_KEY]: defaultSettings,
|
||||
[LISTS_KEY]: defaultLists
|
||||
});
|
||||
});
|
||||
|
||||
// Handle messages
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
// Get items
|
||||
if (message.type === 'GET_ITEMS') {
|
||||
chrome.storage.local.get([STORAGE_KEY], (r) => sendResponse(r[STORAGE_KEY] || []));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get lists
|
||||
if (message.type === 'GET_LISTS') {
|
||||
chrome.storage.local.get([LISTS_KEY], (r) => sendResponse(r[LISTS_KEY] || defaultLists));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add contact to list
|
||||
if (message.type === 'ADD_CONTACT') {
|
||||
chrome.storage.local.get([LISTS_KEY], (r) => {
|
||||
const lists = r[LISTS_KEY] || defaultLists;
|
||||
const list = lists.find(l => l.id === message.listId);
|
||||
if (list && !list.contacts.find(c => c.username === message.contact.username)) {
|
||||
list.contacts.push(message.contact);
|
||||
chrome.storage.local.set({ [LISTS_KEY]: lists });
|
||||
}
|
||||
sendResponse({ success: true });
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove contact from list
|
||||
if (message.type === 'REMOVE_CONTACT') {
|
||||
chrome.storage.local.get([LISTS_KEY], (r) => {
|
||||
const lists = r[LISTS_KEY] || defaultLists;
|
||||
const list = lists.find(l => l.id === message.listId);
|
||||
if (list) {
|
||||
list.contacts = list.contacts.filter(c => c.username !== message.username);
|
||||
chrome.storage.local.set({ [LISTS_KEY]: lists });
|
||||
}
|
||||
sendResponse({ success: true });
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create new list
|
||||
if (message.type === 'CREATE_LIST') {
|
||||
chrome.storage.local.get([LISTS_KEY], (r) => {
|
||||
const lists = r[LISTS_KEY] || defaultLists;
|
||||
lists.push({
|
||||
id: Date.now().toString(),
|
||||
name: message.name,
|
||||
emoji: message.emoji || '📌',
|
||||
contacts: []
|
||||
});
|
||||
chrome.storage.local.set({ [LISTS_KEY]: lists });
|
||||
sendResponse({ success: true });
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// Delete list
|
||||
if (message.type === 'DELETE_LIST') {
|
||||
chrome.storage.local.get([LISTS_KEY], (r) => {
|
||||
let lists = r[LISTS_KEY] || defaultLists;
|
||||
lists = lists.filter(l => l.id !== message.listId);
|
||||
chrome.storage.local.set({ [LISTS_KEY]: lists });
|
||||
sendResponse({ success: true });
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// Share item to list
|
||||
if (message.type === 'SHARE_TO_LIST') {
|
||||
chrome.storage.local.get([STORAGE_KEY], (r) => {
|
||||
const items = r[STORAGE_KEY] || [];
|
||||
// Mark item as shared
|
||||
const item = items.find(i => i.id === message.itemId);
|
||||
if (item) {
|
||||
item.sharedTo = item.sharedTo || [];
|
||||
item.sharedTo.push(message.listId);
|
||||
chrome.storage.local.set({ [STORAGE_KEY]: items });
|
||||
}
|
||||
sendResponse({ success: true });
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// Save item
|
||||
if (message.type === 'SAVE_ITEM') {
|
||||
chrome.storage.local.get([STORAGE_KEY], (r) => {
|
||||
const items = r[STORAGE_KEY] || [];
|
||||
const newItem = {
|
||||
id: Date.now(),
|
||||
...message.data,
|
||||
type: message.data.source || 'dm',
|
||||
saved: false,
|
||||
aiProcessed: false,
|
||||
sharedTo: [],
|
||||
time: new Date().toISOString()
|
||||
};
|
||||
items.unshift(newItem);
|
||||
chrome.storage.local.set({ [STORAGE_KEY]: items });
|
||||
sendResponse({ success: true, item: newItem });
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// Delete items
|
||||
if (message.type === 'DELETE_ITEMS') {
|
||||
chrome.storage.local.get([STORAGE_KEY], (r) => {
|
||||
const items = r[STORAGE_KEY] || [];
|
||||
const remaining = items.filter(i => !message.ids.includes(i.id));
|
||||
chrome.storage.local.set({ [STORAGE_KEY]: remaining });
|
||||
sendResponse({ success: true, deleted: message.ids.length });
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// Update item
|
||||
if (message.type === 'UPDATE_ITEM') {
|
||||
chrome.storage.local.get([STORAGE_KEY], (r) => {
|
||||
const items = r[STORAGE_KEY] || [];
|
||||
const idx = items.findIndex(i => i.id === message.data.id);
|
||||
if (idx !== -1) {
|
||||
items[idx] = { ...items[idx], ...message.data };
|
||||
chrome.storage.local.set({ [STORAGE_KEY]: items });
|
||||
}
|
||||
sendResponse({ success: true });
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
/* Content script styles for X.com */
|
||||
|
||||
/* Hover effect on tweets */
|
||||
[data-testid="tweet"]:hover .hookd-checkbox {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
/* Save button hover */
|
||||
.hookd-save-btn:hover {
|
||||
opacity: 1 !important;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* Notification animation */
|
||||
@keyframes hookd-slide-in {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
#hookd-floating-bar {
|
||||
animation: hookd-slide-in 0.3s ease-out;
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
// 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 = '<span style="font-size:18px;">📌</span><span>Save to Hookd</span>';
|
||||
|
||||
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');
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 552 B |
Binary file not shown.
|
After Width: | Height: | Size: 121 B |
Binary file not shown.
|
After Width: | Height: | Size: 272 B |
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Hookd",
|
||||
"version": "1.1.0",
|
||||
"description": "Save, organize and AI-sort your X content",
|
||||
"permissions": ["storage"],
|
||||
"host_permissions": ["https://x.com/*", "https://twitter.com/*"],
|
||||
"action": {
|
||||
"default_popup": "popup.html"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Hookd</title>
|
||||
<style>
|
||||
body { width: 320px; min-height: 450px; font-family: Arial, sans-serif; background: #0f172a; color: #fff; margin: 0; padding: 0; }
|
||||
.header { background: #4F46E5; padding: 14px; text-align: center; }
|
||||
.header h1 { margin: 0; font-size: 18px; }
|
||||
.tabs { display: flex; background: #1e293b; }
|
||||
.tab { flex: 1; padding: 12px 8px; text-align: center; cursor: pointer; font-size: 11px; border-bottom: 2px solid #334155; }
|
||||
.tab:hover { background: #334155; }
|
||||
.tab.active { border-bottom-color: #4F46E5; background: #1e293b; }
|
||||
.content { padding: 14px; }
|
||||
.tab-content { display: none; }
|
||||
.tab-content.active { display: block; }
|
||||
h3 { margin: 0 0 12px 0; font-size: 13px; color: #94a3b8; }
|
||||
.box { background: #1e293b; border-radius: 8px; padding: 12px; margin-bottom: 8px; }
|
||||
.box-list { background: #1e293b; border-radius: 8px; padding: 10px; margin-bottom: 6px; display: flex; align-items: center; justify-content: space-between; }
|
||||
.btn { background: #4F46E5; color: white; border: none; padding: 10px 16px; border-radius: 6px; cursor: pointer; width: 100%; margin-top: 8px; font-size: 12px; }
|
||||
.btn-sm { padding: 6px 12px; width: auto; }
|
||||
.btn:hover { background: #5a82f0; }
|
||||
.btn-danger { background: #dc2626; }
|
||||
.btn-danger:hover { background: #ef4444; }
|
||||
input { width: 100%; padding: 10px; background: #0f172a; border: 1px solid #334155; border-radius: 6px; color: white; margin-bottom: 8px; font-size: 12px; box-sizing: border-box; }
|
||||
input::placeholder { color: #64748b; }
|
||||
.delete-btn { background: none; border: none; color: #dc2626; cursor: pointer; font-size: 16px; padding: 4px 8px; }
|
||||
.empty { text-align: center; padding: 30px; color: #64748b; font-size: 12px; }
|
||||
.add-form { display: flex; gap: 6px; margin-top: 10px; }
|
||||
.add-form input { margin: 0; flex: 1; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header"><h1>Hookd</h1></div>
|
||||
|
||||
<div class="tabs">
|
||||
<div class="tab active" id="tab-inbox">INBOX</div>
|
||||
<div class="tab" id="tab-lists">LISTS</div>
|
||||
<div class="tab" id="tab-settings">SET</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div id="inbox" class="tab-content active">
|
||||
<h3>INBOX <span id="inbox-count">(0)</span></h3>
|
||||
<div id="inbox-list"></div>
|
||||
<div class="empty" id="inbox-empty">No items.</div>
|
||||
<button class="btn btn-sm" id="btn-test-add">+ Test Add Item</button>
|
||||
</div>
|
||||
|
||||
<div id="lists" class="tab-content">
|
||||
<h3>LISTS</h3>
|
||||
<div id="lists-container"></div>
|
||||
<div class="add-form">
|
||||
<input type="text" id="new-list-name" placeholder="New list name...">
|
||||
<button class="btn btn-sm" id="btn-create-list">+</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="settings" class="tab-content">
|
||||
<h3>SETTINGS</h3>
|
||||
<div class="box">
|
||||
<input type="text" id="setting-handle" placeholder="@username">
|
||||
<button class="btn" id="btn-save-handle">SAVE HANDLE</button>
|
||||
</div>
|
||||
<div class="box" style="margin-top:12px;">
|
||||
<button class="btn btn-danger" id="btn-clear-all">CLEAR ALL DATA</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,180 @@
|
||||
// State
|
||||
var items = [];
|
||||
var lists = [
|
||||
{ id: '1', name: 'Dev Friends', contacts: [] },
|
||||
{ id: '2', name: 'Meme Buddies', contacts: [] },
|
||||
{ id: '3', name: 'Business', contacts: [] }
|
||||
];
|
||||
|
||||
// Load data
|
||||
function loadData() {
|
||||
chrome.storage.local.get(['hookd_items', 'hookd_lists'], function(result) {
|
||||
items = result.hookd_items || [];
|
||||
lists = result.hookd_lists || [
|
||||
{ id: '1', name: 'Dev Friends', contacts: [] },
|
||||
{ id: '2', name: 'Meme Buddies', contacts: [] },
|
||||
{ id: '3', name: 'Business', contacts: [] }
|
||||
];
|
||||
renderAll();
|
||||
});
|
||||
}
|
||||
|
||||
// Save items
|
||||
function saveItems() {
|
||||
chrome.storage.local.set({ hookd_items: items });
|
||||
}
|
||||
|
||||
// Render all
|
||||
function renderAll() {
|
||||
renderInbox();
|
||||
renderLists();
|
||||
loadHandle();
|
||||
}
|
||||
|
||||
// Tab switching
|
||||
function switchTab(tabId) {
|
||||
var contents = document.querySelectorAll('.tab-content');
|
||||
for (var i = 0; i < contents.length; i++) {
|
||||
contents[i].classList.remove('active');
|
||||
}
|
||||
var tabs = document.querySelectorAll('.tab');
|
||||
for (var i = 0; i < tabs.length; i++) {
|
||||
tabs[i].classList.remove('active');
|
||||
}
|
||||
document.getElementById(tabId).classList.add('active');
|
||||
document.getElementById('tab-' + tabId).classList.add('active');
|
||||
}
|
||||
|
||||
// Render inbox
|
||||
function renderInbox() {
|
||||
document.getElementById('inbox-count').textContent = '(' + items.length + ')';
|
||||
var list = document.getElementById('inbox-list');
|
||||
var empty = document.getElementById('inbox-empty');
|
||||
|
||||
if (items.length === 0) {
|
||||
list.innerHTML = '';
|
||||
empty.style.display = 'block';
|
||||
} else {
|
||||
empty.style.display = 'none';
|
||||
var html = '';
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
html += '<div class="box" style="position:relative;">' +
|
||||
'<div style="font-size:10px;color:#64748b;margin-bottom:4px;">@' + (items[i].author || 'unknown') + '</div>' +
|
||||
'<div style="font-size:12px;">' + ((items[i].content || 'No content').substring(0, 60)) + '</div>' +
|
||||
'<button class="delete-btn" data-index="' + i + '">x</button>' +
|
||||
'</div>';
|
||||
}
|
||||
list.innerHTML = html;
|
||||
|
||||
// Add delete handlers
|
||||
var deleteBtns = list.querySelectorAll('.delete-btn');
|
||||
for (var i = 0; i < deleteBtns.length; i++) {
|
||||
deleteBtns[i].addEventListener('click', function() {
|
||||
var idx = parseInt(this.getAttribute('data-index'));
|
||||
items.splice(idx, 1);
|
||||
saveItems();
|
||||
renderInbox();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test add item
|
||||
function testAddItem() {
|
||||
items.unshift({
|
||||
id: Date.now(),
|
||||
author: 'TestUser',
|
||||
content: 'Test item saved!',
|
||||
time: new Date().toISOString()
|
||||
});
|
||||
saveItems();
|
||||
renderInbox();
|
||||
}
|
||||
|
||||
// Render lists
|
||||
function renderLists() {
|
||||
var container = document.getElementById('lists-container');
|
||||
var html = '';
|
||||
for (var i = 0; i < lists.length; i++) {
|
||||
html += '<div class="box-list">' +
|
||||
'<span>' + lists[i].name + ' (' + lists[i].contacts.length + ')</span>' +
|
||||
'<button class="delete-btn" data-list-index="' + i + '">x</button>' +
|
||||
'</div>';
|
||||
}
|
||||
container.innerHTML = html;
|
||||
|
||||
// Add delete handlers
|
||||
var deleteBtns = container.querySelectorAll('.delete-btn');
|
||||
for (var i = 0; i < deleteBtns.length; i++) {
|
||||
deleteBtns[i].addEventListener('click', function() {
|
||||
var idx = parseInt(this.getAttribute('data-list-index'));
|
||||
lists.splice(idx, 1);
|
||||
chrome.storage.local.set({ hookd_lists: lists });
|
||||
renderLists();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Create list
|
||||
function createList() {
|
||||
var input = document.getElementById('new-list-name');
|
||||
var name = input.value.trim();
|
||||
if (!name) return;
|
||||
|
||||
lists.push({
|
||||
id: String(Date.now()),
|
||||
name: name,
|
||||
contacts: []
|
||||
});
|
||||
chrome.storage.local.set({ hookd_lists: lists });
|
||||
input.value = '';
|
||||
renderLists();
|
||||
}
|
||||
|
||||
// Load handle
|
||||
function loadHandle() {
|
||||
chrome.storage.local.get(['hookd_handle'], function(result) {
|
||||
if (result.hookd_handle) {
|
||||
document.getElementById('setting-handle').value = result.hookd_handle;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Save handle
|
||||
function saveHandle() {
|
||||
var handle = document.getElementById('setting-handle').value.trim();
|
||||
if (!handle) return;
|
||||
chrome.storage.local.set({ hookd_handle: handle });
|
||||
alert('Saved: ' + handle);
|
||||
}
|
||||
|
||||
// Clear all
|
||||
function clearAll() {
|
||||
if (!confirm('Delete ALL data?')) return;
|
||||
items = [];
|
||||
lists = [
|
||||
{ id: '1', name: 'Dev Friends', contacts: [] },
|
||||
{ id: '2', name: 'Meme Buddies', contacts: [] },
|
||||
{ id: '3', name: 'Business', contacts: [] }
|
||||
];
|
||||
chrome.storage.local.set({ hookd_items: [], hookd_lists: lists });
|
||||
renderAll();
|
||||
alert('All data cleared');
|
||||
}
|
||||
|
||||
// Setup
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Tab clicks
|
||||
document.getElementById('tab-inbox').addEventListener('click', function() { switchTab('inbox'); });
|
||||
document.getElementById('tab-lists').addEventListener('click', function() { switchTab('lists'); });
|
||||
document.getElementById('tab-settings').addEventListener('click', function() { switchTab('settings'); });
|
||||
|
||||
// Buttons
|
||||
document.getElementById('btn-test-add').addEventListener('click', testAddItem);
|
||||
document.getElementById('btn-create-list').addEventListener('click', createList);
|
||||
document.getElementById('btn-save-handle').addEventListener('click', saveHandle);
|
||||
document.getElementById('btn-clear-all').addEventListener('click', clearAll);
|
||||
|
||||
// Load data
|
||||
loadData();
|
||||
});
|
||||
Reference in New Issue
Block a user