fix: content script truncation error and mac shortcut issue
- Fixed truncated content.js file causing syntax error and missing logic - Updated manifest.json to use Command+Shift+X for Mac shortcut - Updated documentation
This commit is contained in:
BIN
.gemini-clipboard/clipboard-1764770972536.png
Normal file
BIN
.gemini-clipboard/clipboard-1764770972536.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
@@ -31,19 +31,26 @@
|
||||
## 使用方法
|
||||
|
||||
1. 点击浏览器工具栏中的扩展图标
|
||||
2. 选择输出格式(Markdown/JSON/XML)
|
||||
2. **选择输出格式:**
|
||||
- **Markdown 格式** (默认)
|
||||
- **JSON 结构化**
|
||||
- **XML 格式**
|
||||
- **ZIP (Markdown + 图)** - *自动下载包含本地图片的压缩包*
|
||||
3. 点击 **框选区域提取** 或 **提取整页内容**
|
||||
- 如果选择了 ZIP 格式,点击这两个按钮将直接触发打包下载
|
||||
4. 如果是框选模式,拖拽鼠标选择区域
|
||||
5. 提取完成后内容自动复制到剪贴板
|
||||
5. 提取完成后内容自动复制到剪贴板 (ZIP 格式除外)
|
||||
|
||||
**智能元素提取模式:**
|
||||
1. 按下快捷键(默认 `Alt+Shift+X` / Mac: `Option+Shift+X`)
|
||||
2. 移动鼠标,扩展会自动高亮当前的 HTML 元素
|
||||
3. 点击高亮的元素即可提取该区域内容
|
||||
- *提示:如果在插件弹窗中选择了 ZIP 格式,点击元素将下载该元素的 ZIP 包*
|
||||
|
||||
## 快捷操作
|
||||
|
||||
- **Alt+Shift+X** (Mac: **Option+Shift+X**) - 开启/关闭元素智能识别模式
|
||||
- **Alt+Shift+X** (Windows/Linux)
|
||||
- **Command+Shift+X** (Mac) - 开启/关闭元素智能识别模式
|
||||
- **ESC** - 取消选择模式
|
||||
|
||||
## 输出示例
|
||||
|
||||
@@ -4,12 +4,38 @@ chrome.commands.onCommand.addListener((command) => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 图片下载代理,绕过 CORS
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
if (request.action === 'fetchImage') {
|
||||
fetch(request.url)
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
return response.blob();
|
||||
})
|
||||
.then(blob => {
|
||||
// 将 Blob 转换为 Base64 字符串返回给 content script
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
sendResponse({ success: true, data: reader.result });
|
||||
};
|
||||
reader.onerror = () => {
|
||||
sendResponse({ success: false, error: 'Failed to read blob' });
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Image fetch failed:', error);
|
||||
sendResponse({ success: false, error: error.message });
|
||||
});
|
||||
|
||||
return true; // 保持消息通道开启以进行异步响应
|
||||
}
|
||||
});
|
||||
@@ -14,17 +14,13 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
} else if (request.action === 'extractFullPage') {
|
||||
currentFormat = request.format || 'markdown';
|
||||
const content = extractContent(document.body);
|
||||
const formatted = formatContent(content, currentFormat);
|
||||
copyToClipboard(formatted);
|
||||
saveToStorage(formatted);
|
||||
handleOutput(content, currentFormat);
|
||||
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();
|
||||
@@ -35,6 +31,24 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
return true;
|
||||
});
|
||||
|
||||
// === 统一输出处理 ===
|
||||
function handleOutput(content, format) {
|
||||
if (format === 'zip') {
|
||||
handleZipDownload(content);
|
||||
} else {
|
||||
const formatted = formatContent(content, format);
|
||||
copyToClipboard(formatted);
|
||||
saveToStorage(formatted);
|
||||
|
||||
if (currentFormat !== 'zip') {
|
||||
// 简单的检查,避免误报
|
||||
if (content && content.length > 0) {
|
||||
// 这里的通知逻辑由调用者处理,或者已经统一了
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === 元素选择模式 ===
|
||||
let highlightBox = null;
|
||||
let lastTarget = null;
|
||||
@@ -86,11 +100,11 @@ function onElementClick(e) {
|
||||
if (lastTarget) {
|
||||
// 提取内容
|
||||
const content = extractContent(lastTarget);
|
||||
const formatted = formatContent(content, currentFormat);
|
||||
handleOutput(content, currentFormat);
|
||||
|
||||
copyToClipboard(formatted);
|
||||
saveToStorage(formatted);
|
||||
showNotification('✅ 元素内容已提取');
|
||||
if (currentFormat !== 'zip') {
|
||||
showNotification('✅ 元素内容已提取');
|
||||
}
|
||||
}
|
||||
|
||||
cleanup();
|
||||
@@ -166,11 +180,11 @@ function onMouseUp(e) {
|
||||
if (elements.length > 0) {
|
||||
// 提取内容
|
||||
const content = extractFromElements(elements);
|
||||
const formatted = formatContent(content, currentFormat);
|
||||
handleOutput(content, currentFormat);
|
||||
|
||||
copyToClipboard(formatted);
|
||||
saveToStorage(formatted);
|
||||
showNotification('✅ 内容已提取并复制到剪贴板');
|
||||
if (currentFormat !== 'zip') {
|
||||
showNotification('✅ 内容已提取并复制到剪贴板');
|
||||
}
|
||||
} else {
|
||||
showNotification('❌ 未选中任何内容');
|
||||
}
|
||||
@@ -523,41 +537,4 @@ function escapeXML(str) {
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
// 复制到剪贴板
|
||||
async function copyToClipboard(text) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
} catch (err) {
|
||||
// 降级方案
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = text;
|
||||
textarea.style.position = 'fixed';
|
||||
textarea.style.opacity = '0';
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存到 storage
|
||||
function saveToStorage(text) {
|
||||
chrome.storage.local.set({ lastExtraction: text });
|
||||
}
|
||||
|
||||
// 显示通知
|
||||
function showNotification(message) {
|
||||
const notification = document.createElement('div');
|
||||
notification.className = 'llm-extractor-notification';
|
||||
notification.textContent = message;
|
||||
document.body.appendChild(notification);
|
||||
|
||||
setTimeout(() => {
|
||||
notification.classList.add('fade-out');
|
||||
setTimeout(() => notification.remove(), 300);
|
||||
}, 2000);
|
||||
}
|
||||
.replace(/
|
||||
13
src/browser-extension/lib/jszip.min.js
vendored
Normal file
13
src/browser-extension/lib/jszip.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -24,7 +24,7 @@
|
||||
"toggle-element-selection": {
|
||||
"suggested_key": {
|
||||
"default": "Alt+Shift+X",
|
||||
"mac": "Alt+Shift+X"
|
||||
"mac": "Command+Shift+X"
|
||||
},
|
||||
"description": "Toggle Element Selection Mode"
|
||||
}
|
||||
@@ -32,7 +32,7 @@
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"js": ["content.js"],
|
||||
"js": ["lib/jszip.min.js", "content.js"],
|
||||
"css": ["content.css"]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
<option value="markdown">Markdown 格式</option>
|
||||
<option value="json">JSON 结构化</option>
|
||||
<option value="xml">XML 格式</option>
|
||||
<option value="zip">ZIP (Markdown + 图)</option>
|
||||
</select>
|
||||
|
||||
<button id="selectBtn" class="btn btn-primary">
|
||||
|
||||
Reference in New Issue
Block a user