// Text Selection Menu - Monica AI-like functionality
// Provides context menu for selected text with AI features
class TextSelectionMenu {
constructor() {
this.menu = null;
this.selectedText = '';
this.init();
}
init() {
// Create menu element
this.createMenu();
// Add event listeners
document.addEventListener('mouseup', (e) => this.handleTextSelection(e));
document.addEventListener('mousedown', (e) => this.handleClickOutside(e));
// Hide menu on scroll
window.addEventListener('scroll', () => this.hideMenu());
}
createMenu() {
this.menu = document.createElement('div');
this.menu.id = 'text-selection-menu';
this.menu.className = 'selection-menu';
this.menu.innerHTML = `
`;
document.body.appendChild(this.menu);
// Add action listeners
this.menu.querySelectorAll('.menu-action').forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
const action = btn.dataset.action;
this.handleAction(action);
});
});
}
handleTextSelection(e) {
const selection = window.getSelection();
const text = selection.toString().trim();
if (text.length > 0) {
this.selectedText = text;
this.showMenu(e.pageX, e.pageY);
} else {
this.hideMenu();
}
}
showMenu(x, y) {
this.menu.style.display = 'flex';
// Position menu near cursor
const menuWidth = this.menu.offsetWidth;
const menuHeight = this.menu.offsetHeight;
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
// Adjust position to keep menu in viewport
let left = x - menuWidth / 2;
let top = y - menuHeight - 10;
if (left < 10) left = 10;
if (left + menuWidth > windowWidth - 10) left = windowWidth - menuWidth - 10;
if (top < 10) top = y + 20;
this.menu.style.left = `${left}px`;
this.menu.style.top = `${top}px`;
// Animate in
setTimeout(() => {
this.menu.classList.add('visible');
}, 10);
}
hideMenu() {
this.menu.classList.remove('visible');
setTimeout(() => {
this.menu.style.display = 'none';
}, 200);
}
handleClickOutside(e) {
if (!this.menu.contains(e.target)) {
this.hideMenu();
}
}
async handleAction(action) {
switch (action) {
case 'note':
this.createNote();
break;
case 'search':
this.googleSearch();
break;
case 'ai':
this.askAI();
break;
case 'copy':
this.copyText();
break;
case 'share':
this.shareText();
break;
}
this.hideMenu();
}
createNote() {
// Save note to localStorage
const notes = JSON.parse(localStorage.getItem('newsNotes') || '[]');
const note = {
id: Date.now(),
text: this.selectedText,
timestamp: new Date().toISOString(),
url: window.location.href
};
notes.unshift(note);
localStorage.setItem('newsNotes', JSON.stringify(notes));
// Show notification
this.showNotification('📝 Note saved!');
// Optional: Send to backend
fetch('/api/save-note', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(note)
}).catch(err => console.log('Note saved locally only'));
}
googleSearch() {
const query = encodeURIComponent(this.selectedText);
window.open(`https://www.google.com/search?q=${query}`, '_blank');
}
async askAI() {
// Open chat widget and populate with query
const chatWidget = document.getElementById('chat-widget');
const chatInput = document.getElementById('chat-input');
if (chatWidget && chatInput) {
chatWidget.classList.remove('chat-closed');
chatWidget.classList.add('chat-open');
chatInput.value = `Explain: "${this.selectedText}"`;
chatInput.focus();
}
}
async copyText() {
try {
await navigator.clipboard.writeText(this.selectedText);
this.showNotification('📋 Copied to clipboard!');
} catch (err) {
// Fallback for older browsers
const textarea = document.createElement('textarea');
textarea.value = this.selectedText;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
this.showNotification('📋 Copied to clipboard!');
}
}
async shareText() {
const shareData = {
title: 'News Intelligence',
text: this.selectedText,
url: window.location.href
};
if (navigator.share) {
try {
await navigator.share(shareData);
} catch (err) {
if (err.name !== 'AbortError') {
this.fallbackShare();
}
}
} else {
this.fallbackShare();
}
}
fallbackShare() {
// Copy link with selected text
const shareUrl = `${window.location.href}#text=${encodeURIComponent(this.selectedText)}`;
navigator.clipboard.writeText(shareUrl);
this.showNotification('🔗 Share link copied!');
}
showNotification(message) {
const notification = document.createElement('div');
notification.className = 'selection-notification';
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.classList.add('visible');
}, 10);
setTimeout(() => {
notification.classList.remove('visible');
setTimeout(() => {
document.body.removeChild(notification);
}, 300);
}, 2000);
}
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
new TextSelectionMenu();
});
} else {
new TextSelectionMenu();
}