您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Display the estimated token count of the conversation below the last answer.
// ==UserScript== // @name GPT-5 Token Count // @description Display the estimated token count of the conversation below the last answer. // @version 1.5 // @author C89sd // @namespace https://greasyfork.org/users/1376767 // @match https://chatgpt.com/* // @run-at document-body // @noframes // ==/UserScript== 'use strict'; const CONF_SHOW_LAST_ONLY = false; const CONF_SHOW_POST_COUNT = true; const selector = "article .text-message"; const root = document.documentElement; const counts = new Map(); const labels = new Map(); let prevTk = null; let highestId = 0; // reset on SPA navigation const { pushState, replaceState } = history; const wrap = fn => function (...args) { counts.clear(); highestId = 0; prevTk = null; for (const el of labels.values()) if (el && el.remove) el.remove(); labels.clear(); return fn.apply(this, args); }; history.pushState = wrap(pushState); history.replaceState = wrap(replaceState); let c = 0; const observer = new MutationObserver(muts => { for (const m of muts) { let t = m.target; if (m.type === "attributes" && t.nodeType === Node.ELEMENT_NODE && t.tagName === "ARTICLE") {} else if (m.type === "childList" && t.nodeType === Node.ELEMENT_NODE && t.tagName === "DIV" && t.classList.contains('group/turn-messages')) {} else continue; const article = t?.closest('article'); const text = article?.querySelector('.text-message'); if (!text) continue; const msgUid = article.getAttribute("data-turn-id"); const msgId = article.getAttribute("data-testid"); const assist = article.getAttribute("data-turn") === 'assistant'; if (!msgId) continue; const id = parseInt(msgId.split('-').pop(), 10); const count = Math.round(text.textContent.trim().length * 0.235); // 0.235 tokens/char counts.set(id, count); // total = sum of counts for keys <= id let total = 0; for (const [k, v] of counts) if (k <= id) total += v; const count_fmt = CONF_SHOW_POST_COUNT ? Number(count).toLocaleString('en-US') : null; const total_fmt = Number(total).toLocaleString('en-US'); if (assist) { const threeDots = article.querySelector('button[aria-label="More actions"]'); const parent = threeDots?.parentElement; //console.log({threeDots, parent}) if (parent) { if (CONF_SHOW_LAST_ONLY) { if (id < highestId) continue; else { highestId = id; if (prevTk) prevTk.remove(); } } // remove old label on reevaluation if (labels.has(id)) { const old = labels.get(id); if (old && old.remove) old.remove(); } const label = document.createElement('span'); //console.log({label}) label.textContent = (count_fmt?'('+count_fmt+') ':'') + total_fmt; // label.textContent = (count_fmt?count_fmt+' | ':'') + total_fmt; label.style.opacity = '0.5'; label.style.fontSize = '.825em'; label.style.marginLeft = 'auto'; parent.appendChild(label); labels.set(id, label); prevTk = label; } } //console.log('id', id, ', total', total, counts) } }); observer.observe(root, { subtree: true, attributes: true, childList: true, });