From 4b53f767d0c409ba992f0e1da716b6f3f9a16041 Mon Sep 17 00:00:00 2001 From: "K.T.S" <38993658@qq.com> Date: Sat, 27 Jun 2026 18:27:53 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=81=BF=E5=85=8D=E6=AF=8F=E6=97=A5?= =?UTF-8?q?=E7=94=A8=E9=87=8F=E5=85=A5=E5=8F=A3=E9=87=8D=E5=8F=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.json | 6 +- scripts/codex-daily-token-usage.js | 141 +++++++++++++++++++++++++++-- 2 files changed, 137 insertions(+), 10 deletions(-) diff --git a/index.json b/index.json index 43334af..8217100 100644 --- a/index.json +++ b/index.json @@ -1,6 +1,6 @@ { "version": 1, - "updated_at": "2026-06-25T06:01:55.000Z", + "updated_at": "2026-06-27T10:36:45.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.5", + "version": "1.4.6", "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": "b565fe5a5728d52700d4d089d874a3f3e7f258a82fdea69762155ebbdf1c2c2f" + "sha256": "40d0edb049467391ba6c1c40d7712feaaa1427ba187ea46441ec14680ff55592" }, { "id": "codex-list-pagebuster", diff --git a/scripts/codex-daily-token-usage.js b/scripts/codex-daily-token-usage.js index f669d90..730bf27 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.5 +// @version 1.4.6 // @description 每日 Token 统计,近 5 日滚动存储,优先复用已有采集,必要时内置采集,支持 Model 价格、成本估算、日期切换、5 日趋势与分享图。 // @match app://-/* // @run-at document-start @@ -10,7 +10,7 @@ (() => { "use strict"; - const VERSION = "1.4.5"; + const VERSION = "1.4.6"; const API_KEY = "__codexDailyTokenUsage"; const SOURCE_API_KEY = "__codexTokenUsage"; const STORAGE_KEY = "__codexDailyTokenUsageV1"; @@ -29,7 +29,12 @@ const UNKNOWN_MODEL = "Unknown"; const PRICE_FIELDS = ["input", "cachedInput", "output", "reasoning"]; const FLOATING_TOP = 2; - const FLOATING_RIGHT = 280; + const FLOATING_DEFAULT_RIGHT = 280; + const FLOATING_SAFE_GAP = 8; + const FLOATING_SCAN_TOP = 96; + const FLOATING_MIN_WIDTH = 94; + const FLOATING_COMPACT_WIDTH = 31; + const FLOATING_HEIGHT = 31; const PANEL_GAP = 8; const PANEL_MARGIN = 12; const WINDOW_BUTTON_SAFE_RIGHT = 132; @@ -1551,7 +1556,7 @@ #${ROOT_ID}.codex-daily-floating { position: fixed; top: ${FLOATING_TOP}px; - right: ${FLOATING_RIGHT}px; + right: ${FLOATING_DEFAULT_RIGHT}px; bottom: auto; left: auto; } @@ -1589,6 +1594,12 @@ #${ROOT_ID}.is-updated .codex-daily-trigger { animation: codex-daily-token-pulse 420ms ease; } + #${ROOT_ID}[data-layout="compact-icon"] .codex-daily-trigger { + width: ${FLOATING_COMPACT_WIDTH}px; + min-width: ${FLOATING_COMPACT_WIDTH}px; + padding: 0; + gap: 0; + } #${ROOT_ID} .codex-daily-sigma { width: 16px; height: 16px; @@ -1605,6 +1616,10 @@ font-weight: 600; letter-spacing: 0.01em; } + #${ROOT_ID}[data-layout="compact-icon"] .codex-daily-label, + #${ROOT_ID}[data-layout="compact-icon"] .codex-daily-total { + display: none; + } #${PANEL_ID} { position: fixed; width: min(350px, calc(100vw - 24px)); @@ -2117,7 +2132,7 @@ root.innerHTML = ` `; @@ -2471,6 +2486,106 @@ hidePanel(); } + function normalizeRect(rect) { + if (!rect) return null; + const left = Number(rect.left); + const top = Number(rect.top); + const right = Number(rect.right); + const bottom = Number(rect.bottom); + if (![left, top, right, bottom].every(Number.isFinite)) return null; + const width = Math.max(0, right - left); + const height = Math.max(0, bottom - top); + if (!width || !height) return null; + return { left, top, right, bottom, width, height }; + } + + function rectsOverlap(first, second, gap = 0) { + return ( + first.left < second.right + gap && + first.right > second.left - gap && + first.top < second.bottom + gap && + first.bottom > second.top - gap + ); + } + + function candidateRectFromRight(right, top, width, height, viewportWidth) { + const left = viewportWidth - right - width; + return { left, right: left + width, top, bottom: top + height, width, height }; + } + + function resolveFloatingLayout(width, height, viewportWidth, viewportHeight, obstacleRects = []) { + const safeWidth = Math.min(Math.max(1, Number(width) || FLOATING_MIN_WIDTH), Math.max(1, viewportWidth - PANEL_MARGIN * 2)); + const compactWidth = Math.min(FLOATING_COMPACT_WIDTH, safeWidth); + const safeHeight = Math.max(1, Number(height) || FLOATING_HEIGHT); + const top = FLOATING_TOP; + const maxRight = Math.max(PANEL_MARGIN, viewportWidth - PANEL_MARGIN - safeWidth); + const clampRight = (right) => Math.min(Math.max(PANEL_MARGIN, right), maxRight); + const layoutFromLeft = (left, width, compact = false) => ({ + top, + right: viewportWidth - left - width, + left, + width, + compact, + }); + const topObstacles = obstacleRects + .map(normalizeRect) + .filter((rect) => rect && rect.bottom > 0 && rect.top < FLOATING_SCAN_TOP && rect.right > 0 && rect.left < viewportWidth); + const defaultRight = clampRight(FLOATING_DEFAULT_RIGHT); + const defaultRect = candidateRectFromRight(defaultRight, top, safeWidth, safeHeight, viewportWidth); + if (!topObstacles.some((rect) => rectsOverlap(defaultRect, rect, FLOATING_SAFE_GAP))) { + return { top, right: defaultRight, left: defaultRect.left, width: safeWidth, compact: false }; + } + + const blocked = topObstacles + .map((rect) => ({ + left: Math.max(PANEL_MARGIN, rect.left - FLOATING_SAFE_GAP), + right: Math.min(viewportWidth - PANEL_MARGIN, rect.right + FLOATING_SAFE_GAP), + })) + .sort((a, b) => a.left - b.left); + let cursor = PANEL_MARGIN; + const gaps = []; + for (const rect of blocked) { + if (rect.left > cursor) gaps.push({ left: cursor, right: rect.left }); + cursor = Math.max(cursor, rect.right); + } + if (cursor < viewportWidth - PANEL_MARGIN) gaps.push({ left: cursor, right: viewportWidth - PANEL_MARGIN }); + + const fullGap = gaps + .filter((gap) => gap.right - gap.left >= safeWidth) + .sort((a, b) => b.right - a.right)[0]; + if (fullGap) { + return layoutFromLeft(fullGap.right - safeWidth, safeWidth, false); + } + + const compactGap = gaps + .filter((gap) => gap.right - gap.left >= compactWidth) + .sort((a, b) => b.right - a.right)[0]; + if (compactGap) { + return layoutFromLeft(compactGap.right - compactWidth, compactWidth, true); + } + + const fallbackRight = Math.min(Math.max(PANEL_MARGIN, defaultRight), Math.max(PANEL_MARGIN, viewportWidth - PANEL_MARGIN - compactWidth)); + const fallbackRect = candidateRectFromRight(fallbackRight, top, compactWidth, safeHeight, viewportWidth); + return { top, right: fallbackRight, left: fallbackRect.left, width: compactWidth, compact: true }; + } + + function collectTopObstacleRects() { + if (!document.body?.querySelectorAll) return []; + const selector = "button,[role='button'],input,select,textarea,#codex-plus-menu,[data-testid]"; + return Array.from(document.body.querySelectorAll(selector)) + .filter((node) => !root?.contains(node) && !panel?.contains(node)) + .map((node) => { + const rect = normalizeRect(node.getBoundingClientRect?.()); + if (!rect || rect.width < 4 || rect.height < 4) return null; + if (rect.top >= FLOATING_SCAN_TOP || rect.bottom <= 0) return null; + if (rect.width > innerWidth * 0.85 || rect.height > FLOATING_SCAN_TOP) return null; + const style = typeof getComputedStyle === "function" ? getComputedStyle(node) : null; + if (style && (style.display === "none" || style.visibility === "hidden" || style.opacity === "0")) return null; + return rect; + }) + .filter(Boolean); + } + function findToolbar() { return null; @@ -2518,11 +2633,21 @@ if (root.parentElement !== document.body) { document.body.appendChild(root); } - root.style.top = `${FLOATING_TOP}px`; - root.style.right = `${FLOATING_RIGHT}px`; + root.dataset.layout = "top-toolbar"; + const rect = root.getBoundingClientRect(); + const layout = resolveFloatingLayout( + rect.width || FLOATING_MIN_WIDTH, + rect.height || FLOATING_HEIGHT, + innerWidth, + innerHeight, + collectTopObstacleRects() + ); + root.style.top = `${Math.round(layout.top)}px`; + root.style.right = `${Math.round(layout.right)}px`; root.style.left = "auto"; root.style.bottom = "auto"; root.style.transform = "none"; + root.dataset.layout = layout.compact ? "compact-icon" : "top-toolbar"; if (panel?.classList.contains("is-visible")) positionPanel(); } @@ -2787,6 +2912,8 @@ trendPoints, trendPath, buildShareModel, + resolveFloatingLayout, + rectsOverlap, findUsageCandidates, processCapturePayload, processModelPayload,