// ==UserScript== // @name AI 网页总结助手 Pro // @namespace https://github.com/cyx0118/ai_summary_helper-pro // @version 2.2.0 // @description 一键总结网页,侧栏显示,快捷输出,多语言,Q&A问答,导出,深色浅色模式 // @author 超级小忍者 // @match *://*/* // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @grant GM_addStyle // @grant GM_xmlhttpRequest // @connect api.openai.com // @connect api.deepseek.com // @connect * // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/576238/AI%20%E7%BD%91%E9%A1%B5%E6%80%BB%E7%BB%93%E5%8A%A9%E6%89%8B%20Pro.user.js // @updateURL https://update.greasyfork.icu/scripts/576238/AI%20%E7%BD%91%E9%A1%B5%E6%80%BB%E7%BB%93%E5%8A%A9%E6%89%8B%20Pro.meta.js // ==/UserScript== (function () { 'use strict'; const DEFAULT_CONFIG = { shortcutKey: 'q', useCtrl: true, useShift: false, useAlt: false, apiKey: '', apiEndpoint: 'https://api.deepseek.com/v1/chat/completions', modelName: 'deepseek-chat', maxContentLength: 8000, summaryLanguage: 'zh-CN', summaryLength: 'medium', summaryTone: 'neutral', sidebarWidth: 440, enableStreaming: true, theme: 'system', systemPrompt: '你是一个网页内容分析助手。直接输出总结内容,不要添加任何客套话、问候语或引言。直接用 Markdown 格式输出结构化总结。', }; const LANG_OPTIONS = [ { value: 'zh-CN', label: '中文' }, { value: 'en', label: 'English' }, { value: 'ja', label: '日本語' }, { value: 'ko', label: '한국어' }, { value: 'fr', label: 'Français' }, { value: 'de', label: 'Deutsch' }, { value: 'es', label: 'Español' }, { value: 'ru', label: 'Русский' }, { value: 'auto', label: '与原文相同' }, ]; const LENGTH_OPTIONS = [ { value: 'brief', label: '简洁', tokens: 300 }, { value: 'medium', label: '中等', tokens: 800 }, { value: 'detailed', label: '详细', tokens: 2000 }, ]; const TONE_OPTIONS = [ { value: 'neutral', label: '中性客观' }, { value: 'professional', label: '专业严谨' }, { value: 'casual', label: '轻松通俗' }, { value: 'academic', label: '学术风格' }, { value: 'child', label: '小学生能懂' }, { value: 'humorous', label: '幽默风格' }, { value: 'bullet', label: '要点列举' }, ]; function getConfig() { const s = GM_getValue('ai_summary_config', null); if (!s) return { ...DEFAULT_CONFIG }; try { return { ...DEFAULT_CONFIG, ...JSON.parse(s) }; } catch { return { ...DEFAULT_CONFIG }; } } function saveConfig(c) { GM_setValue('ai_summary_config', JSON.stringify(c)); } function getLangLabel(c) { const f = LANG_OPTIONS.find(l => l.value === c); return f ? f.label : c; } function getLengthInfo(v) { return LENGTH_OPTIONS.find(l => l.value === v) || LENGTH_OPTIONS[1]; } function getToneLabel(v) { const f = TONE_OPTIONS.find(t => t.value === v); return f ? f.label : '中性客观'; } // ===================== 主题管理 ===================== function getEffectiveTheme() { const cfg = getConfig(); if (cfg.theme === 'system') { return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } return cfg.theme; } function applyTheme() { const t = getEffectiveTheme(); document.documentElement.setAttribute('ai-theme', t); } // 监听系统主题变化 window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { if (getConfig().theme === 'system') applyTheme(); }); // ===================== 样式 ===================== GM_addStyle(` /* ===== 主题变量 ===== */ :root, [ai-theme="light"] { --ai-bg: rgba(255,255,255,.72); --ai-bg-solid: #fff; --ai-bg-card: rgba(248,249,250,.85); --ai-bg-input: rgba(255,255,255,.9); --ai-bg-header: rgba(250,251,252,.6); --ai-bg-hover: rgba(241,243,244,.9); --ai-bg-btn: rgba(241,243,244,.8); --ai-border: rgba(232,234,237,.8); --ai-border-light: rgba(255,255,255,.5); --ai-border-card: rgba(255,255,255,.4); --ai-border-input: rgba(218,220,224,.8); --ai-text: #202124; --ai-text-heading: #1a1a2e; --ai-text-secondary: #5f6368; --ai-text-muted: #80868b; --ai-text-label: #3c4043; --ai-overlay: rgba(0,0,0,.3); --ai-shadow: 0 20px 60px rgba(0,0,0,.25); --ai-shadow-sidebar: -4px 0 24px rgba(0,0,0,.15); --ai-error-bg: rgba(252,232,230,.9); --ai-error-border: rgba(245,198,203,.8); --ai-error-text: #c62828; --ai-privacy-bg: rgba(255,248,225,.8); --ai-privacy-border: rgba(255,224,130,.5); --ai-toast-bg: rgba(50,50,50,.9); --ai-chat-user-bg: rgba(102,126,234,.12); --ai-chat-user-border: rgba(102,126,234,.2); --ai-chat-ai-bg: rgba(248,249,250,.85); --ai-chat-ai-border: rgba(255,255,255,.4); --ai-scroll-thumb: rgba(0,0,0,.15); --ai-scroll-track: transparent; } [ai-theme="dark"] { --ai-bg: rgba(30,30,35,.82); --ai-bg-solid: #1e1e23; --ai-bg-card: rgba(40,42,50,.85); --ai-bg-input: rgba(45,47,55,.9); --ai-bg-header: rgba(35,37,45,.7); --ai-bg-hover: rgba(55,57,68,.9); --ai-bg-btn: rgba(50,52,62,.8); --ai-border: rgba(60,62,72,.8); --ai-border-light: rgba(50,52,60,.5); --ai-border-card: rgba(60,62,72,.6); --ai-border-input: rgba(70,72,82,.8); --ai-text: #e0e0e0; --ai-text-heading: #f0f0f0; --ai-text-secondary: #aaa; --ai-text-muted: #888; --ai-text-label: #ccc; --ai-overlay: rgba(0,0,0,.55); --ai-shadow: 0 20px 60px rgba(0,0,0,.5); --ai-shadow-sidebar: -4px 0 24px rgba(0,0,0,.4); --ai-error-bg: rgba(60,30,30,.85); --ai-error-border: rgba(100,50,50,.6); --ai-error-text: #ff8a80; --ai-privacy-bg: rgba(50,45,30,.7); --ai-privacy-border: rgba(100,80,40,.5); --ai-toast-bg: rgba(40,40,45,.95); --ai-chat-user-bg: rgba(102,126,234,.18); --ai-chat-user-border: rgba(102,126,234,.3); --ai-chat-ai-bg: rgba(40,42,50,.85); --ai-chat-ai-border: rgba(60,62,72,.5); --ai-scroll-thumb: rgba(255,255,255,.15); --ai-scroll-track: transparent; } /* 滚动条 */ #ai-summary-sidebar ::-webkit-scrollbar { width: 6px; } #ai-summary-sidebar ::-webkit-scrollbar-thumb { background: var(--ai-scroll-thumb); border-radius: 3px; } #ai-summary-sidebar ::-webkit-scrollbar-track { background: var(--ai-scroll-track); } /* ===== 浮动按钮 ===== */ #ai-summary-settings-btn{position:fixed;bottom:20px;right:20px;width:48px;height:48px;border-radius:50%;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);color:#fff;border:none;cursor:pointer;font-size:22px;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 15px rgba(102,126,234,.4);z-index:999998;transition:transform .2s,box-shadow .2s,right .35s cubic-bezier(.4,0,.2,1);user-select:none} #ai-summary-settings-btn:hover{transform:scale(1.1);box-shadow:0 6px 20px rgba(102,126,234,.6)} #ai-summary-settings-btn.sidebar-open{right:460px} /* ===== 侧栏 ===== */ #ai-summary-sidebar{position:fixed;top:0;right:0;height:100vh;z-index:999999;display:flex;flex-direction:column;background:var(--ai-bg);backdrop-filter:blur(24px) saturate(180%);-webkit-backdrop-filter:blur(24px) saturate(180%);border-left:1px solid var(--ai-border-light);box-shadow:var(--ai-shadow-sidebar);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;transform:translateX(100%);transition:transform .35s cubic-bezier(.4,0,.2,1);color:var(--ai-text)} #ai-summary-sidebar.open{transform:translateX(0)} #ai-summary-sidebar-resize{position:absolute;top:0;left:-4px;width:8px;height:100%;cursor:col-resize;z-index:10} #ai-summary-sidebar-resize:hover,#ai-summary-sidebar-resize.dragging{background:rgba(102,126,234,.3)} .ai-sb-header{display:flex;align-items:center;justify-content:space-between;padding:14px 20px;border-bottom:1px solid var(--ai-border);flex-shrink:0;background:var(--ai-bg-header)} .ai-sb-header h2{margin:0;font-size:16px;font-weight:600;color:var(--ai-text-heading);display:flex;align-items:center;gap:8px} .ai-sb-header-actions{display:flex;align-items:center;gap:4px} .ai-sb-btn{width:30px;height:30px;border-radius:50%;border:none;background:var(--ai-bg-btn);cursor:pointer;font-size:14px;display:flex;align-items:center;justify-content:center;transition:background .2s;color:var(--ai-text-secondary)} .ai-sb-btn:hover{background:var(--ai-bg-hover)} .ai-sb-info{padding:10px 20px;border-bottom:1px solid var(--ai-border);flex-shrink:0} .ai-sb-info .pt{font-size:13px;font-weight:600;color:var(--ai-text-label);overflow:hidden;text-overflow:ellipsis;white-space:nowrap} .ai-sb-info .pu{font-size:10px;color:var(--ai-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:2px} .ai-sb-tabs{display:flex;border-bottom:1px solid var(--ai-border);flex-shrink:0} .ai-sb-tab{flex:1;padding:10px 0;text-align:center;font-size:13px;font-weight:500;color:var(--ai-text-secondary);cursor:pointer;border-bottom:2px solid transparent;transition:all .2s;background:none;border-top:none;border-left:none;border-right:none} .ai-sb-tab:hover{color:var(--ai-text-heading);background:rgba(102,126,234,.04)} .ai-sb-tab.active{color:#667eea;border-bottom-color:#667eea;font-weight:600} .ai-sb-body{padding:16px 20px;overflow-y:auto;flex:1} .ai-sb-footer{padding:10px 20px;border-top:1px solid var(--ai-border);display:flex;gap:8px;justify-content:flex-end;flex-shrink:0;background:var(--ai-bg-header)} .ai-sb-content{background:var(--ai-bg-card);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border:1px solid var(--ai-border-card);border-radius:10px;padding:16px;font-size:14px;line-height:1.8;color:var(--ai-text);word-break:break-word} .ai-sb-content strong,.ai-sb-content b{color:var(--ai-text-heading)} .ai-sb-content h1,.ai-sb-content h2,.ai-sb-content h3{margin:14px 0 6px 0;color:var(--ai-text-heading)} .ai-sb-content h1{font-size:18px}.ai-sb-content h2{font-size:16px}.ai-sb-content h3{font-size:15px} .ai-sb-content ul,.ai-sb-content ol{margin:6px 0;padding-left:18px} .ai-sb-content li{margin:3px 0} .ai-sb-content code{background:rgba(100,100,120,.2);padding:1px 5px;border-radius:4px;font-size:13px} .ai-sb-content blockquote{border-left:3px solid #667eea;margin:8px 0;padding:4px 14px;color:var(--ai-text-secondary)} .ai-stream-cursor::after{content:'\u2588';animation:ai-blink 1s step-end infinite;color:#667eea} @keyframes ai-blink{50%{opacity:0}} .ai-sb-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:80px 20px;gap:16px} .ai-sb-spinner{width:36px;height:36px;border:3px solid var(--ai-border);border-top-color:#667eea;border-radius:50%;animation:ai-spin .8s linear infinite} @keyframes ai-spin{to{transform:rotate(360deg)}} .ai-sb-loading-text{font-size:13px;color:var(--ai-text-secondary)} .ai-sb-error{background:var(--ai-error-bg);backdrop-filter:blur(8px);border:1px solid var(--ai-error-border);border-radius:10px;padding:14px 16px;color:var(--ai-error-text);font-size:13px;line-height:1.6} .ai-btn{padding:7px 14px;border-radius:6px;border:none;font-size:12px;font-weight:600;cursor:pointer;transition:all .2s} .ai-btn-primary{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;box-shadow:0 2px 8px rgba(102,126,234,.3)} .ai-btn-primary:hover{box-shadow:0 4px 12px rgba(102,126,234,.5);transform:translateY(-1px)} .ai-btn-secondary{background:var(--ai-bg-btn);color:var(--ai-text-secondary)} .ai-btn-ghost{background:var(--ai-bg);color:var(--ai-text-secondary);border:1px solid var(--ai-border-input)} .ai-btn-ghost:hover{background:var(--ai-bg-hover)} .ai-chat-msgs{display:flex;flex-direction:column;gap:12px} .ai-chat-msg{padding:12px 14px;border-radius:10px;font-size:13px;line-height:1.7;word-break:break-word} .ai-chat-msg.user{background:var(--ai-chat-user-bg);border:1px solid var(--ai-chat-user-border);align-self:flex-end;max-width:85%;color:var(--ai-text)} .ai-chat-msg.assistant{background:var(--ai-chat-ai-bg);border:1px solid var(--ai-chat-ai-border);align-self:flex-start;max-width:95%;color:var(--ai-text)} .ai-chat-input-area{display:flex;gap:8px;padding:10px 0 0;border-top:1px solid var(--ai-border);margin-top:12px} .ai-chat-input{flex:1;padding:8px 12px;border:1px solid var(--ai-border-input);border-radius:8px;font-size:13px;outline:none;background:var(--ai-bg-input);color:var(--ai-text);font-family:inherit;resize:none;min-height:36px;max-height:100px} .ai-chat-input:focus{border-color:#667eea;box-shadow:0 0 0 2px rgba(102,126,234,.1)} .ai-chat-send{padding:8px 14px;border-radius:8px;border:none;background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;font-size:13px;cursor:pointer;font-weight:600;align-self:flex-end} /* ===== 设置面板 ===== */ .ai-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:var(--ai-overlay);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);z-index:1000000;display:flex;align-items:center;justify-content:center;animation:ai-fi .2s ease} @keyframes ai-fi{from{opacity:0}to{opacity:1}} .ai-panel{background:var(--ai-bg);backdrop-filter:blur(24px) saturate(180%);-webkit-backdrop-filter:blur(24px) saturate(180%);border:1px solid var(--ai-border-light);border-radius:16px;box-shadow:var(--ai-shadow);max-width:600px;width:92vw;max-height:88vh;display:flex;flex-direction:column;animation:ai-su .3s ease;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;color:var(--ai-text)} @keyframes ai-su{from{transform:translateY(30px);opacity:0}to{transform:translateY(0);opacity:1}} .ai-panel-header{display:flex;align-items:center;justify-content:space-between;padding:18px 24px;border-bottom:1px solid var(--ai-border)} .ai-panel-header h2{margin:0;font-size:18px;font-weight:600;color:var(--ai-text-heading);display:flex;align-items:center;gap:8px} .ai-panel-close{width:34px;height:34px;border-radius:50%;border:none;background:var(--ai-bg-btn);cursor:pointer;font-size:18px;display:flex;align-items:center;justify-content:center;color:var(--ai-text-secondary)} .ai-panel-close:hover{background:var(--ai-bg-hover)} .ai-panel-body{padding:22px 24px;overflow-y:auto;flex:1} .ai-fg{margin-bottom:16px} .ai-fg label{display:block;font-size:13px;font-weight:600;color:var(--ai-text-label);margin-bottom:5px} .ai-fg .desc{font-size:11px;color:var(--ai-text-muted);margin-bottom:6px} .ai-fg input[type="text"],.ai-fg input[type="password"],.ai-fg select,.ai-fg textarea{width:100%;padding:9px 12px;border:1px solid var(--ai-border-input);border-radius:8px;font-size:13px;color:var(--ai-text);outline:none;transition:border-color .2s,box-shadow .2s;box-sizing:border-box;background:var(--ai-bg-input);font-family:inherit} .ai-fg input:focus,.ai-fg select:focus,.ai-fg textarea:focus{border-color:#667eea;box-shadow:0 0 0 3px rgba(102,126,234,.15)} .ai-fg textarea{resize:vertical;min-height:70px;line-height:1.5} .ai-fg .shortcut-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap} .ai-fg .mod-check{display:flex;align-items:center;gap:4px;font-size:12px;color:var(--ai-text-secondary);cursor:pointer} .ai-fg .mod-check input{width:15px;height:15px;accent-color:#667eea} .ai-fg .key-input{width:70px!important;text-align:center;text-transform:uppercase;font-weight:600;font-size:15px;letter-spacing:2px} .ai-fg .shortcut-preview{font-size:12px;color:#667eea;font-weight:500;padding:3px 8px;background:rgba(102,126,234,.12);border-radius:6px} .ai-btn-group{display:flex;gap:10px;justify-content:flex-end;padding-top:6px} .ai-divider{height:1px;background:var(--ai-border);margin:14px 0} .ai-privacy{background:var(--ai-privacy-bg);border:1px solid var(--ai-privacy-border);border-radius:8px;padding:10px 14px;font-size:11px;color:var(--ai-text-secondary);line-height:1.5;margin-bottom:14px} .ai-privacy strong{color:#e65100} .ai-toast{position:fixed;top:20px;left:50%;transform:translateX(-50%);background:var(--ai-toast-bg);backdrop-filter:blur(12px);color:#fff;padding:10px 24px;border-radius:8px;font-size:13px;z-index:1000001;animation:ai-toast-in .3s ease;box-shadow:0 4px 12px rgba(0,0,0,.3)} @keyframes ai-toast-in{from{transform:translateX(-50%) translateY(-20px);opacity:0}to{transform:translateX(-50%) translateY(0);opacity:1}} /* 主题切换按钮 */ .ai-theme-btn{font-size:16px;line-height:1} .ai-theme-btn[data-theme="light"]::after{content:'\u2600\uFE0F'} .ai-theme-btn[data-theme="dark"]::after{content:'\uD83C\uDF19'} .ai-theme-btn[data-theme="system"]::after{content:'\uD83D\uDCA1'} `); function showToast(m,d){const e=document.querySelector('.ai-toast');if(e)e.remove();const t=document.createElement('div');t.className='ai-toast';t.textContent=m;document.body.appendChild(t);setTimeout(()=>t.remove(),d||2000)} function getShortcutText(c){const p=[];if(c.useCtrl)p.push('Ctrl');if(c.useShift)p.push('Shift');if(c.useAlt)p.push('Alt');p.push(c.shortcutKey.toUpperCase());return p.join(' + ')} function esc(s){return s.replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"')} function md(text){let h=text.replace(/&/g,'&').replace(//g,'>');h=h.replace(/^### (.+)$/gm,'

