dsh-whale-widget 0.3.2 → 0.3.3
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/README.md +15 -0
- package/assets/whale-widget.js +154 -36
- package/lib/index.js +16 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
DeepSeek Harness(DSH)Web 界面右下角的常驻挂件:小鲸鱼气泡图 + DeepSeek API 余额 + 今日已用 + 每轮对话消耗,并且**泡泡内容可以完全自定义**(点击序列、模块化排版、并列加权出泡、随机语句/随机图片)。标准 DSH bundle 插件,`dsh plugin` 一键安装,无需任何会话令牌。
|
|
6
6
|
|
|
7
|
+
## 两条分支怎么选
|
|
8
|
+
|
|
9
|
+
本仓库有两条**互不兼容**的产品线,按你要挂在哪里选一条:
|
|
10
|
+
|
|
11
|
+
| 你要挂在哪 | 用哪条分支 | 安装方式 |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| **DSH Web 界面右下角**(就是这个 README 描述的插件) | `main`(默认分支) | `dsh plugin --profile web add dsh-whale-widget`(推荐,装 npm 已发布版);也可以从本仓库装 `dsh plugin --profile web add github:MeteorNOX/DeepSeek-Balance-Whale-Widget`,但那样装的是 **main 当前状态、不跟随已发布版本** |
|
|
14
|
+
| **Codex 桌面应用**(跟随 Codex 窗口、无独立网页) | [`For-Codex`](https://github.com/MeteorNOX/DeepSeek-Balance-Whale-Widget/tree/For-Codex) | 该分支的 `api-balance-whale`:解压到 `%USERPROFILE%\plugins\api-balance-whale`,再按分支内的 [安装说明](https://github.com/MeteorNOX/DeepSeek-Balance-Whale-Widget/blob/For-Codex/docs/INSTALL-AND-ROLLBACK-0.2.0.md) 注册计划任务(也可以直接交给 Codex 自己装喵~) |
|
|
15
|
+
|
|
16
|
+
⚠️ **两条分支互不兼容**:`For-Codex` 的插件不能用 `dsh plugin … add` 装进 DSH 网页;本主分支的插件也不能在 Codex 桌面里运行。上表第一行是本仓库默认分支(`main`)的能力,第二行是另一个分支的能力。
|
|
17
|
+
|
|
18
|
+
⚠️ 同一分支的两种安装方式(npm 名 / `github:`)**二选一、别混用** —— 它们是同一个包名,先后安装会并存冲突;已经是 `github:` 装的,要换 npm 名请先 `dsh plugin --profile web remove dsh-whale-widget`。
|
|
19
|
+
|
|
20
|
+
> 两条产品线**各自独立维护**(无共享代码/无跨分支依赖):`main` = DSH Web 插件,`For-Codex` = Codex 桌面端口,各自的版本线、发布方式与测试互不影响。
|
|
21
|
+
|
|
7
22
|
## 特性
|
|
8
23
|
|
|
9
24
|
### 记账与显示
|
package/assets/whale-widget.js
CHANGED
|
@@ -11,38 +11,145 @@ window.__dshWhaleWidget = true
|
|
|
11
11
|
function dshwIsChatRoot(r) {
|
|
12
12
|
return !!(r && (r.querySelector('textarea') || r.querySelector('[contenteditable="true"]')))
|
|
13
13
|
}
|
|
14
|
-
var
|
|
14
|
+
var dshwStarted = false
|
|
15
|
+
function dshwStartOnce() {
|
|
16
|
+
if (dshwStarted) return
|
|
17
|
+
dshwStarted = true
|
|
18
|
+
try { dshwInit() } catch (err) {}
|
|
19
|
+
}
|
|
20
|
+
// 是否已到主聊天界面;是则启动(只启动一次,之后由 dshwInit 内部标记去重)
|
|
21
|
+
var dshwLastCheck = 0
|
|
22
|
+
function dshwTryStart(force) {
|
|
23
|
+
if (dshwStarted) return true
|
|
24
|
+
var now = Date.now()
|
|
25
|
+
// 连续 DOM 变化时合并检查,避免每次 mutation 都 querySelector
|
|
26
|
+
if (!force && now - dshwLastCheck < 200) return false
|
|
27
|
+
dshwLastCheck = now
|
|
28
|
+
try {
|
|
29
|
+
if (dshwIsChatRoot(document.getElementById('root'))) { dshwStartOnce(); return true }
|
|
30
|
+
} catch (err) {}
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
15
33
|
try {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
//
|
|
22
|
-
var
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return
|
|
34
|
+
if (!dshwIsChatRoot(document.getElementById('root'))) {
|
|
35
|
+
// 尚未渲染:MutationObserver 无限等待(v739 去掉原来的「5 秒死线」)。
|
|
36
|
+
// 原来 500ms × 10 次就永久放弃,而 window.__dshWhaleWidget 是一次性闸门 ——
|
|
37
|
+
// 慢启动机器、或页面最小化时定时器被浏览器节流,都会让"第一次没赶上"变成
|
|
38
|
+
// "这次会话永远不出现"(用户反馈 / issue #102 第 2 条)。
|
|
39
|
+
// 约束不变:检测到 composer 之前一行 DOM 都不碰、不注册任何全局监听。
|
|
40
|
+
var dshwObserver = null
|
|
41
|
+
try {
|
|
42
|
+
if (typeof MutationObserver === 'function') {
|
|
43
|
+
dshwObserver = new MutationObserver(function () {
|
|
44
|
+
if (dshwTryStart()) { try { dshwObserver.disconnect() } catch (err) {} }
|
|
45
|
+
})
|
|
46
|
+
dshwObserver.observe(document.documentElement || document.body, { childList: true, subtree: true })
|
|
30
47
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
48
|
+
} catch (err) {}
|
|
49
|
+
// 兜底:observer 不可用时低频轮询继续等(不设上限,找到即停)
|
|
50
|
+
var dshwFallbackPoll = setInterval(function () {
|
|
51
|
+
if (dshwTryStart(true)) {
|
|
52
|
+
clearInterval(dshwFallbackPoll)
|
|
53
|
+
try { if (dshwObserver) dshwObserver.disconnect() } catch (err) {}
|
|
34
54
|
}
|
|
35
|
-
},
|
|
55
|
+
}, 2000)
|
|
36
56
|
}
|
|
37
57
|
} catch (err) {}
|
|
38
|
-
if (!dshwEnabled) {
|
|
39
|
-
// 非主界面(或等待超时):不初始化挂件
|
|
40
|
-
return
|
|
41
|
-
}
|
|
42
58
|
function dshwInit() {
|
|
43
59
|
if (window.__dshWhaleInit) return
|
|
44
60
|
window.__dshWhaleInit = true
|
|
45
61
|
|
|
62
|
+
// ===== v739:音效改用 Web Audio 播放(不再用 <audio> / HTMLAudioElement)=====
|
|
63
|
+
// 为什么改:HTMLAudioElement 会被 macOS 注册进系统「正在播放」,带 Touch Bar 的机器上
|
|
64
|
+
// 每次按小鲸鱼都会弹出音频播放条并持续动画 → 明显卡顿(用户反馈)。AudioBufferSourceNode
|
|
65
|
+
// 不经过媒体元素,系统媒体控件不会出现,也没有那层额外的解码/合成开销。
|
|
66
|
+
// 兼容做法:给一个与 HTMLAudioElement 常用表面一致的 shim(play/pause/currentTime/volume/
|
|
67
|
+
// onended/preload/src),这样既有的播放、音量、重播、结束回调逻辑一行都不用改。
|
|
68
|
+
var dshwvAudioCtx = null
|
|
69
|
+
function dshwvAudio() {
|
|
70
|
+
try {
|
|
71
|
+
if (!dshwvAudioCtx) dshwvAudioCtx = new (window.AudioContext || window.webkitAudioContext)()
|
|
72
|
+
if (dshwvAudioCtx.state === 'suspended') { try { dshwvAudioCtx.resume() } catch (err) {} }
|
|
73
|
+
return dshwvAudioCtx
|
|
74
|
+
} catch (err) { return null }
|
|
75
|
+
}
|
|
76
|
+
var dshwvAudioBuffers = {} // url -> Promise<AudioBuffer>(解码结果缓存,同一片段不重复下载/解码)
|
|
77
|
+
function dshwvAudioBuffer(url) {
|
|
78
|
+
if (!url) return Promise.reject(new Error('empty url'))
|
|
79
|
+
if (!dshwvAudioBuffers[url]) {
|
|
80
|
+
dshwvAudioBuffers[url] = fetch(url, { cache: 'force-cache' })
|
|
81
|
+
.then(function (r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.arrayBuffer() })
|
|
82
|
+
.then(function (raw) {
|
|
83
|
+
var c = dshwvAudio()
|
|
84
|
+
if (!c) throw new Error('no audio context')
|
|
85
|
+
return new Promise(function (res, rej) { c.decodeAudioData(raw, res, rej) })
|
|
86
|
+
})
|
|
87
|
+
.catch(function (err) { delete dshwvAudioBuffers[url]; throw err })
|
|
88
|
+
}
|
|
89
|
+
return dshwvAudioBuffers[url]
|
|
90
|
+
}
|
|
91
|
+
function dshwvSoundStop(el) {
|
|
92
|
+
el._token = (el._token || 0) + 1
|
|
93
|
+
var node = el._node
|
|
94
|
+
el._node = null
|
|
95
|
+
if (node) {
|
|
96
|
+
try { node.onended = null } catch (err) {}
|
|
97
|
+
try { node.stop() } catch (err) {}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function dshwvSound(url) {
|
|
101
|
+
var el = { preload: 'auto', volume: 1, onended: null, loop: false, _url: String(url || ''), _node: null, _gain: null, _offset: 0, _token: 0 }
|
|
102
|
+
Object.defineProperty(el, 'src', {
|
|
103
|
+
get: function () { return el._url },
|
|
104
|
+
set: function (v) { dshwvSoundStop(el); el._url = String(v || ''); el._offset = 0 },
|
|
105
|
+
})
|
|
106
|
+
Object.defineProperty(el, 'currentTime', {
|
|
107
|
+
get: function () { return el._offset },
|
|
108
|
+
// 既有逻辑用「currentTime = 0」表示重播 → 这里顺手停掉正在播的那一份
|
|
109
|
+
set: function (v) { el._offset = Number(v) || 0; dshwvSoundStop(el) },
|
|
110
|
+
})
|
|
111
|
+
el.play = function () {
|
|
112
|
+
var c = dshwvAudio()
|
|
113
|
+
if (!c || !el._url) return Promise.resolve()
|
|
114
|
+
try {
|
|
115
|
+
if (!el._gain) { el._gain = c.createGain(); el._gain.connect(c.destination) }
|
|
116
|
+
el._gain.gain.value = Math.max(0, Math.min(1, Number(el.volume) || 0))
|
|
117
|
+
} catch (err) { return Promise.resolve() }
|
|
118
|
+
var token = (el._token = (el._token || 0) + 1)
|
|
119
|
+
var url = el._url
|
|
120
|
+
dshwvAudioBuffer(url).then(function (buf) {
|
|
121
|
+
if (token !== el._token) return // 期间被重播/暂停/换源 → 丢弃这次
|
|
122
|
+
try {
|
|
123
|
+
var src = c.createBufferSource()
|
|
124
|
+
src.buffer = buf
|
|
125
|
+
src.connect(el._gain)
|
|
126
|
+
src.onended = function () {
|
|
127
|
+
if (el._node !== src) return
|
|
128
|
+
el._node = null
|
|
129
|
+
if (typeof el.onended === 'function') { try { el.onended() } catch (err) {} }
|
|
130
|
+
}
|
|
131
|
+
el._node = src
|
|
132
|
+
var dur = Math.max(0.001, buf.duration)
|
|
133
|
+
src.start(0, Math.max(0, el._offset) % dur)
|
|
134
|
+
} catch (err) {}
|
|
135
|
+
}).catch(function () {})
|
|
136
|
+
return Promise.resolve()
|
|
137
|
+
}
|
|
138
|
+
el.pause = function () { dshwvSoundStop(el) }
|
|
139
|
+
return el
|
|
140
|
+
}
|
|
141
|
+
// 自动播放策略:AudioContext 初始是 suspended,要有一次用户手势才能出声。
|
|
142
|
+
// 任务结束音不是手势触发的,所以先挂一次性解锁(首次点击/按键后移除)。
|
|
143
|
+
try {
|
|
144
|
+
var dshwvAudioUnlock = function () {
|
|
145
|
+
dshwvAudio()
|
|
146
|
+
try { document.removeEventListener('pointerdown', dshwvAudioUnlock, true) } catch (err) {}
|
|
147
|
+
try { document.removeEventListener('keydown', dshwvAudioUnlock, true) } catch (err) {}
|
|
148
|
+
}
|
|
149
|
+
document.addEventListener('pointerdown', dshwvAudioUnlock, true)
|
|
150
|
+
document.addEventListener('keydown', dshwvAudioUnlock, true)
|
|
151
|
+
} catch (err) {}
|
|
152
|
+
|
|
46
153
|
var MIN_SCALE = 0.6
|
|
47
154
|
var MAX_SCALE = 2.5
|
|
48
155
|
var STEP = 0.1
|
|
@@ -1006,7 +1113,7 @@ function playTaskEndSound() {
|
|
|
1006
1113
|
url = '/dsh-whale/sound/' + (parts[2] === 'release' ? 'release' : 'press') + '.mp3?set=' + parts[1]
|
|
1007
1114
|
}
|
|
1008
1115
|
if (!url) return
|
|
1009
|
-
var a =
|
|
1116
|
+
var a = dshwvSound(url)
|
|
1010
1117
|
try { a.volume = Number(soundVol) || 0.9 } catch (err) {}
|
|
1011
1118
|
a.play().catch(function () {})
|
|
1012
1119
|
} catch (err) {}
|
|
@@ -1025,7 +1132,7 @@ function playTaskEndGroupClick(groupId) {
|
|
|
1025
1132
|
// 按压留空:无按下音,直接播松开(模拟按下即松开的完整点按);松开留空:只播按压
|
|
1026
1133
|
if (pressEmpty) {
|
|
1027
1134
|
if (!releaseEmpty) {
|
|
1028
|
-
var relOnly =
|
|
1135
|
+
var relOnly = dshwvSound('/dsh-whale/sound/release.mp3?set=' + encodeURIComponent(groupId))
|
|
1029
1136
|
try { relOnly.volume = vol } catch (err) {}
|
|
1030
1137
|
relOnly.currentTime = 0
|
|
1031
1138
|
var pr = relOnly.play()
|
|
@@ -1033,7 +1140,7 @@ function playTaskEndGroupClick(groupId) {
|
|
|
1033
1140
|
}
|
|
1034
1141
|
return
|
|
1035
1142
|
}
|
|
1036
|
-
var press =
|
|
1143
|
+
var press = dshwvSound('/dsh-whale/sound/press.mp3?set=' + encodeURIComponent(groupId))
|
|
1037
1144
|
try { press.volume = vol } catch (err) {}
|
|
1038
1145
|
if (releaseEmpty) {
|
|
1039
1146
|
press.currentTime = 0
|
|
@@ -1041,7 +1148,7 @@ function playTaskEndGroupClick(groupId) {
|
|
|
1041
1148
|
if (pp && pp.catch) pp.catch(function () {})
|
|
1042
1149
|
return
|
|
1043
1150
|
}
|
|
1044
|
-
var release =
|
|
1151
|
+
var release = dshwvSound('/dsh-whale/sound/release.mp3?set=' + encodeURIComponent(groupId))
|
|
1045
1152
|
try { release.volume = vol } catch (err) {}
|
|
1046
1153
|
var relPlayed = false
|
|
1047
1154
|
function playRel() {
|
|
@@ -3484,7 +3591,7 @@ function resPlayFragment(fid) {
|
|
|
3484
3591
|
try {
|
|
3485
3592
|
if (!fid) return
|
|
3486
3593
|
if (resAudEl) { try { resAudEl.pause() } catch (err) {} resAudEl = null }
|
|
3487
|
-
var a =
|
|
3594
|
+
var a = dshwvSound('/dsh-whale/audio-fragment.wav?id=' + encodeURIComponent(fid))
|
|
3488
3595
|
try { a.volume = Number(soundVol) || 0.9 } catch (err) {}
|
|
3489
3596
|
a.onended = function () { resAudEl = null }
|
|
3490
3597
|
resAudEl = a
|
|
@@ -12136,6 +12243,16 @@ function artCenterAt(left, top, w, h, flipped) {
|
|
|
12136
12243
|
var cy = top + h - iw / 2
|
|
12137
12244
|
return { cx: cx, cy: cy }
|
|
12138
12245
|
}
|
|
12246
|
+
// v739(用户反馈「每次新实例的第一次余额请求都失败」):冷启动时凭据服务可能还没就绪,
|
|
12247
|
+
// 首次 DNS+TLS 也最慢;而客户端 25s 超时会先于宿主的两段重试结束 —— 结果是第一次必失败、
|
|
12248
|
+
// 只能干等 60 秒后的下一轮。这里失败后快速重试两次(1.5s / 3s),成功即重置计数。
|
|
12249
|
+
var balanceRetryLeft = 2
|
|
12250
|
+
function balanceRetryLater() {
|
|
12251
|
+
if (balanceRetryLeft <= 0) return
|
|
12252
|
+
var delay = balanceRetryLeft === 2 ? 1500 : 3000
|
|
12253
|
+
balanceRetryLeft--
|
|
12254
|
+
setTimeout(function () { try { refresh(false) } catch (err) {} }, delay)
|
|
12255
|
+
}
|
|
12139
12256
|
function refresh(manual) {
|
|
12140
12257
|
if (busy) return
|
|
12141
12258
|
busy = true
|
|
@@ -12158,6 +12275,7 @@ function refresh(manual) {
|
|
|
12158
12275
|
state.balance = nb
|
|
12159
12276
|
state.currency = nc
|
|
12160
12277
|
state.message = ''
|
|
12278
|
+
balanceRetryLeft = 2
|
|
12161
12279
|
state.todayUsage = data.todayUsage !== undefined ? data.todayUsage : null
|
|
12162
12280
|
state.todayUsageCurrency = data.todayUsageCurrency || data.currency || 'CNY'
|
|
12163
12281
|
state.usageLabel = data.usageLabel || '本地估算'
|
|
@@ -12193,12 +12311,14 @@ function refresh(manual) {
|
|
|
12193
12311
|
state.status = 'error'
|
|
12194
12312
|
state.message = (data && data.error) ? String(data.error) : '获取失败'
|
|
12195
12313
|
render()
|
|
12314
|
+
balanceRetryLater()
|
|
12196
12315
|
}
|
|
12197
12316
|
})
|
|
12198
12317
|
.catch(function () {
|
|
12199
12318
|
state.status = 'error'
|
|
12200
12319
|
state.message = '获取失败'
|
|
12201
12320
|
render()
|
|
12321
|
+
balanceRetryLater()
|
|
12202
12322
|
})
|
|
12203
12323
|
.finally(function () {
|
|
12204
12324
|
busy = false
|
|
@@ -12438,12 +12558,12 @@ function applySoundSet() {
|
|
|
12438
12558
|
var pEmpty = audioGroupSlotEmpty(soundSet, 'press')
|
|
12439
12559
|
var rEmpty = audioGroupSlotEmpty(soundSet, 'release')
|
|
12440
12560
|
if (pEmpty) { pressAudio = null } else {
|
|
12441
|
-
pressAudio =
|
|
12561
|
+
pressAudio = dshwvSound('/dsh-whale/sound/press.mp3?set=' + soundSet)
|
|
12442
12562
|
pressAudio.preload = 'auto'
|
|
12443
12563
|
pressAudio.volume = soundVol
|
|
12444
12564
|
}
|
|
12445
12565
|
if (rEmpty) { releaseAudio = null } else {
|
|
12446
|
-
releaseAudio =
|
|
12566
|
+
releaseAudio = dshwvSound('/dsh-whale/sound/release.mp3?set=' + soundSet)
|
|
12447
12567
|
releaseAudio.preload = 'auto'
|
|
12448
12568
|
releaseAudio.volume = soundVol
|
|
12449
12569
|
}
|
|
@@ -13477,12 +13597,12 @@ function audioEditPreviewEnsure(force) {
|
|
|
13477
13597
|
if (audioEditPreviewRelease) { audioEditPreviewRelease.pause(); audioEditPreviewRelease = null }
|
|
13478
13598
|
} catch (err) {}
|
|
13479
13599
|
if (pressId) {
|
|
13480
|
-
audioEditPreviewEl =
|
|
13600
|
+
audioEditPreviewEl = dshwvSound('/dsh-whale/audio-fragment.wav?id=' + encodeURIComponent(pressId))
|
|
13481
13601
|
audioEditPreviewEl.preload = 'auto'
|
|
13482
13602
|
audioEditPreviewEl.volume = soundVol
|
|
13483
13603
|
}
|
|
13484
13604
|
if (releaseId) {
|
|
13485
|
-
audioEditPreviewRelease =
|
|
13605
|
+
audioEditPreviewRelease = dshwvSound('/dsh-whale/audio-fragment.wav?id=' + encodeURIComponent(releaseId))
|
|
13486
13606
|
audioEditPreviewRelease.preload = 'auto'
|
|
13487
13607
|
audioEditPreviewRelease.volume = soundVol
|
|
13488
13608
|
}
|
|
@@ -14644,8 +14764,6 @@ function pollLastTurn() {
|
|
|
14644
14764
|
}
|
|
14645
14765
|
setInterval(pollLastTurn, 1000)
|
|
14646
14766
|
}
|
|
14647
|
-
//
|
|
14648
|
-
|
|
14649
|
-
try { dshwInit() } catch (err) {}
|
|
14650
|
-
}
|
|
14767
|
+
// 主界面检测通过(或稍后由 MutationObserver 检测到)后执行挂件初始化;非主界面不启动
|
|
14768
|
+
try { dshwTryStart(true) } catch (err) {}
|
|
14651
14769
|
})()
|
package/lib/index.js
CHANGED
|
@@ -716,11 +716,19 @@ export default {
|
|
|
716
716
|
}
|
|
717
717
|
|
|
718
718
|
async function fetchBalance() {
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
719
|
+
// v739(用户反馈「每次新实例的第一次余额请求都失败」):
|
|
720
|
+
// ① 冷启动时凭据服务可能尚未就绪(resolve 抛错),这与「确实没配置」不同 → 短延迟重试一次;
|
|
721
|
+
// ② 单次超时从 20s 收到 8s,保证「2 次尝试 + 退避」明显短于前端 FETCH_TIMEOUT_MS(25s) ——
|
|
722
|
+
// 否则前端先 abort,用户看到的永远是「第一次失败」,只能干等 60 秒后的下一轮。
|
|
723
|
+
let cred = null
|
|
724
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
725
|
+
try {
|
|
726
|
+
cred = await ctx.credentials.resolve('DEEPSEEK_API_KEY')
|
|
727
|
+
break
|
|
728
|
+
} catch (err) {
|
|
729
|
+
if (attempt === 0) { await new Promise((r) => setTimeout(r, 700)); continue }
|
|
730
|
+
return { ok: false, code: 'NO_KEY', error: '凭据读取失败: ' + String((err && err.message) || err).slice(0, 160) }
|
|
731
|
+
}
|
|
724
732
|
}
|
|
725
733
|
if (!cred) {
|
|
726
734
|
return { ok: false, code: 'NO_KEY', error: '未配置 DEEPSEEK_API_KEY' }
|
|
@@ -731,7 +739,7 @@ export default {
|
|
|
731
739
|
try {
|
|
732
740
|
res = await fetch(BALANCE_URL, {
|
|
733
741
|
headers: { Authorization: 'Bearer ' + cred.value },
|
|
734
|
-
signal: AbortSignal.timeout(
|
|
742
|
+
signal: AbortSignal.timeout(8000),
|
|
735
743
|
})
|
|
736
744
|
} catch (err) {
|
|
737
745
|
lastErr = err
|
|
@@ -921,7 +929,7 @@ export default {
|
|
|
921
929
|
const days = new Set([today, ...Object.keys(led.history || {}), ...accountingDays(led)])
|
|
922
930
|
for (const e of events) if (/^\d{4}-\d{2}-\d{2}$/.test(e.day)) days.add(e.day)
|
|
923
931
|
return {
|
|
924
|
-
ok: true, version: '0.3.
|
|
932
|
+
ok: true, version: '0.3.3', today: todayData, days7,
|
|
925
933
|
total7: total7ByCurrency[todayData.currency] || 0, total7Currency: todayData.currency, total7ByCurrency,
|
|
926
934
|
all: {
|
|
927
935
|
days: Array.from(days).filter(d => /^\d{4}-\d{2}-\d{2}$/.test(d)).sort().reverse().map(forDay),
|
|
@@ -1965,7 +1973,7 @@ export default {
|
|
|
1965
1973
|
const led = readUsageLedger()
|
|
1966
1974
|
const summary = daySummary(led, todayKey())
|
|
1967
1975
|
return {
|
|
1968
|
-
...visible, version: '0.3.
|
|
1976
|
+
...visible, version: '0.3.3', isPeak: isPeakTime(Math.floor(Date.now() / 1000)),
|
|
1969
1977
|
todayUsage: summary.amount, todayUsageCurrency: summary.currency,
|
|
1970
1978
|
usageSource: summary.source, usageLabel: summary.label, usageMode: 'ledger',
|
|
1971
1979
|
accounting: balanceSummary(led, todayKey()),
|
package/package.json
CHANGED