dsh-whale-widget 0.3.8 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/whale-widget.js +64 -16
- package/lib/index.js +29 -5
- package/package.json +1 -1
package/assets/whale-widget.js
CHANGED
|
@@ -1450,17 +1450,21 @@ var rowHide = menuRow()
|
|
|
1450
1450
|
rowHide.appendChild(menuLabel('隐藏菜单按钮'))
|
|
1451
1451
|
rowHide.appendChild(menuHideToggle)
|
|
1452
1452
|
menuBox.appendChild(rowHide)
|
|
1453
|
-
// —— Codex 本机统计开关(issue #116
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
codexStatsToggle
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1453
|
+
// —— Codex 本机统计开关(issue #116):v748 起**从主菜单挪进「Codex 模型的设置子菜单」** ——
|
|
1454
|
+
// 主菜单不再有这一项(它对没配 Codex 模型的用户没有意义)。宿主侧同样按"有没有 Codex 模型"兜底:
|
|
1455
|
+
// 没配 → 默认关闭、完全不扫 ~/.codex/sessions。这里只保留一个"当前挂载的那个复选框"的引用,
|
|
1456
|
+
// 供 setCodexStatsOn() 与配置读回时同步勾选状态(菜单是每次重建的,所以元素现建现用)。
|
|
1457
|
+
var codexStatsToggle = null
|
|
1458
|
+
function codexStatsCheckbox() {
|
|
1459
|
+
var el = document.createElement('input')
|
|
1460
|
+
el.type = 'checkbox'
|
|
1461
|
+
el.className = 'dshwv-check'
|
|
1462
|
+
el.checked = codexStatsOn !== false
|
|
1463
|
+
el.title = '关闭后不再读取 ~/.codex/sessions 统计本机 Codex 用量(会话日志很大时建议关闭)'
|
|
1464
|
+
el.addEventListener('change', function () { setCodexStatsOn(el.checked) })
|
|
1465
|
+
codexStatsToggle = el
|
|
1466
|
+
return el
|
|
1467
|
+
}
|
|
1464
1468
|
// —— 资源管理:集中查看/删除已导入的图片与音频(角色图/泡泡图/音频片段/音效组) ——
|
|
1465
1469
|
var rowRes = menuRow()
|
|
1466
1470
|
var resOpenBtn = document.createElement('button')
|
|
@@ -2712,12 +2716,26 @@ function openApiModelPanel(modelId) {
|
|
|
2712
2716
|
} catch (err) {}
|
|
2713
2717
|
}
|
|
2714
2718
|
var apiModelMaskEl = null
|
|
2719
|
+
// v748:Codex 模型的设置菜单打开时,记下"当前是哪个模型"和"用量行元素",
|
|
2720
|
+
// 这样拨动「Codex 本机统计」开关、等宿主确认后可以**原地**刷新那一行,不必关窗重开。
|
|
2721
|
+
var apiModelMenuId = null
|
|
2722
|
+
var codexUsageTextEl = null
|
|
2723
|
+
function refreshOpenCodexRow() {
|
|
2724
|
+
try {
|
|
2725
|
+
if (!codexUsageTextEl || !apiModelMenuId) return
|
|
2726
|
+
var t = apiCodexDetailText(apiModelMenuId)
|
|
2727
|
+
codexUsageTextEl.textContent = t
|
|
2728
|
+
codexUsageTextEl.title = t
|
|
2729
|
+
} catch (err) {}
|
|
2730
|
+
}
|
|
2715
2731
|
function closeApiModelPanel() {
|
|
2716
2732
|
// v744:必须走 dshwBodyDetach —— 这个遮罩是登记在案的 body 节点,
|
|
2717
2733
|
// 若用 parentNode.removeChild 直接摘掉,DOM 守护会认为"被别的插件摘走了"并把它补挂回来,
|
|
2718
2734
|
// 表现就是**点「取消」关不掉这个窗口**(0.3.7 引入的回归)。
|
|
2719
2735
|
try { if (apiModelMaskEl) dshwBodyDetach(apiModelMaskEl) } catch (err) {}
|
|
2720
2736
|
apiModelMaskEl = null
|
|
2737
|
+
apiModelMenuId = null
|
|
2738
|
+
codexUsageTextEl = null
|
|
2721
2739
|
}
|
|
2722
2740
|
// 列宽受限 + 悬浮滚动:内容超出列宽时,鼠标移上去文字自动横向滚动,移开回到起点。
|
|
2723
2741
|
// 需要 .dshwv-marq(外层 overflow:hidden)+ 内层 inline-block span 配合。
|
|
@@ -3079,8 +3097,26 @@ function openApiModelMenu(modelId) {
|
|
|
3079
3097
|
}, function () { openModelAlertBudget(modelId, 'budget', function () { openApiModelMenu(modelId) }) })
|
|
3080
3098
|
rowOf('额度(订阅/资源包)', function () { return apiQuotaSummary(modelId) },
|
|
3081
3099
|
function () { openModelQuotaEditor(modelId) })
|
|
3082
|
-
// Codex
|
|
3083
|
-
|
|
3100
|
+
// Codex 模式:开关(v748 从主菜单挪到这里)+ 只读展示本地会话统计(机器级)
|
|
3101
|
+
// 注意:开关按「是不是 Codex 模型」(apiCodexOf 非空)显示,**不能**按 .ok 判断 ——
|
|
3102
|
+
// 关掉时 .ok 就是 false,那样开关会自己消失、再也没法打开。
|
|
3103
|
+
if (apiCodexOf(modelId)) {
|
|
3104
|
+
var ct = document.createElement('div')
|
|
3105
|
+
ct.className = 'dshwv-audiorow'
|
|
3106
|
+
var ctl = document.createElement('span')
|
|
3107
|
+
ctl.textContent = 'Codex 本机统计'
|
|
3108
|
+
ctl.style.flex = '0 0 auto'
|
|
3109
|
+
ct.appendChild(ctl)
|
|
3110
|
+
var cth = document.createElement('span')
|
|
3111
|
+
cth.className = 'dshwv-usage-hint'
|
|
3112
|
+
cth.style.flex = '1'
|
|
3113
|
+
cth.style.textAlign = 'right'
|
|
3114
|
+
cth.style.paddingRight = '6px'
|
|
3115
|
+
cth.textContent = '读取 ~/.codex/sessions'
|
|
3116
|
+
ct.appendChild(cth)
|
|
3117
|
+
ct.appendChild(codexStatsCheckbox())
|
|
3118
|
+
card.appendChild(ct)
|
|
3119
|
+
// 用量行:关闭/出错时同样显示(文字就是原因),开着时显示今日/近7天/累计
|
|
3084
3120
|
var cr = document.createElement('div')
|
|
3085
3121
|
cr.className = 'dshwv-audiorow'
|
|
3086
3122
|
var cl = document.createElement('span')
|
|
@@ -3099,6 +3135,9 @@ function openApiModelMenu(modelId) {
|
|
|
3099
3135
|
ci.title = ci.textContent
|
|
3100
3136
|
cr.appendChild(ci)
|
|
3101
3137
|
card.appendChild(cr)
|
|
3138
|
+
// 记下来:开关切完拿到服务端确认后,原地刷新这一行(不用关窗重开)
|
|
3139
|
+
codexUsageTextEl = ci
|
|
3140
|
+
apiModelMenuId = modelId
|
|
3102
3141
|
}
|
|
3103
3142
|
// 厂商订阅额度(kind='quota'):只读展示,来自厂商接口
|
|
3104
3143
|
if (apiPlanSupport(modelId)) {
|
|
@@ -12589,8 +12628,10 @@ var costBubbleActive = false
|
|
|
12589
12628
|
var scrollGapOn = false
|
|
12590
12629
|
var scrollGapPx = 17
|
|
12591
12630
|
var menuBtnHide = false // 主菜单开关:隐藏挂件菜单按钮,改为右键小鲸鱼唤出菜单
|
|
12592
|
-
// issue #116:Codex
|
|
12593
|
-
//
|
|
12631
|
+
// issue #116:Codex 本机统计开关。关掉后宿主完全不扫 ~/.codex/sessions(适合会话日志很大的机器)。
|
|
12632
|
+
// v748:默认值仍是「开」,但**宿主会先看有没有 Codex 模型** —— 没配就一律按关闭处理,
|
|
12633
|
+
// 所以这里保持默认 true 是安全的(不会让没配 Codex 的用户被扫盘),也不会因为一次普通保存
|
|
12634
|
+
// 就把老用户的开关写成 false。开关 UI 现只在 Codex 模型的设置子菜单里(见 codexStatsCheckbox)。
|
|
12594
12635
|
var codexStatsOn = true
|
|
12595
12636
|
// —— v734(issue #97 / #88):设置保存的「防覆盖 + 失败可见」——
|
|
12596
12637
|
// #97 根因:首次 GET 还没落地就 PUT,会把内存里的默认值整包写进服务端(重启后设置被洗成默认值)。
|
|
@@ -12764,7 +12805,14 @@ function setMenuBtnHide(v) {
|
|
|
12764
12805
|
function setCodexStatsOn(v) {
|
|
12765
12806
|
codexStatsOn = v !== false
|
|
12766
12807
|
if (codexStatsToggle) codexStatsToggle.checked = codexStatsOn
|
|
12767
|
-
var after = function () {
|
|
12808
|
+
var after = function () {
|
|
12809
|
+
// v748:这里原来调的是 openApiModelPanel 内部的局部函数 refreshModelList(),
|
|
12810
|
+
// 在顶层作用域里根本不存在 → 异常被 try/catch 吞掉,"重取模型列表"其实从没发生过。
|
|
12811
|
+
// 现在按别处同一套做法刷新:模型列表 + (若开着)记账子界面,再原地刷新 Codex 用量行
|
|
12812
|
+
// (用量行的文字来自刚取回的模型列表,所以要等回调,不能立刻读旧缓存)
|
|
12813
|
+
try { loadApiModels(refreshOpenCodexRow, true) } catch (err) { refreshOpenCodexRow() }
|
|
12814
|
+
try { if (usagePanelOpen && usageSet !== null) rebuildUsageSubShell() } catch (err) {}
|
|
12815
|
+
}
|
|
12768
12816
|
var p = null
|
|
12769
12817
|
try { p = saveConfig() } catch (err) {}
|
|
12770
12818
|
// 等服务端确认落盘后再重取模型列表:否则可能读到旧配置,那一行会先显示上一次的状态再跳变
|
package/lib/index.js
CHANGED
|
@@ -985,7 +985,7 @@ export default {
|
|
|
985
985
|
const days = new Set([today, ...Object.keys(led.history || {}), ...accountingDays(led)])
|
|
986
986
|
for (const e of events) if (/^\d{4}-\d{2}-\d{2}$/.test(e.day)) days.add(e.day)
|
|
987
987
|
return {
|
|
988
|
-
ok: true, version: '0.3.
|
|
988
|
+
ok: true, version: '0.3.9', today: todayData, days7,
|
|
989
989
|
total7: total7ByCurrency[todayData.currency] || 0, total7Currency: todayData.currency, total7ByCurrency,
|
|
990
990
|
all: {
|
|
991
991
|
days: Array.from(days).filter(d => /^\d{4}-\d{2}-\d{2}$/.test(d)).sort().reverse().map(forDay),
|
|
@@ -1373,22 +1373,42 @@ export default {
|
|
|
1373
1373
|
// 构建都会试一次),而 readSizeConfig() 是同步文件读 —— 不能放在热路径上。
|
|
1374
1374
|
// 用户改设置时 codexInvalidate() 会清掉缓存,所以开关仍然是"立刻生效"。
|
|
1375
1375
|
let codexStatsOnV = null, codexStatsOnAt = 0
|
|
1376
|
+
// v748:**没配置 Codex 模型时「本机统计」默认不启用** —— 完全不去读 ~/.codex/sessions
|
|
1377
|
+
// (用户没加 Codex 模型时那些日志与他无关,白扫一遍既费磁盘又占事件循环)。
|
|
1378
|
+
// 判定按模板 kind === 'codex'(与模型列表里"Codex 用量"那一行的判定同源)。
|
|
1379
|
+
function hasCodexModel() {
|
|
1380
|
+
try {
|
|
1381
|
+
for (const m of readApiRegistry().models) {
|
|
1382
|
+
const tpl = apiTemplateOf(m && m.provider)
|
|
1383
|
+
if (tpl && tpl.kind === 'codex') return true
|
|
1384
|
+
}
|
|
1385
|
+
} catch (err) {}
|
|
1386
|
+
return false
|
|
1387
|
+
}
|
|
1376
1388
|
function codexStatsOn() {
|
|
1377
1389
|
const now = Date.now()
|
|
1378
1390
|
if (codexStatsOnV !== null && now - codexStatsOnAt < 2000) return codexStatsOnV
|
|
1379
1391
|
let v = true
|
|
1380
1392
|
try {
|
|
1381
|
-
|
|
1382
|
-
|
|
1393
|
+
if (!hasCodexModel()) {
|
|
1394
|
+
v = false // 没有 Codex 模型 → 默认关闭(不是"用户关掉的",见下面的提示文案)
|
|
1395
|
+
} else {
|
|
1396
|
+
const cfg = readSizeConfig()
|
|
1397
|
+
v = !cfg || cfg.codexStatsOn !== false
|
|
1398
|
+
}
|
|
1383
1399
|
} catch (err) { v = true }
|
|
1384
1400
|
codexStatsOnV = v
|
|
1385
1401
|
codexStatsOnAt = now
|
|
1386
1402
|
return v
|
|
1387
1403
|
}
|
|
1404
|
+
// 关闭原因的文案:区分「用户自己关的」与「压根没配 Codex 模型(默认关)」
|
|
1405
|
+
function codexDisabledReason() {
|
|
1406
|
+
try { return hasCodexModel() ? 'Codex 统计已在设置里关闭' : '未添加 Codex 模型,本机统计默认关闭' } catch (err) { return 'Codex 统计已关闭' }
|
|
1407
|
+
}
|
|
1388
1408
|
function codexBackgroundRefresh(delayMs) {
|
|
1389
1409
|
if (codexScanP) return codexScanP // 已在扫:先返回,避免重复读配置/重复排队
|
|
1390
1410
|
if (!codexStatsOn()) {
|
|
1391
|
-
codexSnap = { ok: false, disabled: true, error:
|
|
1411
|
+
codexSnap = { ok: false, disabled: true, error: codexDisabledReason() }
|
|
1392
1412
|
codexSnapAt = Date.now()
|
|
1393
1413
|
return null
|
|
1394
1414
|
}
|
|
@@ -1894,6 +1914,8 @@ export default {
|
|
|
1894
1914
|
}
|
|
1895
1915
|
if (changed) writeBubbleConfig(cfg)
|
|
1896
1916
|
} catch (err) {}
|
|
1917
|
+
// v748:删掉的可能是最后一个 Codex 模型 → 立即重算「本机统计」是否启用(含清掉 2s 开关缓存)
|
|
1918
|
+
try { codexInvalidate() } catch (err) {}
|
|
1897
1919
|
return { ok: true, removedModules: removed }
|
|
1898
1920
|
}
|
|
1899
1921
|
// 新增/更新模型;密钥(keyValue)若提供则写入 DSH 官方凭据
|
|
@@ -2003,6 +2025,8 @@ export default {
|
|
|
2003
2025
|
writeApiRegistry(reg)
|
|
2004
2026
|
// 改完单价立即失效缓存:否则 refreshCustomPrices 的 10 秒节流会让新价最坏 10 秒后才生效
|
|
2005
2027
|
customPriceAt = 0
|
|
2028
|
+
// v748:新增/编辑模型可能改变"有没有 Codex 模型",让 Codex 统计开关与快照立刻重算
|
|
2029
|
+
try { codexInvalidate() } catch (err) {}
|
|
2006
2030
|
let keySaved = false
|
|
2007
2031
|
if (p.keyValue !== undefined && String(p.keyValue).length) {
|
|
2008
2032
|
try {
|
|
@@ -2161,7 +2185,7 @@ export default {
|
|
|
2161
2185
|
const summary = daySummary(led, todayKey())
|
|
2162
2186
|
const nowSec = Math.floor(Date.now() / 1000)
|
|
2163
2187
|
return {
|
|
2164
|
-
...visible, version: '0.3.
|
|
2188
|
+
...visible, version: '0.3.9', isPeak: isPeakTime(nowSec),
|
|
2165
2189
|
// 峰谷切换点与节假日清单:前端倒计时要与宿主同源(否则法定节假日会算错切换点)
|
|
2166
2190
|
peakNextChangeAt: nextPeakChangeAt(nowSec),
|
|
2167
2191
|
peakHolidays: HOLIDAY_VALLEY_LIST,
|
package/package.json
CHANGED