dsh-whale-widget 0.3.6 → 0.3.7
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 +100 -41
- package/lib/index.js +16 -6
- package/package.json +1 -1
package/assets/whale-widget.js
CHANGED
|
@@ -18,8 +18,17 @@ function dshwIsChatRoot(r) {
|
|
|
18
18
|
return !!(
|
|
19
19
|
r.querySelector('textarea') ||
|
|
20
20
|
r.querySelector('[contenteditable="true"]') ||
|
|
21
|
+
// DSH 0.1.6-alpha.1 起的新版 composer:data-composer-input 在整个 DSH 前端产物里
|
|
22
|
+
// **只出现在对话组件**(v742 时实测:1515 个前端文件里仅 dsh-client-ui-conversation 命中),
|
|
23
|
+
// 所以它是最精确的判据。
|
|
21
24
|
r.querySelector('[data-composer-input]') ||
|
|
22
|
-
|
|
25
|
+
// 同一层的 composer 容器属性(data-composer-seat / -card 也各只有一个出处),
|
|
26
|
+
// 万一将来输入框本身的属性改名,容器还在就仍能识别。
|
|
27
|
+
r.querySelector('[data-composer-seat],[data-composer-card]') ||
|
|
28
|
+
// 通用兜底:多行 textbox。注意**不要**用裸的 [role="textbox"] —— pdf.js 的批注编辑器
|
|
29
|
+
// (dsh-client-ui-sidebar-documentpreview)也会设 role=textbox,用它会把判定放宽到
|
|
30
|
+
// 非输入框的元素;加上 aria-multiline 至少排除掉单行输入框。
|
|
31
|
+
r.querySelector('[role="textbox"][aria-multiline="true"]')
|
|
23
32
|
)
|
|
24
33
|
}
|
|
25
34
|
var dshwStarted = false
|
|
@@ -690,6 +699,38 @@ styleEl.setAttribute('data-plugin', 'dsh-whale-widget')
|
|
|
690
699
|
styleEl.textContent = css
|
|
691
700
|
document.head.appendChild(styleEl)
|
|
692
701
|
|
|
702
|
+
// ===== v743:body 挂载登记器(DOM 守护的基础设施,见下面 dshwReattachRoot)=====
|
|
703
|
+
// 挂件会把 30 多个节点挂到 document.body 上(主节点、菜单、各种遮罩/面板、隐藏的 file input…)。
|
|
704
|
+
// 早先的守护只补挂 root + menuBox:SPA 切路由或别的插件整体替换 body 子树后,其余节点会变成
|
|
705
|
+
// "存在但不在文档里"的孤儿 —— 界面看起来恢复了,可一旦点开对应功能就静默失效。
|
|
706
|
+
// 所以这里统一登记:所有挂到 body 的节点都走 dshwBodyAppend(),守护时逐个补挂;
|
|
707
|
+
// 主动移除(目前只有一处)走 dshwBodyDetach(),避免被守护逻辑"复活"。
|
|
708
|
+
var dshwBodyNodes = []
|
|
709
|
+
function dshwBodyAppend(el) {
|
|
710
|
+
try {
|
|
711
|
+
if (!el) return el
|
|
712
|
+
// 注意:这里必须是**原始**的 document.body.appendChild —— 不能走 dshwBodyAppend 自己
|
|
713
|
+
// (v743 批量改写时曾误替换成自我递归,被 try/catch 吞掉后表现为"登记了但从未挂上")
|
|
714
|
+
document.body.appendChild(el)
|
|
715
|
+
if (dshwBodyNodes.indexOf(el) < 0) dshwBodyNodes.push(el)
|
|
716
|
+
} catch (err) {}
|
|
717
|
+
return el
|
|
718
|
+
}
|
|
719
|
+
function dshwBodyDetach(el) {
|
|
720
|
+
try {
|
|
721
|
+
var i = dshwBodyNodes.indexOf(el)
|
|
722
|
+
if (i >= 0) dshwBodyNodes.splice(i, 1)
|
|
723
|
+
if (el && el.parentNode) el.parentNode.removeChild(el)
|
|
724
|
+
} catch (err) {}
|
|
725
|
+
}
|
|
726
|
+
// 兼容性:老 WebView 可能没有 Element.isConnected(Chrome 51+ 才有)。
|
|
727
|
+
// 若直接 `!el.isConnected`,在那种环境里会恒为 true → 每个 DOM 变更批次都会重复 appendChild。
|
|
728
|
+
function dshwConnected(el) {
|
|
729
|
+
if (!el) return false
|
|
730
|
+
try { if (typeof el.isConnected === 'boolean') return el.isConnected } catch (err) {}
|
|
731
|
+
try { return document.documentElement.contains(el) } catch (err) { return true }
|
|
732
|
+
}
|
|
733
|
+
|
|
693
734
|
var root = document.createElement('div')
|
|
694
735
|
root.className = 'dshwv-root'
|
|
695
736
|
|
|
@@ -774,7 +815,7 @@ audioImportBtn.textContent = '导入'
|
|
|
774
815
|
audioImportBtn.title = '新建/编辑音效组'
|
|
775
816
|
audioGroupBtn.addEventListener('click', function (e) { e.stopPropagation(); toggleAudioGroupPanel() })
|
|
776
817
|
audioImportBtn.addEventListener('click', function (e) { e.stopPropagation(); openAudioGroupEditor(null) })
|
|
777
|
-
|
|
818
|
+
dshwBodyAppend(audioGroupPanel)
|
|
778
819
|
function soundOpt(value, label) {
|
|
779
820
|
var o = document.createElement('option')
|
|
780
821
|
o.value = value
|
|
@@ -910,7 +951,7 @@ function dshwCustSel(sel, opts) {
|
|
|
910
951
|
dshwCustSelClose()
|
|
911
952
|
fill()
|
|
912
953
|
sync()
|
|
913
|
-
if (menu.parentNode !== document.body)
|
|
954
|
+
if (menu.parentNode !== document.body) dshwBodyAppend(menu)
|
|
914
955
|
dshwDropOpen(menu, btn)
|
|
915
956
|
// 可选底部参照元素:展开高度不超过该元素的上缘(内部滚动),用于
|
|
916
957
|
// 主菜单内“任务结束音效”等下拉,避免列表盖住底部「小鲸鱼记账」按钮
|
|
@@ -1991,7 +2032,7 @@ function usageAlertBudgetEditor(key, onSave) {
|
|
|
1991
2032
|
mask.appendChild(card)
|
|
1992
2033
|
mask.addEventListener('click', function (e) { if (e.target === mask) cleanup() })
|
|
1993
2034
|
window.__dshwRemindMask = mask
|
|
1994
|
-
|
|
2035
|
+
dshwBodyAppend(mask)
|
|
1995
2036
|
renderBubblePal()
|
|
1996
2037
|
renderBubblePv()
|
|
1997
2038
|
} catch (err) {}
|
|
@@ -2563,7 +2604,7 @@ function openApiModelPanel(modelId) {
|
|
|
2563
2604
|
} catch (err) {}
|
|
2564
2605
|
mask.appendChild(card)
|
|
2565
2606
|
mask.addEventListener('click', function (e) { if (e.target === mask) closeApiModelPanel() })
|
|
2566
|
-
|
|
2607
|
+
dshwBodyAppend(mask)
|
|
2567
2608
|
apiModelMaskEl = mask
|
|
2568
2609
|
// 模板切换:把模板默认值填进各字段(仅新增态)
|
|
2569
2610
|
if (isNew) {
|
|
@@ -2914,7 +2955,7 @@ function openModelQuotaEditor(modelId) {
|
|
|
2914
2955
|
card.appendChild(btns)
|
|
2915
2956
|
mask.appendChild(card)
|
|
2916
2957
|
mask.addEventListener('click', function (e) { if (e.target === mask) closeApiModelPanel() })
|
|
2917
|
-
|
|
2958
|
+
dshwBodyAppend(mask)
|
|
2918
2959
|
apiModelMaskEl = mask
|
|
2919
2960
|
} catch (err) {}
|
|
2920
2961
|
}
|
|
@@ -3102,7 +3143,7 @@ function openApiModelMenu(modelId) {
|
|
|
3102
3143
|
card.appendChild(btns)
|
|
3103
3144
|
mask.appendChild(card)
|
|
3104
3145
|
mask.addEventListener('click', function (e) { if (e.target === mask) closeApiModelPanel() })
|
|
3105
|
-
|
|
3146
|
+
dshwBodyAppend(mask)
|
|
3106
3147
|
apiModelMaskEl = mask
|
|
3107
3148
|
} catch (err) {}
|
|
3108
3149
|
}
|
|
@@ -3260,7 +3301,8 @@ function openBalanceAdjustment(modelId) {
|
|
|
3260
3301
|
function close() {
|
|
3261
3302
|
if (busy) return
|
|
3262
3303
|
document.removeEventListener('keydown', keyHandler)
|
|
3263
|
-
|
|
3304
|
+
// v743:主动移除要走 dshwBodyDetach —— 否则它已被登记,DOM 守护会把这个刚关掉的窗口"复活"
|
|
3305
|
+
dshwBodyDetach(mask)
|
|
3264
3306
|
accountingMask = null
|
|
3265
3307
|
// 关掉校正窗口后回到同一个设置菜单,并把焦点放回「校正」按钮
|
|
3266
3308
|
openApiModelMenu(modelId)
|
|
@@ -3293,7 +3335,7 @@ function openBalanceAdjustment(modelId) {
|
|
|
3293
3335
|
card.appendChild(actions)
|
|
3294
3336
|
mask.appendChild(card)
|
|
3295
3337
|
mask.addEventListener('click', function (e) { if (e.target === mask) close() })
|
|
3296
|
-
|
|
3338
|
+
dshwBodyAppend(mask)
|
|
3297
3339
|
accountingMask = mask
|
|
3298
3340
|
document.addEventListener('keydown', keyHandler)
|
|
3299
3341
|
var rows = []
|
|
@@ -3489,7 +3531,7 @@ var usageMoreCard = document.createElement('div')
|
|
|
3489
3531
|
usageMoreCard.className = 'dshwv-usage-card'
|
|
3490
3532
|
usageMoreMask.appendChild(usageMoreCard)
|
|
3491
3533
|
usageMoreMask.addEventListener('click', function (e) { if (e.target === usageMoreMask) closeUsageRecordsWindow() })
|
|
3492
|
-
|
|
3534
|
+
dshwBodyAppend(usageMoreMask)
|
|
3493
3535
|
function openUsageRecordsWindow() {
|
|
3494
3536
|
usageMoreCard.innerHTML = '<div style="padding:10px;color:#203170">加载中…</div>'
|
|
3495
3537
|
usageMoreMask.style.display = 'flex'
|
|
@@ -3512,7 +3554,7 @@ function resMaskOpen() {
|
|
|
3512
3554
|
resCardEl.className = 'dshwv-usage-card dshwv-rescard'
|
|
3513
3555
|
resMaskEl.appendChild(resCardEl)
|
|
3514
3556
|
resMaskEl.addEventListener('click', function (e) { if (e.target === resMaskEl) resManagerClose() })
|
|
3515
|
-
|
|
3557
|
+
dshwBodyAppend(resMaskEl)
|
|
3516
3558
|
}
|
|
3517
3559
|
resManagerRender()
|
|
3518
3560
|
resMaskEl.style.display = 'flex'
|
|
@@ -4112,7 +4154,7 @@ function usagePopupCard(title, content, below, amount) {
|
|
|
4112
4154
|
card.appendChild(btns)
|
|
4113
4155
|
mask.appendChild(card)
|
|
4114
4156
|
mask.addEventListener('click', function (e) { if (e.target === mask) { try { document.body.removeChild(mask) } catch (err) {} } })
|
|
4115
|
-
|
|
4157
|
+
dshwBodyAppend(mask)
|
|
4116
4158
|
} catch (err) {}
|
|
4117
4159
|
}
|
|
4118
4160
|
var USAGE_PALETTE = ['#203170', '#e0433f', '#2fa24c', '#b060c8', '#e89a2e', '#3aa6c8', '#d06a8a', '#7a8b2f', '#6a6ad0', '#c84a8a']
|
|
@@ -4617,7 +4659,7 @@ function fillUsageRecordsWindow(d) {
|
|
|
4617
4659
|
renderGroups('')
|
|
4618
4660
|
})
|
|
4619
4661
|
}
|
|
4620
|
-
|
|
4662
|
+
dshwBodyAppend(rolePanel)
|
|
4621
4663
|
|
|
4622
4664
|
// —— 吸附与翻转自定义弹窗 ——
|
|
4623
4665
|
// 预览方框内五条可拖线:左/右/上/下四条吸附区边界 + 红色翻转线(竖中线位置)。
|
|
@@ -5034,7 +5076,7 @@ snapBtns.appendChild(snapBtn('重置', 'dshwv-snapbtn-no', resetSnapEdit))
|
|
|
5034
5076
|
snapBtns.appendChild(snapBtn('确认', 'dshwv-snapbtn-ok', function () { closeSnapModal(true) }))
|
|
5035
5077
|
snapCard.appendChild(snapBtns)
|
|
5036
5078
|
snapMask.appendChild(snapCard)
|
|
5037
|
-
|
|
5079
|
+
dshwBodyAppend(snapMask)
|
|
5038
5080
|
|
|
5039
5081
|
// —— 自定义泡泡:主编辑窗口(点击队列) + 单泡模块编辑 + 模块编辑 ——
|
|
5040
5082
|
var bubbleMask = null
|
|
@@ -6890,7 +6932,7 @@ function qeditEnsure() {
|
|
|
6890
6932
|
qeditEl = document.createElement('div')
|
|
6891
6933
|
qeditEl.className = 'dshwv-qedit'
|
|
6892
6934
|
qeditEl.style.display = 'none'
|
|
6893
|
-
|
|
6935
|
+
dshwBodyAppend(qeditEl)
|
|
6894
6936
|
if (!window.__dshwQeditBound) {
|
|
6895
6937
|
window.__dshwQeditBound = true
|
|
6896
6938
|
document.addEventListener('pointerdown', function (e) {
|
|
@@ -8212,7 +8254,7 @@ bubbleBtns.appendChild(bubbleBtn('重置', 'dshwv-bubbtn-no', bubbleEditorReset)
|
|
|
8212
8254
|
bubbleBtns.appendChild(bubbleBtn('保存', 'dshwv-bubbtn-ok', bubbleEditorSave))
|
|
8213
8255
|
bubbleCard.appendChild(bubbleBtns)
|
|
8214
8256
|
bubbleMask.appendChild(bubbleCard)
|
|
8215
|
-
|
|
8257
|
+
dshwBodyAppend(bubbleMask)
|
|
8216
8258
|
|
|
8217
8259
|
// ===== W2 单泡编辑窗口 =====
|
|
8218
8260
|
bubbleItemMask = document.createElement('div')
|
|
@@ -8296,7 +8338,7 @@ bubbleItemBtns.appendChild(bubbleBtn('恢复默认', 'dshwv-bubbtn-no', bubbleIt
|
|
|
8296
8338
|
bubbleItemBtns.appendChild(bubbleBtn('保存', 'dshwv-bubbtn-ok', bubbleItemSave))
|
|
8297
8339
|
bubbleItemCard.appendChild(bubbleItemBtns)
|
|
8298
8340
|
bubbleItemMask.appendChild(bubbleItemCard)
|
|
8299
|
-
|
|
8341
|
+
dshwBodyAppend(bubbleItemMask)
|
|
8300
8342
|
|
|
8301
8343
|
// ===== W3 模块编辑器(文本/随机/图片 + 样式) =====
|
|
8302
8344
|
var moduleMask = null
|
|
@@ -8432,7 +8474,7 @@ function whaleZClean() {
|
|
|
8432
8474
|
// 菜单宽度与触发按钮保持一致(原样式 min-width:100% 在 body 下会按视口撑满,须归零)
|
|
8433
8475
|
function dshwDropOpen(menuEl, anchorEl) {
|
|
8434
8476
|
try {
|
|
8435
|
-
if (menuEl.parentNode !== document.body)
|
|
8477
|
+
if (menuEl.parentNode !== document.body) dshwBodyAppend(menuEl)
|
|
8436
8478
|
menuEl.style.position = 'fixed'
|
|
8437
8479
|
menuEl.style.minWidth = '0px'
|
|
8438
8480
|
menuEl.style.left = '0px'
|
|
@@ -9483,7 +9525,7 @@ moduleBtns.appendChild(saveAsBtn)
|
|
|
9483
9525
|
moduleBtns.appendChild(bubbleBtn('保存', 'dshwv-bubbtn-ok', function () { closeModuleEditor(true) }))
|
|
9484
9526
|
moduleCard.appendChild(moduleBtns)
|
|
9485
9527
|
moduleMask.appendChild(moduleCard)
|
|
9486
|
-
|
|
9528
|
+
dshwBodyAppend(moduleMask)
|
|
9487
9529
|
// 存为可选模块:名称输入弹窗
|
|
9488
9530
|
var moduleNamePromptMask = document.createElement('div')
|
|
9489
9531
|
moduleNamePromptMask.className = 'dshwv-confirmmask'
|
|
@@ -9516,7 +9558,7 @@ moduleNameBtns.appendChild(moduleNameCancel)
|
|
|
9516
9558
|
moduleNameBtns.appendChild(moduleNameOk)
|
|
9517
9559
|
moduleNamePromptCard.appendChild(moduleNameBtns)
|
|
9518
9560
|
moduleNamePromptMask.appendChild(moduleNamePromptCard)
|
|
9519
|
-
|
|
9561
|
+
dshwBodyAppend(moduleNamePromptMask)
|
|
9520
9562
|
// 回车保存/关闭
|
|
9521
9563
|
moduleNameInput.addEventListener('keydown', function (e) {
|
|
9522
9564
|
try {
|
|
@@ -9629,7 +9671,7 @@ cropCard.appendChild(cropZoomWrap)
|
|
|
9629
9671
|
cropCard.appendChild(cropAngleWrap)
|
|
9630
9672
|
cropCard.appendChild(cropBtns)
|
|
9631
9673
|
cropMask.appendChild(cropCard)
|
|
9632
|
-
|
|
9674
|
+
dshwBodyAppend(cropMask)
|
|
9633
9675
|
cropBox.addEventListener('pointerdown', onCropDown)
|
|
9634
9676
|
cropBox.addEventListener('pointermove', onCropMove)
|
|
9635
9677
|
cropBox.addEventListener('pointerup', onCropUp)
|
|
@@ -9729,7 +9771,7 @@ gifCard.appendChild(gifHint)
|
|
|
9729
9771
|
gifCard.appendChild(gifNameInput)
|
|
9730
9772
|
gifCard.appendChild(gifBtns)
|
|
9731
9773
|
gifMask.appendChild(gifCard)
|
|
9732
|
-
|
|
9774
|
+
dshwBodyAppend(gifMask)
|
|
9733
9775
|
gifCancelBtn.addEventListener('click', hideGifRoleModal)
|
|
9734
9776
|
gifOkBtn.addEventListener('click', confirmGifRole)
|
|
9735
9777
|
var gifRoleDataUrl = null
|
|
@@ -9810,7 +9852,7 @@ confirmBtns.appendChild(confirmYesBtn)
|
|
|
9810
9852
|
confirmCard.appendChild(confirmText)
|
|
9811
9853
|
confirmCard.appendChild(confirmBtns)
|
|
9812
9854
|
confirmMask.appendChild(confirmCard)
|
|
9813
|
-
|
|
9855
|
+
dshwBodyAppend(confirmMask)
|
|
9814
9856
|
confirmNoBtn.addEventListener('click', hideConfirm)
|
|
9815
9857
|
confirmYesBtn.addEventListener('click', function () {
|
|
9816
9858
|
var cb = confirmCb
|
|
@@ -9902,7 +9944,7 @@ audioEditCard.appendChild(audioEditPressRow)
|
|
|
9902
9944
|
audioEditCard.appendChild(audioEditReleaseRow)
|
|
9903
9945
|
audioEditCard.appendChild(audioEditBtns)
|
|
9904
9946
|
audioEditMask.appendChild(audioEditCard)
|
|
9905
|
-
|
|
9947
|
+
dshwBodyAppend(audioEditMask)
|
|
9906
9948
|
audioEditCancel.addEventListener('click', hideAudioEditor)
|
|
9907
9949
|
audioEditPlay.addEventListener('pointerdown', function (e) { e.stopPropagation(); audioEditPreviewDown() })
|
|
9908
9950
|
audioEditPlay.addEventListener('pointerup', function (e) { e.stopPropagation(); audioEditPreviewUp() })
|
|
@@ -10055,7 +10097,7 @@ audioCropCard.appendChild(audioCropTime)
|
|
|
10055
10097
|
audioCropCard.appendChild(audioCropNameRow)
|
|
10056
10098
|
audioCropCard.appendChild(audioCropBtns)
|
|
10057
10099
|
audioCropMask.appendChild(audioCropCard)
|
|
10058
|
-
|
|
10100
|
+
dshwBodyAppend(audioCropMask)
|
|
10059
10101
|
audioCropCancel.addEventListener('click', hideAudioCrop)
|
|
10060
10102
|
audioCropOk.addEventListener('click', confirmAudioCrop)
|
|
10061
10103
|
audioCropPlay.addEventListener('click', previewAudioCrop)
|
|
@@ -10126,8 +10168,8 @@ body.appendChild(img)
|
|
|
10126
10168
|
body.appendChild(bubbleBox)
|
|
10127
10169
|
root.appendChild(body)
|
|
10128
10170
|
root.appendChild(menuBtn)
|
|
10129
|
-
|
|
10130
|
-
|
|
10171
|
+
dshwBodyAppend(root)
|
|
10172
|
+
dshwBodyAppend(menuBox)
|
|
10131
10173
|
|
|
10132
10174
|
// ===== PR #105 后半:DOM 守护(SPA 切路由 / 别的插件替换 body 子树时把节点摘掉)=====
|
|
10133
10175
|
// 背景:DSH 是 SPA,切到会话列表 / 设置 / 插件市场再回来、或其它客户端插件整体替换
|
|
@@ -10135,19 +10177,29 @@ document.body.appendChild(menuBox)
|
|
|
10135
10177
|
// 做法:暴露 window.__dshWhaleRoot 供外部定位/调试,并用一个 MutationObserver 盯着;
|
|
10136
10178
|
// 一旦发现节点已不在文档里就**把同一个节点补挂回 body**(不重建、不重新初始化,
|
|
10137
10179
|
// 位置/设置/状态全部保留)。
|
|
10180
|
+
// v743:补挂范围从 root+menuBox 扩到**全部登记过的 body 节点**(见 dshwBodyNodes)——
|
|
10181
|
+
// 否则被整体替换后,遮罩/面板/隐藏 file input 会变成孤儿,功能静默失效;
|
|
10182
|
+
// 另外 root 还在、只有个别节点被摘掉的"局部移除"也要能自愈,所以做了节流的全量核对。
|
|
10138
10183
|
try { window.__dshWhaleRoot = root } catch (err) {}
|
|
10139
10184
|
function dshwReattachRoot() {
|
|
10140
10185
|
try {
|
|
10141
|
-
|
|
10142
|
-
|
|
10143
|
-
|
|
10144
|
-
|
|
10186
|
+
for (var i = 0; i < dshwBodyNodes.length; i++) {
|
|
10187
|
+
var el = dshwBodyNodes[i]
|
|
10188
|
+
if (el && !dshwConnected(el)) dshwBodyAppend(el)
|
|
10189
|
+
}
|
|
10145
10190
|
} catch (err) {}
|
|
10146
10191
|
}
|
|
10147
10192
|
try {
|
|
10148
10193
|
if (typeof MutationObserver === 'function') {
|
|
10194
|
+
var dshwGuardLastFull = 0
|
|
10149
10195
|
var dshwRootGuard = new MutationObserver(function () {
|
|
10150
|
-
|
|
10196
|
+
try {
|
|
10197
|
+
if (!root) return
|
|
10198
|
+
if (!dshwConnected(root)) { dshwReattachRoot(); dshwGuardLastFull = Date.now(); return }
|
|
10199
|
+
// root 正常时也定期全量核对一次(最多 1.5 秒一次):覆盖"只有个别浮层被摘掉"的情况
|
|
10200
|
+
var now = Date.now()
|
|
10201
|
+
if (now - dshwGuardLastFull > 1500) { dshwGuardLastFull = now; dshwReattachRoot() }
|
|
10202
|
+
} catch (err) {}
|
|
10151
10203
|
})
|
|
10152
10204
|
dshwRootGuard.observe(document.documentElement, { childList: true, subtree: true })
|
|
10153
10205
|
}
|
|
@@ -11236,7 +11288,7 @@ function bubbleTplHelpToggle(m, anchor) {
|
|
|
11236
11288
|
if (!dshwvTplHelpEl) {
|
|
11237
11289
|
dshwvTplHelpEl = document.createElement('div')
|
|
11238
11290
|
dshwvTplHelpEl.className = 'dshwv-tplhelp'
|
|
11239
|
-
|
|
11291
|
+
dshwBodyAppend(dshwvTplHelpEl)
|
|
11240
11292
|
document.addEventListener('pointerdown', function (e) {
|
|
11241
11293
|
if (!dshwvTplHelpEl || dshwvTplHelpEl.style.display === 'none') return
|
|
11242
11294
|
try {
|
|
@@ -11280,7 +11332,7 @@ function dshwvHintEnsure() {
|
|
|
11280
11332
|
dshwvHintEl = document.createElement('div')
|
|
11281
11333
|
dshwvHintEl.className = 'dshwv-tplhelp dshwv-hintbox'
|
|
11282
11334
|
dshwvHintEl.style.display = 'none'
|
|
11283
|
-
|
|
11335
|
+
dshwBodyAppend(dshwvHintEl)
|
|
11284
11336
|
document.addEventListener('pointerdown', function (e) {
|
|
11285
11337
|
if (!dshwvHintEl || dshwvHintEl.style.display === 'none') return
|
|
11286
11338
|
try {
|
|
@@ -12420,7 +12472,7 @@ function dshwvToast(msg) {
|
|
|
12420
12472
|
'max-width:min(560px,calc(100vw - 32px));box-sizing:border-box;padding:10px 14px;border-radius:10px;' +
|
|
12421
12473
|
'background:#8a1f1f;color:#fff;font-size:13px;line-height:1.6;box-shadow:0 6px 20px rgba(0,0,0,.28);' +
|
|
12422
12474
|
'pointer-events:none;text-align:center'
|
|
12423
|
-
|
|
12475
|
+
dshwBodyAppend(el)
|
|
12424
12476
|
dshwvToastEl = el
|
|
12425
12477
|
}
|
|
12426
12478
|
dshwvToastEl.innerHTML = msg
|
|
@@ -12459,12 +12511,15 @@ function configPut(payload, retried) {
|
|
|
12459
12511
|
}
|
|
12460
12512
|
function saveConfig() {
|
|
12461
12513
|
// issue #97:加载完成前只记待办,绝不 PUT(否则把默认值整包写进服务端)
|
|
12462
|
-
if (!configLoaded) { configSavePending = true; return }
|
|
12514
|
+
if (!configLoaded) { configSavePending = true; return null }
|
|
12463
12515
|
try {
|
|
12464
|
-
|
|
12516
|
+
// 返回 Promise(v743:Codex 统计开关需要"等服务端确认后再刷新")。
|
|
12517
|
+
// configPut 自带重试与失败提示、且链尾有 catch,所以不会有 unhandled rejection。
|
|
12518
|
+
var p = configPut(configPayload(), false)
|
|
12465
12519
|
// 锚点位置记忆:记录相对边框的离边距离,窗口 resize 后保持(localStorage)。
|
|
12466
12520
|
saveAnchorPos()
|
|
12467
|
-
|
|
12521
|
+
return p
|
|
12522
|
+
} catch (err) { return null }
|
|
12468
12523
|
}
|
|
12469
12524
|
// 单独抽出:只写 localStorage 锚点(不碰尺寸设置);applyAnchorPos / settle 自愈时也要用。
|
|
12470
12525
|
// v:2 = 净距离格式(剥离避让距离),v:1 旧格式含避让距离,恢复时废弃旧格式。
|
|
@@ -12572,8 +12627,12 @@ function setMenuBtnHide(v) {
|
|
|
12572
12627
|
function setCodexStatsOn(v) {
|
|
12573
12628
|
codexStatsOn = v !== false
|
|
12574
12629
|
if (codexStatsToggle) codexStatsToggle.checked = codexStatsOn
|
|
12575
|
-
|
|
12576
|
-
|
|
12630
|
+
var after = function () { try { refreshModelList() } catch (err) {} }
|
|
12631
|
+
var p = null
|
|
12632
|
+
try { p = saveConfig() } catch (err) {}
|
|
12633
|
+
// 等服务端确认落盘后再重取模型列表:否则可能读到旧配置,那一行会先显示上一次的状态再跳变
|
|
12634
|
+
if (p && typeof p.then === 'function') p.then(after, after)
|
|
12635
|
+
else after()
|
|
12577
12636
|
}
|
|
12578
12637
|
function scaleToDisplay(s) {
|
|
12579
12638
|
return Math.round((s - MIN_SCALE) / ((MAX_SCALE - MIN_SCALE) / 19)) + 1
|
|
@@ -13819,7 +13878,7 @@ var audioCropFileInput = document.createElement('input')
|
|
|
13819
13878
|
audioCropFileInput.type = 'file'
|
|
13820
13879
|
audioCropFileInput.accept = 'audio/*'
|
|
13821
13880
|
audioCropFileInput.style.display = 'none'
|
|
13822
|
-
|
|
13881
|
+
dshwBodyAppend(audioCropFileInput)
|
|
13823
13882
|
var audioCropTarget = null // 'press' | 'release'
|
|
13824
13883
|
var audioCropCtx = null // AudioContext
|
|
13825
13884
|
var audioCropBuffer = null // AudioBuffer
|
package/lib/index.js
CHANGED
|
@@ -929,7 +929,7 @@ export default {
|
|
|
929
929
|
const days = new Set([today, ...Object.keys(led.history || {}), ...accountingDays(led)])
|
|
930
930
|
for (const e of events) if (/^\d{4}-\d{2}-\d{2}$/.test(e.day)) days.add(e.day)
|
|
931
931
|
return {
|
|
932
|
-
ok: true, version: '0.3.
|
|
932
|
+
ok: true, version: '0.3.7', today: todayData, days7,
|
|
933
933
|
total7: total7ByCurrency[todayData.currency] || 0, total7Currency: todayData.currency, total7ByCurrency,
|
|
934
934
|
all: {
|
|
935
935
|
days: Array.from(days).filter(d => /^\d{4}-\d{2}-\d{2}$/.test(d)).sort().reverse().map(forDay),
|
|
@@ -1312,20 +1312,30 @@ export default {
|
|
|
1312
1312
|
let codexCatchupT = null
|
|
1313
1313
|
// 配置变更(例如用户在设置里关掉 Codex 统计)后必须让快照失效,
|
|
1314
1314
|
// 否则下一次请求还会命中关闭前的旧快照、开关看起来"没生效"。
|
|
1315
|
-
function codexInvalidate() { codexSnap = null; codexSnapAt = 0 }
|
|
1315
|
+
function codexInvalidate() { codexSnap = null; codexSnapAt = 0; codexStatsOnV = null; codexStatsOnAt = 0 }
|
|
1316
|
+
// 开关读取加 2 秒缓存:codexBackgroundRefresh 可能被高频调用(快照过期时每次 payload
|
|
1317
|
+
// 构建都会试一次),而 readSizeConfig() 是同步文件读 —— 不能放在热路径上。
|
|
1318
|
+
// 用户改设置时 codexInvalidate() 会清掉缓存,所以开关仍然是"立刻生效"。
|
|
1319
|
+
let codexStatsOnV = null, codexStatsOnAt = 0
|
|
1316
1320
|
function codexStatsOn() {
|
|
1321
|
+
const now = Date.now()
|
|
1322
|
+
if (codexStatsOnV !== null && now - codexStatsOnAt < 2000) return codexStatsOnV
|
|
1323
|
+
let v = true
|
|
1317
1324
|
try {
|
|
1318
1325
|
const cfg = readSizeConfig()
|
|
1319
|
-
|
|
1320
|
-
} catch (err) {
|
|
1326
|
+
v = !cfg || cfg.codexStatsOn !== false
|
|
1327
|
+
} catch (err) { v = true }
|
|
1328
|
+
codexStatsOnV = v
|
|
1329
|
+
codexStatsOnAt = now
|
|
1330
|
+
return v
|
|
1321
1331
|
}
|
|
1322
1332
|
function codexBackgroundRefresh(delayMs) {
|
|
1333
|
+
if (codexScanP) return codexScanP // 已在扫:先返回,避免重复读配置/重复排队
|
|
1323
1334
|
if (!codexStatsOn()) {
|
|
1324
1335
|
codexSnap = { ok: false, disabled: true, error: 'Codex 统计已在设置里关闭' }
|
|
1325
1336
|
codexSnapAt = Date.now()
|
|
1326
1337
|
return null
|
|
1327
1338
|
}
|
|
1328
|
-
if (codexScanP) return codexScanP
|
|
1329
1339
|
if (delayMs > 0) {
|
|
1330
1340
|
if (codexCatchupT) return null
|
|
1331
1341
|
codexCatchupT = setTimeout(() => { codexCatchupT = null; codexBackgroundRefresh(0) }, delayMs)
|
|
@@ -2094,7 +2104,7 @@ export default {
|
|
|
2094
2104
|
const led = readUsageLedger()
|
|
2095
2105
|
const summary = daySummary(led, todayKey())
|
|
2096
2106
|
return {
|
|
2097
|
-
...visible, version: '0.3.
|
|
2107
|
+
...visible, version: '0.3.7', isPeak: isPeakTime(Math.floor(Date.now() / 1000)),
|
|
2098
2108
|
todayUsage: summary.amount, todayUsageCurrency: summary.currency,
|
|
2099
2109
|
usageSource: summary.source, usageLabel: summary.label, usageMode: 'ledger',
|
|
2100
2110
|
accounting: balanceSummary(led, todayKey()),
|
package/package.json
CHANGED