feat: add smart element selection mode with shortcut support

- Added background script for handling global shortcuts (Alt+Shift+X)
- Implemented element highlighting and click-to-extract in content script
- Updated manifest to include background worker and commands
- Updated documentation with new feature usage
- Added GEMINI.md project context
This commit is contained in:
empty
2025-12-03 17:20:26 +08:00
parent 6059865523
commit 2ab8ba9c3c
6 changed files with 194 additions and 2 deletions

View File

@@ -0,0 +1,15 @@
chrome.commands.onCommand.addListener((command) => {
if (command === 'toggle-element-selection') {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
if (tabs.length === 0) return;
const tabId = tabs[0].id;
// 尝试发送消息如果失败可能是页面未加载content script
chrome.tabs.sendMessage(tabId, { action: 'toggleElementSelection' })
.catch(() => {
// 可以选择注入脚本或忽略
console.log('Cannot send message to tab', tabId);
});
});
}
});

View File

@@ -17,6 +17,16 @@
pointer-events: none;
}
.llm-extractor-element-highlight {
position: fixed;
border: 2px solid #ff4757;
background: rgba(255, 71, 87, 0.1);
z-index: 999999;
pointer-events: none;
transition: all 0.1s ease-out;
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.5);
}
.llm-extractor-hint {
position: fixed;
top: 20px;
@@ -73,4 +83,4 @@
transform: translateY(0);
opacity: 1;
}
}
}

View File

@@ -18,10 +18,85 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
copyToClipboard(formatted);
saveToStorage(formatted);
sendResponse({ success: true });
} else if (request.action === 'toggleElementSelection') {
// 如果已经在选择模式,则退出
if (isSelecting) {
cleanup();
} else {
// 默认格式或者读取存储的格式? 这里暂时用默认markdown
// 理想情况应该从 storage 读取,这里简化处理
chrome.storage.local.get('format', (res) => {
currentFormat = res.format || 'markdown';
startElementSelectionMode();
});
}
sendResponse({ success: true });
}
return true;
});
// === 元素选择模式 ===
let highlightBox = null;
let lastTarget = null;
function startElementSelectionMode() {
isSelecting = true;
// 创建提示
const hint = document.createElement('div');
hint.className = 'llm-extractor-hint';
hint.textContent = '移动鼠标选择元素点击提取ESC 取消';
document.body.appendChild(hint);
// 创建高亮框
highlightBox = document.createElement('div');
highlightBox.className = 'llm-extractor-element-highlight';
highlightBox.style.display = 'none';
document.body.appendChild(highlightBox);
// 绑定事件
document.addEventListener('mousemove', onElementMouseMove, true);
document.addEventListener('click', onElementClick, true);
document.addEventListener('keydown', onKeyDown);
}
function onElementMouseMove(e) {
if (!isSelecting) return;
const target = document.elementFromPoint(e.clientX, e.clientY);
if (target && target !== lastTarget && target !== highlightBox && !target.classList.contains('llm-extractor-hint')) {
lastTarget = target;
const rect = target.getBoundingClientRect();
highlightBox.style.display = 'block';
highlightBox.style.left = rect.left + 'px';
highlightBox.style.top = rect.top + 'px';
highlightBox.style.width = rect.width + 'px';
highlightBox.style.height = rect.height + 'px';
}
}
function onElementClick(e) {
if (!isSelecting) return;
e.preventDefault();
e.stopPropagation();
if (lastTarget) {
// 提取内容
const content = extractContent(lastTarget);
const formatted = formatContent(content, currentFormat);
copyToClipboard(formatted);
saveToStorage(formatted);
showNotification('✅ 元素内容已提取');
}
cleanup();
}
// === 框选模式 ===
// 开始框选模式
function startSelectionMode() {
isSelecting = true;
@@ -111,12 +186,18 @@ function onKeyDown(e) {
function cleanup() {
isSelecting = false;
lastTarget = null;
if (selectionBox) {
selectionBox.remove();
selectionBox = null;
}
if (highlightBox) {
highlightBox.remove();
highlightBox = null;
}
if (overlay) {
overlay.remove();
overlay = null;
@@ -128,6 +209,10 @@ function cleanup() {
document.removeEventListener('mousedown', onMouseDown);
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
document.removeEventListener('mousemove', onElementMouseMove, true);
document.removeEventListener('click', onElementClick, true);
document.removeEventListener('keydown', onKeyDown);
}

View File

@@ -17,6 +17,18 @@
"128": "icons/icon128.png"
}
},
"background": {
"service_worker": "background.js"
},
"commands": {
"toggle-element-selection": {
"suggested_key": {
"default": "Alt+Shift+X",
"mac": "Alt+Shift+X"
},
"description": "Toggle Element Selection Mode"
}
},
"content_scripts": [
{
"matches": ["<all_urls>"],