if(!window.hasOwnProperty("aiExecuted")){ console.log(`%cPost-Summary-AI 文章摘要AI生成工具:%chttps://github.com/qxchuckle/Post-Summary-AI%c`, "border:1px #888 solid;border-right:0;border-radius:5px 0 0 5px;padding: 5px 10px;color:white;background:#4976f5;margin:10px 0", "border:1px #888 solid;border-left:0;border-radius:0 5px 5px 0;padding: 5px 10px;",""); window.aiExecuted = "chuckle"; } function ChucklePostAI(AI_option) { MAIN(AI_option); if(AI_option.pjax){ document.addEventListener('pjax:complete', ()=>{ setTimeout(()=>{ MAIN(AI_option); }, 0); }); } function MAIN(AI_option) { // 如果有则删除 const box = document.querySelector(".post-ai"); if (box) { box.parentElement.removeChild(box); } const currentPath = window.location.pathname; const currentURL = "https://blog.hzchu.top" + currentPath; // 排除页面 if(AI_option.eliminate && AI_option.eliminate.length && AI_option.eliminate.some(item => currentURL.includes(item))){ console.log("Post-Summary-AI 已排除当前页面(黑名单)"); return; } if(AI_option.whitelist && AI_option.whitelist.length && !AI_option.whitelist.some(item => currentURL.includes(item))){ console.log("Post-Summary-AI 已排除当前页面(白名单)"); return; } // 获取挂载元素,即文章内容所在的容器元素 let targetElement = ""; // 若el配置不存在则自动获取,如果auto_mount配置为真也自动获取 if(!AI_option.auto_mount && AI_option.el){ targetElement = document.querySelector(AI_option.el ? AI_option.el : '#post #article-container'); }else{ targetElement = getArticleElements(); } // 获取文章标题,默认获取网页标题 const post_title = document.querySelector(AI_option.title_el) ? document.querySelector(AI_option.title_el).textContent : document.title; if (!targetElement) { return; }; const interface = { name: "QX-AI", introduce: "我是文章辅助AI: QX-AI,点击下方的按钮,让我生成本文简介、推荐相关文章等。", version: "GPT-4", button: ["介绍自己", "推荐相关文章", "生成AI简介", "矩阵穿梭"], ...AI_option.interface } insertCSS(); // 插入css // 插入html结构 const post_ai_box = document.createElement('div'); post_ai_box.className = 'post-ai'; post_ai_box.setAttribute('id', 'post-ai'); targetElement.insertBefore(post_ai_box, targetElement.firstChild); post_ai_box.innerHTML = `
${interface.name}
切换简介
${interface.version}
${interface.name}初始化中...
${interface.button[0]}
${interface.button[1]}
${interface.button[2]}
${interface.button[3]}
`; // ai主体业务逻辑 let animationRunning = true; // 标志变量,控制动画函数的运行 let explanation = document.querySelector('.ai-explanation'); let post_ai = document.querySelector('.post-ai'); let ai_btn_item = document.querySelectorAll('.ai-btn-item'); let ai_toggle = document.querySelector('.ai-Toggle'); let ai_speech = document.querySelector('.ai-speech-box'); let ai_str = ''; let ai_str_length = ''; let delay_init = 600; let i = 0; let j = 0; let speed = AI_option.speed || 20; let character_speed = speed*7.5; let sto = []; let elapsed = 0; let completeGenerate = false; let controller = new AbortController();//控制fetch let signal = controller.signal; let summaryId = ""; // 记录当前摘要ID const summary_toggle = AI_option.summary_toggle ?? true; const summary_speech = AI_option.summary_speech ?? true; let switch_control = 0; let executedForSwitchControl = false; let summary_audio = ''; let audioBlob = ''; let isPaused = false; const summary_num = AI_option.summary_num || 3; // 切换时允许生成的摘要总数,默认3个 //默认true,使用tianliGPT,false使用官方api,记得配置Key const choiceApi = true; const apiKey = "填入chatGPT的apiKey"; //tianliGPT的参数 const tlReferer = `https://${window.location.host}/`; const tlKey = AI_option.key ? AI_option.key : '123456'; //----------------------------------------------- const animate = (timestamp) => { if (!animationRunning) { return; // 动画函数停止运行 } if (!animate.start) animate.start = timestamp; elapsed = timestamp - animate.start; if (elapsed >= speed) { animate.start = timestamp; if (i < ai_str_length - 1) { let char = ai_str.charAt(i + 1); let delay = /[,.,。!?!?]/.test(char) ? character_speed : speed; if (explanation.firstElementChild) { explanation.removeChild(explanation.firstElementChild); } explanation.innerHTML += char; let div = document.createElement('div'); div.className = "ai-cursor"; explanation.appendChild(div); i++; if (delay === character_speed) { document.querySelector('.ai-explanation .ai-cursor').style.opacity = "0"; } if (i === ai_str_length - 1) { observer.disconnect();// 暂停监听 explanation.removeChild(explanation.firstElementChild); } sto[0] = setTimeout(() => { requestAnimationFrame(animate); }, delay); } } else { requestAnimationFrame(animate); } }; const observer = new IntersectionObserver((entries) => { let isVisible = entries[0].isIntersecting; animationRunning = isVisible; // 标志变量更新 if (animationRunning) { delay_init = i === 0 ? 200 : 20; sto[1] = setTimeout(() => { if (j) { i = 0; j = 0; } if (i === 0) { explanation.innerHTML = ai_str.charAt(0); } requestAnimationFrame(animate); }, delay_init); } }, { threshold: 0 }); function clearSTO() { if (sto.length) { sto.forEach((item) => { if (item) { clearTimeout(item); } }); } } function resetAI(df = true, str = '生成中. . .') { i = 0;//重置计数器 j = 1; clearSTO(); animationRunning = false; elapsed = 0; if (df) { explanation.innerHTML = str; } else { explanation.innerHTML = '请等待. . .'; } if (!completeGenerate) { controller.abort(); } ai_str = ''; ai_str_length = ''; if(summary_toggle){ ai_toggle.style.opacity = "0"; ai_toggle.style.pointerEvents = "none"; } if(summary_speech){ summarySpeechInit(); ai_speech.style.opacity = "0"; ai_speech.style.pointerEvents = "none"; } observer.disconnect();// 暂停上一次监听 } function startAI(str, df = true) { // 如果打字机配置项存在且为false,则关闭打字机,否则默认开启打字机效果 if(AI_option.hasOwnProperty('typewriter') && !AI_option.typewriter){ explanation.innerHTML = str; }else{ resetAI(df); ai_str = str; ai_str_length = ai_str.length; observer.observe(post_ai);//启动新监听 } } function aiIntroduce() { startAI(interface.introduce); } function aiRecommend() { resetAI(); sto[2] = setTimeout(async() => { let info = await recommendList(); if(info === "" || info === false){ startAI(`${interface.name}未能找到任何可推荐的文章。`); }else if(info){ explanation.innerHTML = info; } }, 200); } async function aiGenerateAbstract() { resetAI(); const ele = targetElement; const content = getTextContent(ele); const response = await getGptResponse(content, choiceApi);//true使用tianliGPT,false使用官方api if(response){ startAI(response.summary); if(summary_toggle){ ai_toggle.style.opacity = "1"; ai_toggle.style.pointerEvents = "auto"; summarySpeechShow(); } } } async function switchAbstract() { resetAI(); audioBlob = null; const ele = targetElement; switch_control = (switch_control + 1) % summary_num; const content = getTextContent(ele) + "#".repeat(switch_control); let response = ""; if(switch_control === 1 && !executedForSwitchControl){ sessionStorage.setItem('backupsSummary', sessionStorage.getItem('summary')); // 将第一次的简介存起来 executedForSwitchControl = true; } if(!sessionStorage.getItem(`summary${"#".repeat(switch_control)}`)){ sessionStorage.removeItem('summary'); response = await getGptResponse(content, choiceApi); if(response){ sessionStorage.setItem(`summary${"#".repeat(switch_control)}`, JSON.stringify(response)); } }else{ response = JSON.parse(sessionStorage.getItem(`summary${"#".repeat(switch_control)}`)); summaryId = response.id; if(switch_control === 0){ sessionStorage.setItem('summary', sessionStorage.getItem('backupsSummary')); }else{ sessionStorage.setItem('summary', sessionStorage.getItem(`summary${"#".repeat(switch_control)}`)); } } if(response){ startAI(response.summary); ai_toggle.style.opacity = "1"; ai_toggle.style.pointerEvents = "auto"; summarySpeechShow(); } } async function recommendList() { completeGenerate = false; controller = new AbortController(); signal = controller.signal; let response = ''; let info = ''; let data = ''; const options = { signal, method: 'GET', headers: {'content-type': 'application/x-www-form-urlencoded'}, }; // 利用sessionStorage缓存推荐列表,有则缓存中读取,无则获取后缓存 if(sessionStorage.getItem('recommendList')){ data = JSON.parse(sessionStorage.getItem('recommendList')); }else{ try { response = await fetch(`https://dolgpt.hzchu.top/recommends?url=${encodeURIComponent(window.location.href)}&author=${AI_option.rec_method ? AI_option.rec_method : 'all'}`, options); completeGenerate = true; if (response.status === 429) { startAI('请求过于频繁,请稍后再请求AI。'); } if (!response.ok) { throw new Error('Response not ok'); } // 处理响应 } catch (error) { if (error.name === "AbortError") { // console.log("请求已被中止"); }else{ console.error('Error occurred:', error); startAI("获取推荐出错了,请稍后再试。"); } completeGenerate = true; return false; } // 解析响应并返回结果 data = await response.json(); sessionStorage.setItem('recommendList', JSON.stringify(data)); } if(data.hasOwnProperty("success") && !data.success){ return false; }else{ info = `推荐文章:
`; info += '
'; data.forEach((item, index) => { info += `
推荐${index + 1}:${item.title ? item.title : "未获取到题目"}
`; }); info += '
' } return info; } // 矩阵穿梭 async function matrixShuttle(){ resetAI(true, '矩阵穿梭中. . .'); completeGenerate = false; controller = new AbortController(); signal = controller.signal; let response = ''; let data = ''; const options = { signal, method: 'GET', headers: {'content-type': 'application/x-www-form-urlencoded'}, }; if(sessionStorage.getItem('matrixShuttle')){ data = JSON.parse(sessionStorage.getItem('matrixShuttle')); }else{ try { response = await fetch('https://dolgpt.hzchu.top/websites_used', options); completeGenerate = true; if (response.status === 429) { startAI('请求过于频繁,请稍后再请求AI。'); } if (!response.ok) { throw new Error('Response not ok'); } // 处理响应 } catch (error) { if (error.name === "AbortError") { // console.log("请求已被中止"); }else{ console.error('Error occurred:', error); startAI("矩阵穿梭失败了,请稍后再试。"); } completeGenerate = true; return false; } // 解析响应并返回结果 data = await response.json(); sessionStorage.setItem('matrixShuttle', JSON.stringify(data)); } const randomElement = getRandomElementFromArray(data.websites); if(randomElement){ startAI(`正在前往 ${randomElement} ,已有 ${data.count} 个网站接入AI摘要。`); sto[2] = setTimeout(() => { window.open(`https://${randomElement}`, '_blank'); }, speed*100); }else{ startAI(`没有可以穿梭的网站。`); } } // 随机返回数组中一个元素 function getRandomElementFromArray(array) { if (array.length === 0) { return null; // 返回null表示数组为空 } const randomIndex = getRandomIndex(array.length); return array[randomIndex]; } function getRandomIndex(max) { const array = new Uint32Array(1); window.crypto.getRandomValues(array); return array[0] % max; } async function summarySpeech(){ if (!summaryId) return; let response = ''; if(audioBlob && !summary_audio){ await summarySpeechPlay(audioBlob); return; } if(summary_audio && summary_audio){ if(isPaused){ isPaused = false; summary_audio.play(); ai_speech.style.opacity = "0.4"; ai_speech.style.animation = "ai_breathe .7s linear infinite"; }else{ isPaused = true; summary_audio.pause(); ai_speech.style.opacity = "1"; ai_speech.style.animation = ""; } return; }else{ const options = { method: 'GET', headers: { "Content-Type": "application/json", "Referer": tlReferer }, }; const requestParams = new URLSearchParams({ key: tlKey, id: summaryId, }); try { ai_speech.style.pointerEvents = "none"; ai_speech.style.opacity = "0.4"; response = await fetch(`https://dolgpt.hzchu.top/audio?${requestParams}`, options); if (response.status === 403) { console.error("403 refer与key不匹配。"); } else if (response.status === 500) { console.error("500 系统内部错误"); }else{ audioBlob = await response.blob(); ai_speech.style.pointerEvents = "auto"; await summarySpeechPlay(audioBlob); } }catch (error) { console.log("摘要语音请求出错:", error); ai_speech.style.opacity = "1"; ai_speech.style.pointerEvents = "auto"; } } } function summarySpeechInit(clBlob = false){ if(!summary_speech){ return; } if(summary_audio){ summary_audio.pause(); summary_audio.remove(); } summary_audio = null; ai_speech.style.opacity = "1"; ai_speech.style.animation = ""; if(clBlob){ audioBlob = null; } } function summarySpeechShow(){ if(!summary_speech){ return; } ai_speech.style.opacity = "1"; ai_speech.style.animation = ""; ai_speech.style.pointerEvents = "auto"; } async function summarySpeechPlay(audioBlob) { if(!summary_speech){ return; } const audioURL = URL.createObjectURL(audioBlob); summary_audio = new Audio(audioURL); summary_audio.play(); if(AI_option.pjax){ function handlePjaxComplete() { summary_audio.pause(); summary_audio.remove(); document.removeEventListener('pjax:complete', handlePjaxComplete); } document.removeEventListener('pjax:complete', handlePjaxComplete); document.addEventListener('pjax:complete', handlePjaxComplete); } ai_speech.style.opacity = "0.4"; ai_speech.style.animation = "ai_breathe .7s linear infinite"; summary_audio.removeEventListener("ended", handleSummaryAudioEnded); summary_audio.addEventListener("ended", handleSummaryAudioEnded); } function handleSummaryAudioEnded() { summarySpeechInit(); } //ai首屏初始化,绑定按钮注册事件 async function ai_init() { // 清除缓存 sessionStorage.removeItem('recommendList'); sessionStorage.removeItem('backupsSummary'); for (let i = 0; i < summary_num; i++) { sessionStorage.removeItem(`summary${"#".repeat(i)}`); } explanation = document.querySelector('.ai-explanation'); post_ai = document.querySelector('.post-ai'); ai_btn_item = document.querySelectorAll('.ai-btn-item'); const funArr = [aiIntroduce, aiRecommend, aiGenerateAbstract, matrixShuttle]; ai_btn_item.forEach((item, index) => { if(AI_option.hide_shuttle && index === ai_btn_item.length - 1){ item.style.display = 'none'; return; } item.addEventListener('click', () => { funArr[index](); }); }); ai_toggle = document.querySelector('.ai-Toggle'); if(summary_toggle){ ai_toggle.addEventListener('click', () => { switchAbstract(); }); }else{ ai_toggle.style.display = 'none'; } ai_speech = document.querySelector('.ai-speech-box'); if(summary_speech){ ai_speech.addEventListener('click', () => { summarySpeech(); }); }else{ ai_speech.style.display = 'none'; } if(AI_option.summary_directly){ aiGenerateAbstract(); }else{ aiIntroduce(); } } //获取某个元素内的所有纯文本,并按顺序拼接返回 function getText(element) { // 需要排除的元素及其子元素 const excludeClasses = AI_option.exclude ? AI_option.exclude : ['highlight', 'Copyright-Notice', 'post-ai', 'post-series', 'mini-sandbox']; if (!excludeClasses.includes('post-ai')) { excludeClasses.push('post-ai'); } const excludeTags = ['script', 'style', 'iframe', 'embed', 'video', 'audio', 'source', 'canvas', 'img', 'svg', 'hr', 'input', 'form'];// 需要排除的标签名数组 let textContent = ''; for (let node of element.childNodes) { if (node.nodeType === Node.TEXT_NODE) { // 如果是纯文本节点则获取内容拼接 textContent += node.textContent.trim(); } else if (node.nodeType === Node.ELEMENT_NODE) { let hasExcludeClass = false; // 遍历类名 for (let className of node.classList) { if (excludeClasses.includes(className)) { hasExcludeClass = true; break; } } let hasExcludeTag = excludeTags.includes(node.tagName.toLowerCase()); // 检查是否是需要排除的标签 // 如果hasExcludeClass和hasExcludeTag都为false,即不包含需要排除的类和标签,可以继续向下遍历子元素 if (!hasExcludeClass && !hasExcludeTag) { let innerTextContent = getText(node); textContent += innerTextContent; } } } // 返回纯文本节点的内容 return textContent.replace(/\s+/g, ''); } //获取各级标题 function extractHeadings(element) { const headings = element.querySelectorAll('h1, h2, h3, h4'); const result = []; for (let i = 0; i < headings.length; i++) { const heading = headings[i]; const headingText = heading.textContent.trim(); result.push(headingText); const childHeadings = extractHeadings(heading); result.push(...childHeadings); } return result.join(";"); } //按比例切割字符串 function extractString(str, totalLength = 1000, ratioString = "5:3:2") { totalLength = Math.min(totalLength, 5000); // 最大5000字数 if (str.length <= totalLength) { return str; } const ratios = ratioString.split(":").map(Number); const sumRatios = ratios.reduce((sum, ratio) => sum + ratio, 0); const availableLength = Math.min(str.length, totalLength); const partLengths = ratios.map(ratio => Math.floor((availableLength * ratio) / sumRatios)); const firstPart = str.substring(0, partLengths[0]); const midStartIndex = (str.length - 300) / 2; // 计算中间部分的起始索引 const middlePart = str.substring(midStartIndex, midStartIndex + partLengths[1]); const lastPart = str.substring(str.length - partLengths[2]); const result = firstPart + middlePart + lastPart; return result; } //获得字符串,默认进行切割,false返回原文纯文本 function getTextContent(element, i = true) { let content; if (i) { const totalLength = AI_option.total_length || 1000; const ratioString = AI_option.ratio_string || "5:3:2"; content = `文章标题:${post_title}。文章的各级标题:${extractHeadings(element)}。文章内容的截取:${extractString(getText(element), totalLength, ratioString)}`; } else { content = `${getText(element)}`; } return content; } //发送请求获得简介 async function getGptResponse(content, i = true) { if (!tlKey) { return "没有获取到key,代码可能没有安装正确,详细请查看文档。"; } if (tlKey === "123456") { return "请购买 key 使用,如果你能看到此条内容,则说明代码安装正确。"; } completeGenerate = false; controller = new AbortController(); signal = controller.signal; let response = ''; if(sessionStorage.getItem('summary')){ return JSON.parse(sessionStorage.getItem('summary')); } if (i) { try { response = await fetch('https://dolgpt.hzchu.top/', { signal: signal, method: "POST", headers: { "Content-Type": "application/json", "Referer": tlReferer }, body: JSON.stringify({ content: content, key: tlKey, title: post_title, url: currentURL, }) }); completeGenerate = true; if (response.status === 429) { startAI('请求过于频繁,请稍后再请求AI。'); } if (!response.ok) { throw new Error('Response not ok'); } // 处理响应 } catch (error) { if (error.name === "AbortError") { // console.log("请求已被中止"); }else if(window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1") { startAI(`${interface.name}请求tianliGPT出错了,你正在本地进行调试,请前往summary.zhheo.com添加本地域名(127.0.0.1:端口)的白名单。`); }else{ startAI(`${interface.name}请求tianliGPT出错了,请稍后再试。`); } completeGenerate = true; return ""; } // 解析响应并返回结果 const data = await response.json(); summaryId = data.id; sessionStorage.setItem('summary', JSON.stringify(data)); summarySpeechInit(true); return data; } else { const prompt = `你是一个摘要生成工具,你需要解释我发送给你的内容,不要换行,不要超过200字,只需要介绍文章的内容,不需要提出建议和缺少的东西。请用中文回答,文章内容为:${content}`; const apiUrl = "https://api.openai.com/v1/chat/completions"; try { response = await fetch(apiUrl, { signal: signal, method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` }, body: JSON.stringify({ model: "gpt-3.5-turbo", messages: [{ "role": "user", "content": prompt }], }) }); completeGenerate = true; if (response.status === 429) { startAI('请求过于频繁,请稍后再请求AI。'); } if (!response.ok) { throw new Error('Response not ok'); } // 处理响应 } catch (error) { console.error('Error occurred:', error); startAI(`${interface.name}请求chatGPT出错了,请稍后再试。`); completeGenerate = true; return ""; } // 解析响应并返回结果 const data = await response.json(); const outputText = data.choices[0].message.content; sessionStorage.setItem('summary', outputText); return outputText; } } // 实验性功能,自动获取文章内容所在容器元素 function getArticleElements(){ // 计算元素的后代元素总个数 function countDescendants(element) { let count = 1; for (const child of element.children) { count += countDescendants(child); } return count; } // 判断是否有要排除的元素 function judgeElement(element) { const excludedTags = ['IFRAME', 'FOOTER', 'HEADER', 'BLOCKQUOTE']; // 添加要排除的标签 if(excludedTags.includes(element.tagName)){ return true; } const exclusionStrings = ['aplayer', 'comment']; // 排除包含其中字符串的className return Array.from(element.classList).some(className => exclusionStrings.some(exclusion => className.includes(exclusion))); } // 深度搜索,找到得分最高的父元素 function findMaxHeadingParentElement(element) { const tagScores = { 'H1': 1.5, 'H2': 1, 'H3': 0.5, 'P': 1 }; let maxScore = 0; let maxHeadingParentElement = null; function dfs(element) { if (judgeElement(element)) { return; } let score = 0; for (const child of element.children) { if (child.tagName in tagScores) { score += tagScores[child.tagName]; } } if (score > maxScore) { maxScore = score; maxHeadingParentElement = element; } for (const child of element.children) { dfs(child); } } dfs(element); return maxHeadingParentElement; } // 广度优先搜索,标记所有元素,并找到得分最高的父元素 function findArticleContentElement() { const queue = [document.body]; let maxDescendantsCount = 0; let articleContentElement = null; while (queue.length > 0) { const currentElement = queue.shift(); // 判断当前元素是否要排除 if (judgeElement(currentElement)) { continue; } const descendantsCount = countDescendants(currentElement); if (descendantsCount > maxDescendantsCount) { maxDescendantsCount = descendantsCount; articleContentElement = currentElement; } for (const child of currentElement.children) { queue.push(child); } } return findMaxHeadingParentElement(articleContentElement); } // 返回文章内容所在的容器元素 return findArticleContentElement(); } // 插入css function insertCSS(){ const styleId = 'qx-ai-style'; if(document.getElementById(styleId)) { return; } const styleElement = document.createElement('style'); styleElement.id = styleId; styleElement.textContent = AI_option.css || `:root{--ai-font-color:#353535;--ai-post-bg:#f1f3f8;--ai-content-bg:#fff;--ai-content-border:1px solid #e3e8f7;--ai-border:1px solid #e3e8f7bd;--ai-tag-bg:rgba(48,52,63,0.80);--ai-cursor:#333;--ai-btn-bg:rgba(48,52,63,0.75);--ai-title-color:#4c4948;--ai-btn-color:#fff;--ai-speech-content:#fff;}[data-theme=dark],.theme-dark,body.dark,body.dark-theme{--ai-font-color:rgba(255,255,255,0.9);--ai-post-bg:#30343f;--ai-content-bg:#1d1e22;--ai-content-border:1px solid #42444a;--ai-border:1px solid #3d3d3f;--ai-tag-bg:#1d1e22;--ai-cursor:rgb(255,255,255,0.9);--ai-btn-bg:#1d1e22;--ai-title-color:rgba(255,255,255,0.86);--ai-btn-color:rgb(255,255,255,0.9);--ai-speech-content:#1d1e22;}#post-ai.post-ai{background:var(--ai-post-bg);border-radius:12px;padding:10px 12px 11px;line-height:1.3;border:var(--ai-border);margin-top:10px;margin-bottom:6px;transition:all 0.3s;-webkit-transition:all 0.3s;-moz-transition:all 0.3s;-ms-transition:all 0.3s;-o-transition:all 0.3s;}#post-ai .ai-title{display:flex;color:var(--ai-title-color);border-radius:8px;align-items:center;padding:0 6px;position:relative;}#post-ai .ai-title i{font-weight:800;}#post-ai .ai-title-text{font-weight:bold;margin-left:8px;font-size:17px;}#post-ai .ai-tag{font-size:12px;background-color:var(--ai-tag-bg);color:var(--ai-btn-color);border-radius:4px;margin-left:auto;line-height:1;padding:4px 5px;border:var(--ai-border);}#post-ai .ai-explanation{margin-top:10px;padding:8px 12px;background:var(--ai-content-bg);border-radius:8px;border:var(--ai-content-border);font-size:15.5px;line-height:1.4;color:var(--ai-font-color);}#post-ai .ai-cursor{display:inline-block;width:7px;background:var(--ai-cursor);height:16px;margin-bottom:-2px;opacity:0.95;margin-left:3px;transition:all 0.3s;-webkit-transition:all 0.3s;-moz-transition:all 0.3s;-ms-transition:all 0.3s;-o-transition:all 0.3s;}#post-ai .ai-btn-box{font-size:15.5px;width:100%;display:flex;flex-direction:row;flex-wrap:wrap;}#post-ai .ai-btn-item{padding:5px 10px;margin:10px 16px 0px 5px;width:fit-content;line-height:1;background:var(--ai-btn-bg);border:var(--ai-border);color:var(--ai-btn-color);border-radius:6px 6px 6px 0;-webkit-border-radius:6px 6px 6px 0;-moz-border-radius:6px 6px 6px 0;-ms-border-radius:6px 6px 6px 0;-o-border-radius:6px 6px 6px 0;user-select:none;transition:all 0.3s;-webkit-transition:all 0.3s;-moz-transition:all 0.3s;-ms-transition:all 0.3s;-o-transition:all 0.3s;cursor:pointer;}#post-ai .ai-btn-item:hover{background:#49b0f5dc;}#post-ai .ai-recommend{display:flex;flex-direction:row;flex-wrap:wrap;}#post-ai .ai-recommend-item{width:50%;margin-top:2px;}#post-ai .ai-recommend-item a{border-bottom:2px solid #4c98f7;padding:0 .2em;color:#4c98f7;font-weight:700;text-decoration:none;transition:all 0.3s;-webkit-transition:all 0.3s;-moz-transition:all 0.3s;-ms-transition:all 0.3s;-o-transition:all 0.3s;}#post-ai .ai-recommend-item a:hover{background-color:#49b1f5;border-bottom:2px solid #49b1f5;color:#fff;border-radius:5px;}@media screen and (max-width:768px){#post-ai .ai-btn-box{justify-content:center;}}#post-ai .ai-title>svg{width:21px;height:21px;}#post-ai .ai-title>svg path{fill:var(--ai-font-color);}#post-ai .ai-Toggle{font-size:12px;border:var(--ai-border);background:var(--ai-btn-bg);color:var(--ai-btn-color);padding:3px 4px;border-radius:4px;margin-left:6px;cursor:pointer;-webkit-transition:.3s;-moz-transition:.3s;-o-transition:.3s;-ms-transition:.3s;transition:.3s;font-weight:bolder;pointer-events:none;opacity:0;}#post-ai .ai-Toggle:hover{background:#49b0f5dc;}#post-ai .ai-speech-box{width:21px;height:21px;background:var(--ai-font-color);margin-left:7px;border-radius:50%;display:flex;flex-direction:row;flex-wrap:wrap;align-content:center;justify-content:center;pointer-events:none;opacity:0;-webkit-transition:.3s;-moz-transition:.3s;-o-transition:.3s;-ms-transition:.3s;transition:.3s;cursor:pointer;}#post-ai .ai-speech-content{width:8px;background:var(--ai-speech-content);height:8px;border-radius:50%;-webkit-transition:.3s;-moz-transition:.3s;-o-transition:.3s;-ms-transition:.3s;transition:.3s;}#post-ai .ai-speech-box:hover .ai-speech-content{background:#49b0f5;}@keyframes ai_breathe{0%{transform:scale(0.9);-webkit-transform:scale(0.9);-moz-transform:scale(0.9);-ms-transform:scale(0.9);-o-transform:scale(0.9);}50%{transform:scale(1);-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);}}`; AI_option.additional_css && (styleElement.textContent += AI_option.additional_css); document.head.appendChild(styleElement); } ai_init(); } } // 兼容旧版本配置项 if(typeof ai_option!=="undefined"){ console.log("正在使用旧版本配置方式,请前往项目仓库查看最新配置写法"); new ChucklePostAI(ai_option); }