$1

').replace(/^## (.+)$/gm,'

$1

').replace(/^# (.+)$/gm,'

$1

');h=h.replace(/\*\*(.+?)\*\*/g,'$1').replace(/\*(.+?)\*/g,'$1');h=h.replace(/`([^`]+)`/g,'$1').replace(/^> (.+)$/gm,'
$1
');h=h.replace(/^\- (.+)$/gm,'
  • $1
  • ').replace(/^\* (.+)$/gm,'
  • $1
  • ');h=h.replace(/(
  • .*<\/li>\n?)+/g,'').replace(/^\d+\. (.+)$/gm,'
  • $1
  • ');h=h.replace(/\n\n/g,'

    ').replace(/\n/g,'
    ');return h} // ===================== 内容提取 ===================== function extractContent(){const sels=['article','[itemprop="articleBody"]','[data-testid="article-body"]','main','[role="main"]','.post-content','.article-content','.entry-content','.article-body','.post-body','.story-body','.markdown-body','.content-body','.page-content','.text-content','#content','#main-content','#article-content','.post','.article','.story'];let el=null;for(const s of sels){for(const c of document.querySelectorAll(s)){if(c.innerText.trim().length>200){el=c;break}}if(el)break}if(!el)el=document.body;const cl=el.cloneNode(true);['script','style','nav','header','footer','iframe','noscript','.ad','.ads','.advertisement','.ad-container','[class*="ad-"]','.sidebar','.side-bar','.widget','.widget-area','.comment','.comments','.comment-section','#comments','.social-share','.share-buttons','.related-posts','.related-articles','.navigation','.nav','.menu','.breadcrumb','.breadcrumbs','.cookie-banner','.cookie-notice','.popup','.modal','[role="banner"]','[role="navigation"]','[role="complementary"]','[aria-hidden="true"]','.sr-only','.visually-hidden','.newsletter','.subscribe','.signup','.paywall'].forEach(s=>{try{cl.querySelectorAll(s).forEach(e=>e.remove())}catch(x){}});let t=cl.innerText||cl.textContent||'';return t.replace(/\s+/g,' ').replace(/\n\s*\n/g,'\n').trim()} // ===================== API ===================== function buildSummaryPrompt(content,config){const url=location.href,title=document.title;const tc=content.substring(0,config.maxContentLength);const lang=config.summaryLanguage==='auto'?'':`请使用${getLangLabel(config.summaryLanguage)}回答。`;const tone=getToneLabel(config.summaryTone);const lenMap={brief:'用3句话高度概括核心内容。',medium:'用结构化格式总结,包含主题概述、3-5个关键要点、核心观点、简要评价。',detailed:'进行全面深入的总结分析,包含:主题概述、详细要点分析、核心观点与建议、数据/案例提取、优缺点评价。'};return{system:config.systemPrompt,user:`${lang}请以"${tone}"的语气,${lenMap[config.summaryLength]||lenMap.medium}\n\n网页标题:${title}\n网页地址:${url}\n\n---\n${tc}`,maxTokens:getLengthInfo(config.summaryLength).tokens||800}} function buildQAPrompt(content,config,ch){const tc=content.substring(0,config.maxContentLength);const lang=config.summaryLanguage==='auto'?'':`请使用${getLangLabel(config.summaryLanguage)}回答。`;const msgs=[{role:'system',content:`你是一个网页内容问答助手。基于以下网页内容回答用户问题。如果问题与网页内容无关,请说明。${lang}\n\n【网页内容】\n${tc}`}];ch.forEach(m=>msgs.push(m));return{messages:msgs,maxTokens:1500}} function simulateStream(text,onChunk,onDone,speed){const chars=text.split('');let idx=0;const interval=speed||12;function tick(){idx=Math.min(idx+Math.ceil(chars.length/60),chars.length);onChunk(chars.slice(0,idx).join(''));if(idx>=chars.length){onDone(text);return}setTimeout(tick,interval)}tick()} function callAPI(requestBody,config,onChunk,onDone,onErr){delete requestBody.stream;GM_xmlhttpRequest({method:'POST',url:config.apiEndpoint,headers:{'Content-Type':'application/json','Authorization':'Bearer '+config.apiKey},data:JSON.stringify(requestBody),timeout:120000,onload:function(resp){let result='';try{const text=resp.responseText||'';if(text.indexOf('data:')!==-1&&text.indexOf('"choices"')!==-1){const lines=text.split('\n');for(const line of lines){const tr=line.trim();if(!tr||!tr.startsWith('data:'))continue;const ds=tr.substring(5).trim();if(ds==='[DONE]')continue;try{const d=JSON.parse(ds);const delta=d.choices&&d.choices[0]&&d.choices[0].delta;if(delta&&delta.content)result+=delta.content}catch(e){}}if(result){if(config.enableStreaming)simulateStream(result,onChunk,onDone,10);else onDone(result);return}}const d=JSON.parse(text);if(d.choices&&d.choices[0]){result=d.choices[0].message?d.choices[0].message.content:(d.choices[0].text||'');if(config.enableStreaming&&result.length>20)simulateStream(result,onChunk,onDone,10);else onDone(result)}else if(d.error)onErr(new Error(d.error.message||JSON.stringify(d.error)));else onErr(new Error('API 返回格式异常'))}catch(e){if(result)onDone(result);else onErr(new Error('解析响应失败'))}},onerror:function(){onErr(new Error('网络请求失败'))},ontimeout:function(){onErr(new Error('请求超时'))}})} // ===================== 侧栏 ===================== let sidebar=null,summaryCache=null,chatHistory=[]; function closeSidebar(){if(sidebar){sidebar.classList.remove('open');const b=document.getElementById('ai-summary-settings-btn');if(b)b.classList.remove('sidebar-open')}} function openSidebar(){if(sidebar){sidebar.classList.add('open');const b=document.getElementById('ai-summary-settings-btn');if(b)b.classList.add('sidebar-open');return}createSidebar()} function cycleTheme(){ const cfg=getConfig(); const order=['light','dark','system']; const idx=order.indexOf(cfg.theme); cfg.theme=order[(idx+1)%3]; saveConfig(cfg); applyTheme(); // 更新按钮 const btn=document.getElementById('ai-theme-toggle'); if(btn)btn.setAttribute('data-theme',cfg.theme); const map={light:'浅色',dark:'深色',system:'跟随系统'}; showToast(`🎨 主题:${map[cfg.theme]}`); } function createSidebar(){const config=getConfig();applyTheme();const el=document.createElement('div');el.id='ai-summary-sidebar';el.style.width=config.sidebarWidth+'px';el.innerHTML=`

    🤖 AI 助手

    ${esc(document.title)}
    ${esc(location.href)}
    `;document.body.appendChild(el);sidebar=el;requestAnimationFrame(()=>{el.classList.add('open');const b=document.getElementById('ai-summary-settings-btn');if(b)b.classList.add('sidebar-open')});document.getElementById('ai-sb-close').onclick=closeSidebar;document.getElementById('ai-sb-refresh').onclick=()=>{summaryCache=null;runSummary()};document.getElementById('ai-fb-copy').onclick=()=>{copyResult()};document.getElementById('ai-fb-md').onclick=()=>{exportMD()};document.getElementById('ai-fb-pdf').onclick=()=>{exportPDF()};document.getElementById('ai-theme-toggle').onclick=cycleTheme;el.querySelectorAll('.ai-sb-tab').forEach(tab=>{tab.onclick=()=>{el.querySelectorAll('.ai-sb-tab').forEach(t=>t.classList.remove('active'));tab.classList.add('active');if(tab.dataset.mode==='summary')runSummary();else showQA()}});setupResize(el);runSummary()} function runSummary(){const area=document.getElementById('ai-sb-content'),footer=document.getElementById('ai-sb-footer');if(!area)return;const config=getConfig();const ck=`${location.href}_${config.summaryLength}_${config.summaryLanguage}_${config.summaryTone}`;if(summaryCache&&summaryCache.ck===ck){displayResult(summaryCache.result,area,footer);return}area.innerHTML='
    正在分析网页内容...
    ';if(footer)footer.style.display='none';const content=extractContent();if(!content||content.length<50){area.innerHTML='
    ⚠️ 无法提取到足够的网页内容
    ';return}const prompt=buildSummaryPrompt(content,config);const reqBody={model:config.modelName,messages:[{role:'system',content:prompt.system},{role:'user',content:prompt.user}],temperature:0.3,max_tokens:prompt.maxTokens};if(config.enableStreaming){area.innerHTML='
    ';callAPI(reqBody,config,(text)=>{const s=document.getElementById('ai-stream-el');if(s)s.innerHTML=md(text)},(result)=>{summaryCache={ck,result};displayResult(result,area,footer)},(err)=>{area.innerHTML=`
    ❌ ${esc(err.message)}
    `})}else{callAPI(reqBody,config,()=>{},(result)=>{summaryCache={ck,result};displayResult(result,area,footer)},(err)=>{area.innerHTML=`
    ❌ ${esc(err.message)}
    `})}} function displayResult(result,area,footer){area.innerHTML=`
    ${md(result)}
    `;if(footer)footer.style.display='flex';sidebar._result=result} function showQA(){const area=document.getElementById('ai-sb-content'),footer=document.getElementById('ai-sb-footer');if(!area)return;if(footer)footer.style.display='none';chatHistory=[];area.innerHTML=`
    👋 基于当前网页内容向我提问吧!
    `;const input=document.getElementById('ai-chat-input');const doSend=()=>{const q=input.value.trim();if(!q)return;input.value='';input.style.height='auto';sendQA(q)};document.getElementById('ai-chat-send').onclick=doSend;input.onkeydown=(e)=>{if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();doSend()}};input.oninput=()=>{input.style.height='auto';input.style.height=Math.min(input.scrollHeight,100)+'px'}} function sendQA(question){const msgsEl=document.getElementById('ai-chat-msgs');if(!msgsEl)return;const config=getConfig();msgsEl.innerHTML+=`
    ${esc(question)}
    `;chatHistory.push({role:'user',content:question});const aDiv=document.createElement('div');aDiv.className='ai-chat-msg assistant';aDiv.textContent='思考中...';msgsEl.appendChild(aDiv);msgsEl.scrollTop=msgsEl.scrollHeight;const content=extractContent();const prompt=buildQAPrompt(content,config,chatHistory);const reqBody={model:config.modelName,messages:prompt.messages,temperature:0.3,max_tokens:prompt.maxTokens};callAPI(reqBody,config,(text)=>{aDiv.innerHTML=md(text);msgsEl.scrollTop=msgsEl.scrollHeight},(result)=>{aDiv.innerHTML=md(result);chatHistory.push({role:'assistant',content:result});msgsEl.scrollTop=msgsEl.scrollHeight},(err)=>{aDiv.textContent='❌'+err.message})} function copyResult(){const t=sidebar._result||'';if(!t){showToast('没有可复制的内容');return}navigator.clipboard.writeText(t).then(()=>showToast('✅ 已复制')).catch(()=>{const ta=document.createElement('textarea');ta.value=t;document.body.appendChild(ta);ta.select();document.execCommand('copy');ta.remove();showToast('✅ 已复制')})} function exportMD(){const t=sidebar._result||'';if(!t){showToast('没有可导出的内容');return}const b=new Blob([t],{type:'text/markdown;charset=utf-8'});const u=URL.createObjectURL(b);const a=document.createElement('a');a.href=u;a.download=`summary-${document.title.replace(/[^\w\u4e00-\u9fff]/g,'_').substring(0,50)}.md`;a.click();URL.revokeObjectURL(u);showToast('✅ Markdown 已下载')} function exportPDF(){const t=sidebar._result||'';if(!t){showToast('没有可导出的内容');return}const h=`总结

    📄 网页总结

    来源:${esc(document.title)}
    URL:${esc(location.href)}
    时间:${new Date().toLocaleString()}

    ${md(t)}`;const w=window.open('','_blank');w.document.write(h);w.document.close();setTimeout(()=>w.print(),500);showToast('✅ 已打开打印窗口')} function setupResize(el){const h=document.getElementById('ai-summary-sidebar-resize');if(!h)return;h.onmousedown=(e)=>{e.preventDefault();const sx=e.clientX,sw=el.offsetWidth;h.classList.add('dragging');const mv=(e)=>{el.style.width=Math.min(Math.max(sx-e.clientX+sw,320),800)+'px'};const up=()=>{h.classList.remove('dragging');document.removeEventListener('mousemove',mv);document.removeEventListener('mouseup',up);const c=getConfig();c.sidebarWidth=el.offsetWidth;saveConfig(c)};document.addEventListener('mousemove',mv);document.addEventListener('mouseup',up)}} // ===================== 设置面板 ===================== function createSettings(){const config=getConfig();document.querySelectorAll('.ai-overlay').forEach(e=>e.remove());const ov=document.createElement('div');ov.className='ai-overlay';ov.innerHTML=`

    ⚙️ AI 助手设置

    🔒 隐私声明:网页内容将发送至您配置的 AI API 服务器处理。插件不收集/存储数据,配置保存在本地浏览器。
    支持 OpenAI / DeepSeek / Claude 等兼容接口
    模拟逐字显示,减少等待感
    切换深色/浅色模式,或跟随系统
    自定义 AI 角色和行为
    +
    ${getShortcutText(config)}
    `;document.body.appendChild(ov);document.getElementById('ai-set-close').onclick=()=>ov.remove();ov.onclick=(e)=>{if(e.target===ov)ov.remove()};const ms=document.getElementById('s-model');const cg=document.getElementById('s-custom-grp');if(ms.value==='custom')cg.style.display='block';ms.onchange=()=>{cg.style.display=ms.value==='custom'?'block':'none'};document.getElementById('s-reset-prompt').onclick=()=>{document.getElementById('s-prompt').value=DEFAULT_CONFIG.systemPrompt;showToast('✅ 已恢复默认提示词')};const up=()=>{const c={useCtrl:document.getElementById('s-ctrl').checked,useShift:document.getElementById('s-shift').checked,useAlt:document.getElementById('s-alt').checked,shortcutKey:document.getElementById('s-key-input').value||'Q'};document.getElementById('s-preview').textContent=getShortcutText(c)};['s-ctrl','s-shift','s-alt','s-key-input'].forEach(id=>{document.getElementById(id).addEventListener('input',up);document.getElementById(id).addEventListener('change',up)});document.getElementById('s-key-input').addEventListener('input',(e)=>{e.target.value=e.target.value.replace(/[^a-zA-Z]/g,'').substring(0,1)});document.getElementById('s-reset').onclick=()=>{if(confirm('确定恢复所有设置为默认值吗?')){saveConfig(DEFAULT_CONFIG);ov.remove();applyTheme();showToast('✅ 已恢复默认');setTimeout(()=>createSettings(),300)}};document.getElementById('s-save').onclick=()=>{const nc={apiKey:document.getElementById('s-key').value.trim(),apiEndpoint:document.getElementById('s-endpoint').value.trim(),modelName:ms.value==='custom'?document.getElementById('s-custom').value.trim():ms.value,maxContentLength:parseInt(document.getElementById('s-maxlen').value)||8000,enableStreaming:document.getElementById('s-stream').checked,theme:document.getElementById('s-theme').value,summaryLanguage:document.getElementById('s-lang').value,summaryLength:document.getElementById('s-length').value,summaryTone:document.getElementById('s-tone').value,systemPrompt:document.getElementById('s-prompt').value.trim()||DEFAULT_CONFIG.systemPrompt,shortcutKey:(document.getElementById('s-key-input').value||'q').toLowerCase(),useCtrl:document.getElementById('s-ctrl').checked,useShift:document.getElementById('s-shift').checked,useAlt:document.getElementById('s-alt').checked,sidebarWidth:config.sidebarWidth};if(!nc.apiKey){showToast('⚠️ 请输入 API Key');return}if(!nc.apiEndpoint){showToast('⚠️ 请输入 API 端点');return}saveConfig(nc);ov.remove();summaryCache=null;applyTheme();showToast('✅ 已保存,快捷键:'+getShortcutText(nc))}} // ===================== 初始化 ===================== function createBtn(){if(document.getElementById('ai-summary-settings-btn'))return;const b=document.createElement('button');b.id='ai-summary-settings-btn';b.title='AI 助手设置';b.textContent='⚙️';document.body.appendChild(b);b.onclick=()=>createSettings()} function regShortcut(){document.addEventListener('keydown',(e)=>{const c=getConfig();const km=e.key.toLowerCase()===c.shortcutKey.toLowerCase();const cm=c.useCtrl?(e.ctrlKey||e.metaKey):(!e.ctrlKey&&!e.metaKey);const sm=c.useShift?e.shiftKey:!e.shiftKey;const am=c.useAlt?e.altKey:!e.altKey;if(km&&cm&&sm&&am){const tag=e.target.tagName.toLowerCase();if(tag==='input'||tag==='textarea'||tag==='select'||e.target.isContentEditable)return;e.preventDefault();e.stopPropagation();if(sidebar&&sidebar.classList.contains('open'))closeSidebar();else{if(!c.apiKey){showToast('⚠️ 请先设置 API Key');createSettings();return}openSidebar()}}if(e.key==='Escape')closeSidebar()})} GM_registerMenuCommand('⚙️ 设置',()=>createSettings()); GM_registerMenuCommand('🤖 总结当前页面',()=>{const c=getConfig();if(!c.apiKey){showToast('⚠️ 请先设置 API Key');createSettings();return}openSidebar()}); function init(){applyTheme();createBtn();regShortcut();console.log('[AI 助手] 已加载,快捷键:'+getShortcutText(getConfig()))} if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);else init(); })();