deepseek-harness-wallet 0.2.0 → 0.2.1

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 CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## 0.2.1 - 2026-08-20
6
+
7
+ - 标签上「本场」恢复常显(¥0.00 也显示,新会话不再缺席);仅未定价模型仍隐藏。/ The chip shows the session cost again even at ¥0.00; only unpriced models stay hidden.
8
+ - 面板/浮动窗版本号移至底部右下角,不再与顶栏按钮挤压。 / Panel version tags moved to the footer, clear of the header buttons.
9
+
5
10
  ## 0.2.0 - 2026-08-18
6
11
 
7
12
  - 新增多账户管理与热切换:面板内“账户管理”可添加多个账户(名称 + API Key),切换后无需重启,下一次 LLM 调用即按新账户计费;key 界面掩码显示,余额查询跟随当前账户(contributed in PR #4 by mxchen-xyz)。 / Added multi-account management with hot switching: manage accounts in the panel, and the very next LLM call is billed with the newly activated key — no restart needed; keys stay masked in the UI and balance follows the active account.
package/index.js CHANGED
@@ -110,6 +110,41 @@ export function normalizeThreshold(value) {
110
110
  return Math.min(100000, Math.max(0, Math.round(parsed * 100) / 100))
111
111
  }
112
112
 
113
+ // Low-balance thresholds are per-currency: a USD account warns against its
114
+ // own $ threshold, a CNY account against its own ¥ one; they never compare
115
+ // across currencies. The legacy single-value store migrates into CNY.
116
+ // Per-ACCOUNT thresholds: two accounts in the same currency may keep
117
+ // different warning lines ("one account is 1, another is 2"). Keyed by
118
+ // account id; the per-currency map stays as the fallback for the
119
+ // no-active-account (system key) case and as the migration source.
120
+ export function normalizeAccountThresholds(value) {
121
+ const source = value !== null && typeof value === 'object' && !Array.isArray(value) ? value : {}
122
+ const out = {}
123
+ for (const [id, raw] of Object.entries(source)) {
124
+ if (typeof id !== 'string' || id === '' || id.length > 100) continue
125
+ if (id === '__proto__' || id === 'prototype' || id === 'constructor') continue
126
+ const parsed = Number.parseFloat(raw)
127
+ if (!Number.isFinite(parsed)) continue
128
+ out[id] = Math.min(100000, Math.max(0, Math.round(parsed * 100) / 100))
129
+ if (Object.keys(out).length >= 50) break
130
+ }
131
+ return out
132
+ }
133
+
134
+ export function normalizeThresholds(value) {
135
+ const source = value !== null && typeof value === 'object' && !Array.isArray(value) ? value : {}
136
+ const out = {}
137
+ for (const [code, raw] of Object.entries(source)) {
138
+ if (!/^[A-Z]{3}$/.test(code)) continue
139
+ if (code === '__proto__' || code === 'prototype' || code === 'constructor') continue
140
+ const parsed = Number.parseFloat(raw)
141
+ if (!Number.isFinite(parsed)) continue
142
+ out[code] = Math.min(100000, Math.max(0, Math.round(parsed * 100) / 100))
143
+ if (Object.keys(out).length >= 20) break
144
+ }
145
+ return out
146
+ }
147
+
113
148
  function finiteCounter(value) {
114
149
  return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : 0
115
150
  }
@@ -188,7 +223,8 @@ export function normalizeStoreData(value, atMs = Date.now()) {
188
223
  }
189
224
  const normalized = {
190
225
  version: STORE_VERSION,
191
- threshold: normalizeThreshold(source.threshold),
226
+ thresholds: normalizeThresholds(source.thresholds && Object.keys(source.thresholds).length > 0 ? source.thresholds : { CNY: normalizeThreshold(source.threshold) }),
227
+ accountThresholds: normalizeAccountThresholds(source.accountThresholds),
192
228
  sessions,
193
229
  officialProviders: normalizeProviderList(source.officialProviders),
194
230
  knownProviders: normalizeProviderList(source.knownProviders),
@@ -201,7 +237,7 @@ function loadStore() {
201
237
  return normalizeStoreData(JSON.parse(readFileSync(STORE_PATH, 'utf8')))
202
238
  } catch {
203
239
  return {
204
- store: { version: STORE_VERSION, threshold: DEFAULT_THRESHOLD, sessions: {}, officialProviders: [], knownProviders: [] },
240
+ store: { version: STORE_VERSION, thresholds: { CNY: DEFAULT_THRESHOLD }, accountThresholds: {}, sessions: {}, officialProviders: [], knownProviders: [] },
205
241
  migrated: false,
206
242
  }
207
243
  }
@@ -346,6 +382,11 @@ export function removeAccount(id) {
346
382
  const index = accounts.accounts.findIndex((account) => account.id === id)
347
383
  if (index < 0) return { ok: false, error: 'account not found' }
348
384
  accounts.accounts.splice(index, 1)
385
+ // Drop the account's own threshold line along with it.
386
+ if (store.accountThresholds && Object.hasOwn(store.accountThresholds, id)) {
387
+ delete store.accountThresholds[id]
388
+ scheduleAccountsSave(null)
389
+ }
349
390
  // Deliberately NOT unsetting the credentials seam: the key currently in
350
391
  // .credentials.yaml keeps working for LLM billing; the UI just reports
351
392
  // "no active account" and balance falls back to the seam key.
@@ -551,13 +592,14 @@ function sessionView(sessionId) {
551
592
  official: {
552
593
  tokens: official,
553
594
  cost: session.official.priced === true ? session.official.cost : null,
595
+ priced: session.official.priced === true,
554
596
  models: session.official.models,
555
597
  },
556
598
  third: { tokens: third, models: session.third.models },
557
599
  }
558
600
  }
559
601
 
560
- function snapshotView(sessionId, threshold) {
602
+ function snapshotView(sessionId) {
561
603
  const currency = balanceCurrency(balance.balances)
562
604
  const active = activeAccount()
563
605
  return {
@@ -571,10 +613,19 @@ function snapshotView(sessionId, threshold) {
571
613
  error: balance.available ? null : balance.error,
572
614
  },
573
615
  session: sessionView(sessionId),
574
- threshold,
575
- // The configured threshold is CNY-denominated; do not compare unlike
576
- // currencies for international accounts that do not expose a CNY row.
577
- lowBalance: balance.available && currency === 'CNY' && threshold > 0 && balanceTotal() < threshold,
616
+ // Thresholds are per-ACCOUNT first (each account keeps its own line even
617
+ // in the same currency); the per-currency map only serves the
618
+ // no-active-account case and inherits into accounts without one yet.
619
+ threshold: active !== null
620
+ ? (store.accountThresholds[active.id] ?? store.thresholds[currency || 'CNY'] ?? 0)
621
+ : (store.thresholds[currency || 'CNY'] ?? 0),
622
+ lowBalance: balance.available && currency !== null
623
+ && (function () {
624
+ const line = active !== null
625
+ ? (store.accountThresholds[active.id] ?? store.thresholds[currency] ?? 0)
626
+ : (store.thresholds[currency] ?? 0)
627
+ return line > 0 && balanceTotal() < line
628
+ })(),
578
629
  rechargeUrl: RECHARGE_URL,
579
630
  accounts: {
580
631
  activeId: accounts.activeId,
@@ -637,12 +688,10 @@ export function apply(ctx, config) {
637
688
  storeNeedsSave = false
638
689
  scheduleSave(ctx.logger)
639
690
  }
640
- // Threshold lives ONLY in the persisted store; an explicit row config may
641
- // still override it for power users, but the bundle patch sets none.
642
- let threshold = store.threshold
691
+ // Thresholds live ONLY in the persisted store; an explicit row config may
692
+ // still override the CNY line for power users, but the bundle patch sets none.
643
693
  if (Number.isFinite(config.threshold)) {
644
- threshold = normalizeThreshold(config.threshold)
645
- store.threshold = threshold
694
+ store.thresholds = normalizeThresholds({ ...store.thresholds, CNY: normalizeThreshold(config.threshold) })
646
695
  scheduleSave(ctx.logger)
647
696
  }
648
697
 
@@ -694,7 +743,7 @@ export function apply(ctx, config) {
694
743
  path: '/api/wallet/snapshot',
695
744
  handler: (req, res) => {
696
745
  if (req.method !== 'GET') return json(res, 405, { ok: false, error: 'method-not-allowed' })
697
- return json(res, 200, snapshotView(sessionParam(req), threshold))
746
+ return json(res, 200, snapshotView(sessionParam(req)))
698
747
  },
699
748
  })
700
749
  const disposeThreshold = ctx.webServer.register({
@@ -706,10 +755,19 @@ export function apply(ctx, config) {
706
755
  if (body === null || typeof body.threshold !== 'number' || !Number.isFinite(body.threshold)) {
707
756
  return json(res, 400, { ok: false, error: 'threshold must be a number' })
708
757
  }
709
- threshold = Math.min(100000, Math.max(0, Math.round(body.threshold * 100) / 100))
710
- store.threshold = threshold
758
+ const currency = typeof body.currency === 'string' && /^[A-Z]{3}$/.test(body.currency) ? body.currency : (balanceCurrency(balance.balances) || 'CNY')
759
+ if (currency === '__proto__' || currency === 'prototype' || currency === 'constructor') return json(res, 400, { ok: false, error: 'bad currency' })
760
+ const clamped = Math.min(100000, Math.max(0, Math.round(body.threshold * 100) / 100))
761
+ // An active account keeps its own threshold line; only the system-key
762
+ // mode writes the shared per-currency map.
763
+ const active = activeAccount()
764
+ if (active !== null) {
765
+ store.accountThresholds = normalizeAccountThresholds({ ...store.accountThresholds, [active.id]: clamped })
766
+ } else {
767
+ store.thresholds = normalizeThresholds({ ...store.thresholds, [currency]: clamped })
768
+ }
711
769
  scheduleSave(ctx.logger)
712
- return json(res, 200, { ok: true, threshold: threshold })
770
+ return json(res, 200, { ok: true, threshold: clamped, currency, accountId: active !== null ? active.id : null })
713
771
  },
714
772
  })
715
773
  const disposeRefresh = ctx.webServer.register({
@@ -797,7 +855,11 @@ export function apply(ctx, config) {
797
855
  if (body === null || typeof body.id !== 'string') return json(res, 400, { ok: false, error: 'id is required' })
798
856
  const result = await activateAccount(ctx, body.id)
799
857
  if (!result.ok) return json(res, 400, { ok: false, error: result.error })
800
- return json(res, 200, { ok: true, account: result.account })
858
+ // Carry the account's own threshold so the input can jump instantly,
859
+ // before the balance refresh lands.
860
+ const currency = balanceCurrency(balance.balances) || 'CNY'
861
+ const threshold = store.accountThresholds[result.account.id] ?? store.thresholds[currency] ?? 0
862
+ return json(res, 200, { ok: true, account: result.account, threshold })
801
863
  },
802
864
  })
803
865
  const disposeRemove = ctx.webServer.register({
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "integration": "dsh-session-delete",
4
- "packageVersion": "0.1.4",
4
+ "packageVersion": "0.2.0",
5
5
  "upstream": {
6
6
  "repository": "https://github.com/deepseek-ai/DeepSeek-Harness",
7
7
  "commit": "47f943859bef60e4160492346772ded9b24f765a",
package/lib/client.js CHANGED
@@ -14,7 +14,7 @@ window.__ModuleLoader__.load({
14
14
 
15
15
  var POLL_MS = 15000
16
16
  // Keep in lockstep with package.json; a test enforces the sync.
17
- var WALLET_VERSION = '0.2.0'
17
+ var WALLET_VERSION = '0.2.1'
18
18
  var CONFIRM_KEY = 'dsh-wallet-recharge-confirmed'
19
19
  var CHIP_LAYOUT_KEY = 'dshw-chip-layout-v4'
20
20
  var PANEL_POS_KEY = 'dshw-panel-pos-v1'
@@ -23,6 +23,7 @@ window.__ModuleLoader__.load({
23
23
  var NOTIFY_CONFIG_KEY = 'dshw-completion-notify-v1'
24
24
  var NOTIFY_CONFIG_EVENT = 'dshw-completion-notify-change'
25
25
  var SETTINGS_EVENT = 'dshw-settings-change'
26
+ var LOW_BLINK_KEY = 'dshw-low-blink-v1'
26
27
  var NOTIFY_LEADER_KEY = 'dshw-completion-notify-leader-v1'
27
28
  var PERMANENT_DELETE_KEY = 'dshw-permanent-delete-v1'
28
29
  var PERMANENT_DELETE_EVENT = 'dshw-permanent-delete-change'
@@ -259,7 +260,8 @@ window.__ModuleLoader__.load({
259
260
  '.dshw_anchorHome>.dshw_chip{box-sizing:border-box;max-width:100%;min-width:0;overflow:hidden}',
260
261
  '.dshw_chip{border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.15));background:transparent;height:22px;color:var(--dsw-alias-label-primary,#1f2328);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}',
261
262
  '.dshw_chip:hover,.dshw_chip:focus-within{border-color:var(--dsw-alias-brand-primary,#4aa3ff)}',
262
- '.dshw_chipLow{border-color:var(--dsw-alias-state-error-primary,#e5534b);color:var(--dsw-alias-state-error-primary,#e5534b);animation:dshwPulse 1.8s infinite}',
263
+ '.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}',
264
+ '.dshw_chipLow:hover,.dshw_chipLow:focus-within{border-color:var(--dsw-alias-state-error-primary,#e5534b)}',
263
265
  '.dshw_chipDocked{position:fixed;z-index:79;background:var(--dsw-alias-bg-overlay,#fff);box-shadow:0 2px 10px rgba(0,0,0,.16)}',
264
266
  '.dshw_chipDragging{cursor:grabbing;box-shadow:0 6px 18px rgba(0,0,0,.22)}',
265
267
  '.dshw_snapPreview{position:fixed;z-index:78;pointer-events:none;box-sizing:border-box;border:2px dashed var(--dsw-alias-brand-primary,#4aa3ff);background:rgba(74,163,255,.10);box-shadow:0 0 0 3px rgba(74,163,255,.10);border-radius:999px;transition:left .06s ease,top .06s ease,width .06s ease,height .06s ease}',
@@ -272,6 +274,7 @@ window.__ModuleLoader__.load({
272
274
  '.dshw_chipVertical .dshw_sep{display:none}',
273
275
  '.dshw_chipVertical .dshw_recharge{width:100%;box-sizing:border-box;border-left:0;border-top:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.15));padding:3px 1px;justify-content:center;border-radius:0 0 7px 7px}',
274
276
  '.dshw_bottomDockHost{box-sizing:border-box!important;padding-bottom:30px!important}',
277
+ '@keyframes dshwChipPulseIn{0%,100%{box-shadow:inset 0 0 0 0 rgba(229,83,75,0)}50%{box-shadow:inset 0 0 0 3px rgba(229,83,75,.32)}}',
275
278
  '@keyframes dshwPulse{0%,100%{box-shadow:0 0 0 0 rgba(229,83,75,.45)}50%{box-shadow:0 0 0 5px rgba(229,83,75,0)}}',
276
279
  '.dshw_sep{opacity:.45}',
277
280
  '.dshw_chipMain,.dshw_recharge{border:0;background:transparent;color:inherit;font:inherit;cursor:pointer;align-items:center;display:inline-flex;gap:6px;margin:0}',
@@ -318,24 +321,95 @@ window.__ModuleLoader__.load({
318
321
  '.dshw_chipToggle{display:inline-flex;align-items:center;gap:6px;height:24px;padding:0 9px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.15));border-radius:999px;background:transparent;font-size:11.5px;color:var(--dsw-alias-label-secondary,rgba(31,35,40,.68));cursor:pointer;white-space:nowrap;transition:color .12s,border-color .12s,background-color .12s}',
319
322
  '.dshw_chipToggle:hover{border-color:var(--dsw-alias-brand-primary,#4aa3ff)}',
320
323
  '.dshw_chipToggle input{margin:0;accent-color:var(--dsw-alias-brand-primary,#4aa3ff)}',
321
- '.dshw_acctScroll{border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.15));border-radius:8px;overflow-y:auto;max-height:88px;scrollbar-width:thin;scrollbar-color:var(--dsw-alias-border-l3,rgba(0,0,0,.25)) transparent;margin:2px 0}',
324
+ '.dshw_acctScroll{border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.15));border-radius:8px;overflow-y:auto;max-height:58px;scrollbar-width:thin;scrollbar-color:var(--dsw-alias-border-l3,rgba(0,0,0,.25)) transparent;margin:2px 0}',
322
325
  '.dshw_acctScroll::-webkit-scrollbar{width:6px}',
323
326
  '.dshw_acctScroll::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l3,rgba(0,0,0,.25));border-radius:3px}',
324
- '.dshw_acctScroll .dshw_row{padding:5px 8px}',
327
+ '.dshw_acctScroll .dshw_row{padding:2px 6px}',
328
+ '.dshw_balanceCard.dshw_low{border-color:var(--dsw-alias-state-error-primary,#e5534b)}',
329
+ '.dshw_balanceCard.dshw_low .dshw_balNum{color:var(--dsw-alias-state-error-primary,#e5534b)}',
330
+ '.dshw_chipLow .dshw_chipMain>span:not(.dshw_homePrimary):not(.dshw_sep){color:var(--dsw-alias-label-primary,#1f2328)}',
331
+ '.dshw_chipLow .dshw_thirdText .dshw_homePrimaryValue{color:inherit}',
332
+ '.dshw_chipLow .dshw_balanceText .dshw_homePrimaryLabel{color:var(--dsw-alias-label-primary,#1f2328)!important}',
333
+ '.dshw_chipLow .dshw_balanceText .dshw_homePrimaryValue{color:var(--dsw-alias-state-error-primary,#e5534b)!important}',
334
+ '.dshw_chipLow.dshw_chipVertical .dshw_chipMain>.dshw_metric:not(:first-child) .dshw_metricLabel,.dshw_chipLow.dshw_chipVertical .dshw_chipMain>.dshw_metric:not(:first-child) .dshw_metricValue{color:var(--dsw-alias-label-primary,#1f2328)}',
335
+ '.dshw_chipLow.dshw_chipVertical .dshw_chipMain>.dshw_metric:first-child .dshw_metricValue{color:var(--dsw-alias-state-error-primary,#e5534b)}',
336
+ '.dshw_chipLow.dshw_chipVertical .dshw_chipMain>.dshw_metric:first-child .dshw_metricLabel{color:var(--dsw-alias-label-primary,#1f2328)}',
337
+ '.dshw_chipLow.dshw_noBlink{animation:none}',
325
338
  '.dshw_acctMore{display:flex;justify-content:flex-end;font-size:10.5px;color:var(--dsw-alias-label-quaternary,rgba(31,35,40,.45));margin-top:3px}',
326
- '.dshw_acctRow{display:flex;align-items:center;gap:8px;padding:6px 8px}',
327
- '.dshw_acctRow+.dshw_acctRow{border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}',
339
+ ''.trim() || '.dshw_acctRow{display:flex;align-items:center;gap:5px;padding:2px 6px;min-height:26px}',
340
+ '.dshw_acctRow+.dshw_acctRow{border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.06))}',
328
341
  '.dshw_acctRow.current{background:var(--dsw-alias-brand-soft,rgba(74,163,255,.10))}',
329
- '.dshw_acctAvatar{flex:none;width:24px;height:24px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:11.5px;font-weight:650;user-select:none}',
330
- '.dshw_acctInfo{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}',
331
- '.dshw_acctName{font-size:12px;font-weight:550;color:var(--dsw-alias-label-primary,inherit);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:flex;align-items:center;gap:5px}',
342
+ '.dshw_acctAvatar{flex:none;width:18px;height:18px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:10px;font-weight:650;user-select:none}',
343
+ '.dshw_acctInfo{flex:1;min-width:0;display:flex;align-items:center;gap:6px}',
344
+ '.dshw_acctName{font-size:11.5px;font-weight:550;color:var(--dsw-alias-label-primary,inherit);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:0 1 auto}',
332
345
  '.dshw_acctBadge{flex:none;font-size:9.5px;color:var(--dsw-alias-brand-primary,#4aa3ff);border:1px solid var(--dsw-alias-brand-primary,#4aa3ff);border-radius:4px;padding:0 4px;line-height:14px;opacity:.85}',
333
- '.dshw_acctKey{font-family:ui-monospace,Consolas,monospace;font-size:10px;color:var(--dsw-alias-label-quaternary,rgba(31,35,40,.45));white-space:nowrap;overflow:hidden;text-overflow:ellipsis}',
346
+ '.dshw_acctKey{font-family:ui-monospace,Consolas,monospace;font-size:10px;color:var(--dsw-alias-label-quaternary,rgba(31,35,40,.45));white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1 1 auto}',
334
347
  '.dshw_acctNow{flex:none;padding:1px 7px;border-radius:999px;background:var(--dsw-alias-brand-soft,rgba(74,163,255,.14));color:var(--dsw-alias-brand-primary,#4aa3ff);font-size:10.5px;font-weight:550;line-height:16px}',
335
348
  '.dshw_settingsSection .dshw_setCard{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:var(--dsw-alias-border-l1,rgba(0,0,0,.08))}',
336
349
  '.dshw_settingsSection .dshw_setCell{background:var(--dsw-alias-bg-elevated,transparent);border-top:none!important;padding:8px 10px}',
337
350
  '.dshw_settingsSection .dshw_setCell:hover{background:var(--dsw-alias-bg-l1,rgba(127,127,127,.06))}',
338
351
  '.dshw_settingsSection .dshw_setCell.wide{grid-column:1/-1}',
352
+ '.dshw_settingsSection{--dshw-card-radius:11px;max-width:900px;gap:12px!important}',
353
+ '.dshw_settingsHeader{display:flex;align-items:flex-end;justify-content:space-between;gap:12px}',
354
+ '.dshw_settingsHeading{font-size:16px;font-weight:650;letter-spacing:-.01em}',
355
+ '.dshw_settingsLead{margin-top:2px;font-size:11.5px;color:var(--dsw-alias-label-tertiary,#6b7280)}',
356
+ '.dshw_versionBadge{flex:none;padding:2px 7px;border-radius:999px;background:var(--dsw-alias-bg-l1,rgba(127,127,127,.07));color:var(--dsw-alias-label-quaternary,rgba(31,35,40,.45));font-size:10px}',
357
+ '.dshw_settingsHero{border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.13));border-radius:var(--dshw-card-radius);background:var(--dsw-alias-bg-elevated,#fff);padding:13px 15px}',
358
+ '.dshw_settingsHero.dshw_low{border-color:var(--dsw-alias-state-error-primary,#e5534b)}',
359
+ '.dshw_settingsHeroTop{display:flex;align-items:center;gap:8px}',
360
+ '.dshw_settingsHeroLabel{font-size:11.5px;color:var(--dsw-alias-label-tertiary,#6b7280)}',
361
+ '.dshw_accountPill{display:inline-flex;align-items:center;max-width:180px;padding:2px 7px;border-radius:999px;background:var(--dsw-alias-brand-soft,rgba(74,163,255,.10));color:var(--dsw-alias-label-secondary,rgba(31,35,40,.68));font-size:10.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}',
362
+ '.dshw_settingsHeroMain{display:flex;align-items:flex-end;gap:18px;margin-top:7px}',
363
+ '.dshw_settingsBalance{font-size:28px;font-weight:700;line-height:1;font-variant-numeric:tabular-nums;letter-spacing:-.025em}',
364
+ '.dshw_settingsHero.dshw_low .dshw_settingsBalance{color:var(--dsw-alias-state-error-primary,#e5534b)}',
365
+ '.dshw_settingsSpend{display:flex;flex-direction:column;gap:2px;padding-bottom:1px}',
366
+ '.dshw_settingsSpend strong{font-size:14px;font-variant-numeric:tabular-nums}',
367
+ '.dshw_settingsHeroMeta{display:flex;align-items:center;gap:7px;margin-top:9px;color:var(--dsw-alias-label-tertiary,#6b7280);font-size:11px;white-space:nowrap}',
368
+ '.dshw_settingsGrid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:10px}',
369
+ '.dshw_settingsSection .dshw_setCard{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin:0;border:0;background:transparent;overflow:visible}',
370
+ '.dshw_settingsSection .dshw_setCard>.dshw_setCell{min-width:0;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.13))!important;border-radius:var(--dshw-card-radius);background:var(--dsw-alias-bg-elevated,#fff);padding:11px 12px}',
371
+ '.dshw_settingsSection .dshw_setCard>.dshw_setCell:hover{background:var(--dsw-alias-bg-elevated,#fff)}',
372
+ '.dshw_settingsSection .dshw_setCard>.dshw_setCell.wide{grid-column:1/-1}',
373
+ '.dshw_reminderCard{display:block!important;padding:0!important;overflow:hidden}',
374
+ '.dshw_settingsGroup{min-width:0;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.13));border-radius:var(--dshw-card-radius);background:var(--dsw-alias-bg-elevated,#fff);overflow:hidden}',
375
+ '.dshw_settingsGroupHeader{padding:10px 12px;border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08))}',
376
+ '.dshw_settingsGroupTitle{font-size:12px;font-weight:650}',
377
+ '.dshw_settingsGroupHint{margin-top:2px;color:var(--dsw-alias-label-quaternary,rgba(31,35,40,.45));font-size:10.5px}',
378
+ '.dshw_settingsField{display:flex;align-items:center;justify-content:space-between;gap:10px;min-height:42px;padding:8px 12px}',
379
+ '.dshw_settingsField+.dshw_settingsField{border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}',
380
+ '.dshw_settingsFieldLabel{display:flex;min-width:0;flex-direction:column;gap:2px}',
381
+ '.dshw_settingsFieldLabel>strong{font-size:11.5px;font-weight:550}',
382
+ '.dshw_settingsFieldLabel>span{font-size:10px;color:var(--dsw-alias-label-quaternary,rgba(31,35,40,.45))}',
383
+ '.dshw_settingsInline{display:flex;align-items:center;justify-content:flex-end;gap:6px;min-width:0}',
384
+ '.dshw_settingsInline .dshw_input{height:26px;box-sizing:border-box}',
385
+ '.dshw_settingsInline .dshw_select{height:26px}',
386
+ '.dshw_settingChoices{display:grid;grid-template-columns:1fr 1fr;gap:7px;padding:9px 12px}',
387
+ '.dshw_settingChoice{display:flex;align-items:center;justify-content:space-between;gap:8px;min-width:0;padding:7px 9px;border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.09));border-radius:8px;background:var(--dsw-alias-bg-l1,rgba(127,127,127,.04))}',
388
+ '.dshw_settingChoiceCopy{min-width:0;display:flex;flex-direction:column;gap:1px}',
389
+ '.dshw_settingChoiceCopy strong{font-size:11px;font-weight:550;white-space:nowrap}',
390
+ '.dshw_settingChoiceCopy span{font-size:9.5px;color:var(--dsw-alias-label-quaternary,rgba(31,35,40,.45));white-space:nowrap;overflow:hidden;text-overflow:ellipsis}',
391
+ '.dshw_switch{position:relative;flex:none;width:34px;height:20px;display:inline-flex;cursor:pointer}',
392
+ '.dshw_switch>input{position:absolute;inset:0;opacity:0;margin:0;cursor:pointer}',
393
+ '.dshw_track{position:absolute;inset:0;border-radius:999px;background:var(--dsw-alias-border-l3,rgba(0,0,0,.22));transition:background-color .15s}',
394
+ '.dshw_knob{position:absolute;left:2px;top:2px;width:16px;height:16px;border-radius:50%;background:var(--dsw-alias-bg-overlay,#fff);box-shadow:0 1px 3px rgba(0,0,0,.24);transition:transform .15s}',
395
+ '.dshw_switch>input:checked+.dshw_track{background:var(--dsw-alias-brand-primary,#4aa3ff)}',
396
+ '.dshw_switch>input:checked~.dshw_knob{transform:translateX(14px)}',
397
+ '.dshw_switch>input:focus-visible+.dshw_track{outline:2px solid var(--dsw-alias-brand-primary,#4aa3ff);outline-offset:2px}',
398
+ '.dshw_switch>input:disabled+.dshw_track{opacity:.38}.dshw_switch>input:disabled~.dshw_knob{opacity:.7}',
399
+ '.dshw_accountHeader{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:2px}',
400
+ '.dshw_accountCount{font-size:10.5px;color:var(--dsw-alias-label-quaternary,rgba(31,35,40,.45))}',
401
+ '.dshw_settingsSection .dshw_acctScroll{max-height:150px;margin:0;border-radius:var(--dshw-card-radius)}',
402
+ '.dshw_settingsSection .dshw_acctRow{min-height:38px;padding:6px 9px;gap:8px}',
403
+ '.dshw_settingsSection .dshw_acctAvatar{width:24px;height:24px;font-size:11px}',
404
+ '.dshw_settingsSection .dshw_acctInfo{align-items:flex-start;flex-direction:column;gap:1px}',
405
+ '.dshw_settingsSection .dshw_acctName{font-size:11.5px}',
406
+ '.dshw_settingsSection .dshw_acctKey{font-size:9.5px}',
407
+ '.dshw_accountAdd{display:grid;grid-template-columns:120px minmax(150px,1fr) auto;gap:6px;padding:9px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.13));border-radius:var(--dshw-card-radius);background:var(--dsw-alias-bg-l1,rgba(127,127,127,.035))}',
408
+ '.dshw_accountAdd .dshw_input{box-sizing:border-box;width:100%;height:28px}',
409
+ '.dshw_settingsFooter{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:2px}',
410
+ '.dshw_settingsFooterActions{display:flex;align-items:center;gap:7px}',
411
+ '.dshw_settingsFooter .dshw_btn{height:30px;padding:0 14px}',
412
+ '@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}}',
339
413
  '.dshw_actionRow{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin:2px 0}',
340
414
  '.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-quaternary,rgba(31,35,40,.45));cursor:pointer;border-radius:4px;transition:color .12s,background-color .12s}',
341
415
  '.dshw_textDanger:hover{color:var(--dsw-alias-state-error-primary,#e5534b);background:var(--dsw-alias-state-error-soft,rgba(229,83,75,.12))}',
@@ -865,6 +939,22 @@ function fmtCurrency(value, currency) {
865
939
  var cfg = readNotifyConfig()
866
940
  return cfg.autoCloseMs === null ? 'keep' : String(Math.round(cfg.autoCloseMs / 1000))
867
941
  })
942
+ var [lowBlinkEnabled, setLowBlinkEnabled] = React.useState(function () {
943
+ try { return compatibility.storage.getItem(LOW_BLINK_KEY) !== 'false' } catch (e) { return true }
944
+ })
945
+ var [pdEnabled, setPdEnabled] = React.useState(function () {
946
+ try { return compatibility.storage.getItem(PERMANENT_DELETE_KEY) === 'true' } catch (e) { return false }
947
+ })
948
+ var [pdSupported, setPdSupported] = React.useState(function () {
949
+ return compatibility.hasCapability('permanentDelete')
950
+ })
951
+ React.useEffect(function () {
952
+ if (typeof window.addEventListener !== 'function') return
953
+ function refreshCap() { setPdSupported(compatibility.hasCapability('permanentDelete')) }
954
+ window.addEventListener(HOST_CAPABILITY_EVENT, refreshCap)
955
+ refreshCap()
956
+ return function () { window.removeEventListener(HOST_CAPABILITY_EVENT, refreshCap) }
957
+ }, [])
868
958
 
869
959
  React.useEffect(function () {
870
960
  var stopped = false
@@ -903,6 +993,12 @@ function fmtCurrency(value, currency) {
903
993
  compatibility.dispatch(SETTINGS_EVENT)
904
994
  }
905
995
 
996
+ var thresholdSaveTimerRef = React.useRef(null)
997
+ function queueThresholdSave(value) {
998
+ if (thresholdSaveTimerRef.current) clearTimeout(thresholdSaveTimerRef.current)
999
+ thresholdSaveTimerRef.current = setTimeout(function () { saveThreshold(value) }, 600)
1000
+ }
1001
+
906
1002
  function persistNotify(enabled, timeout) {
907
1003
  var autoCloseMs = timeout === 'keep' ? null : Number.parseInt(timeout, 10) * 1000
908
1004
  if (!Number.isFinite(autoCloseMs) && timeout !== 'keep') autoCloseMs = 10000
@@ -914,14 +1010,21 @@ function fmtCurrency(value, currency) {
914
1010
  compatibility.dispatch(NOTIFY_CONFIG_EVENT)
915
1011
  }
916
1012
 
917
- function saveThreshold() {
918
- var value = Number.parseFloat(thresholdDraft)
1013
+ function persistLowBlink(enabled) {
1014
+ enabled = enabled === true
1015
+ setLowBlinkEnabled(enabled)
1016
+ try { compatibility.storage.setItem(LOW_BLINK_KEY, String(enabled)) } catch (e) { /* ignore */ }
1017
+ compatibility.dispatch(SETTINGS_EVENT)
1018
+ }
1019
+
1020
+ function saveThreshold(valueArg) {
1021
+ var value = Number.parseFloat(valueArg !== undefined ? valueArg : thresholdDraft)
919
1022
  if (!Number.isFinite(value)) return
920
1023
  value = Math.min(100000, Math.max(0, Math.round(value * 100) / 100))
921
1024
  fetch('/api/wallet/threshold', {
922
1025
  method: 'POST',
923
1026
  headers: { 'content-type': 'application/json' },
924
- body: JSON.stringify({ threshold: value })
1027
+ body: JSON.stringify({ threshold: value, currency: (snapshot && snapshot.balance && snapshot.balance.currency) || 'CNY' })
925
1028
  }).then(function (resp) { return resp.json() }).then(function (json) {
926
1029
  if (json && json.ok) {
927
1030
  setThresholdDraft(json.threshold.toFixed(2))
@@ -936,7 +1039,11 @@ function fmtCurrency(value, currency) {
936
1039
  if (json && json.ok) { setAccounts(json); setAccountsError(null) }
937
1040
  }).catch(function () { /* ignore */ })
938
1041
  fetch('/api/wallet/snapshot').then(function (resp) { return resp.json() }).then(function (json) {
939
- if (json && json.ok) setSnapshot(json)
1042
+ if (json && json.ok) {
1043
+ setSnapshot(json)
1044
+ // 切换账号后阈值输入框立即跳到该账号货币的阈值
1045
+ if (json.threshold !== undefined && json.threshold !== null) setThresholdDraft(json.threshold.toFixed(2))
1046
+ }
940
1047
  }).catch(function () { /* ignore */ })
941
1048
  }
942
1049
 
@@ -969,6 +1076,8 @@ function fmtCurrency(value, currency) {
969
1076
  }).then(function (resp) { return resp.json() }).then(function (json) {
970
1077
  setSwitchingId(null)
971
1078
  setAccountNotice(json && json.ok ? '\u5df2\u5207\u6362\u5230\u300c' + name + '\u300d' : (json && json.error ? String(json.error) : '\u5207\u6362\u5931\u8d25'))
1079
+ // 阈值输入框零等待跳转: 激活响应自带该账号阈值
1080
+ if (json && json.ok && json.threshold !== undefined && json.threshold !== null) setThresholdDraft(json.threshold.toFixed(2))
972
1081
  reloadAccounts()
973
1082
  }).catch(function () { setSwitchingId(null); setAccountNotice('\u5207\u6362\u5931\u8d25') })
974
1083
  }
@@ -994,22 +1103,38 @@ function fmtCurrency(value, currency) {
994
1103
  // —— 余额卡 ——
995
1104
  var lowBalance = snapshot && snapshot.lowBalance === true
996
1105
  var sessionCost = snapshot && snapshot.session && snapshot.session.official && snapshot.session.official.cost !== null && snapshot.session.official.cost !== undefined ? sessionCostText(snapshot.session.official.cost, snapshot && snapshot.balance ? snapshot.balance.currency : null) : '--'
997
- rows.push(React.createElement('div', { key: 'balcard', className: 'dshw_balanceCard' },
998
- React.createElement('div', { className: 'dshw_balLine' },
999
- React.createElement('span', { className: 'dshw_muted' }, '\u4f59\u989d'),
1000
- React.createElement('span', { className: 'dshw_balNum' }, balanceText),
1001
- lowBalance ? React.createElement('span', { className: 'dshw_balWarn' }, '\u4f59\u989d\u504f\u4f4e') : null,
1106
+ var currencyCode = bal && typeof bal.currency === 'string' ? bal.currency : 'CNY'
1107
+ var primaryBalance = bal && Array.isArray(bal.balances) && bal.balances.length > 0 ? bal.balances[0] : null
1108
+ var toppedText = primaryBalance ? fmtCurrency(primaryBalance.topped_up_balance, primaryBalance.currency) : '--'
1109
+ var grantedText = primaryBalance ? fmtCurrency(primaryBalance.granted_balance, primaryBalance.currency) : '--'
1110
+ rows.push(React.createElement('div', { key: 'head', className: 'dshw_settingsHeader' },
1111
+ React.createElement('div', null,
1112
+ React.createElement('div', { className: 'dshw_settingsHeading' }, 'DeepSeek 账户中心'),
1113
+ React.createElement('div', { className: 'dshw_settingsLead' }, '余额、展示、提醒与账户切换'))))
1114
+ rows.push(React.createElement('div', { key: 'balcard', className: lowBalance ? 'dshw_settingsHero dshw_low' : 'dshw_settingsHero' },
1115
+ React.createElement('div', { className: 'dshw_settingsHeroTop' },
1116
+ React.createElement('span', { className: 'dshw_settingsHeroLabel' }, 'DeepSeek 官方余额'),
1117
+ React.createElement('span', { className: 'dshw_accountPill', title: activeName || '跟随系统 Key' }, activeName ? '当前 · ' + activeName : '跟随系统 Key'),
1002
1118
  React.createElement('span', { className: 'dshw_balFill' }),
1003
- React.createElement('span', { className: 'dshw_balSession' },
1004
- React.createElement('span', { className: 'dshw_muted' }, sessionCostLabel(snapshot && snapshot.balance ? snapshot.balance.currency : null)),
1005
- React.createElement('span', { className: 'dshw_balSessionNum' }, sessionCost))),
1006
- React.createElement('div', { className: 'dshw_balSub' },
1007
- '\u5f53\u524d\u8d26\u6237\uff1a' + (activeName ? activeName + ' \u00b7 LLM \u8ba1\u8d39' : '\u8ddf\u968f\u7cfb\u7edf Key'))))
1119
+ lowBalance ? React.createElement('span', { className: 'dshw_balWarn' }, '余额偏低') : null),
1120
+ React.createElement('div', { className: 'dshw_settingsHeroMain' },
1121
+ React.createElement('strong', { className: 'dshw_settingsBalance' }, balanceText),
1122
+ React.createElement('span', { className: 'dshw_settingsSpend' },
1123
+ React.createElement('span', { className: 'dshw_muted' }, sessionCostLabel(currencyCode)),
1124
+ React.createElement('strong', null, sessionCost))),
1125
+ React.createElement('div', { className: 'dshw_settingsHeroMeta' },
1126
+ React.createElement('span', null, '充值 ' + toppedText),
1127
+ React.createElement('span', { className: 'dshw_balDot' }, '·'),
1128
+ React.createElement('span', null, '赠送 ' + grantedText),
1129
+ React.createElement('span', { className: 'dshw_balDot' }, '·'),
1130
+ React.createElement('span', null, currencyCode))))
1008
1131
 
1009
1132
  // —— 设置卡 ——
1010
1133
  var cells = []
1011
1134
  cells.push(React.createElement('div', { key: 'vis', className: 'dshw_setCell' },
1012
- React.createElement('span', { className: 'dshw_muted' }, '\u663e\u793a\u5185\u5bb9'),
1135
+ React.createElement('span', { className: 'dshw_settingsFieldLabel' },
1136
+ React.createElement('strong', null, '显示内容'),
1137
+ React.createElement('span', null, '选择标签中的数据来源')),
1013
1138
  React.createElement('span', { className: 'dshw_balFill' }),
1014
1139
  React.createElement('label', { className: 'dshw_chipToggle' },
1015
1140
  React.createElement('input', {
@@ -1024,7 +1149,9 @@ function fmtCurrency(value, currency) {
1024
1149
  onChange: function (event) { persistVisibility({ official: visibility.official, third: event.target.checked }) }
1025
1150
  }), '\u7b2c\u4e09\u65b9')))
1026
1151
  cells.push(React.createElement('div', { key: 'sc', className: 'dshw_setCell' },
1027
- React.createElement('span', { className: 'dshw_muted' }, '\u82af\u7247\u6bd4\u4f8b'),
1152
+ React.createElement('span', { className: 'dshw_settingsFieldLabel' },
1153
+ React.createElement('strong', null, '标签比例'),
1154
+ React.createElement('span', null, '按当前停靠位置限制上限')),
1028
1155
  React.createElement('span', { className: 'dshw_balFill' }),
1029
1156
  React.createElement('span', { className: 'dshw_scaleControl' },
1030
1157
  React.createElement('input', {
@@ -1035,44 +1162,78 @@ function fmtCurrency(value, currency) {
1035
1162
  onChange: function (event) { persistScale(Number.parseFloat(event.target.value) / 100) }
1036
1163
  }),
1037
1164
  React.createElement('span', { className: 'dshw_scaleValue' }, Math.round(scale * 100) + '%'))))
1038
- cells.push(React.createElement('div', { key: 'nf', className: 'dshw_setCell' },
1039
- React.createElement('span', { className: 'dshw_muted' }, '\u5b8c\u6210\u63d0\u9192'),
1040
- React.createElement('span', { className: 'dshw_balFill' }),
1041
- React.createElement('label', { className: 'dshw_check', style: { margin: 0, gap: '5px' } },
1042
- React.createElement('input', {
1043
- type: 'checkbox', checked: notifyEnabled,
1044
- 'aria-label': '\u5f00\u542f\u5bf9\u8bdd\u5b8c\u6210\u540e\u63d0\u9192',
1045
- onChange: function (event) { persistNotify(event.target.checked, notifyTimeout) }
1046
- }), '\u5f00\u542f'),
1047
- React.createElement('select', {
1048
- className: 'dshw_select',
1049
- 'aria-label': '\u63d0\u9192\u81ea\u52a8\u5173\u95ed\u65f6\u95f4',
1050
- value: notifyTimeout, disabled: !notifyEnabled,
1051
- onChange: function (event) { persistNotify(notifyEnabled, event.target.value) }
1052
- },
1053
- React.createElement('option', { value: 'keep' }, '\u4e00\u76f4\u4fdd\u7559'),
1054
- React.createElement('option', { value: '5' }, '5 \u79d2'),
1055
- React.createElement('option', { value: '10' }, '10 \u79d2'),
1056
- React.createElement('option', { value: '30' }, '30 \u79d2'),
1057
- React.createElement('option', { value: '60' }, '60 \u79d2'))))
1058
- cells.push(React.createElement('div', { key: 'th', className: 'dshw_setCell' },
1059
- React.createElement('span', { className: 'dshw_muted' }, '\u4f59\u989d\u9608\u503c'),
1060
- React.createElement('span', { className: 'dshw_balFill' }),
1061
- React.createElement('input', {
1062
- className: 'dshw_input', type: 'number', min: '0', step: '0.01',
1063
- style: { width: '76px' },
1064
- value: thresholdDraft,
1065
- 'aria-label': '\u4f59\u989d\u63d0\u9192\u9608\u503c\uff08CNY\uff09',
1066
- onInput: function (event) { setThresholdDraft(event.target.value) },
1067
- onChange: function (event) { setThresholdDraft(event.target.value) }
1068
- }),
1069
- React.createElement('button', {
1070
- type: 'button', className: 'dshw_btn',
1071
- style: { height: '24px', padding: '0 12px', fontSize: '11.5px' },
1072
- onClick: saveThreshold
1073
- }, '\u4fdd\u5b58')))
1074
- if (thresholdNotice) cells.push(React.createElement('div', { key: 'tn', className: 'dshw_setCell wide', style: { minHeight: '26px' } },
1075
- React.createElement('span', { className: 'dshw_muted', style: { marginLeft: 'auto' } }, thresholdNotice)))
1165
+ cells.push(React.createElement('div', { key: 'quad', className: 'dshw_setCell wide dshw_reminderCard' },
1166
+ React.createElement('div', { className: 'dshw_settingsGroupHeader' },
1167
+ React.createElement('div', { className: 'dshw_settingsGroupTitle' }, '提醒与会话'),
1168
+ React.createElement('div', { className: 'dshw_settingsGroupHint' }, '提醒方式、低余额状态与会话控制')),
1169
+ React.createElement('div', { className: 'dshw_settingChoices' },
1170
+ React.createElement('div', { className: 'dshw_settingChoice' },
1171
+ React.createElement('span', { className: 'dshw_settingChoiceCopy' },
1172
+ React.createElement('strong', null, '完成提醒'),
1173
+ React.createElement('span', null, '对话结束后通知')),
1174
+ React.createElement('select', {
1175
+ className: 'dshw_select',
1176
+ 'aria-label': '完成提醒',
1177
+ value: notifyEnabled ? notifyTimeout : 'off',
1178
+ onChange: function (event) {
1179
+ var v = event.target.value
1180
+ if (v === 'off') { persistNotify(false, notifyTimeout); return }
1181
+ persistNotify(true, v)
1182
+ }
1183
+ },
1184
+ React.createElement('option', { value: 'off' }, '关闭'),
1185
+ React.createElement('option', { value: '5' }, '5 '),
1186
+ React.createElement('option', { value: '10' }, '10 秒'),
1187
+ React.createElement('option', { value: '30' }, '30 秒'),
1188
+ React.createElement('option', { value: '60' }, '60 秒'),
1189
+ React.createElement('option', { value: 'keep' }, '一直保留'))),
1190
+ React.createElement('div', { className: 'dshw_settingChoice' },
1191
+ React.createElement('span', { className: 'dshw_settingChoiceCopy' },
1192
+ React.createElement('strong', null, '低余额阈值'),
1193
+ React.createElement('span', null, thresholdNotice || (currencyCode + ' · 输入后自动保存'))),
1194
+ React.createElement('span', { className: 'dshw_settingsInline' },
1195
+ React.createElement('span', { style: { fontWeight: 700, color: 'var(--dsw-alias-label-primary,inherit)' } }, currencyCode === 'USD' ? '$' : '¥'),
1196
+ React.createElement('input', {
1197
+ className: 'dshw_input', type: 'number', min: '0', step: '0.01',
1198
+ style: { width: '64px' },
1199
+ value: thresholdDraft,
1200
+ title: '低于此余额时提醒;0 表示关闭',
1201
+ 'aria-label': '低余额阈值 (' + currencyCode + ')',
1202
+ onInput: function (event) { setThresholdDraft(event.target.value); queueThresholdSave(event.target.value) },
1203
+ onChange: function (event) { setThresholdDraft(event.target.value); queueThresholdSave(event.target.value) }
1204
+ }),
1205
+ React.createElement('span', { className: 'dshw_muted' }, currencyCode))),
1206
+ React.createElement('div', { className: 'dshw_settingChoice' },
1207
+ React.createElement('span', { className: 'dshw_settingChoiceCopy' },
1208
+ React.createElement('strong', null, '低余额闪烁'),
1209
+ React.createElement('span', null, '红框向内轻微闪烁')),
1210
+ React.createElement('label', { className: 'dshw_switch' },
1211
+ React.createElement('input', {
1212
+ type: 'checkbox', checked: lowBlinkEnabled,
1213
+ 'aria-label': '低余额时红色闪烁',
1214
+ onChange: function (event) { persistLowBlink(event.target.checked) }
1215
+ }),
1216
+ React.createElement('span', { className: 'dshw_track', 'aria-hidden': 'true' }),
1217
+ React.createElement('span', { className: 'dshw_knob', 'aria-hidden': 'true' }))),
1218
+ React.createElement('div', { className: 'dshw_settingChoice' },
1219
+ React.createElement('span', { className: 'dshw_settingChoiceCopy' },
1220
+ React.createElement('strong', null, '永久删除会话'),
1221
+ React.createElement('span', null, pdSupported ? '在会话菜单中提供入口' : '当前宿主不支持')),
1222
+ React.createElement('label', { className: 'dshw_switch', title: pdSupported ? '在会话菜单中显示永久删除' : '当前宿主未提供永久删除能力' },
1223
+ React.createElement('input', {
1224
+ type: 'checkbox', checked: pdSupported && pdEnabled, disabled: !pdSupported,
1225
+ 'aria-label': '开启永久删除会话',
1226
+ onChange: function (event) {
1227
+ if (!pdSupported) return
1228
+ var enabled = event.target.checked
1229
+ setPdEnabled(enabled)
1230
+ try { compatibility.storage.setItem(PERMANENT_DELETE_KEY, String(enabled)) } catch (e) { /* ignore */ }
1231
+ compatibility.dispatch(PERMANENT_DELETE_EVENT)
1232
+ }
1233
+ }),
1234
+ React.createElement('span', { className: 'dshw_track', 'aria-hidden': 'true' }),
1235
+ React.createElement('span', { className: 'dshw_knob', 'aria-hidden': 'true' })))))
1236
+ )
1076
1237
  rows.push(React.createElement('div', { key: 'setcard', className: 'dshw_setCard' }, cells))
1077
1238
 
1078
1239
  // —— Provider 分桶(Issue #21:包装官方的路由勾选计入官方计费) ——
@@ -1113,7 +1274,9 @@ function fmtCurrency(value, currency) {
1113
1274
  }
1114
1275
 
1115
1276
  // —— 账户管理 ——
1116
- rows.push(React.createElement('div', { key: 'acc-t', className: 'dshw_title', style: { marginTop: '8px' } }, '\u8d26\u6237\u7ba1\u7406'))
1277
+ rows.push(React.createElement('div', { key: 'acc-t', className: 'dshw_accountHeader' },
1278
+ React.createElement('span', { className: 'dshw_title' }, '账户管理'),
1279
+ React.createElement('span', { className: 'dshw_accountCount' }, list.length + ' 个账户')))
1117
1280
  if (accountsError) {
1118
1281
  rows.push(React.createElement('div', { key: 'acc-e', className: 'dshw_muted' }, accountsError))
1119
1282
  } else if (list.length === 0) {
@@ -1142,52 +1305,50 @@ function fmtCurrency(value, currency) {
1142
1305
  }, switchingId === acc.id ? '\u2026' : '\u5207\u6362'),
1143
1306
  React.createElement('button', {
1144
1307
  key: 'r', type: 'button', className: 'dshw_btn',
1145
- style: { height: '22px', padding: '0 7px', fontSize: '11px' },
1308
+ style: { height: '24px', padding: '0 8px', fontSize: '10.5px' },
1146
1309
  title: '\u5220\u9664\u8d26\u6237',
1310
+ 'aria-label': '删除账户 ' + acc.name,
1147
1311
  onClick: function () { removeAccount(acc.id, acc.name) }
1148
- }, '\u00d7'))
1312
+ }, '删除'))
1149
1313
  })
1150
1314
  rows.push(React.createElement('div', { key: 'acc-scroll', className: 'dshw_acctScroll' }, acctRows))
1151
- if (list.length > 2) rows.push(React.createElement('div', { key: 'acc-more', className: 'dshw_acctMore' }, '\u5171 ' + list.length + ' \u4e2a\u8d26\u6237\uff0c\u6ed1\u52a8\u67e5\u770b\u66f4\u591a'))
1152
1315
  }
1153
- rows.push(React.createElement('div', { key: 'acc-add', className: 'dshw_row', style: { gap: '4px' } },
1316
+ rows.push(React.createElement('div', { key: 'acc-add', className: 'dshw_accountAdd' },
1154
1317
  React.createElement('input', {
1155
- className: 'dshw_input', style: { width: '84px' },
1318
+ className: 'dshw_input',
1156
1319
  placeholder: '\u540d\u79f0', 'aria-label': '\u540d\u79f0',
1157
1320
  value: nameDraft,
1158
1321
  onInput: function (event) { setNameDraft(event.target.value) },
1159
1322
  onChange: function (event) { setNameDraft(event.target.value) }
1160
1323
  }),
1161
1324
  React.createElement('input', {
1162
- className: 'dshw_input', style: { flex: '1 1 auto', minWidth: '60px' },
1325
+ className: 'dshw_input',
1163
1326
  placeholder: 'sk-...', 'aria-label': 'API Key',
1164
1327
  value: keyDraft,
1165
1328
  onInput: function (event) { setKeyDraft(event.target.value) },
1166
1329
  onChange: function (event) { setKeyDraft(event.target.value) }
1167
1330
  }),
1168
1331
  React.createElement('button', {
1169
- type: 'button', className: 'dshw_btn dshw_btnPrimary',
1332
+ type: 'button', className: 'dshw_btn dshw_btnPrimary', style: { height: '28px', padding: '0 14px' },
1170
1333
  onClick: addAccount
1171
1334
  }, '添加')))
1172
1335
  if (accountNotice) {
1173
1336
  rows.push(React.createElement('div', { key: 'acc-n', className: 'dshw_muted' }, accountNotice))
1174
1337
  }
1175
1338
 
1176
- // —— 操作行 ——
1177
- rows.push(React.createElement('div', { key: 'acts', className: 'dshw_actionRow', style: { marginTop: '8px' } },
1178
- React.createElement('button', {
1179
- type: 'button', className: 'dshw_btn dshw_btnPrimary',
1180
- onClick: function () { if (close) close(); window.open('https://platform.deepseek.com/top_up', '_blank', 'noopener') }
1181
- }, '\u2197 \u53bb\u5b98\u65b9\u5145\u503c'),
1339
+ rows.push(React.createElement('div', { key: 'footer', className: 'dshw_settingsFooter' },
1340
+ React.createElement('span', { className: 'dshw_versionBadge' }, 'DeepSeek Harness Control Center v' + WALLET_VERSION),
1341
+ React.createElement('span', { className: 'dshw_settingsFooterActions' },
1182
1342
  React.createElement('button', {
1183
1343
  type: 'button', className: 'dshw_btn',
1184
1344
  onClick: function () {
1185
1345
  fetch('/api/wallet/refresh', { method: 'POST' }).then(reloadAccounts).catch(function () { /* ignore */ })
1186
1346
  }
1187
- }, '\u5237\u65b0\u4f59\u989d')))
1188
-
1189
-
1190
- rows.push(React.createElement('div', { key: 'ver', style: { marginTop: '8px', textAlign: 'right', fontSize: '10.5px', color: 'var(--dsw-alias-label-quaternary,rgba(31,35,40,.45))' } }, 'DeepSeek Harness Control Center v' + WALLET_VERSION))
1347
+ }, '刷新余额'),
1348
+ React.createElement('button', {
1349
+ type: 'button', className: 'dshw_btn dshw_btnPrimary',
1350
+ onClick: function () { if (close) close(); window.open('https://platform.deepseek.com/top_up', '_blank', 'noopener') }
1351
+ }, '↗ 去官方充值'))))
1191
1352
  return React.createElement('div', { className: 'dshw_settingsSection', style: { display: 'flex', flexDirection: 'column', gap: '6px', fontSize: '12px', color: 'var(--dsw-alias-label-primary,#1f2328)' } }, rows)
1192
1353
  }
1193
1354
 
@@ -1206,6 +1367,7 @@ function fmtCurrency(value, currency) {
1206
1367
  var confirmButtonRef = React.useRef(null)
1207
1368
  var restoreFocusRef = React.useRef(null)
1208
1369
  var thresholdInitializedRef = React.useRef(false)
1370
+ var thresholdEditTimerRef = React.useRef(null)
1209
1371
  var [data, setData] = React.useState(null)
1210
1372
  var [open, setOpen] = React.useState(false)
1211
1373
  var [panelStyle, setPanelStyle] = React.useState({ visibility: 'hidden' })
@@ -1350,9 +1512,14 @@ function fmtCurrency(value, currency) {
1350
1512
  if (!alive) return
1351
1513
  dataRef.current = json
1352
1514
  setData(json)
1353
- if (!thresholdInitializedRef.current && json.threshold !== undefined && json.threshold !== null) {
1354
- thresholdInitializedRef.current = true
1355
- setThresholdDraft(json.threshold.toFixed(2))
1515
+ if (json.threshold !== undefined && json.threshold !== null) {
1516
+ // Follow the active currency's own threshold line: switching
1517
+ // accounts (and thus currency) refreshes the draft unless the
1518
+ // user is mid-edit (the 600ms auto-save owns that window).
1519
+ setThresholdDraft(function (prev) {
1520
+ if (thresholdInitializedRef.current === 'editing') return prev
1521
+ return json.threshold.toFixed(2)
1522
+ })
1356
1523
  }
1357
1524
  if (showOfficial && json.lowBalance) {
1358
1525
  if (!notifiedRef.current) {
@@ -1853,14 +2020,14 @@ function fmtCurrency(value, currency) {
1853
2020
  }).catch(function () { /* page reminders remain available */ })
1854
2021
  }
1855
2022
 
1856
- function saveThreshold() {
1857
- var value = Number.parseFloat(thresholdDraft)
2023
+ function saveThreshold(valueArg) {
2024
+ var value = Number.parseFloat(valueArg !== undefined ? valueArg : thresholdDraft)
1858
2025
  if (!Number.isFinite(value)) return
1859
2026
  value = Math.min(100000, Math.max(0, Math.round(value * 100) / 100))
1860
2027
  fetch('/api/wallet/threshold', {
1861
2028
  method: 'POST',
1862
2029
  headers: { 'content-type': 'application/json' },
1863
- body: JSON.stringify({ threshold: value })
2030
+ body: JSON.stringify({ threshold: value, currency: (dataRef.current && dataRef.current.balance && dataRef.current.balance.currency) || 'CNY' })
1864
2031
  }).then(function (resp) { return resp.json() }).then(function (json) {
1865
2032
  if (json && json.ok) {
1866
2033
  setThresholdDraft(json.threshold.toFixed(2))
@@ -1869,6 +2036,12 @@ function fmtCurrency(value, currency) {
1869
2036
  }).catch(function () { /* ignore */ })
1870
2037
  }
1871
2038
 
2039
+ var thresholdSaveTimerRef = React.useRef(null)
2040
+ function queueThresholdSave(value) {
2041
+ if (thresholdSaveTimerRef.current) clearTimeout(thresholdSaveTimerRef.current)
2042
+ thresholdSaveTimerRef.current = setTimeout(function () { saveThreshold(value) }, 600)
2043
+ }
2044
+
1872
2045
  function clearSession() {
1873
2046
  if (!sessionId) return
1874
2047
  fetch('/api/wallet/clear-session', {
@@ -1961,6 +2134,9 @@ function fmtCurrency(value, currency) {
1961
2134
  setAccountNotice('\u5df2\u5207\u6362\u5230\u300c' + json.account.name + '\u300d')
1962
2135
  loadAccounts()
1963
2136
  refreshBalance()
2137
+ // 切换账号: 解锁 + 激活响应自带阈值, 输入框零等待跳转
2138
+ thresholdInitializedRef.current = false
2139
+ if (json.threshold !== undefined && json.threshold !== null) setThresholdDraft(json.threshold.toFixed(2))
1964
2140
  } else {
1965
2141
  setAccountNotice((json && json.error) || '\u5207\u6362\u5931\u8d25')
1966
2142
  }
@@ -2102,7 +2278,10 @@ function fmtCurrency(value, currency) {
2102
2278
  // cap the slider there; floating and side docks keep the full range.
2103
2279
  var scaleMaxPercent = chipDock === 'home' ? 105 : 125
2104
2280
  var chipVertical = chipDock === 'left' || chipDock === 'right' || chipDock === 'content-left'
2105
- var chipClass = low ? 'dshw_chip dshw_chipLow' : 'dshw_chip'
2281
+ var lowBlinkOff = false
2282
+ try { lowBlinkOff = compatibility.storage.getItem(LOW_BLINK_KEY) === 'false' } catch (e) { /* ignore */ }
2283
+ var lowBlinkOn = !lowBlinkOff
2284
+ var chipClass = low ? (lowBlinkOff ? 'dshw_chip dshw_chipLow dshw_noBlink' : 'dshw_chip dshw_chipLow') : 'dshw_chip'
2106
2285
  var cssZoom = compatibility.supportsCssZoom()
2107
2286
  var chipStyle = cssZoom
2108
2287
  ? { zoom: chipScale }
@@ -2122,15 +2301,16 @@ function fmtCurrency(value, currency) {
2122
2301
  React.createElement('span', { className: 'dshw_metricLabel' }, label),
2123
2302
  React.createElement('span', { className: 'dshw_metricValue' }, value))
2124
2303
  }
2125
- // A session spend below the display precision renders as $0.00
2126
- // pure noise on the space-constrained chip, so hide it there. The
2127
- // panels keep showing it.
2128
- var chipCostVisible = official.cost !== undefined && official.cost !== null
2129
- && (bal.currency === 'USD' ? official.cost / USD_ESTIMATE_PER_CNY : official.cost) >= 0.005
2304
+ // The session cost stays on the chip even at ¥0.00. Three states:
2305
+ // no session record yet show ¥0.00 (a fresh chat must not lose the
2306
+ // segment), priced show the value, unpriced (priced === false) →
2307
+ // hide, because no honest number exists.
2308
+ var chipCostHidden = official.priced === false
2309
+ var chipCostValue = (official.cost === null || official.cost === undefined) ? 0 : official.cost
2130
2310
  if (chipVertical) {
2131
2311
  if (showOfficial) {
2132
2312
  chipTextParts.push(verticalMetric('bal', '余额', balanceText))
2133
- if (chipCostVisible) chipTextParts.push(verticalMetric('cost', sessionCostLabel(bal.currency), sessionCostText(official.cost, bal.currency)))
2313
+ if (!chipCostHidden) chipTextParts.push(verticalMetric('cost', sessionCostLabel(bal.currency), sessionCostText(chipCostValue, bal.currency)))
2134
2314
  chipTextParts.push(verticalMetric('off', '\u5b98', fmtTokens(officialTokens)))
2135
2315
  }
2136
2316
  if (showThird) chipTextParts.push(verticalMetric('third', '\u4e09\u65b9', fmtTokens(thirdTokens)))
@@ -2139,7 +2319,7 @@ function fmtCurrency(value, currency) {
2139
2319
  chipTextParts.push(React.createElement('span', { key: 'bal', className: 'dshw_balanceText dshw_homePrimary' },
2140
2320
  React.createElement('span', { className: 'dshw_homePrimaryLabel' }, '余额 '),
2141
2321
  React.createElement('span', { className: 'dshw_homePrimaryValue' }, balanceText)))
2142
- if (chipCostVisible) chipTextParts.push(React.createElement('span', { key: 'cost' }, sessionCostLabel(bal.currency) + ' ' + sessionCostText(official.cost, bal.currency)))
2322
+ if (!chipCostHidden) chipTextParts.push(React.createElement('span', { key: 'cost' }, sessionCostLabel(bal.currency) + ' ' + sessionCostText(chipCostValue, bal.currency)))
2143
2323
  chipTextParts.push(React.createElement('span', { key: 'off' }, '\u5b98 ' + fmtTokens(officialTokens)))
2144
2324
  }
2145
2325
  if (showOfficial && showThird) chipTextParts.push(React.createElement('span', { key: 'sep', className: 'dshw_sep' }, '|'))
@@ -2248,7 +2428,9 @@ function fmtCurrency(value, currency) {
2248
2428
  'aria-label': '\u4f59\u989d\u63d0\u9192\u9608\u503c\uff08CNY\uff09',
2249
2429
  value: thresholdDraft === null ? '' : thresholdDraft,
2250
2430
  onChange: function (event) {
2251
- thresholdInitializedRef.current = true
2431
+ thresholdInitializedRef.current = 'editing'
2432
+ if (thresholdEditTimerRef.current) clearTimeout(thresholdEditTimerRef.current)
2433
+ thresholdEditTimerRef.current = setTimeout(function () { thresholdInitializedRef.current = false }, 15000)
2252
2434
  setThresholdDraft(event.target.value)
2253
2435
  }
2254
2436
  })
@@ -2345,7 +2527,6 @@ function fmtCurrency(value, currency) {
2345
2527
  title: '拖动控制面板',
2346
2528
  onPointerDown: onPanelDown
2347
2529
  },
2348
- React.createElement('span', { className: 'dshw_muted', style: { flex: '1', textAlign: 'center', fontSize: '10px', letterSpacing: '.02em' } }, 'v' + WALLET_VERSION),
2349
2530
  React.createElement('button', {
2350
2531
  type: 'button',
2351
2532
  className: 'dshw_btn',
@@ -2392,7 +2573,7 @@ function fmtCurrency(value, currency) {
2392
2573
  subParts.push(React.createElement('span', { key: 'd' + i, className: 'dshw_balDot' }, '\u00b7'))
2393
2574
  subParts.push(React.createElement('span', { key: 'x' + i }, info.currency + ' ' + fmtCurrency(info.total_balance, info.currency)))
2394
2575
  })
2395
- return React.createElement('div', { className: 'dshw_balanceCard' },
2576
+ return React.createElement('div', { className: low ? 'dshw_balanceCard dshw_low' : 'dshw_balanceCard' },
2396
2577
  React.createElement('div', { className: 'dshw_balLine' },
2397
2578
  React.createElement('span', { className: 'dshw_muted' }, '\u4f59\u989d'),
2398
2579
  React.createElement('span', { className: 'dshw_balNum' }, fmtCurrency(first.total_balance, first.currency)),
@@ -2429,49 +2610,72 @@ function fmtCurrency(value, currency) {
2429
2610
  }),
2430
2611
  React.createElement('span', null, '\u7b2c\u4e09\u65b9'))))
2431
2612
  cells.push(React.createElement('div', { key: 'sc', className: 'dshw_setCell' }, scaleControl))
2432
- cells.push(React.createElement('div', { key: 'nf', className: 'dshw_setCell' },
2433
- React.createElement('span', { className: 'dshw_muted' }, '\u5b8c\u6210\u63d0\u9192'),
2613
+ // 完成提醒 + 低额阈值 同行(无保存按钮后 276px 放得下)
2614
+ cells.push(React.createElement('div', { key: 'nfth', className: 'dshw_setCell', style: { gap: '5px' } },
2615
+ React.createElement('span', { className: 'dshw_muted', style: { flex: 'none' } }, '\u63d0\u9192'),
2616
+ React.createElement('select', {
2617
+ className: 'dshw_select',
2618
+ style: { flex: '0 0 auto' },
2619
+ value: notifyConfig.enabled ? (notifyConfig.timeout === 0 ? 'keep' : String(notifyConfig.timeout)) : 'off',
2620
+ 'aria-label': '\u5b8c\u6210\u63d0\u9192',
2621
+ onChange: function (event) {
2622
+ var v = event.target.value
2623
+ if (v === 'off') { saveNotifyConfig({ enabled: false, timeout: notifyConfig.timeout }); return }
2624
+ saveNotifyConfig({ enabled: true, timeout: v === 'keep' ? 0 : Number.parseInt(v, 10) })
2625
+ requestNotify()
2626
+ }
2627
+ },
2628
+ React.createElement('option', { value: 'off' }, '\u5173\u95ed'),
2629
+ React.createElement('option', { value: '5' }, '5 \u79d2'),
2630
+ React.createElement('option', { value: '10' }, '10 \u79d2'),
2631
+ React.createElement('option', { value: '30' }, '30 \u79d2'),
2632
+ React.createElement('option', { value: '60' }, '60 \u79d2'),
2633
+ React.createElement('option', { value: 'keep' }, '\u4e00\u76f4\u4fdd\u7559')),
2434
2634
  React.createElement('span', { className: 'dshw_balFill' }),
2435
- React.createElement('span', { className: 'dshw_visibilityControl' },
2436
- React.createElement('label', { className: 'dshw_check' },
2437
- React.createElement('input', {
2438
- type: 'checkbox',
2439
- checked: notifyConfig.enabled,
2440
- 'aria-label': '开启对话完成后提醒',
2441
- onChange: function (event) {
2442
- var enabled = event.target.checked
2443
- saveNotifyConfig({ enabled: enabled, timeout: notifyConfig.timeout })
2444
- if (enabled) requestNotify()
2445
- }
2446
- }),
2447
- React.createElement('span', null, '\u5f00\u542f')),
2448
- React.createElement('select', {
2449
- className: 'dshw_select',
2450
- value: String(notifyConfig.timeout),
2451
- disabled: !notifyConfig.enabled,
2452
- 'aria-label': '提醒自动关闭时间',
2635
+ React.createElement('span', { className: 'dshw_muted', style: { flex: 'none' } }, '\u9608\u503c'),
2636
+ React.createElement('input', {
2637
+ className: 'dshw_input', type: 'number', min: '0', step: '0.01',
2638
+ style: { width: '64px', flex: 'none' },
2639
+ value: thresholdDraft,
2640
+ title: '\u4f4e\u4e8e\u6b64\u4f59\u989d\u65f6\u63d0\u9192\uff080 \u5173\u95ed\uff09\uff1b\u8f93\u5165\u540e\u81ea\u52a8\u4fdd\u5b58',
2641
+ 'aria-label': '\u4f4e\u989d\u9608\u503c (' + (bal.currency === 'USD' ? 'USD' : 'CNY') + ')',
2642
+ onInput: function (event) { setThresholdDraft(event.target.value); queueThresholdSave(event.target.value) },
2643
+ onChange: function (event) { setThresholdDraft(event.target.value); queueThresholdSave(event.target.value) }
2644
+ })))
2645
+ // 低额闪烁 + 永久删除 同行
2646
+ cells.push(React.createElement('div', { key: 'blinkpd', className: 'dshw_setCell', style: { gap: '5px' } },
2647
+ React.createElement('span', { className: 'dshw_muted', style: { flex: 'none' } }, '\u4f4e\u989d\u95ea\u70c1'),
2648
+ React.createElement('label', { className: 'dshw_switch' },
2649
+ React.createElement('input', {
2650
+ type: 'checkbox',
2651
+ checked: lowBlinkOn,
2652
+ 'aria-label': '\u4f4e\u989d\u65f6\u7ea2\u8272\u95ea\u70c1',
2453
2653
  onChange: function (event) {
2454
- saveNotifyConfig({ enabled: notifyConfig.enabled, timeout: Number.parseInt(event.target.value, 10) })
2654
+ try { compatibility.storage.setItem(LOW_BLINK_KEY, String(event.target.checked)) } catch (e) { /* ignore */ }
2655
+ compatibility.dispatch(SETTINGS_EVENT)
2455
2656
  }
2456
- },
2457
- React.createElement('option', { value: '0' }, '\u4e00\u76f4\u4fdd\u7559'),
2458
- React.createElement('option', { value: '5' }, '5 \u79d2'),
2459
- React.createElement('option', { value: '10' }, '10 \u79d2'),
2460
- React.createElement('option', { value: '30' }, '30 \u79d2'),
2461
- React.createElement('option', { value: '60' }, '60 \u79d2')))))
2462
- cells.push(permanentDeleteControl === null ? null : React.createElement('div', { key: 'pd', className: 'dshw_setCell' }, permanentDeleteControl))
2463
- if (showOfficial) {
2464
- cells.push(React.createElement('div', { key: 'th', className: 'dshw_setCell' },
2465
- React.createElement('span', { className: 'dshw_muted' }, '\u4f59\u989d\u9608\u503c'),
2466
- React.createElement('span', { className: 'dshw_balFill' }),
2467
- thresholdInput,
2468
- React.createElement('button', {
2469
- type: 'button',
2470
- className: 'dshw_btn',
2471
- style: { height: '24px', padding: '0 12px', fontSize: '11.5px' },
2472
- onClick: saveThreshold
2473
- }, '\u4fdd\u5b58')))
2474
- }
2657
+ }),
2658
+ React.createElement('span', { className: 'dshw_track', 'aria-hidden': 'true' }),
2659
+ React.createElement('span', { className: 'dshw_knob', 'aria-hidden': 'true' })),
2660
+ React.createElement('span', { className: 'dshw_balFill' }),
2661
+ React.createElement('span', { className: 'dshw_muted', style: { flex: 'none' } }, permanentDeleteSupported ? '\u6c38\u4e45\u5220\u9664' : '\u6c38\u4e45\u5220\u9664(\u4e0d\u652f\u6301)'),
2662
+ React.createElement('label', { className: 'dshw_switch' },
2663
+ React.createElement('input', {
2664
+ type: 'checkbox',
2665
+ checked: permanentDeleteSupported && permanentDeleteEnabled,
2666
+ disabled: !permanentDeleteSupported,
2667
+ 'aria-label': '\u5f00\u542f\u6c38\u4e45\u5220\u9664\u4f1a\u8bdd',
2668
+ title: permanentDeleteSupported ? '\u5728\u4f1a\u8bdd\u83dc\u5355\u4e2d\u663e\u793a\u6c38\u4e45\u5220\u9664' : '\u5f53\u524d\u5bbf\u4e3b\u672a\u63d0\u4f9b\u6c38\u4e45\u5220\u9664\u80fd\u529b',
2669
+ onChange: function (event) {
2670
+ if (!permanentDeleteSupported) return
2671
+ var enabled = event.target.checked
2672
+ setPermanentDeleteEnabled(enabled)
2673
+ try { compatibility.storage.setItem(PERMANENT_DELETE_KEY, String(enabled)) } catch (e) { /* ignore */ }
2674
+ compatibility.dispatch(PERMANENT_DELETE_EVENT)
2675
+ }
2676
+ }),
2677
+ React.createElement('span', { className: 'dshw_track', 'aria-hidden': 'true' }),
2678
+ React.createElement('span', { className: 'dshw_knob', 'aria-hidden': 'true' }))))
2475
2679
  return React.createElement('div', { className: 'dshw_setCard' }, cells)
2476
2680
  }
2477
2681
 
@@ -2485,7 +2689,7 @@ function fmtCurrency(value, currency) {
2485
2689
  },
2486
2690
  floatBtnRow,
2487
2691
  showOfficial ? React.createElement(React.Fragment, null,
2488
- React.createElement('div', { className: 'dshw_title' }, '\u5b98\u65b9 DeepSeek'),
2692
+ React.createElement('div', { className: 'dshw_title' }, (snapshot.accounts && snapshot.accounts.activeName) ? '\u5b98\u65b9 DeepSeek \u00b7 ' + snapshot.accounts.activeName : '\u5b98\u65b9 DeepSeek'),
2489
2693
  balanceCard(),
2490
2694
  official.tokens ? React.createElement('div', { key: 'o-t', className: 'dshw_row' },
2491
2695
  React.createElement('span', { className: 'dshw_muted' }, '\u5b98\u65b9 token'),
@@ -2497,23 +2701,25 @@ function fmtCurrency(value, currency) {
2497
2701
  React.createElement('div', { className: 'dshw_divider' })) : null,
2498
2702
  settingsCard(),
2499
2703
  showOfficial ? React.createElement(React.Fragment, null,
2500
- React.createElement('div', { className: 'dshw_actionRow' },
2704
+ React.createElement('div', { className: 'dshw_actionRow', style: { gridTemplateColumns: '1fr 1fr 1fr' } },
2501
2705
  React.createElement('button', {
2502
2706
  type: 'button',
2503
2707
  className: 'dshw_btn dshw_btnPrimary',
2504
2708
  onClick: onRechargeClick
2505
- }, '\u2197 \u53bb\u5b98\u65b9\u5145\u503c'),
2506
- React.createElement('button', { type: 'button', className: 'dshw_btn', onClick: refreshBalance }, '\u5237\u65b0\u4f59\u989d')),
2507
- React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end' } },
2709
+ }, '\u2197 \u5145\u503c'),
2710
+ React.createElement('button', { type: 'button', className: 'dshw_btn', onClick: refreshBalance }, '\u5237\u65b0\u4f59\u989d'),
2508
2711
  React.createElement('button', {
2509
2712
  type: 'button',
2510
- className: 'dshw_textDanger',
2713
+ className: 'dshw_btn',
2714
+ style: { color: 'var(--dsw-alias-state-error-primary,#e5534b)' },
2715
+ title: '\u6e05\u9664\u672c\u4f1a\u8bdd\u7684\u4f59\u989d\u4e0e token \u6570\u636e\uff0c\u4e0d\u53ef\u6062\u590d',
2511
2716
  onClick: function () {
2512
2717
  if (window.confirm('确认清除本会话的余额与 token 数据?不可恢复。')) clearSession()
2513
2718
  }
2514
- }, '\u6e05\u9664\u4f59\u989d\u4e0e token')),
2719
+ }, '\u6e05\u9664')),
2515
2720
  React.createElement('div', { className: 'dshw_divider' })) : null,
2516
- accountsSection()
2721
+ accountsSection(),
2722
+ React.createElement('div', { style: { marginTop: '8px', textAlign: 'right', fontSize: '10px', color: 'var(--dsw-alias-label-quaternary,rgba(31,35,40,.45))', userSelect: 'none' } }, 'DeepSeek Harness Control Center v' + WALLET_VERSION)
2517
2723
  )
2518
2724
 
2519
2725
  if (floated) {
@@ -2587,7 +2793,8 @@ function fmtCurrency(value, currency) {
2587
2793
  }
2588
2794
  }, '清除余额与 token'),
2589
2795
  React.createElement('div', { className: 'dshw_divider' }),
2590
- accountsSection()
2796
+ accountsSection(),
2797
+ React.createElement('div', { style: { marginTop: '8px', textAlign: 'right', fontSize: '10px', color: 'var(--dsw-alias-label-quaternary,rgba(31,35,40,.45))', userSelect: 'none' } }, 'DeepSeek Harness Control Center v' + WALLET_VERSION)
2591
2798
  )
2592
2799
  return React.createElement(React.Fragment, null, floatPanel, confirmOverlay)
2593
2800
  }
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.0",
4
+ "version": "0.2.1",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "exports": {