diff --git a/index.json b/index.json index c13d76b..43334af 100644 --- a/index.json +++ b/index.json @@ -1,6 +1,6 @@ { "version": 1, - "updated_at": "2026-06-16T06:18:38.000Z", + "updated_at": "2026-06-25T06:01:55.000Z", "scripts": [ { "id": "codex-context-ring-restore", @@ -72,7 +72,7 @@ "id": "codex-daily-token-usage", "name": "Codex Daily Token Usage", "description": "每日 Token 统计,近 5 日滚动存储,自动复用已有采集,支持 Model 价格、成本估算、趋势明细和分享图", - "version": "1.4.3", + "version": "1.4.5", "author": "kts-kris", "tags": [ "codex", @@ -84,7 +84,7 @@ ], "homepage": "", "script_url": "https://raw.githubusercontent.com/BigPizzaV3/CodexPlusPlusScriptMarket/main/scripts/codex-daily-token-usage.js", - "sha256": "890df2074a4ef0be3d424c86a21dca1ed1b9bd9a121dea112baa16fbb242d04a" + "sha256": "b565fe5a5728d52700d4d089d874a3f3e7f258a82fdea69762155ebbdf1c2c2f" }, { "id": "codex-list-pagebuster", diff --git a/scripts/codex-daily-token-usage.js b/scripts/codex-daily-token-usage.js index e6e4c7d..f669d90 100644 --- a/scripts/codex-daily-token-usage.js +++ b/scripts/codex-daily-token-usage.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Codex Daily Token Usage // @namespace codex-plus-plus -// @version 1.4.3 +// @version 1.4.5 // @description 每日 Token 统计,近 5 日滚动存储,优先复用已有采集,必要时内置采集,支持 Model 价格、成本估算、日期切换、5 日趋势与分享图。 // @match app://-/* // @run-at document-start @@ -10,7 +10,7 @@ (() => { "use strict"; - const VERSION = "1.4.3"; + const VERSION = "1.4.5"; const API_KEY = "__codexDailyTokenUsage"; const SOURCE_API_KEY = "__codexTokenUsage"; const STORAGE_KEY = "__codexDailyTokenUsageV1"; @@ -23,12 +23,16 @@ const MAX_TURNS_PER_DAY = 2000; const CAPTURE_DEDUPE_WINDOW_MS = 3000; const MAX_CAPTURE_BODY_CHARS = 2_000_000; - const EXTERNAL_SOURCE_GRACE_MS = 4000; const EXTERNAL_EMPTY_LIMIT = 4; const TREND_DAYS = 5; const MODEL_BIND_WINDOW_MS = 30 * 60 * 1000; const UNKNOWN_MODEL = "Unknown"; const PRICE_FIELDS = ["input", "cachedInput", "output", "reasoning"]; + const FLOATING_TOP = 2; + const FLOATING_RIGHT = 280; + const PANEL_GAP = 8; + const PANEL_MARGIN = 12; + const WINDOW_BUTTON_SAFE_RIGHT = 132; const previous = window[API_KEY]; if (previous && typeof previous.destroy === "function") { @@ -55,7 +59,6 @@ let lastCaptureAt = 0; let captureSeq = 0; let externalEmptyCount = 0; - let startedAt = Date.now(); let lastRenderedTotal = -1; let lastDateKey = getDateKey(Date.now()); let selectedDateKey = lastDateKey; @@ -140,32 +143,47 @@ const usage = rawUsage && typeof rawUsage === "object" ? rawUsage : {}; const input = firstCount( usage.inputTotalTokens, + usage.input_total_tokens, + usage.promptTotalTokens, + usage.prompt_total_tokens, usage.inputTokens, usage.input_tokens, + usage.promptTokens, usage.prompt_tokens ); const output = firstCount( usage.outputTotalTokens, usage.outputTokens, usage.output_tokens, + usage.completionTokens, usage.completion_tokens ); const cached = firstCount( usage.cachedReadTokens, usage.cachedTokens, usage.cacheReadTokens, + usage.cachedInputTokens, usage.cached_input_tokens, + usage.cacheReadInputTokens, + usage.cache_read_input_tokens, + usage.promptTokensDetails?.cachedTokens, + usage.prompt_tokens_details?.cached_tokens, + usage.inputTokensDetails?.cachedTokens, usage.input_tokens_details?.cached_tokens ); const reasoning = firstCount( usage.reasoningTokens, usage.reasoning_tokens, + usage.outputTokensDetails?.reasoningTokens, usage.output_tokens_details?.reasoning_tokens ); const total = firstCount( usage.requestTotalTokens, usage.totalTokens, usage.total_tokens, + usage.usedTokens, + usage.used_tokens, + usage.used, input + output ); @@ -1162,6 +1180,8 @@ "usage", "token_usage", "tokenUsage", + "context_usage", + "contextUsage", "last_usage", "lastUsage", "last_token_usage", @@ -1189,6 +1209,7 @@ "item", "output", "details", + "info", ]) { candidates.push(...findUsageCandidates(value[key], depth + 1, id, model)); } @@ -1270,6 +1291,10 @@ return changed; } + function shouldProcessStandalonePayload() { + return sourceMode !== "external" && (captureInstalled || !externalSourceAvailable()); + } + function processModelPayload(payload) { if (!payload) return false; if (typeof payload === "string") { @@ -1294,7 +1319,11 @@ "codex-message-from-view", (event) => { try { - processModelPayload(event.detail); + if (shouldProcessStandalonePayload()) { + processCapturePayload(event.detail, "codex-message"); + } else { + processModelPayload(event.detail); + } } catch { // 不影响 Codex 自身消息投递。 } @@ -1464,7 +1493,7 @@ function shouldInstallStandaloneCapture(externalTurns) { if (captureInstalled) return false; if (!externalSourceAvailable()) { - return Date.now() - startedAt >= EXTERNAL_SOURCE_GRACE_MS; + return true; } if (externalTurns.length > 0) return false; externalEmptyCount += 1; @@ -1514,15 +1543,17 @@ display: flex; flex: 0 0 auto; align-items: center; - pointer-events: auto; + pointer-events: none; -webkit-app-region: no-drag; z-index: 2147483600; font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; } #${ROOT_ID}.codex-daily-floating { position: fixed; - top: 10px; - right: 80px; + top: ${FLOATING_TOP}px; + right: ${FLOATING_RIGHT}px; + bottom: auto; + left: auto; } #${ROOT_ID} .codex-daily-trigger { box-sizing: border-box; @@ -1546,6 +1577,7 @@ user-select: none; outline: none; transition: background 120ms ease, border-color 120ms ease, transform 120ms ease; + pointer-events: auto; -webkit-app-region: no-drag; } #${ROOT_ID} .codex-daily-trigger:hover, @@ -2167,11 +2199,12 @@ document.body.appendChild(panel); const trigger = root.querySelector(".codex-daily-trigger"); - trigger.addEventListener("click", () => { + trigger.addEventListener("click", (event) => { + event.stopPropagation(); pinnedOpen = !pinnedOpen; if (pinnedOpen) { showPanel(); - } else if (!root.matches(":hover") && !panel.matches(":hover")) { + } else { hidePanel(); } }); @@ -2184,11 +2217,10 @@ }); trigger.addEventListener("focus", showPanel); trigger.addEventListener("blur", schedulePanelClose); - root.addEventListener("mouseenter", showPanel); - root.addEventListener("mouseleave", schedulePanelClose); + trigger.addEventListener("mouseenter", showPanel); + trigger.addEventListener("mouseleave", schedulePanelClose); panel.addEventListener("mouseenter", cancelPanelClose); panel.addEventListener("mouseleave", schedulePanelClose); - panel.querySelector('[data-action="previous-day"]').addEventListener("click", () => { selectDate(shiftDateKey(selectedDateKey, -1)); }); @@ -2383,8 +2415,19 @@ function positionPanel() { if (!root || !panel) return; const rect = root.getBoundingClientRect(); - panel.style.top = `${Math.round(rect.bottom + 8)}px`; - panel.style.right = `${Math.max(12, Math.round(innerWidth - rect.right))}px`; + const panelRect = panel.getBoundingClientRect(); + const panelWidth = panelRect.width || Math.min(350, innerWidth - PANEL_MARGIN * 2); + const panelHeight = panelRect.height || 580; + const maxLeft = Math.max(PANEL_MARGIN, innerWidth - panelWidth - PANEL_MARGIN); + const safeMaxLeft = Math.max(PANEL_MARGIN, innerWidth - panelWidth - WINDOW_BUTTON_SAFE_RIGHT); + const preferredLeft = rect.left; + const left = Math.min(Math.max(PANEL_MARGIN, preferredLeft), safeMaxLeft, maxLeft); + const preferredTop = rect.bottom + PANEL_GAP; + const maxTop = Math.max(PANEL_MARGIN, innerHeight - panelHeight - PANEL_MARGIN); + const top = Math.min(Math.max(PANEL_MARGIN, preferredTop), maxTop); + panel.style.top = `${Math.round(top)}px`; + panel.style.left = `${Math.round(left)}px`; + panel.style.right = "auto"; } function cancelPanelClose() { @@ -2414,7 +2457,9 @@ function schedulePanelClose() { cancelPanelClose(); closeTimer = window.setTimeout(() => { - if (!pinnedOpen && !root?.matches(":hover") && !panel?.matches(":hover")) { + const trigger = root?.querySelector(".codex-daily-trigger"); + const triggerActive = trigger?.matches(":hover") || trigger?.matches(":focus"); + if (!pinnedOpen && !triggerActive && !panel?.matches(":hover")) { hidePanel(); } }, 140); @@ -2427,12 +2472,16 @@ } function findToolbar() { + return null; + const plusMenu = document.getElementById("codex-plus-menu"); - if (plusMenu?.parentElement) return plusMenu.parentElement; + if (plusMenu?.parentElement && plusMenu.getBoundingClientRect().top >= 30) { + return plusMenu.parentElement; + } const candidates = Array.from(document.querySelectorAll("button")).filter((button) => { const rect = button.getBoundingClientRect(); - return rect.width > 0 && rect.height > 0 && rect.top >= 0 && rect.top < 60 && rect.right > innerWidth - 360; + return rect.width > 0 && rect.height > 0 && rect.top >= 30 && rect.top < 72 && rect.right > innerWidth - 360; }); for (const button of candidates) { @@ -2444,7 +2493,7 @@ style.display === "flex" && rect.height > 20 && rect.height < 60 && - rect.right > innerWidth - 24 && + rect.right > innerWidth - WINDOW_BUTTON_SAFE_RIGHT && current.children.length > 1 ) { return current; @@ -2466,7 +2515,14 @@ } root.classList.add("codex-daily-floating"); - if (root.parentElement !== document.body) document.body.appendChild(root); + if (root.parentElement !== document.body) { + document.body.appendChild(root); + } + root.style.top = `${FLOATING_TOP}px`; + root.style.right = `${FLOATING_RIGHT}px`; + root.style.left = "auto"; + root.style.bottom = "auto"; + root.style.transform = "none"; if (panel?.classList.contains("is-visible")) positionPanel(); } @@ -2739,9 +2795,6 @@ externalSourceAvailable, getSourceMode: () => sourceMode, isCaptureInstalled: () => captureInstalled, - setStartedAt(value) { - startedAt = value; - }, getRawState() { return JSON.parse(JSON.stringify(state)); },