✴️1、可以导出 claude ai对话的内容。✴️2、统计当前字数 (包括粘贴、上传、article的内容,含换行符/markdown语法符号等)。✴️3、显示对话的时间、模型信息、Token用量。ℹ️显示的信息均来自网页内本身存在但未显示的属性值。
< 腳本Claude helper (对话导出\字数统计\时间显示)的回應
脚本把页面搞得太宽了,在 0.6.5 的基础上改了一下,可以自己调节宽度
// ==UserScript== // @name Claude helper (对话导出\字数统计\时间显示) // @name:zh-CN Claude 助手 (对话导出\字数统计\时间显示) // @version 0.6.5 // @description ✴️1、可以导出 claude ai对话的内容。✴️2、统计当前字数 (包括粘贴、上传、article的内容,含换行符/markdown语法符号等)。✴️3、显示对话的时间、模型信息、Token用量。ℹ️显示的信息均来自网页内本身存在但未显示的属性值。 // @author Yearly // @match https://claude.ai/* // @include https://*claude*.com/* // @match https://chat.kelaode.ai/* // @include https://*claude*.cc/* // @icon data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgODAgODAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTAgMGg4MHY4MEgweiIgZmlsbD0iIzQ0NSIvPjxwYXRoIGQ9Im0zMyA0NC0yMy0xYy0xIDAtMi0yLTItM3MwLTEgMS0xbDI0IDItMjEtMTVjMC0xLTEtMS0xLTNzMy00IDYtMmwxNCAxMi05LTE3di0yYzAtMSAxLTUgMy01IDEgMCAzIDAgNCAxbDExIDIzIDItMjBjMC0yIDEtNCAzLTRzMyAxIDMgMmwtMyAyMCAxMi0xNGMxLTEgMy0yIDQtMSAyIDIgMiA0IDEgNkw1MSAzN2gxbDEyLTJjMy0xIDYtMiA3IDAgMSAxIDAgMyAwIDNsLTIxIDVjMTQgMSAxNSAwIDE4IDEgMiAwIDMgMiAzIDMgMCAzLTIgMy0zIDNsLTE5LTQgMTUgMTR2MWwtMiAxYy0xIDAtOS03LTE0LTExbDcgMTFjMSAxIDEgMyAwIDRzLTMgMS0zIDBMNDEgNTBjMCA3LTEgMTMtMiAxOSAwIDEtMSAxLTMgMi0xIDAtMy0xLTItM2wxLTQgMy0xNi0xMCAxMy00IDVoLTFjLTEgMC0yLTEtMi0zbDE0LTE4LTE3IDExaC00cy0xLTIgMC0zbDUtNHoiIGZpbGw9IiNENzUiLz48L3N2Zz4= // @license AGPL-v3.0 // @namespace https://greasyfork.org/zh-CN/scripts/502829-claude-helper // @supportURL https://greasyfork.org/zh-CN/scripts/502829-claude-helper // @homepageURL https://greasyfork.org/zh-CN/scripts/502829-claude-helper // @grant GM_addStyle // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // @grant GM_download // @noframes // ==/UserScript== (function() { // 设置一个全局CSS变量来控制宽度 GM_addStyle(` :root { --claude-content-width: 800px; } .xl\\:max-w-\\[48rem\\] { width: auto !important; max-width: var(--claude-content-width) !important; } div.mx-auto.md\\:max-w-3xl { max-width: var(--claude-content-width); } div.mx-auto.flex { max-width: var(--claude-content-width); } body > div.flex.min-h-screen.w-full div.flex.flex-col div.flex.gap-2 div.mt-1.max-h-96.w-full.overflow-y-auto.break-words > div.ProseMirror.break-words { max-width: var(--claude-content-width); } body > div.flex.min-h-screen.w-full > div > main > div.top-5.z-10.mx-auto.w-full.max-w-2xl.md { max-width: var(--claude-content-width); } body > div.flex.min-h-screen.w-full > div > main > div.mx-auto.w-full.max-w-2xl.px-1.md { max-width: var(--claude-content-width); } body > div.flex.min-h-screen.w-full > div > main.max-w-7xl { max-width: var(--claude-content-width); } main > div.composer-parent article > div.text-base > div.mx-auto { max-width: var(--claude-content-width); } main article > div.text-base > div.mx-auto { max-width: var(--claude-content-width); } div.relative.flex.w-full.overflow-x-hidden.overflow-y-scroll > div.relative.mx-auto.flex.h-full.w-full > div.flex.mx-auto.w-full > div[data-test-render-count] > div.mb-1.mt-1 > div.group.relative.inline-flex { border: 1px solid #dfded7; } `); // model info function conversation_model() { let conversation = document.querySelector("body > div.flex.min-h-screen.w-full > div > div.flex.h-screen") ; if(!conversation) return null; let reactProps = Object.keys(conversation).find(key => key.startsWith('__reactProps$')); if (!reactProps) return null; let conversProps = conversation[reactProps]; if (!conversProps) return null; let model = conversProps.children[1]?.props?.children[0]?.props?.conversation?.model; //claude-3-5-sonnet-20240620 return model; } // tokensSoFar function conversation_tokensSoFar() { let conversation = document.querySelector("body > div.flex.min-h-screen.w-full > div > div.flex.h-screen") ; if(!conversation) return null; let reactProps = Object.keys(conversation).find(key => key.startsWith('__reactProps$')); if (!reactProps) return null; let conversProps = conversation[reactProps]; if (!conversProps) return null; let tokensSoFar = conversProps.children[1]?.props?.children[0]?.props?.conversation?.tokensSoFar; return tokensSoFar; } // msg count var last_uuid = '', last_length = 0; function get_msg_count() { let mainScreen = document.querySelector("body > div.flex.min-h-screen.w-full > div > div > div.relative.flex.w-full > div.relative.mx-auto.flex"); if(!mainScreen) return; let tx_cnts = 0, tx_sz = 0; let rx_cnts = 0, rx_sz = 0; let fp_cnts = 0, fp_sz = 0, img_cnts = 0; let i = 0; let reactProps = Object.keys(mainScreen).find(key => key.startsWith('__reactProps$')); if (!reactProps) return null; let msgProps = mainScreen[reactProps]; let Msgs = (msgProps.children[1].props.children[0]); /* if (Msgs && Msgs.length > 0) { let newest_msg = Msgs[Msgs.length-1].newest_msgs.props.children[0].props.msg; if(newest_msg.content[0].text) { let uuid = newest_msg.uuid; let length = newest_msg.content[0].text.length; if (uuid == last_uuid && length == last_length) { return null; } last_uuid = uuid; last_length = length; } }else { return null; } */ Msgs.forEach(function(msg_item){ let msg = msg_item.props.children[0].props.msg; if(msg.sender == "human" && msg.content[0].text) { tx_cnts +=1; tx_sz += msg.content[0].text.length; for(i = 0; i < msg.attachments.length; i++) { tx_sz += msg.attachments[i].file_size; fp_cnts += 1; fp_sz += msg.attachments[i].file_size;; } img_cnts += msg.files.length; } else if(msg.sender == "assistant" && msg.content[0].text) { rx_cnts +=1; rx_sz += msg.content[0].text.length; } }); return { tx_cnts: tx_cnts, tx_sz: tx_sz, rx_cnts: rx_cnts, rx_sz: rx_sz, fp_cnts: fp_cnts, fp_sz: fp_sz, img_cnts: img_cnts, }; } function msg_counter_main() { let fieldset = document.querySelector("body > div.flex.min-h-screen.w-full fieldset") || document.querySelector("body > div.flex.min-h-screen.w-full > div > div > div.relative.flex.w-full> div.relative.mx-auto.flex.h-full.w-full > div.sticky.bottom-0.mx-auto.w-full"); if (fieldset) { let ret = get_msg_count(); if(!ret) return; let count_result = document.querySelector("#claude-msg-counter") if(!count_result) { count_result = document.createElement("pre"); count_result.id = "claude-msg-counter"; count_result.className = "border-0.5 relative z-[5] text-text-200 border-accent-pro-100/20 bg-accent-pro-900 rounded-t-xl border-b-0"; count_result.style = ` font-size: 12px; padding: 5px 7px 14px; margin: -3px auto -12px; white-space: pre-wrap; word-wrap: break-word; max-width: var(--claude-content-width); width: calc(100% - 20px); overflow-wrap: break-word; line-height: 1.4; z-index: 6; `; let targetParent = fieldset.querySelector("div.flex.md\\:px-2.flex-col-reverse"); if(targetParent) { targetParent.insertBefore(count_result, targetParent.firstChild); } else if(fieldset.querySelector("div.flex.w-full.flex-col.items-center")) { fieldset.querySelector("div.flex.w-full.flex-col.items-center").before(count_result); } } let all_length = ret.tx_sz + ret.rx_sz; let file_info = ret.fp_cnts ? ` (含${ret.fp_cnts}个文本,${ret.fp_sz}字)` : ""; let img_file_info = ret.img_cnts ? `\n另有${ret.img_cnts}个非文本内容的上传/粘贴` : ""; const model = conversation_model(); const token = conversation_tokensSoFar(); let model_info = model ? `\n模型:${model}` : ''; let token_info = token ? `\nToken:${token}` : ''; count_result.innerText = `发出:${ret.tx_cnts}条,${ret.tx_sz}字${file_info} | 回复:${ret.rx_cnts}条,${ret.rx_sz}字 | 总计:${all_length}字${img_file_info}${model_info}${token_info}`; } } // 添加自定义宽度设置函数 function setCustomWidth() { // 获取当前宽度值 const currentWidth = getComputedStyle(document.documentElement) .getPropertyValue('--claude-content-width') .replace('px', ''); // 创建输入对话框 const width = prompt( '请输入想要的宽度值(单位:px)\n当前宽度: ' + currentWidth + 'px', currentWidth ); // 验证输入 if (width === null) return; // 用户点击取消 const numWidth = parseInt(width); if (isNaN(numWidth)) { alert('请输入有效的数字!'); return; } // 设置合理的最小和最大限制 if (numWidth < 300) { alert('宽度不能小于300px'); return; } if (numWidth > 2000) { alert('宽度不能大于2000px'); return; } // 保存用户设置到localStorage localStorage.setItem('claude-content-width', numWidth); // 应用新宽度 document.documentElement.style.setProperty('--claude-content-width', numWidth + 'px'); } // 添加一个初始化函数,在页面加载时恢复上次的宽度设置 function initWidth() { const savedWidth = localStorage.getItem('claude-content-width'); if (savedWidth) { document.documentElement.style.setProperty('--claude-content-width', savedWidth + 'px'); } } // 注册菜单命令 GM_registerMenuCommand("自定义内容宽度", setCustomWidth); setInterval(() => { msg_counter_main(); }, 1600); // show message date/time function show_msg_time() { let mainScreen = document.querySelector("body > div.flex.min-h-screen.w-full > div > div.flex.h-screen") ; if(!mainScreen) return; const msg_divs = mainScreen.querySelectorAll("div[data-test-render-count] > div.mb-1.mt-1, div[data-test-render-count] > div > div[data-is-streaming].group"); msg_divs.forEach(function(msg_div){ if (msg_div.nextSibling) return; let reactProps = Object.keys(msg_div).find(key => key.startsWith('__reactProps$')); if (!reactProps) return; let divProps = msg_div[reactProps]; let updated_at = divProps.children?.[1]?.props?.message?.updated_at ?? divProps.children?.[1]?.props?.children?.[2]?.props?.message?.updated_at; if (!updated_at) return; const date = new Date(updated_at); if (!date) return; const localDateStr = date.toLocaleString(); let timeNode = document.createElement("div"); timeNode.innerText = localDateStr; timeNode.className = 'msg-uptime'; msg_div.after(timeNode); }); } GM_addStyle(` div[data-test-render-count] > div > .msg-uptime { margin: 1px 5px 5px; font-size: 13px; font-weight: 300; } div[data-test-render-count] > .msg-uptime { margin: -2px 5px 5px; font-size: 13px; font-weight: 300; } `); setInterval(() => { show_msg_time(); }, 2100); // Add Download Button function createPersistentElement(selector, createElementCallback) { function ensureElement() { const targetElement = document.querySelector(selector); if (targetElement) { if (!targetElement.querySelector('.-added-element')) { const newElement = createElementCallback(); newElement.classList.add('-added-element'); targetElement.appendChild(newElement); } } } ensureElement(); const observer = new MutationObserver(() => { ensureElement(); }); observer.observe(document.body, { childList: true, subtree: true }); } function get_account_email() { let user_menu = document.querySelector('button[data-testid="user-menu-button"][id^=radix-] > div.relative.flex.w-full.items-center'); if(!user_menu) return ''; let reactProps = Object.keys(user_menu).find(key => key.startsWith('__reactProps$')); if (!reactProps) return ''; let __reactProps = user_menu[reactProps]; if (!__reactProps) return ''; let account = __reactProps.children[0]?.props?.account?.email_address || ''; console.log(account); return account; } function get_msg_context() { let context = ""; // let mainScreen = document.querySelector("body > div.flex.min-h-screen.w-full > div > div.flex.h-screen") ; let mainScreen = document.querySelector("body > div.flex.min-h-screen.w-full > div > div > div.relative.flex.w-full > div.relative.mx-auto.flex"); if(!mainScreen) { console.warn("not found div") return null; } let tx_cnts = 0, tx_sz = 0; let rx_cnts = 0, rx_sz = 0; let fp_cnts = 0, fp_sz = 0; let i = 0; let reactProps = Object.keys(mainScreen).find(key => key.startsWith('__reactProps$')); if (!reactProps) { console.warn("not found reactProps") return null; } let msgProps = mainScreen[reactProps]; let convID = (msgProps.children[0]?.props?.conversationUuid); let name = (msgProps.children[1]?.props?.name) || document.title; let Msgs = (msgProps.children[1].props.children[0]); if ( !convID || !name || !Msgs && !Msgs.length <= 0) { console.warn("not found Msg", convID , name, Msgs); return null; } const model = conversation_model(); const token = conversation_tokensSoFar(); let model_info = ''; if (model) { model_info = `- model : ${model}\n`; } let token_info = ''; if (token) { token_info = `- tokensSoFar : ${token}\n`; } let time_info=''; let time_start_str = ''; let time_start = Msgs[0].props.children[0].props.msg.updated_at || Msgs[0].props.children[0].props.msg.created_at; if (time_start) { const date = new Date(time_start); if (date) { time_start_str = date.toLocaleString(); } } let time_last_str = ''; let time_last = Msgs[Msgs.length-1].updated_at || Msgs[Msgs.length-1].created_at; if (time_last) { const date = new Date(time_last); if (date) { time_last_str = date.toLocaleString(); } } if(time_start_str && time_last_str) { time_info = `- time : ${time_start_str} ~ ${time_last_str}\n` }; let account_info = '' let account_email = get_account_email(); if (account_email) { account_info = `- account : ${account_email}\n`; } context += `# ${name}\n\n${account_info}${model_info}${token_info}${time_info}- conversationUUID : ${convID}\n`; // Msgs.forEach(function(msg){ Msgs.forEach(function(msg_item){ let msg = msg_item.props.children[0].props.msg; context += `\n## ${msg.sender}:\n\n` context += msg.text + msg.content[0].text + '\n' for(i = 0; i < msg.attachments.length; i++) { context += `\nfile: ${msg.attachments[i].file_name}\n` if(msg.attachments[i].extracted_content) { context += `\n\`\`\`file_context\n ${msg.attachments[i].extracted_content}\n\`\`\`\n`; } } for(i = 0; i < msg.files.length; i++) { context += `file: ${msg.files[i].file_name}\n` if(msg.files[i].preview_url) { context += `preview_url: ${window.location.origin + msg.files[i].preview_url}\n`; } } context += `\n------------------------------------------------------\n` }); let blob = new Blob([context], {type: 'text/plain;charset=utf-8'}); let fileUrl = URL.createObjectURL(blob); let tempLink = document.createElement('a'); tempLink.href = fileUrl; let fileTitle = name.replaceAll(' ','_') + ".ClaudeAI.export.md"; tempLink.setAttribute('download', fileTitle); tempLink.style.display = 'none'; document.body.appendChild(tempLink); tempLink.click(); document.body.removeChild(tempLink); URL.revokeObjectURL(fileUrl); return; } function createDownloadButton() { const button = document.createElement("button"); button.className = "inline-flex items-center justify-center relative shrink-0 ring-offset-2 ring-offset-bg-300 ring-accent-main-100 focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none disabled:drop-shadow-none text-text-200 transition-all font-styrene active:bg-bg-400 hover:bg-bg-500/40 hover:text-text-100 h-9 w-9 rounded-md active:scale-95 shrink-0"; button.innerHTML = `<svg width="20" height="20" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" fill="none"><path stroke="#535358" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M27 7H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h22a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2"/><path stroke="#535358" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 20v-8l-4 4-4-4v8m12-3.5 3.5 3.5 3.5-3.5M22.5 20v-9"/></svg>`; button.title="Download Conversation" button.addEventListener("click", () => { get_msg_context(); }); return button; } // 添加按钮 createPersistentElement("body > div.flex.min-h-screen.w-full div.sticky.items-center div.right-3 div.hidden.flex-row-reverse", createDownloadButton); })(); (function() { function AddDownloadAllChats(){ let user_menu = document.querySelector('button[data-testid="user-menu-button"][id^=radix-] > div.relative.flex.w-full.items-center'); if(!user_menu) { setTimeout(() => AddDownloadAllChats(), 1000); return null; } let reactProps = Object.keys(user_menu).find(key => key.startsWith('__reactProps$')); if (!reactProps) return null; let __reactProps = user_menu[reactProps]; if (!__reactProps) return null; let master_id = __reactProps.children[0]?.props?.account?.memberships[0]?.organization?.uuid; let account = __reactProps.children[0]?.props?.account?.email_address || ''; console.log(master_id); let progressWindow, progressBar, statusText, downloadButton, debugInfo; function createProgressWindow() { progressWindow = document.createElement('div'); progressWindow.style.cssText = ` position: fixed; top: 20px; right: 20px; width: 300px; padding: 10px; background: white; border: 1px solid #ccc; box-shadow: 0 0 10px rgba(0,0,0,0.1); z-index: 99999; `; statusText = document.createElement('div'); progressWindow.appendChild(statusText); progressBar = document.createElement('progress'); progressBar.style.width = '100%'; progressBar.max = 100; progressWindow.appendChild(progressBar); downloadButton = document.createElement('button'); downloadButton.textContent = 'Download TAR'; downloadButton.style.cssText = ` display: none; margin-top: 10px; padding: 5px 10px; background-color: #4CAF50; color: white; border: none; cursor: pointer; `; progressWindow.appendChild(downloadButton); debugInfo = document.createElement('div'); debugInfo.style.marginTop = '10px'; progressWindow.appendChild(debugInfo); document.body.appendChild(progressWindow); } function updateProgress(percent, status) { progressBar.value = percent; statusText.textContent = status; } function updateDebugInfo(info) { debugInfo.textContent = info; console.log('Debug:', info); } function makeRequest(url) { return fetch(url) .then(response => { if (!response.ok) { throw new Error(`Request failed: ${response.status}`); } return response.json(); }) .catch(error => { if (error instanceof SyntaxError) { throw new Error(`Failed to parse JSON: ${error}`); } throw error; }); } function createTarHeader(filename, size) { const header = new Uint8Array(512); const encoder = new TextEncoder(); encoder.encodeInto(filename, header.subarray(0, 100)); // File name encoder.encodeInto('000644 \0', header.subarray(100, 108)); // File mode (default to 644 octal) encoder.encodeInto('0000000 \0', header.subarray(108, 116)); // Owner's numeric user ID (default to 0) encoder.encodeInto('0000000 \0', header.subarray(116, 124)); // Group's numeric user ID (default to 0) encoder.encodeInto(size.toString(8).padStart(11, '0') + ' ', header.subarray(124, 136)); // File size in bytes (octal) const mtime = Math.floor(Date.now() / 1000).toString(8).padStart(11, '0') + ' '; // Last modification time in numeric Unix time format (octal) encoder.encodeInto(mtime, header.subarray(136, 148)); encoder.encodeInto(' ', header.subarray(148, 156)); // Checksum for header block (calculate later) header[156] = '0'.charCodeAt(0); // Type flag (default to '0' for normal file) let checksum = 0; for (let i = 0; i < 512; i++) { checksum += header[i]; } encoder.encodeInto(checksum.toString(8).padStart(6, '0') + '\0 ', header.subarray(148, 156)); // Calculate and set the checksum return header; } function createTarFile(files) { const chunks = []; for (const [filename, content] of Object.entries(files)) { const encoder = new TextEncoder(); const fileContent = encoder.encode(content); const header = createTarHeader(filename, fileContent.length); chunks.push(header); chunks.push(fileContent); // Pad the file content to a multiple of 512 bytes const padding = 512 - (fileContent.length % 512); if (padding < 512) { chunks.push(new Uint8Array(padding)); } } // Add two 512-byte blocks filled with zeros to mark the end of the archive chunks.push(new Uint8Array(1024)); return new Blob(chunks, { type: 'application/x-tar' }); } var jsonfiles = {}; async function backupAllChats() { createProgressWindow(); updateProgress(0, 'Fetching conversation list...'); try { const conversations = await makeRequest(`/api/organizations/${master_id}/chat_conversations`); updateDebugInfo(`Total conversations: ${conversations.length}`); const totalConversations = conversations.length; for (let i = 0; i < conversations.length; i++) { const conversation = conversations[i]; updateProgress((i / totalConversations) * 100, `Fetching conversation ${i + 1} of ${totalConversations}`); try { const detailedConversation = await makeRequest(`/api/organizations/${master_id}/chat_conversations/${conversation.uuid}?tree=True&rendering_mode=raw`); if (detailedConversation.chat_messages?.length == 0) { continue; } const fileContent = JSON.stringify(detailedConversation, null, 2); let fileName = `${conversation.name.replaceAll(' ','_').substr(0,50)}_${conversation.updated_at.substr(0,10)}`; fileName+='.json'; jsonfiles[fileName] = fileContent; updateDebugInfo(`Added file: ${fileName}, Size: ${fileContent.length} bytes`); } catch (error) { console.error(`Error fetching conversation ${conversation.uuid}:`, error); updateDebugInfo(`Error fetching conversation ${conversation.uuid}: ${error}`); } } updateProgress(95, 'Generating Tar file...'); updateDebugInfo('Starting Tar generation...'); const tarBlob = createTarFile(jsonfiles); const sizeInKB = (tarBlob.size / (1024)).toFixed(2); updateDebugInfo(`Tar file size: ${sizeInKB} KB`); downloadButton.style.display = 'block'; updateProgress(100, 'Click the button below to download'); downloadButton.onclick = function() { const url = window.URL.createObjectURL(tarBlob); const a = document.createElement('a'); a.href = url; const timeStr = (new Date()).toLocaleString().replace(/[\/:]/g,'-').replaceAll(' ','_'); a.download = `ClaudeBackup_${account}_${timeStr}.tar`; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); updateProgress(100, 'Download started!'); setTimeout(() => progressWindow.remove(), 3000); }; } catch (error) { console.error('Error:', error); updateProgress(100, 'Error occurred. Check console for details.'); updateDebugInfo(`Error: ${error}`); } } GM_registerMenuCommand("备份全部对话记录(Json格式)", backupAllChats); }; setTimeout(() => AddDownloadAllChats(), 1000); // 将页面上的重置时间用更完整的本地时间表示 let lastProcessedTime = ''; const updateInterval = 2000; // 每2秒更新一次 function convertTime(shortTime) { const now = new Date(); const [time, period] = shortTime.split(' '); let [hours, minutes] = time.split(':'); hours = parseInt(hours); if (period.toLowerCase() === 'pm' && hours !== 12) { hours += 12; } else if (period.toLowerCase() === 'am' && hours === 12) { hours = 0; } now.setHours(hours); now.setMinutes(minutes || 0); now.setSeconds(0); now.setMilliseconds(0); return now.toLocaleString(); } function updateTime() { const element = document.querySelector("div.sticky div.w-full > div.items-center > div.text-danger-000 > div > div.text-sm > span"); if (!element) return; const originalText = element.getAttribute('data-original-time') || element.textContent.trim(); if (!element.hasAttribute('data-original-time')) { element.setAttribute('data-original-time', originalText); } if (originalText !== lastProcessedTime) { const fullTime = convertTime(originalText); element.textContent = `${originalText} (${fullTime})`; lastProcessedTime = originalText; } } function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } const debouncedUpdateTime = debounce(updateTime, 350); function checkForChanges() { debouncedUpdateTime(); requestAnimationFrame(checkForChanges); } requestAnimationFrame(checkForChanges); setInterval(updateTime, updateInterval); })();
登入以回復
脚本把页面搞得太宽了,在 0.6.5 的基础上改了一下,可以自己调节宽度