// ==UserScript==
// @name AI Page Summarizer Pro
// @name:zh-CN AI网页内容智能总结助手
// @namespace http://tampermonkey.net/
// @version 0.9.8.1
// @description 网页内容智能总结,支持自定义API和提示词
// @description:zh-CN 网页内容智能总结,支持自定义API和提示词
// @author Your Name
// @match *://*/*
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// @grant GM_addStyle
// @grant GM.xmlHttpRequest
// @grant GM.setValue
// @grant GM.getValue
// @grant GM.registerMenuCommand
// @grant GM.addStyle
// @connect api.openai.com
// @connect *
// @require https://cdn.jsdelivr.net/npm/marked@4.3.0/marked.min.js
// @run-at document-idle
// @license MIT
// @compatible chrome
// @compatible firefox
// @compatible edge
// @compatible opera
// @compatible safari
// @compatible android
// @downloadURL none
// ==/UserScript==
(function() {
'use strict';
// 兼容性处理层
const scriptHandler = {
// 存储值
setValue: async function(key, value) {
try {
if (typeof GM_setValue !== 'undefined') {
GM_setValue(key, value);
} else if (typeof GM !== 'undefined' && GM.setValue) {
await GM.setValue(key, value);
} else {
localStorage.setItem(key, JSON.stringify(value));
}
} catch (error) {
console.error('存储值失败:', error);
}
},
// 获取值
getValue: async function(key, defaultValue) {
try {
if (typeof GM_getValue !== 'undefined') {
return GM_getValue(key, defaultValue);
} else if (typeof GM !== 'undefined' && GM.getValue) {
return await GM.getValue(key, defaultValue);
} else {
const value = localStorage.getItem(key);
return value ? JSON.parse(value) : defaultValue;
}
} catch (error) {
console.error('获取值失败:', error);
return defaultValue;
}
},
// HTTP请求
xmlHttpRequest: function(details) {
if (typeof GM_xmlhttpRequest !== 'undefined') {
return GM_xmlhttpRequest(details);
} else if (typeof GM !== 'undefined' && GM.xmlHttpRequest) {
return GM.xmlHttpRequest(details);
} else {
return fetch(details.url, {
method: details.method,
headers: details.headers,
body: details.data
}).then(response => response.json())
.then(data => {
if (details.onload) {
details.onload({ responseText: JSON.stringify(data) });
}
})
.catch(error => {
if (details.onerror) {
details.onerror(error);
}
});
}
},
// 注册菜单命令
registerMenuCommand: function(name, fn) {
try {
if (typeof GM_registerMenuCommand !== 'undefined') {
GM_registerMenuCommand(name, fn);
} else if (typeof GM !== 'undefined' && GM.registerMenuCommand) {
GM.registerMenuCommand(name, fn);
}
} catch (error) {
console.log('注册菜单命令失败:', error);
}
},
// 添加样式
addStyle: function(css) {
try {
if (typeof GM_addStyle !== 'undefined') {
GM_addStyle(css);
} else if (typeof GM !== 'undefined' && GM.addStyle) {
GM.addStyle(css);
} else {
const style = document.createElement('style');
style.textContent = css;
document.head.appendChild(style);
}
} catch (error) {
console.error('添加样式失败:', error);
}
}
};
// 配置项
let config = {
apiUrl: 'https://api.openai.com/v1/chat/completions',
apiKey: '',
model: 'gpt-3.5-turbo',
prompt: `You are a professional content summarizer in chinese. Your task is to create a clear, concise, and well-structured summary of the webpage content. Follow these guidelines:
1. Output Format:
- Use Markdown formatting
- Start with a brief overview
- Use appropriate headings (h2, h3)
- Include bullet points for key points
- Use bold for important terms
- Use blockquotes for notable quotes
- Use code blocks for technical content
2. Content Structure:
- Main Topic/Title
- Key Points
- Important Details
- Conclusions/Summary
3. Writing Style:
- Clear and concise language
- Professional tone
- Logical flow
- Easy to understand
- Focus on essential information
4. Important Rules:
- DO NOT show your reasoning process
- DO NOT include meta-commentary
- DO NOT explain your methodology
- DO NOT use phrases like "this summary shows" or "the content indicates"
- Start directly with the content summary
5. Length Guidelines:
- Overview: 1-2 sentences
- Key Points: 3-5 bullet points
- Important Details: 2-3 paragraphs
- Summary: 1-2 sentences
Remember: Focus on delivering the information directly without any meta-analysis or explanation of your process.`,
iconPosition: { y: 20 },
shortcut: 'option+a'
};
// 初始化配置
async function initConfig() {
config.apiUrl = await scriptHandler.getValue('apiUrl', config.apiUrl);
config.apiKey = await scriptHandler.getValue('apiKey', config.apiKey);
config.model = await scriptHandler.getValue('model', config.model);
config.prompt = await scriptHandler.getValue('prompt', config.prompt);
config.iconPosition = await scriptHandler.getValue('iconPosition', config.iconPosition);
config.shortcut = await scriptHandler.getValue('shortcut', config.shortcut);
}
// DOM 元素引用
const elements = {
icon: null,
container: null,
settings: null,
backdrop: null
};
// 全局变量用于判断是否已经监听了键盘事件
let keyboardListenerActive = false;
// 快捷键处理
const keyManager = {
setup() {
try {
// 移除旧的监听器
if (keyboardListenerActive) {
document.removeEventListener('keydown', this._handleKeyDown);
}
// 添加新的监听器,使用普通函数而非方法
this._handleKeyDown = (e) => {
// 忽略输入框中的按键
if (e.target.tagName === 'INPUT' ||
e.target.tagName === 'TEXTAREA' ||
e.target.isContentEditable ||
e.target.getAttribute('role') === 'textbox') {
return;
}
// 解析配置的快捷键
const shortcutParts = config.shortcut.toLowerCase().split('+');
// 获取主键(非修饰键)
const mainKey = shortcutParts.filter(part =>
!['alt', 'option', 'ctrl', 'control', 'shift', 'cmd', 'command', 'meta']
.includes(part)
)[0] || 'a';
// 检查所需的修饰键
const needAlt = shortcutParts.some(p => p === 'alt' || p === 'option');
const needCtrl = shortcutParts.some(p => p === 'ctrl' || p === 'control');
const needShift = shortcutParts.some(p => p === 'shift');
const needMeta = shortcutParts.some(p => p === 'cmd' || p === 'command' || p === 'meta');
// 检查按键是否匹配 - 同时检查key和code
const isMainKeyMatched =
e.key.toLowerCase() === mainKey ||
(e.code && e.code.toLowerCase() === 'key' + mainKey);
// 检查修饰键是否匹配
if (isMainKeyMatched &&
e.altKey === needAlt &&
e.ctrlKey === needCtrl &&
e.shiftKey === needShift &&
e.metaKey === needMeta) {
console.log('快捷键触发成功:', config.shortcut);
e.preventDefault();
e.stopPropagation();
showSummary();
return false;
}
};
// 使用捕获阶段来确保我们能先捕获到事件
document.addEventListener('keydown', this._handleKeyDown, true);
keyboardListenerActive = true;
// 设置全局访问方法
window.activateSummary = showSummary;
console.log('快捷键已设置:', config.shortcut);
return true;
} catch (error) {
console.error('设置快捷键失败:', error);
return false;
}
},
// 测试快捷键是否工作
test() {
try {
if (!keyboardListenerActive) {
console.warn('键盘监听器未激活,请先调用 keyManager.setup()');
return false;
}
// 解析当前快捷键
const parts = config.shortcut.toLowerCase().split('+');
const mainKey = parts.filter(part =>
!['alt', 'option', 'ctrl', 'control', 'shift', 'cmd', 'command', 'meta']
.includes(part)
)[0] || 'a';
// 创建事件对象
const eventOptions = {
key: mainKey,
code: 'Key' + mainKey.toUpperCase(),
altKey: parts.includes('alt') || parts.includes('option'),
ctrlKey: parts.includes('ctrl') || parts.includes('control'),
shiftKey: parts.includes('shift'),
metaKey: parts.includes('cmd') || parts.includes('command') || parts.includes('meta'),
bubbles: true,
cancelable: true
};
console.log('模拟快捷键按下:', JSON.stringify(eventOptions));
const event = new KeyboardEvent('keydown', eventOptions);
// 由于 KeyboardEvent 的限制,某些属性可能无法正确设置,所以我们通过这种方式确认
if (!event.altKey && eventOptions.altKey) {
console.warn('注意: 无法在模拟事件中设置 altKey 属性');
}
// 分发事件
document.dispatchEvent(event);
// 因为可能无法模拟事件,所以直接提供一个调用方法
console.log('您也可以通过控制台调用 window.activateSummary() 来直接触发');
return true;
} catch (error) {
console.error('测试快捷键失败:', error);
return false;
}
}
};
// 等待依赖库加载
function waitForDependencies(callback) {
if (window.marked) {
window.marked.setOptions({ breaks: true, gfm: true });
callback();
return;
}
setTimeout(() => waitForDependencies(callback), 100);
}
// 创建图标
function createIcon() {
const icon = document.createElement('div');
icon.id = 'website-summary-icon';
// 使用星星emoji
icon.innerHTML = `🌟`;
icon.style.cssText = `
position: fixed;
z-index: 999999;
width: 40px;
height: 40px;
border-radius: 20px;
background: rgba(255, 255, 255, 0.9);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
backdrop-filter: blur(5px);
right: 20px;
top: ${config.iconPosition.y || 20}px;
touch-action: none;
will-change: transform;
`;
icon.addEventListener('mouseover', () => {
icon.style.transform = 'scale(1.1)';
icon.style.boxShadow = '0 4px 12px rgba(255, 215, 0, 0.3)';
});
icon.addEventListener('mouseout', () => {
icon.style.transform = 'scale(1)';
icon.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.1)';
});
icon.addEventListener('click', showSummary);
icon.addEventListener('contextmenu', (e) => {
e.preventDefault();
showSettings();
});
makeDraggable(icon);
document.body.appendChild(icon);
elements.icon = icon;
return icon;
}
// 显示设置界面
function showSettings() {
const settings = elements.settings || createSettingsUI();
settings.style.display = 'block';
}
// 显示摘要
async function showSummary() {
const container = elements.container || createSummaryUI();
const content = container.querySelector('#website-summary-content');
// 显示容器和背景
showBackdrop();
container.style.display = 'block';
setTimeout(() => container.style.opacity = '1', 10);
// 显示加载中
content.innerHTML = '
正在获取总结...
';
try {
const pageContent = getPageContent();
if (!pageContent) throw new Error('无法获取页面内容');
const summary = await getSummary(pageContent);
if (!summary) throw new Error('获取总结失败');
renderContent(summary);
} catch (error) {
content.innerHTML = `
获取总结失败:${error.message}
请检查API配置是否正确
`;
}
}
// 创建/显示背景
function showBackdrop() {
if (!elements.backdrop) {
const backdrop = document.createElement('div');
backdrop.id = 'website-summary-backdrop';
backdrop.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(250, 250, 252, 0.75);
backdrop-filter: blur(5px);
z-index: 999997;
display: none;
opacity: 0;
transition: opacity 0.3s ease;
`;
backdrop.addEventListener('click', (e) => {
if (e.target === backdrop) {
hideUI();
}
});
document.body.appendChild(backdrop);
elements.backdrop = backdrop;
}
elements.backdrop.style.display = 'block';
setTimeout(() => elements.backdrop.style.opacity = '1', 10);
}
// 隐藏UI
function hideUI() {
// 隐藏背景
if (elements.backdrop) {
elements.backdrop.style.opacity = '0';
setTimeout(() => elements.backdrop.style.display = 'none', 300);
}
// 隐藏摘要容器
if (elements.container) {
elements.container.style.opacity = '0';
setTimeout(() => elements.container.style.display = 'none', 300);
}
}
// 创建摘要UI
function createSummaryUI() {
const container = document.createElement('div');
container.id = 'website-summary-container';
container.style.cssText = `
position: fixed;
z-index: 999998;
background: rgba(255, 255, 255, 0.9);
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
padding: 16px;
width: 80%;
max-width: 800px;
max-height: 80vh;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
display: none;
backdrop-filter: blur(10px);
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
overflow: hidden;
opacity: 0;
transition: opacity 0.3s ease;
`;
// 标题栏
const header = document.createElement('div');
header.style.cssText = `
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
cursor: move;
padding-bottom: 8px;
border-bottom: 1px solid #eee;
`;
// 标题
const title = document.createElement('h3');
title.textContent = '网页总结';
title.style.cssText = 'margin: 0; font-size: 18px; color: #333;';
// 按钮容器
const buttonContainer = document.createElement('div');
buttonContainer.style.cssText = 'display: flex; gap: 8px; align-items: center;';
// 复制按钮
const copyBtn = document.createElement('button');
copyBtn.textContent = '复制';
copyBtn.style.cssText = `
background: #4CAF50;
color: white;
border: none;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s;
`;
copyBtn.addEventListener('mouseover', () => copyBtn.style.backgroundColor = '#45a049');
copyBtn.addEventListener('mouseout', () => copyBtn.style.backgroundColor = '#4CAF50');
copyBtn.addEventListener('click', () => {
const content = document.getElementById('website-summary-content').innerText;
navigator.clipboard.writeText(content).then(() => {
copyBtn.textContent = '已复制';
setTimeout(() => copyBtn.textContent = '复制', 2000);
});
});
// 关闭按钮
const closeBtn = document.createElement('button');
closeBtn.textContent = '×';
closeBtn.style.cssText = `
background: none;
border: none;
font-size: 24px;
cursor: pointer;
padding: 0 8px;
color: #666;
transition: color 0.2s;
`;
closeBtn.addEventListener('mouseover', () => closeBtn.style.color = '#ff4444');
closeBtn.addEventListener('mouseout', () => closeBtn.style.color = '#666');
closeBtn.addEventListener('click', hideUI);
// 内容区域
const content = document.createElement('div');
content.id = 'website-summary-content';
content.style.cssText = `
max-height: calc(80vh - 60px);
overflow-y: auto;
font-size: 14px;
line-height: 1.6;
padding: 8px 0;
`;
// 组装界面
buttonContainer.appendChild(copyBtn);
buttonContainer.appendChild(closeBtn);
header.appendChild(title);
header.appendChild(buttonContainer);
container.appendChild(header);
container.appendChild(content);
document.body.appendChild(container);
makeDraggable(container);
elements.container = container;
return container;
}
// 创建设置界面
function createSettingsUI() {
const settingsContainer = document.createElement('div');
settingsContainer.id = 'website-summary-settings';
settingsContainer.style.cssText = `
position: fixed;
z-index: 1000000;
background: rgba(255, 255, 255, 0.98);
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
padding: 20px;
width: 400px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
display: none;
backdrop-filter: blur(10px);
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
`;
// 标题栏
const header = document.createElement('div');
header.style.cssText = 'display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; cursor: move;';
const title = document.createElement('h3');
title.textContent = '设置';
title.style.margin = '0';
const closeBtn = document.createElement('button');
closeBtn.textContent = '×';
closeBtn.style.cssText = 'background: none; border: none; font-size: 24px; cursor: pointer; padding: 0 8px; color: #666;';
closeBtn.addEventListener('click', () => settingsContainer.style.display = 'none');
// 表单
const form = document.createElement('form');
form.style.cssText = 'display: flex; flex-direction: column; gap: 16px;';
// 创建输入字段函数
function createField(id, label, value, type = 'text', placeholder = '') {
const container = document.createElement('div');
container.style.cssText = 'display: flex; flex-direction: column; gap: 4px;';
const labelElem = document.createElement('label');
labelElem.textContent = label;
labelElem.style.cssText = 'font-size: 14px; color: #333; font-weight: 500;';
const input = document.createElement(type === 'textarea' ? 'textarea' : 'input');
if (type !== 'textarea') input.type = type;
input.id = id;
input.value = value;
input.placeholder = placeholder;
input.autocomplete = 'off';
input.setAttribute('data-form-type', 'other');
const baseStyle = 'width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 6px; font-family: inherit;';
input.style.cssText = type === 'textarea' ? baseStyle + 'height: 100px; resize: vertical;' : baseStyle;
container.appendChild(labelElem);
container.appendChild(input);
return { container, input };
}
// 创建字段
const apiUrlField = createField('apiUrl', 'API URL', config.apiUrl, 'text', '输入API URL');
const apiKeyField = createField('apiKey', 'API Key', config.apiKey, 'text', '输入API Key');
const modelField = createField('model', 'AI 模型', config.model, 'text', '输入AI模型名称');
const shortcutField = createField('shortcut', '快捷键', config.shortcut, 'text', '例如: option+a, ctrl+shift+s');
const promptField = createField('prompt', '提示词', config.prompt, 'textarea', '输入提示词');
// 添加字段到表单
form.appendChild(apiUrlField.container);
form.appendChild(apiKeyField.container);
form.appendChild(modelField.container);
form.appendChild(shortcutField.container);
form.appendChild(promptField.container);
// 保存按钮
const saveBtn = document.createElement('button');
saveBtn.textContent = '保存设置';
saveBtn.type = 'button';
saveBtn.style.cssText = `
background: #007AFF;
color: white;
border: none;
padding: 10px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: background-color 0.2s;
`;
saveBtn.addEventListener('mouseover', () => saveBtn.style.backgroundColor = '#0056b3');
saveBtn.addEventListener('mouseout', () => saveBtn.style.backgroundColor = '#007AFF');
// 保存逻辑
saveBtn.addEventListener('click', (e) => {
e.preventDefault();
// 获取并验证表单值
const newApiUrl = apiUrlField.input.value.trim();
const newApiKey = apiKeyField.input.value.trim();
const newModel = modelField.input.value.trim();
const newPrompt = promptField.input.value.trim();
const newShortcut = shortcutField.input.value.trim();
if (!newApiUrl || !newApiKey) {
alert('请至少填写API URL和API Key');
return;
}
// 保存到存储
GM_setValue('apiUrl', newApiUrl);
GM_setValue('apiKey', newApiKey);
GM_setValue('model', newModel);
GM_setValue('prompt', newPrompt);
GM_setValue('shortcut', newShortcut);
// 更新内存配置
config.apiUrl = newApiUrl;
config.apiKey = newApiKey;
config.model = newModel;
config.prompt = newPrompt;
config.shortcut = newShortcut;
// 更新快捷键
keyManager.setup();
// 显示成功提示
showToast('设置已保存');
// 关闭设置
settingsContainer.style.display = 'none';
});
// 组装界面
header.appendChild(title);
header.appendChild(closeBtn);
form.appendChild(saveBtn);
settingsContainer.appendChild(header);
settingsContainer.appendChild(form);
document.body.appendChild(settingsContainer);
makeDraggable(settingsContainer);
elements.settings = settingsContainer;
return settingsContainer;
}
// 显示提示消息
function showToast(message) {
const toast = document.createElement('div');
toast.textContent = message;
toast.style.cssText = `
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: #4CAF50;
color: white;
padding: 10px 20px;
border-radius: 4px;
z-index: 1000001;
font-size: 14px;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
`;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 2000);
}
// 拖拽功能
function makeDraggable(element) {
const header = element.querySelector('div') || element;
let startX = 0, startY = 0;
let elementX = 0, elementY = 0;
let dragging = false;
// 鼠标事件
header.addEventListener('mousedown', startDrag);
// 触摸事件
header.addEventListener('touchstart', (e) => {
const touch = e.touches[0];
startDrag({ clientX: touch.clientX, clientY: touch.clientY, preventDefault: () => e.preventDefault() });
}, { passive: false });
function startDrag(e) {
e.preventDefault();
dragging = true;
// 记录起始位置
startX = e.clientX;
startY = e.clientY;
elementX = element.offsetLeft;
elementY = element.offsetTop;
// 设置样式
if (element.id === 'website-summary-icon') {
element.style.transition = 'none';
element.style.opacity = '0.9';
}
// 添加移动和结束事件
document.addEventListener('mousemove', onDrag);
document.addEventListener('touchmove', onTouchDrag, { passive: false });
document.addEventListener('mouseup', stopDrag);
document.addEventListener('touchend', stopDrag);
}
function onDrag(e) {
if (!dragging) return;
move(e.clientX, e.clientY);
}
function onTouchDrag(e) {
if (!dragging) return;
e.preventDefault();
const touch = e.touches[0];
move(touch.clientX, touch.clientY);
}
function move(clientX, clientY) {
// 计算新位置
const deltaX = clientX - startX;
const deltaY = clientY - startY;
if (element.id === 'website-summary-icon') {
// 仅垂直移动图标
const newY = elementY + deltaY;
const maxY = window.innerHeight - element.offsetHeight - 10;
element.style.top = Math.max(10, Math.min(newY, maxY)) + 'px';
} else {
// 自由移动其他元素
element.style.left = (elementX + deltaX) + 'px';
element.style.top = (elementY + deltaY) + 'px';
// 重置transform以避免与translate(-50%, -50%)冲突
element.style.transform = 'none';
}
}
function stopDrag() {
if (!dragging) return;
dragging = false;
// 恢复样式
if (element.id === 'website-summary-icon') {
element.style.transition = 'transform 0.2s ease, box-shadow 0.2s ease';
element.style.opacity = '1';
// 保存图标位置
config.iconPosition = { y: element.offsetTop };
GM_setValue('iconPosition', config.iconPosition);
}
// 移除事件监听器
document.removeEventListener('mousemove', onDrag);
document.removeEventListener('touchmove', onTouchDrag);
document.removeEventListener('mouseup', stopDrag);
document.removeEventListener('touchend', stopDrag);
}
}
// 获取页面内容
function getPageContent() {
try {
const clone = document.body.cloneNode(true);
const elementsToRemove = clone.querySelectorAll('script, style, iframe, nav, header, footer, .ad, .advertisement, .social-share, .comment, .related-content');
elementsToRemove.forEach(el => el.remove());
return clone.innerText.replace(/\s+/g, ' ').trim().slice(0, 5000);
} catch (error) {
return document.body.innerText.slice(0, 5000);
}
}
// 调用API获取总结
function getSummary(content) {
return new Promise((resolve, reject) => {
const apiKey = config.apiKey.trim();
if (!apiKey) {
resolve('请先设置API Key');
return;
}
const requestData = {
model: config.model,
messages: [
{
role: 'system',
content: '你是一个专业的网页内容总结助手,善于使用markdown格式来组织信息。'
},
{
role: 'user',
content: config.prompt + '\n\n' + content
}
],
temperature: 0.7
};
GM_xmlhttpRequest({
method: 'POST',
url: config.apiUrl,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
data: JSON.stringify(requestData),
onload: function(response) {
try {
const data = JSON.parse(response.responseText);
if (data.error) {
resolve('API调用失败: ' + data.error.message);
return;
}
if (data.choices && data.choices[0] && data.choices[0].message) {
resolve(data.choices[0].message.content);
} else {
resolve('API响应格式错误,请检查配置。');
}
} catch (error) {
resolve('解析API响应失败,请检查网络连接。');
}
},
onerror: function() {
resolve('API调用失败,请检查网络连接和API配置。');
}
});
});
}
// 渲染Markdown内容
function renderContent(content) {
const container = document.getElementById('website-summary-content');
if (!container) return;
try {
if (!content) throw new Error('内容为空');
// 渲染Markdown
let html = window.marked.parse(content);
container.innerHTML = html;
// 添加样式
addMarkdownStyles();
} catch (error) {
container.innerHTML = '渲染内容失败,请刷新页面重试。
';
}
}
// 添加Markdown样式
function addMarkdownStyles() {
const styleId = 'website-summary-styles';
if (document.getElementById(styleId)) return;
const style = document.createElement('style');
style.id = styleId;
style.textContent = `
#website-summary-content {
font-size: 14px;
line-height: 1.6;
color: #333;
}
#website-summary-content h1, #website-summary-content h2, #website-summary-content h3 {
margin-top: 20px;
margin-bottom: 10px;
color: #222;
}
#website-summary-content p {
margin: 10px 0;
}
#website-summary-content code {
background: #f5f5f5;
padding: 2px 4px;
border-radius: 3px;
font-family: monospace;
}
#website-summary-content pre {
background: #f5f5f5;
padding: 15px;
border-radius: 5px;
overflow-x: auto;
}
#website-summary-content blockquote {
border-left: 4px solid #ddd;
margin: 10px 0;
padding-left: 15px;
color: #666;
}
#website-summary-content ul, #website-summary-content ol {
margin: 10px 0;
padding-left: 20px;
}
#website-summary-content li {
margin: 5px 0;
}
#website-summary-content table {
border-collapse: collapse;
width: 100%;
margin: 10px 0;
}
#website-summary-content th, #website-summary-content td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
#website-summary-content th {
background: #f5f5f5;
}
`;
document.head.appendChild(style);
}
// 添加菜单命令
function registerMenuCommands() {
try {
GM_registerMenuCommand('显示网页总结 (快捷键: ' + config.shortcut + ')', showSummary);
GM_registerMenuCommand('打开设置', showSettings);
} catch (error) {
console.log('注册菜单命令失败:', error);
}
}
// 初始化
async function init() {
// 等待页面完全加载
if (document.readyState !== 'complete') {
window.addEventListener('load', init);
return;
}
// 初始化配置
await initConfig();
createIcon();
// 设置快捷键
const keySetupSuccess = keyManager.setup();
// 在页面获得焦点时重新注册快捷键
window.addEventListener('focus', () => keyManager.setup());
// 注册菜单命令
registerMenuCommands();
console.log('AI Page Summarizer Pro 初始化完成');
console.log('快捷键:', config.shortcut);
// 在页面加载完成后测试快捷键
setTimeout(() => {
keyManager.test();
console.log('快捷键使用提示: 按下 ' + config.shortcut + ' 触发网页总结,或右键点击图标打开设置');
console.log('也可以通过控制台调用 window.activateSummary() 直接触发');
}, 2000);
}
// 启动应用
waitForDependencies(() => init().catch(console.error));
})();