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.
@@ -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):关掉后宿主不再扫描 ~/.codex/sessions ——
1454
- var codexStatsToggle = document.createElement('input')
1455
- codexStatsToggle.type = 'checkbox'
1456
- codexStatsToggle.className = 'dshwv-check'
1457
- codexStatsToggle.checked = true
1458
- codexStatsToggle.title = '关闭后不再读取 ~/.codex/sessions 统计本机 Codex 用量(会话日志很大时建议关闭)'
1459
- codexStatsToggle.addEventListener('change', function () { setCodexStatsOn(codexStatsToggle.checked) })
1460
- var rowCodexStats = menuRow()
1461
- rowCodexStats.appendChild(menuLabel('Codex 本机统计'))
1462
- rowCodexStats.appendChild(codexStatsToggle)
1463
- menuBox.appendChild(rowCodexStats)
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
- if (apiCodexOf(modelId) && apiCodexOf(modelId).ok) {
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 本机统计开关(默认开)。关掉后宿主完全不扫 ~/.codex/sessions
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 () { try { refreshModelList() } catch (err) {} }
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.8', today: todayData, days7,
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
- const cfg = readSizeConfig()
1382
- v = !cfg || cfg.codexStatsOn !== false
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: 'Codex 统计已在设置里关闭' }
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.8', isPeak: isPeakTime(nowSec),
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-whale-widget",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
4
  "description": "DSH Web 界面右下角的 DeepSeek 余额小鲸鱼挂件:余额/今日已用/峰谷定价、自定义泡泡点击序列(文本/余额/今日/峰谷/图片/随机语句与并列加权选择)、逐行样式与字体、悬浮快捷编辑、音效与每轮消耗、自定义角色/动图/音效、吸附与翻转自定义",
5
5
  "keywords": [
6
6
  "dsh",