然后让GEMINI也写,然后再交叉验证然后再把两个人的融合。基于现有的别人的一个插件,出来了这个,你们试试

// ==UserScript==// @name         ChatGPT 智商检测器 · 始终展开版(右下角)// @namespace    http://tampermonkey.net/// @version      1.2.2// @description  右下角固定展开:显示是否降级 + 启发式质量评分(TTR/重复/套话/关键词重叠/结构),含设置与性能优化;模型名多来源提取(SSE/请求体/DOM)。// @author       You + NBAI// @license      GPL-3.0// @match        https://chatgpt.com/*// @match        https://*.chatgpt.com/*// @match        https://chat.openai.com/*// @match        https://*.openai.com/*// @grant        none// @run-at       document-start// ==/UserScript==(function () {  'use strict';  // 仅在 ChatGPT/OpenAI 页面运行  const HOST_OK = /(^|\.)chatgpt\.com$|(^|\.)openai\.com$/i.test(location.hostname);  if (!HOST_OK) return;  /* ---------------- 配置(持久化) ---------------- */  const DEFAULT_CFG = {    enableTTR: true, enableNgram: true, enableFillers: true, enableOverlap: true,    preset: 'qa', scoreWarn: 60,    tokenSampleEachSide: 1200, longTokenThreshold: 2400  };  function loadCfg(){ try{return {...DEFAULT_CFG, ...JSON.parse(localStorage.getItem('iq_cfg')||'{}')}}catch{return {...DEFAULT_CFG}}}  function saveCfg(cfg){ localStorage.setItem('iq_cfg', JSON.stringify(cfg)); }  let CFG = loadCfg();  function tuneThresholdsByPreset(){    const p=CFG.preset;    if(p==='writing') return { ttrMin:.40, repTrigger:.16, fillerMin:.012, overlapMin:.04 };    if(p==='code')    return { ttrMin:.25, repTrigger:.30, fillerMin:.020, overlapMin:.02 };    return             { ttrMin:.35, repTrigger:.18, fillerMin:.015, overlapMin:.06 };  }  /* ---------------- 多语言 ---------------- */  const languages = {    'zh-CN': { title:'ChatGPT 检测器', normal:'智商正常', downgraded:'检测到降智(模型或能力降级)', model:'模型',      author:'made by NBAI + enhanced', score:'质量分', tokens:'词数', sentences:'句子', ttr:'TTR',      ngram:'n-gram重复率(2/3取max)', filler:'套话/填充密度', overlap:'与问题关键词重叠',      status_good:'良好', status_warn:'注意', status_bad:'警示', settings:'设置', save:'保存',      preset:'预设', preset_qa:'问答', preset_writing:'写作', preset_code:'代码', guard:'分数警戒' },    'en': { title:'ChatGPT Detector', normal:'IQ Normal', downgraded:'IQ Downgraded (model/capability)', model:'Model',      author:'made by NBAI + enhanced', score:'Quality', tokens:'Tokens', sentences:'Sentences', ttr:'TTR',      ngram:'n-gram repetition (max of 2/3)', filler:'Filler density', overlap:'Keyword overlap',      status_good:'Good', status_warn:'Caution', status_bad:'Alert', settings:'Settings', save:'Save',      preset:'Preset', preset_qa:'Q&A', preset_writing:'Writing', preset_code:'Code', guard:'Warn threshold' }  };  let currentLanguage = (document.documentElement.lang||'').toLowerCase().startsWith('zh')?'zh-CN':'en';  const getText = k => (languages[currentLanguage] && languages[currentLanguage][k]) || languages['en'][k];  function detectLanguageFromHeaders(headers){    try{      let v='';      if(headers && headers.get) v = headers.get('oai-language')||'';      else if(headers && typeof headers==='object') v = headers['oai-language']||headers['Oai-Language']||headers['OAI-Language']||'';      if(String(v).toLowerCase()==='zh-cn') currentLanguage='zh-CN';      else if(v) currentLanguage='en';    }catch{}  }  /* ---------------- 样式(始终展开:取消缩放动画/小圆点状态) ---------------- */  function injectStyles(){    const style=document.createElement('style');    style.textContent=`      .iq-notification{        position:fixed!important; bottom:20px!important; right:20px!important;        z-index:999999!important; width:340px!important; height:auto!important;        border-radius:16px!important; color:#fff; font-size:14px; display:flex;        align-items:flex-start; justify-content:stretch; overflow:hidden; cursor:default;        box-sizing:border-box!important; transform:none; transition:none;        font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','Roboto','Inter',sans-serif;        backdrop-filter:blur(18px);        box-shadow:0 8px 32px rgba(0,0,0,.12),0 4px 16px rgba(0,0,0,.08),inset 0 1px 0 rgba(255,255,255,.08);        border:1px solid rgba(255,255,255,.1);        padding:14px 16px;      }      .iq-notification::before{content:'';position:absolute;inset:0;border-radius:inherit;        background:linear-gradient(135deg,rgba(255,255,255,.12),rgba(255,255,255,.06));pointer-events:none}      .iq-notification.normal{background:linear-gradient(135deg,rgba(16,185,129,.9),rgba(5,150,105,.9),rgba(6,182,212,.8))}      .iq-notification.downgraded{background:linear-gradient(135deg,rgba(239,68,68,.9),rgba(220,38,38,.9),rgba(251,113,133,.8))}      /* 始终展开:去掉小圆点/展开态差异 */      .iq-notification .circle-icon{display:none}      .iq-notification .expanded-content{display:block;width:100%}      .iq-notification .close-btn{        position:absolute; top:8px; right:8px; width:24px; height:24px; border-radius:50%;        background:rgba(255,255,255,.1); border:1px solid rgba(255,255,255,.2); color:#fff;        font-size:14px; display:flex; align-items:center; justify-content:center; cursor:pointer;        opacity:.75; transition:all .2s ease; backdrop-filter:blur(10px)      }      .iq-notification .close-btn:hover{ opacity:1; transform:scale(1.05) }      .iq-title{font-size:12px;letter-spacing:.5px;text-transform:uppercase;margin:0 0 8px 0;        background:linear-gradient(90deg,rgba(255,255,255,.9),rgba(255,255,255,.7));-webkit-background-clip:text;-webkit-text-fill-color:transparent;font-weight:700}      .iq-row{display:flex;align-items:center;gap:10px;margin:6px 0;flex-wrap:wrap}      .iq-badge{display:inline-flex;align-items:center;gap:6px;padding:2px 8px;border-radius:999px;background:rgba(255,255,255,.16);font-weight:700;font-size:12px}      .iq-badge.good{background:#d1fae5;color:#065f46} .iq-badge.warn{background:#fef3c7;color:#92400e} .iq-badge.bad{background:#fee2e2;color:#991b1b}      .iq-kv{display:flex;justify-content:space-between;gap:10px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace}      .iq-kv + .iq-kv{margin-top:4px} .iq-k{opacity:.75} .iq-v{font-weight:700}      .iq-reasons{margin-top:8px;padding:8px;border-radius:8px;background:rgba(0,0,0,.08);border:1px solid rgba(255,255,255,.12);font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px;line-height:1.5;white-space:pre-wrap;display:block}      .iq-author{font-size:10px;opacity:.6;margin-top:6px;text-align:center;font-style:italic}      .iq-settings-btn{margin-top:8px;padding:6px 10px;border-radius:10px;border:1px solid rgba(255,255,255,.2);background:rgba(255,255,255,.12);color:#fff;cursor:pointer;font-size:12px}      .iq-settings{margin-top:8px;padding:8px;border-radius:8px;background:rgba(0,0,0,.06);border:1px dashed rgba(255,255,255,.2);display:none}      .iq-settings label{margin-right:10px;display:inline-flex;align-items:center;gap:4px}      .iq-settings select,.iq-settings input[type="number"]{padding:2px 6px;border-radius:6px;border:1px solid rgba(0,0,0,.15)}      .iq-settings .row{margin:6px 0;display:flex;align-items:center;gap:8px;flex-wrap:wrap}      .iq-save-btn{margin-left:8px;padding:4px 10px;border-radius:8px;border:1px solid rgba(0,0,0,.15);background:#fff;color:#333;cursor:pointer;font-size:12px}    `;    document.head.appendChild(style);  }  /* ---------------- 质量分析 ---------------- */  function tokenizeMixed(t){const out=[];const re=/[\p{Script=Han}]{1}|[A-Za-z0-9]+(?:'[A-Za-z0-9]+)?/gu;let m;while((m=re.exec(t))!==null)out.push(m[0].toLowerCase());return out;}  function sentenceSplit(t){return t.split(/[.!?。!?;;]\s*/).map(s=>s.trim()).filter(s=>s.length>=4);}  function ngramRepeatRatio(tokens,n=2){if(tokens.length<n*3)return 0;const grams=[];for(let i=0;i+n<=tokens.length;i++)grams.push(tokens.slice(i,i+n).join(' '));const total=grams.length;const uniq=new Set(grams).size;return total?(1-uniq/total):0;}  const FILLERS=[/as an ai/gi,/it is important to/gi,/in general/gi,/however/gi,/please note/gi,/i cannot/gi,/i am not able/gi,/on the other hand/gi,/it's possible that/gi,/作为一个ai/gi,/作为一名ai/gi,/作为语言模型/gi,/需要注意的是/gi,/通常来说/gi,/总体而言/gi,/我无法访问互联网/gi,/无法提供实时/gi,/从某种程度上/gi];  function lexicalDiversity(tokens){if(!tokens.length)return 0;const uniq=new Set(tokens).size;return uniq/tokens.length;}  function fillerDensity(text){let c=0;for(const re of FILLERS)c+=(text.match(re)||[]).length;const tokens=tokenizeMixed(text);return tokens.length?c/tokens.length:0;}  function keywordOverlap(u,a){const stop=new Set(['the','is','are','and','or','to','of','in','on','for','a','an','with','that','this','it','be','as','at','by','from','than','then','等','的','了','和','是','在','与','及','对','或','把','被','而','并','就','也','及其','以及','通过']);const tu=tokenizeMixed(u).filter(w=>!stop.has(w));const ta=tokenizeMixed(a).filter(w=>!stop.has(w));if(!tu.length||!ta.length)return 0;const su=new Set(tu);let inter=0;for(const w of new Set(ta))if(su.has(w))inter++;const union=new Set([...tu,...ta]).size;return union?inter/union:0;}  function analyzeQuality(answer,user=''){    const thr=tuneThresholdsByPreset();    const baseTokens=tokenizeMixed(answer);    const needSample=baseTokens.length>(CFG.longTokenThreshold||2400);    const tokens=needSample?baseTokens.slice(0,CFG.tokenSampleEachSide).concat(baseTokens.slice(-CFG.tokenSampleEachSide)):baseTokens;    const ttr=lexicalDiversity(tokens);    const rep2=ngramRepeatRatio(tokens,2), rep3=ngramRepeatRatio(tokens,3), rep=Math.max(rep2,rep3);    const fill=fillerDensity(answer);    const sent=sentenceSplit(answer).length;    const ovlp=user?keywordOverlap(user,answer):null;    let score=100, reasons=[];    if(CFG.enableTTR && ttr<thr.ttrMin && tokens.length>80){const p=Math.min((thr.ttrMin-ttr)*120,25);score-=p;reasons.push(`TTR=${ttr.toFixed(3)} (<${thr.ttrMin}) (-${p.toFixed(0)})${needSample?' · sample':''}`);}    if(CFG.enableNgram && rep>thr.repTrigger){const p=Math.min((rep-thr.repTrigger)*220,30);score-=p;reasons.push(`n-gram≈${(rep*100).toFixed(1)}% (>${(thr.repTrigger*100).toFixed(0)}%) (-${p.toFixed(0)})${needSample?' · sample':''}`);}    if(CFG.enableFillers && fill>thr.fillerMin){const p=Math.min((fill-thr.fillerMin)*3000,25);score-=p;reasons.push(`fill=${fill.toFixed(3)} (>${thr.fillerMin}) (-${p.toFixed(0)})`);}    if(CFG.enableOverlap && ovlp!==null && ovlp<thr.overlapMin){score-=20;reasons.push(`overlap=${(ovlp*100).toFixed(1)}% (<${(thr.overlapMin*100).toFixed(0)}%) (-20)`);}    if(sent<=1 && tokens.length>60){score-=10;reasons.push('1 段落过长 (-10)');}    score=Math.max(0,Math.min(100,Math.round(score)));    return {score,reasons,ttr,rep,fill,ovlp,tokens:baseTokens.length,sentences:sent};  }  /* ---------------- DOM/气泡 ---------------- */  function escapeHtml(s){return String(s).replaceAll('&','&amp;').replaceAll('<','&lt;').replaceAll('>','&gt;').replaceAll('"','&quot;').replaceAll("'","&#39;");}  function getLastUserText(){const users=[...document.querySelectorAll('[data-message-author-role="user"]')];const u=users[users.length-1];return u?((u.innerText||u.textContent||'').trim()):'';}  function getAssistantNodes(){return [...document.querySelectorAll('[data-message-author-role="assistant"]')];}  let modelSlugMemo=''; let downgradedMemo=null; let lastScorePayload=null; let notif=null;  function ensureNotification(){    if(notif && document.body.contains(notif)) return notif;    notif=document.createElement('div');    // 始终展开:类名直接使用“expanded”效果样式(但我们 CSS 已让 expanded/非expanded一致)    notif.className=`iq-notification normal expanded`;    notif.innerHTML=`      <div class="expanded-content">        <button class="close-btn" title="close">&times;</button>        <div class="iq-title">${getText('title')}</div>        <div class="iq-row">          <span class="iq-badge" id="iq-badge-score">--</span>          <span class="iq-badge" id="iq-badge-model">${getText('model')}: --</span>          <button class="iq-settings-btn" id="iq-settings-btn">⚙️ ${getText('settings')}</button>        </div>        <div id="iq-kvs"></div>        <div class="iq-reasons" id="iq-reasons"></div>        <div class="iq-settings" id="iq-settings"></div>        <div class="iq-author">${getText('author')}</div>      </div>`;    document.body.appendChild(notif);    // 始终展开:禁止折叠/展开切换    notif.addEventListener('click',(e)=>{ /* no-op,保持展开 */ });    const closeBtn=notif.querySelector('.close-btn');    if(closeBtn){      closeBtn.addEventListener('click',(e)=>{        e.stopPropagation(); // 保持可手动关闭        if (notif && notif.parentNode) notif.remove();      });    }    const settingsBtn=notif.querySelector('#iq-settings-btn');    const settingsPanel=notif.querySelector('#iq-settings');    if(settingsBtn && settingsPanel){      settingsBtn.addEventListener('click',(e)=>{        e.stopPropagation();        settingsPanel.style.display = settingsPanel.style.display==='none'?'block':'none';        if(settingsPanel.style.display==='block') renderSettingsPanel(settingsPanel);      });    }    return notif;  }  function renderSettingsPanel(container){    container.innerHTML=`      <div class="row">        <label><input type="checkbox" id="cfg-ttr" ${CFG.enableTTR?'checked':''}> TTR</label>        <label><input type="checkbox" id="cfg-ng"  ${CFG.enableNgram?'checked':''}> n-gram</label>        <label><input type="checkbox" id="cfg-fi"  ${CFG.enableFillers?'checked':''}> ${getText('filler')}</label>        <label><input type="checkbox" id="cfg-ov"  ${CFG.enableOverlap?'checked':''}> ${getText('overlap')}</label>      </div>      <div class="row">        ${getText('preset')}:        <select id="cfg-preset">          <option value="qa" ${CFG.preset==='qa'?'selected':''}>${getText('preset_qa')}</option>          <option value="writing" ${CFG.preset==='writing'?'selected':''}>${getText('preset_writing')}</option>          <option value="code" ${CFG.preset==='code'?'selected':''}>${getText('preset_code')}</option>        </select>        ${getText('guard')}:        <input id="cfg-warn" type="number" min="0" max="100" value="${CFG.scoreWarn}" style="width:72px">        <button class="iq-save-btn" id="cfg-save">${getText('save')}</button>      </div>`;    container.querySelector('#cfg-save').onclick=()=>{      CFG.enableTTR=container.querySelector('#cfg-ttr').checked;      CFG.enableNgram=container.querySelector('#cfg-ng').checked;      CFG.enableFillers=container.querySelector('#cfg-fi').checked;      CFG.enableOverlap=container.querySelector('#cfg-ov').checked;      CFG.preset=container.querySelector('#cfg-preset').value;      CFG.scoreWarn=Math.max(0,Math.min(100,+container.querySelector('#cfg-warn').value||60));      saveCfg(CFG);      scanLatestAssistant(true);    };  }  function updateNotification(){    const el=ensureNotification();    const downgraded=!!(typeof downgradedMemo==='boolean'?downgradedMemo:false);    el.classList.remove('normal','downgraded');    el.classList.add(downgraded?'downgraded':'normal');    const scoreBadge=el.querySelector('#iq-badge-score');    if(scoreBadge){      if(lastScorePayload){        const s=lastScorePayload.score;        const status= s>=80 ? {t:getText('status_good'),c:'good'}                    : s>=CFG.scoreWarn ? {t:getText('status_warn'),c:'warn'}                    : {t:getText('status_bad'),c:'bad'};        scoreBadge.textContent = `${getText('score')} ${s}/100 · ${status.t}`;        scoreBadge.className = `iq-badge ${status.c}`;      }else{        scoreBadge.textContent = `${getText('score')} --`;        scoreBadge.className = `iq-badge`;      }    }    const modelBadge=el.querySelector('#iq-badge-model');    if(modelBadge){      const ms = modelSlugMemo || tryReadModelFromDOM() || '--';      modelBadge.textContent = `${getText('model')}: ${ms}`;    }    const kvs=el.querySelector('#iq-kvs');    if(kvs && lastScorePayload){      const m=lastScorePayload;      kvs.innerHTML=`        <div class="iq-kv"><span class="iq-k">📦 ${getText('tokens')}</span><span class="iq-v">${m.tokens}</span></div>        <div class="iq-kv"><span class="iq-k">✂️ ${getText('sentences')}</span><span class="iq-v">${m.sentences}</span></div>        <div class="iq-kv"><span class="iq-k">🔤 ${getText('ttr')}</span><span class="iq-v">${m.ttr.toFixed(3)}</span></div>        <div class="iq-kv"><span class="iq-k">🔁 ${getText('ngram')}</span><span class="iq-v">${m.rep.toFixed(3)}</span></div>        <div class="iq-kv"><span class="iq-k">🧩 ${getText('filler')}</span><span class="iq-v">${m.fill.toFixed(3)}</span></div>        ${typeof m.ovlp==='number'?`<div class="iq-kv"><span class="iq-k">🔗 ${getText('overlap')}</span><span class="iq-v">${(m.ovlp*100).toFixed(1)}%</span></div>`:''}      `;    }    const reasonsBox=el.querySelector('#iq-reasons');    if(reasonsBox){      if(lastScorePayload && lastScorePayload.reasons.length){        reasonsBox.innerHTML = escapeHtml(lastScorePayload.reasons.map(r=>`• ${r}`).join('\n'));      }else{        reasonsBox.textContent='';      }    }  }  /* ---------------- 模型提取:SSE / 请求体 / DOM ---------------- */  function safeGet(obj, pathArr){ try{ return pathArr.reduce((o,k)=> (o && (k in o)) ? o[k] : undefined, obj); }catch{return undefined;} }  function extractModelFromJson(obj){    const paths = [      ['metadata','model_slug'],      ['model'],      ['response','model'],      ['message','model'],      ['payload','model'],      ['conversation','model'],      ['meta','model'],    ];    for(const p of paths){      const v = safeGet(obj, p);      if(typeof v==='string' && v.trim()) return v.trim();    }    try{      const q=[obj]; const seen=new Set();      while(q.length){        const cur=q.shift();        if(!cur || typeof cur!=='object' || seen.has(cur)) continue;        seen.add(cur);        for(const [k,v] of Object.entries(cur)){          if(k==='model' && typeof v==='string' && v.trim()) return v.trim();          if(v && typeof v==='object') q.push(v);        }      }    }catch{}    return '';  }  function parseStreamData(text){    const lines=text.split('\n'); let model='';    for(const line of lines){      if(!line.startsWith('data: ') || line.includes('[DONE]')) continue;      const jsonStr=line.slice(6).trim(); if(!jsonStr) continue;      try{ const obj=JSON.parse(jsonStr); const m=extractModelFromJson(obj); if(m){ model=m; break; } }catch{}    }    if(model){ return { modelSlug: model, isDowngraded: /^i-/i.test(model) }; }    return { modelSlug:'', isDowngraded:null };  }  function tryReadModelFromDOM(){    const candidates = [      '[data-testid="model-switcher"]',      '[aria-label*="Model"]',      '[aria-label*="模型"]',      'header button[role="combobox"]',    ];    for(const sel of candidates){      const el=document.querySelector(sel);      if(el){        const txt=(el.innerText||el.textContent||'').trim();        if(/gpt|o\d|4o|mini|sonnet|haiku|4\.1|o3|o4|turbo/i.test(txt)) return txt.replace(/\s+/g,' ');      }    }    return '';  }  /* ---- 劫持 fetch/XHR:读 SSE;并从请求体捕获 model ---- */  function interceptFetch(){    if(window.__iq_fetch_patched__) return;    window.__iq_fetch_patched__=true;    const orig=window.fetch;    window.fetch=function(...args){      let [url, options]=args;      const urlStr = typeof url==='string' ? url : (url&&url.url)||'';      const target = /\/backend-api\/(f\/)?conversation/.test(urlStr);      // 从请求体抓 model      try{        if(target && options && options.body){          const bodyText = typeof options.body==='string' ? options.body : (options.body?.toString?.()||'');          if(bodyText && bodyText[0]==='{'){            const obj=JSON.parse(bodyText);            const m=extractModelFromJson(obj);            if(m){ modelSlugMemo=m; downgradedMemo=/^i-/i.test(m); updateNotification(); }          }        }      }catch{}      if(target && options && options.headers) detectLanguageFromHeaders(options.headers);      const p=orig.apply(this,args);      if(!target) return p;      return p.then(response=>{        try{          if(response.ok && response.body){            const cloned=response.clone();            const reader=cloned.body.getReader();            let fullText='';            (function pump(){              return reader.read().then(({done,value})=>{                if(done){                  const res=parseStreamData(fullText);                  if(res.modelSlug){ modelSlugMemo=res.modelSlug; downgradedMemo=!!res.isDowngraded; updateNotification(); }                  return;                }                try{ fullText += new TextDecoder().decode(value); }catch{}                return pump();              });            })().catch(()=>{});          }        }catch{}        return response;      });    };  }  function interceptXHR(){    if(window.__iq_xhr_patched__) return;    window.__iq_xhr_patched__=true;    const open=XMLHttpRequest.prototype.open;    const send=XMLHttpRequest.prototype.send;    const setHeader=XMLHttpRequest.prototype.setRequestHeader;    XMLHttpRequest.prototype.open=function(method,url,...rest){      this.__iq_url=url; this.__iq_headers={}; return open.call(this,method,url,...rest);    };    XMLHttpRequest.prototype.setRequestHeader=function(k,v){      try{ (this.__iq_headers||(this.__iq_headers={}))[k]=v; }catch{}      return setHeader.call(this,k,v);    };    XMLHttpRequest.prototype.send=function(body){      const url=this.__iq_url||'';      const target=/\/backend-api\/(f\/)?conversation/.test(url);      // 从请求体抓 model      try{        if(target && typeof body==='string' && body[0]==='{'){          const obj=JSON.parse(body);          const m=extractModelFromJson(obj);          if(m){ modelSlugMemo=m; downgradedMemo=/^i-/i.test(m); updateNotification(); }        }      }catch{}      if(target && this.__iq_headers) detectLanguageFromHeaders(this.__iq_headers);      if(target){        this.addEventListener('readystatechange',function(){          if(this.readyState===4 && this.status===200 && typeof this.responseText==='string'){            try{              const res=parseStreamData(this.responseText);              if(res.modelSlug){ modelSlugMemo=res.modelSlug; downgradedMemo=!!res.isDowngraded; updateNotification(); }            }catch{}          }        });      }      return send.call(this, body);    };  }  /* ---------------- 观察与评分 ---------------- */  let observer=null, debounceTimer=null;  function ensureObserver(){    if(observer) return observer;    observer = new MutationObserver(()=>{ clearTimeout(debounceTimer); debounceTimer=setTimeout(()=>scanLatestAssistant(false),300); });    return observer;  }  function connectObserver(){ ensureObserver().observe(document.documentElement,{subtree:true,childList:true,characterData:true}); }  function disconnectObserver(){ if(observer) observer.disconnect(); }  document.addEventListener('visibilitychange',()=>{ if(document.hidden) disconnectObserver(); else { connectObserver(); setTimeout(()=>scanLatestAssistant(false),200); }});  function scanLatestAssistant(forceRefreshUI){    const nodes=getAssistantNodes();    if(!nodes.length){ if(forceRefreshUI) updateNotification(); return; }    const last=nodes[nodes.length-1];    const text=((last.innerText||last.textContent||'')+'').trim();    if(!text || text.length<30){ if(forceRefreshUI) updateNotification(); return; }    const prevLen=last.__iq_last_len||0;    if(!forceRefreshUI && prevLen!==text.length){ last.__iq_last_len=text.length; return; }    last.__iq_last_len=text.length;    try{      const userText=getLastUserText();      lastScorePayload=analyzeQuality(text,userText);      if(!modelSlugMemo){        const domModel=tryReadModelFromDOM();        if(domModel){ modelSlugMemo=domModel; downgradedMemo=/^i-/i.test(domModel); }      }      updateNotification();    }catch(e){ console.error('[IQ Detector] analyze error:',e); }  }  /* ---------------- 路由/初始化 ---------------- */  function setupRouteListener(){    let href=location.href;    const reinit=()=>{ setTimeout(()=>{ interceptFetch(); interceptXHR(); updateNotification(); },120); };    window.addEventListener('popstate',()=>{ if(location.href!==href){ href=location.href; reinit(); }});    const push=history.pushState, replace=history.replaceState;    history.pushState=function(...a){ const r=push.apply(this,a); if(location.href!==href){ href=location.href; reinit(); } return r; };    history.replaceState=function(...a){ const r=replace.apply(this,a); if(location.href!==href){ href=location.href; reinit(); } return r; };  }  function start(){    injectStyles(); interceptFetch(); interceptXHR(); connectObserver();    setTimeout(()=>scanLatestAssistant(false),1500);    updateNotification();  }  if(document.readyState==='loading'){    document.addEventListener('DOMContentLoaded',()=>{ start(); setupRouteListener(); });  }else{    start(); setupRouteListener();  }})();