deepseek-harness-wallet 0.2.4 → 0.2.5
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/CHANGELOG.md +8 -0
- package/index.js +52 -18
- package/lib/client.js +121 -34
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.2.5 - 2026-08-23
|
|
6
|
+
|
|
7
|
+
- 修复浮动峰谷卡片在窗口缩放、卡片放大或历史坐标过期后可能跑出视口的问题;加载、缩放、排版切换和 resize 都会重新钳制位置。 / Fixed floating peak cards escaping the viewport after resize, scale changes, or stale saved coordinates; load, scale, layout changes, and resize now re-clamp the position.
|
|
8
|
+
- 恢复 Portal 挂载,确保自由浮动卡片和控制面板脱离侧边栏溢出裁剪。 / Restored Portal mounting so floating cards and the control panel escape sidebar overflow clipping.
|
|
9
|
+
- 完成主题回退清理,移除峰谷控制面板、浮动卡片和开关中的硬编码浅色背景/文字回退。 / Completed theme fallback cleanup for the peak panel, floating card, and switches.
|
|
10
|
+
- 保留账户安全、存储权限、刷新异常和偏好同步加固,并避免原始凭证错误文本进入日志或浏览器。 / Kept account-safety, storage-permission, refresh-failure, and preference-sync hardening without exposing raw credential errors to logs or the browser.
|
|
11
|
+
- 新增浮动坐标边界、Portal 和完整主题回退回归测试;当前测试基线为 78/78。 / Added regression coverage for floating-coordinate bounds, Portal mounting, and complete theme fallbacks; the test baseline is now 78/78.
|
|
12
|
+
|
|
5
13
|
## 0.2.4 - 2026-08-22
|
|
6
14
|
|
|
7
15
|
- 彻底修复暗色模式(Dark Mode)主题适配(Issue #26):移除所有未主题化的浅色硬编码回退(`--dsw-alias-bg-elevated,#fff`、`--dsw-alias-bg-overlay,#fff` 等),全站统一使用 DSH 原生主题变量(`--dsw-alias-bg-layer-*` 与 `--dsw-alias-label-dimmed`),在深色模式下完美融入背景。 / Fully fixed Dark Mode theme compliance (Issue #26): replaced unthemed hardcoded light fallbacks with DSH native theme variables (`--dsw-alias-bg-layer-*`), rendering dark themes seamlessly without blinding white boxes.
|
package/index.js
CHANGED
|
@@ -78,7 +78,9 @@ export const PRICE_POLICIES = [
|
|
|
78
78
|
export function ratesFor(model, atMs) {
|
|
79
79
|
let entry
|
|
80
80
|
for (const policy of PRICE_POLICIES) {
|
|
81
|
-
|
|
81
|
+
// Own-property check: a model named `__proto__`/`toString` would otherwise
|
|
82
|
+
// resolve to an Object.prototype member and produce NaN costs.
|
|
83
|
+
if (atMs >= policy.since && Object.hasOwn(policy.models, model)) entry = policy
|
|
82
84
|
}
|
|
83
85
|
if (entry === undefined) return null
|
|
84
86
|
const rates = entry.models[model]
|
|
@@ -258,9 +260,10 @@ function persistStore(logger) {
|
|
|
258
260
|
saveTimer = null
|
|
259
261
|
}
|
|
260
262
|
try {
|
|
261
|
-
mkdirSync(dirname(STORE_PATH), { recursive: true })
|
|
263
|
+
mkdirSync(dirname(STORE_PATH), { recursive: true, mode: 0o700 })
|
|
262
264
|
const tmp = STORE_PATH + '.tmp'
|
|
263
|
-
|
|
265
|
+
// Owner-only: the store carries usage accounting next to credential files.
|
|
266
|
+
writeFileSync(tmp, JSON.stringify(store), { mode: 0o600 })
|
|
264
267
|
renameSync(tmp, STORE_PATH)
|
|
265
268
|
} catch (error) {
|
|
266
269
|
if (logger && typeof logger.warn === 'function') {
|
|
@@ -325,9 +328,10 @@ function persistAccounts(logger) {
|
|
|
325
328
|
accountsSaveTimer = null
|
|
326
329
|
}
|
|
327
330
|
try {
|
|
328
|
-
mkdirSync(dirname(ACCOUNTS_PATH), { recursive: true })
|
|
331
|
+
mkdirSync(dirname(ACCOUNTS_PATH), { recursive: true, mode: 0o700 })
|
|
329
332
|
const tmp = ACCOUNTS_PATH + '.tmp'
|
|
330
|
-
|
|
333
|
+
// This file holds plaintext API keys: owner-only, never group/world readable.
|
|
334
|
+
writeFileSync(tmp, JSON.stringify(accounts), { mode: 0o600 })
|
|
331
335
|
renameSync(tmp, ACCOUNTS_PATH)
|
|
332
336
|
} catch (error) {
|
|
333
337
|
if (logger && typeof logger.warn === 'function') {
|
|
@@ -436,15 +440,20 @@ async function resolveBalanceKey(ctx) {
|
|
|
436
440
|
*/
|
|
437
441
|
export async function activateAccount(ctx, id) {
|
|
438
442
|
const account = findAccount(id)
|
|
439
|
-
if (account === null) return { ok: false, error: 'account
|
|
443
|
+
if (account === null) return { ok: false, error: 'account-not-found' }
|
|
440
444
|
const credentials = ctx.get('credentials')
|
|
441
|
-
if (credentials === undefined) return { ok: false, error: 'credentials
|
|
445
|
+
if (credentials === undefined) return { ok: false, error: 'credentials-unavailable' }
|
|
442
446
|
try {
|
|
443
447
|
await credentials.set(CREDENTIAL_REF, account.apiKey)
|
|
444
448
|
} catch (error) {
|
|
445
449
|
// e.g. the launching environment supplies DEEPSEEK_API_KEY read-only, so
|
|
446
|
-
// the write is shadowed and refused by the credentials provider.
|
|
447
|
-
|
|
450
|
+
// the write is shadowed and refused by the credentials provider. Keep the
|
|
451
|
+
// browser error bounded and do not log provider text that might contain
|
|
452
|
+
// paths, environment details, or key fragments.
|
|
453
|
+
if (ctx.logger && typeof ctx.logger.warn === 'function') {
|
|
454
|
+
ctx.logger.warn('dsh-wallet: credential write refused')
|
|
455
|
+
}
|
|
456
|
+
return { ok: false, error: 'credential-write-refused' }
|
|
448
457
|
}
|
|
449
458
|
accounts.activeId = account.id
|
|
450
459
|
scheduleAccountsSave(ctx.logger)
|
|
@@ -509,12 +518,12 @@ let balance = { fetchedAt: 0, available: false, balances: [], error: null }
|
|
|
509
518
|
let balanceRefresh = null
|
|
510
519
|
|
|
511
520
|
async function performBalanceRefresh(ctx) {
|
|
512
|
-
const credentials = ctx.get('credentials')
|
|
513
|
-
if (credentials === undefined) {
|
|
514
|
-
balance = { fetchedAt: Date.now(), available: false, balances: [], error: 'no-credentials' }
|
|
515
|
-
return
|
|
516
|
-
}
|
|
517
521
|
try {
|
|
522
|
+
const credentials = ctx.get('credentials')
|
|
523
|
+
if (credentials === undefined) {
|
|
524
|
+
balance = { fetchedAt: Date.now(), available: false, balances: [], error: 'no-credentials' }
|
|
525
|
+
return
|
|
526
|
+
}
|
|
518
527
|
const key = await resolveBalanceKey(ctx)
|
|
519
528
|
if (key === undefined || key === '') {
|
|
520
529
|
balance = { fetchedAt: Date.now(), available: false, balances: [], error: 'no-api-key' }
|
|
@@ -564,9 +573,24 @@ async function performBalanceRefresh(ctx) {
|
|
|
564
573
|
|
|
565
574
|
function refreshBalance(ctx) {
|
|
566
575
|
if (balanceRefresh !== null) return balanceRefresh
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
576
|
+
// Never let a refresh escape as an unhandled rejection: every caller uses
|
|
577
|
+
// `void refreshBalance(ctx)`, so a throw here would take the host process
|
|
578
|
+
// down (Node exits on unhandled rejections). Absorb it into the error state.
|
|
579
|
+
balanceRefresh = performBalanceRefresh(ctx)
|
|
580
|
+
.catch((error) => {
|
|
581
|
+
balance = {
|
|
582
|
+
fetchedAt: Date.now(),
|
|
583
|
+
available: false,
|
|
584
|
+
balances: [],
|
|
585
|
+
error: 'balance-unavailable',
|
|
586
|
+
}
|
|
587
|
+
if (ctx && ctx.logger && typeof ctx.logger.warn === 'function') {
|
|
588
|
+
ctx.logger.warn('dsh-wallet: balance refresh failed')
|
|
589
|
+
}
|
|
590
|
+
})
|
|
591
|
+
.finally(() => {
|
|
592
|
+
balanceRefresh = null
|
|
593
|
+
})
|
|
570
594
|
return balanceRefresh
|
|
571
595
|
}
|
|
572
596
|
|
|
@@ -609,8 +633,18 @@ function balanceTotal() {
|
|
|
609
633
|
|
|
610
634
|
function sessionView(sessionId) {
|
|
611
635
|
if (sessionId === undefined || sessionId === '') return { official: null, third: null }
|
|
636
|
+
// Own-property lookup only: session ids like `__proto__`, `constructor` or
|
|
637
|
+
// `toString` otherwise resolve to an Object.prototype member, slip past the
|
|
638
|
+
// undefined guard, and throw inside bucketTotals.
|
|
639
|
+
if (!Object.prototype.hasOwnProperty.call(store.sessions, sessionId)) {
|
|
640
|
+
return { official: null, third: null }
|
|
641
|
+
}
|
|
612
642
|
const session = store.sessions[sessionId]
|
|
613
|
-
if (session ===
|
|
643
|
+
if (session === null || typeof session !== 'object'
|
|
644
|
+
|| session.official === null || typeof session.official !== 'object'
|
|
645
|
+
|| session.third === null || typeof session.third !== 'object') {
|
|
646
|
+
return { official: null, third: null }
|
|
647
|
+
}
|
|
614
648
|
const official = bucketTotals(session.official)
|
|
615
649
|
const third = bucketTotals(session.third)
|
|
616
650
|
return {
|
package/lib/client.js
CHANGED
|
@@ -23,7 +23,7 @@ window.__ModuleLoader__.load({
|
|
|
23
23
|
|
|
24
24
|
var POLL_MS = 15000
|
|
25
25
|
// Keep in lockstep with package.json; a test enforces the sync.
|
|
26
|
-
var WALLET_VERSION = '0.2.
|
|
26
|
+
var WALLET_VERSION = '0.2.5'
|
|
27
27
|
var CONFIRM_KEY = 'dsh-wallet-recharge-confirmed'
|
|
28
28
|
var CHIP_LAYOUT_KEY = 'dshw-chip-layout-v4'
|
|
29
29
|
var PANEL_POS_KEY = 'dshw-panel-pos-v1'
|
|
@@ -62,11 +62,18 @@ window.__ModuleLoader__.load({
|
|
|
62
62
|
? root.__DSH_WALLET_ADAPTER__
|
|
63
63
|
: {}
|
|
64
64
|
var memory = Object.create(null)
|
|
65
|
+
// Keys whose native write was refused (quota, private mode, sandboxed
|
|
66
|
+
// WebView). Reads for those keys must come from memory, otherwise the
|
|
67
|
+
// fallback write is invisible and the UI keeps reading a stale value.
|
|
68
|
+
var memoryOnly = Object.create(null)
|
|
65
69
|
var nativeStorage = extension.storage || root.localStorage || null
|
|
66
70
|
var pageNotices = new Map()
|
|
67
71
|
|
|
68
72
|
var storage = {
|
|
69
73
|
getItem: function (key) {
|
|
74
|
+
if (memoryOnly[key] === true) {
|
|
75
|
+
return Object.prototype.hasOwnProperty.call(memory, key) ? memory[key] : null
|
|
76
|
+
}
|
|
70
77
|
if (nativeStorage && typeof nativeStorage.getItem === 'function') {
|
|
71
78
|
try { return nativeStorage.getItem(key) } catch (e) { /* storage may be disabled */ }
|
|
72
79
|
}
|
|
@@ -76,13 +83,19 @@ window.__ModuleLoader__.load({
|
|
|
76
83
|
value = String(value)
|
|
77
84
|
memory[key] = value
|
|
78
85
|
if (nativeStorage && typeof nativeStorage.setItem === 'function') {
|
|
79
|
-
try {
|
|
86
|
+
try {
|
|
87
|
+
nativeStorage.setItem(key, value)
|
|
88
|
+
delete memoryOnly[key]
|
|
89
|
+
return
|
|
90
|
+
} catch (e) { /* fall through to the memory-only marker */ }
|
|
80
91
|
}
|
|
92
|
+
memoryOnly[key] = true
|
|
81
93
|
},
|
|
82
94
|
removeItem: function (key) {
|
|
83
95
|
delete memory[key]
|
|
96
|
+
delete memoryOnly[key]
|
|
84
97
|
if (nativeStorage && typeof nativeStorage.removeItem === 'function') {
|
|
85
|
-
try { nativeStorage.removeItem(key); return } catch (e) { /*
|
|
98
|
+
try { nativeStorage.removeItem(key); return } catch (e) { /* memory copy is already gone */ }
|
|
86
99
|
}
|
|
87
100
|
}
|
|
88
101
|
}
|
|
@@ -277,7 +290,7 @@ window.__ModuleLoader__.load({
|
|
|
277
290
|
'.dshw_anchorHome{overflow:hidden;min-width:44px}',
|
|
278
291
|
'.dshw_chipLift{z-index:80!important}',
|
|
279
292
|
'.dshw_anchorHome>.dshw_chip{box-sizing:border-box;max-width:100%;min-width:0;overflow:hidden}',
|
|
280
|
-
'.dshw_chip{border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));background:transparent;height:22px;color:var(--dsw-alias-label-primary
|
|
293
|
+
'.dshw_chip{border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));background:transparent;height:22px;color:var(--dsw-alias-label-primary,inherit);white-space:nowrap;border-radius:999px;align-items:stretch;font-size:12px;line-height:1;display:inline-flex;position:relative;touch-action:none;user-select:none;cursor:grab}',
|
|
281
294
|
'.dshw_chip:hover,.dshw_chip:focus-within{border-color:var(--dsw-alias-brand-primary,#4aa3ff)}',
|
|
282
295
|
'.dshw_chipLow{border-color:var(--dsw-alias-state-error-primary,#e5534b);color:var(--dsw-alias-state-error-primary,#e5534b);animation:dshwChipPulseIn 1.8s ease-in-out infinite}',
|
|
283
296
|
'.dshw_chipLow:hover,.dshw_chipLow:focus-within{border-color:var(--dsw-alias-state-error-primary,#e5534b)}',
|
|
@@ -302,7 +315,7 @@ window.__ModuleLoader__.load({
|
|
|
302
315
|
'.dshw_chipVertical.dshw_chipNoRecharge .dshw_chipMain{border-radius:7px}',
|
|
303
316
|
'.dshw_recharge{color:var(--dsw-alias-brand-primary,#4aa3ff);border-left:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));padding:0 7px 0 6px;border-radius:0 999px 999px 0}',
|
|
304
317
|
'.dshw_chipMain:focus-visible,.dshw_recharge:focus-visible,.dshw_btn:focus-visible,.dshw_floatBtn:focus-visible,.dshw_dot:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4aa3ff);outline-offset:2px}',
|
|
305
|
-
'.dshw_panel{z-index:40;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1
|
|
318
|
+
'.dshw_panel{z-index:40;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base,transparent)));box-sizing:border-box;width:min(276px,calc(100vw - 12px));max-height:calc(100vh - 16px);overflow:auto;color:var(--dsw-alias-label-primary,inherit);border-radius:8px;flex-direction:column;gap:4px;padding:8px 9px;font-size:12px;display:flex;position:fixed;box-shadow:0 4px 16px rgba(0,0,0,.35)}',
|
|
306
319
|
'.dshw_panelHeader{min-height:24px;cursor:move;touch-action:none;user-select:none}',
|
|
307
320
|
'.dshw_panelHeader .dshw_btn{cursor:pointer}',
|
|
308
321
|
'.dshw_row{justify-content:space-between;align-items:center;gap:6px;display:flex}',
|
|
@@ -319,7 +332,7 @@ window.__ModuleLoader__.load({
|
|
|
319
332
|
'.dshw_check input{margin:0;accent-color:var(--dsw-alias-brand-primary,#4aa3ff)}',
|
|
320
333
|
'.dshw_select{background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base,rgba(127,127,127,.08)));border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));color:var(--dsw-alias-label-primary,inherit);border-radius:5px;padding:2px 4px;font-size:12px}',
|
|
321
334
|
'.dshw_btn{border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));background:var(--dsw-alias-bg-layer-1,transparent);color:var(--dsw-alias-label-primary,inherit);border-radius:5px;padding:2px 6px;font-size:12px;cursor:pointer}',
|
|
322
|
-
'.dshw_btnPrimary{background:var(--dsw-alias-brand-primary,#4aa3ff);border-color:transparent;color:var(--dsw-alias-label-primary-foreground
|
|
335
|
+
'.dshw_btnPrimary{background:var(--dsw-alias-brand-primary,#4aa3ff);border-color:transparent;color:var(--dsw-alias-label-primary-foreground,var(--dsw-alias-label-primary,inherit))}',
|
|
323
336
|
// ---- v0.2 panel restyle: balance card, settings card, account scroll ----
|
|
324
337
|
'.dshw_balanceCard{margin:2px 0 2px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.2));border-radius:8px;background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base,rgba(127,127,127,.06)))}',
|
|
325
338
|
'.dshw_balLine{display:flex;align-items:center;gap:7px;white-space:nowrap}',
|
|
@@ -411,7 +424,7 @@ window.__ModuleLoader__.load({
|
|
|
411
424
|
'.dshw_switch{position:relative;flex:none;width:34px;height:20px;display:inline-flex;cursor:pointer}',
|
|
412
425
|
'.dshw_switch>input{position:absolute;inset:0;opacity:0;margin:0;cursor:pointer}',
|
|
413
426
|
'.dshw_track{position:absolute;inset:0;border-radius:999px;background:var(--dsw-alias-border-l3,rgba(127,127,127,.3));transition:background-color .15s}',
|
|
414
|
-
'.dshw_knob{position:absolute;left:2px;top:2px;width:16px;height:16px;border-radius:50%;background:var(--dsw-alias-bg-layer-1
|
|
427
|
+
'.dshw_knob{position:absolute;left:2px;top:2px;width:16px;height:16px;border-radius:50%;background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base,transparent));box-shadow:0 1px 3px rgba(0,0,0,.24);transition:transform .15s}',
|
|
415
428
|
'.dshw_switch>input:checked+.dshw_track{background:var(--dsw-alias-brand-primary,#4aa3ff)}',
|
|
416
429
|
'.dshw_switch>input:checked~.dshw_knob{transform:translateX(14px)}',
|
|
417
430
|
'.dshw_switch>input:focus-visible+.dshw_track{outline:2px solid var(--dsw-alias-brand-primary,#4aa3ff);outline-offset:2px}',
|
|
@@ -442,7 +455,7 @@ window.__ModuleLoader__.load({
|
|
|
442
455
|
'.dshw_footRingVertical .dshw_footRingCountdown{font-size:10.5px;overflow:visible;white-space:nowrap}',
|
|
443
456
|
'.dshw_footRingVertical .dshw_footRingBottom{justify-content:center}',
|
|
444
457
|
'.dshw_footRingNoRecharge{padding-right:10px}',
|
|
445
|
-
'.dshw_footRingFloating{position:fixed;z-index:90;width:auto;min-width:180px;max-width:260px;margin:0!important;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1
|
|
458
|
+
'.dshw_footRingFloating{position:fixed;z-index:90;width:auto;min-width:180px;max-width:260px;margin:0!important;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base,transparent)));box-shadow:0 8px 24px rgba(0,0,0,.35);cursor:grab;touch-action:none}',
|
|
446
459
|
'.dshw_footRingFloating.dshw_footRingVertical{min-width:132px;max-width:152px;width:140px;padding:9px 8px;gap:4px}',
|
|
447
460
|
'.dshw_footRingFloating.dshw_footRingVertical .dshw_footRingResetBtn{position:absolute;top:4px;right:4px;font-size:9.5px;padding:1px 5px;line-height:13px}',
|
|
448
461
|
'.dshw_footRingFloating.dshw_footRingDragging{cursor:grabbing;box-shadow:0 10px 30px rgba(0,0,0,.45)}',
|
|
@@ -459,8 +472,8 @@ window.__ModuleLoader__.load({
|
|
|
459
472
|
'.dshw_footRingBottom{display:flex;align-items:center;justify-content:space-between;gap:6px;width:100%;min-height:18px}',
|
|
460
473
|
'.dshw_footRingCountdown{font-size:11px;color:var(--dsw-alias-label-secondary,rgba(127,127,127,.85));line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1 1 auto;min-width:0}',
|
|
461
474
|
'.dshw_footRingBtnRechargeInline{border:1px solid var(--dsw-alias-brand-primary,#4aa3ff);background:var(--dsw-alias-brand-soft,rgba(74,163,255,.10));color:var(--dsw-alias-brand-primary,#4aa3ff);border-radius:4px;height:18px;padding:0 6px;font-size:10.5px;font-weight:600;line-height:16px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;transition:all .15s ease;user-select:none;flex:none}',
|
|
462
|
-
'.dshw_footRingBtnRechargeInline:hover{background:var(--dsw-alias-brand-primary,#4aa3ff);color
|
|
463
|
-
'.dshw_peakPanel{position:fixed;z-index:95;width:290px;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1
|
|
475
|
+
'.dshw_footRingBtnRechargeInline:hover{background:var(--dsw-alias-brand-primary,#4aa3ff);color:var(--dsw-alias-label-primary-foreground,var(--dsw-alias-label-primary,inherit))}',
|
|
476
|
+
'.dshw_peakPanel{position:fixed;z-index:95;width:290px;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base,transparent)));color:var(--dsw-alias-label-primary,inherit);border-radius:12px;box-shadow:0 10px 32px rgba(0,0,0,.38);display:flex;flex-direction:column;gap:8px;padding:12px 14px;font-size:12px;user-select:none;touch-action:none}',
|
|
464
477
|
'.dshw_peakPanelHeader{display:flex;align-items:center;justify-content:space-between;font-weight:650;font-size:12.5px;color:var(--dsw-alias-label-primary,inherit)}',
|
|
465
478
|
'.dshw_peakPanelClose{border:none;background:transparent;color:inherit;font-size:16px;line-height:1;cursor:pointer;padding:2px 6px;border-radius:4px}',
|
|
466
479
|
'.dshw_peakPanelClose:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.12))}',
|
|
@@ -471,23 +484,23 @@ window.__ModuleLoader__.load({
|
|
|
471
484
|
'.dshw_ringNeutral{stroke:var(--dsw-alias-border-l3,rgba(127,127,127,.3))}',
|
|
472
485
|
'.dshw_peakRing circle{transition:stroke-width .3s ease}',
|
|
473
486
|
'.dshw_ringNow{stroke-width:6;filter:brightness(1.15)}',
|
|
474
|
-
'.dshw_ringPointer{fill:var(--dsw-alias-label-primary
|
|
487
|
+
'.dshw_ringPointer{fill:var(--dsw-alias-label-primary,inherit)}',
|
|
475
488
|
'.dshw_ringTick{fill:var(--dsw-alias-label-dimmed,var(--dsw-alias-label-tertiary,#8b949e))}',
|
|
476
489
|
'@media (max-width:760px){.dshw_settingsGrid,.dshw_settingsSection .dshw_setCard{grid-template-columns:1fr}.dshw_settingsSection .dshw_setCard>.dshw_setCell.wide{grid-column:1}.dshw_accountAdd{grid-template-columns:1fr}.dshw_settingsFooter{align-items:flex-start;flex-direction:column-reverse}.dshw_settingsFooterActions{width:100%}.dshw_settingsFooterActions .dshw_btn{flex:1}.dshw_settingsHeroMeta{white-space:normal;flex-wrap:wrap}}',
|
|
477
490
|
'.dshw_actionRow{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin:2px 0}',
|
|
478
491
|
'.dshw_textDanger{display:inline-flex;align-items:center;gap:5px;height:22px;padding:0 4px;border:none;background:transparent;font:inherit;font-size:11px;color:var(--dsw-alias-label-dimmed,var(--dsw-alias-label-tertiary,#8b949e));cursor:pointer;border-radius:4px;transition:color .12s,background-color .12s}',
|
|
479
492
|
'.dshw_textDanger:hover{color:var(--dsw-alias-state-error-primary,#e5534b);background:var(--dsw-alias-state-error-soft,rgba(229,83,75,.12))}',
|
|
480
493
|
'.dshw_overlay{position:fixed;inset:0;background:var(--dsw-alias-bg-mask-drop,rgba(0,0,0,.55));display:flex;align-items:center;justify-content:center;z-index:100}',
|
|
481
|
-
'.dshw_overlayBox{background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1
|
|
494
|
+
'.dshw_overlayBox{background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base,transparent)));border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));color:var(--dsw-alias-label-primary,inherit);border-radius:8px;padding:14px 18px;width:min(320px,calc(100vw - 32px));box-sizing:border-box;font-size:13px;line-height:1.7}',
|
|
482
495
|
'.dshw_overlayRow{margin-top:10px;display:flex;gap:8px;justify-content:flex-end}',
|
|
483
|
-
'.dshw_float{position:fixed;z-index:80;min-width:230px;max-width:280px;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1
|
|
496
|
+
'.dshw_float{position:fixed;z-index:80;min-width:230px;max-width:280px;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base,transparent)));color:var(--dsw-alias-label-primary,inherit);border-radius:12px;box-shadow:0 6px 20px rgba(0,0,0,.25);display:flex;flex-direction:column;gap:6px;padding:10px 12px;font-size:12px;user-select:none}',
|
|
484
497
|
'.dshw_floatHeader{display:flex;align-items:center;justify-content:space-between;cursor:move;font-weight:600;opacity:.9;touch-action:none}',
|
|
485
498
|
'.dshw_floatBtn{border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));background:transparent;color:inherit;border-radius:5px;width:22px;height:20px;font-size:12px;line-height:1;cursor:pointer;padding:0}',
|
|
486
499
|
'.dshw_floatBtn:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.1))}',
|
|
487
|
-
'.dshw_dot{position:fixed;z-index:80;width:36px;height:36px;border-radius:50%;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1
|
|
500
|
+
'.dshw_dot{position:fixed;z-index:80;width:36px;height:36px;border-radius:50%;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base,transparent)));color:var(--dsw-alias-label-primary,inherit);font-size:11px;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 2px 10px rgba(0,0,0,.25);user-select:none;touch-action:none}',
|
|
488
501
|
'.dshw_dotLow{border-color:var(--dsw-alias-state-error-primary,#e5534b);color:var(--dsw-alias-state-error-primary,#e5534b)}',
|
|
489
502
|
'.dshw_noticeStack{position:fixed;z-index:120;top:max(12px,env(safe-area-inset-top));right:max(12px,env(safe-area-inset-right));width:min(340px,calc(100vw - 24px));display:flex;flex-direction:column;gap:8px;pointer-events:none}',
|
|
490
|
-
'.dshw_notice{box-sizing:border-box;width:100%;display:flex;align-items:flex-start;gap:8px;padding:10px 10px 10px 12px;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));border-radius:9px;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1
|
|
503
|
+
'.dshw_notice{box-sizing:border-box;width:100%;display:flex;align-items:flex-start;gap:8px;padding:10px 10px 10px 12px;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.25));border-radius:9px;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base,transparent)));color:var(--dsw-alias-label-primary,inherit);box-shadow:0 6px 20px rgba(0,0,0,.25);font-size:12px;line-height:1.45;pointer-events:auto;cursor:pointer}',
|
|
491
504
|
'.dshw_noticeCopy{min-width:0;display:flex;flex:1;flex-direction:column;gap:2px}.dshw_noticeCopy span{white-space:pre-line;overflow-wrap:anywhere}',
|
|
492
505
|
'.dshw_noticeClose{flex:none;width:24px;height:24px;border:0;border-radius:5px;background:transparent;color:inherit;font-size:18px;line-height:1;cursor:pointer}.dshw_noticeClose:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.1))}',
|
|
493
506
|
// Home-fit and compact modes are toggled by measured space (see the
|
|
@@ -1085,6 +1098,26 @@ window.__ModuleLoader__.load({
|
|
|
1085
1098
|
return (counters.input || 0) + (counters.output || 0) + (counters.cacheRead || 0) + (counters.cacheWrite || 0)
|
|
1086
1099
|
}
|
|
1087
1100
|
|
|
1101
|
+
// Keep the floating peak card reachable after a viewport resize, a
|
|
1102
|
+
// persisted position from another screen size, or a scale change. `width`
|
|
1103
|
+
// and `height` are the rendered dimensions from getBoundingClientRect(),
|
|
1104
|
+
// so transformed 120% cards are clamped using their actual footprint.
|
|
1105
|
+
function clampPeakPosition(pos, width, height, viewportWidth, viewportHeight, margin) {
|
|
1106
|
+
margin = Number.isFinite(margin) ? Math.max(0, margin) : 8
|
|
1107
|
+
viewportWidth = Number.isFinite(viewportWidth) ? viewportWidth : 1024
|
|
1108
|
+
viewportHeight = Number.isFinite(viewportHeight) ? viewportHeight : 768
|
|
1109
|
+
width = Number.isFinite(width) && width > 0 ? width : 180
|
|
1110
|
+
height = Number.isFinite(height) && height > 0 ? height : 60
|
|
1111
|
+
var x = pos && Number.isFinite(pos.x) ? pos.x : margin
|
|
1112
|
+
var y = pos && Number.isFinite(pos.y) ? pos.y : margin
|
|
1113
|
+
var maxX = Math.max(margin, viewportWidth - width - margin)
|
|
1114
|
+
var maxY = Math.max(margin, viewportHeight - height - margin)
|
|
1115
|
+
return {
|
|
1116
|
+
x: Math.round(Math.min(maxX, Math.max(margin, x))),
|
|
1117
|
+
y: Math.round(Math.min(maxY, Math.max(margin, y))),
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1088
1121
|
/**
|
|
1089
1122
|
* Host settings-panel section: the same wallet controls as the chip panel,
|
|
1090
1123
|
* as an independent card. Shares state with the chip through the same
|
|
@@ -1662,7 +1695,7 @@ window.__ModuleLoader__.load({
|
|
|
1662
1695
|
type: 'button', className: 'dshw_btn dshw_btnPrimary',
|
|
1663
1696
|
onClick: function () { if (close) close(); window.open('https://platform.deepseek.com/top_up', '_blank', 'noopener') }
|
|
1664
1697
|
}, '↗ 去官方充值'))))
|
|
1665
|
-
return React.createElement('div', { className: 'dshw_settingsSection', style: { display: 'flex', flexDirection: 'column', gap: '6px', fontSize: '12px', color: 'var(--dsw-alias-label-primary
|
|
1698
|
+
return React.createElement('div', { className: 'dshw_settingsSection', style: { display: 'flex', flexDirection: 'column', gap: '6px', fontSize: '12px', color: 'var(--dsw-alias-label-primary,inherit)' } }, rows)
|
|
1666
1699
|
}
|
|
1667
1700
|
|
|
1668
1701
|
// Wall-clock hour (0-24, fractional) inside an IANA timezone, via Intl —
|
|
@@ -1809,6 +1842,12 @@ window.__ModuleLoader__.load({
|
|
|
1809
1842
|
var [nowMs, setNowMs] = React.useState(function () { return Date.now() })
|
|
1810
1843
|
var dragRef = React.useRef(null)
|
|
1811
1844
|
var didDragRef = React.useRef(false)
|
|
1845
|
+
// Guards the self-echo: every preference write dispatches the shared
|
|
1846
|
+
// change events so OTHER surfaces resync, but this component must not
|
|
1847
|
+
// re-read storage from its own dispatch — the listener would setState a
|
|
1848
|
+
// second time from a different call stack and race the update already
|
|
1849
|
+
// queued, which can drop the write that started it.
|
|
1850
|
+
var suppressSelfSyncRef = React.useRef(false)
|
|
1812
1851
|
var [panelOpen, setPanelOpen] = React.useState(false)
|
|
1813
1852
|
var [panelPos, setPanelPos] = React.useState({ left: 16, top: 16 })
|
|
1814
1853
|
var cardNodeRef = React.useRef(null)
|
|
@@ -1828,6 +1867,8 @@ window.__ModuleLoader__.load({
|
|
|
1828
1867
|
loadSnap()
|
|
1829
1868
|
var listensEvents = typeof window.addEventListener === 'function'
|
|
1830
1869
|
function onRingChange() {
|
|
1870
|
+
// Skip our own echo (see suppressSelfSyncRef).
|
|
1871
|
+
if (suppressSelfSyncRef.current) return
|
|
1831
1872
|
try {
|
|
1832
1873
|
setShown(compatibility.storage.getItem(PEAK_RING_KEY) !== 'false')
|
|
1833
1874
|
setOrient(compatibility.storage.getItem(PEAK_ORIENT_KEY) || 'horizontal')
|
|
@@ -1956,6 +1997,33 @@ window.__ModuleLoader__.load({
|
|
|
1956
1997
|
} catch (e) { /* ignore */ }
|
|
1957
1998
|
}, [policy, nowMs])
|
|
1958
1999
|
|
|
2000
|
+
var isFreeFloating = dock === 'free'
|
|
2001
|
+
var isVertical = orient === 'vertical' && (wide || isFreeFloating)
|
|
2002
|
+
var isRail = !wide && !isFreeFloating
|
|
2003
|
+
|
|
2004
|
+
// A saved floating coordinate is not trustworthy after a window resize
|
|
2005
|
+
// or scale change. Re-measure the rendered card (including transform)
|
|
2006
|
+
// and persist the corrected coordinate before it can disappear outside
|
|
2007
|
+
// the viewport. This hook must run even while the card is still loading,
|
|
2008
|
+
// otherwise React sees a changing hook count when the snapshot arrives.
|
|
2009
|
+
useLayoutEffect(function () {
|
|
2010
|
+
if (!isFreeFloating || !cardNodeRef.current) return
|
|
2011
|
+
var node = cardNodeRef.current
|
|
2012
|
+
function fitFloatingPeakCard() {
|
|
2013
|
+
if (!node || typeof node.getBoundingClientRect !== 'function') return
|
|
2014
|
+
var rect = node.getBoundingClientRect()
|
|
2015
|
+
var current = latestPosRef.current || pos || { x: rect.left, y: rect.top }
|
|
2016
|
+
var fitted = clampPeakPosition(current, rect.width, rect.height, window.innerWidth, window.innerHeight, 8)
|
|
2017
|
+
if (fitted.x === current.x && fitted.y === current.y && pos !== null) return
|
|
2018
|
+
latestPosRef.current = fitted
|
|
2019
|
+
setPos(fitted)
|
|
2020
|
+
try { compatibility.storage.setItem(PEAK_POS_KEY, JSON.stringify(fitted)) } catch (e) { /* ignore */ }
|
|
2021
|
+
}
|
|
2022
|
+
fitFloatingPeakCard()
|
|
2023
|
+
window.addEventListener('resize', fitFloatingPeakCard)
|
|
2024
|
+
return function () { window.removeEventListener('resize', fitFloatingPeakCard) }
|
|
2025
|
+
}, [isFreeFloating, pos, peakScale, orient, showRecharge])
|
|
2026
|
+
|
|
1959
2027
|
if (!shown || snapshot === undefined) return null
|
|
1960
2028
|
var tzName = policy && policy.timezone ? policy.timezone : 'Asia/Shanghai'
|
|
1961
2029
|
var offsetMinutes = policy && typeof policy.offsetMinutes === 'number' ? policy.offsetMinutes : 480
|
|
@@ -1971,33 +2039,41 @@ window.__ModuleLoader__.load({
|
|
|
1971
2039
|
var costLabel = sessionCostLabel(balCurrency)
|
|
1972
2040
|
var low = snapshot && snapshot.lowBalance === true
|
|
1973
2041
|
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
2042
|
+
// Announce a preference change to the other surfaces (settings page, a
|
|
2043
|
+
// second window) while suppressing our own listener, so this component's
|
|
2044
|
+
// queued setState is the single writer for this interaction.
|
|
2045
|
+
function announcePrefs(includeRingEvent) {
|
|
2046
|
+
suppressSelfSyncRef.current = true
|
|
2047
|
+
try {
|
|
2048
|
+
try { compatibility.dispatch(SETTINGS_EVENT) } catch (e) { /* ignore */ }
|
|
2049
|
+
if (includeRingEvent !== false) {
|
|
2050
|
+
try { compatibility.dispatch(PEAK_RING_EVENT) } catch (e) { /* ignore */ }
|
|
2051
|
+
}
|
|
2052
|
+
} finally {
|
|
2053
|
+
suppressSelfSyncRef.current = false
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
1977
2056
|
|
|
1978
2057
|
function updateOrient(val) {
|
|
1979
2058
|
setOrient(val)
|
|
1980
2059
|
try { compatibility.storage.setItem(PEAK_ORIENT_KEY, val) } catch (e) { /* ignore */ }
|
|
1981
|
-
|
|
1982
|
-
try { compatibility.dispatch(PEAK_RING_EVENT) } catch (e) { /* ignore */ }
|
|
2060
|
+
announcePrefs()
|
|
1983
2061
|
}
|
|
1984
2062
|
function updateScale(val) {
|
|
1985
2063
|
val = Math.min(1.2, Math.max(1.0, Math.round(val * 20) / 20))
|
|
1986
2064
|
setPeakScale(val)
|
|
1987
2065
|
try { compatibility.storage.setItem(PEAK_SCALE_KEY, String(val)) } catch (e) { /* ignore */ }
|
|
1988
|
-
|
|
1989
|
-
try { compatibility.dispatch(PEAK_RING_EVENT) } catch (e) { /* ignore */ }
|
|
2066
|
+
announcePrefs()
|
|
1990
2067
|
}
|
|
1991
2068
|
function updateRecharge(val) {
|
|
1992
2069
|
setShowRecharge(val)
|
|
1993
2070
|
try { compatibility.storage.setItem(PEAK_RECHARGE_KEY, String(val)) } catch (e) { /* ignore */ }
|
|
1994
|
-
|
|
1995
|
-
try { compatibility.dispatch(PEAK_RING_EVENT) } catch (e) { /* ignore */ }
|
|
2071
|
+
announcePrefs()
|
|
1996
2072
|
}
|
|
1997
2073
|
function updateNotify(val) {
|
|
1998
2074
|
setPeakNotify(val)
|
|
1999
2075
|
try { compatibility.storage.setItem(PEAK_NOTIFY_KEY, String(val)) } catch (e) { /* ignore */ }
|
|
2000
|
-
|
|
2076
|
+
announcePrefs(false)
|
|
2001
2077
|
}
|
|
2002
2078
|
|
|
2003
2079
|
function handlePointerDown(e) {
|
|
@@ -2037,11 +2113,10 @@ window.__ModuleLoader__.load({
|
|
|
2037
2113
|
if (dragRef.current.dragged) {
|
|
2038
2114
|
var w = dragRef.current.rectWidth || (isVertical ? 140 : 180)
|
|
2039
2115
|
var h = dragRef.current.rectHeight || (isVertical ? 120 : 60)
|
|
2040
|
-
var
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
var nextPos = { x: Math.round(nextX), y: Math.round(nextY) }
|
|
2116
|
+
var nextPos = clampPeakPosition({
|
|
2117
|
+
x: moveEvent.clientX - dragRef.current.grabOffsetX,
|
|
2118
|
+
y: moveEvent.clientY - dragRef.current.grabOffsetY,
|
|
2119
|
+
}, w, h, window.innerWidth, window.innerHeight, 8)
|
|
2045
2120
|
latestPosRef.current = nextPos
|
|
2046
2121
|
setPos(nextPos)
|
|
2047
2122
|
}
|
|
@@ -2056,9 +2131,20 @@ window.__ModuleLoader__.load({
|
|
|
2056
2131
|
setDragging(false)
|
|
2057
2132
|
setDock('free')
|
|
2058
2133
|
setPos(finalPos)
|
|
2059
|
-
|
|
2134
|
+
announcePrefs(false)
|
|
2060
2135
|
}
|
|
2061
2136
|
dragRef.current = null
|
|
2137
|
+
// Release the "this was a drag, swallow the click" latch after the
|
|
2138
|
+
// click that follows pointerup. Without this the flag stays true
|
|
2139
|
+
// whenever the next pointerdown never arrives (pointer released
|
|
2140
|
+
// outside the window, a cancelled gesture), and every later click on
|
|
2141
|
+
// the card is swallowed — the control panel then refuses to open
|
|
2142
|
+
// until the page is reloaded.
|
|
2143
|
+
if (typeof window.setTimeout === 'function') {
|
|
2144
|
+
window.setTimeout(function () { didDragRef.current = false }, 0)
|
|
2145
|
+
} else {
|
|
2146
|
+
didDragRef.current = false
|
|
2147
|
+
}
|
|
2062
2148
|
window.removeEventListener('pointermove', onMove)
|
|
2063
2149
|
window.removeEventListener('pointerup', onUp)
|
|
2064
2150
|
window.removeEventListener('pointercancel', onUp)
|
|
@@ -2076,11 +2162,11 @@ window.__ModuleLoader__.load({
|
|
|
2076
2162
|
try {
|
|
2077
2163
|
compatibility.storage.setItem(PEAK_DOCK_KEY, 'sidebar')
|
|
2078
2164
|
compatibility.storage.removeItem(PEAK_POS_KEY)
|
|
2079
|
-
compatibility.dispatch(SETTINGS_EVENT)
|
|
2080
|
-
compatibility.dispatch(PEAK_RING_EVENT)
|
|
2081
2165
|
} catch (err) { /* ignore */ }
|
|
2166
|
+
announcePrefs()
|
|
2082
2167
|
}
|
|
2083
2168
|
|
|
2169
|
+
|
|
2084
2170
|
var cardStyle = {}
|
|
2085
2171
|
if (isFreeFloating) {
|
|
2086
2172
|
cardStyle.position = 'fixed'
|
|
@@ -3853,6 +3939,7 @@ window.__ModuleLoader__.load({
|
|
|
3853
3939
|
computeSnapPreview: computeSnapPreview,
|
|
3854
3940
|
computeSideDockX: computeSideDockX,
|
|
3855
3941
|
computePanelPosition: computePanelPosition,
|
|
3942
|
+
clampPeakPosition: clampPeakPosition,
|
|
3856
3943
|
createCompatibilityAdapter: createCompatibilityAdapter,
|
|
3857
3944
|
fmtCurrency: fmtCurrency,
|
|
3858
3945
|
selectBalanceInfo: selectBalanceInfo,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deepseek-harness-wallet",
|
|
3
3
|
"description": "Local-first account monitoring, usage accounting, official recharge, completion reminders, flexible layout, host-gated session controls, and multi-account management with hot account switching for DeepSeek Harness.",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"exports": {
|